This commit is contained in:
vfs committed 2026-04-28 19:46:41 +02:00
1 parent a0b7e4c0d8
commit 6883d103ff
36 files changed
+2736 -1428

No files matched your search

+637 -3
View File
@@ -1,3 +1,637 @@
//
// Created by Vicente Ferrari Smith on 27.02.26.
//
/*
See LICENSE folder for this sample’s licensing information.
Abstract:
Implementation of vector, matrix, and quaternion math utility functions useful for 3D graphics
rendering with Metal
Metal uses column-major matrices and column-vector inputs.
linearIndex cr example with reference elements
0 4 8 12 00 10 20 30 sx 10 20 tx
1 5 9 13 --> 01 11 21 31 --> 01 sy 21 ty
2 6 10 14 02 12 22 32 02 12 sz tz
3 7 11 15 03 13 23 33 03 13 1/d 33
The "cr" names are for <column><row>
*/
#include "AAPLMathUtilities.h"
#include <assert.h>
#include <stdlib.h>
uint32_t seed_lo, seed_hi;
static float inline F16ToF32(const __fp16 *address) {
return *address;
}
float AAPL_SIMD_OVERLOAD float32_from_float16(uint16_t i) {
return F16ToF32((__fp16 *)&i);
}
static inline void F32ToF16(float F32, __fp16 *F16Ptr) {
*F16Ptr = F32;
}
uint16_t AAPL_SIMD_OVERLOAD float16_from_float32(float f) {
uint16_t f16;
F32ToF16(f, (__fp16 *)&f16);
return f16;
}
vector_float3 AAPL_SIMD_OVERLOAD generate_random_vector(float min, float max)
{
vector_float3 rand;
float range = max - min;
rand.x = ((double)random() / (double) (0x7FFFFFFF)) * range + min;
rand.y = ((double)random() / (double) (0x7FFFFFFF)) * range + min;
rand.z = ((double)random() / (double) (0x7FFFFFFF)) * range + min;
return rand;
}
void AAPL_SIMD_OVERLOAD seedRand(uint32_t seed) {
seed_lo = seed; seed_hi = ~seed;
}
int32_t AAPL_SIMD_OVERLOAD randi(void) {
seed_hi = (seed_hi<<16) + (seed_hi>>16);
seed_hi += seed_lo; seed_lo += seed_hi;
return seed_hi;
}
float AAPL_SIMD_OVERLOAD randf(float x) {
return (x * randi() / (float)0x7FFFFFFF);
}
float AAPL_SIMD_OVERLOAD degrees_from_radians(float radians) {
return (radians / M_PI) * 180;
}
float AAPL_SIMD_OVERLOAD radians_from_degrees(float degrees) {
return (degrees / 180) * M_PI;
}
static vector_float3 AAPL_SIMD_OVERLOAD vector_make(float x, float y, float z) {
return (vector_float3){ x, y, z };
}
vector_float3 AAPL_SIMD_OVERLOAD vector_lerp(vector_float3 v0, vector_float3 v1, float t) {
return ((1 - t) * v0) + (t * v1);
}
vector_float4 AAPL_SIMD_OVERLOAD vector_lerp(vector_float4 v0, vector_float4 v1, float t) {
return ((1 - t) * v0) + (t * v1);
}
//------------------------------------------------------------------------------
// matrix_make_rows takes input data with rows of elements.
// This way, the calling code matrix data can look like the rows
// of a matrix made for transforming column vectors.
// Indices are m<column><row>
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix_make_rows(
float m00, float m10, float m20,
float m01, float m11, float m21,
float m02, float m12, float m22) {
return (matrix_float3x3){ {
{ m00, m01, m02 }, // each line here provides column data
{ m10, m11, m12 },
{ m20, m21, m22 } } };
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_make_rows(
float m00, float m10, float m20, float m30,
float m01, float m11, float m21, float m31,
float m02, float m12, float m22, float m32,
float m03, float m13, float m23, float m33) {
return (matrix_float4x4){ {
{ m00, m01, m02, m03 }, // each line here provides column data
{ m10, m11, m12, m13 },
{ m20, m21, m22, m23 },
{ m30, m31, m32, m33 } } };
}
// each arg is a column vector
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix_make_columns(
vector_float3 col0,
vector_float3 col1,
vector_float3 col2) {
return (matrix_float3x3){ col0, col1, col2 };
}
// each arg is a column vector
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_make_columns(
vector_float4 col0,
vector_float4 col1,
vector_float4 col2,
vector_float4 col3) {
return (matrix_float4x4){ col0, col1, col2, col3 };
}
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_from_quaternion(quaternion_float q) {
float xx = q.x * q.x;
float xy = q.x * q.y;
float xz = q.x * q.z;
float xw = q.x * q.w;
float yy = q.y * q.y;
float yz = q.y * q.z;
float yw = q.y * q.w;
float zz = q.z * q.z;
float zw = q.z * q.w;
// indices are m<column><row>
float m00 = 1 - 2 * (yy + zz);
float m10 = 2 * (xy - zw);
float m20 = 2 * (xz + yw);
float m01 = 2 * (xy + zw);
float m11 = 1 - 2 * (xx + zz);
float m21 = 2 * (yz - xw);
float m02 = 2 * (xz - yw);
float m12 = 2 * (yz + xw);
float m22 = 1 - 2 * (xx + yy);
return matrix_make_rows(m00, m10, m20,
m01, m11, m21,
m02, m12, m22);
}
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_rotation(float radians, vector_float3 axis) {
axis = vector_normalize(axis);
float ct = cosf(radians);
float st = sinf(radians);
float ci = 1 - ct;
float x = axis.x, y = axis.y, z = axis.z;
return matrix_make_rows( ct + x * x * ci, x * y * ci - z * st, x * z * ci + y * st,
y * x * ci + z * st, ct + y * y * ci, y * z * ci - x * st,
z * x * ci - y * st, z * y * ci + x * st, ct + z * z * ci );
}
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_rotation(float radians, float x, float y, float z) {
return matrix3x3_rotation(radians, vector_make(x, y, z));
}
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_scale(float sx, float sy, float sz) {
return matrix_make_rows(sx, 0, 0,
0, sy, 0,
0, 0, sz);
}
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_scale(vector_float3 s) {
return matrix_make_rows(s.x, 0, 0,
0, s.y, 0,
0, 0, s.z);
}
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_upper_left(matrix_float4x4 m) {
vector_float3 x = m.columns[0].xyz;
vector_float3 y = m.columns[1].xyz;
vector_float3 z = m.columns[2].xyz;
return matrix_make_columns(x, y, z);
}
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix_inverse_transpose(matrix_float3x3 m) {
return matrix_invert(matrix_transpose(m));
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_from_quaternion(quaternion_float q) {
float xx = q.x * q.x;
float xy = q.x * q.y;
float xz = q.x * q.z;
float xw = q.x * q.w;
float yy = q.y * q.y;
float yz = q.y * q.z;
float yw = q.y * q.w;
float zz = q.z * q.z;
float zw = q.z * q.w;
// indices are m<column><row>
float m00 = 1 - 2 * (yy + zz);
float m10 = 2 * (xy - zw);
float m20 = 2 * (xz + yw);
float m01 = 2 * (xy + zw);
float m11 = 1 - 2 * (xx + zz);
float m21 = 2 * (yz - xw);
float m02 = 2 * (xz - yw);
float m12 = 2 * (yz + xw);
float m22 = 1 - 2 * (xx + yy);
matrix_float4x4 matrix = matrix_make_rows(m00, m10, m20, 0,
m01, m11, m21, 0,
m02, m12, m22, 0,
0, 0, 0, 1);
return matrix;
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_rotation(float radians, vector_float3 axis) {
axis = vector_normalize(axis);
float ct = cosf(radians);
float st = sinf(radians);
float ci = 1 - ct;
float x = axis.x, y = axis.y, z = axis.z;
return matrix_make_rows(
ct + x * x * ci, x * y * ci - z * st, x * z * ci + y * st, 0,
y * x * ci + z * st, ct + y * y * ci, y * z * ci - x * st, 0,
z * x * ci - y * st, z * y * ci + x * st, ct + z * z * ci, 0,
0, 0, 0, 1);
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_rotation(float radians, float x, float y, float z) {
return matrix4x4_rotation(radians, vector_make(x, y, z));
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_identity(void) {
return matrix_make_rows(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_scale(float sx, float sy, float sz) {
return matrix_make_rows(sx, 0, 0, 0,
0, sy, 0, 0,
0, 0, sz, 0,
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_scale(vector_float3 s) {
return matrix_make_rows(s.x, 0, 0, 0,
0, s.y, 0, 0,
0, 0, s.z, 0,
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_translation(float tx, float ty, float tz) {
return matrix_make_rows(1, 0, 0, tx,
0, 1, 0, ty,
0, 0, 1, tz,
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_translation(vector_float3 t) {
return matrix_make_rows(1, 0, 0, t.x,
0, 1, 0, t.y,
0, 0, 1, t.z,
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_scale_translation(vector_float3 s, vector_float3 t) {
return matrix_make_rows(s.x, 0, 0, t.x,
0, s.y, 0, t.y,
0, 0, s.z, t.z,
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_look_at_left_hand(vector_float3 eye,
vector_float3 target,
vector_float3 up) {
vector_float3 z = vector_normalize(target - eye);
vector_float3 x = vector_normalize(vector_cross(up, z));
vector_float3 y = vector_cross(z, x);
vector_float3 t = vector_make(-vector_dot(x, eye), -vector_dot(y, eye), -vector_dot(z, eye));
return matrix_make_rows(x.x, x.y, x.z, t.x,
y.x, y.y, y.z, t.y,
z.x, z.y, z.z, t.z,
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_look_at_left_hand(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ) {
vector_float3 eye = vector_make(eyeX, eyeY, eyeZ);
vector_float3 center = vector_make(centerX, centerY, centerZ);
vector_float3 up = vector_make(upX, upY, upZ);
return matrix_look_at_left_hand(eye, center, up);
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_look_at_right_hand(vector_float3 eye,
vector_float3 target,
vector_float3 up) {
vector_float3 z = vector_normalize(eye - target);
vector_float3 x = vector_normalize(vector_cross(up, z));
vector_float3 y = vector_cross(z, x);
vector_float3 t = vector_make(-vector_dot(x, eye), -vector_dot(y, eye), -vector_dot(z, eye));
return matrix_make_rows(x.x, x.y, x.z, t.x,
y.x, y.y, y.z, t.y,
z.x, z.y, z.z, t.z,
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_look_at_right_hand(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ) {
vector_float3 eye = vector_make(eyeX, eyeY, eyeZ);
vector_float3 center = vector_make(centerX, centerY, centerZ);
vector_float3 up = vector_make(upX, upY, upZ);
return matrix_look_at_right_hand(eye, center, up);
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_ortho_left_hand(float left, float right, float bottom, float top, float nearZ, float farZ) {
return matrix_make_rows(
2 / (right - left), 0, 0, (left + right) / (left - right),
0, 2 / (top - bottom), 0, (top + bottom) / (bottom - top),
0, 0, 1 / (farZ - nearZ), nearZ / (nearZ - farZ),
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_ortho_right_hand(float left, float right, float bottom, float top, float nearZ, float farZ) {
return matrix_make_rows(
2 / (right - left), 0, 0, (left + right) / (left - right),
0, 2 / (top - bottom), 0, (top + bottom) / (bottom - top),
0, 0, -1 / (farZ - nearZ), nearZ / (nearZ - farZ),
0, 0, 0, 1 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_perspective_left_hand(float fovyRadians, float aspect, float nearZ, float farZ) {
float ys = 1 / tanf(fovyRadians * 0.5);
float xs = ys / aspect;
float zs = farZ / (farZ - nearZ);
return matrix_make_rows(xs, 0, 0, 0,
0, ys, 0, 0,
0, 0, zs, -nearZ * zs,
0, 0, 1, 0 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_perspective_right_hand(float fovyRadians, float aspect, float nearZ, float farZ) {
float ys = 1 / tanf(fovyRadians * 0.5);
float xs = ys / aspect;
float zs = farZ / (nearZ - farZ);
return matrix_make_rows(xs, 0, 0, 0,
0, ys, 0, 0,
0, 0, zs, nearZ * zs,
0, 0, -1, 0 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_perspective_frustum_right_hand(float l, float r, float b, float t, float n, float f) {
return matrix_make_rows(
2 * n / (r - l), 0, (r + l) / (r - l), 0,
0, 2 * n / (t - b), (t + b) / (t - b), 0,
0, 0, -f / (f - n), -f * n / (f - n),
0, 0, -1, 0 );
}
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_inverse_transpose(matrix_float4x4 m) {
return matrix_invert(matrix_transpose(m));
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion(float x, float y, float z, float w) {
return (quaternion_float){ x, y, z, w };
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion(vector_float3 v, float w) {
return (quaternion_float){ v.x, v.y, v.z, w };
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_identity() {
return quaternion(0, 0, 0, 1);
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_axis_angle(vector_float3 axis, float radians) {
float t = radians * 0.5;
return quaternion(axis.x * sinf(t), axis.y * sinf(t), axis.z * sinf(t), cosf(t));
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_euler(vector_float3 euler) {
quaternion_float q;
float cx = cosf(euler.x / 2.f);
float cy = cosf(euler.y / 2.f);
float cz = cosf(euler.z / 2.f);
float sx = sinf(euler.x / 2.f);
float sy = sinf(euler.y / 2.f);
float sz = sinf(euler.z / 2.f);
q.w = cx * cy * cz + sx * sy * sz;
q.x = sx * cy * cz - cx * sy * sz;
q.y = cx * sy * cz + sx * cy * sz;
q.z = cx * cy * sz - sx * sy * cz;
return q;
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion(matrix_float3x3 m) {
float m00 = m.columns[0].x;
float m11 = m.columns[1].y;
float m22 = m.columns[2].z;
float x = sqrtf(1 + m00 - m11 - m22) * 0.5;
float y = sqrtf(1 - m00 + m11 - m22) * 0.5;
float z = sqrtf(1 - m00 - m11 + m22) * 0.5;
float w = sqrtf(1 + m00 + m11 + m22) * 0.5;
return quaternion(x, y, z, w);
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion(matrix_float4x4 m) {
return quaternion(matrix3x3_upper_left(m));
}
float AAPL_SIMD_OVERLOAD quaternion_length(quaternion_float q) {
// return sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
return vector_length(q);
}
float AAPL_SIMD_OVERLOAD quaternion_length_squared(quaternion_float q) {
// return q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
return vector_length_squared(q);
}
vector_float3 AAPL_SIMD_OVERLOAD quaternion_axis(quaternion_float q) {
// This query doesn't make sense if w > 1, but we do our best by
// forcing q to be a unit quaternion if it obviously isn't
if (q.w > 1.0)
{
q = quaternion_normalize(q);
}
float axisLen = sqrtf(1 - q.w * q.w);
if (axisLen < 1e-5)
{
// At lengths this small, direction is arbitrary
return vector_make(1, 0, 0);
}
else
{
return vector_make(q.x / axisLen, q.y / axisLen, q.z / axisLen);
}
}
float AAPL_SIMD_OVERLOAD quaternion_angle(quaternion_float q) {
return 2 * acosf(q.w);
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_normalize(quaternion_float q) {
// return q / quaternion_length(q);
return vector_normalize(q);
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_inverse(quaternion_float q) {
return quaternion_conjugate(q) / quaternion_length_squared(q);
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_conjugate(quaternion_float q) {
return quaternion(-q.x, -q.y, -q.z, q.w);
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_multiply(quaternion_float q0, quaternion_float q1) {
quaternion_float q;
q.x = q0.w*q1.x + q0.x*q1.w + q0.y*q1.z - q0.z*q1.y;
q.y = q0.w*q1.y - q0.x*q1.z + q0.y*q1.w + q0.z*q1.x;
q.z = q0.w*q1.z + q0.x*q1.y - q0.y*q1.x + q0.z*q1.w;
q.w = q0.w*q1.w - q0.x*q1.x - q0.y*q1.y - q0.z*q1.z;
return q;
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_slerp(quaternion_float q0, quaternion_float q1, float t) {
quaternion_float q;
float cosHalfTheta = vector_dot(q0, q1);
if (fabs(cosHalfTheta) >= 1.f) ///q0=q1 or q0=q1
{
return q0;
}
float halfTheta = acosf(cosHalfTheta);
float sinHalfTheta = sqrtf(1.f - cosHalfTheta * cosHalfTheta);
if (fabs(sinHalfTheta) < 0.001f)
{ // q0 & q1 180 degrees not defined
return q0*0.5f + q1*0.5f;
}
float srcWeight = sin((1 - t) * halfTheta) / sinHalfTheta;
float dstWeight = sin(t * halfTheta) / sinHalfTheta;
q = srcWeight*q0 + dstWeight*q1;
return q;
}
vector_float3 AAPL_SIMD_OVERLOAD quaternion_rotate_vector(quaternion_float q, vector_float3 v) {
vector_float3 qp = vector_make(q.x, q.y, q.z);
float w = q.w;
return 2 * vector_dot(qp, v) * qp +
((w * w) - vector_dot(qp, qp)) * v +
2 * w * vector_cross(qp, v);
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_matrix3x3(matrix_float3x3 m)
{
quaternion_float q;
float trace = 1 + m.columns[0][0] + m.columns[1][1] + m.columns[2][2];
if(trace > 0)
{
float diagonal = sqrt(trace) * 2.0;
q.x = (m.columns[2][1] - m.columns[1][2]) / diagonal;
q.y = (m.columns[0][2] - m.columns[2][0]) / diagonal;
q.z = (m.columns[1][0] - m.columns[0][1]) / diagonal;
q.w = diagonal / 4.0;
} else if ((m.columns[0][0] > m.columns[1][1] ) &&
(m.columns[0][0] > m.columns[2][2])) {
float diagonal = sqrt( 1.0 + m.columns[0][0] - m.columns[1][1] - m.columns[2][2] ) * 2.0;
q.x = diagonal / 4.0;
q.y = (m.columns[0][1] + m.columns[1][0]) / diagonal;
q.z = (m.columns[0][2] + m.columns[2][0]) / diagonal;
q.w = (m.columns[2][1] - m.columns[1][2]) / diagonal;
} else if ( m.columns[1][1] > m.columns[2][2]) {
float diagonal = sqrt(1.0 + m.columns[1][1] - m.columns[0][0] - m.columns[2][2]) * 2.0;
q.x = (m.columns[0][1] + m.columns[1][0]) / diagonal;
q.y = diagonal / 4.0;
q.z = (m.columns[1][2] + m.columns[2][1]) / diagonal;
q.w = (m.columns[0][2] - m.columns[2][0]) / diagonal;
} else {
float diagonal = sqrt(1.0 + m.columns[2][2] - m.columns[0][0] - m.columns[1][1]) * 2.0;
q.x = (m.columns[0][2] + m.columns[2][0]) / diagonal;
q.y = (m.columns[1][2] + m.columns[2][1]) / diagonal;
q.z = diagonal / 4.0;
q.w = (m.columns[1][0] - m.columns[0][1]) / diagonal;
}
q = quaternion_normalize(q);
return q;
}
static inline quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_direction_vectors(vector_float3 forward, vector_float3 up, int right_handed) {
forward = vector_normalize(forward);
up = vector_normalize(up);
vector_float3 side = vector_normalize(vector_cross(up, forward));
matrix_float3x3 m = { side, up, forward };
quaternion_float q = quaternion_from_matrix3x3(m);
if(right_handed) {
q = q.yxwz;
q.xw = -q.xw;
}
q = vector_normalize(q);
return q;
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_direction_vectors_right_hand(vector_float3 forward, vector_float3 up) {
return quaternion_from_direction_vectors(forward, up, 1);
}
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_direction_vectors_left_hand(vector_float3 forward, vector_float3 up) {
return quaternion_from_direction_vectors(forward, up, 0);
}
vector_float3 AAPL_SIMD_OVERLOAD forward_direction_vector_from_quaternion(quaternion_float q) {
vector_float3 direction;
direction.x = 2.0 * (q.x*q.z - q.w*q.y);
direction.y = 2.0 * (q.y*q.z + q.w*q.x);
direction.z = 1.0 - 2.0 * ((q.x * q.x) + (q.y * q.y));
direction = vector_normalize(direction);
return direction;
}
vector_float3 AAPL_SIMD_OVERLOAD up_direction_vector_from_quaternion(quaternion_float q) {
vector_float3 direction;
direction.x = 2.0 * (q.x*q.y + q.w*q.z);
direction.y = 1.0 - 2.0 * (q.x*q.x + q.z*q.z);
direction.z = 2.0 * (q.y*q.z - q.w*q.x);
direction = vector_normalize(direction);
// Negate for a right-handed coordinate system
return direction;
}
vector_float3 AAPL_SIMD_OVERLOAD right_direction_vector_from_quaternion(quaternion_float q) {
vector_float3 direction;
direction.x = 1.0 - 2.0 * (q.y * q.y + q.z * q.z);
direction.y = 2.0 * (q.x * q.y - q.w * q.z);
direction.z = 2.0 * (q.x * q.z + q.w * q.y);
direction = vector_normalize(direction);
// Negate for a right-handed coordinate system
return direction;
}
+264 -6
View File
@@ -1,8 +1,266 @@
//
// Created by Vicente Ferrari Smith on 27.02.26.
//
/*
See LICENSE folder for this sample’s licensing information.
#ifndef V_AAPLMATHUTILITIES_H
#define V_AAPLMATHUTILITIES_H
Abstract:
Header for vector, matrix, and quaternion math utility functions useful for 3D graphics rendering.
*/
#endif //V_AAPLMATHUTILITIES_H
#include <stdlib.h>
#include <simd/simd.h>
// Because these are common methods, allow other libraries to overload their implementation.
#define AAPL_SIMD_OVERLOAD __attribute__((__overloadable__))
/// A single-precision quaternion type.
typedef vector_float4 quaternion_float;
/// Given a uint16_t encoded as a 16-bit float, returns a 32-bit float.
float AAPL_SIMD_OVERLOAD float32_from_float16(uint16_t i);
// Given a 32-bit float, returns a uint16_t encoded as a 16-bit float.
uint16_t AAPL_SIMD_OVERLOAD float16_from_float32(float f);
/// Returns the number of degrees in the specified number of radians.
float AAPL_SIMD_OVERLOAD degrees_from_radians(float radians);
/// Returns the number of radians in the specified number of degrees.
float AAPL_SIMD_OVERLOAD radians_from_degrees(float degrees);
// Generates a random float value inside the given range.
inline static float AAPL_SIMD_OVERLOAD random_float(float min, float max)
{
return (((double)random()/RAND_MAX) * (max-min)) + min;
}
/// Generate a random three-component vector with values between min and max.
vector_float3 AAPL_SIMD_OVERLOAD generate_random_vector(float min, float max);
/// Fast random seed.
void AAPL_SIMD_OVERLOAD seedRand(uint32_t seed);
/// Fast integer random.
int32_t AAPL_SIMD_OVERLOAD randi(void);
/// Fast floating-point random.
float AAPL_SIMD_OVERLOAD randf(float x);
/// Returns a vector that is linearly interpolated between the two given vectors.
vector_float3 AAPL_SIMD_OVERLOAD vector_lerp(vector_float3 v0, vector_float3 v1, float t);
/// Returns a vector that is linearly interpolated between the two given vectors.
vector_float4 AAPL_SIMD_OVERLOAD vector_lerp(vector_float4 v0, vector_float4 v1, float t);
/// Converts a unit-norm quaternion into its corresponding rotation matrix.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_from_quaternion(quaternion_float q);
/// Constructs a matrix_float3x3 from three rows of three columns with float values.
/// Indices are m<column><row>.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix_make_rows(float m00, float m10, float m20,
float m01, float m11, float m21,
float m02, float m12, float m22);
/// Constructs a matrix_float4x4 from four rows of four columns with float values.
/// Indices are m<column><row>.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_make_rows(float m00, float m10, float m20, float m30,
float m01, float m11, float m21, float m31,
float m02, float m12, float m22, float m32,
float m03, float m13, float m23, float m33);
/// Constructs a matrix_float3x3 from 3 vector_float3 column vectors.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix_make_columns(vector_float3 col0,
vector_float3 col1,
vector_float3 col2);
/// Constructs a matrix_float4x4 from 4 vector_float4 column vectors.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_make_columns(vector_float4 col0,
vector_float4 col1,
vector_float4 col2,
vector_float4 col3);
/// Constructs a rotation matrix from the given angle and axis.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_rotation(float radians, vector_float3 axis);
/// Constructs a rotation matrix from the given angle and axis.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_rotation(float radians, float x, float y, float z);
/// Constructs a scaling matrix with the specified scaling factors.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_scale(float x, float y, float z);
/// Constructs a scaling matrix, using the given vector as an array of scaling factors.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_scale(vector_float3 s);
/// Extracts the upper-left 3x3 submatrix of the given 4x4 matrix.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix3x3_upper_left(matrix_float4x4 m);
/// Returns the inverse of the transpose of the given matrix.
matrix_float3x3 AAPL_SIMD_OVERLOAD matrix_inverse_transpose(matrix_float3x3 m);
/// Constructs a homogeneous rotation matrix from the given angle and axis.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_from_quaternion(quaternion_float q);
/// Constructs a rotation matrix from the provided angle and axis
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_rotation(float radians, vector_float3 axis);
/// Constructs a rotation matrix from the given angle and axis.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_rotation(float radians, float x, float y, float z);
/// Constructs an identity matrix.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_identity(void);
/// Constructs a scaling matrix with the given scaling factors.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_scale(float sx, float sy, float sz);
/// Constructs a scaling matrix, using the given vector as an array of scaling factors.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_scale(vector_float3 s);
/// Constructs a translation matrix that translates by the vector (tx, ty, tz).
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_translation(float tx, float ty, float tz);
/// Constructs a translation matrix that translates by the vector (t.x, t.y, t.z).
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_translation(vector_float3 t);
/// Constructs a translation matrix that scales by the vector (s.x, s.y, s.z)
/// and translates by the vector (t.x, t.y, t.z).
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix4x4_scale_translation(vector_float3 s, vector_float3 t);
/// Starting with left-hand world coordinates, constructs a view matrix that is
/// positioned at (eyeX, eyeY, eyeZ) and looks toward (centerX, centerY, centerZ),
/// with the vector (upX, upY, upZ) pointing up for a left-hand coordinate system.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_look_at_left_hand(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ);
/// Starting with left-hand world coordinates, constructs a view matrix that is
/// positioned at (eye) and looks toward (target), with the vector (up) pointing
/// up for a left-hand coordinate system.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_look_at_left_hand(vector_float3 eye,
vector_float3 target,
vector_float3 up);
/// Starting with right-hand world coordinates, constructs a view matrix that is
/// positioned at (eyeX, eyeY, eyeZ) and looks toward (centerX, centerY, centerZ),
/// with the vector (upX, upY, upZ) pointing up for a right-hand coordinate system.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_look_at_right_hand(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ);
/// Starting with right-hand world coordinates, constructs a view matrix that is
/// positioned at (eye) and looks toward (target), with the vector (up) pointing
/// up for a right-hand coordinate system.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_look_at_right_hand(vector_float3 eye,
vector_float3 target,
vector_float3 up);
/// Constructs a symmetric orthographic projection matrix, from left-hand eye
/// coordinates to left-hand clip coordinates.
/// That maps (left, top) to (-1, 1), (right, bottom) to (1, -1), and (nearZ, farZ) to (0, 1).
/// The first four arguments are signed eye coordinates.
/// nearZ and farZ are absolute distances from the eye to the near and far clip planes.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_ortho_left_hand(float left, float right, float bottom, float top, float nearZ, float farZ);
/// Constructs a symmetric orthographic projection matrix, from right-hand eye
/// coordinates to right-hand clip coordinates.
/// That maps (left, top) to (-1, 1), (right, bottom) to (1, -1), and (nearZ, farZ) to (0, 1).
/// The first four arguments are signed eye coordinates.
/// nearZ and farZ are absolute distances from the eye to the near and far clip planes.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_ortho_right_hand(float left, float right, float bottom, float top, float nearZ, float farZ);
/// Constructs a symmetric perspective projection matrix, from left-hand eye
/// coordinates to left-hand clip coordinates, with a vertical viewing angle of
/// fovyRadians, the given aspect ratio, and the given absolute near and far
/// z distances from the eye.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_perspective_left_hand(float fovyRadians, float aspect, float nearZ, float farZ);
/// Constructs a symmetric perspective projection matrix, from right-hand eye
/// coordinates to right-hand clip coordinates, with a vertical viewing angle of
/// fovyRadians, the given aspect ratio, and the given absolute near and far
/// z distances from the eye.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_perspective_right_hand(float fovyRadians, float aspect, float nearZ, float farZ);
/// Construct a general frustum projection matrix, from right-hand eye
/// coordinates to left-hand clip coordinates.
/// The bounds left, right, bottom, and top, define the visible frustum at the near clip plane.
/// The first four arguments are signed eye coordinates.
/// nearZ and farZ are absolute distances from the eye to the near and far clip planes.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_perspective_frustum_right_hand(float left, float right, float bottom, float top, float nearZ, float farZ);
/// Returns the inverse of the transpose of the given matrix.
matrix_float4x4 AAPL_SIMD_OVERLOAD matrix_inverse_transpose(matrix_float4x4 m);
/// Constructs an identity quaternion.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_identity(void);
/// Constructs a quaternion of the form w + xi + yj + zk.
quaternion_float AAPL_SIMD_OVERLOAD quaternion(float x, float y, float z, float w);
/// Constructs a quaternion of the form w + v.x*i + v.y*j + v.z*k.
quaternion_float AAPL_SIMD_OVERLOAD quaternion(vector_float3 v, float w);
/// Constructs a unit-norm quaternion that represents rotation by the given angle about the axis (x, y, z).
quaternion_float AAPL_SIMD_OVERLOAD quaternion(float radians, float x, float y, float z);
/// Constructs a unit-norm quaternion that represents rotation by the given angle about the specified axis.
quaternion_float AAPL_SIMD_OVERLOAD quaternion(float radians, vector_float3 axis);
/// Constructs a unit-norm quaternion from the given matrix.
/// The result is undefined if the matrix does not represent a pure rotation.
quaternion_float AAPL_SIMD_OVERLOAD quaternion(matrix_float3x3 m);
/// Constructs a unit-norm quaternion from the given matrix.
/// The result is undefined if the matrix does not represent a pure rotation.
quaternion_float AAPL_SIMD_OVERLOAD quaternion(matrix_float4x4 m);
/// Returns the length of the given quaternion.
float AAPL_SIMD_OVERLOAD quaternion_length(quaternion_float q);
float AAPL_SIMD_OVERLOAD quaternion_length_squared(quaternion_float q);
/// Returns the rotation axis of the given unit-norm quaternion.
vector_float3 AAPL_SIMD_OVERLOAD quaternion_axis(quaternion_float q);
/// Returns the rotation angle of the given unit-norm quaternion.
float AAPL_SIMD_OVERLOAD quaternion_angle(quaternion_float q);
/// Returns a quaternion from the given rotation axis and angle, in radians.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_axis_angle(vector_float3 axis, float radians);
/// Returns a quaternion from the given 3x3 rotation matrix.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_matrix3x3(matrix_float3x3 m);
/// Returns a quaternion from the given Euler angle, in radians.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_euler(vector_float3 euler);
/// Returns a unit-norm quaternion.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_normalize(quaternion_float q);
/// Returns the inverse quaternion of the given quaternion.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_inverse(quaternion_float q);
/// Returns the conjugate quaternion of the given quaternion.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_conjugate(quaternion_float q);
/// Returns the product of the two given quaternions.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_multiply(quaternion_float q0, quaternion_float q1);
/// Returns the quaternion that results from spherically interpolating between the two given quaternions.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_slerp(quaternion_float q0, quaternion_float q1, float t);
/// Returns the vector that results from rotating the given vector by the given unit-norm quaternion.
vector_float3 AAPL_SIMD_OVERLOAD quaternion_rotate_vector(quaternion_float q, vector_float3 v);
/// Returns the quaternion for the given forward and up vectors for right-hand coordinate systems.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_direction_vectors_right_hand(vector_float3 forward, vector_float3 up);
/// Returns the quaternion for the given forward and up vectors for left-hand coordinate systems.
quaternion_float AAPL_SIMD_OVERLOAD quaternion_from_direction_vectors_left_hand(vector_float3 forward, vector_float3 up);
/// Returns a vector in the +Z direction for the given quaternion.
vector_float3 AAPL_SIMD_OVERLOAD forward_direction_vector_from_quaternion(quaternion_float q);
/// Returns a vector in the +Y direction for the given quaternion (for a left-handed coordinate system,
/// negate for a right-hand coordinate system).
vector_float3 AAPL_SIMD_OVERLOAD up_direction_vector_from_quaternion(quaternion_float q);
/// Returns a vector in the +X direction for the given quaternion (for a left-hand coordinate system,
/// negate for a right-hand coordinate system).
vector_float3 AAPL_SIMD_OVERLOAD right_direction_vector_from_quaternion(quaternion_float q);
+103 -113
View File
@@ -2,8 +2,18 @@
// Created by Vicente Ferrari Smith on 26.02.26.
//
#include "init.h"
#define NS_PRIVATE_IMPLEMENTATION
#define MTL_PRIVATE_IMPLEMENTATION
#define MTK_PRIVATE_IMPLEMENTATION
#define CA_PRIVATE_IMPLEMENTATION
#include <Foundation/Foundation.hpp>
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp>
#include "metal.h"
#include "../graphics.h"
#include "../texture.h"
#include <print>
#include <iostream>
#include <fstream>
@@ -12,20 +22,41 @@
#include <GLFW/glfw3native.h>
#include <objc/message.h>
#include <objc/objc.h>
#include "vertex_data.h"
Device metal_device{};
MTL::Buffer* triangle_vertex_buffer{};
MTL::CommandQueue *queue{};
CA::MetalLayer *metal_layer{};
MTL::RenderPipelineState *pipeline_state{};
CA::MetalDrawable *metal_drawable{};
MTL::CommandBuffer* metal_command_buffer{};
extern int32_t window_width;
extern int32_t window_height;
MTL::Function *vertex_shader{};
MTL::Function *fragment_shader{};
Device metal_device{};
MTL::CommandQueue *queue{};
CA::MetalLayer *metal_layer{};
CA::MetalDrawable *metal_drawable{};
void create_window(GLFWwindow *window) {
Renderer renderer;
void upload_texture(
const int w,
const int h,
const void *pixels,
Texture *texture)
{
MTL::TextureDescriptor *td = MTL::TextureDescriptor::alloc()->init();
td->setPixelFormat(MTL::PixelFormatRGBA8Unorm);
td->setWidth(w);
td->setHeight(h);
td->setStorageMode(MTL::StorageModeShared);
td->setUsage(MTL::TextureUsageShaderRead);
texture->p_texture->texture = metal_device.device->newTexture(td);
MTL::Region region = MTL::Region(0, 0, 0, w, h, 1);
NS::UInteger bytesPerRow = 4 * w;
texture->p_texture->texture->replaceRegion(region, 0, pixels, bytesPerRow);
td->release();
}
void create_metal_layer(GLFWwindow *window) {
void *ns_window = glfwGetCocoaWindow(window);
if (!ns_window) {
throw std::runtime_error("Failed to get Cocoa window from GLFWwindow");
@@ -51,44 +82,11 @@ void create_window(GLFWwindow *window) {
metal_layer->setColorspace(p3Space);
}
void encode_render_command(MTL::RenderCommandEncoder *renderCommandEncoder) {
renderCommandEncoder->setRenderPipelineState(pipeline_state);
renderCommandEncoder->setVertexBuffer(triangle_vertex_buffer, 0, 0);
MTL::PrimitiveType typeTriangle = MTL::PrimitiveTypeTriangle;
NS::UInteger vertexStart = 0;
NS::UInteger vertexCount = 6;
renderCommandEncoder->drawPrimitives(typeTriangle, vertexStart, vertexCount);
}
void send_render_command() {
metal_command_buffer = queue->commandBuffer();
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
MTL::RenderPassColorAttachmentDescriptor *cd = renderPassDescriptor->colorAttachments()->object(0);
cd->setTexture(metal_drawable->texture());
cd->setLoadAction(MTL::LoadActionClear);
cd->setClearColor(MTL::ClearColor(
100.0f / 255.0f,
149.0f / 255.0f,
237.0f / 255.0f,
1.0
));
cd->setStoreAction(MTL::StoreActionStore);
MTL::RenderCommandEncoder* renderCommandEncoder = metal_command_buffer->renderCommandEncoder(renderPassDescriptor);
encode_render_command(renderCommandEncoder);
renderCommandEncoder->endEncoding();
metal_command_buffer->presentDrawable(metal_drawable);
metal_command_buffer->commit();
metal_command_buffer->waitUntilCompleted();
renderPassDescriptor->release();
}
void LoadMetalShader(const std::string &shader_path,
const std::string &vertex_fn_name,
const std::string &fragment_fn_name)
void load_metal_shader(const std::string &shader_path,
const std::string &vertex_fn_name,
const std::string &fragment_fn_name,
MTL::Function **vertex_shader,
MTL::Function **fragment_shader)
{
NS::Error *error = nullptr;
MTL::Library *library = nullptr;
@@ -137,80 +135,72 @@ void LoadMetalShader(const std::string &shader_path,
}
NS::String *vname = NS::String::string(vertex_fn_name.c_str(), NS::UTF8StringEncoding);
NS::String *fname = NS::String::string(fragment_fn_name.c_str(), NS::UTF8StringEncoding);
vertex_shader = library->newFunction(vname);
fragment_shader = library->newFunction(fname);
*vertex_shader = library->newFunction(vname);
*fragment_shader = library->newFunction(fname);
if (vertex_shader == nullptr || fragment_shader == nullptr) {
if (*vertex_shader == nullptr || *fragment_shader == nullptr) {
throw std::runtime_error("Failed to create Metal shader functions");
}
library->release();
}
void create_render_pipeline() {
LoadMetalShader("shaders/shader.metal", "vertex_main", "fragment_main");
MTL::RenderPipelineDescriptor* renderPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
renderPipelineDescriptor->setLabel(NS::String::string("Triangle Rendering Pipeline", NS::ASCIIStringEncoding));
renderPipelineDescriptor->setVertexFunction(vertex_shader);
renderPipelineDescriptor->setFragmentFunction(fragment_shader);
assert(renderPipelineDescriptor);
const MTL::PixelFormat pixel_format = metal_layer->pixelFormat();
renderPipelineDescriptor->colorAttachments()->object(0)->setPixelFormat(pixel_format);
NS::Error* error;
pipeline_state = metal_device.device->newRenderPipelineState(renderPipelineDescriptor, &error);
renderPipelineDescriptor->release();
}
void create_command_queue() {
queue = metal_device.device->newCommandQueue();
}
void create_triangle() {
VertexData square_vertices[] = {
{{-0.5, -0.5}, {1.0, 0.0, 0.0, 1.0}},
{{0.5, -0.5}, {0.0, 1.0, 0.0, 1.0}},
{{0.5, 0.5}, {0.0, 0.0, 1.0, 1.0}},
{{0.5, 0.5}, {0.0, 0.0, 1.0, 1.0}},
{{-0.5, 0.5}, {0.0, 1.0, 0.0, 1.0}},
{{-0.5, -0.5}, {1.0, 0.0, 0.0, 1.0}},
};
triangle_vertex_buffer = metal_device.device->newBuffer(&square_vertices,
sizeof(square_vertices),
MTL::ResourceStorageModeShared);
}
void graphics_init(GLFWwindow *window) {
std::println("wow, we are on macos!! crazy!!");
create_device();
create_window(window);
create_triangle();
create_command_queue();
create_render_pipeline();
}
void graphics_deinit() {
}
void begin_frame() {
}
void end_frame() {
auto pPool = NS::AutoreleasePool::alloc()->init();
metal_drawable = metal_layer->nextDrawable();
send_render_command();
pPool->release();
}
void create_device() {
metal_device.device = MTL::CreateSystemDefaultDevice();
}
void platform_graphics_init(GLFWwindow *window) {
std::println("wow, we are on macos!! crazy!!");
auto slangTargets{ std::to_array<slang::TargetDesc>({ {
.format = SLANG_METAL,
.profile = slangGlobalSession->findProfile("metallib_2_4"),
} })};
// auto slangOptions{ std::to_array<slang::CompilerOptionEntry>({ {
// slang::CompilerOptionName::EmitSpirvDirectly,
// {slang::CompilerOptionValueKind::Int, 1}
// } })};
const char *search_paths[] = {"shaders"};
slang::SessionDesc slangSessionDesc{
.targets = slangTargets.data(),
.targetCount = SlangInt(slangTargets.size()),
.searchPaths = search_paths,
.searchPathCount = 1,
// .defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR,
};
slangGlobalSession->createSession(slangSessionDesc, slangSession.writeRef());
create_device();
create_metal_layer(window);
create_command_queue();
renderer = Renderer(window);
}
void graphics_deinit() {
queue->release();
metal_layer->release();
metal_device.device->release();
}
void begin_frame() {
renderer.begin_frame();
}
void end_frame(GLFWwindow *window) {
auto pPool = NS::AutoreleasePool::alloc()->init();
metal_drawable = metal_layer->nextDrawable();
renderer.end_frame(window);
pPool->release();
}
void submit_sprite(glm::vec2 pos, const sprite_t &sprite) {
renderer.submit_sprite({pos.x, pos.y}, sprite);
}
+16 -4
View File
@@ -2,8 +2,8 @@
// Created by Vicente Ferrari Smith on 26.02.26.
//
#ifndef M_INIT_H
#define M_INIT_H
#ifndef V_METAL_H
#define V_METAL_H
#include <GLFW/glfw3.h>
#define GLFW_EXPOSE_NATIVE_COCOA
@@ -17,6 +17,18 @@ struct Device {
MTL::Device *device;
};
void create_device();
struct PlatformTexture {
MTL::Texture *texture;
};
#endif //M_INIT_H
struct Queue {
MTL::CommandQueue *queue;
};
void load_metal_shader(const std::string &shader_path,
const std::string &vertex_fn_name,
const std::string &fragment_fn_name,
MTL::Function **vertex_shader,
MTL::Function **fragment_shader);
#endif //V_METAL_H
File diff suppressed because it is too large. Load diff
+92 -309
View File
@@ -5,25 +5,13 @@
#ifndef V_RENDERER_H
#define V_RENDERER_H
#include "init.h"
#include <volk/volk.h>
#include "metal.h"
#include <GLFW/glfw3.h>
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/ext/matrix_clip_space.hpp>
#include "glm/gtx/string_cast.hpp"
#include <vma/vk_mem_alloc.h>
#include "../sprite.h"
#include "../texture.h"
#include <misc.h>
#include <array>
#include <span>
#include <slang/slang.h>
#include <slang/slang-com-ptr.h>
#include <simd/simd.h>
inline Slang::ComPtr<slang::IGlobalSession> slangGlobalSession;
static const int kMaxFramesInFlight = 3;
enum class PROJECTION_TYPE : uint8_t {
NONE,
@@ -34,32 +22,6 @@ enum class PROJECTION_TYPE : uint8_t {
COUNT,
};
struct vertex_p2_s2_st2_col4_a1_u32 {
glm::vec2 pos;
glm::vec2 scale;
glm::vec2 uv;
glm::vec4 color;
float alpha;
uint32_t textureID;
static VkVertexInputBindingDescription getBindingDescription() {
return {0, sizeof(vertex_p2_s2_st2_col4_a1_u32), VK_VERTEX_INPUT_RATE_VERTEX};
}
static std::array<VkVertexInputAttributeDescription, 6> getAttributeDescriptions() {
return {
{
{0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, pos)},
{1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, scale)},
{2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, uv)},
{3, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, color)},
{4, 0, VK_FORMAT_R32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, alpha)},
{5, 0, VK_FORMAT_R32_UINT, offsetof(vertex_p2_s2_st2_col4_a1_u32, textureID)},
}
};
}
};
// commands
enum class PipelineType : uint8_t {
@@ -72,24 +34,24 @@ enum class PipelineType : uint8_t {
};
struct TexturedQuadCmd {
glm::vec2 position;
glm::vec2 size;
glm::vec2 uvMin;
glm::vec2 uvMax;
glm::vec4 color;
uint16_t textureID;
simd::float2 pos;
simd::float2 scale;
simd::float2 uv0;
simd::float2 uv1;
simd::float4 colour;
MTL::Texture *texture;
};
struct ColoredQuadCmd {
glm::vec2 pos;
glm::vec2 scale;
glm::vec4 color;
simd::float2 pos;
simd::float2 scale;
simd::float4 colour;
};
struct LineCmd {
glm::vec2 start;
glm::vec2 end;
glm::vec4 color;
simd::float2 start;
simd::float2 end;
simd::float4 color;
};
// struct TextCmd {
@@ -99,11 +61,11 @@ struct LineCmd {
// glm::vec4 color;
// };
struct ChunkCmd {
VkBuffer vertexBuffer;
VkBuffer indexBuffer;
uint32_t indexCount;
};
//struct ChunkCmd {
// VkBuffer vertexBuffer;
// VkBuffer indexBuffer;
// uint32_t indexCount;
//};
struct SortKey {
uint16_t depth; // world Z or Y-sorted depth
@@ -123,280 +85,101 @@ struct RenderCommand {
ColoredQuadCmd colored_quad;
LineCmd line;
// TextCmd text;
ChunkCmd chunk;
// ChunkCmd chunk;
};
};
////////////////////////////////////////////////////////////////////////////////////////////////
struct AllocatedBuffer {
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo info;
};
// struct AllocatedBuffer {
// VkBuffer buffer;
// VmaAllocation allocation;
// VmaAllocationInfo info;
// };
struct GPUMeshBuffers {
AllocatedBuffer indexBuffer;
AllocatedBuffer vertexBuffer;
VkDeviceAddress vertexBufferAddress;
};
// struct GPUMeshBuffers {
// AllocatedBuffer indexBuffer;
// AllocatedBuffer vertexBuffer;
// VkDeviceAddress vertexBufferAddress;
// };
struct Renderer {
std::vector<RenderCommand> commands{};
VkDescriptorSetLayout descriptor_set_layout{};
VkPipelineLayout pipelineLayout{};
VkPipeline textured_quad_pipeline{};
VkPipeline colored_quad_pipeline{};
VkPipeline line_pipeline{};
VkPipeline text_pipeline{};
VkPipeline chunk_pipeline{};
VkDescriptorSet set{};
VkSampler defaultSampler{};
MTL::RenderPipelineState *textured_quad_pipeline{};
MTL::RenderPipelineState *colored_quad_pipeline{};
MTL::RenderPipelineState *line_pipeline{};
MTL::RenderPipelineState *text_pipeline{};
MTL::RenderPipelineState *chunk_pipeline{};
uint32_t nextTextureSlot = 0;
MTL::CommandBuffer *command_buffer{};
struct Frame {
VkCommandPool commandPool{};
VkCommandBuffer command_buffer{};
VkSemaphore imageAvailable{};
VkFence in_flight_fence{};
AllocatedBuffer vertexBuffer{};
MTL::Buffer *vertex_buffer{};
MTL::Buffer *uniform_buffer{};
};
std::vector<Frame> frames;
uint32_t currentFrame = 0;
Frame frames[kMaxFramesInFlight];
VkDescriptorPool descriptorPool{};
std::vector<VkDescriptorSet> textureSets{};
dispatch_semaphore_t frame_semaphore;
uint8_t current_frame = 0;
// struct Frame {
// VkCommandPool commandPool{};
// VkCommandBuffer command_buffer{};
//
// VkSemaphore imageAvailable{};
// VkFence in_flight_fence{};
//
// AllocatedBuffer vertexBuffer{};
// };
//
// std::vector<Frame> frames;
// uint32_t currentFrame = 0;
//
// VkDescriptorPool descriptorPool{};
// std::vector<VkDescriptorSet> textureSets{};
//
void begin_frame();
void end_frame();
void end_frame(GLFWwindow *window);
void flush();
void submit_sprite(glm::vec2 pos, const sprite_t &sprite);
void submit_quad(glm::vec2 pos, glm::vec2 scale);
void submit_sprite(simd::float2 pos, const sprite_t &sprite);
void submit_quad(simd::float2 pos, simd::float2 scale);
Renderer() = default;
explicit Renderer(GLFWwindow *window);
void create_pipeline_layout();
void createFrameResources();
void create_default_sampler();
void recordCommandBuffer(
VkCommandBuffer cmd,
VkImage image,
VkImageView imageView,
VkExtent2D extent,
VkImageLayout oldLayout,
const Frame &frame,
const std::vector<vertex_p2_s2_st2_col4_a1_u32> &vertices) const;
void immediate_submit(std::function<void(VkCommandBuffer)>&& func) const;
void transition_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const;
VkImageView create_image_view(VkImage image, VkFormat format) const;
AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);
void destroy_buffer(const AllocatedBuffer& buffer);
// GPUMeshBuffers uploadMesh(std::span<uint32_t> indices, std::span<vertex_p2_st2_col4_a1_u32> vertices);
void upload_vertex_buffer(
VkCommandBuffer cmd,
const Frame &frame,
std::span<const vertex_p2_s2_st2_col4_a1_u32> vertices) const;
[[nodiscard]] VkPipeline get_pipeline(PipelineType type) const;
// void bind_material(VkCommandBuffer cmd, uint16_t materialID);
void create_descriptor_pool();
void update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const;
// Returns the resource info so the Manager can store it
void upload_texture(
int w,
int h,
const void* pixels,
VkImage *image,
VmaAllocation *allocation,
VkImageView *view,
uint32_t *descriptor_index);
template <typename T>
VkPipeline create_graphics_pipeline(
VkDevice device,
VkPipelineLayout layout,
VkFormat colorFormat,
// VkShaderModule vertShader,
// VkShaderModule fragShader,
VkPrimitiveTopology topology,
bool enableBlending)
{
auto slangTargets{ std::to_array<slang::TargetDesc>({ {
.format = SLANG_SPIRV,
.profile = slangGlobalSession->findProfile("spirv_1_4")
} })};
auto slangOptions{ std::to_array<slang::CompilerOptionEntry>({ {
slang::CompilerOptionName::EmitSpirvDirectly,
{slang::CompilerOptionValueKind::Int, 1}
} })};
slang::SessionDesc slangSessionDesc{
.targets = slangTargets.data(),
.targetCount = SlangInt(slangTargets.size()),
.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR,
.compilerOptionEntries = slangOptions.data(),
.compilerOptionEntryCount = uint32_t(slangOptions.size())
};
Slang::ComPtr<slang::ISession> slangSession;
slangGlobalSession->createSession(slangSessionDesc, slangSession.writeRef());
Slang::ComPtr<slang::IModule> slangModule{
slangSession->loadModuleFromSource("triangle", "shaders/shader.slang", nullptr, nullptr)
};
Slang::ComPtr<ISlangBlob> spirv;
slangModule->getTargetCode(0, spirv.writeRef());
VkShaderModuleCreateInfo shaderModuleCI{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = spirv->getBufferSize(),
.pCode = (uint32_t*)spirv->getBufferPointer()
};
VkShaderModule shaderModule{};
vkCreateShaderModule(device, &shaderModuleCI, nullptr, &shaderModule);
auto vsCode = loadFile("shaders/triangle.vert.spv");
auto fsCode = loadFile("shaders/triangle.frag.spv");
VkShaderModuleCreateInfo smci{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
};
smci.codeSize = vsCode.size();
smci.pCode = reinterpret_cast<uint32_t*>(vsCode.data());
// VkShaderModule vs;
// vkCreateShaderModule(device, &smci, nullptr, &vs);
smci.codeSize = fsCode.size();
smci.pCode = reinterpret_cast<uint32_t*>(fsCode.data());
// VkShaderModule fs;
// vkCreateShaderModule(device, &smci, nullptr, &fs);
// --- Shaders ---
std::vector<VkPipelineShaderStageCreateInfo> shaderStages{
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = shaderModule, .pName = "main"},
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = shaderModule, .pName = "main" }
};
// --- Vertex Input (Generic) ---
auto binding = T::getBindingDescription();
auto attrs = T::getAttributeDescriptions();
// --- Vertex Input (Matching our vertex_p2_st2_col4 struct) ---
VkPipelineVertexInputStateCreateInfo vi{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &binding,
.vertexAttributeDescriptionCount = attrs.size(),
.pVertexAttributeDescriptions = attrs.data(),
};
// --- Input Assembly (Changes based on Topology parameter) ---
VkPipelineInputAssemblyStateCreateInfo ia{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
ia.topology = topology;
// --- Blending (Changes based on enableBlending parameter) ---
VkPipelineColorBlendAttachmentState colorBlend{
.blendEnable = enableBlending ? VK_TRUE : VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = 0xF
};
// --- Boilerplate (Standard 2D Defaults) ---
VkPipelineViewportStateCreateInfo vp{
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
nullptr,
0,
1,
nullptr,
1,
nullptr
};
VkPipelineRasterizationStateCreateInfo rs{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
nullptr,
0,
0,
0,
VK_POLYGON_MODE_FILL,
VK_CULL_MODE_NONE,
VK_FRONT_FACE_COUNTER_CLOCKWISE,
0,
0,
0,
0,
1.0f
};
VkPipelineMultisampleStateCreateInfo ms{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
nullptr,
0,
VK_SAMPLE_COUNT_1_BIT
};
VkPipelineColorBlendStateCreateInfo cb{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
nullptr,
0,
0,
VK_LOGIC_OP_AND,
1,
&colorBlend
};
VkDynamicState dyns[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo ds{
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
nullptr,
0,
2,
dyns
};
VkPipelineRenderingCreateInfo rci{
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
nullptr,
0,
1,
&colorFormat
};
VkGraphicsPipelineCreateInfo gpci{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &rci,
.stageCount = (uint32_t) shaderStages.size(),
.pStages = shaderStages.data(),
.pVertexInputState = &vi,
.pInputAssemblyState = &ia,
.pViewportState = &vp,
.pRasterizationState = &rs,
.pMultisampleState = &ms,
.pColorBlendState = &cb,
.pDynamicState = &ds,
.layout = layout
};
VkPipeline pipeline;
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline);
return pipeline;
}
void create_render_pipeline();
void send_render_command(GLFWwindow *window);
void encode_render_command(GLFWwindow *window, MTL::RenderCommandEncoder *render_command_encoder);
// void create_pipeline_layout();
// void createFrameResources();
// void create_default_sampler();
// void recordCommandBuffer(
// VkCommandBuffer cmd,
// VkImage image,
// VkImageView imageView,
// VkExtent2D extent,
// VkImageLayout oldLayout,
// const Frame &frame,
// const std::vector<vertex_p2_s2_st2_col4_a1_u32> &vertices) const;
// void immediate_submit(std::function<void(VkCommandBuffer)>&& func) const;
// void transition_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const;
// VkImageView create_image_view(VkImage image, VkFormat format) const;
// AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);
// void destroy_buffer(const AllocatedBuffer& buffer);
// // GPUMeshBuffers uploadMesh(std::span<uint32_t> indices, std::span<vertex_p2_st2_col4_a1_u32> vertices);
// void upload_vertex_buffer(
// VkCommandBuffer cmd,
// const Frame &frame,
// std::span<const vertex_p2_s2_st2_col4_a1_u32> vertices) const;
//
[[nodiscard]] MTL::RenderPipelineState *get_pipeline(PipelineType type) const;
// // void bind_material(VkCommandBuffer cmd, uint16_t materialID);
// void create_descriptor_pool();
// void update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler) const;
};
#endif //V_RENDERER_H
+52 -3
View File
@@ -2,7 +2,56 @@
// Created by Vicente Ferrari Smith on 27.02.26.
//
#ifndef V_VERTEX_DATA_H
#define V_VERTEX_DATA_H
#pragma once
#include <simd/simd.h>
#endif //V_VERTEX_DATA_H
using namespace simd;
struct vertex_p2_s2_uv2_c4_a1 {
float2 pos;
float2 scale;
float2 uv;
float4 color;
float alpha;
static MTL::VertexDescriptor* vertexDescriptor() {
MTL::VertexDescriptor* vd = MTL::VertexDescriptor::alloc()->init();
// ATTRIBUTE 0 — float2 pos
vd->attributes()->object(0)->setFormat(MTL::VertexFormatFloat2);
vd->attributes()->object(0)->setOffset(offsetof(vertex_p2_s2_uv2_c4_a1, pos));
vd->attributes()->object(0)->setBufferIndex(0);
// ATTRIBUTE 1 — float2 scale
vd->attributes()->object(1)->setFormat(MTL::VertexFormatFloat2);
vd->attributes()->object(1)->setOffset(offsetof(vertex_p2_s2_uv2_c4_a1, scale));
vd->attributes()->object(1)->setBufferIndex(0);
// ATTRIBUTE 2 — float2 uv
vd->attributes()->object(2)->setFormat(MTL::VertexFormatFloat2);
vd->attributes()->object(2)->setOffset(offsetof(vertex_p2_s2_uv2_c4_a1, uv));
vd->attributes()->object(2)->setBufferIndex(0);
// ATTRIBUTE 3 — float4 color
vd->attributes()->object(3)->setFormat(MTL::VertexFormatFloat4);
vd->attributes()->object(3)->setOffset(offsetof(vertex_p2_s2_uv2_c4_a1, color));
vd->attributes()->object(3)->setBufferIndex(0);
// ATTRIBUTE 4 — float alpha
vd->attributes()->object(4)->setFormat(MTL::VertexFormatFloat);
vd->attributes()->object(4)->setOffset(offsetof(vertex_p2_s2_uv2_c4_a1, alpha));
vd->attributes()->object(4)->setBufferIndex(0);
// Layout for buffer 0
vd->layouts()->object(0)->setStride(sizeof(vertex_p2_s2_uv2_c4_a1));
vd->layouts()->object(0)->setStepFunction(MTL::VertexStepFunctionPerVertex);
return vd;
}
};
struct TransformationData {
float4x4 model_matrix;
float4x4 view_matrix;
float4x4 perspective_matrix;
};