first commit

This commit is contained in:
saarsena@gmail.com 2026-04-02 03:41:50 -04:00
commit 5c7d1905a9
25 changed files with 4034 additions and 0 deletions

11
include/components.hpp Normal file
View file

@ -0,0 +1,11 @@
/**
* @file components.hpp
* @brief Master include for all ECS components
*/
#pragma once
#include "components/transform.hpp"
#include "components/render.hpp"
#include "components/input.hpp"
#include "components/common.hpp"

View file

@ -0,0 +1,24 @@
/**
* @file common.hpp
* @brief Common/utility components
*/
#pragma once
#include <string>
struct Name {
std::string value;
};
// Tag component - empty struct marks entities for destruction
struct ToDestroy {};
struct Lifetime {
float remaining = 0.0f;
};
struct Health {
float current = 100.0f;
float max = 100.0f;
};

View file

@ -0,0 +1,21 @@
/**
* @file input.hpp
* @brief Input-related components
*/
#pragma once
struct PlayerControlled {
int player_id = 0;
};
struct InputState {
bool move_up = false;
bool move_down = false;
bool move_left = false;
bool move_right = false;
bool action_primary = false;
bool action_secondary = false;
float mouse_x = 0.0f;
float mouse_y = 0.0f;
};

View file

@ -0,0 +1,32 @@
/**
* @file render.hpp
* @brief Rendering-related components
*/
#pragma once
#include <SDL3/SDL.h>
#include <cstdint>
struct Color {
uint8_t r = 255;
uint8_t g = 255;
uint8_t b = 255;
uint8_t a = 255;
};
struct Sprite {
SDL_Texture* texture = nullptr;
SDL_FRect src_rect{};
int z_order = 0;
bool visible = true;
};
struct RectShape {
float width = 0.0f;
float height = 0.0f;
};
struct CircleShape {
float radius = 0.0f;
};

View file

@ -0,0 +1,25 @@
/**
* @file transform.hpp
* @brief Transform components for position, velocity, and rotation
*/
#pragma once
struct Position {
float x = 0.0f;
float y = 0.0f;
};
struct Velocity {
float x = 0.0f;
float y = 0.0f;
};
struct Rotation {
float angle = 0.0f;
};
struct Scale {
float x = 1.0f;
float y = 1.0f;
};

52
include/game.hpp Normal file
View file

@ -0,0 +1,52 @@
/**
* @file game.hpp
* @brief Main game state and context
*/
#pragma once
#include <SDL3/SDL.h>
#include <flecs.h>
struct WindowConfig {
const char *title = "RIDGE RACER 37";
int width = 1280;
int height = 720;
SDL_WindowFlags flags = 0;
};
struct PipelineState;
/**
* @brief Game context containing all core resources
*/
struct GameContext {
// SDL Resources
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
// FLECS World (C++ wrapper)
flecs::world ecs;
// Timing
uint64_t last_time = 0;
float delta_time = 0.0f;
// State
bool running = true;
bool paused = false;
// Window info
int window_width = 0;
int window_height = 0;
// Pipeline visualization
PipelineState *pipeline = nullptr;
};
bool game_init(GameContext &ctx, const WindowConfig &config);
void game_shutdown(GameContext &ctx);
void game_process_events(GameContext &ctx);
void game_update(GameContext &ctx);
void game_render(GameContext &ctx);
bool game_loop(GameContext &ctx);

129
include/math3d.hpp Normal file
View file

