682 lines
21 KiB
C
682 lines
21 KiB
C
#include "twn_draw_c.h"
|
|
#include "twn_util.h"
|
|
#include "twn_util_c.h"
|
|
#include "twn_engine_context_c.h"
|
|
#include "twn_types.h"
|
|
#include "twn_deferred_commands.h"
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#include <GL/gl.h>
|
|
#include <GL/glext.h>
|
|
#else
|
|
#include <glad/glad.h>
|
|
#endif
|
|
#include <stb_ds.h>
|
|
|
|
/* note: care must be taken to always have interleaved VBOs with all data, */
|
|
/* as it is optimized in emscripten legacy gl emulation. */
|
|
/* constant color isn't supported there, so care must be given to provide alternative path with VBO as well. */
|
|
|
|
|
|
static TextureMode texture_mode_last_used = TEXTURE_MODE_UNKNOWN;
|
|
static Pipeline pipeline_last_used = PIPELINE_NO;
|
|
|
|
|
|
static void APIENTRY opengl_log(GLenum source,
|
|
GLenum type,
|
|
GLuint id,
|
|
GLenum severity,
|
|
GLsizei length,
|
|
const GLchar* message,
|
|
const void* userParam)
|
|
{
|
|
(void)source;
|
|
(void)type;
|
|
(void)id;
|
|
(void)severity;
|
|
(void)userParam;
|
|
|
|
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION || severity == GL_DEBUG_SEVERITY_LOW)
|
|
return;
|
|
|
|
log_info("OpenGL: %.*s\n", length, message);
|
|
}
|
|
|
|
|
|
bool render_init(void) {
|
|
#ifndef __EMSCRIPTEN__
|
|
if (gladLoadGLLoader(&SDL_GL_GetProcAddress) == 0) {
|
|
CRY("Init", "GLAD failed");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
log_info("OpenGL context: %s\n", glGetString(GL_VERSION));
|
|
|
|
glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST);
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
|
|
glHint(GL_FOG_HINT, GL_FASTEST);
|
|
|
|
/* hook up opengl debugging callback */
|
|
if (ctx.game.debug) {
|
|
glEnable(GL_DEBUG_OUTPUT);
|
|
glDebugMessageCallback(opengl_log, NULL);
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void start_render_frame(void) {
|
|
clear_draw_buffer();
|
|
pipeline_last_used = PIPELINE_NO;
|
|
}
|
|
|
|
|
|
void end_render_frame(void) {
|
|
if (!ctx.render_double_buffered || (fabsf(1.0f - ctx.game.frame_number) < 0.00001f)) {
|
|
issue_deferred_draw_commands();
|
|
SDL_GL_SwapWindow(ctx.window);
|
|
arrsetlen(deferred_commands, 0);
|
|
restart_scratch_vertex_arrays();
|
|
} else {
|
|
/* instead of waiting for frame to finish for the swap, we continue */
|
|
/* while issuing new state for the next call, but deferring any fragment emitting calls for later */
|
|
/* actual swap will happen when next frame is fully finished, introducing a delay */
|
|
SDL_GL_SwapWindow(ctx.window);
|
|
issue_deferred_draw_commands();
|
|
restart_scratch_vertex_arrays();
|
|
glFlush();
|
|
arrsetlen(deferred_commands, 0);
|
|
}
|
|
}
|
|
|
|
|
|
static void finally_use_space_pipeline(void) {
|
|
if (pipeline_last_used == PIPELINE_SPACE)
|
|
return;
|
|
|
|
depth_range_high = 1.0;
|
|
depth_range_low = 0.0;
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
static GLuint list = 0;
|
|
if (!list) {
|
|
list = glGenLists(1);
|
|
|
|
glNewList(list, GL_COMPILE);
|
|
#endif
|
|
{
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
|
glShadeModel(GL_FLAT);
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
if (GLAD_GL_ARB_depth_clamp)
|
|
glDisable(GL_DEPTH_CLAMP);
|
|
#endif
|
|
|
|
if (ctx.cull_faces)
|
|
glEnable(GL_CULL_FACE);
|
|
else
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
/* solid white, no modulation */
|
|
glColor4ub(255, 255, 255, 255);
|
|
#ifndef __EMSCRIPTEN__
|
|
} glEndList();
|
|
|
|
glCallList(list);
|
|
#endif
|
|
}
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixf(&camera_projection_matrix.row[0].x);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadMatrixf(&camera_look_at_matrix.row[0].x);
|
|
|
|
texture_mode_last_used = -1;
|
|
pipeline_last_used = PIPELINE_SPACE;
|
|
}
|
|
|
|
|
|
static void finally_use_2d_pipeline(void) {
|
|
if (pipeline_last_used == PIPELINE_SPACE) {
|
|
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
|
glFlush();
|
|
}
|
|
|
|
if (pipeline_last_used == PIPELINE_2D)
|
|
return;
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
static GLuint list = 0;
|
|
if (!list) {
|
|
list = glGenLists(1);
|
|
glNewList(list, GL_COMPILE); {
|
|
#endif
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
|
|
glShadeModel(GL_FLAT);
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
/* removes near/far plane comparison and discard */
|
|
if (GLAD_GL_ARB_depth_clamp)
|
|
glEnable(GL_DEPTH_CLAMP);
|
|
#endif
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glDisable(GL_CULL_FACE);
|
|
glEnable(GL_DEPTH_TEST);
|
|
#ifndef __EMSCRIPTEN__
|
|
} glEndList();
|
|
|
|
} glCallList(list);
|
|
#endif
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0, (double)ctx.base_render_width, (double)ctx.base_render_height, 0, 0, 1);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
/* rotate against center of the viewport */
|
|
Vec2 const center_offset = { ctx.game.resolution.x / 2, ctx.game.resolution.y / 2 };
|
|
glTranslatef(center_offset.x, center_offset.y, 0);
|
|
glRotatef(camera_2d_rotation, 0, 0, 1);
|
|
glScalef(camera_2d_zoom, camera_2d_zoom, 1);
|
|
glTranslatef(-center_offset.x, -center_offset.y, 0);
|
|
/* apply the rest */
|
|
glTranslatef(-camera_2d_position.x, -camera_2d_position.y, 0);
|
|
|
|
texture_mode_last_used = -1;
|
|
pipeline_last_used = PIPELINE_2D;
|
|
}
|
|
|
|
|
|
static void setup_ghostly_texture_mode(void) {
|
|
#ifndef __EMSCRIPTEN__
|
|
static GLuint list = 0;
|
|
if (!list) {
|
|
list = glGenLists(1);
|
|
glNewList(list, GL_COMPILE);
|
|
#endif
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glDepthFunc(GL_LESS);
|
|
glDepthMask(GL_FALSE);
|
|
glDisable(GL_ALPHA_TEST);
|
|
#ifndef __EMSCRIPTEN__
|
|
glEndList();
|
|
}
|
|
glCallList(list);
|
|
#endif
|
|
}
|
|
|
|
|
|
static void setup_seethrough_texture_mode(void) {
|
|
#ifndef __EMSCRIPTEN__
|
|
static GLuint list = 0;
|
|
if (!list) {
|
|
list = glGenLists(1);
|
|
glNewList(list, GL_COMPILE);
|
|
#endif
|
|
glDisable(GL_BLEND);
|
|
glDepthFunc(GL_LESS);
|
|
glDepthMask(GL_TRUE);
|
|
glEnable(GL_ALPHA_TEST);
|
|
glAlphaFunc(GL_EQUAL, 1.0f);
|
|
#ifndef __EMSCRIPTEN__
|
|
glEndList();
|
|
}
|
|
glCallList(list);
|
|
#endif
|
|
}
|
|
|
|
|
|
static void setup_opaque_texture_mode(void) {
|
|
#ifndef __EMSCRIPTEN__
|
|
static GLuint list = 0;
|
|
if (!list) {
|
|
list = glGenLists(1);
|
|
glNewList(list, GL_COMPILE);
|
|
#endif
|
|
glDisable(GL_BLEND);
|
|
glDepthFunc(GL_LESS);
|
|
glDepthMask(GL_TRUE);
|
|
glDisable(GL_ALPHA_TEST);
|
|
#ifndef __EMSCRIPTEN__
|
|
glEndList();
|
|
}
|
|
glCallList(list);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* TODO: ensure we minimize depth func switching to enable Hi-Z (hierarchical depth) optimizations */
|
|
static void finally_use_texture_mode(TextureMode mode) {
|
|
if (texture_mode_last_used == mode)
|
|
return;
|
|
|
|
if (mode == TEXTURE_MODE_GHOSTLY) {
|
|
setup_ghostly_texture_mode();
|
|
} else if (mode == TEXTURE_MODE_SEETHROUGH) {
|
|
setup_seethrough_texture_mode();
|
|
} else {
|
|
setup_opaque_texture_mode();
|
|
}
|
|
|
|
texture_mode_last_used = mode;
|
|
}
|
|
|
|
|
|
VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes) {
|
|
SDL_assert(bytes != 0);
|
|
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
|
glBufferData(GL_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW);
|
|
#ifndef __EMSCRIPTEN__
|
|
void *mapping = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
|
|
#else
|
|
void *mapping = SDL_malloc(bytes);
|
|
#endif
|
|
if (!mapping)
|
|
CRY("build_vertex_buffer", "Error mapping a vertex array buffer");
|
|
|
|
return (VertexBufferBuilder) {
|
|
.base = mapping,
|
|
.size = bytes,
|
|
};
|
|
}
|
|
|
|
|
|
void finish_vertex_builder(VertexBufferBuilder *builder) {
|
|
#ifndef __EMSCRIPTEN__
|
|
if (!glUnmapBuffer(GL_ARRAY_BUFFER))
|
|
CRY("finish_vertex_builder", "Error unmapping a vertex array buffer");
|
|
#else
|
|
glBufferData(GL_ARRAY_BUFFER, builder->size, builder->base, GL_STREAM_DRAW);
|
|
SDL_free(builder->base);
|
|
#endif
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
builder->base = 0;
|
|
builder->size = 0;
|
|
}
|
|
|
|
|
|
IndexBufferBuilder build_index_buffer(IndexBuffer buffer, size_t bytes) {
|
|
SDL_assert(bytes != 0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW);
|
|
#ifndef __EMSCRIPTEN__
|
|
void *mapping = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
|
|
#else
|
|
void *mapping = SDL_malloc(bytes);
|
|
#endif
|
|
if (!mapping)
|
|
CRY("build_vertex_buffer", "Error mapping a vertex array buffer");
|
|
|
|
return (IndexBufferBuilder) {
|
|
.base = mapping,
|
|
.size = bytes,
|
|
};
|
|
}
|
|
|
|
|
|
void finish_index_builder(IndexBufferBuilder *builder) {
|
|
#ifndef __EMSCRIPTEN__
|
|
if (!glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER))
|
|
CRY("finish_vertex_builder", "Error unmapping a vertex array buffer");
|
|
#else
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, builder->size, builder->base, GL_STREAM_DRAW);
|
|
SDL_free(builder->base);
|
|
#endif
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
builder->base = 0;
|
|
builder->size = 0;
|
|
}
|
|
|
|
|
|
static void load_cubemap_side(const char *path, GLenum target) {
|
|
SDL_Surface *surface = textures_load_surface(path);
|
|
/* TODO: sanity check whether all of them have same dimensions? */
|
|
glTexImage2D(target,
|
|
0,
|
|
GL_RGBA8,
|
|
surface->w, surface->h,
|
|
0,
|
|
surface->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB,
|
|
GL_UNSIGNED_BYTE,
|
|
surface->pixels);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
|
|
SDL_free(surface->pixels);
|
|
SDL_FreeSurface(surface);
|
|
}
|
|
|
|
|
|
void finally_render_skybox(DeferredCommandDrawSkybox command) {
|
|
static GLuint cubemap = 0;
|
|
static char *paths_cache = NULL;
|
|
|
|
bool loading_needed = false;
|
|
|
|
/* drop it */
|
|
if (!paths_cache || (SDL_strcmp(paths_cache, command.paths) != 0)) {
|
|
if (cubemap)
|
|
glDeleteTextures(1, &cubemap);
|
|
glGenTextures(1, &cubemap);
|
|
if (paths_cache)
|
|
SDL_free(paths_cache);
|
|
paths_cache = command.paths;
|
|
loading_needed = true;
|
|
} else
|
|
SDL_free(command.paths);
|
|
|
|
Matrix4 camera_look_at_matrix_solipsist = camera_look_at_matrix;
|
|
camera_look_at_matrix_solipsist.row[3].x = 0;
|
|
camera_look_at_matrix_solipsist.row[3].y = 0;
|
|
camera_look_at_matrix_solipsist.row[3].z = 0;
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadMatrixf(&camera_look_at_matrix_solipsist.row[0].x);
|
|
|
|
glDepthRange(0.0f, 1.0f);
|
|
glDisable(GL_FOG);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
glEnable(GL_TEXTURE_CUBE_MAP);
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap);
|
|
|
|
if (loading_needed) {
|
|
/* load all the sides */
|
|
char *expanded = expand_asterisk(command.paths, "up");
|
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_Y);
|
|
SDL_free(expanded);
|
|
|
|
expanded = expand_asterisk(command.paths, "down");
|
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);
|
|
SDL_free(expanded);
|
|
|
|
expanded = expand_asterisk(command.paths, "east");
|
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_X);
|
|
SDL_free(expanded);
|
|
|
|
expanded = expand_asterisk(command.paths, "north");
|
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_Z);
|
|
SDL_free(expanded);
|
|
|
|
expanded = expand_asterisk(command.paths, "west");
|
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_X);
|
|
SDL_free(expanded);
|
|
|
|
expanded = expand_asterisk(command.paths, "south");
|
|
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
|
|
SDL_free(expanded);
|
|
}
|
|
|
|
/* TODO: figure out which coordinates to use to not have issues with far z */
|
|
/* TODO: recalculate the list if far z requirement changes */
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
static GLuint list = 0;
|
|
if (!list) {
|
|
list = glGenLists(1);
|
|
|
|
glNewList(list, GL_COMPILE);
|
|
#endif
|
|
{
|
|
/* note: assumes that space pipeline is applied already */
|
|
glDisable(GL_ALPHA_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
/* removes near/far plane comparison and discard */
|
|
if (GLAD_GL_ARB_depth_clamp)
|
|
glEnable(GL_DEPTH_CLAMP);
|
|
#endif
|
|
|
|
glBegin(GL_QUADS); {
|
|
/* up */
|
|
glTexCoord3f(50.f, 50.f, 50.f);
|
|
glVertex3f(50.f, 50.f, 50.f);
|
|
glTexCoord3f(-50.f, 50.f, 50.f);
|
|
glVertex3f(-50.f, 50.f, 50.f);
|
|
glTexCoord3f(-50.f, 50.f, -50.f);
|
|
glVertex3f(-50.f, 50.f, -50.f);
|
|
glTexCoord3f(50.f, 50.f, -50.f);
|
|
glVertex3f(50.f, 50.f, -50.f);
|
|
|
|
/* down */
|
|
glTexCoord3f(50.f, -50.f, 50.f);
|
|
glVertex3f(50.f, -50.f, 50.f);
|
|
glTexCoord3f(50.f, -50.f, -50.f);
|
|
glVertex3f(50.f, -50.f, -50.f);
|
|
glTexCoord3f(-50.f, -50.f, -50.f);
|
|
glVertex3f(-50.f, -50.f, -50.f);
|
|
glTexCoord3f(-50.f, -50.f, 50.f);
|
|
glVertex3f(-50.f, -50.f, 50.f);
|
|
|
|
/* east */
|
|
glTexCoord3f(50.f, -50.f, 50.f);
|
|
glVertex3f(50.f, -50.f, 50.f);
|
|
glTexCoord3f(50.f, 50.f, 50.f);
|
|
glVertex3f(50.f, 50.f, 50.f);
|
|
glTexCoord3f(50.f, 50.f, -50.f);
|
|
glVertex3f(50.f, 50.f, -50.f);
|
|
glTexCoord3f(50.f, -50.f, -50.f);
|
|
glVertex3f(50.f, -50.f, -50.f);
|
|
|
|
/* west */
|
|
glTexCoord3f(-50.f, -50.f, 50.f);
|
|
glVertex3f(-50.f, -50.f, 50.f);
|
|
glTexCoord3f(-50.f, -50.f, -50.f);
|
|
glVertex3f(-50.f, -50.f, -50.f);
|
|
glTexCoord3f(-50.f, 50.f, -50.f);
|
|
glVertex3f(-50.f, 50.f, -50.f);
|
|
glTexCoord3f(-50.f, 50.f, 50.f);
|
|
glVertex3f(-50.f, 50.f, 50.f);
|
|
|
|
/* north */
|
|
glTexCoord3f(-50.f, -50.f, 50.f);
|
|
glVertex3f(-50.f, -50.f, 50.f);
|
|
glTexCoord3f(-50.f, 50.f, 50.f);
|
|
glVertex3f(-50.f, 50.f, 50.f);
|
|
glTexCoord3f(50.f, 50.f, 50.f);
|
|
glVertex3f(50.f, 50.f, 50.f);
|
|
glTexCoord3f(50.f, -50.f, 50.f);
|
|
glVertex3f(50.f, -50.f, 50.f);
|
|
|
|
/* south */
|
|
glTexCoord3f(-50.f, -50.f, -50.f);
|
|
glVertex3f(-50.f, -50.f, -50.f);
|
|
glTexCoord3f(50.f, -50.f, -50.f);
|
|
glVertex3f(50.f, -50.f, -50.f);
|
|
glTexCoord3f(50.f, 50.f, -50.f);
|
|
glVertex3f(50.f, 50.f, -50.f);
|
|
glTexCoord3f(-50.f, 50.f, -50.f);
|
|
glVertex3f(-50.f, 50.f, -50.f);
|
|
} glEnd();
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
if (GLAD_GL_ARB_depth_clamp)
|
|
glDisable(GL_DEPTH_CLAMP);
|
|
#endif
|
|
|
|
glDepthMask(GL_TRUE);
|
|
glDisable(GL_TEXTURE_CUBE_MAP);
|
|
|
|
#ifndef __EMSCRIPTEN__
|
|
} glEndList();
|
|
glCallList(list);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void finally_clear_draw_buffer(DeferredCommandClear command) {
|
|
glClearColor((1.0f / 255) * command.color.r,
|
|
(1.0f / 255) * command.color.g,
|
|
(1.0f / 255) * command.color.b,
|
|
(1.0f / 255) * command.color.a);
|
|
|
|
/* needed as we might mess with it */
|
|
glDepthRange(0.0, 1.0);
|
|
glDepthMask(GL_TRUE);
|
|
|
|
glClear((command.clear_color ? GL_COLOR_BUFFER_BIT : 0) |
|
|
(command.clear_depth ? GL_DEPTH_BUFFER_BIT : 0) |
|
|
(command.clear_stencil ? GL_STENCIL_BUFFER_BIT : 0) );
|
|
}
|
|
|
|
|
|
static GLsizei to_gl_type_enum(int value) {
|
|
switch (value) {
|
|
case TWN_FLOAT: return GL_FLOAT;
|
|
case TWN_INT: return GL_INT;
|
|
case TWN_SHORT: return GL_SHORT;
|
|
case TWN_UNSIGNED_SHORT: return GL_UNSIGNED_SHORT;
|
|
case TWN_UNSIGNED_BYTE: return GL_UNSIGNED_BYTE;
|
|
case TWN_BYTE: return GL_BYTE;
|
|
default:
|
|
CRY("to_gl_type_enum", "Unknown primitive type");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
void finally_draw_command(DeferredCommandDraw command) {
|
|
/* TODO: don't assume a single vertex array ? */
|
|
SDL_assert(command.vertices.arity != 0);
|
|
SDL_assert(command.vertices.buffer);
|
|
SDL_assert((command.element_buffer && command.element_count != 0) || command.primitive_count != 0);
|
|
|
|
/* TODO: cache previous setting, don't recommit */
|
|
glDepthRange(command.depth_range_low, command.depth_range_high);
|
|
|
|
/* TODO: cache it for constant parameters, which is a common case */
|
|
if (command.pipeline == PIPELINE_SPACE && fabsf(0.0f - ctx.game_copy.fog_density) >= 0.00001f) {
|
|
glEnable(GL_FOG);
|
|
|
|
/* clamp to valid range */
|
|
ctx.game_copy.fog_density = clampf(ctx.game_copy.fog_density, 0.0, 1.0);
|
|
|
|
glFogf(GL_FOG_DENSITY, ctx.game_copy.fog_density);
|
|
|
|
float color_conv[4];
|
|
color_conv[0] = (float)ctx.game_copy.fog_color.r / UINT8_MAX;
|
|
color_conv[1] = (float)ctx.game_copy.fog_color.g / UINT8_MAX;
|
|
color_conv[2] = (float)ctx.game_copy.fog_color.b / UINT8_MAX;
|
|
color_conv[3] = (float)ctx.game_copy.fog_color.a / UINT8_MAX;
|
|
|
|
glFogfv(GL_FOG_COLOR, color_conv);
|
|
} else
|
|
glDisable(GL_FOG);
|
|
|
|
if (command.pipeline == PIPELINE_SPACE)
|
|
finally_use_space_pipeline();
|
|
else
|
|
finally_use_2d_pipeline();
|
|
|
|
finally_use_texture_mode(command.texture_mode);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, command.vertices.buffer);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, command.element_buffer);
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glVertexPointer(command.vertices.arity,
|
|
to_gl_type_enum(command.vertices.type),
|
|
command.vertices.stride,
|
|
(void *)command.vertices.offset);
|
|
|
|
if (command.texcoords.arity != 0) {
|
|
SDL_assert(command.texcoords.buffer == command.vertices.buffer);
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
glTexCoordPointer(command.texcoords.arity,
|
|
to_gl_type_enum(command.texcoords.type),
|
|
command.texcoords.stride,
|
|
(void *)command.texcoords.offset);
|
|
}
|
|
|
|
if (command.colors.arity != 0) {
|
|
SDL_assert(command.colors.buffer == command.vertices.buffer);
|
|
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
glColorPointer(command.colors.arity,
|
|
to_gl_type_enum(command.colors.type),
|
|
command.colors.stride,
|
|
(void *)command.colors.offset);
|
|
|
|
} else if (command.constant_colored)
|
|
glColor4ub(command.color.r,
|
|
command.color.g,
|
|
command.color.b,
|
|
command.color.a);
|
|
|
|
if (command.textured) {
|
|
if (command.uses_gpu_key)
|
|
glBindTexture(GL_TEXTURE_2D, command.gpu_texture);
|
|
else if (command.texture_repeat)
|
|
textures_bind_repeating(&ctx.texture_cache, command.texture_key);
|
|
else
|
|
textures_bind(&ctx.texture_cache, command.texture_key);
|
|
}
|
|
|
|
if (command.element_buffer) {
|
|
SDL_assert(command.element_count != 0);
|
|
if (command.range_start == command.range_end)
|
|
glDrawElements(GL_TRIANGLES, command.element_count, GL_UNSIGNED_SHORT, NULL);
|
|
else
|
|
glDrawRangeElements(GL_TRIANGLES,
|
|
command.range_start,
|
|
command.range_end,
|
|
command.element_count,
|
|
GL_UNSIGNED_SHORT,
|
|
NULL);
|
|
} else {
|
|
SDL_assert(command.primitive_count != 0);
|
|
glDrawArrays(GL_TRIANGLES, 0, command.primitive_count);
|
|
}
|
|
|
|
/* state clearing */
|
|
|
|
if (command.textured)
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
if (command.colors.arity != 0)
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
|
|
if (command.texcoords.arity != 0)
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
|
|
void render_line(const LinePrimitive *line) {
|
|
finally_use_2d_pipeline();
|
|
glBegin(GL_LINES);
|
|
glColor4ub(line->color.r, line->color.g, line->color.b, line->color.a);
|
|
glVertex2f(line->start.x, line->start.y);
|
|
glColor4ub(line->color.r, line->color.g, line->color.b, line->color.a);
|
|
glVertex2f(line->finish.x, line->finish.y);
|
|
glEnd();
|
|
}
|