#ifndef _PIUMA_LIB_GEOMETRY_H_ #define _PIUMA_LIB_GEOMETRY_H_ #include "types.h" #include "math.h" // Rect union Rect { struct { f32 x, y; f32 w, h; }; struct { v2 position; // Usually, the top-left corner of the rectangle v2 size; }; }; inline bool is_inside(Rect rect, v2 p) { bool in_range_x = rect.x <= p.x && p.x <= (rect.x + rect.w); bool in_range_y = rect.y <= p.y && p.y <= (rect.y + rect.h); return in_range_x && in_range_y; } // @Feature: add Cube/Parallelepiped? Unlike Box, it has rotations // Box struct Box { v3 min; v3 max; }; inline bool is_inside(Box b, v3 p) { return (p.x > b.min.x && p.x < b.max.x) && (p.y > b.min.y && p.y < b.max.y) && (p.z > b.min.z && p.z < b.max.z); } inline bool overlaps(Box a, Box b) { if(a.min.x > b.max.x) // no overlap return false; if(a.max.x < b.min.x) // no overlap return false; if(a.min.y > b.max.y) // no overlap return false; if(a.max.y < b.min.y) // no overlap return false; if(a.min.z > b.max.z) // no overlap return false; if(a.max.z < b.min.z) // no overlap return false; return true; } inline Box box_from_point_cloud(v3 *points, u32 count) { if(count == 0) return Box{.min = {0,0,0}, .max = {0,0,0}}; Box box; box.min = points[0]; box.max = points[0]; for(u32 i = 1; i < count; i++) { v3 p = points[i]; box.min.x = minimum(box.min.x, p.x); box.min.y = minimum(box.min.y, p.y); box.min.z = minimum(box.min.z, p.z); box.max.x = maximum(box.max.x, p.x); box.max.y = maximum(box.max.y, p.y); box.max.z = maximum(box.max.z, p.z); } return box; } inline v3 box_closest_point(Box b, v3 point); inline f32 box_SDF(Box b, v3 point, v3* res_closest = NULL) { v3 closest = box_closest_point(b, point); f32 sign = -1 * is_inside(b, point); if(res_closest) *res_closest = closest; return sign * distance(closest, point); } // Ray struct Ray { v3 position; v3 direction; }; // Circle // @Cleanup: Should Circle be merged with Sphere? struct Circle { v2 center; f32 radius; }; inline bool is_inside(Circle c, v2 p) { v2 v = p - c.center; return dot(v, v) <= square(c.radius); } // Sphere struct Sphere { v3 center; f32 radius; }; inline bool is_inside(Sphere s, v3 p) { v3 v = p - s.center; return dot(v, v) <= square(s.radius); // distance² <= radius² } inline f32 sphere_SDF(Sphere s, v3 point) { return distance(s.center, point) - s.radius; } // Segment struct Segment { v3 a; v3 b; }; // Plane struct Plane { v3 normal; f32 offset; }; inline f32 plane_SDF(Plane plane, v3 point) { f32 projected = dot(point, plane.normal); return plane.offset - projected; } // Projections inline f32 project_point_on_vector(v3 vector, v3 point) { return dot(vector, point); } inline v3 project_point_on_plane(Plane plane, v3 point) { f32 distance = plane_SDF(plane, point); return point - plane.normal * distance; } // Closest point inline v3 segment_closest_point(Segment seg, v3 point) { v3 ab = seg.b - seg.a; v3 ap = point - seg.a; f32 u = dot(ab, ap); return seg.a + ab * clamp(0.0, 1.0, u); } inline v3 box_closest_point(Box b, v3 point) { v3 closest; // Closest point on the box is the one with coords closer to the ones of the point, // so for each axis we can clamp to the nearest point of the border. f32 dx1 = abs(b.min.x - point.x); f32 dx2 = abs(b.max.x - point.x); closest.x = (dx1 < dx2) ? b.min.x : b.max.x; f32 dy1 = abs(b.min.y - point.y); f32 dy2 = abs(b.max.y - point.y); closest.y = (dy1 < dy2) ? b.min.y : b.max.y; f32 dz1 = abs(b.min.z - point.z); f32 dz2 = abs(b.max.z - point.z); closest.z = (dz1 < dz2) ? b.min.z : b.max.z; return closest; } // Triangles functions inline v3 triangle_normal(v3 a, v3 b, v3 c) { v3 ba = b - a; v3 ca = c - a; v3 orthogonal = cross(ba, ca); return normalize(orthogonal); } // Transformations (all angles in radians) inline m4 rotation_x(f32 angle) { f32 c = cos(angle); f32 s = sin(angle); m4 result = m4_identity; result.E[1][1] = c; result.E[1][2] = -s; result.E[2][1] = s; result.E[2][2] = c; return result; } inline m4 rotation_y(f32 angle) { f32 c = cos(angle); f32 s = sin(angle); m4 result = m4_identity; result.E[0][0] = c; result.E[0][2] = s; result.E[2][0] = -s; result.E[2][2] = c; return result; } inline m4 rotation_z(f32 angle) { f32 c = cos(angle); f32 s = sin(angle); m4 result = m4_identity; result.E[0][0] = c; result.E[0][1] = -s; result.E[1][0] = s; result.E[1][1] = c; return result; } inline m4 rotation(f32 x, f32 y, f32 z) { return rotation_z(z) * rotation_y(y) * rotation_x(x); } inline m4 rotation_v3(v3 angle) { return rotation(angle.x, angle.y, angle.z); } inline m4 translation(f32 x, f32 y, f32 z) { m4 result = m4_identity; result.E[0][3] = x; result.E[1][3] = y; result.E[2][3] = z; return result; } inline m4 translation_v3(v3 t) { return translation(t.x, t.y, t.z); } inline m4 scale(f32 x, f32 y, f32 z) { m4 result = m4_zero; result.E[0][0] = x; result.E[1][1] = y; result.E[2][2] = z; result.E[3][3] = 1.0; return result; } inline m4 scale_v3(v3 factor) { return scale(factor.x, factor.y, factor.z); } inline m4 scale(f32 factor) { return scale(factor, factor, factor); } // Other geometric algebra inline void compute_basis(v3 a, v3 *b, v3 *c) { // from https://box2d.org/posts/2014/02/computing-a-basis/ if(abs(a.x) >= 0.57735f) *b = {a.y, -a.x, 0.0f}; else *b = {0.0f, a.z, -a.y}; *b = normalize(*b); *c = cross(a, *b); } // Primitives // Pass array of 8 elements to fill with coordinates inline void build_cube_vertices(v3 *vertices) { for(int x = 0; x < 2; x++) for(int y = 0; y < 2; y++) for(int z = 0; z < 2; z++) vertices[x*2*2 + y*2 + z] = v3{.x = x - 0.5f, .y = y - 0.5f, .z = z - 0.5f}; } #endif