Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c6f831353 | ||
|
|
65e7d1b401 | ||
|
|
c9c5fc428f | ||
|
|
a428be256c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
SDL
|
SDL
|
||||||
.idea/
|
.idea/
|
||||||
|
external/
|
||||||
|
|||||||
@@ -8,10 +8,26 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIGURATION>")
|
|||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIGURATION>")
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$<CONFIGURATION>")
|
||||||
|
|
||||||
# This assumes the SDL source is available in SDL
|
# This assumes the SDL source is available in SDL
|
||||||
add_subdirectory(SDL EXCLUDE_FROM_ALL)
|
add_subdirectory(external/SDL EXCLUDE_FROM_ALL)
|
||||||
|
add_subdirectory(external/cglm EXCLUDE_FROM_ALL)
|
||||||
|
#add_subdirectory(glad EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
# Create your game executable target as usual
|
# Create your game executable target as usual
|
||||||
add_executable(BeyondDepth WIN32 main.c)
|
add_executable(BeyondDepth src/main.c
|
||||||
|
src/engine/core/logger/log.c
|
||||||
|
src/engine/renderer/camera/camera.c
|
||||||
|
src/engine/renderer/camera/camera.h
|
||||||
|
external/glad/src/glad.c
|
||||||
|
src/engine/renderer/shaders/shader.c
|
||||||
|
src/engine/renderer/shaders/shader.h
|
||||||
|
src/engine/renderer/data/engine_data.h
|
||||||
|
src/engine/renderer/mesh/mesh.c
|
||||||
|
src/engine/renderer/mesh/mesh.h)
|
||||||
|
|
||||||
# Link to the actual SDL3 library.
|
set_target_properties(BeyondDepth PROPERTIES WIN32_EXECUTABLE FALSE)
|
||||||
target_link_libraries(BeyondDepth PRIVATE SDL3::SDL3)
|
|
||||||
|
target_include_directories(BeyondDepth PRIVATE external/glad/include)
|
||||||
|
|
||||||
|
target_link_libraries(BeyondDepth PRIVATE
|
||||||
|
SDL3::SDL3
|
||||||
|
cglm)
|
||||||
67
main.c
67
main.c
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied
|
|
||||||
warranty. In no event will the authors be held liable for any damages
|
|
||||||
arising from the use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose,
|
|
||||||
including commercial applications, and to alter it and redistribute it
|
|
||||||
freely.
|
|
||||||
*/
|
|
||||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
#include <SDL3/SDL_main.h>
|
|
||||||
|
|
||||||
static SDL_Window *window = NULL;
|
|
||||||
static SDL_Renderer *renderer = NULL;
|
|
||||||
|
|
||||||
/* This function runs once at startup. */
|
|
||||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
|
||||||
{
|
|
||||||
/* Create the window */
|
|
||||||
if (!SDL_CreateWindowAndRenderer("Hello World", 800, 600, SDL_WINDOW_FULLSCREEN, &window, &renderer)) {
|
|
||||||
SDL_Log("Couldn't create window and renderer: %s", SDL_GetError());
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
|
||||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
|
||||||
{
|
|
||||||
if (event->type == SDL_EVENT_KEY_DOWN ||
|
|
||||||
event->type == SDL_EVENT_QUIT) {
|
|
||||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
|
||||||
}
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function runs once per frame, and is the heart of the program. */
|
|
||||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
|
||||||
{
|
|
||||||
const char *message = "Hello World!";
|
|
||||||
int w = 0, h = 0;
|
|
||||||
float x, y;
|
|
||||||
const float scale = 4.0f;
|
|
||||||
|
|
||||||
/* Center the message and scale it up */
|
|
||||||
SDL_GetRenderOutputSize(renderer, &w, &h);
|
|
||||||
SDL_SetRenderScale(renderer, scale, scale);
|
|
||||||
x = ((w / scale) - SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE * SDL_strlen(message)) / 2;
|
|
||||||
y = ((h / scale) - SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) / 2;
|
|
||||||
|
|
||||||
/* Draw the message */
|
|
||||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
|
||||||
SDL_RenderClear(renderer);
|
|
||||||
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
|
||||||
SDL_RenderDebugText(renderer, x, y, message);
|
|
||||||
SDL_RenderPresent(renderer);
|
|
||||||
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function runs once at shutdown. */
|
|
||||||
void SDL_AppQuit(void *appstate, SDL_AppResult result)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
168
src/engine/core/logger/log.c
Normal file
168
src/engine/core/logger/log.c
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 rxi
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#define MAX_CALLBACKS 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
log_LogFn fn;
|
||||||
|
void *udata;
|
||||||
|
int level;
|
||||||
|
} Callback;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
void *udata;
|
||||||
|
log_LockFn lock;
|
||||||
|
int level;
|
||||||
|
bool quiet;
|
||||||
|
Callback callbacks[MAX_CALLBACKS];
|
||||||
|
} L;
|
||||||
|
|
||||||
|
|
||||||
|
static const char *level_strings[] = {
|
||||||
|
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef LOG_USE_COLOR
|
||||||
|
static const char *level_colors[] = {
|
||||||
|
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static void stdout_callback(log_Event *ev) {
|
||||||
|
char buf[16];
|
||||||
|
buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0';
|
||||||
|
#ifdef LOG_USE_COLOR
|
||||||
|
fprintf(
|
||||||
|
ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ",
|
||||||
|
buf, level_colors[ev->level], level_strings[ev->level],
|
||||||
|
ev->file, ev->line);
|
||||||
|
#else
|
||||||
|
fprintf(
|
||||||
|
ev->udata, "%s %-5s %s:%d: ",
|
||||||
|
buf, level_strings[ev->level], ev->file, ev->line);
|
||||||
|
#endif
|
||||||
|
vfprintf(ev->udata, ev->fmt, ev->ap);
|
||||||
|
fprintf(ev->udata, "\n");
|
||||||
|
fflush(ev->udata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void file_callback(log_Event *ev) {
|
||||||
|
char buf[64];
|
||||||
|
buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
|
||||||
|
fprintf(
|
||||||
|
ev->udata, "%s %-5s %s:%d: ",
|
||||||
|
buf, level_strings[ev->level], ev->file, ev->line);
|
||||||
|
vfprintf(ev->udata, ev->fmt, ev->ap);
|
||||||
|
fprintf(ev->udata, "\n");
|
||||||
|
fflush(ev->udata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void lock(void) {
|
||||||
|
if (L.lock) { L.lock(true, L.udata); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void unlock(void) {
|
||||||
|
if (L.lock) { L.lock(false, L.udata); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char* log_level_string(int level) {
|
||||||
|
return level_strings[level];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_set_lock(log_LockFn fn, void *udata) {
|
||||||
|
L.lock = fn;
|
||||||
|
L.udata = udata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_set_level(int level) {
|
||||||
|
L.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_set_quiet(bool enable) {
|
||||||
|
L.quiet = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int log_add_callback(log_LogFn fn, void *udata, int level) {
|
||||||
|
for (int i = 0; i < MAX_CALLBACKS; i++) {
|
||||||
|
if (!L.callbacks[i].fn) {
|
||||||
|
L.callbacks[i] = (Callback) { fn, udata, level };
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int log_add_fp(FILE *fp, int level) {
|
||||||
|
return log_add_callback(file_callback, fp, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void init_event(log_Event *ev, void *udata) {
|
||||||
|
if (!ev->time) {
|
||||||
|
time_t t = time(NULL);
|
||||||
|
ev->time = localtime(&t);
|
||||||
|
}
|
||||||
|
ev->udata = udata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void log_log(int level, const char *file, int line, const char *fmt, ...) {
|
||||||
|
log_Event ev = {
|
||||||
|
.fmt = fmt,
|
||||||
|
.file = file,
|
||||||
|
.line = line,
|
||||||
|
.level = level,
|
||||||
|
};
|
||||||
|
|
||||||
|
lock();
|
||||||
|
|
||||||
|
if (!L.quiet && level >= L.level) {
|
||||||
|
init_event(&ev, stderr);
|
||||||
|
va_start(ev.ap, fmt);
|
||||||
|
stdout_callback(&ev);
|
||||||
|
va_end(ev.ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
|
||||||
|
Callback *cb = &L.callbacks[i];
|
||||||
|
if (level >= cb->level) {
|
||||||
|
init_event(&ev, cb->udata);
|
||||||
|
va_start(ev.ap, fmt);
|
||||||
|
cb->fn(&ev);
|
||||||
|
va_end(ev.ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
49
src/engine/core/logger/log.h
Normal file
49
src/engine/core/logger/log.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 rxi
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the MIT license. See `log.c` for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LOG_H
|
||||||
|
#define LOG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define LOG_VERSION "0.1.0"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
va_list ap;
|
||||||
|
const char *fmt;
|
||||||
|
const char *file;
|
||||||
|
struct tm *time;
|
||||||
|
void *udata;
|
||||||
|
int line;
|
||||||
|
int level;
|
||||||
|
} log_Event;
|
||||||
|
|
||||||
|
typedef void (*log_LogFn)(log_Event *ev);
|
||||||
|
typedef void (*log_LockFn)(bool lock, void *udata);
|
||||||
|
|
||||||
|
enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL };
|
||||||
|
|
||||||
|
#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
|
||||||
|
const char* log_level_string(int level);
|
||||||
|
void log_set_lock(log_LockFn fn, void *udata);
|
||||||
|
void log_set_level(int level);
|
||||||
|
void log_set_quiet(bool enable);
|
||||||
|
int log_add_callback(log_LogFn fn, void *udata, int level);
|
||||||
|
int log_add_fp(FILE *fp, int level);
|
||||||
|
|
||||||
|
void log_log(int level, const char *file, int line, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif
|
||||||
127
src/engine/renderer/camera/camera.c
Normal file
127
src/engine/renderer/camera/camera.c
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
//
|
||||||
|
// Created by Tristan on 10/16/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "camera.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) {
|
||||||
|
// copy initial_pos vector
|
||||||
|
glm_vec3_copy(initial_position, self->Position);
|
||||||
|
|
||||||
|
// initialize orientation vector
|
||||||
|
glm_vec3_copy((vec3){0.0f, 0.0f, -1.0f}, self->Orientation);
|
||||||
|
|
||||||
|
// initialize up vector
|
||||||
|
glm_vec3_copy((vec3){0.0f, 1.0f, 0.0f}, self->Up);
|
||||||
|
|
||||||
|
// initialize controls and viewport
|
||||||
|
self->speed = 0.9f;
|
||||||
|
self->sensitivity = 3.5f;
|
||||||
|
self->FOV_deg = 45.0f;
|
||||||
|
self->yaw = -90.0f;
|
||||||
|
self->pitch = 0.0f;
|
||||||
|
|
||||||
|
self->width = width;
|
||||||
|
self->height = height;
|
||||||
|
|
||||||
|
// Init view matrix to identity (for first frame)
|
||||||
|
glm_mat4_identity(self->View);
|
||||||
|
}
|
||||||
|
|
||||||
|
void camera_update_view_matrix(Camera *self) {
|
||||||
|
// calc the center or target point (Pos + Orientation)
|
||||||
|
vec3 target;
|
||||||
|
glm_vec3_add(self->Position, self->Orientation, target);
|
||||||
|
|
||||||
|
// create view matrix
|
||||||
|
glm_lookat(self->Position, target, self->Up, self->View);
|
||||||
|
}
|
||||||
|
|
||||||
|
void camera_update_projection(Camera *self, float nearPlane, float farPlane, mat4 dest) {
|
||||||
|
float aspect = (float)self->width / (float)self->height;
|
||||||
|
glm_perspective(glm_rad(self->FOV_deg), aspect, nearPlane, farPlane, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Update matrix function signature to accept camera entity?
|
||||||
|
// change name to camera_upload_matrix for clarity?
|
||||||
|
void camera_matrix_to_shader(Camera *self, Shader *shader, const char *uniform_name) {
|
||||||
|
// update the view matrix first, camera position might have moved
|
||||||
|
camera_update_view_matrix(self);
|
||||||
|
|
||||||
|
// calculate projection matrix (P) with temporary matrix 'projection' for the result
|
||||||
|
mat4 projection;
|
||||||
|
camera_update_projection(self, 0.001f, 100.0f, projection);
|
||||||
|
|
||||||
|
// combine the matrices -- P * V -> PV
|
||||||
|
mat4 view_projection;
|
||||||
|
glm_mat4_mul(projection, self->View, view_projection);
|
||||||
|
|
||||||
|
// get uniform location
|
||||||
|
GLint uniform_location = shader->camMatrixLocation;
|
||||||
|
|
||||||
|
if (uniform_location == -1) {
|
||||||
|
// log_error("Warning: Uniform '%s' not found in shader program %u.\n", uniform_name, shader);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload combined matrix to the shader
|
||||||
|
// view_projection[0] array pointer decays to first value
|
||||||
|
glUniformMatrix4fv(uniform_location, 1, GL_FALSE, *view_projection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void camera_process_keyboard(Camera *self, const Uint8 *keyState, float deltaTime) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
45
src/engine/renderer/camera/camera.h
Normal file
45
src/engine/renderer/camera/camera.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// Created by Tristan on 10/16/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CAMERA_H
|
||||||
|
#define CAMERA_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <cglm/cglm.h>
|
||||||
|
#include "../shaders/shader.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vec3 Position;
|
||||||
|
vec3 Orientation;
|
||||||
|
vec3 Up;
|
||||||
|
float yaw;
|
||||||
|
float pitch;
|
||||||
|
float speed;
|
||||||
|
float sensitivity;
|
||||||
|
float FOV_deg;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
mat4 View; // cache for the calculated View Matrix
|
||||||
|
} Camera;
|
||||||
|
|
||||||
|
// initializer
|
||||||
|
void camera_init(Camera *self, vec3 initial_position, int width, int height);
|
||||||
|
|
||||||
|
/* --- Matrix calculation functions --- */
|
||||||
|
// Updates self->View matrix based on pos, orientation and up
|
||||||
|
void camera_update_view_matrix(Camera *self);
|
||||||
|
|
||||||
|
// calculates the projection matrix for a given set of planes
|
||||||
|
void camera_update_projection(Camera *self, float nearPlane, float forPlane, mat4 dest);
|
||||||
|
|
||||||
|
// generates (projection * view) and uploads to the shader program
|
||||||
|
void camera_matrix_to_shader(Camera *self, Shader *shader, const char *uniform_name);
|
||||||
|
|
||||||
|
// TODO: Input handling (needs full SDL context later)
|
||||||
|
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
|
||||||
33
src/engine/renderer/data/engine_data.h
Normal file
33
src/engine/renderer/data/engine_data.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// Created by Tristan on 10/17/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ENGINE_DATA_H
|
||||||
|
#define ENGINE_DATA_H
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include "../camera/camera.h"
|
||||||
|
#include "../mesh/mesh.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Camera instance
|
||||||
|
Camera camera;
|
||||||
|
|
||||||
|
// Shader program instance
|
||||||
|
Shader shader;
|
||||||
|
|
||||||
|
// Mesh instance
|
||||||
|
Mesh triangle_mesh;
|
||||||
|
mat4 pyramidModel;
|
||||||
|
|
||||||
|
// Keep track of window size
|
||||||
|
int window_width;
|
||||||
|
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...
|
||||||
|
} Engine_Data;
|
||||||
|
|
||||||
|
#endif //ENGINE_DATA_H
|
||||||
93
src/engine/renderer/mesh/mesh.c
Normal file
93
src/engine/renderer/mesh/mesh.c
Normal 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;
|
||||||
|
}
|
||||||
43
src/engine/renderer/mesh/mesh.h
Normal file
43
src/engine/renderer/mesh/mesh.h
Normal 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
|
||||||
18
src/engine/renderer/shaders/default.frag
Normal file
18
src/engine/renderer/shaders/default.frag
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#version 330 core
|
||||||
|
|
||||||
|
// Outputs colors in RGBA
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
// Inputs the color from the vertex shader
|
||||||
|
in vec3 color;
|
||||||
|
|
||||||
|
// Inputs the texture coordinates from the vertex shader
|
||||||
|
in vec2 texCoord;
|
||||||
|
|
||||||
|
// get the texture unit from the main function
|
||||||
|
uniform sampler2D tex0;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
FragColor = texture(tex0, texCoord);
|
||||||
|
}
|
||||||
22
src/engine/renderer/shaders/default.vert
Normal file
22
src/engine/renderer/shaders/default.vert
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#version 330 core
|
||||||
|
layout (location = 0) in vec3 aPos; // Vertex Position
|
||||||
|
layout (location = 1) in vec3 aColor; // Vertex Color
|
||||||
|
layout (location = 2) in vec2 aTex; // Texture coordinates
|
||||||
|
|
||||||
|
// Outputs the color for the Fragment shader
|
||||||
|
out vec3 color;
|
||||||
|
// Outputs the texture coordinates for the Fragment shader
|
||||||
|
out vec2 texCoord;
|
||||||
|
|
||||||
|
// Imports the camera matrix from the main function
|
||||||
|
uniform mat4 camMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Apply the combined P*V matrix to transform the vertex position.
|
||||||
|
gl_Position = camMatrix * vec4(aPos, 1.0);
|
||||||
|
// Assigns the colors from the Vertex Data to "color"
|
||||||
|
color = aColor;
|
||||||
|
// Assigns the texture coordinates from the Vertex Data to "texCoord"
|
||||||
|
texCoord = aTex;
|
||||||
|
}
|
||||||
129
src/engine/renderer/shaders/shader.c
Normal file
129
src/engine/renderer/shaders/shader.c
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
//
|
||||||
|
// Created by Tristan on 10/16/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "shader.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "../../core/logger/log.h"
|
||||||
|
|
||||||
|
// reads entire file contents into a string
|
||||||
|
char *get_file_contents(const char *filename) {
|
||||||
|
FILE *fp;
|
||||||
|
long length;
|
||||||
|
char *buffer = NULL;
|
||||||
|
|
||||||
|
fp = fopen(filename, "rb");
|
||||||
|
if (!fp) {
|
||||||
|
log_error("Could not open file '%s'. Reason: %s\n", filename, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
length = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
buffer = (char *)malloc(length + 1);
|
||||||
|
if (!buffer) {
|
||||||
|
log_error("Memory allocation failed for file '%s'.\n", filename);
|
||||||
|
fclose(fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(buffer, 1, length, fp);
|
||||||
|
buffer[length] = '\0';
|
||||||
|
fclose(fp);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the different shaders have compiled properly
|
||||||
|
void compile_errors(unsigned int shader, const char *type) {
|
||||||
|
GLint hasCompiled;
|
||||||
|
char infoLog[1024];
|
||||||
|
|
||||||
|
if (strcmp(type, "PROGRAM") != 0) {
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &hasCompiled);
|
||||||
|
if (hasCompiled == GL_FALSE) {
|
||||||
|
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
|
||||||
|
log_error("SHADER_COMPILATION_ERROR for: %s\n%s", type, infoLog);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glGetProgramiv(shader, GL_LINK_STATUS, &hasCompiled);
|
||||||
|
if (hasCompiled == GL_FALSE) {
|
||||||
|
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
|
||||||
|
log_error("SHADER_LINKING_ERROR for %s\n%s", type, infoLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the shader program from 2 different shaders
|
||||||
|
Shader shader_create(const char *vertexFile, const char *fragmentFile) {
|
||||||
|
// read shader source files
|
||||||
|
char *vertexSource = get_file_contents(vertexFile);
|
||||||
|
char *fragmentSource = get_file_contents(fragmentFile);
|
||||||
|
|
||||||
|
if (vertexSource == NULL || fragmentSource == NULL) {
|
||||||
|
log_error("Failed to load shader files. Returning empty shader\n");
|
||||||
|
Shader emptyShader = { .ID = 0, .camMatrixLocation = -1};
|
||||||
|
if (vertexSource)
|
||||||
|
free(vertexSource);
|
||||||
|
if (fragmentSource)
|
||||||
|
free(fragmentSource);
|
||||||
|
return emptyShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile vertex shader
|
||||||
|
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vertexShader, 1, (const char *const *)&vertexSource, NULL);
|
||||||
|
glCompileShader(vertexShader);
|
||||||
|
compile_errors(vertexShader, "VERTEX");
|
||||||
|
|
||||||
|
// compile fragment shader
|
||||||
|
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(fragmentShader, 1, (const char *const *)&fragmentSource, NULL);
|
||||||
|
glCompileShader(fragmentShader);
|
||||||
|
compile_errors(fragmentShader, "FRAGMENT");
|
||||||
|
|
||||||
|
// cleanup strings after compilation
|
||||||
|
free(vertexSource);
|
||||||
|
free(fragmentSource);
|
||||||
|
|
||||||
|
// link shaders into a program
|
||||||
|
GLuint programID = glCreateProgram();
|
||||||
|
glAttachShader(programID, vertexShader);
|
||||||
|
glAttachShader(programID, fragmentShader);
|
||||||
|
glLinkProgram(programID);
|
||||||
|
compile_errors(programID, "PROGRAM");
|
||||||
|
|
||||||
|
// cleanup temporary shaders now that we've linked into the program
|
||||||
|
glDeleteShader(vertexShader);
|
||||||
|
glDeleteShader(fragmentShader);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// activates the shader program
|
||||||
|
void shader_activate(Shader *self) {
|
||||||
|
glUseProgram(self->ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// deletes the shader program
|
||||||
|
void shader_delete(Shader *self) {
|
||||||
|
glDeleteProgram(self->ID);
|
||||||
|
self->ID = 0; // just in-case
|
||||||
|
}
|
||||||
31
src/engine/renderer/shaders/shader.h
Normal file
31
src/engine/renderer/shaders/shader.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// Created by Tristan on 10/16/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SHADER_H
|
||||||
|
#define SHADER_H
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Reference ID of the shader program
|
||||||
|
GLuint ID;
|
||||||
|
// Location of the camera matrix uniform, set at creation
|
||||||
|
GLint camMatrixLocation;
|
||||||
|
GLint modelMatrixLocation;
|
||||||
|
} Shader;
|
||||||
|
|
||||||
|
char *get_file_contents(const char *filename);
|
||||||
|
|
||||||
|
Shader shader_create(const char *vertexFile, const char *fragmentFile);
|
||||||
|
|
||||||
|
// Takes a pointer to the instance
|
||||||
|
void shader_activate(Shader *self);
|
||||||
|
void shader_delete(Shader *self);
|
||||||
|
|
||||||
|
// Private helper on shaderClass.h C++ header -- no private in C :)
|
||||||
|
void compile_errors(unsigned int shader, const char *type);
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SHADER_H
|
||||||
213
src/main.c
Normal file
213
src/main.c
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely.
|
||||||
|
*/
|
||||||
|
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_main.h>
|
||||||
|
#include <cglm/cglm.h>
|
||||||
|
#include "engine/core/logger/log.h"
|
||||||
|
#include "engine/renderer/camera/camera.h"
|
||||||
|
#include "engine/renderer/data/engine_data.h"
|
||||||
|
#include "engine/renderer/shaders/shader.h"
|
||||||
|
#include "engine/renderer/mesh/mesh.h"
|
||||||
|
|
||||||
|
static SDL_Window *window = NULL;
|
||||||
|
static SDL_GLContext gl_context = NULL;
|
||||||
|
|
||||||
|
/* This function runs once at startup. */
|
||||||
|
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||||
|
{
|
||||||
|
//log_set_level(2);
|
||||||
|
log_info("Starting program and initializing SDL...");
|
||||||
|
/* Create the window */
|
||||||
|
|
||||||
|
int win_width = 1280;
|
||||||
|
int win_height = 720;
|
||||||
|
|
||||||
|
// TODO: make use of arena allocators potentially? see: digital grove repo
|
||||||
|
Engine_Data *state = (Engine_Data*)malloc(sizeof(Engine_Data));
|
||||||
|
if (state == NULL) {
|
||||||
|
log_error("Failed to allocate engine_data state, %s", SDL_GetError());
|
||||||
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->window_width = win_width;
|
||||||
|
state->window_height = win_height;
|
||||||
|
|
||||||
|
// create openGL context with openGL 3.3 core profile
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||||
|
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);
|
||||||
|
if (!gl_context) {
|
||||||
|
log_error("Failed to create OpenGL context: %s", SDL_GetError());
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
free(state);
|
||||||
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize GLAD
|
||||||
|
// loads all the OpenGL function pointers
|
||||||
|
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
|
||||||
|
log_error("Failed to initialize GLAD");
|
||||||
|
SDL_GL_DestroyContext(gl_context);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
free(state);
|
||||||
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// z-buffer
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
state->shader = shader_create("default.vert", "default.frag");
|
||||||
|
if (state->shader.ID == 0) {
|
||||||
|
log_error("Failed to initialize shader program");
|
||||||
|
// TODO: cleanup memory
|
||||||
|
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");
|
||||||
|
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||||
|
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||||
|
{
|
||||||
|
Engine_Data *state = (Engine_Data *)appstate;
|
||||||
|
|
||||||
|
// handles quitting
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This function runs once per frame, and is the heart of the program. */
|
||||||
|
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
glViewport(0, 0, 1280, 720);
|
||||||
|
|
||||||
|
// clear the screen before drawing
|
||||||
|
// TODO: macros for colours so its easier to read? 0.07f does nothing for me context wise lol
|
||||||
|
glClearColor(0.07f, 0.13f, 0.17f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
SDL_GL_SwapWindow(window);
|
||||||
|
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function runs once at shutdown. */
|
||||||
|
void SDL_AppQuit(void *appstate, SDL_AppResult result)
|
||||||
|
{
|
||||||
|
Engine_Data *state = (Engine_Data *)appstate;
|
||||||
|
|
||||||
|
// cleanup all resources
|
||||||
|
if (state) {
|
||||||
|
mesh_delete(&state->triangle_mesh);
|
||||||
|
|
||||||
|
if (state->shader.ID != 0) {
|
||||||
|
shader_delete(&state->shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gl_context) {
|
||||||
|
SDL_GL_DestroyContext(gl_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window) {
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("Application quit and resources cleaned up");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user