feat(dashboard): assembled mesh route, icon parameter groups and preset samples, pose math in JS

GET /api/ugc/assembly converts the UGC server's assembled .nif for the 3D
view. The icon parameter list names each parameter's group; the kinds carry a
sample (a model, or the most used combination) to edit a preset on. The icon
preview is viewable as the /ugc page. ugc-pose-math.js mirrors UgcIconPose and
both are checked against one fixture (gtest and node).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 15:26:21 -05:00
parent a5deacfc54
commit 4f77b239e3
8 changed files with 376 additions and 26 deletions

View File

@@ -15,26 +15,26 @@ namespace UgcIconParams {
// Framing from LU Toolbox's icon renderer (its UGC render add-on's BrickBuild scene); the light matched to the
// brightness of the game's own model icons (res/textures/ui/inventory/models), docs/UgcServer.md
static const std::vector<Param> list = {
{ "yaw", "icon_yaw", "Angle around", "degrees", -180, 180, 1, 53.36f, "The camera's angle around the model, from its front.", [](Options& o, float v) { o.yawDegrees = v; } },
{ "pitch", "icon_pitch", "Angle above", "degrees", -89, 89, 1, 19.54f, "", [](Options& o, float v) { o.pitchDegrees = v; } },
{ "fov", "icon_fov", "Field of view", "degrees", 5, 90, 0.5f, 39.6f, "", [](Options& o, float v) { o.fovDegrees = v; } },
{ "margin", "icon_margin", "Border (zoom)", "", 0.5f, 3, 0.01f, 1.03f, "1 fills the icon, more leaves a border.", [](Options& o, float v) { o.margin = v; } },
{ "offsetX", "icon_offset_x", "Shift right", "", -0.5f, 0.5f, 0.01f, 0.0f, "Share of the icon's width.", [](Options& o, float v) { o.offsetX = v; } },
{ "offsetY", "icon_offset_y", "Shift up", "", -0.5f, 0.5f, 0.01f, 0.0f, "Share of the icon's height.", [](Options& o, float v) { o.offsetY = v; } },
{ "modelYaw", "icon_model_yaw", "Model turned around", "degrees", -180, 180, 1, 0.0f, "The model turned around its up axis before the camera looks at it.", [](Options& o, float v) { o.modelYawDegrees = v; } },
{ "modelPitch", "icon_model_pitch", "Model tipped forward", "degrees", -90, 90, 1, 0.0f, "Turned around its side axis, after the turn around.", [](Options& o, float v) { o.modelPitchDegrees = v; } },
{ "modelRoll", "icon_model_roll", "Model rolled", "degrees", -180, 180, 1, 0.0f, "Turned around its front axis, last.", [](Options& o, float v) { o.modelRollDegrees = v; } },
{ "sunYaw", "icon_sun_yaw", "Sun around", "degrees", -180, 180, 1, 21.0f, "", [](Options& o, float v) { o.sunYawDegrees = v; } },
{ "sunPitch", "icon_sun_pitch", "Sun above", "degrees", -10, 90, 1, 50.3f, "", [](Options& o, float v) { o.sunPitchDegrees = v; } },
{ "sunStrength", "icon_sun_light", "Sun strength", "", 0, 10, 0.05f, 2.0f, "", [](Options& o, float v) { o.sunStrength = v; } },
{ "ambient", "icon_world_light", "World light", "", 0, 2, 0.01f, 1.0f, "What every face gets.", [](Options& o, float v) { o.ambient = v; } },
{ "fill", "icon_fill", "Fill light", "", 0, 5, 0.05f, 0.8f, "A light from the camera.", [](Options& o, float v) { o.fill = v; } },
{ "specular", "icon_specular", "Highlights", "", 0, 2, 0.05f, 0.2f, "The sun's highlight on the plastic.", [](Options& o, float v) { o.specular = v; } },
{ "shininess", "icon_shininess", "Highlight tightness", "", 1, 200, 1, 40.0f, "", [](Options& o, float v) { o.shininess = v; } },
{ "exposure", "icon_exposure", "Exposure", "", 0.1f, 4, 0.05f, 2.6f, "Brightness of everything.", [](Options& o, float v) { o.exposure = v; } },
{ "contrast", "icon_contrast", "Contrast", "", 0.5f, 1.5f, 0.01f, 1.0f, "", [](Options& o, float v) { o.contrast = v; } },
{ "shadows", "icon_shadow_strength", "Shadows", "", 0, 1, 0.05f, 0.4f, "How much the sun's shadows darken.", [](Options& o, float v) { o.shadows = v; } },
{ "aoStrength", "icon_ao_strength", "Ambient occlusion", "", 0, 1, 0.05f, 0.0f,
{ "yaw", "camera", "icon_yaw", "Angle around", "degrees", -180, 180, 1, 53.36f, "The camera's angle around the model, from its front.", [](Options& o, float v) { o.yawDegrees = v; } },
{ "pitch", "camera", "icon_pitch", "Angle above", "degrees", -89, 89, 1, 19.54f, "", [](Options& o, float v) { o.pitchDegrees = v; } },
{ "fov", "camera", "icon_fov", "Field of view", "degrees", 5, 90, 0.5f, 39.6f, "", [](Options& o, float v) { o.fovDegrees = v; } },
{ "margin", "framing", "icon_margin", "Border (zoom)", "", 0.5f, 3, 0.01f, 1.03f, "1 fills the icon, more leaves a border.", [](Options& o, float v) { o.margin = v; } },
{ "offsetX", "framing", "icon_offset_x", "Shift right", "", -0.5f, 0.5f, 0.01f, 0.0f, "Share of the icon's width.", [](Options& o, float v) { o.offsetX = v; } },
{ "offsetY", "framing", "icon_offset_y", "Shift up", "", -0.5f, 0.5f, 0.01f, 0.0f, "Share of the icon's height.", [](Options& o, float v) { o.offsetY = v; } },
{ "modelYaw", "model", "icon_model_yaw", "Model turned around", "degrees", -180, 180, 1, 0.0f, "The model turned around its up axis before the camera looks at it.", [](Options& o, float v) { o.modelYawDegrees = v; } },
{ "modelPitch", "model", "icon_model_pitch", "Model tipped forward", "degrees", -90, 90, 1, 0.0f, "Turned around its side axis, after the turn around.", [](Options& o, float v) { o.modelPitchDegrees = v; } },
{ "modelRoll", "model", "icon_model_roll", "Model rolled", "degrees", -180, 180, 1, 0.0f, "Turned around its front axis, last.", [](Options& o, float v) { o.modelRollDegrees = v; } },
{ "sunYaw", "sun", "icon_sun_yaw", "Sun around", "degrees", -180, 180, 1, 21.0f, "", [](Options& o, float v) { o.sunYawDegrees = v; } },
{ "sunPitch", "sun", "icon_sun_pitch", "Sun above", "degrees", -10, 90, 1, 50.3f, "", [](Options& o, float v) { o.sunPitchDegrees = v; } },
{ "sunStrength", "sun", "icon_sun_light", "Sun strength", "", 0, 10, 0.05f, 2.0f, "", [](Options& o, float v) { o.sunStrength = v; } },
{ "ambient", "light", "icon_world_light", "World light", "", 0, 2, 0.01f, 1.0f, "What every face gets.", [](Options& o, float v) { o.ambient = v; } },
{ "fill", "light", "icon_fill", "Fill light", "", 0, 5, 0.05f, 0.8f, "A light from the camera.", [](Options& o, float v) { o.fill = v; } },
{ "specular", "light", "icon_specular", "Highlights", "", 0, 2, 0.05f, 0.2f, "The sun's highlight on the plastic.", [](Options& o, float v) { o.specular = v; } },
{ "shininess", "light", "icon_shininess", "Highlight tightness", "", 1, 200, 1, 40.0f, "", [](Options& o, float v) { o.shininess = v; } },
{ "exposure", "look", "icon_exposure", "Exposure", "", 0.1f, 4, 0.05f, 2.6f, "Brightness of everything.", [](Options& o, float v) { o.exposure = v; } },
{ "contrast", "look", "icon_contrast", "Contrast", "", 0.5f, 1.5f, 0.01f, 1.0f, "", [](Options& o, float v) { o.contrast = v; } },
{ "shadows", "sun", "icon_shadow_strength", "Shadows", "", 0, 1, 0.05f, 0.4f, "How much the sun's shadows darken.", [](Options& o, float v) { o.shadows = v; } },
{ "aoStrength", "look", "icon_ao_strength", "Ambient occlusion", "", 0, 1, 0.05f, 0.0f,
"Occlusion worked out for the icon. Player models' meshes have theirs baked in already.", [](Options& o, float v) { o.ao.strength = v; o.ao.enabled = v > 0.0f; } },
};
return list;

View File

@@ -20,6 +20,7 @@
namespace UgcIconParams {
struct Param {
std::string key; // in presets and overrides, e.g. "sunStrength"
std::string group; // what it is part of, for the editor: camera, framing, model, sun, light, look
std::string setting; // ugcconfig.ini, e.g. "icon_sun_strength"
std::string label;
std::string unit;