Small program to quickly test OpenGL GLSL shaders.
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.
 
 
 

386 lines
9.5 KiB

#include <cassert>
#include "tangerine.h"
#define UTIL_IMPLEMENTATION
#include "util.h"
// forward declarations
bool initGraphics(SDLHandles* handles);
LightsBuffer* initLights(MemoryArena* arena, GLContext* gl_ctx, u32 max_lights,
glm::vec4 ambient_color);
// interface
RenderState*
initRenderState(GLClearColor clear_col,
glm::vec4 ambient_color,
u32 max_models,
u32 max_textures,
u32 max_shaders,
u32 max_render_groups,
u32 max_ubos,
u32 max_lights)
{
LOGF(Info, "Initializing Renderer\n");
RenderState* rs = UTIL_ALLOC(1, RenderState);
if (rs) {
rs->clear_col = clear_col;
rs->assets.arena = arenaInit(DEFAULT_ARENA_SIZE);
rs->assets.max_models = max_models;
rs->assets.models = ARENA_ALLOC(rs->assets.arena, Model, max_models);
rs->assets.max_textures = max_textures;
rs->assets.textures =
ARENA_ALLOC(rs->assets.arena, Texture, max_textures);
rs->rg_arena = arenaInit(DEFAULT_ARENA_SIZE);
rs->max_render_groups = DEFAULT_RENDER_GROUP_COUNT;
rs->render_groups = ARENA_ALLOC(rs->rg_arena, RenderGroup,
DEFAULT_RENDER_GROUP_COUNT);
if (!initGraphics(&rs->handles)) {
LOGF(Error, "error initializing renderer\n");
return nullptr;
}
rs->gl_ctx = initGLContext(rs->assets.arena,
max_shaders,
max_textures,
max_ubos);
rs->xforms = ARENA_ALLOC(rs->assets.arena, Transforms, 1);
GLBuffer* xforms_ubo = getFreeUBO(rs->gl_ctx);
assert(xforms_ubo);
initTransforms(rs->assets.arena, rs->xforms, xforms_ubo, rs->gl_ctx);
rs->lights_buf = initLights(rs->rg_arena, rs->gl_ctx, max_lights,
ambient_color);
bool ret = loadDefaultShaders(rs);
assert(ret);
}
return rs;
}
void
freeRenderState(RenderState*& rs)
{
if (rs) {
SDL_GL_DeleteContext(rs->handles.sdl_gl_ctx);
SDL_DestroyWindow(rs->handles.window);
arenaFree(rs->assets.arena);
arenaFree(rs->rg_arena);
utilSafeFree(rs);
rs = nullptr;
}
SDL_Quit();
}
void
initRenderGroup(RenderGroup* rg,
MemoryArena* arena,
ShaderProgram* shader,
u32 num_entities,
const char* name)
{
rg->max_entities = num_entities;
rg->shader = shader;
rg->name = arenaCopyCStr(arena, name);
rg->entities = ARENA_ALLOC(arena, Entity, num_entities);
}
void
freeRenderGroup(RenderGroup* rg, MemoryArena* arena)
{
LOGF(Info, "should probably look into freeing arena memory?\n");
assert(0);
}
RenderGroup*
getFreeRenderGroup(RenderState* rs)
{
if (rs->num_render_groups < rs->max_render_groups)
return &rs->render_groups[rs->num_render_groups++];
LOGF(Error, "no free render group\n");
return nullptr;
}
RenderGroup*
getRenderGroupByName(RenderState* rs, const char* name)
{
RenderGroup* rg_out = nullptr;
for (u32 i = 0; i < rs->num_render_groups; i++) {
RenderGroup* rg = &rs->render_groups[i];
if (utilCStrMatch(name, rg->name))
rg_out = rg;
}
if (rg_out == nullptr)
LOGF(Error, "render group with name, \"%s\", not found\n", name);
return rg_out;
}
Entity*
getFreeEntity(RenderGroup* rg)
{
if (rg->num_entities < rg->max_entities)
return &rg->entities[rg->num_entities++];
LOGF(Error, "render group full\n");
return nullptr;
}
void
doRenderLoop(RenderState* rs,
u32 framerate,
frame_callback_fn cb_func_pre,
frame_callback_fn cb_func_post)
{
u32 delay = (framerate > 0) ? 1 / framerate : 0;
u32 frameStart, frameTime;
rs->running = true;
SDL_Event e;
while (rs->running) {
frameStart = SDL_GetTicks();
if (cb_func_pre != nullptr) {
cb_func_pre(rs);
} else {
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT ||
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE))
{
rs->running = false;
break;
}
}
}
renderFrame(rs, rs->clear_col);
if (cb_func_post != nullptr)
cb_func_post(rs);
SDL_GL_SwapWindow(rs->handles.window);
frameTime = SDL_GetTicks() - frameStart;
if (delay > frameTime)
SDL_Delay(delay - frameTime);
}
}
void
renderFrame(RenderState* rs, const GLClearColor& clear_col)
{
glClearColor(clear_col.R,
clear_col.G,
clear_col.B,
clear_col.A);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
for (u32 i = 0; i < rs->num_render_groups; i++) {
RenderGroup* rg = &rs->render_groups[i];
for (u32 j = 0; j < rg->num_entities; j++) {
Entity* e = &rg->entities[j];
for (u32 k = 0; k < e->num_meshes; k++) {
GLMesh& glm = e->meshes[k];
renderVAO(&glm, e->model_xform, rg->shader,
e->diffuse_texture);
}
}
}
}
bool
loadDefaultShaders(RenderState* rs,
u32 num_shaders,
const ShaderInit shaders[])
{
for (u32 i = 0; i < num_shaders; i++) {
const ShaderInit& si = shaders[i];
if (!addShaderProgram(rs->assets.arena,
rs->gl_ctx,
si.vert_path,
si.frag_path,
si.name))
{
LOG(Error) << "failed to load shader " << si.name << "\n";
return false;
}
ShaderProgram* shader = getShaderByName(si.name, rs->gl_ctx);
assert(shader);
// NOTE: not every buffer will be available for every shader, so we
// enumerate them all, and store the ones that are present
u32 attrib_idx = 0;
for (u32 i = 0; i < MESH_BUFFER_TYPE_COUNT; i++) {
MeshBufferType buf_type = (MeshBufferType) i;
GLVertexAttrib* attrib = getVertexAttribByType(shader, buf_type);
if (attrib)
shader->attrib_mappings[attrib_idx++] = { attrib, buf_type };
}
}
return true;
}
GLVertexAttrib*
getVertexAttribByType(ShaderProgram* shader, MeshBufferType buf_type)
{
switch (buf_type) {
case VERTEX: return getVertexAttribByName(shader, "position");
case NORMAL: return getVertexAttribByName(shader, "normal");
case UV: return getVertexAttribByName(shader, "uv");
case COLOR: return getVertexAttribByName(shader, "color");
default: return nullptr;
}
}
// internal
bool
initGraphics(SDLHandles* handles)
{
handles->window = SDL_CreateWindow(
"shader_testing",
SDL_WINDOWPOS_CENTERED_DISPLAY(0),
SDL_WINDOWPOS_CENTERED_DISPLAY(0),
1280,
720,
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cout << "error, sdl init: " << SDL_GetError() << "\n";
return false;
}
SDL_GL_SetSwapInterval(1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,
SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GetCurrentDisplayMode(0, &handles->display_mode);
handles->sdl_gl_ctx = SDL_GL_CreateContext(handles->window);
if (!handles->sdl_gl_ctx) {
std::cout << "error creating context\n";
return false;
}
if (glewInit()) {
std::cout << "error initializing opengl\n";
return false;
}
std::cout << "opengl vendor: " << glGetString(GL_VENDOR) << "\n";
std::cout << "opengl renderer: " << glGetString(GL_RENDERER) << "\n";
std::cout << "opengl version: " << glGetString(GL_VERSION) << "\n";
glEnable(GL_DEPTH_TEST);
glEnable(GL_LINE_SMOOTH);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable (GL_DEBUG_OUTPUT);
glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0);
return handles->window != nullptr;
}
LightsBuffer*
initLights(MemoryArena* arena,
GLContext* gl_ctx,
u32 max_lights,
glm::vec4 ambient_color)
{
// FIXME: revisit for 'Scene' abstraction
LightsBuffer* lb = ARENA_ALLOC(arena, LightsBuffer, 1);
lb->buf_size = 8 * sizeof(u32) // NOTE: 'header'
+ sizeof(glm::vec4) // NOTE: ambient color
+ 6 * max_lights * sizeof(glm::vec4); // NOTE: vector arrays
LOGF(Debug, "buf_size: %d\n", lb->buf_size);
lb->buffer = ARENA_ALLOC(arena, u8, lb->buf_size);
lb->max_p_lights = (u32*) lb->buffer;
lb->active_p_lights =
(u32*) arenaGetAddressOffset(lb->max_p_lights, sizeof(u32));
lb->max_d_lights =
(u32*) arenaGetAddressOffset(lb->active_p_lights, sizeof(u32));
lb->active_d_lights =
(u32*) arenaGetAddressOffset(lb->max_d_lights, sizeof(u32));
*lb->max_p_lights = max_lights;
*lb->max_d_lights = max_lights;
// NOTE: add padding, we're not actually using this since 4 * u32 is on a
// 16 byte boundary, but will be helpful if we need to add more 'headers'
// in the future
void* arr_start = arenaGetAddressOffset(lb->buffer, 8 * sizeof(u32));
// NOTE: ambient color
lb->ambient_color = (glm::vec4*) arr_start;
*lb->ambient_color = ambient_color;
// NOTE: set offsets for array pointers
u32 arr_size = max_lights * sizeof(glm::vec4);
lb->pl_positions = //(glm::vec4*) arr_start;
(glm::vec4*) arenaGetAddressOffset(arr_start, sizeof(glm::vec4));
lb->pl_colors =
(glm::vec4*) arenaGetAddressOffset(lb->pl_positions, arr_size);
lb->pl_intensities =
(glm::uvec4*) arenaGetAddressOffset(lb->pl_colors, arr_size);
lb->dl_directions =
(glm::vec4*) arenaGetAddressOffset(lb->pl_intensities, arr_size);
lb->dl_colors =
(glm::vec4*) arenaGetAddressOffset(lb->dl_directions, arr_size);
lb->dl_intensities =
(glm::uvec4*) arenaGetAddressOffset(lb->dl_colors, arr_size);
GLBuffer* lights_ubo = getFreeUBO(gl_ctx);
assert(lights_ubo);
lights_ubo->target = GL_UNIFORM_BUFFER;
lights_ubo->data_type = GL_BYTE; // NOTE: mixed types in structure
lights_ubo->data_size = lb->buf_size;
lights_ubo->name = arenaCopyCStr(arena, "lights");
assert((GLint) gl_ctx->binding_count < gl_ctx->max_binding_points);
lights_ubo->binding_idx = gl_ctx->binding_count++;
glGenBuffers(1, &lights_ubo->id);
glBindBuffer(lights_ubo->target, lights_ubo->id);
glBufferData(lights_ubo->target,
lb->buf_size,
lb->buffer,
GL_DYNAMIC_DRAW);
glBindBufferBase(lights_ubo->target,
lights_ubo->binding_idx,
lights_ubo->id);
return lb;
}