feat: Enable MaterialX support and refactor rendering pipeline to utilize view state

This commit is contained in:
2026-01-06 15:56:44 +00:00
parent 512eced3b0
commit 06e31cf536
20 changed files with 329 additions and 46 deletions

View File

@@ -61,7 +61,7 @@
"renderer": "vulkan"
},
"materialx": {
"enabled": false,
"enabled": true,
"parameters_enabled": true,
"document": "MaterialX/resources/Materials/Examples/StandardSurface/standard_surface_carpaint.mtlx",
"shader_key": "materialx",

View File

@@ -510,6 +510,21 @@ local function update_audio_controls()
end
local rotation_speed = 0.9
local default_material_shader = "pbr"
local function resolve_material_shader()
if type(config) ~= "table" then
return default_material_shader
end
local materialx = config.materialx
if type(materialx) ~= "table" or not materialx.enabled then
return default_material_shader
end
if type(materialx.shader_key) == "string" and materialx.shader_key ~= "" then
return materialx.shader_key
end
return "materialx"
end
local function build_static_model_matrix(position, scale)
local translation = math3d.translation(position[1], position[2], position[3])
@@ -523,6 +538,7 @@ local function apply_color_to_vertices(color)
local v = cube_vertices[i]
colored_vertices[i] = {
position = v.position,
normal = v.normal,
color = color,
}
end
@@ -564,7 +580,8 @@ local function create_skybox()
end
local function create_spinning_cube()
log_debug("Spinning cube shader=pbr (MaterialX-driven)")
local shader_key = resolve_material_shader()
log_debug("Spinning cube shader=%s", shader_key)
local function compute_model_matrix(time)
local rotation = math3d.rotation_y(time * rotation_speed)
local scale = scale_matrix(1.5, 1.5, 1.5) -- Make cube 3x3x3 units
@@ -576,7 +593,7 @@ local function create_spinning_cube()
vertices = cube_vertices,
indices = (#cube_indices_double_sided > 0) and cube_indices_double_sided or cube_indices,
compute_model_matrix = compute_model_matrix,
shader_key = "pbr",
shader_key = shader_key,
}
end
@@ -764,7 +781,7 @@ function get_shader_paths()
end
function get_view_projection(aspect)
local function build_view_state(aspect)
local now = os.clock()
local dt = 0.0
if last_frame_time then
@@ -789,7 +806,21 @@ function get_view_projection(aspect)
local view = math3d.look_at(camera.position, center, world_up)
local projection = math3d.perspective(camera.fov, aspect, camera.near, camera.far)
return math3d.multiply(projection, view)
return {
view = view,
proj = projection,
view_proj = math3d.multiply(projection, view),
camera_pos = {camera.position[1], camera.position[2], camera.position[3]},
}
end
function get_view_state(aspect)
return build_view_state(aspect)
end
function get_view_projection(aspect)
local state = build_view_state(aspect)
return state.view_proj
end
function get_gui_commands()

View File

@@ -315,7 +315,8 @@ local vertex_color_source = [[
#version 450
layout(location = 0) in vec3 inPos;
layout(location = 1) in vec3 inColor;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec3 inColor;
layout(location = 0) out vec3 fragColor;
@@ -389,8 +390,8 @@ local shadow_vertex_source = [[
#version 450
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
// layout(location = 2) in vec2 inTexCoord; // Not used
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec3 inColor;
layout(push_constant) uniform PushConstants {
mat4 model;
@@ -594,7 +595,8 @@ local vertex_world_color_source = [[
#version 450
layout(location = 0) in vec3 inPos;
layout(location = 1) in vec3 inColor;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec3 inColor;
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec3 fragWorldPos;
@@ -1027,8 +1029,8 @@ local pbr_vertex_source = [[
#version 450
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor; // Color instead of normal
layout(location = 2) in vec2 inTexCoord; // Not used for now
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec3 inColor;
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec3 fragWorldPos;
@@ -1061,8 +1063,8 @@ void main() {
gl_Position = pc.proj * pc.view * worldPos;
fragWorldPos = worldPos.xyz;
fragNormal = normalize(mat3(pc.model) * vec3(0.0, 0.0, 1.0)); // Simple normal for flat shading
fragTexCoord = vec2(0.0, 0.0); // Not used
fragNormal = normalize(mat3(pc.model) * inNormal);
fragTexCoord = vec2(0.0, 0.0);
fragColor = inColor; // Use vertex color
}
]]

View File

@@ -202,7 +202,8 @@ local function build_static_cube_variants()
#version 450
layout(location = 0) in vec3 inPos;
layout(location = 1) in vec3 inColor;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec3 inColor;
layout(location = 0) out vec3 fragColor;

View File

@@ -7,6 +7,7 @@ namespace sdl3cpp::core {
struct Vertex {
std::array<float, 3> position;
std::array<float, 3> normal;
std::array<float, 3> color;
};

View File

@@ -12,6 +12,9 @@
#include <cstring>
#include <filesystem>
#include <fstream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iterator>
#include <string>
#include <stdexcept>
@@ -25,6 +28,31 @@ std::string ToLower(std::string value) {
return value;
}
glm::mat4 ToMat4(const std::array<float, 16>& value) {
return glm::make_mat4(value.data());
}
bool IsIdentityMatrix(const std::array<float, 16>& value) {
const float identity[16] = {
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
};
for (size_t i = 0; i < 16; ++i) {
if (value[i] != identity[i]) {
return false;
}
}
return true;
}
void SetUniformIfValid(bgfx::UniformHandle handle, const void* data, uint16_t count = 1) {
if (bgfx::isValid(handle)) {
bgfx::setUniform(handle, data, count);
}
}
bgfx::RendererType::Enum RendererFromString(const std::string& value) {
const std::string lower = ToLower(value);
if (lower == "vulkan") {
@@ -48,8 +76,20 @@ BgfxGraphicsBackend::BgfxGraphicsBackend(std::shared_ptr<IConfigService> configS
}
vertexLayout_.begin()
.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
.add(bgfx::Attrib::Normal, 3, bgfx::AttribType::Float)
.add(bgfx::Attrib::Color0, 3, bgfx::AttribType::Float)
.end();
const std::array<float, 16> identity = {
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
};
viewState_.view = identity;
viewState_.proj = identity;
viewState_.viewProj = identity;
viewState_.cameraPosition = {0.0f, 0.0f, 0.0f};
}
BgfxGraphicsBackend::~BgfxGraphicsBackend() {
@@ -134,6 +174,7 @@ void BgfxGraphicsBackend::Initialize(void* window, const GraphicsConfig& config)
bgfx::setViewClear(viewId_, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x1f1f1fff, 1.0f, 0);
bgfx::setDebug(BGFX_DEBUG_TEXT);
InitializeUniforms();
initialized_ = true;
}
@@ -148,6 +189,7 @@ void BgfxGraphicsBackend::Shutdown() {
DestroyPipelines();
DestroyBuffers();
DestroyUniforms();
bgfx::shutdown();
initialized_ = false;
}
@@ -235,6 +277,64 @@ bgfx::ShaderHandle BgfxGraphicsBackend::CreateShader(const std::string& label,
return bgfx::createShader(mem);
}
void BgfxGraphicsBackend::InitializeUniforms() {
materialXUniforms_.worldMatrix = bgfx::createUniform("u_worldMatrix", bgfx::UniformType::Mat4);
materialXUniforms_.viewMatrix = bgfx::createUniform("u_viewMatrix", bgfx::UniformType::Mat4);
materialXUniforms_.projectionMatrix = bgfx::createUniform("u_projectionMatrix", bgfx::UniformType::Mat4);
materialXUniforms_.viewProjectionMatrix = bgfx::createUniform("u_viewProjectionMatrix", bgfx::UniformType::Mat4);
materialXUniforms_.worldViewMatrix = bgfx::createUniform("u_worldViewMatrix", bgfx::UniformType::Mat4);
materialXUniforms_.worldViewProjectionMatrix = bgfx::createUniform("u_worldViewProjectionMatrix", bgfx::UniformType::Mat4);
materialXUniforms_.worldInverseTransposeMatrix = bgfx::createUniform("u_worldInverseTransposeMatrix", bgfx::UniformType::Mat4);
materialXUniforms_.viewPosition = bgfx::createUniform("u_viewPosition", bgfx::UniformType::Vec4);
}
void BgfxGraphicsBackend::DestroyUniforms() {
bgfx::UniformHandle handles[] = {
materialXUniforms_.worldMatrix,
materialXUniforms_.viewMatrix,
materialXUniforms_.projectionMatrix,
materialXUniforms_.viewProjectionMatrix,
materialXUniforms_.worldViewMatrix,
materialXUniforms_.worldViewProjectionMatrix,
materialXUniforms_.worldInverseTransposeMatrix,
materialXUniforms_.viewPosition
};
for (bgfx::UniformHandle handle : handles) {
if (bgfx::isValid(handle)) {
bgfx::destroy(handle);
}
}
materialXUniforms_ = MaterialXUniforms{};
}
void BgfxGraphicsBackend::ApplyMaterialXUniforms(const std::array<float, 16>& modelMatrix) {
glm::mat4 model = ToMat4(modelMatrix);
glm::mat4 view = ToMat4(viewState_.view);
glm::mat4 proj = ToMat4(viewState_.proj);
glm::mat4 viewProj = (IsIdentityMatrix(viewState_.view) && IsIdentityMatrix(viewState_.proj))
? ToMat4(viewState_.viewProj)
: proj * view;
glm::mat4 worldView = view * model;
glm::mat4 worldViewProj = viewProj * model;
glm::mat4 worldInverseTranspose = glm::transpose(glm::inverse(model));
SetUniformIfValid(materialXUniforms_.worldMatrix, glm::value_ptr(model));
SetUniformIfValid(materialXUniforms_.viewMatrix, glm::value_ptr(view));
SetUniformIfValid(materialXUniforms_.projectionMatrix, glm::value_ptr(proj));
SetUniformIfValid(materialXUniforms_.viewProjectionMatrix, glm::value_ptr(viewProj));
SetUniformIfValid(materialXUniforms_.worldViewMatrix, glm::value_ptr(worldView));
SetUniformIfValid(materialXUniforms_.worldViewProjectionMatrix, glm::value_ptr(worldViewProj));
SetUniformIfValid(materialXUniforms_.worldInverseTransposeMatrix, glm::value_ptr(worldInverseTranspose));
float viewPosition[4] = {
viewState_.cameraPosition[0],
viewState_.cameraPosition[1],
viewState_.cameraPosition[2],
1.0f
};
SetUniformIfValid(materialXUniforms_.viewPosition, viewPosition);
}
GraphicsPipelineHandle BgfxGraphicsBackend::CreatePipeline(GraphicsDeviceHandle device,
const std::string& shaderKey,
const ShaderPaths& shaderPaths) {
@@ -339,14 +439,7 @@ bool BgfxGraphicsBackend::BeginFrame(GraphicsDeviceHandle device) {
if (!initialized_) {
return false;
}
const float identity[16] = {
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
};
bgfx::setViewRect(viewId_, 0, 0, viewportWidth_, viewportHeight_);
bgfx::setViewTransform(viewId_, viewProj_.data(), identity);
bgfx::touch(viewId_);
return true;
}
@@ -359,8 +452,9 @@ bool BgfxGraphicsBackend::EndFrame(GraphicsDeviceHandle device) {
return true;
}
void BgfxGraphicsBackend::SetViewProjection(const std::array<float, 16>& viewProj) {
viewProj_ = viewProj;
void BgfxGraphicsBackend::SetViewState(const ViewState& viewState) {
viewState_ = viewState;
bgfx::setViewTransform(viewId_, viewState_.view.data(), viewState_.proj.data());
}
void BgfxGraphicsBackend::Draw(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline,
@@ -394,6 +488,7 @@ void BgfxGraphicsBackend::Draw(GraphicsDeviceHandle device, GraphicsPipelineHand
}
bgfx::setTransform(modelMatrix.data());
ApplyMaterialXUniforms(modelMatrix);
bgfx::setVertexBuffer(0, vb->handle, startVertex, availableVertices);
bgfx::setIndexBuffer(ib->handle, indexOffset, indexCount);
bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_WRITE_Z |

View File

@@ -35,7 +35,7 @@ public:
void DestroyBuffer(GraphicsDeviceHandle device, GraphicsBufferHandle buffer) override;
bool BeginFrame(GraphicsDeviceHandle device) override;
bool EndFrame(GraphicsDeviceHandle device) override;
void SetViewProjection(const std::array<float, 16>& viewProj) override;
void SetViewState(const ViewState& viewState) override;
void Draw(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline,
GraphicsBufferHandle vertexBuffer, GraphicsBufferHandle indexBuffer,
uint32_t indexOffset, uint32_t indexCount, int32_t vertexOffset,
@@ -61,6 +61,17 @@ private:
uint32_t indexCount = 0;
};
struct MaterialXUniforms {
bgfx::UniformHandle worldMatrix = BGFX_INVALID_HANDLE;
bgfx::UniformHandle viewMatrix = BGFX_INVALID_HANDLE;
bgfx::UniformHandle projectionMatrix = BGFX_INVALID_HANDLE;
bgfx::UniformHandle viewProjectionMatrix = BGFX_INVALID_HANDLE;
bgfx::UniformHandle worldViewMatrix = BGFX_INVALID_HANDLE;
bgfx::UniformHandle worldViewProjectionMatrix = BGFX_INVALID_HANDLE;
bgfx::UniformHandle worldInverseTransposeMatrix = BGFX_INVALID_HANDLE;
bgfx::UniformHandle viewPosition = BGFX_INVALID_HANDLE;
};
void SetupPlatformData(void* window);
bgfx::RendererType::Enum ResolveRendererType() const;
std::vector<uint8_t> ReadShaderSource(const std::string& path,
@@ -68,6 +79,9 @@ private:
bgfx::ShaderHandle CreateShader(const std::string& label,
const std::string& source,
bool isVertex) const;
void InitializeUniforms();
void DestroyUniforms();
void ApplyMaterialXUniforms(const std::array<float, 16>& modelMatrix);
void DestroyPipelines();
void DestroyBuffers();
@@ -77,7 +91,8 @@ private:
std::unordered_map<GraphicsPipelineHandle, std::unique_ptr<PipelineEntry>> pipelines_;
std::unordered_map<GraphicsBufferHandle, std::unique_ptr<VertexBufferEntry>> vertexBuffers_;
std::unordered_map<GraphicsBufferHandle, std::unique_ptr<IndexBufferEntry>> indexBuffers_;
std::array<float, 16> viewProj_{};
ViewState viewState_{};
MaterialXUniforms materialXUniforms_{};
uint32_t viewportWidth_ = 0;
uint32_t viewportHeight_ = 0;
bool initialized_ = false;

View File

@@ -144,17 +144,17 @@ bool GraphicsService::BeginFrame() {
}
void GraphicsService::RenderScene(const std::vector<RenderCommand>& commands,
const std::array<float, 16>& viewProj) {
const ViewState& viewState) {
logger_->Trace("GraphicsService", "RenderScene",
"commands.size=" + std::to_string(commands.size()) +
", viewProj.size=" + std::to_string(viewProj.size()));
", viewProj.size=" + std::to_string(viewState.viewProj.size()));
if (!initialized_) {
return;
}
// Set the view-projection matrix for the frame
backend_->SetViewProjection(viewProj);
backend_->SetViewState(viewState);
// Execute draw calls
for (const auto& command : commands) {

View File

@@ -38,7 +38,7 @@ public:
void UploadIndexData(const std::vector<uint16_t>& indices) override;
bool BeginFrame() override;
void RenderScene(const std::vector<RenderCommand>& commands,
const std::array<float, 16>& viewProj) override;
const ViewState& viewState) override;
bool EndFrame() override;
void WaitIdle() override;
GraphicsDeviceHandle GetDevice() const override;

View File

@@ -461,8 +461,9 @@ bool GxmGraphicsBackend::EndFrame(GraphicsDeviceHandle device) {
return true;
}
void GxmGraphicsBackend::SetViewProjection(const std::array<float, 16>& viewProj) {
std::cout << "GXM: Setting view-projection matrix" << std::endl;
void GxmGraphicsBackend::SetViewState(const ViewState& viewState) {
std::cout << "GXM: Setting view state" << std::endl;
(void)viewState;
// Matrix will be set when drawing with specific pipeline
}

View File

@@ -36,7 +36,7 @@ public:
bool BeginFrame(GraphicsDeviceHandle device) override;
bool EndFrame(GraphicsDeviceHandle device) override;
void SetViewProjection(const std::array<float, 16>& viewProj) override;
void SetViewState(const ViewState& viewState) override;
void Draw(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline,
GraphicsBufferHandle vertexBuffer, GraphicsBufferHandle indexBuffer,

View File

@@ -53,7 +53,8 @@ bool MeshService::LoadFromFile(const std::string& requestedPath,
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(
resolved.string(),
aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_PreTransformVertices);
aiProcess_Triangulate | aiProcess_JoinIdenticalVertices |
aiProcess_PreTransformVertices | aiProcess_GenNormals);
if (!scene) {
outError = importer.GetErrorString() ? importer.GetErrorString() : "Assimp failed to load mesh";
@@ -72,9 +73,11 @@ bool MeshService::LoadFromFile(const std::string& requestedPath,
}
outPayload.positions.clear();
outPayload.normals.clear();
outPayload.colors.clear();
outPayload.indices.clear();
outPayload.positions.reserve(mesh->mNumVertices);
outPayload.normals.reserve(mesh->mNumVertices);
outPayload.colors.reserve(mesh->mNumVertices);
outPayload.indices.reserve(mesh->mNumFaces * 3);
@@ -93,6 +96,12 @@ bool MeshService::LoadFromFile(const std::string& requestedPath,
const aiVector3D& vertex = mesh->mVertices[i];
outPayload.positions.push_back({vertex.x, vertex.y, vertex.z});
aiVector3D normal(0.0f, 0.0f, 1.0f);
if (mesh->HasNormals()) {
normal = mesh->mNormals[i];
}
outPayload.normals.push_back({normal.x, normal.y, normal.z});
aiColor3D color = materialColor;
if (mesh->HasVertexColors(0) && mesh->mColors[0]) {
const aiColor4D& vertexColor = mesh->mColors[0][i];
@@ -123,6 +132,7 @@ void MeshService::PushMeshToLua(lua_State* L, const MeshPayload& payload) {
if (logger_) {
logger_->Trace("MeshService", "PushMeshToLua",
"positions.size=" + std::to_string(payload.positions.size()) +
", normals.size=" + std::to_string(payload.normals.size()) +
", colors.size=" + std::to_string(payload.colors.size()) +
", indices.size=" + std::to_string(payload.indices.size()) +
", luaStateIsNull=" + std::string(L ? "false" : "true"));
@@ -140,6 +150,17 @@ void MeshService::PushMeshToLua(lua_State* L, const MeshPayload& payload) {
}
lua_setfield(L, -2, "position");
lua_newtable(L);
std::array<float, 3> normal = {0.0f, 0.0f, 1.0f};
if (vertexIndex < payload.normals.size()) {
normal = payload.normals[vertexIndex];
}
for (int component = 0; component < 3; ++component) {
lua_pushnumber(L, normal[component]);
lua_rawseti(L, -2, component + 1);
}
lua_setfield(L, -2, "normal");
lua_newtable(L);
for (int component = 0; component < 3; ++component) {
lua_pushnumber(L, payload.colors[vertexIndex][component]);

View File

@@ -101,9 +101,9 @@ void RenderCoordinatorService::RenderFrame(float time) {
auto extent = graphicsService_->GetSwapchainExtent();
float aspect = extent.second == 0 ? 1.0f
: static_cast<float>(extent.first) / static_cast<float>(extent.second);
auto viewProj = sceneScriptService_->GetViewProjectionMatrix(aspect);
auto viewState = sceneScriptService_->GetViewState(aspect);
graphicsService_->RenderScene(renderCommands, viewProj);
graphicsService_->RenderScene(renderCommands, viewState);
}
if (!graphicsService_->EndFrame()) {

View File

@@ -6,6 +6,9 @@
#include <lua.hpp>
#include <array>
#include <cstring>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <stdexcept>
#include <string>
#include <utility>
@@ -14,6 +17,46 @@
namespace sdl3cpp::services::impl {
namespace {
std::array<float, 16> MultiplyMatrices(const std::array<float, 16>& left,
const std::array<float, 16>& right) {
glm::mat4 leftMat = glm::make_mat4(left.data());
glm::mat4 rightMat = glm::make_mat4(right.data());
glm::mat4 combined = leftMat * rightMat;
std::array<float, 16> result{};
std::memcpy(result.data(), glm::value_ptr(combined), sizeof(float) * result.size());
return result;
}
bool ReadMatrixField(lua_State* L, int tableIndex, const char* field, std::array<float, 16>& target) {
lua_getfield(L, tableIndex, field);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
return false;
}
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
throw std::runtime_error(std::string("Field '") + field + "' must be a 4x4 matrix");
}
target = lua::ReadMatrix(L, -1);
lua_pop(L, 1);
return true;
}
bool ReadVector3Field(lua_State* L, int tableIndex, const char* field, std::array<float, 3>& target) {
lua_getfield(L, tableIndex, field);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
return false;
}
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
throw std::runtime_error(std::string("Field '") + field + "' must be a vec3");
}
target = lua::ReadVector3(L, -1);
lua_pop(L, 1);
return true;
}
std::vector<core::Vertex> ReadVertexArray(lua_State* L, int index, const std::shared_ptr<ILogger>& logger) {
int absIndex = lua_absindex(L, index);
if (!lua_istable(L, absIndex)) {
@@ -44,6 +87,14 @@ std::vector<core::Vertex> ReadVertexArray(lua_State* L, int index, const std::sh
vertex.position = lua::ReadVector3(L, -1);
lua_pop(L, 1);
lua_getfield(L, vertexIndex, "normal");
if (lua_istable(L, -1)) {
vertex.normal = lua::ReadVector3(L, -1);
} else {
vertex.normal = {0.0f, 0.0f, 1.0f};
}
lua_pop(L, 1);
lua_getfield(L, vertexIndex, "color");
vertex.color = lua::ReadVector3(L, -1);
lua_pop(L, 1);
@@ -232,12 +283,66 @@ std::array<float, 16> SceneScriptService::ComputeModelMatrix(int functionRef, fl
return matrix;
}
std::array<float, 16> SceneScriptService::GetViewProjectionMatrix(float aspect) {
ViewState SceneScriptService::GetViewState(float aspect) {
if (logger_) {
logger_->Trace("SceneScriptService", "GetViewProjectionMatrix", "aspect=" + std::to_string(aspect));
logger_->Trace("SceneScriptService", "GetViewState", "aspect=" + std::to_string(aspect));
}
lua_State* L = GetLuaState();
ViewState state;
state.view = lua::IdentityMatrix();
state.proj = lua::IdentityMatrix();
state.viewProj = lua::IdentityMatrix();
state.cameraPosition = {0.0f, 0.0f, 0.0f};
lua_getglobal(L, "get_view_state");
if (lua_isfunction(L, -1)) {
lua_pushnumber(L, aspect);
if (lua_pcall(L, 1, 1, 0) != LUA_OK) {
std::string message = lua::GetLuaError(L);
lua_pop(L, 1);
if (logger_) {
logger_->Error("Lua get_view_state failed: " + message);
}
throw std::runtime_error("Lua get_view_state failed: " + message);
}
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
if (logger_) {
logger_->Error("'get_view_state' did not return a table");
}
throw std::runtime_error("'get_view_state' did not return a table");
}
bool hasView = false;
bool hasProj = false;
bool hasViewProj = false;
try {
hasView = ReadMatrixField(L, -1, "view", state.view);
hasProj = ReadMatrixField(L, -1, "proj", state.proj);
hasViewProj = ReadMatrixField(L, -1, "view_proj", state.viewProj);
if (!hasViewProj) {
hasViewProj = ReadMatrixField(L, -1, "viewProj", state.viewProj);
}
ReadVector3Field(L, -1, "camera_pos", state.cameraPosition);
ReadVector3Field(L, -1, "camera_position", state.cameraPosition);
} catch (const std::exception& ex) {
lua_pop(L, 1);
if (logger_) {
logger_->Error("Lua get_view_state returned invalid data: " + std::string(ex.what()));
}
throw;
}
lua_pop(L, 1);
if (!hasViewProj && hasView && hasProj) {
state.viewProj = MultiplyMatrices(state.proj, state.view);
}
return state;
}
lua_pop(L, 1);
lua_getglobal(L, "get_view_projection");
if (!lua_isfunction(L, -1)) {
lua_pop(L, 1);
@@ -262,9 +367,9 @@ std::array<float, 16> SceneScriptService::GetViewProjectionMatrix(float aspect)
}
throw std::runtime_error("'get_view_projection' did not return a table");
}
std::array<float, 16> matrix = lua::ReadMatrix(L, -1);
state.viewProj = lua::ReadMatrix(L, -1);
lua_pop(L, 1);
return matrix;
return state;
}
lua_State* SceneScriptService::GetLuaState() const {

View File

@@ -19,7 +19,7 @@ public:
std::vector<SceneObject> LoadSceneObjects() override;
std::array<float, 16> ComputeModelMatrix(int functionRef, float time) override;
std::array<float, 16> GetViewProjectionMatrix(float aspect) override;
ViewState GetViewState(float aspect) override;
private:
lua_State* GetLuaState() const;

View File

@@ -34,6 +34,16 @@ struct ShaderPaths {
bool disableDepthTest = false;
};
/**
* @brief View state used for per-frame uniforms.
*/
struct ViewState {
std::array<float, 16> view{};
std::array<float, 16> proj{};
std::array<float, 16> viewProj{};
std::array<float, 3> cameraPosition{};
};
/**
* @brief Render command for a single draw call.
*/

View File

@@ -140,11 +140,11 @@ public:
virtual bool EndFrame(GraphicsDeviceHandle device) = 0;
/**
* @brief Set the view-projection matrix for the current frame.
* @brief Set the view state for the current frame.
*
* @param viewProj View-projection matrix
* @param viewState Per-frame view state
*/
virtual void SetViewProjection(const std::array<float, 16>& viewProj) = 0;
virtual void SetViewState(const ViewState& viewState) = 0;
/**
* @brief Draw with a pipeline.

View File

@@ -91,7 +91,7 @@ public:
* @param viewProj View-projection matrix
*/
virtual void RenderScene(const std::vector<RenderCommand>& commands,
const std::array<float, 16>& viewProj) = 0;
const ViewState& viewState) = 0;
/**
* @brief End the frame and present the rendered image.

View File

@@ -1,7 +1,7 @@
#pragma once
#include "scene_types.hpp"
#include <array>
#include "graphics_types.hpp"
#include <vector>
namespace sdl3cpp::services {
@@ -15,7 +15,7 @@ public:
virtual std::vector<SceneObject> LoadSceneObjects() = 0;
virtual std::array<float, 16> ComputeModelMatrix(int functionRef, float time) = 0;
virtual std::array<float, 16> GetViewProjectionMatrix(float aspect) = 0;
virtual ViewState GetViewState(float aspect) = 0;
};
} // namespace sdl3cpp::services

View File

@@ -8,6 +8,7 @@ namespace sdl3cpp::services {
struct MeshPayload {
std::vector<std::array<float, 3>> positions;
std::vector<std::array<float, 3>> normals;
std::vector<std::array<float, 3>> colors;
std::vector<uint32_t> indices;
};