@ -0,0 +1,129 @@
/**
* @file math3d.hpp
* @brief Minimal 3D math library for render pipeline visualization
*/
#pragma once
#include <cmath>
#include <algorithm>
constexpr float PI = 3.14159265358979323846f;
struct Vec3 {
float x = 0, y = 0, z = 0;
Vec3 operator+(Vec3 o) const { return {x+o.x, y+o.y, z+o.z}; }
Vec3 operator-(Vec3 o) const { return {x-o.x, y-o.y, z-o.z}; }
Vec3 operator*(float s) const { return {x*s, y*s, z*s}; }
float dot(Vec3 o) const { return x*o.x + y*o.y + z*o.z; }
Vec3 cross(Vec3 o) const { return {y*o.z - z*o.y, z*o.x - x*o.z, x*o.y - y*o.x}; }
float length() const { return std::sqrt(x*x + y*y + z*z); }
Vec3 normalized() const {
float l = length();
return l > 0 ? Vec3{x/l, y/l, z/l} : Vec3{0, 0, 0};
}
};
inline Vec3 lerp(Vec3 a, Vec3 b, float t) {
return {a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t};
}
inline float smoothstep(float t) {
return t * t * (3.0f - 2.0f * t);
}
struct Vec4 {
float x = 0, y = 0, z = 0, w = 1;
Vec3 xyz() const { return {x, y, z}; }
Vec3 perspDiv() const {
return w != 0 ? Vec3{x/w, y/w, z/w} : Vec3{x, y, z};
}
};
struct Mat4 {
float m[4][4] = {};
static Mat4 identity() {
Mat4 r;
r.m[0][0] = r.m[1][1] = r.m[2][2] = r.m[3][3] = 1;
return r;
}
Vec4 operator*(Vec4 v) const {
return {
m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3]*v.w,
m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3]*v.w,
m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3]*v.w,
m[3][0]*v.x + m[3][1]*v.y + m[3][2]*v.z + m[3][3]*v.w,
};
}
Mat4 operator*(const Mat4& o) const {
Mat4 r;
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
for (int k = 0; k < 4; k++)
r.m[i][j] += m[i][k] * o.m[k][j];
return r;
}
static Mat4 translate(float x, float y, float z) {
Mat4 r = identity();
r.m[0][3] = x; r.m[1][3] = y; r.m[2][3] = z;
return r;
}
static Mat4 scale(float sx, float sy, float sz) {
Mat4 r = identity();
r.m[0][0] = sx; r.m[1][1] = sy; r.m[2][2] = sz;
return r;
}
static Mat4 rotateX(float angle) {
float c = std::cos(angle), s = std::sin(angle);
Mat4 r = identity();
r.m[1][1] = c; r.m[1][2] = -s;
r.m[2][1] = s; r.m[2][2] = c;
return r;
}
static Mat4 rotateY(float angle) {
float c = std::cos(angle), s = std::sin(angle);
Mat4 r = identity();
r.m[0][0] = c; r.m[0][2] = s;
r.m[2][0] = -s; r.m[2][2] = c;
return r;
}
static Mat4 rotateZ(float angle) {
float c = std::cos(angle), s = std::sin(angle);
Mat4 r = identity();
r.m[0][0] = c; r.m[0][1] = -s;
r.m[1][0] = s; r.m[1][1] = c;
return r;
}
// Standard OpenGL-style perspective (right-handed, -Z forward)
static Mat4 perspective(float fov, float aspect, float near, float far) {
float t = std::tan(fov * 0.5f);
Mat4 r;
r.m[0][0] = 1.0f / (aspect * t);
r.m[1][1] = 1.0f / t;
r.m[2][2] = -(far + near) / (far - near);
r.m[2][3] = -(2.0f * far * near) / (far - near);
r.m[3][2] = -1.0f;
return r;
}
// Standard lookAt (right-handed)
static Mat4 lookAt(Vec3 eye, Vec3 target, Vec3 up) {
Vec3 f = (target - eye).normalized();
Vec3 r = f.cross(up).normalized();
Vec3 u = r.cross(f);
Mat4 mat = identity();
mat.m[0][0] = r.x; mat.m[0][1] = r.y; mat.m[0][2] = r.z; mat.m[0][3] = -r.dot(eye);
mat.m[1][0] = u.x; mat.m[1][1] = u.y; mat.m[1][2] = u.z; mat.m[1][3] = -u.dot(eye);
mat.m[2][0] = -f.x; mat.m[2][1] = -f.y; mat.m[2][2] = -f.z; mat.m[2][3] = f.dot(eye);
return mat;
}
};

129
include/pipeline.hpp Normal file
View file

