From 3ca9ce773daa33165713bf12866e96cd2cd8efbd Mon Sep 17 00:00:00 2001 From: Richard Ward Date: Thu, 18 Dec 2025 19:13:31 +0000 Subject: [PATCH] code --- scripts/cube_logic.lua | 74 +++++++--------------- scripts/math3d.lua | 119 ++++++++++++++++++++++++++++++++++++ src/app/vulkan_cube_app.cpp | 7 +-- src/app/vulkan_cube_app.hpp | 2 +- src/core/math.hpp | 102 ------------------------------- src/core/vertex.hpp | 22 +++++++ src/script/cube_script.cpp | 50 ++++++++++++++- src/script/cube_script.hpp | 3 +- 8 files changed, 218 insertions(+), 161 deletions(-) create mode 100644 scripts/math3d.lua delete mode 100644 src/core/math.hpp create mode 100644 src/core/vertex.hpp diff --git a/scripts/cube_logic.lua b/scripts/cube_logic.lua index 7ccbf10..9674e22 100644 --- a/scripts/cube_logic.lua +++ b/scripts/cube_logic.lua @@ -35,6 +35,8 @@ local pyramid_indices = { 4, 5, 2, } +local math3d = require("math3d") + local rotation_speeds = {x = 0.5, y = 0.7} local shader_variants = { @@ -52,62 +54,26 @@ local shader_variants = { }, } -local function rotation_y(radians) - local c = math.cos(radians) - local s = math.sin(radians) - return { - c, 0.0, -s, 0.0, - 0.0, 1.0, 0.0, 0.0, - s, 0.0, c, 0.0, - 0.0, 0.0, 0.0, 1.0, - } -end - -local function rotation_x(radians) - local c = math.cos(radians) - local s = math.sin(radians) - return { - 1.0, 0.0, 0.0, 0.0, - 0.0, c, s, 0.0, - 0.0, -s, c, 0.0, - 0.0, 0.0, 0.0, 1.0, - } -end - -local function multiply_matrices(a, b) - local result = {} - for row = 1, 4 do - for col = 1, 4 do - local sum = 0.0 - for idx = 1, 4 do - sum = sum + a[(idx - 1) * 4 + row] * b[(col - 1) * 4 + idx] - end - result[(col - 1) * 4 + row] = sum - end - end - return result -end - -local function translation(x, y, z) - return { - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - x, y, z, 1.0, - } -end +local camera = { + eye = {2.0, 2.0, 2.5}, + center = {0.0, 0.0, 0.0}, + up = {0.0, 1.0, 0.0}, + fov = 0.78, + near = 0.1, + far = 10.0, +} local function build_model(time) - local y = rotation_y(time * rotation_speeds.y) - local x = rotation_x(time * rotation_speeds.x) - return multiply_matrices(y, x) + local y = math3d.rotation_y(time * rotation_speeds.y) + local x = math3d.rotation_x(time * rotation_speeds.x) + return math3d.multiply(y, x) end local function create_cube(position, speed_scale, shader_key) local function compute_model_matrix(time) local base = build_model(time * speed_scale) - local offset = translation(position[1], position[2], position[3]) - return multiply_matrices(offset, base) + local offset = math3d.translation(position[1], position[2], position[3]) + return math3d.multiply(offset, base) end return { @@ -121,8 +87,8 @@ end local function create_pyramid(position, shader_key) local function compute_model_matrix(time) local base = build_model(time * 0.6) - local offset = translation(position[1], position[2], position[3]) - return multiply_matrices(offset, base) + local offset = math3d.translation(position[1], position[2], position[3]) + return math3d.multiply(offset, base) end return { @@ -145,3 +111,9 @@ end function get_shader_paths() return shader_variants end + +function get_view_projection(aspect) + local view = math3d.look_at(camera.eye, camera.center, camera.up) + local projection = math3d.perspective(camera.fov, aspect, camera.near, camera.far) + return math3d.multiply(projection, view) +end diff --git a/scripts/math3d.lua b/scripts/math3d.lua new file mode 100644 index 0000000..2f7fdc4 --- /dev/null +++ b/scripts/math3d.lua @@ -0,0 +1,119 @@ +local math3d = {} + +local function normalize(vec) + local x, y, z = vec[1], vec[2], vec[3] + local len = math.sqrt(x * x + y * y + z * z) + if len == 0.0 then + return {x, y, z} + end + return {x / len, y / len, z / len} +end + +local function cross(a, b) + return { + a[2] * b[3] - a[3] * b[2], + a[3] * b[1] - a[1] * b[3], + a[1] * b[2] - a[2] * b[1], + } +end + +local function dot(a, b) + return a[1] * b[1] + a[2] * b[2] + a[3] * b[3] +end + +local function identity_matrix() + return { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + } +end + +function math3d.identity() + return identity_matrix() +end + +function math3d.multiply(a, b) + local result = {} + for row = 1, 4 do + for col = 1, 4 do + local sum = 0.0 + for idx = 1, 4 do + sum = sum + a[(idx - 1) * 4 + row] * b[(col - 1) * 4 + idx] + end + result[(col - 1) * 4 + row] = sum + end + end + return result +end + +function math3d.translation(x, y, z) + return { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + x, y, z, 1.0, + } +end + +function math3d.rotation_x(radians) + local c = math.cos(radians) + local s = math.sin(radians) + return { + 1.0, 0.0, 0.0, 0.0, + 0.0, c, s, 0.0, + 0.0, -s, c, 0.0, + 0.0, 0.0, 0.0, 1.0, + } +end + +function math3d.rotation_y(radians) + local c = math.cos(radians) + local s = math.sin(radians) + return { + c, 0.0, -s, 0.0, + 0.0, 1.0, 0.0, 0.0, + s, 0.0, c, 0.0, + 0.0, 0.0, 0.0, 1.0, + } +end + +function math3d.look_at(eye, center, up) + local f = normalize({center[1] - eye[1], center[2] - eye[2], center[3] - eye[3]}) + local s = normalize(cross(f, up)) + local u = cross(s, f) + + local result = identity_matrix() + result[1] = s[1] + result[2] = u[1] + result[3] = -f[1] + result[5] = s[2] + result[6] = u[2] + result[7] = -f[2] + result[9] = s[3] + result[10] = u[3] + result[11] = -f[3] + result[13] = -dot(s, eye) + result[14] = -dot(u, eye) + result[15] = dot(f, eye) + return result +end + +function math3d.perspective(fov, aspect, zNear, zFar) + local tanHalf = math.tan(fov / 2.0) + local result = { + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, + } + result[1] = 1.0 / (aspect * tanHalf) + result[6] = -1.0 / tanHalf + result[11] = zFar / (zNear - zFar) + result[12] = -1.0 + result[15] = (zNear * zFar) / (zNear - zFar) + return result +end + +return math3d diff --git a/src/app/vulkan_cube_app.cpp b/src/app/vulkan_cube_app.cpp index 309a8e9..aceed77 100644 --- a/src/app/vulkan_cube_app.cpp +++ b/src/app/vulkan_cube_app.cpp @@ -705,11 +705,8 @@ void VulkanCubeApp::DrawFrame(float time) { throw std::runtime_error("Failed to acquire swap chain image"); } - auto view = core::LookAt({2.0f, 2.0f, 2.5f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}); - auto projection = core::Perspective(0.78f, static_cast(swapChainExtent_.width) / - static_cast(swapChainExtent_.height), - 0.1f, 10.0f); - auto viewProj = core::MultiplyMatrix(projection, view); + float aspect = static_cast(swapChainExtent_.width) / static_cast(swapChainExtent_.height); + auto viewProj = cubeScript_.GetViewProjectionMatrix(aspect); vkResetCommandBuffer(commandBuffers_[imageIndex], 0); RecordCommandBuffer(commandBuffers_[imageIndex], imageIndex, time, viewProj); diff --git a/src/app/vulkan_cube_app.hpp b/src/app/vulkan_cube_app.hpp index e902e33..a950408 100644 --- a/src/app/vulkan_cube_app.hpp +++ b/src/app/vulkan_cube_app.hpp @@ -16,7 +16,7 @@ #include #include -#include "core/math.hpp" +#include "core/vertex.hpp" #include "script/cube_script.hpp" namespace sdl3cpp::app { diff --git a/src/core/math.hpp b/src/core/math.hpp deleted file mode 100644 index 55746ef..0000000 --- a/src/core/math.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef SDL3CPP_CORE_MATH_HPP -#define SDL3CPP_CORE_MATH_HPP - -#include -#include -#include - -namespace sdl3cpp::core { - -struct Vec3 { - float x; - float y; - float z; -}; - -struct Vertex { - std::array position; - std::array color; -}; - -struct PushConstants { - std::array model; - std::array viewProj; -}; - -static_assert(sizeof(PushConstants) == sizeof(float) * 32, "push constant size mismatch"); - -inline std::array MultiplyMatrix(const std::array& a, - const std::array& b) { - std::array result{}; - for (int row = 0; row < 4; ++row) { - for (int col = 0; col < 4; ++col) { - float sum = 0.0f; - for (int idx = 0; idx < 4; ++idx) { - sum += a[idx * 4 + row] * b[col * 4 + idx]; - } - result[col * 4 + row] = sum; - } - } - return result; -} - -inline std::array IdentityMatrix() { - return {1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; -} - -inline Vec3 Normalize(Vec3 v) { - float len = std::sqrt(v.x * v.x + v.y * v.y + v.z * v.z); - if (len == 0.0f) { - return v; - } - return {v.x / len, v.y / len, v.z / len}; -} - -inline Vec3 Cross(const Vec3& a, const Vec3& b) { - return {a.y * b.z - a.z * b.y, - a.z * b.x - a.x * b.z, - a.x * b.y - a.y * b.x}; -} - -inline float Dot(const Vec3& a, const Vec3& b) { - return a.x * b.x + a.y * b.y + a.z * b.z; -} - -inline std::array LookAt(const Vec3& eye, const Vec3& center, const Vec3& up) { - Vec3 f = Normalize({center.x - eye.x, center.y - eye.y, center.z - eye.z}); - Vec3 s = Normalize(Cross(f, up)); - Vec3 u = Cross(s, f); - - std::array result = IdentityMatrix(); - result[0] = s.x; - result[1] = u.x; - result[2] = -f.x; - result[4] = s.y; - result[5] = u.y; - result[6] = -f.y; - result[8] = s.z; - result[9] = u.z; - result[10] = -f.z; - result[12] = -Dot(s, eye); - result[13] = -Dot(u, eye); - result[14] = Dot(f, eye); - return result; -} - -inline std::array Perspective(float fovRadians, float aspect, float zNear, float zFar) { - float tanHalf = std::tan(fovRadians / 2.0f); - std::array result{}; - result[0] = 1.0f / (aspect * tanHalf); - result[5] = -1.0f / tanHalf; - result[10] = zFar / (zNear - zFar); - result[11] = -1.0f; - result[14] = (zNear * zFar) / (zNear - zFar); - return result; -} - -} // namespace sdl3cpp::core - -#endif // SDL3CPP_CORE_MATH_HPP diff --git a/src/core/vertex.hpp b/src/core/vertex.hpp new file mode 100644 index 0000000..d679e96 --- /dev/null +++ b/src/core/vertex.hpp @@ -0,0 +1,22 @@ +#ifndef SDL3CPP_CORE_VERTEX_HPP +#define SDL3CPP_CORE_VERTEX_HPP + +#include + +namespace sdl3cpp::core { + +struct Vertex { + std::array position; + std::array color; +}; + +struct PushConstants { + std::array model; + std::array viewProj; +}; + +static_assert(sizeof(PushConstants) == sizeof(float) * 32, "push constant size mismatch"); + +} // namespace sdl3cpp::core + +#endif // SDL3CPP_CORE_VERTEX_HPP diff --git a/src/script/cube_script.cpp b/src/script/cube_script.cpp index d41ddfe..fb274d4 100644 --- a/src/script/cube_script.cpp +++ b/src/script/cube_script.cpp @@ -6,11 +6,38 @@ namespace sdl3cpp::script { +namespace { + +std::array IdentityMatrix() { + return {1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; +} + +} // namespace + CubeScript::CubeScript(const std::filesystem::path& scriptPath) : L_(luaL_newstate()) { if (!L_) { throw std::runtime_error("Failed to create Lua state"); } luaL_openlibs(L_); + auto scriptDir = scriptPath.parent_path(); + if (!scriptDir.empty()) { + lua_getglobal(L_, "package"); + if (lua_istable(L_, -1)) { + lua_getfield(L_, -1, "path"); + const char* currentPath = lua_tostring(L_, -1); + std::string newPath = scriptDir.string() + "/?.lua;"; + if (currentPath) { + newPath += currentPath; + } + lua_pop(L_, 1); + lua_pushstring(L_, newPath.c_str()); + lua_setfield(L_, -2, "path"); + } + lua_pop(L_, 1); + } if (luaL_dofile(L_, scriptPath.string().c_str()) != LUA_OK) { std::string message = LuaErrorMessage(L_); lua_pop(L_, 1); @@ -97,7 +124,7 @@ std::array CubeScript::ComputeModelMatrix(int functionRef, float time lua_getglobal(L_, "compute_model_matrix"); if (!lua_isfunction(L_, -1)) { lua_pop(L_, 1); - return core::IdentityMatrix(); + return IdentityMatrix(); } } else { lua_rawgeti(L_, LUA_REGISTRYINDEX, functionRef); @@ -119,6 +146,27 @@ std::array CubeScript::ComputeModelMatrix(int functionRef, float time return matrix; } +std::array CubeScript::GetViewProjectionMatrix(float aspect) { + lua_getglobal(L_, "get_view_projection"); + if (!lua_isfunction(L_, -1)) { + lua_pop(L_, 1); + throw std::runtime_error("Lua function 'get_view_projection' is missing"); + } + lua_pushnumber(L_, aspect); + if (lua_pcall(L_, 1, 1, 0) != LUA_OK) { + std::string message = LuaErrorMessage(L_); + lua_pop(L_, 1); + throw std::runtime_error("Lua get_view_projection failed: " + message); + } + if (!lua_istable(L_, -1)) { + lua_pop(L_, 1); + throw std::runtime_error("'get_view_projection' did not return a table"); + } + std::array matrix = ReadMatrix(L_, -1); + lua_pop(L_, 1); + return matrix; +} + std::vector CubeScript::ReadVertexArray(lua_State* L, int index) { int absIndex = lua_absindex(L, index); if (!lua_istable(L, absIndex)) { diff --git a/src/script/cube_script.hpp b/src/script/cube_script.hpp index 2dbe7e5..dd24962 100644 --- a/src/script/cube_script.hpp +++ b/src/script/cube_script.hpp @@ -9,7 +9,7 @@ #include -#include "core/math.hpp" +#include "core/vertex.hpp" namespace sdl3cpp::script { @@ -32,6 +32,7 @@ public: std::vector LoadSceneObjects(); std::array ComputeModelMatrix(int functionRef, float time); + std::array GetViewProjectionMatrix(float aspect); std::unordered_map LoadShaderPathsMap(); private: