#include "twn_engine_context_c.h" #include "twn_util_c.h" #include "twn_draw_c.h" #include #ifdef EMSCRIPTEN #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); } 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); } VertexBuffer get_quad_element_buffer(void) { static VertexBuffer 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_vertex_buffer(); VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * QUAD_ELEMENT_BUFFER_LENGTH * 6 ); for (size_t i = 0; i < QUAD_ELEMENT_BUFFER_LENGTH; ++i) { ((GLshort *)builder.base)[i * 6 + 0] = (GLshort)(i * 4 + 0); ((GLshort *)builder.base)[i * 6 + 1] = (GLshort)(i * 4 + 1); ((GLshort *)builder.base)[i * 6 + 2] = (GLshort)(i * 4 + 2); ((GLshort *)builder.base)[i * 6 + 3] = (GLshort)(i * 4 + 2); ((GLshort *)builder.base)[i * 6 + 4] = (GLshort)(i * 4 + 3); ((GLshort *)builder.base)[i * 6 + 5] = (GLshort)(i * 4 + 0); } finish_vertex_builder(&builder); } SDL_assert_always(buffer); return buffer; } VertexBuffer get_circle_element_buffer(void) { static VertexBuffer buffer = 0; if (buffer == 0) { buffer = create_vertex_buffer(); VertexBufferBuilder builder = build_vertex_buffer(buffer, sizeof (GLshort) * (CIRCLE_VERTICES_MAX - 2) * 3); for (size_t i = 1; i < CIRCLE_VERTICES_MAX - 1; ++i) { /* first one is center point index, always zero */ ((GLshort *)builder.base)[(i - 1) * 3 + 0] = 0; /* generated point index */ ((GLshort *)builder.base)[(i - 1) * 3 + 1] = (GLshort)i; ((GLshort *)builder.base)[(i - 1) * 3 + 2] = (GLshort)i + 1; } finish_vertex_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); #if !defined(EMSCRIPTEN) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps); #else if (generate_mipmaps) glGenerateMipmap(GL_TEXTURE_2D); #endif if (filter == TEXTURE_FILTER_NEAREAST) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 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, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); #if !defined(EMSCRIPTEN) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); #endif int format_internal, format; if (channels == 4) { #ifdef EMSCRIPTEN format_internal = GL_RGBA; #else format_internal = GL_RGBA8; #endif format = GL_RGBA; } else if (channels == 3) { #ifdef EMSCRIPTEN format_internal = GL_RGBA; #else format_internal = GL_RGBA8; #endif 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); }