diff --git a/data/blackboard.art b/data/blackboard.art index 4d65b22..61c8d0c 100644 Binary files a/data/blackboard.art and b/data/blackboard.art differ diff --git a/src/Asteroid.c b/src/Asteroid.c index 365cb50..20c9641 100644 --- a/src/Asteroid.c +++ b/src/Asteroid.c @@ -61,26 +61,41 @@ void initRenderer(GameState *state, v2 windowSize) { GL_CHECK_ERROR(); /* Create buffers */ - glGenVertexArrays(1, &renderer->vao); - glGenBuffers(1, &renderer->vbo); + glGenVertexArrays(ARRAY_COUNT(renderer->vao), renderer->vao); + glGenBuffers(ARRAY_COUNT(renderer->vbo), renderer->vbo); GL_CHECK_ERROR(); - /* Bind buffers */ - glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo); - glBindVertexArray(renderer->vao); + { + // Bind buffers and configure vao, vao automatically intercepts + // glBindCalls and associates the state with that buffer for us + glBindVertexArray(renderer->vao[rendermode_quad]); + glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[rendermode_quad]); - /* Configure VAO */ - u32 numVertexElements = 4; - u32 stride = sizeof(Vertex); - glVertexAttribPointer(0, numVertexElements, GL_FLOAT, GL_FALSE, stride, - (GLvoid *)0); - glEnableVertexAttribArray(0); + glEnableVertexAttribArray(0); + u32 numVertexElements = 4; + u32 stride = sizeof(Vertex); - GL_CHECK_ERROR(); + glVertexAttribPointer(0, numVertexElements, GL_FLOAT, + GL_FALSE, stride, (GLvoid *)0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + + { + glBindVertexArray(renderer->vao[rendermode_triangle]); + glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[rendermode_triangle]); + + glEnableVertexAttribArray(0); + u32 numVertexElements = 3; + u32 stride = sizeof(Vertex); + + glVertexAttribPointer(0, numVertexElements, GL_FLOAT, + GL_FALSE, stride, (GLvoid *)0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } /* Unbind */ - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); GL_CHECK_ERROR(); // TODO(doyle): Lazy allocate render group capacity @@ -167,6 +182,8 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, initAssetManager(state); initRenderer(state, windowSize); + state->pixelsPerMeter = 70.0f; + { // Init ship entity Entity *ship = &state->entityList[state->entityIndex++]; ship->id = 0; @@ -183,6 +200,7 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, state->camera.pos = V2(0, 0); state->camera.size = state->renderer.size; + state->init = TRUE; } { @@ -217,25 +235,75 @@ void asteroid_gameUpdateAndRender(GameState *state, Memory *memory, if (entity->type == entitytype_ship) { - { // Parse input - if (getKeyStatus(&state->input.keys[keycode_up], - readkeytype_repeat, 0.0f, dt)) - { - entity->pos.y += 10.0f; - } - if (getKeyStatus(&state->input.keys[keycode_down], - readkeytype_repeat, 0.0f, dt)) - { - entity->pos.y -= 10.0f; - } + v2 acceleration = {0}; + if (getKeyStatus(&state->input.keys[keycode_up], readkeytype_repeat, + 0.0f, dt)) + { + acceleration.y = 1.0f; } + if (getKeyStatus(&state->input.keys[keycode_down], + readkeytype_repeat, 0.0f, dt)) + { + acceleration.y = -1.0f; + } + + if (getKeyStatus(&state->input.keys[keycode_left], + readkeytype_repeat, 0.0f, dt)) + { + acceleration.x = -1.0f; + } + + if (getKeyStatus(&state->input.keys[keycode_right], + readkeytype_repeat, 0.0f, dt)) + { + acceleration.x = 1.0f; + } + + if (acceleration.x != 0.0f && acceleration.y != 0.0f) + { + // NOTE(doyle): Cheese it and pre-compute the vector for + // diagonal using pythagoras theorem on a unit triangle 1^2 + // + 1^2 = c^2 + acceleration = v2_scale(acceleration, 0.70710678118f); + } + + /* + Assuming acceleration A over t time, then integrate twice to get + + newVelocity = a*t + oldVelocity + newPos = (a*t^2)/2 + oldVelocity*t + oldPos + */ + + acceleration = v2_scale(acceleration, state->pixelsPerMeter * 25); + + v2 oldVelocity = entity->velocity; + v2 resistance = v2_scale(oldVelocity, 4.0f); + acceleration = v2_sub(acceleration, resistance); + + entity->velocity = v2_add(v2_scale(acceleration, dt), oldVelocity); + + v2 halfAcceleration = v2_scale(acceleration, 0.5f); + v2 halfAccelerationDtSquared = + v2_scale(halfAcceleration, (SQUARED(dt))); + v2 oldVelocityDt = v2_scale(oldVelocity, dt); + v2 oldPos = entity->pos; + entity->pos = v2_add( + v2_add(halfAccelerationDtSquared, oldVelocityDt), oldPos); } renderer_entity(&state->renderer, state->camera, entity, V2(0, 0), 0, - V4(1.0f, 1.0f, 1.0f, 1.0f)); + V4(0.0f, 1.0f, 1.0f, 1.0f)); } + RenderTex nullRenderTex = renderer_createNullRenderTex(&state->assetManager); + TrianglePoints triangle = {0}; + triangle.points[0] = V2(100, 200); + triangle.points[2] = V2(100, 100); + triangle.points[1] = V2(200, 100); + renderer_triangle(&state->renderer, state->camera, triangle, V2(0, 0), 0, + nullRenderTex, V4(1, 1, 1, 1)); + renderer_renderGroups(&state->renderer); } diff --git a/src/Renderer.c b/src/Renderer.c index df39a4e..bf81cb1 100644 --- a/src/Renderer.c +++ b/src/Renderer.c @@ -10,8 +10,19 @@ #define RENDER_BOUNDING_BOX FALSE +typedef struct RenderQuad +{ + Vertex vertex[4]; +} RenderQuad_; + +typedef struct RenderTriangle +{ + Vertex vertex[3]; +} RenderTriangle_; + INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, - Vertex *vertexList, i32 numVertexes) + Vertex *vertexList, i32 numVertexes, + enum RenderMode targetRenderMode) { #ifdef DENGINE_DEBUG @@ -32,7 +43,7 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, /* If the textures match and have the same color modulation, we can * add these vertices to the current group */ if (group->tex->id == tex->id && - v4_equals(group->color, color)) + v4_equals(group->color, color) && group->mode == targetRenderMode) { groupIsValid = TRUE; } @@ -46,6 +57,7 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, group->vertexIndex++; group->tex = tex; group->color = color; + group->mode = targetRenderMode; #ifdef DENGINE_DEBUG debug_countIncrement(debugcount_renderGroups); @@ -90,37 +102,6 @@ INTERNAL void addVertexToRenderGroup(Renderer *renderer, Texture *tex, v4 color, } } -INTERNAL inline void addRenderQuadToRenderGroup(Renderer *renderer, - RenderQuad_ quad, - Texture *tex, v4 color) -{ - /* - NOTE(doyle): Entity rendering is always done in two pairs of - triangles, i.e. quad. To batch render quads as a triangle strip, we - need to create zero-area triangles which OGL will omit from - rendering. Render groups are initialised with 1 degenerate vertex and - then the first two vertexes sent to the render group are the same to - form 1 zero-area triangle strip. - - A degenerate vertex has to be copied from the last vertex in the - rendering quad, to repeat this process as more entities are - renderered. - - Alternative implementation is recognising if the rendered - entity is the first in its render group, then we don't need to init - a degenerate vertex, and only at the end of its vertex list. But on - subsequent renders, we need a degenerate vertex at the front to - create the zero-area triangle strip. - - The first has been chosen for simplicity of code, at the cost of - 1 degenerate vertex at the start of each render group. - */ - Vertex vertexList[6] = {quad.vertex[0], quad.vertex[0], quad.vertex[1], - quad.vertex[2], quad.vertex[3], quad.vertex[3]}; - addVertexToRenderGroup(renderer, tex, color, vertexList, - ARRAY_COUNT(vertexList)); -}; - INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY) { if (flipX) @@ -138,24 +119,19 @@ INTERNAL inline void flipTexCoord(v4 *texCoords, b32 flipX, b32 flipY) } } -INTERNAL void updateBufferObject(Renderer *const renderer, - const Vertex *const vertexList, - const i32 numVertex) +INTERNAL void bufferRenderGroupToGL(Renderer *renderer, RenderGroup *group) { + Vertex *vertexList = group->vertexList; + i32 numVertex = group->vertexIndex; + // TODO(doyle): We assume that vbo and vao are assigned renderer->numVertexesInVbo = numVertex; - - glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo); + glBindBuffer(GL_ARRAY_BUFFER, renderer->vbo[group->mode]); glBufferData(GL_ARRAY_BUFFER, numVertex * sizeof(Vertex), vertexList, GL_STREAM_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } -INTERNAL void bufferRenderGroupToGL(Renderer *renderer, RenderGroup *group) -{ - updateBufferObject(renderer, group->vertexList, group->vertexIndex); -} - INTERNAL RenderQuad_ createRenderQuad(Renderer *renderer, v2 pos, v2 size, v2 pivotPoint, f32 rotate, RenderTex renderTex) @@ -237,6 +213,39 @@ INTERNAL RenderQuad_ createRenderQuad(Renderer *renderer, v2 pos, v2 size, return result; } +INTERNAL RenderTriangle_ createRenderTriangle(Renderer *renderer, + TrianglePoints triangle, + v2 pivotPoint, f32 rotate, + RenderTex renderTex) +{ + /* Convert texture coordinates to normalised texture coordinates */ + v4 texRectNdc = renderTex.texRect; + if (renderTex.tex) + { + v2 texNdcFactor = + V2(1.0f / renderTex.tex->width, 1.0f / renderTex.tex->height); + texRectNdc.e[0] *= texNdcFactor.w; + texRectNdc.e[1] *= texNdcFactor.h; + texRectNdc.e[2] *= texNdcFactor.w; + texRectNdc.e[3] *= texNdcFactor.h; + } + + RenderTriangle_ result = {0}; + + result.vertex[0].pos = triangle.points[0]; + result.vertex[0].texCoord = V2(texRectNdc.x, texRectNdc.w); + + result.vertex[1].pos = triangle.points[1]; + result.vertex[1].texCoord = V2(texRectNdc.x, texRectNdc.y); + + result.vertex[2].pos = triangle.points[2]; + result.vertex[2].texCoord = V2(texRectNdc.z, texRectNdc.w); + + if (rotate == 0) return result; + + return result; +} + INTERNAL inline RenderQuad_ createDefaultTexQuad(Renderer *renderer, RenderTex renderTex) { @@ -248,6 +257,7 @@ createDefaultTexQuad(Renderer *renderer, RenderTex renderTex) INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup) { + ASSERT(renderGroup->mode < rendermode_invalid); /* Load transformation matrix */ shader_use(renderer->shader); GL_CHECK_ERROR(); @@ -255,10 +265,11 @@ INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup) /* Set color modulation value */ shader_uniformSetVec4f(renderer->shader, "spriteColor", renderGroup->color); + GL_CHECK_ERROR(); /* Send draw calls */ #if RENDER_BOUNDING_BOX - glBindVertexArray(renderer->vao); + glBindVertexArray(renderer->vao[renderGroup->mode]); glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo); glBindVertexArray(0); #endif @@ -271,8 +282,9 @@ INTERNAL void renderGLBufferedData(Renderer *renderer, RenderGroup *renderGroup) shader_uniformSet1i(renderer->shader, "tex", 0); } - glBindVertexArray(renderer->vao); + glBindVertexArray(renderer->vao[renderGroup->mode]); glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo); + GL_CHECK_ERROR(); #ifdef DENGINE_DEBUG debug_countIncrement(debugcount_drawArrays); @@ -298,7 +310,56 @@ void renderer_rect(Renderer *const renderer, Rect camera, v2 pos, v2 size, v2 posInCameraSpace = v2_sub(pos, camera.pos); RenderQuad_ quad = createRenderQuad(renderer, posInCameraSpace, size, pivotPoint, rotate, renderTex); - addRenderQuadToRenderGroup(renderer, quad, renderTex.tex, color); + + { // addRenderQuadToRenderGroup + /* + NOTE(doyle): Entity rendering is always done in two pairs of + triangles, i.e. quad. To batch render quads as a triangle strip, we + need to create zero-area triangles which OGL will omit from + rendering. Render groups are initialised with 1 degenerate vertex and + then the first two vertexes sent to the render group are the same to + form 1 zero-area triangle strip. + + A degenerate vertex has to be copied from the last vertex in the + rendering quad, to repeat this process as more entities are + renderered. + + Alternative implementation is recognising if the rendered + entity is the first in its render group, then we don't need to init + a degenerate vertex, and only at the end of its vertex list. But on + subsequent renders, we need a degenerate vertex at the front to + create the zero-area triangle strip. + + The first has been chosen for simplicity of code, at the cost of + 1 degenerate vertex at the start of each render group. + */ + Vertex vertexList[6] = {quad.vertex[0], quad.vertex[0], quad.vertex[1], + quad.vertex[2], quad.vertex[3], quad.vertex[3]}; + addVertexToRenderGroup(renderer, renderTex.tex, color, vertexList, + ARRAY_COUNT(vertexList), rendermode_quad); + } + +} + +void renderer_triangle(Renderer *const renderer, Rect camera, + TrianglePoints triangle, v2 pivotPoint, f32 rotate, + RenderTex renderTex, v4 color) +{ + TrianglePoints triangleInCamSpace = {0}; + ASSERT(ARRAY_COUNT(triangle.points) == + ARRAY_COUNT(triangleInCamSpace.points)); + + for (i32 i = 0; i < ARRAY_COUNT(triangleInCamSpace.points); i++) + { + triangleInCamSpace.points[i] = v2_sub(triangle.points[i], camera.pos); + } + + RenderTriangle_ renderTriangle = createRenderTriangle( + renderer, triangleInCamSpace, pivotPoint, rotate, renderTex); + + addVertexToRenderGroup( + renderer, renderTex.tex, color, renderTriangle.vertex, + ARRAY_COUNT(renderTriangle.vertex), rendermode_triangle); } void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera, @@ -361,7 +422,7 @@ void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera, } addVertexToRenderGroup(renderer, tex, color, vertexList, - numVertexesToAlloc); + numVertexesToAlloc, rendermode_quad); // TODO(doyle): Mem free // PLATFORM_MEM_FREE(arena, vertexList, // sizeof(Vertex) * numVertexesToAlloc); diff --git a/src/dengine.c b/src/dengine.c index 8a10878..15bfc66 100644 --- a/src/dengine.c +++ b/src/dengine.c @@ -233,7 +233,7 @@ i32 main(void) char textBuffer[256]; snprintf(textBuffer, ARRAY_COUNT(textBuffer), "Dengine | %f ms/f | %f fps | Entity Count: %d", - msPerFrame, framesPerSecond, 0); + msPerFrame, framesPerSecond, gameState.entityIndex); glfwSetWindowTitle(window, textBuffer); titleUpdateFrequencyInSeconds = 0.5f; diff --git a/src/include/Dengine/Asteroid.h b/src/include/Dengine/Asteroid.h index 54a9523..3a9b6b4 100644 --- a/src/include/Dengine/Asteroid.h +++ b/src/include/Dengine/Asteroid.h @@ -13,6 +13,7 @@ typedef struct GameState { Entity entityList[1024]; i32 entityIndex; + f32 pixelsPerMeter; Rect camera; diff --git a/src/include/Dengine/Entity.h b/src/include/Dengine/Entity.h index c90f30b..8667824 100644 --- a/src/include/Dengine/Entity.h +++ b/src/include/Dengine/Entity.h @@ -39,8 +39,8 @@ typedef struct Entity i32 childIds[8]; i32 numChilds; - v2 pos; // Position - v2 dPos; // Velocity + v2 pos; + v2 velocity; v2 hitbox; v2 size; diff --git a/src/include/Dengine/Renderer.h b/src/include/Dengine/Renderer.h index fa6f2df..9dac7e3 100644 --- a/src/include/Dengine/Renderer.h +++ b/src/include/Dengine/Renderer.h @@ -24,25 +24,36 @@ typedef struct RenderTex v4 texRect; } RenderTex; -typedef struct RenderQuad -{ - Vertex vertex[4]; -} RenderQuad_; +typedef struct TrianglePoints { + v2 points[3]; +} TrianglePoints; typedef struct RenderGroup { + enum RenderMode mode; Texture *tex; v4 color; Vertex *vertexList; i32 vertexIndex; + } RenderGroup; +enum RenderMode +{ + rendermode_quad, + rendermode_triangle, + rendermode_count, + rendermode_invalid, +}; + typedef struct Renderer { Shader *shader; - u32 vao; - u32 vbo; + + u32 vao[rendermode_count]; + u32 vbo[rendermode_count]; + i32 numVertexesInVbo; v2 vertexNdcFactor; v2 size; @@ -52,7 +63,8 @@ typedef struct Renderer } Renderer; // TODO(doyle): Use z-index occluding for rendering -RenderTex renderer_createNullRenderTex(AssetManager *const assetManager); +RenderTex +renderer_createNullRenderTex(AssetManager *const assetManager); // TODO(doyle): Clean up lines // Renderer::~Renderer() { glDeleteVertexArrays(1, &this->quadVAO); } @@ -68,6 +80,10 @@ inline void renderer_staticRect(Renderer *const renderer, v2 pos, v2 size, renderTex, color); } +void renderer_triangle(Renderer *const renderer, Rect camera, + TrianglePoints triangle, v2 pivotPoint, f32 rotate, + RenderTex renderTex, v4 color); + void renderer_string(Renderer *const renderer, MemoryArena_ *arena, Rect camera, Font *const font, const char *const string, v2 pos, v2 pivotPoint,