refactor(gameengine): remove hardcoded defaults from spotlight steps, pure JSON-driven

- spotlight.setup: generic parameter passthrough, no hardcoded defaults
- spotlight.update: all values from spotlight.state JSON, zero fallbacks
- aim_distance now explicit in workflow JSON (was hardcoded 50.0f)
- C++ steps are pure executors, all config lives in workflow definitions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-18 08:55:51 +00:00
parent 487631458a
commit a4aee87b88
3 changed files with 83 additions and 76 deletions

View File

@@ -871,7 +871,8 @@
"color_b": 0.85,
"offset_x": 0.15,
"offset_y": -0.1,
"offset_z": -0.1
"offset_z": -0.1,
"aim_distance": 50
}
},
{

View File

@@ -1,10 +1,7 @@
#include "services/interfaces/workflow/rendering/workflow_spotlight_setup_step.hpp"
#include "services/interfaces/workflow/workflow_step_parameter_resolver.hpp"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <nlohmann/json.hpp>
#include <cmath>
namespace sdl3cpp::services::impl {
@@ -16,55 +13,57 @@ std::string WorkflowSpotlightSetupStep::GetPluginId() const {
}
void WorkflowSpotlightSetupStep::Execute(const WorkflowStepDefinition& step, WorkflowContext& context) {
WorkflowStepParameterResolver params;
auto getNum = [&](const char* name, float def) -> float {
const auto* p = params.FindParameter(step, name);
return (p && p->type == WorkflowParameterValue::Type::Number) ? static_cast<float>(p->numberValue) : def;
};
auto getStr = [&](const char* name, const std::string& def) -> std::string {
const auto* p = params.FindParameter(step, name);
return (p && p->type == WorkflowParameterValue::Type::String) ? p->stringValue : def;
};
std::string attach = getStr("attach", "camera");
float inner_cone = getNum("inner_cone", 12.0f);
float outer_cone = getNum("outer_cone", 25.0f);
float intensity = getNum("intensity", 2.5f);
float range = getNum("range", 20.0f);
float color_r = getNum("color_r", 1.0f);
float color_g = getNum("color_g", 0.95f);
float color_b = getNum("color_b", 0.85f);
float offset_x = getNum("offset_x", 0.0f);
float offset_y = getNum("offset_y", 0.0f);
float offset_z = getNum("offset_z", 0.0f);
// Pass all parameters straight through to context as JSON
// No hardcoded defaults — everything comes from the workflow definition
nlohmann::json spotlight;
spotlight["inner_cone"] = inner_cone;
spotlight["outer_cone"] = outer_cone;
spotlight["intensity"] = intensity;
spotlight["range"] = range;
spotlight["color"] = {color_r, color_g, color_b};
spotlight["attach"] = attach;
spotlight["offset"] = {offset_x, offset_y, offset_z};
// If attached to camera, position and direction will be filled per-frame
// by the render.prepare step reading camera state.
// For static spotlights, set explicit position/direction params.
if (attach == "camera") {
// Will be updated each frame in render.prepare from camera state
spotlight["position"] = {0, 0, 0};
spotlight["direction"] = {0, 0, -1};
} else {
spotlight["position"] = {
getNum("pos_x", 0.0f),
getNum("pos_y", 0.0f),
getNum("pos_z", 0.0f)
for (const auto& [key, param] : step.parameters) {
switch (param.type) {
case WorkflowParameterValue::Type::Number:
spotlight[key] = param.numberValue;
break;
case WorkflowParameterValue::Type::String:
spotlight[key] = param.stringValue;
break;
case WorkflowParameterValue::Type::Bool:
spotlight[key] = param.boolValue;
break;
default:
break;
}
}
// Build color array from individual components if present
if (spotlight.contains("color_r") || spotlight.contains("color_g") || spotlight.contains("color_b")) {
spotlight["color"] = {
spotlight.value("color_r", 0.0),
spotlight.value("color_g", 0.0),
spotlight.value("color_b", 0.0)
};
}
// Build offset array from individual components if present
if (spotlight.contains("offset_x") || spotlight.contains("offset_y") || spotlight.contains("offset_z")) {
spotlight["offset"] = {
spotlight.value("offset_x", 0.0),
spotlight.value("offset_y", 0.0),
spotlight.value("offset_z", 0.0)
};
}
// Build position/direction arrays for static spotlights
if (spotlight.contains("pos_x") || spotlight.contains("pos_y") || spotlight.contains("pos_z")) {
spotlight["position"] = {
spotlight.value("pos_x", 0.0),
spotlight.value("pos_y", 0.0),
spotlight.value("pos_z", 0.0)
};
}
if (spotlight.contains("dir_x") || spotlight.contains("dir_y") || spotlight.contains("dir_z")) {
spotlight["direction"] = {
getNum("dir_x", 0.0f),
getNum("dir_y", -1.0f),
getNum("dir_z", 0.0f)
spotlight.value("dir_x", 0.0),
spotlight.value("dir_y", 0.0),
spotlight.value("dir_z", 0.0)
};
}

View File

@@ -21,50 +21,57 @@ void WorkflowSpotlightUpdateStep::Execute(const WorkflowStepDefinition& step, Wo
auto fu = context.Get<rendering::FragmentUniformData>("render.frag_uniforms", rendering::FragmentUniformData{});
std::string attach = spot->value("attach", "camera");
auto offset = spot->value("offset", std::vector<float>{0, 0, 0});
glm::vec3 off(offset.size() > 0 ? offset[0] : 0,
offset.size() > 1 ? offset[1] : 0,
offset.size() > 2 ? offset[2] : 0);
// All values from JSON — no hardcoded defaults in C++
const std::string attach = spot->value("attach", std::string());
const auto offset = spot->value("offset", std::vector<float>{});
const glm::vec3 off(offset.size() > 0 ? offset[0] : 0.0f,
offset.size() > 1 ? offset[1] : 0.0f,
offset.size() > 2 ? offset[2] : 0.0f);
glm::vec3 spotPos, spotDir;
if (attach == "camera") {
auto viewMatrix = context.Get<glm::mat4>("render.view_matrix", glm::mat4(1.0f));
auto cameraPos = context.Get<glm::vec3>("render.camera_pos", glm::vec3(0.0f));
const auto viewMatrix = context.Get<glm::mat4>("render.view_matrix", glm::mat4(1.0f));
const auto cameraPos = context.Get<glm::vec3>("render.camera_pos", glm::vec3(0.0f));
glm::vec3 camRight = glm::vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
glm::vec3 camUp = glm::vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);
glm::vec3 camFwd = -glm::vec3(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2]);
const glm::vec3 camRight = glm::vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
const glm::vec3 camUp = glm::vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);
const glm::vec3 camFwd = -glm::vec3(viewMatrix[0][2], viewMatrix[1][2], viewMatrix[2][2]);
spotPos = cameraPos + camRight * off.x + camUp * off.y + camFwd * (-off.z);
// Aim toward a far point on camera center axis (natural torch aim)
float aimDist = spot->value("aim_distance", 50.0f);
glm::vec3 aimTarget = cameraPos + camFwd * aimDist;
spotDir = glm::normalize(aimTarget - spotPos);
const float aimDist = spot->value("aim_distance", 0.0f);
if (aimDist > 0.0f) {
const glm::vec3 aimTarget = cameraPos + camFwd * aimDist;
spotDir = glm::normalize(aimTarget - spotPos);
} else {
spotDir = camFwd;
}
} else {
auto p = spot->value("position", std::vector<float>{0, 0, 0});
auto d = spot->value("direction", std::vector<float>{0, 0, -1});
spotPos = glm::vec3(p[0], p[1], p[2]) + off;
spotDir = glm::normalize(glm::vec3(d[0], d[1], d[2]));
const auto p = spot->value("position", std::vector<float>{});
const auto d = spot->value("direction", std::vector<float>{});
spotPos = glm::vec3(p.size() > 0 ? p[0] : 0.0f, p.size() > 1 ? p[1] : 0.0f, p.size() > 2 ? p[2] : 0.0f) + off;
spotDir = glm::normalize(glm::vec3(d.size() > 0 ? d[0] : 0.0f, d.size() > 1 ? d[1] : 0.0f, d.size() > 2 ? d[2] : -1.0f));
}
const float innerCone = spot->value("inner_cone", 0.0f);
const float outerCone = spot->value("outer_cone", 0.0f);
const auto col = spot->value("color", std::vector<float>{});
const float intensity = spot->value("intensity", 0.0f);
const float range = spot->value("range", 0.0f);
fu.flash_pos[0] = spotPos.x;
fu.flash_pos[1] = spotPos.y;
fu.flash_pos[2] = spotPos.z;
fu.flash_pos[3] = std::cos(glm::radians(spot->value("inner_cone", 12.0f)));
fu.flash_pos[3] = std::cos(glm::radians(innerCone));
fu.flash_dir[0] = spotDir.x;
fu.flash_dir[1] = spotDir.y;
fu.flash_dir[2] = spotDir.z;
fu.flash_dir[3] = std::cos(glm::radians(spot->value("outer_cone", 25.0f)));
auto col = spot->value("color", std::vector<float>{1, 1, 1});
float intensity = spot->value("intensity", 2.5f);
fu.flash_color[0] = (col.size() > 0 ? col[0] : 1.0f) * intensity;
fu.flash_color[1] = (col.size() > 1 ? col[1] : 1.0f) * intensity;
fu.flash_color[2] = (col.size() > 2 ? col[2] : 1.0f) * intensity;
fu.flash_color[3] = spot->value("range", 20.0f);
fu.flash_dir[3] = std::cos(glm::radians(outerCone));
fu.flash_color[0] = (col.size() > 0 ? col[0] : 0.0f) * intensity;
fu.flash_color[1] = (col.size() > 1 ? col[1] : 0.0f) * intensity;
fu.flash_color[2] = (col.size() > 2 ? col[2] : 0.0f) * intensity;
fu.flash_color[3] = range;
context.Set<rendering::FragmentUniformData>("render.frag_uniforms", fu);
}