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.
 
 
 

310 lines
7.2 KiB

#include <cassert>
#include "tangerine.h"
#define UTIL_IMPLEMENTATION
#include "util.h"
// forward declarations
bool initGraphics(SDLHandles* handles);
GLContext* initGLContext(MemoryArena* arena,
u32 max_shaders,
u32 max_textures,
u32 max_ubos);
// interface
RenderState*
initRenderState(u32 max_models,
u32 max_textures,
u32 max_shaders,
u32 max_render_groups,
u32 max_ubos)
{
LOGF(Info, "Initializing Renderer\n");
RenderState* rs = UTIL_ALLOC(1, RenderState);
if (rs) {
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);
// FIXME: revisit for 'Scene' abstraction
rs->max_lights = 32;
rs->lights = ARENA_ALLOC(rs->rg_arena, PointLight, rs->max_lights);
rs->gl_ctx = initGLContext(rs->assets.arena,
max_shaders,
max_textures,
max_ubos);
rs->xforms = ARENA_ALLOC(rs->assets.arena, Transforms, 1);
rs->handles = UTIL_ALLOC(1, SDLHandles);
initGraphics(rs->handles);
// FIXME: need to test this with another shader that has another UBO
GLBuffer& ubo = rs->gl_ctx->uniform_buffers[rs->gl_ctx->num_ubos++];
initTransforms(rs->assets.arena, rs->xforms, &ubo, rs->gl_ctx);
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->handles);
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;
}
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->id);
}
}
}
}
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;
}
GLContext*
initGLContext(MemoryArena* arena,
u32 max_shaders,
u32 max_textures,
u32 max_ubos)
{
GLContext* gl_ctx = ARENA_ALLOC(arena, GLContext, 1);
gl_ctx->max_shaders = max_shaders;
gl_ctx->shaders = ARENA_ALLOC(arena, ShaderProgram, max_shaders);
gl_ctx->max_ubos = max_ubos;
gl_ctx->uniform_buffers = ARENA_ALLOC(arena, GLBuffer, max_ubos);
gl_ctx->max_textures = max_textures;
gl_ctx->textures = ARENA_ALLOC(arena, GLTexture, max_textures);
return gl_ctx;
}