A small OpenGL 3+ renderer and game engine
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.
 
 
 

289 lines
8.9 KiB

/*
* libTangerine, a small, modern openGL renderer specializing in flat shaded,
* solid color models using pallete textures. See README for build instructions
* and look in the examples folder for... examples
*/
/*
* === TODO: ===
* - try to improve the setup interface for examples
* see if we can remove some boilerplate with more helpers, or
* re-organizing
* - add orbit function to camera, see orbitPositionZ0 in example
* maybe implement with quaternion, so we can set arbitrary orbit axis
* - update camera interal state and rotations to use quaternions
* - add scene abstrastion for RenderState
* - add an example of dynamically switching shaders for an entity
* - fix debug load times (either by using cgltf, or hiding tinygltf.h)
* - RenderGroups and Entities need to come into and out of existence during
* gameplay. So, we need to extend MemoryArena to be a pool allocator
* instead of an allocate only linear allocator
* - resizable arrays for entities and asset system (^ see above)
* - add libTangerine namespace?
* - make a test case for overflowing default array sizes, rg->assets
* rg_info->groups, render_group->enitities, rg->textures
* - test for video memory leaking since we're not actually deleting GLBuffers
* anywhere
* - look for glm::mat4 in structs, and replace with pointers (easier debugging)
* - defaults include for various #defines
* - make separate shaders for per vertex, and per pixel/fragment lighting
* see red book chapter 7
* - add a bloom/blur render to texture shader for light sources?
* https://learnopengl.com/Advanced-Lighting/Bloom
*
* === TODONE: ===
* - don't allocate Asset structure on asset arena
* - store on RenderState as reference
* - store asset arena to asset structure
* - condense get_X_ByPath functions with assetLoad functions
* - move to asset interface
* - load diffuse texture automatically when loading a model
* - move entity structure and functions to new file?
* - load diffuse texture into OpenGL, and store GLTexture structure onto
* GLContext
* - need a cache algorithm starting in initEntity()
* - check for GLTexture by path hash
* - load texture into gl if not cached
* - pass result GLTexture to loadGLMesh()
* - update loadScene function with textured models, and test texture loading
* - work on cleaner interface for initEntity and loadScene...
* - add a LOGF macro for printf style logging
* - replace instances of printf
* - rename data structures to be in the new format, eg) UpperCaseStyleNames
* - rename instances of 'model_xform' that refer to a mesh node to something
* like node_xform. model_xform should be reserved for the entity node
* - move default shaders out of git-lfs
* - full lighting model
* - add buffer backed storage for light arrays in GLSL
* - remove hard-coded values in full_lighting.frag
* - test buffer backed lights, probably need to pad any scalars/vec3s
* - add point light type with tweakable attenuation parameters
* - test complex entities
* - need a separate GLBufferToAttribMapping for each mesh on an entity
* - maybe fixed now with MeshBufferType enum and getMeshData()
* - allow entities to have an empty diffuse texture (see initEntity())
* - use dynamic shader parsing to set 'sampler_id' for shaders with textures
* - add ambient light to LightsBuffer structure
* - remember to update offsets in initLights()
* - use rg_arena for store GLMeshes, see initGLMesh()
* - merge back into libTangerine
* - merge render_group_fix branch back into master
*/
#pragma once
#include <SDL2/SDL.h>
#include "asset.h"
#include "camera.h"
#include "entity.h"
#include "dumbLog.h"
#include "GLDebug.h"
#include "shader.h"
#include "types.h"
#include "util.h"
struct SDLHandles
{
SDL_Window* window;
SDL_GLContext sdl_gl_ctx;
SDL_DisplayMode display_mode;
u32 SDL_flags;
};
// TODO: node/tree structure for entities
struct Node;
struct RenderGroup
{
ShaderProgram* shader;
u32 num_entities;
u32 max_entities;
Entity* entities;
char* name;
};
#if 0
struct Scene
{
MemoryArena* rg_arena;
u32 num_render_groups;
u32 max_render_groups;
RenderGroup* render_groups;
u32 num_point_lights;
u32 max_point_lights;
PointLight* p_lights;
u32 num_d_lights;
u32 max_d_lights;
DirectionalLight* d_lights;
u32 num_spot_lights;
u32 max_spot_lights;
SpotLight* s_lights;
Node root_node;
Camera cam;
};
#endif
struct GLClearColor
{
GLfloat R;
GLfloat G;
GLfloat B;
GLfloat A;
};
// NOTE: structure to match the layout of the 'lights' uniform in a shader
// all the pointers are pointers into the address space of 'buffer'. the vec4
// pointers are required to start on 16 byte boundaries as per layout std140,
// so there may be some padding added between the 'header', and the start of
// the arrays
// NOTE: this is also very fragile. Since c/c++ doesn't support reflection, we
// need to manually update 'initLights()' if we ever update this structure.
// And remember to update the 'padding' if the number of 'header' attributes
// change
struct LightsBuffer
{
u32 buf_size;
u32* max_p_lights;
u32* active_p_lights;
u32* max_d_lights;
u32* active_d_lights;
vec4* ambient_color;
vec4* pl_positions;
vec4* pl_colors;
uvec4* pl_intensities;
vec4* dl_directions;
vec4* dl_colors;
uvec4* dl_intensities;
void* buffer;
};
struct RenderState
{
bool running;
GLClearColor clear_col;
SDLHandles handles;
GLContext* gl_ctx;
Assets assets;
uvec2 window_dims;
MemoryArena* rg_arena;
u32 num_render_groups;
u32 max_render_groups;
RenderGroup* render_groups;
// TODO: should we have a 'scene' abstraction here?
// could have render groups, lights, camera
// could match up with gltf scene graph where everyting is part of a node
LightsBuffer* lights_buf;
Camera* camera;
};
#define DEFAULT_WIDTH 1280
#define DEFAULT_HEIGHT 720
#define DEFAULT_SDL_FLAGS SDL_INIT_VIDEO
#define DEFAULT_MODEL_COUNT 256
#define DEFAULT_TEXTURE_COUNT 64
#define DEFAULT_SHADER_COUNT 64
#define DEFAULT_UBO_COUNT 32
#define DEFAULT_RENDER_GROUP_COUNT 256
#define DEFAULT_CLEAR_COLOR { 0.2, 0.2, 0.2, 1 }
#define DEFAULT_AMBIENT_COLOR { 0.1, 0.1, 0.1, 1 }
#define DEFAULT_MAX_LIGHTS 32 // NOTE: needs to match NUM_LIGHTS in shaders
RenderState* initRenderState(const char* window_title = "tangerine",
uvec2 window_dims = uvec2(DEFAULT_WIDTH, DEFAULT_HEIGHT),
u32 SDL_flags = DEFAULT_SDL_FLAGS,
GLClearColor clear_col = DEFAULT_CLEAR_COLOR,
vec4 ambient_color = DEFAULT_AMBIENT_COLOR,
u32 max_models = DEFAULT_MODEL_COUNT,
u32 max_textures = DEFAULT_TEXTURE_COUNT,
u32 max_shaders = DEFAULT_SHADER_COUNT,
u32 max_render_groups = DEFAULT_RENDER_GROUP_COUNT,
u32 max_ubos = DEFAULT_UBO_COUNT,
u32 max_lights = DEFAULT_MAX_LIGHTS);
void freeRenderState(RenderState*& rs);
#define DEFAULT_ENTITY_COUNT 256
void initRenderGroup(RenderGroup* rg,
MemoryArena* arena,
ShaderProgram* shader,
u32 num_entities = DEFAULT_ENTITY_COUNT,
const char* name = "");
void freeRenderGroup(RenderGroup* rg, MemoryArena* arena);
RenderGroup* getFreeRenderGroup(RenderState* rs);
RenderGroup* getRenderGroupByName(RenderState* rs, const char* name);
Entity* getFreeEntity(RenderGroup* rg);
Entity* getEntityByName(RenderGroup* rg, const char* name);
// NOTE: callback function signature to use with renDoRenderLoop()
typedef void (*frame_callback_fn) (RenderState*);
// NOTE: There are 2 callbacks to use here, cb_func_pre is called before
// the call to renRenderFrame(), cb_fun_post is called after
// NOTE: if you use cb_func_pre, you will have to use SDL_PollEvent() manually
// and at minimum set rs->running = false on SDL_QUIT event
void doRenderLoop(RenderState* rs,
uint framerate = 60,
frame_callback_fn cb_func_pre = nullptr,
frame_callback_fn cb_func_post = nullptr);
void renderFrame(RenderState* rs, const GLClearColor& clear_col);
// NOTE: automatic loading of default shaders
struct ShaderInit
{
const char* name;
const char* vert_path;
const char* frag_path;
};
#define SHADER_INIT_COUNT 4
#define TEXTURE_ONLY_SHADER_INIT { "texture_only", \
"../data/texture_only.vert", \
"../data/texture_only.frag" }
#define FULL_LIGHTING_SHADER_INIT { "full_lighting", \
"../data/full_lighting.vert", \
"../data/full_lighting.frag" }
#define DEBUG_SHADER_INIT { "debug", \
"../data/debug.vert", \
"../data/debug.frag", }
#define COLORED_VERT_SHADER_INIT { "colored_vertices", \
"../data/colored_vertices.vert", \
"../data/colored_vertices.frag", }
const ShaderInit SHADER_INIT_LIST[SHADER_INIT_COUNT]
{
TEXTURE_ONLY_SHADER_INIT,
FULL_LIGHTING_SHADER_INIT,
DEBUG_SHADER_INIT,
COLORED_VERT_SHADER_INIT
};
bool loadDefaultShaders(RenderState* rs,
u32 num_shaders = SHADER_INIT_COUNT,
const ShaderInit shaders[] = SHADER_INIT_LIST);
// NOTE: only useful if the shader attribute names match the names in the
// default shaders. If loading a custom shader, this may not work as expected,
// and you should use getVertexAttribByName instead
GLVertexAttrib* getVertexAttribByType(ShaderProgram* shader,
MeshBufferType buf_type);