From a67ec7ef24f659b6ad4132768070ac3e3071f958 Mon Sep 17 00:00:00 2001 From: Brody Brooks Date: Wed, 19 Aug 2026 10:27:00 -0400 Subject: [PATCH] Added MathUtils class, reworked matrix4x4 and camera --- src/core/camera.vala | 198 +++++++++++++++++++++++++++++++++++++--- src/core/math.vala | 11 +++ src/core/matrix4x4.vala | 29 +++++- src/core/texture2d.vala | 2 +- src/core/vector2.vala | 2 +- src/core/vector3.vala | 4 +- src/meson.build | 1 + src/window.ui | 1 - 8 files changed, 229 insertions(+), 19 deletions(-) create mode 100644 src/core/math.vala diff --git a/src/core/camera.vala b/src/core/camera.vala index e819929..0fd036a 100644 --- a/src/core/camera.vala +++ b/src/core/camera.vala @@ -1,17 +1,189 @@ -public class Worldbuilder.Camera : Object { - /** - * The position of the camera - */ - public Vector3 position { get; set; } +using GL; +namespace Worldbuilder.Core { + /** - * The direction the camera is facing. + * Encapsulates a camera object. A camera contains a projection matrix and a view matrix. + * + * The main purpose of this class is to provide equivalents of legacy OpenGL functions + * removed in modern OpenGL. Advanced features like quaternion-based rotations are beyond + * the scope of this project. */ - public Vector3 direction { get; set; } - - public Camera (Vector3 position = Vector3 (), Vector3 direction = Vector3 (0, 0, -1)) { - this.position = position; - this.direction = direction; + public class Camera : Object { + private Matrix4x4 projection_matrix; + private Matrix4x4 view_matrix; + private Matrix4x4 result_matrix; + private Matrix4x4 total_matrix; + + /** + * Instantiates a camera object. + * + * The projection and view matrices are set to the identity matrix. + */ + public Camera() { + reset(); + } + + private void update() { + result_matrix = projection_matrix; + result_matrix.multiply(view_matrix); + } + + /** + * Resets both the projection matrix and the view matrix. + */ + public void reset() { + reset_projection(); + reset_view(); + } + + /** + * Resets the projection matrix, setting it to the identity matrix. + */ + public void reset_projection() { + projection_matrix = Matrix4x4.identity(); + update(); + } + + /** + * Resets the view matrix, setting it to the identity matrix. + */ + public void reset_view() { + view_matrix = Matrix4x4.identity(); + update(); + } + + /** + * Applies the camera to the next model object that will be drawn, by + * multiplying the current projection and view matrices by the model matrix + * passed to the function (which specifies the offset and rotation of the model + * relative to the camera), and binds the matrix data to the specified + * shader uniform variable. + * + * @param uniform_id The shader uniform variable to bind to + * @param model_matrix The model matrix + */ + public void apply(int uniform_id, ref Matrix4x4 model_matrix) { + total_matrix = result_matrix; + total_matrix.multiply(model_matrix); + glUniformMatrix4fv(uniform_id, 1, (GL.GLboolean)false, total_matrix.data); + } + + /** + * Sets up a perspective projection. The previous projection matrix is ignored and overwritten. + * + * Replicates the effects of the legacy ``glFrustum`` function within this camera. + * Refer to that function for the complete description of the projection parameters. + * + * @param left The coordinate of the left vertical clipping plane. + * @param right The coordinate of the right vertical clipping plane. + * @param bottom The coordinate of the bottom horizontal clipping plane. + * @param top The coordinate of the top horizontal clipping plane. + * @param near The coordinate of the near vertical clipping plane. Must be positive. + * @param far The coordinate of the far vertical clipping plane. Must be positive. + */ + public void set_frustum_projection( + float left, + float right, + float bottom, + float top, + float near, float far + ) { + projection_matrix = Matrix4x4.from_data( + 2f * near / (right - left), 0, (right + left) / (right - left), 0, + 0, 2f * near / (top - bottom), (top + bottom) / (top - bottom), 0, + 0, 0, -(far + near) / (far - near), -2f * far * near / (far - near), + 0, 0, -1, 0 + ); + + update(); + } + + /** + * Sets up a symmetrical perspective projection. The previous projection matrix is ignored and overwritten. + * + * Replicates the effects of the legacy ``gluPerspective`` function within this camera. + * Refer to that function for the complete description of the projection parameters. + * + * @param fovy_deg The field of view angle, in degrees, in the y direction. + * @param aspect The aspect ratio that determines the field of view in the x direction. + * The aspect ratio is the ratio of x (width) to y (height). + * @param near The coordinate of the near vertical clipping plane. Must be positive. + * @param far The coordinate of the far vertical clipping plane. Must be positive. + */ + public void set_perspective_projection(float fovy_deg, float aspect, float near, float far) { + var f = 1 / Math.tanf(MathUtils.DegToRad(fovy_deg / 2)); + + projection_matrix = Matrix4x4.from_data( + f / aspect, 0, 0, 0, + 0, f, 0, 0, + 0, 0, -(far + near) / (far - near), -2f * far * near / (far - near), + 0, 0, -1, 0 + ); + + update(); + } + + /** + * Sets up an orthogonal projection. The previous projection matrix is ignored and overwritten. + * + * Replicates the effects of the legacy ``glOrtho`` function within this camera. + * Refer to that function for the complete description of the projection parameters. + * + * @param left The coordinate of the left vertical clipping plane. + * @param right The coordinate of the right vertical clipping plane. + * @param bottom The coordinate of the bottom horizontal clipping plane. + * @param top The coordinate of the top horizontal clipping plane. + * @param near The coordinate of the near vertical clipping plane. + * @param far The coordinate of the far vertical clipping plane. + */ + public void set_ortho_projection( + float left, + float right, + float bottom, + float top, + float near, float far + ) { + projection_matrix = Matrix4x4.from_data( + 2f / (right - left), 0, 0, (right + left) / (right - left), + 0, 2f / (top - bottom), 0, (top + bottom) / (top - bottom), + 0, 0, -2f * far * near / (far - near), -(far + near) / (far - near), + 0, 0, 0, 1 + ); + + update(); + } + + /** + * Replicates the effects of the legacy ``gluLookAt`` function within this camera. + * Refer to that function for the complete description of how the camera is positioned. + * + * @param eye The 3D position of the camera + * @param center The 3D position of the point the camera is looking at + * @param up The up-direction + */ + public void look_at(ref Vector3 eye, ref Vector3 center, ref Vector3 up) { + Vector3 l = center; + l.subtract(eye); + l.normalized(); + + Vector3 uptemp = up; + uptemp.normalized(); + + Vector3 s = l.cross(uptemp); + Vector3 u = s.cross(l); + + view_matrix = Matrix4x4.from_data( + s.x, s.y, s.z, 0, + u.x, u.y, u.z, 0, + -l.x, -l.y, -l.z, 0, + 0, 0, 0, 1 + ); + + Vector3 translation = Vector3(); + translation.subtract(eye); + view_matrix = view_matrix.translate(translation.x, translation.y, translation.z); + update(); + } } -} - +} \ No newline at end of file diff --git a/src/core/math.vala b/src/core/math.vala new file mode 100644 index 0000000..3033933 --- /dev/null +++ b/src/core/math.vala @@ -0,0 +1,11 @@ + +namespace Worldbuilder.Core +{ + public class MathUtils : Object + { + public static float DegToRad(float deg) + { + return deg * (float)Math.PI / 180f; + } + } +} \ No newline at end of file diff --git a/src/core/matrix4x4.vala b/src/core/matrix4x4.vala index ee9fbf5..7cd81e3 100644 --- a/src/core/matrix4x4.vala +++ b/src/core/matrix4x4.vala @@ -1,7 +1,7 @@ /** * A 4-by-4 matrix useful in OpenGL matrix operations. */ -public struct Worldbuilder.Matrix4x4 +public struct Worldbuilder.Core.Matrix4x4 { public float data[4*4]; @@ -17,6 +17,33 @@ public struct Worldbuilder.Matrix4x4 this (1.0f); } + public Matrix4x4.from_data( + float a11, float a12, float a13, float a14, + float a21, float a22, float a23, float a24, + float a31, float a32, float a33, float a34, + float a41, float a42, float a43, float a44) + { + data[0] = a11; + data[1] = a21; + data[2] = a31; + data[3] = a41; + + data[4] = a12; + data[5] = a22; + data[6] = a32; + data[7] = a42; + + data[8] = a13; + data[9] = a23; + data[10] = a33; + data[11] = a43; + + data[12] = a14; + data[13] = a24; + data[14] = a34; + data[15] = a44; + } + /** * Construct a perspective transform. * diff --git a/src/core/texture2d.vala b/src/core/texture2d.vala index efe677c..fc3c3a2 100644 --- a/src/core/texture2d.vala +++ b/src/core/texture2d.vala @@ -1,4 +1,4 @@ -public class Worldbuilder.Texture2D : Object { +public class Worldbuilder.Core.Texture2D : Object { public string path { get; construct; } public GL.uint id { get; private set; } diff --git a/src/core/vector2.vala b/src/core/vector2.vala index 2b91ce6..921b0d3 100644 --- a/src/core/vector2.vala +++ b/src/core/vector2.vala @@ -1,4 +1,4 @@ -public struct Worldbuilder.Vector2 { +public struct Worldbuilder.Core.Vector2 { public float x; public float y; diff --git a/src/core/vector3.vala b/src/core/vector3.vala index cc7bde3..7e89b98 100644 --- a/src/core/vector3.vala +++ b/src/core/vector3.vala @@ -1,4 +1,5 @@ -public struct Worldbuilder.Vector3 { + +public struct Worldbuilder.Core.Vector3 { public float x; public float y; public float z; @@ -41,4 +42,3 @@ public struct Worldbuilder.Vector3 { return @"<$x,$y,$z>"; } } - diff --git a/src/meson.build b/src/meson.build index 875a442..6bd9fe6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -4,6 +4,7 @@ worldbuilder_sources = [ 'window.vala', 'scene_renderer.vala', + 'core/math.vala', 'core/vector3.vala', 'core/matrix4x4.vala', 'core/vector2.vala', diff --git a/src/window.ui b/src/window.ui index 188f374..0998ca6 100644 --- a/src/window.ui +++ b/src/window.ui @@ -12,7 +12,6 @@ - content Sidebar