diff options
Diffstat (limited to 'src/maths')
-rw-r--r-- | src/maths/maths.h | 157 | ||||
-rw-r--r-- | src/maths/maths_types.h | 10 | ||||
-rw-r--r-- | src/maths/primitives.h | 163 |
3 files changed, 323 insertions, 7 deletions
diff --git a/src/maths/maths.h b/src/maths/maths.h index 7352aeb..e0d39d7 100644 --- a/src/maths/maths.h +++ b/src/maths/maths.h @@ -9,12 +9,19 @@ #pragma once #include <math.h> +#include <stdio.h> #include "maths_types.h" +// --- Helpers +#define deg_to_rad(x) (x * 3.14 / 180.0) +#define min(a, b) (a < b ? a : b) +#define max(a, b) (a > b ? a : b) + // --- Vector Implementations // Dimension 3 static inline vec3 vec3_create(f32 x, f32 y, f32 z) { return (vec3){ x, y, z }; } +#define vec3(x, y, z) (vec3_create(x, y, z)) static inline vec3 vec3_add(vec3 a, vec3 b) { return (vec3){ a.x + b.x, a.y + b.y, a.z + b.z }; } static inline vec3 vec3_sub(vec3 a, vec3 b) { return (vec3){ a.x - b.x, a.y - b.y, a.z - b.z }; } static inline vec3 vec3_mult(vec3 a, f32 s) { return (vec3){ a.x * s, a.y * s, a.z * s }; } @@ -42,12 +49,86 @@ static inline vec3 vec3_cross(vec3 a, vec3 b) { #define VEC3_Z ((vec3){ .x = 0.0, .y = 0.0, .z = 1.0 }) #define VEC3_NEG_Z ((vec3){ .x = 0.0, .y = 0.0, .z = -1.0 }) +static inline void print_vec3(vec3 v) { printf("{ x: %f, y: %f, z: %f )\n", v.x, v.y, v.z); } + // TODO: Dimension 2 static inline vec2 vec2_create(f32 x, f32 y) { return (vec2){ x, y }; } // TODO: Dimension 4 +static inline vec4 vec4_create(f32 x, f32 y, f32 z, f32 w) { return (vec4){ x, y, z, w }; } +#define vec4(x, y, z, w) (vec4_create(x, y, z, w)) #define VEC4_ZERO ((vec4){ .x = 0.0, .y = 0.0, .z = 0.0, .w = 0.0 }) +// --- Quaternion Implementations + +static inline f32 quat_dot(quat a, quat b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } + +static inline quat quat_normalise(quat a) { + f32 length = sqrtf(quat_dot(a, a)); // same as len squared + + return (quat){ a.x / length, a.y / length, a.z / length, a.w / length }; +} + +static inline quat quat_ident() { return (quat){ .x = 0.0, .y = 0.0, .z = 0.0, .w = 1.0 }; } + +static quat quat_from_axis_angle(vec3 axis, f32 angle, bool normalize) { + const f32 half_angle = 0.5f * angle; + f32 s = sinf(half_angle); + f32 c = cosf(half_angle); + + quat q = (quat){ s * axis.x, s * axis.y, s * axis.z, c }; + if (normalize) { + return quat_normalise(q); + } + return q; +} + +// TODO: grok this. +static inline quat quat_slerp(quat a, quat b, f32 percentage) { + quat out_quaternion; + + quat q0 = quat_normalise(a); + quat q1 = quat_normalise(b); + + // Compute the cosine of the angle between the two vectors. + f32 dot = quat_dot(q0, q1); + + // If the dot product is negative, slerp won't take + // the shorter path. Note that v1 and -v1 are equivalent when + // the negation is applied to all four components. Fix by + // reversing one quaternion. + if (dot < 0.0f) { + q1.x = -q1.x; + q1.y = -q1.y; + q1.z = -q1.z; + q1.w = -q1.w; + dot = -dot; + } + + const f32 DOT_THRESHOLD = 0.9995f; + if (dot > DOT_THRESHOLD) { + // If the inputs are too close for comfort, linearly interpolate + // and normalize the result. + out_quaternion = + (quat){ q0.x + ((q1.x - q0.x) * percentage), q0.y + ((q1.y - q0.y) * percentage), + q0.z + ((q1.z - q0.z) * percentage), q0.w + ((q1.w - q0.w) * percentage) }; + + return quat_normalise(out_quaternion); + } + + // Since dot is in range [0, DOT_THRESHOLD], acos is safe + f32 theta_0 = cos(dot); // theta_0 = angle between input vectors + f32 theta = theta_0 * percentage; // theta = angle between v0 and result + f32 sin_theta = sin(theta); // compute this value only once + f32 sin_theta_0 = sin(theta_0); // compute this value only once + + f32 s0 = cos(theta) - dot * sin_theta / sin_theta_0; // == sin(theta_0 - theta) / sin(theta_0) + f32 s1 = sin_theta / sin_theta_0; + + return (quat){ (q0.x * s0) + (q1.x * s1), (q0.y * s0) + (q1.y * s1), (q0.z * s0) + (q1.z * s1), + (q0.w * s0) + (q1.w * s1) }; +} + // --- Matrix Implementations static inline mat4 mat4_ident() { @@ -70,6 +151,26 @@ static inline mat4 mat4_scale(f32 scale) { return out_matrix; } +// TODO: double check this +static inline mat4 mat4_rotation(quat rotation) { + mat4 out_matrix = mat4_ident(); + quat n = quat_normalise(rotation); + + out_matrix.data[0] = 1.0f - 2.0f * n.y * n.y - 2.0f * n.z * n.z; + out_matrix.data[1] = 2.0f * n.x * n.y - 2.0f * n.z * n.w; + out_matrix.data[2] = 2.0f * n.x * n.z + 2.0f * n.y * n.w; + + out_matrix.data[4] = 2.0f * n.x * n.y + 2.0f * n.z * n.w; + out_matrix.data[5] = 1.0f - 2.0f * n.x * n.x - 2.0f * n.z * n.z; + out_matrix.data[6] = 2.0f * n.y * n.z - 2.0f * n.x * n.w; + + out_matrix.data[8] = 2.0f * n.x * n.z - 2.0f * n.y * n.w; + out_matrix.data[9] = 2.0f * n.y * n.z + 2.0f * n.x * n.w; + out_matrix.data[10] = 1.0f - 2.0f * n.x * n.x - 2.0f * n.y * n.y; + + return out_matrix; +} + static inline mat4 mat4_mult(mat4 lhs, mat4 rhs) { mat4 out_matrix = mat4_ident(); @@ -89,6 +190,43 @@ static inline mat4 mat4_mult(mat4 lhs, mat4 rhs) { return out_matrix; } +static mat4 mat4_transposed(mat4 matrix) { + mat4 out_matrix = mat4_ident(); + out_matrix.data[0] = matrix.data[0]; + out_matrix.data[1] = matrix.data[4]; + out_matrix.data[2] = matrix.data[8]; + out_matrix.data[3] = matrix.data[12]; + out_matrix.data[4] = matrix.data[1]; + out_matrix.data[5] = matrix.data[5]; + out_matrix.data[6] = matrix.data[9]; + out_matrix.data[7] = matrix.data[13]; + out_matrix.data[8] = matrix.data[2]; + out_matrix.data[9] = matrix.data[6]; + out_matrix.data[10] = matrix.data[10]; + out_matrix.data[11] = matrix.data[14]; + out_matrix.data[12] = matrix.data[3]; + out_matrix.data[13] = matrix.data[7]; + out_matrix.data[14] = matrix.data[11]; + out_matrix.data[15] = matrix.data[15]; + return out_matrix; +} + +#if defined(CEL_REND_BACKEND_VULKAN) +/** @brief Creates a perspective projection matrix compatible with Vulkan */ +static inline mat4 mat4_perspective(f32 fov_radians, f32 aspect_ratio, f32 near_clip, + f32 far_clip) { + f32 half_tan_fov = tanf(fov_radians * 0.5f); + mat4 out_matrix = { .data = { 0 } }; + + out_matrix.data[0] = 1.0f / (aspect_ratio * half_tan_fov); + out_matrix.data[5] = -1.0f / half_tan_fov; // Flip Y-axis for Vulkan + out_matrix.data[10] = -((far_clip + near_clip) / (far_clip - near_clip)); + out_matrix.data[11] = -1.0f; + out_matrix.data[14] = -((2.0f * far_clip * near_clip) / (far_clip - near_clip)); + + return out_matrix; +} +#else /** @brief Creates a perspective projection matrix */ static inline mat4 mat4_perspective(f32 fov_radians, f32 aspect_ratio, f32 near_clip, f32 far_clip) { @@ -101,6 +239,7 @@ static inline mat4 mat4_perspective(f32 fov_radians, f32 aspect_ratio, f32 near_ out_matrix.data[14] = -((2.0f * far_clip * near_clip) / (far_clip - near_clip)); return out_matrix; } +#endif /** @brief Creates an orthographic projection matrix */ static inline mat4 mat4_orthographic(f32 left, f32 right, f32 bottom, f32 top, f32 near_clip, @@ -156,23 +295,23 @@ static inline mat4 mat4_look_at(vec3 position, vec3 target, vec3 up) { // ... -// --- Quaternion Implementations - // --- Transform Implementations #define TRANSFORM_DEFAULT \ ((transform){ .position = VEC3_ZERO, \ - .rotation = (quat){ .x = 0., .y = 0., .z = 0., .w = 0. }, \ + .rotation = (quat){ .x = 0., .y = 0., .z = 0., .w = 1. }, \ .scale = 1.0, \ .is_dirty = false }) static transform transform_create(vec3 pos, quat rot, f32 scale) { - return (transform){ .position = pos, .rotation = rot, .scale = scale, .is_dirty = false }; + return (transform){ .position = pos, .rotation = rot, .scale = scale, .is_dirty = true }; } static inline mat4 transform_to_mat(transform *tf) { - // TODO: rotation - return mat4_mult(mat4_translation(tf->position), mat4_scale(tf->scale)); + mat4 scale = mat4_scale(tf->scale); + mat4 rotation = mat4_rotation(tf->rotation); + mat4 translation = mat4_translation(tf->position); + return mat4_mult(translation, mat4_mult(rotation, scale)); } // --- Sizing asserts @@ -198,3 +337,9 @@ typedef struct u32x3 { }; } u32x3; #define u32x3(x, y, z) ((u32x3){ x, y, z }) + +typedef struct u32x2 { + u32 x; + u32 y; +} u32x2; +#define u32x2(x, y) ((u32x3){ x, y })
\ No newline at end of file diff --git a/src/maths/maths_types.h b/src/maths/maths_types.h index ba741b9..53cac55 100644 --- a/src/maths/maths_types.h +++ b/src/maths/maths_types.h @@ -60,4 +60,12 @@ typedef struct transform { quat rotation; f32 scale; bool is_dirty; -} transform;
\ No newline at end of file +} transform; + +typedef struct vec4i { + i32 x, y, z, w; +} vec4i; + +typedef struct vec4u { + u32 x, y, z, w; +} vec4u;
\ No newline at end of file diff --git a/src/maths/primitives.h b/src/maths/primitives.h new file mode 100644 index 0000000..ed52c8c --- /dev/null +++ b/src/maths/primitives.h @@ -0,0 +1,163 @@ +#pragma once + +#include <assert.h> +#include <stdlib.h> +#include "core.h" +#include "maths.h" +#include "render_types.h" + +static const vec3 BACK_BOT_LEFT = (vec3){ 0, 0, 0 }; +static const vec3 BACK_BOT_RIGHT = (vec3){ 1, 0, 0 }; +static const vec3 BACK_TOP_LEFT = (vec3){ 0, 1, 0 }; +static const vec3 BACK_TOP_RIGHT = (vec3){ 1, 1, 0 }; +static const vec3 FRONT_BOT_LEFT = (vec3){ 0, 0, 1 }; +static const vec3 FRONT_BOT_RIGHT = (vec3){ 1, 0, 1 }; +static const vec3 FRONT_TOP_LEFT = (vec3){ 0, 1, 1 }; +static const vec3 FRONT_TOP_RIGHT = (vec3){ 1, 1, 1 }; + +static mesh prim_cube_mesh_create() { + mesh cube = { 0 }; + cube.vertices = vertex_darray_new(36); + + // back faces + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); + + // front faces + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); + + // top faces + vertex_darray_push(cube.vertices, + (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); + vertex_darray_push(cube.vertices, + (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 0 } }); + + // bottom faces + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + + // right faces + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 1 } }); + + // left faces + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + + cube.indices_len = cube.vertices->len; + cube.indices = malloc(sizeof(u32) * cube.indices_len); + + for (u32 i = 0; i < cube.indices_len; i++) { + cube.indices[i] = i; + } + + cube.has_indices = true; + + return cube; +} + +/** @brief create a new model with the shape of a cube */ +static model_handle prim_cube_new(core* core) { + model model = { 0 }; + mesh cube = prim_cube_mesh_create(); + + mesh_darray_push(model.meshes, cube); + assert(mesh_darray_len(model.meshes) == 1); + + u32 index = (u32)model_darray_len(core->models); + model_darray_push_copy(core->models, &model); + return (model_handle){ .raw = index }; +}
\ No newline at end of file |