243 lines
6.9 KiB
C
243 lines
6.9 KiB
C
#include "twn_engine_context_c.h"
|
|
#include "twn_util_c.h"
|
|
#include "twn_draw_c.h"
|
|
|
|
#include <stb_ds.h>
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#include <GL/gl.h>
|
|
#include <GL/glext.h>
|
|
#else
|
|
#include <glad/glad.h>
|
|
#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);
|
|
}
|