Files
DarkflameServer/dUgcServer/Render/UgcRender.h
Aaron Kimbrell 57110a7081 feat(ugc): rays behind a backend interface, with Embree as a second backend
UgcRays::Scene answers the two ray queries the UGC server makes: the nearest
hit for the hidden faces' paths (never the triangle a path leaves) and any hit
for the ambient occlusion rays. Two backends:

- builtin: the two hierarchies the queries had before, moved unchanged (each
  built the first time it is asked), so the files made are the same bytes
- embree: Embree 4 on the job's thread (a device per worker thread, no threads
  of its own, so its time counts in the CPU budget), watertight, the skipped
  triangle filtered out

UgcHsr::Options::rays and UgcRender::AoOptions::rays pick the backend; both
default to builtin, and nothing sets them yet.

Check: the UGC tests (builtin's files keep their hashes; UgcRays tests compare
embree's hits, hidden faces and occlusion with builtin's).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:29:08 -05:00

88 lines
4.5 KiB
C++

#pragma once
#include <cstdint>
#include <vector>
#include <glm/glm.hpp>
#include "UgcGlitter.h"
#include "UgcModel.h"
#include "UgcRays.h"
/**
* A small software rasterizer (no GPU or display needed) for what the UGC server draws: the icons, and the occlusion
* rays that bake ambient occlusion into the vertex colors.
*/
namespace UgcRender {
// 8-bit RGBA, rows top to bottom, not premultiplied
struct Image {
int width{};
int height{};
std::vector<uint8_t> rgba;
};
struct AoOptions {
bool enabled{ true };
float distance{ 5.0f }; // LU Toolbox's AO distance (Bake Lighting, AO Only)
int samples{ 64 }; // rays per vertex (AO Samples)
float strength{ 1.0f }; // 0 leaves the colors, 1 is the full bake
float glowStrength{ 6.0f }; // what glowing colors add to the light (Glow Strength 3 x Glow Multiplier 2)
UgcRays::eBackend rays{}; // what traces the occlusion rays (ugc_ray_backend)
};
/**
* How an icon is drawn. The values that can be set (settings icon_*, presets and overrides) are listed once, with
* their ranges and shipped defaults, in UgcIconParams; the ones here are only what the struct starts with.
*/
struct IconOptions {
int size{ 128 };
int supersample{ 4 };
float yawDegrees{ 53.36f }; // camera around the model, from +Z towards +X
float pitchDegrees{ 19.54f }; // camera above the model
float fovDegrees{ 39.6f };
float margin{ 1.03f }; // 1 fills the icon, more leaves a border
float offsetX{}; // the model moved right by this share of the icon's width (after framing)
float offsetY{}; // and up by this share of its height
float modelYawDegrees{}; // the model turned (UgcIconPose::ModelRotation), after modelRotation
float modelPitchDegrees{};
float modelRollDegrees{};
glm::mat4 modelRotation{ 1.0f }; // the model's own turn before that (a build type's AdditionalModelRotation)
float sunYawDegrees{ 21.0f };
float sunPitchDegrees{ 50.3f };
float sunStrength{ 2.5f };
float ambient{ 0.192f }; // the world light (radiance) every face gets
float fill{ 0.0f }; // a light from the camera (irradiance), what lifts the sides facing the viewer
float specular{ 0.0f }; // the sun's highlight on the plastic
float shininess{ 60.0f }; // how tight the highlight is
float exposure{ 1.0f }; // everything times this before it's written as sRGB
float contrast{ 1.0f }; // around the middle grey of the sRGB result
float shadows{ 1.0f }; // how much the sun's shadows darken, 0 to 1
AoOptions ao{ false, 5.0f, 32, 1.0f, 0.0f };
float glowEmissive{ 1.0f }; // how far glowing shapes go from lit to their plain color (the glow_emissive setting)
UgcGlitter::Params glitter; // the glitter's flecks (glitter_size, glitter_density), drawn where they are at the start
};
// The model drawn from the icon's camera, framed to fit, on a transparent background. `opaqueAo`: the opaque mesh's
// ambient occlusion (AmbientOcclusion) when it is known already, else it is worked out when options.ao wants it.
// Opaque vertices with a look (UgcModel::Mesh::looks) are drawn roughly as the game's shaders draw them: GLOW goes
// from lit to its plain color by glowEmissive (LEGO-Emissive), METAL and BRUSHED dim the diffuse light and add a
// sky-and-ground reflection tinted by the color and a highlight, sharp for polished metal and broad for brushed
// steel (Polished Metal, Brushed Steel: an environment map tinted by the vertex color). GLITTER vertices (opaque or
// transparent) get the glitter texture's white flecks over their color before the light (LEGO-AnimUV), still.
Image RenderIcon(const UgcModel::Model& model, const IconOptions& options, const std::vector<float>* opaqueAo = nullptr);
/**
* Ambient occlusion of each vertex of `mesh`: the share of `samples` rays (cosine weighted around the vertex
* normal, the same pattern every time) that leave without hitting a triangle of `occluders` within `distance`.
* 1 is open, 0 fully hidden.
*/
std::vector<float> AmbientOcclusion(const UgcModel::Mesh& mesh, const UgcModel::Mesh& occluders, float distance, int samples,
UgcRays::eBackend rays = UgcRays::eBackend::BUILTIN);
/**
* LU Toolbox's Bake Lighting with AO Only (its defaults): the opaque mesh's occlusion (transparent bricks are hidden
* while baking and not baked) plus the glow colors, multiplied into the vertex colors.
*/
std::vector<float> BakeAo(UgcModel::Model& model, const AoOptions& options); // the occlusion used, per opaque vertex
}