A 3d fps game made in OpenGL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
Symmetry/src/libsymmetry/renderer.c

520 lines
19 KiB

#include "renderer.h"
#include "gl_load.h"
#include "../common/log.h"
#include "camera.h"
#include "model.h"
#include "texture.h"
#include "framebuffer.h"
#include "../common/array.h"
#include "shader.h"
#include "../common/num_types.h"
#include "light.h"
#include "entity.h"
#include "transform.h"
#include "game.h"
#include "gui.h"
#include "../common/hashmap.h"
#include "geometry.h"
#include "material.h"
#include "editor.h"
#include "sprite.h"
#include "../common/variant.h"
#include "../common/common.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_UNIFORM_NAME_LEN 64
/* TODO: Move all this into a struct called renderer state */
static int def_fbo = -1;
static int def_albedo_tex = -1;
static int def_depth_tex = -1;
static int quad_geo = -1;
static int composition_shader = -1;
static int debug_shader = -1;
static int num_culled = 0, num_rendered = 0, num_indices = 0;
static int num_culled_slot = -1, num_rendered_slot = -1, num_indices_slot = -1;
static struct Sprite_Batch* sprite_batch = NULL;
void on_framebuffer_size_change(int width, int height);
void renderer_init(void)
{
glClearColor(0.3f, 0.6f, 0.9f, 1.0f);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
platform->windowresize_callback_set(on_framebuffer_size_change);
gui_init();
/* Quad geometry for final render */
vec3* vertices = array_new(vec3);
vec2* uvs = array_new(vec2);
vec3* normals = array_new(vec3);
uint* indices = array_new(uint);
vec3 temp_v3;
vec2 temp_v2;
/* Vertices */
temp_v3.x = -1; temp_v3.y = -1; temp_v3.z = 0; array_push(vertices, temp_v3, vec3);
temp_v3.x = 1; temp_v3.y = -1; temp_v3.z = 0; array_push(vertices, temp_v3, vec3);
temp_v3.x = 1; temp_v3.y = 1; temp_v3.z = 0; array_push(vertices, temp_v3, vec3);
temp_v3.x = -1; temp_v3.y = 1; temp_v3.z = 0; array_push(vertices, temp_v3, vec3);
/* Normals */
temp_v3.x = 0; temp_v3.y = 1; temp_v3.z = 0; array_push(normals, temp_v3, vec3);
temp_v3.x = 0; temp_v3.y = 1; temp_v3.z = 0; array_push(normals, temp_v3, vec3);
/* Uvs */
temp_v2.x = 0; temp_v2.y = 0; array_push(uvs, temp_v2, vec2);
temp_v2.x = 1; temp_v2.y = 0; array_push(uvs, temp_v2, vec2);
temp_v2.x = 1; temp_v2.y = 1; array_push(uvs, temp_v2, vec2);
temp_v2.x = 0; temp_v2.y = 1; array_push(uvs, temp_v2, vec2);
/* Indices */
array_push(indices, 0, uint); array_push(indices, 1, uint); array_push(indices, 2, uint);
array_push(indices, 2, uint); array_push(indices, 3, uint); array_push(indices, 0, uint);
quad_geo = geom_create("Quad", vertices, uvs, normals, indices, NULL);
array_free(vertices);
array_free(uvs);
array_free(normals);
array_free(indices);
int width = -1, height = -1;
struct Game_State* game_state = game_state_get();
platform->window.get_size(game_state->window, &width, &height);
def_albedo_tex = texture_create("def_albedo_texture",
TU_DIFFUSE,
width, height,
GL_RGB,
GL_RGB16F,
GL_FLOAT,
NULL);
texture_set_param(def_albedo_tex, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
texture_set_param(def_albedo_tex, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
texture_set_param(def_albedo_tex, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
texture_set_param(def_albedo_tex, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
def_depth_tex = texture_create("def_depth_texture",
TU_SHADOWMAP4,
width, height,
GL_DEPTH_COMPONENT,
GL_DEPTH_COMPONENT32F,
GL_FLOAT,
NULL);
texture_set_param(def_depth_tex, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
texture_set_param(def_depth_tex, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
texture_set_param(def_depth_tex, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
texture_set_param(def_depth_tex, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
texture_set_param(def_depth_tex, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
texture_set_param(def_depth_tex, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
def_fbo = framebuffer_create(width, height, true, false, true);
framebuffer_texture_set(def_fbo, def_albedo_tex, FA_COLOR_ATTACHMENT0);
framebuffer_texture_set(def_fbo, def_depth_tex, FA_DEPTH_ATTACHMENT);
composition_shader = shader_create("fbo.vert", "fbo.frag");
debug_shader = shader_create("debug.vert", "debug.frag");
num_culled_slot = editor_debugvar_slot_create("Culled Geom", VT_INT);
num_rendered_slot = editor_debugvar_slot_create("Rendered Geom", VT_INT);
num_indices_slot = editor_debugvar_slot_create("Total Indices", VT_INT);
sprite_batch = malloc(sizeof(*sprite_batch));
if(!sprite_batch)
{
log_error("renderer:init", "Failed to allocated sprite batch");
}
else
{
sprite_batch_create(sprite_batch, "sprite_map.tga", "sprite.vert", "sprite.frag", GL_TRIANGLES);
}
}
void renderer_draw(struct Entity* active_viewer)
{
/* Render each camera output into it's framebuffer or to the default framebuffer */
struct Entity* entity_list = entity_get_all();
for(int i = 0; i < array_len(entity_list); i++)
{
struct Entity* viewer = &entity_list[i];
if(entity_list[i].type != ET_CAMERA) continue;
struct Camera* camera = &viewer->camera;
int fbo = camera->fbo == -1 ? def_fbo : camera->fbo;
framebuffer_bind(fbo);
{
glViewport(0, 0, framebuffer_width_get(fbo), framebuffer_height_get(fbo));
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearColor(camera->clear_color.x,
camera->clear_color.y,
camera->clear_color.z,
camera->clear_color.w);
glEnable(GL_CULL_FACE );
glCullFace(GL_BACK);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
static mat4 mvp;
struct Material* material_list = material_get_all_materials();
for(int i = 0; i < array_len(material_list); i++)
{
/* for each material, get all the registered models and render them */
struct Material* material = &material_list[i];
if(!material->active || array_len(material->registered_models) == 0)
continue;
shader_bind(material->shader);
renderer_check_glerror("model:render_all:shader_bind");
if(material->lit) /* Set light information */
{
int valid_light_count = 0;
int* light_index_list = light_get_valid_indices(&valid_light_count);
char uniform_name[MAX_UNIFORM_NAME_LEN];
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
for(int i = 0; i < valid_light_count; i++)
{
struct Entity* light_entity = entity_get(light_index_list[i]);
struct Light* light = &light_entity->light; /* TODO: Cull lights according to camera frustum */
vec3 light_pos = {0, 0, 0};
transform_get_absolute_pos(light_entity, &light_pos);
if(light->type != LT_POINT)
{
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].direction", i);
vec3 light_dir = {0.f, 0.f, 0.f};
transform_get_absolute_lookat(light_entity, &light_dir);
vec3_norm(&light_dir, &light_dir);
shader_set_uniform_vec3(material->shader, uniform_name, &light_dir);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
}
if(light->type != LT_DIR)
{
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].position", i);
shader_set_uniform_vec3(material->shader, uniform_name, &light_pos);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].outer_angle", i);
shader_set_uniform_float(material->shader, uniform_name, light->outer_angle);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].inner_angle", i);
shader_set_uniform_float(material->shader, uniform_name, light->inner_angle);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].falloff", i);
shader_set_uniform_float(material->shader, uniform_name, light->falloff);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].radius", i);
shader_set_uniform_int(material->shader, uniform_name, light->radius);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
}
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].color", i);
shader_set_uniform_vec3(material->shader, uniform_name, &light->color);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].intensity", i);
shader_set_uniform_float(material->shader, uniform_name, light->intensity);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
snprintf(uniform_name, MAX_UNIFORM_NAME_LEN, "lights[%d].type", i);
shader_set_uniform_int(material->shader, uniform_name, light->type);
memset(uniform_name, '\0', MAX_UNIFORM_NAME_LEN);
}
shader_set_uniform_int(material->shader, "total_active_lights", valid_light_count);
vec3 camera_pos = {0, 0, 0};
transform_get_absolute_pos(viewer, &camera_pos);
shader_set_uniform_vec3(material->shader, "camera_pos", &camera_pos);
}
/* Set material pipeline uniforms */
static struct Render_Settings render_settings;
renderer_settings_get(&render_settings);
for(int k = 0; k < array_len(material->pipeline_params); k++)
{
struct Uniform* uniform = &material->pipeline_params[k];
if(strcmp(uniform->name, "view_mat") == 0)
{
shader_set_uniform(uniform->type, uniform->location, &viewer->camera.view_mat);
renderer_check_glerror("model:render_all:material_pipeline");
}
else if(strcmp(uniform->name, "fog.mode") == 0)
{
shader_set_uniform(uniform->type, uniform->location, &render_settings.fog.mode);
renderer_check_glerror("model:render_all:material_pipeline");
}
else if(strcmp(uniform->name, "fog.density") == 0)
{
shader_set_uniform(uniform->type, uniform->location, &render_settings.fog.density);
renderer_check_glerror("model:render_all:material_pipeline");
}
else if(strcmp(uniform->name, "fog.start_dist") == 0)
{
shader_set_uniform(uniform->type, uniform->location, &render_settings.fog.start_dist);
renderer_check_glerror("model:render_all:material_pipeline");
}
else if(strcmp(uniform->name, "fog.max_dist") == 0)
{
shader_set_uniform(uniform->type, uniform->location, &render_settings.fog.max_dist);
renderer_check_glerror("model:render_all:material_pipeline");
}
else if(strcmp(uniform->name, "fog.color") == 0)
{
shader_set_uniform(uniform->type, uniform->location, &render_settings.fog.color);
renderer_check_glerror("model:render_all:material_pipeline");
}
else if(strcmp(uniform->name, "ambient_light") == 0)
{
shader_set_uniform(uniform->type, uniform->location, &render_settings.ambient_light);
renderer_check_glerror("model:render_all:material_pipeline");
}
}
for(int j = 0; j < array_len(material->registered_models); j++)
{
/* for each registered model, set up uniforms and render */
struct Entity* entity = entity_get(material->registered_models[j]);
struct Model* model = &entity->model;
struct Transform* transform = &entity->transform;
struct Geometry* geometry = geom_get(model->geometry_index);
/* Check if model is in frustum */
int intersection = bv_intersect_frustum_sphere(viewer->camera.frustum, &geometry->bounding_sphere, entity);
if(intersection == IT_OUTSIDE)
{
num_culled++;
continue;
}
else
{
num_indices += array_len(geometry->indices);
num_rendered++;
}
/* set material params for the model */
for(int k = 0; k < array_len(model->material_params); k++)
{
struct Material_Param* param = &model->material_params[k];
struct Uniform* uniform = &material->model_params[param->uniform_index];
shader_set_uniform(uniform->type, uniform->location, param->value);
renderer_check_glerror("model:render_all:material_param");
}
/* Set pipeline uniforms that are derived per model */
for(int k = 0; k < array_len(material->pipeline_params); k++)
{
/* TODO: change this into something better */
struct Uniform* uniform = &material->pipeline_params[k];
if(strcmp(uniform->name, "mvp") == 0)
{
mat4_identity(&mvp);
mat4_mul(&mvp, &viewer->camera.view_proj_mat, &transform->trans_mat);
shader_set_uniform(uniform->type, uniform->location, &mvp);
renderer_check_glerror("model:render_all:material_pipeline");
}
else if(strcmp(uniform->name, "model_mat") == 0)
{
shader_set_uniform(uniform->type, uniform->location, &transform->trans_mat);
renderer_check_glerror("model:render_all:material_pipeline");
}
else if(strcmp(uniform->name, "inv_model_mat") == 0)
{
mat4 inv_mat;
mat4_identity(&inv_mat);
mat4_inverse(&inv_mat, &transform->trans_mat);
shader_set_uniform(uniform->type, uniform->location, &inv_mat);
renderer_check_glerror("model:render_all:material_pipeline");
}
}
/* Render the geometry */
//int indices = geom_render_in_frustum(model->geometry_index, &viewer->camera.frustum[0], entity, draw_mode);
//geom_render(model->geometry_index, draw_mode);
geom_render(model->geometry_index, GDM_TRIANGLES);
for(int k = 0; k < array_len(model->material_params); k++)
{
/* unbind textures, if any */
struct Material_Param* param = &model->material_params[k];
struct Uniform* uniform = &material->model_params[param->uniform_index];
if(uniform->type == UT_TEX)
{
texture_unbind(*(int*)param->value);
renderer_check_glerror("model:render_all:unbind_texture_uniform");
}
}
}
shader_unbind();
}
editor_debugvar_slot_set_int(num_rendered_slot, num_rendered);
editor_debugvar_slot_set_int(num_culled_slot, num_culled);
editor_debugvar_slot_set_int(num_indices_slot, num_indices);
num_culled = num_rendered = num_indices = 0;
}
framebuffer_unbind();
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
}
/* Final Render */
int width, height;
struct Game_State* game_state = game_state_get();
platform->window.get_size(game_state->window, &width, &height);
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader_bind(composition_shader);
struct Camera* active_camera = &active_viewer->camera;
int final_render_tex = active_camera->render_tex == -1 ? def_albedo_tex : active_camera->render_tex;
texture_bind(final_render_tex);
geom_render(quad_geo, GDM_TRIANGLES);
texture_unbind(final_render_tex);
shader_unbind();
/* Debug Render */
struct Hashmap* cvars = platform->config.get();
if(hashmap_bool_get(cvars, "debug_draw_enabled"))
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
vec4 debug_draw_color = hashmap_vec4_get(cvars, "debug_draw_color");
shader_bind(debug_shader);
{
static mat4 mvp;
shader_set_uniform_vec4(debug_shader, "debug_color", &debug_draw_color);
struct Entity* entity_list = entity_get_all();
for(int i = 0; i < array_len(entity_list); i++)
{
if(!entity_list[i].renderable) continue;
struct Model* model = &entity_list[i].model;
struct Transform* transform = &entity_list[i].transform;
int geometry = model->geometry_index;
mat4_identity(&mvp);
mat4_mul(&mvp, &active_viewer->camera.view_proj_mat, &transform->trans_mat);
shader_set_uniform_mat4(debug_shader, "mvp", &mvp);
geom_render(geometry, hashmap_int_get(cvars, "debug_draw_mode"));
}
}
shader_unbind();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
/* Render 2D stuff */
shader_bind(sprite_batch->shader);
{
static mat4 ortho_mat;
mat4_identity(&ortho_mat);
int width, height;
struct Game_State* game_state = game_state_get();
platform->window.get_size(game_state->window, &width, &height);
mat4_ortho(&ortho_mat, 0, width, height, 0, -10.f, 10.f);
shader_set_uniform_mat4(sprite_batch->shader, "mvp", &ortho_mat);
sprite_batch_render(sprite_batch);
}
shader_unbind();
/* Render UI */
gui_render(NK_ANTI_ALIASING_ON);
}
void renderer_cleanup(void)
{
sprite_batch_remove(sprite_batch);
free(sprite_batch);
gui_cleanup();
geom_remove(quad_geo);
framebuffer_remove(def_fbo);
texture_remove(def_albedo_tex);
texture_remove(def_depth_tex);
}
void on_framebuffer_size_change(int width, int height)
{
struct Entity* entity_list = entity_get_all();
float aspect = (float)width / (float)height;
for(int i = 0; i < array_len(entity_list); i++)
{
struct Entity* viewer = &entity_list[i];
if(viewer->type != ET_CAMERA) continue;
viewer->camera.aspect_ratio = aspect > 0.f ? aspect : 4.f / 3.f;
camera_update_proj(viewer);
}
framebuffer_resize_all(width, height);
}
void renderer_clearcolor_set(float red, float green, float blue, float alpha)
{
glClearColor(red, green, blue, alpha);
}
int renderer_check_glerror(const char* context)
{
int error = 1;
GLenum error_code = glGetError();
const char* error_string = "No Error";
switch(error_code)
{
case GL_INVALID_OPERATION: error_string = "Invalid Operation"; break;
case GL_NO_ERROR: error_string = "No Error"; break;
case GL_INVALID_ENUM: error_string = "Invalid ENUM"; break;
case GL_INVALID_VALUE: error_string = "Invalid Value"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: error_string = "Invalid FrameBuffer Operation"; break;
case GL_OUT_OF_MEMORY: error_string = "Out of Memory"; break;
}
if(error_code != GL_NO_ERROR)
log_error(context, error_string);
else
error = 0;
return error;
}
struct Sprite_Batch * get_batch(void)
{
return sprite_batch;
}
void renderer_debug_draw_enabled(bool enabled)
{
struct Hashmap* cvars = platform->config.get();
hashmap_bool_set(cvars, "debug_draw_enabled", enabled);
}
void renderer_settings_get(struct Render_Settings* settings)
{
struct Hashmap* cvars = platform->config.get();
settings->fog.mode = hashmap_int_get(cvars, "fog_mode");
settings->fog.density = hashmap_float_get(cvars, "fog_density");
settings->fog.start_dist = hashmap_float_get(cvars, "fog_start_dist");
settings->fog.max_dist = hashmap_float_get(cvars, "fog_max_dist");
settings->fog.color = hashmap_vec3_get(cvars, "fog_color");
settings->debug_draw_enabled = hashmap_bool_get(cvars, "debug_draw_enabled");
settings->debug_draw_mode = hashmap_int_get(cvars, "debug_draw_mode");
settings->debug_draw_color = hashmap_vec4_get(cvars, "debug_draw_color");
settings->ambient_light = hashmap_vec3_get(cvars, "ambient_light");
}
void renderer_settings_set(const struct Render_Settings* settings)
{
struct Hashmap* cvars = platform->config.get();
hashmap_int_set(cvars, "fog_mode", settings->fog.mode);
hashmap_float_set(cvars, "fog_density", settings->fog.density);
hashmap_float_set(cvars, "fog_start_dist", settings->fog.start_dist);
hashmap_float_set(cvars, "fog_max_dist", settings->fog.max_dist);
hashmap_vec3_set(cvars, "fog_color", &settings->fog.color);
hashmap_bool_set(cvars, "debug_draw_enabled", settings->debug_draw_enabled);
hashmap_int_set(cvars, "debug_draw_mode", settings->debug_draw_mode);
hashmap_vec4_set(cvars, "debug_draw_color", &settings->debug_draw_color);
hashmap_vec3_set(cvars, "ambient_light", &settings->ambient_light);
}