From 56fa075716361197d6879eb431aef3c9a43de40c Mon Sep 17 00:00:00 2001 From: cwj3rcb Date: Sat, 13 Sep 2025 14:13:27 -0500 Subject: [PATCH] collision mechanics --- main.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/main.c b/main.c index a099035..69e159e 100644 --- a/main.c +++ b/main.c @@ -6,6 +6,7 @@ #include "stdlib.h" #include "stdint.h" #include "rlgl.h" +#include // for FLT_MAX //---------------------------------------------------------------------------------- // Defines and Macros @@ -36,6 +37,9 @@ #define FLOOR_EXTENT 25 #define TILE_SIZE 5.0f +#define PLAYER_RADIUS 0.5f +#define PLAYER_HEIGHT 1.8f + enum game_state { STATE_MENU, // 0 by default STATE_GAME, // 1 @@ -109,6 +113,9 @@ int Random(int x); static void RenderTrees(Master* g, Texture2D tree, Vector3 treepos, float treeSize); static void ApplyFogShaderToModel(Model *m, Shader fog); +static BoundingBox MakeModelWorldAABB(Model m, Vector3 pos, Vector3 scale, float yawDeg); +static BoundingBox PlayerAABB(Vector3 p); +static void ResolveAgainstBoxXZ(Master* g, BoundingBox box); //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -356,6 +363,12 @@ int main(void) bool crouching = IsKeyDown(KEY_LEFT_CONTROL); UpdateBody(&g, g.player.lookRotation.x, sideway, forward, IsKeyDown(KEY_SPACE), crouching); + // Build wall collider (scale must match your DrawModelEx scale) + BoundingBox wallBox = MakeModelWorldAABB(wall, wall_position, (Vector3){5,5,5}, wall_yaw); + + // Collide player vs wall (XZ slide) + ResolveAgainstBoxXZ(&g, wallBox); + float delta = GetFrameTime(); g.player.headLerp = Lerp(g.player.headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0f*delta); g.player.camera.position = (Vector3){ @@ -830,3 +843,105 @@ static void ApplyFogShaderToModel(Model *m, Shader fog) mat->shader = fog; } } + +// Build player's AABB from its position (XZ radius, fixed height) +static BoundingBox PlayerAABB(Vector3 p) +{ + BoundingBox bb; + bb.min = (Vector3){ p.x - PLAYER_RADIUS, p.y, p.z - PLAYER_RADIUS }; + bb.max = (Vector3){ p.x + PLAYER_RADIUS, p.y + PLAYER_HEIGHT, p.z + PLAYER_RADIUS }; + return bb; +} + +// Combine all mesh AABBs of a Model in LOCAL space +static BoundingBox ModelLocalAABB(Model m) +{ + BoundingBox out; + out.min = (Vector3){ FLT_MAX, FLT_MAX, FLT_MAX }; + out.max = (Vector3){ -FLT_MAX, -FLT_MAX, -FLT_MAX }; + + for (int i = 0; i < m.meshCount; ++i) { + BoundingBox b = GetMeshBoundingBox(m.meshes[i]); // local-space box of this mesh + + if (b.min.x < out.min.x) out.min.x = b.min.x; + if (b.min.y < out.min.y) out.min.y = b.min.y; + if (b.min.z < out.min.z) out.min.z = b.min.z; + + if (b.max.x > out.max.x) out.max.x = b.max.x; + if (b.max.y > out.max.y) out.max.y = b.max.y; + if (b.max.z > out.max.z) out.max.z = b.max.z; + } + return out; +} + +// Transform a BoundingBox by a matrix and return its world AABB +static BoundingBox TransformAABB(BoundingBox bb, Matrix M) +{ + Vector3 c[8] = { + {bb.min.x, bb.min.y, bb.min.z}, {bb.max.x, bb.min.y, bb.min.z}, + {bb.min.x, bb.max.y, bb.min.z}, {bb.max.x, bb.max.y, bb.min.z}, + {bb.min.x, bb.min.y, bb.max.z}, {bb.max.x, bb.min.y, bb.max.z}, + {bb.min.x, bb.max.y, bb.max.z}, {bb.max.x, bb.max.y, bb.max.z}, + }; + + BoundingBox out; + out.min = (Vector3){ FLT_MAX, FLT_MAX, FLT_MAX }; + out.max = (Vector3){ -FLT_MAX, -FLT_MAX, -FLT_MAX }; + + for (int i = 0; i < 8; ++i) { + Vector3 t = Vector3Transform(c[i], M); + if (t.x < out.min.x) out.min.x = t.x; + if (t.y < out.min.y) out.min.y = t.y; + if (t.z < out.min.z) out.min.z = t.z; + + if (t.x > out.max.x) out.max.x = t.x; + if (t.y > out.max.y) out.max.y = t.y; + if (t.z > out.max.z) out.max.z = t.z; + } + return out; +} + +// Build world AABB for a model using the same T*R*S you render with +static BoundingBox MakeModelWorldAABB(Model m, Vector3 pos, Vector3 scale, float yawDeg) +{ + BoundingBox local = ModelLocalAABB(m); + + Matrix S = MatrixScale(scale.x, scale.y, scale.z); + Matrix R = MatrixRotateY(DEG2RAD * yawDeg); + Matrix T = MatrixTranslate(pos.x, pos.y, pos.z); + + Matrix M = MatrixMultiply(MatrixMultiply(T, R), S); // T * R * S + + return TransformAABB(local, M); +} + +// Push player out of a box on the smallest X/Z overlap (slide) and zero that velocity axis +static void ResolveAgainstBoxXZ(Master* g, BoundingBox box) +{ + BoundingBox p = PlayerAABB(g->player.position); + + // Quick reject + if (!CheckCollisionBoxes(p, box)) return; + + // Overlaps on X and Z + float ox = fminf(p.max.x, box.max.x) - fmaxf(p.min.x, box.min.x); + float oz = fminf(p.max.z, box.max.z) - fmaxf(p.min.z, box.min.z); + if (ox <= 0 || oz <= 0) return; + + // Centers to know which way to push + float cxp = 0.5f * (p.min.x + p.max.x); + float czp = 0.5f * (p.min.z + p.max.z); + float cxb = 0.5f * (box.min.x + box.max.x); + float czb = 0.5f * (box.min.z + box.max.z); + + // Resolve along the smallest axis + if (ox < oz) { + float push = (cxp < cxb) ? -ox : ox; + g->player.position.x += push; + g->player.velocity.x = 0.0f; + } else { + float push = (czp < czb) ? -oz : oz; + g->player.position.z += push; + g->player.velocity.z = 0.0f; + } +}