#include "twn_engine_context_c.h" #include "twn_util_c.h" #include "twn_draw_c.h" #include #ifdef __EMSCRIPTEN__ #define GL_GLEXT_PROTOTYPES #include #include #else #include #endif void setup_viewport(int x, int y, int width, int height) { glViewport(x, y, width, height); } VertexBuffer create_vertex_buffer(void) { GLuint result; glGenBuffers(1, &result); return result; } void delete_vertex_buffer(VertexBuffer buffer) { glDeleteBuffers(1, &buffer); } IndexBuffer create_index_buffer(void) { GLuint result; glGenBuffers(1, &result); return result; } void delete_index_buffer(IndexBuffer buffer) { glDeleteBuffers(1, &buffer); } void specify_vertex_buffer(VertexBuffer buffer, void const *data, size_t bytes) { glBindBuffer(GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, bytes, data, GL_STREAM_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } void specify_index_buffer(IndexBuffer buffer, void const *data, size_t bytes) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, bytes, data, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } IndexBuffer get_quad_element_buffer(void) { static IndexBuffer buffer = 0; /* it's only generated once at runtime */ /* TODO: use builder interface, not direct calls (glMapBuffer isn't portable) */ if (buffer == 0) { buffer = create_index_buffer(); IndexBufferBuilder builder = build_index_buffer(buffer, sizeof (GLuint) * QUAD_ELEMENT_BUFFER_LENGTH * 6 ); for (size_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { ((GLuint *)builder.base)[i * 6 + 0] = (GLuint)(i * 4 + 0); ((GLuint *)builder.base)[i * 6 + 1] = (GLuint)(i * 4 + 1); ((GLuint *)builder.base)[i * 6 + 2] = (GLuint)(i * 4 + 2); ((GLuint *)builder.base)[i * 6 + 3] = (GLuint)(i * 4 + 2); ((GLuint *)builder.base)[i * 6 + 4] = (GLuint)(i * 4 + 3); ((GLuint *)builder.base)[i * 6 + 5] = (GLuint)(i * 4 + 0); } finish_index_builder(&builder); } SDL_assert_always(buffer); return buffer; } IndexBuffer get_circle_element_buffer(void) { static IndexBuffer buffer = 0; if (buffer == 0) { buffer = create_index_buffer(); IndexBufferBuilder builder = build_index_buffer(buffer, sizeof (GLint) * (CIRCLE_VERTICES_MAX - 2) * 3); for (size_t i = 1; i < CIRCLE_VERTICES_MAX - 1; ++i) { /* first one is center point index, always zero */ ((GLint *)builder.base)[(i - 1) * 3 + 0] = 0; /* generated point index */ ((GLint *)builder.base)[(i - 1) * 3 + 1] = (GLint)i; ((GLint *)builder.base)[(i - 1) * 3 + 2] = (GLint)i + 1; } finish_index_builder(&builder); } SDL_assert_always(buffer); return buffer; } /* potentially double buffered array of vertex array handles */ /* we assume they will be refilled fully each frame */ static size_t scratch_va_front_used, scratch_va_back_used; static GLuint *front_scratch_vertex_arrays, *back_scratch_vertex_arrays; static GLuint **current_scratch_vertex_array = &front_scratch_vertex_arrays; void restart_scratch_vertex_arrays(void) { scratch_va_front_used = 0; scratch_va_back_used = 0; if (ctx.render_double_buffered) { current_scratch_vertex_array = current_scratch_vertex_array == &front_scratch_vertex_arrays ? &back_scratch_vertex_arrays : &front_scratch_vertex_arrays; } } GLuint get_scratch_vertex_array(void) { size_t *used = current_scratch_vertex_array == &front_scratch_vertex_arrays ? &scratch_va_front_used : &scratch_va_back_used; if (arrlenu(*current_scratch_vertex_array) <= *used) { GLuint handle; glGenBuffers(1, &handle); arrpush(*current_scratch_vertex_array, handle); } (*used)++; return (*current_scratch_vertex_array)[*used - 1]; } GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps, int channels, int width, int height) { GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); SDL_assert(width > 0 && height > 0); SDL_assert(channels > 0 && channels <= 4); /* TODO: test whether emscripten emulates this */ #ifndef __EMSCRIPTEN__ if (generate_mipmaps) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3); } #else (void)generate_mipmaps; #endif if (filter == TEXTURE_FILTER_NEAREAST) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generate_mipmaps ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else if (filter == TEXTURE_FILTER_LINEAR) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generate_mipmaps ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } /* it's assumed to be default gl state, so, don't bother */ // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); int format_internal, format; if (channels == 4) { format_internal = GL_RGBA8; format = GL_RGBA; } else if (channels == 3) { format_internal = GL_RGBA8; format = GL_RGB; } else if (channels == 1) { format_internal = GL_ALPHA; format = GL_ALPHA; } else { CRY("upload_gpu_texture", "Unsupported channel count"); format_internal = GL_ALPHA; format = GL_ALPHA; } /* preallocate texture storage in advance */ glTexImage2D(GL_TEXTURE_2D, 0, format_internal, width, height, 0, format, GL_UNSIGNED_BYTE, NULL); glBindTexture(GL_TEXTURE_2D, 0); return texture; } void delete_gpu_texture(GPUTexture texture) { glDeleteTextures(1, &texture); } void upload_gpu_texture(GPUTexture texture, void *pixels, int channels, int width, int height) { glBindTexture(GL_TEXTURE_2D, texture); int format; if (channels == 4) { format = GL_RGBA; } else if (channels == 3) { format = GL_RGB; } else if (channels == 1) { format = GL_ALPHA; } else { CRY("upload_gpu_texture", "Unsupported channel count"); return; } glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_2D, 0); } void bind_gpu_texture(GPUTexture texture) { glBindTexture(GL_TEXTURE_2D, texture); }