/* * 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 // NOTE: optional compile-time defines: // TANGERINE_GL_DEBUG_QUIET disable extra debug messages from openGL #include "asset.h" #include "camera.h" #include "entity.h" #include "dumbLog.h" #include "GLDebug.h" #include "input.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; }; struct RenderGroup { ShaderProgram* shader; u32 num_entities; u32 max_entities; Entity* entities; char* name; }; // TODO: node/tree structure for entities #if 0 struct Node; 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*, void*); // 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* user_data = 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);