feat: add model matrix transformation hierarchy and delta time tracking

This commit is contained in:
Natsirt867
2025-10-17 20:44:22 -05:00
parent 65e7d1b401
commit 6c6f831353
9 changed files with 323 additions and 47 deletions

View File

@@ -20,7 +20,9 @@ add_executable(BeyondDepth src/main.c
external/glad/src/glad.c external/glad/src/glad.c
src/engine/renderer/shaders/shader.c src/engine/renderer/shaders/shader.c
src/engine/renderer/shaders/shader.h src/engine/renderer/shaders/shader.h
src/engine/renderer/data/engine_data.h) src/engine/renderer/data/engine_data.h
src/engine/renderer/mesh/mesh.c
src/engine/renderer/mesh/mesh.h)
set_target_properties(BeyondDepth PROPERTIES WIN32_EXECUTABLE FALSE) set_target_properties(BeyondDepth PROPERTIES WIN32_EXECUTABLE FALSE)

View File

@@ -4,6 +4,8 @@
#include "camera.h" #include "camera.h"
#include "../..//core/logger/log.h" #include "../..//core/logger/log.h"
#include <cglm/cglm.h>
#include "../shaders/shader.h"
void camera_init(Camera *self, vec3 initial_position, int width, int height) { void camera_init(Camera *self, vec3 initial_position, int width, int height) {
// copy initial_pos vector // copy initial_pos vector
@@ -16,9 +18,11 @@ void camera_init(Camera *self, vec3 initial_position, int width, int height) {
glm_vec3_copy((vec3){0.0f, 1.0f, 0.0f}, self->Up); glm_vec3_copy((vec3){0.0f, 1.0f, 0.0f}, self->Up);
// initialize controls and viewport // initialize controls and viewport
self->speed = 0.1f; self->speed = 0.9f;
self->sensitivity = 100.0f; self->sensitivity = 3.5f;
self->FOV_deg = 45.0f; self->FOV_deg = 45.0f;
self->yaw = -90.0f;
self->pitch = 0.0f;
self->width = width; self->width = width;
self->height = height; self->height = height;
@@ -27,13 +31,6 @@ void camera_init(Camera *self, vec3 initial_position, int width, int height) {
glm_mat4_identity(self->View); glm_mat4_identity(self->View);
} }
/*
Camera l_camera = {
.Position = { 0.0f, 0.0f, 0.0f },
.Orientation = { 0.0f, 0.0f, -1.0f },
.Up = { 0.0f, 1.0f, 0.0f }
};*/
void camera_update_view_matrix(Camera *self) { void camera_update_view_matrix(Camera *self) {
// calc the center or target point (Pos + Orientation) // calc the center or target point (Pos + Orientation)
vec3 target; vec3 target;
@@ -50,31 +47,81 @@ void camera_update_projection(Camera *self, float nearPlane, float farPlane, mat
// TODO: Update matrix function signature to accept camera entity? // TODO: Update matrix function signature to accept camera entity?
// change name to camera_upload_matrix for clarity? // change name to camera_upload_matrix for clarity?
void camera_matrix_to_shader(Camera *self, GLuint shader_program_ID, const char *uniform_name) { void camera_matrix_to_shader(Camera *self, Shader *shader, const char *uniform_name) {
// update the view matrix first, camera position might have moved // update the view matrix first, camera position might have moved
camera_update_view_matrix(self); camera_update_view_matrix(self);
// calculate projection matrix (P) with temporary matrix 'projection' for the result // calculate projection matrix (P) with temporary matrix 'projection' for the result
mat4 projection; mat4 projection;
camera_update_projection(self, 0.1f, 100.0f, projection); camera_update_projection(self, 0.001f, 100.0f, projection);
// combine the matrices -- P * V -> PV // combine the matrices -- P * V -> PV
mat4 view_projection; mat4 view_projection;
glm_mat4_mul(projection, self->View, view_projection); glm_mat4_mul(projection, self->View, view_projection);
// get uniform location // get uniform location
GLint uniform_location = glGetUniformLocation(shader_program_ID, uniform_name); GLint uniform_location = shader->camMatrixLocation;
if (uniform_location == -1) { if (uniform_location == -1) {
log_error("Warning: Uniform '%s' not found in shader program %u.\n", uniform_name, shader_program_ID); // log_error("Warning: Uniform '%s' not found in shader program %u.\n", uniform_name, shader);
return; return;
} }
// upload combined matrix to the shader // upload combined matrix to the shader
// view_projection[0] array pointer decays to first value // view_projection[0] array pointer decays to first value
glUniformMatrix4fv(uniform_location, 1, GL_FALSE, view_projection[0]); glUniformMatrix4fv(uniform_location, 1, GL_FALSE, *view_projection);
} }
void camera_inputs(Camera *self, SDL_Event *event) { void camera_process_keyboard(Camera *self, const Uint8 *keyState, float deltaTime) {
// TODO: implementation for handling keyboard/mouse inputs for camera vec3 move;
float current_speed = self->speed * deltaTime;
// forward/back
if (keyState[SDL_SCANCODE_W]) {
glm_vec3_scale(self->Orientation, current_speed, move);
glm_vec3_add(self->Position, move, self->Position);
}
if (keyState[SDL_SCANCODE_S]) {
glm_vec3_scale(self->Orientation, current_speed, move);
glm_vec3_sub(self->Position, move, self->Position);
}
// strafe left/right
vec3 right;
glm_vec3_cross(self->Orientation, self->Up, right);
glm_vec3_normalize(right);
if (keyState[SDL_SCANCODE_A]) {
glm_vec3_scale(right, current_speed, move);
glm_vec3_sub(self->Position, move, self->Position);
}
if (keyState[SDL_SCANCODE_D]) {
glm_vec3_scale(right, current_speed, move);
glm_vec3_add(self->Position, move, self->Position);
}
}
// TODO: add deltaTime arg for smoother rotation
void camera_process_mouse(Camera *self, float xoffset, float yoffset, float deltaTime) {
float effective_sensitivity = self->sensitivity * deltaTime;
xoffset *= effective_sensitivity;
yoffset *= effective_sensitivity;
self->yaw += xoffset;
self->pitch += yoffset;
// clamps the pitch to prevent camera from flipping upside down
if (self->pitch > 89.0f)
self->pitch = 89.0f;
if (self->pitch < -89.0f)
self->pitch = -89.0f;
// calc new orientation vector from yaw and pitch
vec3 new_orientation;
new_orientation[0] = cos(glm_rad(self->yaw)) * cos(glm_rad(self->pitch));
new_orientation[1] = sin(glm_rad(self->pitch));
new_orientation[2] = sin(glm_rad(self->yaw)) * cos(glm_rad(self->pitch));
glm_vec3_normalize_to(new_orientation, self->Orientation);
} }

View File

@@ -13,6 +13,8 @@ typedef struct {
vec3 Position; vec3 Position;
vec3 Orientation; vec3 Orientation;
vec3 Up; vec3 Up;
float yaw;
float pitch;
float speed; float speed;
float sensitivity; float sensitivity;
float FOV_deg; float FOV_deg;
@@ -32,9 +34,12 @@ void camera_update_view_matrix(Camera *self);
void camera_update_projection(Camera *self, float nearPlane, float forPlane, mat4 dest); void camera_update_projection(Camera *self, float nearPlane, float forPlane, mat4 dest);
// generates (projection * view) and uploads to the shader program // generates (projection * view) and uploads to the shader program
void camera_matrix_to_shader(Camera *self, GLuint shader_program_ID, const char *uniform_name); void camera_matrix_to_shader(Camera *self, Shader *shader, const char *uniform_name);
// TODO: Input handling (needs full SDL context later) // TODO: Input handling (needs full SDL context later)
void camera_inputs(Camera *self, SDL_Event *event); void camera_inputs(Camera *self, SDL_Event *event);
void camera_process_keyboard(Camera *self, const Uint8 *keystate, float deltaTime);
void camera_process_mouse(Camera *self, float xoffset, float yoffset, float deltaTime);
#endif //CAMERA_H #endif //CAMERA_H

View File

@@ -7,6 +7,7 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include "../camera/camera.h" #include "../camera/camera.h"
#include "../mesh/mesh.h"
typedef struct { typedef struct {
// Camera instance // Camera instance
@@ -15,10 +16,17 @@ typedef struct {
// Shader program instance // Shader program instance
Shader shader; Shader shader;
// Mesh instance
Mesh triangle_mesh;
mat4 pyramidModel;
// Keep track of window size // Keep track of window size
int window_width; int window_width;
int window_height; int window_height;
float lastFrameTime; // time of the previous frame
float deltaTime; // time elapsed since the last frame
// TODO: Add other components, SDL_Window* or Meshes, etc... // TODO: Add other components, SDL_Window* or Meshes, etc...
} Engine_Data; } Engine_Data;

View File

@@ -0,0 +1,93 @@
//
// Created by Tristan on 10/17/2025.
//
#include "mesh.h"
#include "../../core/logger/log.h"
#include <stdlib.h>
GLfloat vertices[] = {
// Position // Color
// X Y Z R G B // Index
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 0: Apex
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 1: Base Corner 1 (Red)
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 2: Base Corner 2 (Green)
0.0f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f // 3: Base Corner 3 (Blue)
};
const size_t vertices_size = sizeof(vertices);
GLuint indices[] = {
// side 1: (Apex, C1, C2)
0, 1, 2, // vertices that form the triangle
// side2: (Apex, C2, C3)
0, 2, 3,
// side 3: (Apex, C3, C1)
0, 3, 1,
// base: (C1, C3, C2) - order is crucial for culling
1, 3, 2
};
void mesh_init(Mesh *self, GLfloat *vertices, GLsizei vertices_size, GLuint *indices, GLsizei num_indices) {
self->num_indices = num_indices;
// generate buffers, vao, vbo, ebo
glGenVertexArrays(1, &self->VAO);
glGenBuffers(1, &self->VBO);
glGenBuffers(1, &self->EBO);
// bind the VAO
glBindVertexArray(self->VAO);
// bind and upload VBO data
glBindBuffer(GL_ARRAY_BUFFER, self->VBO);
glBufferData(GL_ARRAY_BUFFER, vertices_size, vertices, GL_STATIC_DRAW);
// bind and upload ebo data
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * num_indices, indices, GL_STATIC_DRAW);
// configure vertex attributes
// a. Position attribute (layout = 0)
// 3 floats per pos, stride = 6 floats total (3 pos + 3 colour), offset is 0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(0);
// b. Color attribute (layout = 1)
// 3 floats per color, stride = 6 floats total, offset is 3 floats (after pos)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
// TODO: hader also expects layout = 2 but lazy, pray no crash
// unbind the VAO (so it can't be modified)
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
log_info("Mesh initialized with VAO: %u", self->VAO);
}
void mesh_draw(Mesh *self) {
// bind the vao
glBindVertexArray(self->VAO);
// draw the triangle
glDrawElements(GL_TRIANGLES, self->num_indices, GL_UNSIGNED_INT, 0);
// unbind
glBindVertexArray(0);
}
void mesh_delete(Mesh *self) {
glDeleteVertexArrays(1, &self->VAO);
glDeleteBuffers(1, &self->VBO);
glDeleteBuffers(1, &self->EBO);
self->VAO = 0;
self->VBO = 0;
self->EBO = 0;
}

View File

@@ -0,0 +1,43 @@
//
// Created by Tristan on 10/17/2025.
//
#ifndef MESH_H
#define MESH_H
#include <glad/glad.h>
#include <cglm/cglm.h>
// Vertices for a simple 2D triangle (x, y, z)
// Add (r, g, b) color data after each pos
// X, Y, Z, R, G, B
extern GLfloat vertices[];
extern const size_t vertices_size;
// Indices define the order vertices are drawn
extern GLuint indices[];
#define INDICES_COUNT 12
typedef struct {
// Vertex Array Object
GLuint VAO;
// Vertex Buffer Object
GLuint VBO;
// Element Buffer Object
GLuint EBO;
// Number of indices to draw
GLsizei num_indices;
} Mesh;
// initializes and uploads mesh data to gpu
void mesh_init(Mesh *self, GLfloat *vertices, GLsizei vertices_size, GLuint *indices, GLsizei num_indices);
// Draws the mesh
void mesh_draw(Mesh *self);
void mesh_delete(Mesh *self);
#endif //MESH_H

View File

@@ -64,9 +64,9 @@ Shader shader_create(const char *vertexFile, const char *fragmentFile) {
char *vertexSource = get_file_contents(vertexFile); char *vertexSource = get_file_contents(vertexFile);
char *fragmentSource = get_file_contents(fragmentFile); char *fragmentSource = get_file_contents(fragmentFile);
if (!vertexSource || !fragmentSource) { if (vertexSource == NULL || fragmentSource == NULL) {
log_error("Failed to load shader files. Returning empty shader\n"); log_error("Failed to load shader files. Returning empty shader\n");
Shader emptyShader = { .ID = 0}; Shader emptyShader = { .ID = 0, .camMatrixLocation = -1};
if (vertexSource) if (vertexSource)
free(vertexSource); free(vertexSource);
if (fragmentSource) if (fragmentSource)
@@ -101,7 +101,19 @@ Shader shader_create(const char *vertexFile, const char *fragmentFile) {
glDeleteShader(vertexShader); glDeleteShader(vertexShader);
glDeleteShader(fragmentShader); glDeleteShader(fragmentShader);
Shader newShader = { .ID = programID }; GLint camMatrixLocation = glGetUniformLocation(programID, "camMatrix");
if (camMatrixLocation == -1) {
log_error("Uniform 'camMatrix' not found after linking shader program %u. Is it used in default.vert?", programID);
}
GLint modelMatrixLocation = glGetUniformLocation(programID, "modelMatrix");
if (modelMatrixLocation == -1) {
log_warn("Uniform 'modelMatrix' not found in shader program %u. Is it used in default.vert?", programID);
}
Shader newShader = { .ID = programID,
.camMatrixLocation = camMatrixLocation,
.modelMatrixLocation = modelMatrixLocation };
return newShader; return newShader;
} }

View File

@@ -11,6 +11,9 @@
typedef struct { typedef struct {
// Reference ID of the shader program // Reference ID of the shader program
GLuint ID; GLuint ID;
// Location of the camera matrix uniform, set at creation
GLint camMatrixLocation;
GLint modelMatrixLocation;
} Shader; } Shader;
char *get_file_contents(const char *filename); char *get_file_contents(const char *filename);

View File

@@ -17,6 +17,7 @@
#include "engine/renderer/camera/camera.h" #include "engine/renderer/camera/camera.h"
#include "engine/renderer/data/engine_data.h" #include "engine/renderer/data/engine_data.h"
#include "engine/renderer/shaders/shader.h" #include "engine/renderer/shaders/shader.h"
#include "engine/renderer/mesh/mesh.h"
static SDL_Window *window = NULL; static SDL_Window *window = NULL;
static SDL_GLContext gl_context = NULL; static SDL_GLContext gl_context = NULL;
@@ -28,6 +29,9 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
log_info("Starting program and initializing SDL..."); log_info("Starting program and initializing SDL...");
/* Create the window */ /* Create the window */
int win_width = 1280;
int win_height = 720;
// TODO: make use of arena allocators potentially? see: digital grove repo // TODO: make use of arena allocators potentially? see: digital grove repo
Engine_Data *state = (Engine_Data*)malloc(sizeof(Engine_Data)); Engine_Data *state = (Engine_Data*)malloc(sizeof(Engine_Data));
if (state == NULL) { if (state == NULL) {
@@ -35,25 +39,22 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
// pass allocated state back to the SDL loop state->window_width = win_width;
*appstate = state; state->window_height = win_height;
int win_width = 1280;
int win_height = 720;
// use of SDL_WINDOW_OPENGL flag to create a context-compatible window with shaders
window = SDL_CreateWindow("Beyond Depth", win_width, win_height, SDL_WINDOW_OPENGL);
if (!window) {
log_error("Couldn't create SDL window: %s", SDL_GetError());
free(state);
return SDL_APP_FAILURE;
}
// create openGL context with openGL 3.3 core profile // create openGL context with openGL 3.3 core profile
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
// use of SDL_WINDOW_OPENGL flag to create a context-compatible window with shaders
window = SDL_CreateWindow("Beyond Depth", win_width, win_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!window) {
log_error("Couldn't create SDL window: %s", SDL_GetError());
free(state);
return SDL_APP_FAILURE;
}
gl_context = SDL_GL_CreateContext(window); gl_context = SDL_GL_CreateContext(window);
if (!gl_context) { if (!gl_context) {
log_error("Failed to create OpenGL context: %s", SDL_GetError()); log_error("Failed to create OpenGL context: %s", SDL_GetError());
@@ -72,22 +73,27 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
// TODO: if still crashing on initial startup, ensure all of OpenGL, GLAD is properly initialized // z-buffer
state->window_width = win_width; glEnable(GL_DEPTH_TEST);
state->window_height = win_height;
vec3 initial_pos = {0.0f, 0.0f, 3.0f};
camera_init(&state->camera, initial_pos, state->window_width, state->window_height);
state->shader = shader_create("default.vert", "default.frag"); state->shader = shader_create("default.vert", "default.frag");
if (state->shader.ID == 0) { if (state->shader.ID == 0) {
log_error("Failed to initialize shader program"); log_error("Failed to initialize shader program");
// TODO: cleanup memory // TODO: cleanup memory
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
vec3 initial_pos = {0.0f, 0.0f, 2.0f};
camera_init(&state->camera, initial_pos, state->window_width, state->window_height);
mesh_init(&state->triangle_mesh, vertices, (GLsizei)vertices_size, indices, INDICES_COUNT);
glm_mat4_identity(state->pyramidModel);
SDL_SetWindowRelativeMouseMode(window, true);
// pass allocated state back to the SDL loop
*appstate = state;
log_info("Camera, Shaders and App State initialized"); log_info("Camera, Shaders and App State initialized");
@@ -97,11 +103,24 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ /* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{ {
if (event->type == SDL_EVENT_KEY_DOWN || Engine_Data *state = (Engine_Data *)appstate;
event->type == SDL_EVENT_QUIT) {
log_info("User pressed a key"); // handles quitting
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS;
} }
// handles esc key to quit
if (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_ESCAPE) {
return SDL_APP_SUCCESS;
}
// handle mouse motion for camera look
if (event->type == SDL_EVENT_MOUSE_MOTION) {
// y movement inverted?
camera_process_mouse(&state->camera, event->motion.xrel, -event->motion.yrel, state->deltaTime);
}
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
@@ -111,6 +130,13 @@ SDL_AppResult SDL_AppIterate(void *appstate)
{ {
Engine_Data *state = (Engine_Data *)appstate; // good practice Engine_Data *state = (Engine_Data *)appstate; // good practice
float currentTime = (float)SDL_GetTicks() / 1000.0f;
state->deltaTime = currentTime - state->lastFrameTime;
state->lastFrameTime = currentTime;
const Uint8 *keyState = SDL_GetKeyboardState(NULL);
camera_process_keyboard(&state->camera, keyState, state->deltaTime);
// set viewport // set viewport
glViewport(0, 0, 1280, 720); glViewport(0, 0, 1280, 720);
@@ -119,7 +145,39 @@ SDL_AppResult SDL_AppIterate(void *appstate)
glClearColor(0.07f, 0.13f, 0.17f, 1.0f); glClearColor(0.07f, 0.13f, 0.17f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// TODO: add drawing logic // calculate "orbit" angle for total rotation overtime
float rotation_speed_deg = 45.0f;
float rotation_amount = glm_rad(rotation_speed_deg * state->deltaTime);
// accumulate total rotation
static float total_rotation = 0.0f;
total_rotation += rotation_amount;
shader_activate(&state->shader);
// upload camera matrix (P*V) to the shader
camera_matrix_to_shader(&state->camera, &state->shader, "camMatrix");
float rotation = (float)SDL_GetTicks() / 1000.0f;
glm_mat4_identity(state->pyramidModel);
// rotate around the y-axis
glm_rotate(state->pyramidModel, total_rotation * 0.5f, (vec3){0.0f, 1.0f, 0.0f});
mat4 orbitModel; // temporary object
glm_mat4_identity(orbitModel);
glm_rotate(orbitModel, total_rotation, (vec3){0.0f, 1.0f, 0.0f});
glm_translate(orbitModel, (vec3){3.0f, 0.0f, 0.0f});
glm_rotate(orbitModel, total_rotation * 2.0f, (vec3){0.5f, 1.0f, 0.0f});
// upload model matrix to shader
glUniformMatrix4fv(state->shader.modelMatrixLocation, 1, GL_FALSE, *state->pyramidModel);
mesh_draw(&state->triangle_mesh);
glUniformMatrix4fv(state->shader.modelMatrixLocation, 1, GL_FALSE, *orbitModel);
mesh_draw(&state->triangle_mesh);
// swap front and back buffers to display rendered frame // swap front and back buffers to display rendered frame
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
@@ -134,7 +192,12 @@ void SDL_AppQuit(void *appstate, SDL_AppResult result)
// cleanup all resources // cleanup all resources
if (state) { if (state) {
mesh_delete(&state->triangle_mesh);
if (state->shader.ID != 0) {
shader_delete(&state->shader); shader_delete(&state->shader);
}
free(state); free(state);
} }