@ -0,0 +1,129 @@
/**
* @file pipeline.hpp
* @brief Render pipeline visualization state and interface
*/
#pragma once
#include "math3d.hpp"
#include <SDL3/SDL.h>
struct GameContext;
constexpr int NUM_CUBE_VERTS = 8;
constexpr int NUM_CUBE_EDGES = 12;
constexpr int NUM_TRI_VERTS = 3;
constexpr int NUM_TRI_EDGES = 3;
struct PipelineState {
int current_stage = 0; // 0-5
int prev_stage = 0;
float transition_t = 1.0f; // 0→1 animation progress (1 = complete)
float fade_alpha = 1.0f; // for 3D↔2D mode transitions
// Interpolated vertices for rendering during transitions
Vec3 display_verts[NUM_CUBE_VERTS];
// Vertex selection
int selected_vertex = -1; // -1 = none, 0-7 = selected vertex index
bool pending_click = false;
float click_x = 0, click_y = 0;
// Object transform parameters (user-adjustable)
float obj_rot_y = 0.0f;
float obj_pos_x = 2.0f;
float obj_pos_y = 1.0f;
float obj_pos_z = -3.0f;
float obj_scale = 1.0f;
bool auto_rotate = false;
// Display camera (orbit around scene for visualization)
float cam_orbit_pitch = 25.0f; // degrees
float cam_orbit_yaw = -30.0f; // degrees
float cam_distance = 10.0f;
// Pipeline camera (the one being visualized in the pipeline)
float pipe_cam_x = 0.0f;
float pipe_cam_y = 2.0f;
float pipe_cam_z = 5.0f;
float pipe_cam_fov = 1.0f; // radians (~57 degrees)
float pipe_cam_near = 0.1f;
float pipe_cam_far = 100.0f;
// Mouse drag state
bool mouse_down = false;
float mouse_start_x = 0, mouse_start_y = 0;
float orbit_start_pitch = 0, orbit_start_yaw = 0;
// Slider drag state
int dragging_slider = -1; // -1 = none, 0+ = slider index
bool show_sliders = true;
// First-person camera view (picture-in-picture)
bool show_first_person = false;
// Matrix color breakdown
bool show_matrix_breakdown = false;
// Vertex shader simulation
bool shader_mode = false;
float shader_amplitude = 0.3f;
float shader_frequency = 2.0f;
float shader_time = 0.0f;
// Help overlay
bool show_help = false;
// Clipping visualizer
bool show_clipping = false;
struct ClippedFace {
static constexpr int MAX_VERTS = 24;
Vec4 verts[MAX_VERTS];
int count = 0;
bool was_clipped = false;
};
ClippedFace clipped_faces[6];
// Depth buffer / Z-fighting demo
bool show_depth_viz = false;
bool show_zfight_plane = false;
float zfight_plane_z = -3.0f;
static constexpr int NUM_PLANE_VERTS = 4;
Vec4 plane_world[4];
Vec3 plane_screen[4];
Vec3 plane_display[4];
// Input flags (set per event, cleared after processing)
bool key_left = false;
bool key_right = false;
bool key_space = false;
bool key_r = false;
bool key_num[6] = {};
// Computed transforms
Mat4 model_matrix;
Mat4 view_matrix;
Mat4 proj_matrix;
// Transformed vertices at each pipeline stage (cube)
Vec4 verts_object[NUM_CUBE_VERTS];
Vec4 verts_world[NUM_CUBE_VERTS];
Vec4 verts_view[NUM_CUBE_VERTS];
Vec4 verts_clip[NUM_CUBE_VERTS];
Vec3 verts_ndc[NUM_CUBE_VERTS];
Vec3 verts_screen[NUM_CUBE_VERTS];
// Second shape: triangle
bool show_triangle = false;
Vec4 tri_object[NUM_TRI_VERTS];
Vec4 tri_world[NUM_TRI_VERTS];
Vec4 tri_view[NUM_TRI_VERTS];
Vec4 tri_clip[NUM_TRI_VERTS];
Vec3 tri_ndc[NUM_TRI_VERTS];
Vec3 tri_screen[NUM_TRI_VERTS];
Vec3 tri_display[NUM_TRI_VERTS];
};
void pipeline_handle_event(PipelineState& ps, const SDL_Event& event);
void pipeline_update(PipelineState& ps, GameContext& ctx);
void pipeline_render(PipelineState& ps, GameContext& ctx);

16
include/systems.hpp Normal file
View file

@ -0,0 +1,16 @@
/**
* @file systems.hpp
* @brief ECS System registration
*/
#pragma once
#include <flecs.h>
// Forward declaration
struct GameContext;
/**
* @brief Register all components and systems with the ECS world
*/
void register_systems(flecs::world& ecs, GameContext* ctx);