mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-06 04:43:43 +00:00
Why the glitter never moved: player models (LOT 14) are wrapped in weeblewobble.kfm (RenderComponentWrapper 9845), so the client makes them an LWOSkinnedRenderComponent, whose Run (0x00d6d3d0) updates the scene graph (and so any NiTextureTransformController) only while animation is enabled, and LWOModelBehaviorComponent::EnableAnimation (0x00be2740) turns it off for modelType 2, which every placed property model is. The root flags 0x102 added earlier are only read by the base render component. Nothing in a placed model's .nif can move. What does move: shader classes set globals in their own per-frame Run. Distortion Directional (Ocean) (mapShaders 79, Run 0x010b90c0) slides its texture layers by fixed shares of a tile a second, as the game's own pond ripples (S79__pond_ripplesShape). Glitter bricks now get a sparkle group, S79_GlitterSparkle_Model: their triangles lifted 0.005 off the brick, vertex colors white tinted by the brick, UVs placed per brick, alpha tested (ShaderCommon's alpha test phase, GREATEREQUAL 127), with a stored texture of flat sparkles at alpha 230: one layer's sparkle alone averages under the test, two meeting pass, so sparkles flash and go out as the layers cross. The flecks stay (LEGO-AnimUV, now without the controllers and flags that never ran). The icon and the dashboard's 3D view leave the sparkles out. New settings: shader_glitter_sparkle (79, 0 off), glitter_sparkle_size, glitter_sparkle_amount, glitter_sparkle_tint, glitter_sparkle_brightness; glitter_speed is now how fast sparkles flash (the sparkle tile). Only glitter output changes; non-glitter models are byte-identical. Check in game: reprocess a property with glitter models, then look at them from a few angles and distances, on each graphics quality: - sparkles flash on and off all over the glitter bricks, continuously - no flickering fight between the sparkles and the brick surface - transparent glitter bricks still see-through, flecks still visible - nothing drawn where there is no glitter brick; icons unchanged apart from the flecks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
138 lines
7.5 KiB
C++
138 lines
7.5 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
|
|
#include "UgcBricks.h"
|
|
#include "UgcHsr.h"
|
|
#include "UgcIconParams.h"
|
|
#include "UgcRender.h"
|
|
#include "UgcStorage.h"
|
|
|
|
/**
|
|
* The work a UGC worker thread does for one item: everything from the stored LXFML (or a modular build's modules) to
|
|
* the finished files. No database, network or CDClient: the main thread gathers the input and stores the result.
|
|
*/
|
|
namespace UgcJobs {
|
|
/**
|
|
* How models are made. The defaults are LU Toolbox's (Process Model, Bake Lighting and the icon renderer), see the
|
|
* parity table in docs/UgcServer.md.
|
|
*/
|
|
/**
|
|
* The shaders of the metal, glow and glitter colors (UgcModel::eLook): the mapShaders id each look's own NiLODNode
|
|
* names (S88_Metal_Model, ...), 0 for none (the colors stay in S01_Opaque_Model, or S01_Alpha_Model when
|
|
* transparent, as on live). Not what live did: live's models are all S01 (docs/UgcServer.md, "Metal and glow").
|
|
*/
|
|
struct Shaders {
|
|
uint32_t metal{}; // shader_metal: 88 Polished Metal
|
|
uint32_t brushed{}; // shader_brushed: 89 Brushed Steel
|
|
uint32_t glow{}; // shader_glow: 46 LEGO-Emissive
|
|
uint32_t glitter{}; // shader_glitter: 21 LEGO-AnimUV (opaque and transparent glitter, each a group)
|
|
// shader_glitter_sparkle: 79 Distortion Directional (Ocean), the glitter bricks' sparkles, a group over both
|
|
// glitter groups (so only with shader_glitter)
|
|
uint32_t sparkle{};
|
|
float glowEmissive{ 1.0f }; // glow_emissive: the glow shapes' NiMaterialProperty emissive (how much the vertex color shows unlit)
|
|
UgcGlitter::Params glitterParams; // glitter_* (flecks and sparkles)
|
|
|
|
// The mapShaders id of a look's group, 0 for the plastic S01_Opaque_Model
|
|
uint32_t TagOf(UgcModel::eLook look) const;
|
|
// Multishader tag -> look, for reading the looks back out of a .nif (the icon): these settings' ids, and the
|
|
// client's Polished Metal (88), Brushed Steel (89), LEGO-Emissive (46) and LEGO-AnimUV (21) for .nifs made with
|
|
// other settings
|
|
std::map<int32_t, UgcModel::eLook> TagLooks() const;
|
|
// The tags of groups drawn over others (the sparkles: this setting's id and the client's 79), which the icon
|
|
// leaves out (UgcModel::FromNif)
|
|
std::set<int32_t> OverlayTags() const;
|
|
};
|
|
|
|
struct Settings {
|
|
UgcModel::BuildOptions build; // palette, color variation, transparent opacity
|
|
std::vector<uint32_t> lods{ 0, 2 }; // brickprimitives levels made (LU Toolbox imports LOD 0 and 2; the client has no 3)
|
|
UgcModel::LodDistances lodDistances;
|
|
std::string shaderOpaque{ "01" }; // S<shader>_Opaque_...; transparent shapes are always S01
|
|
bool combineTransparent{ false }; // one shape for all transparent bricks, else one per brick (Combine Transparent)
|
|
Shaders shaders; // metal and glow groups (all off by default)
|
|
UgcHsr::Options hsr; // hidden surface removal (Remove Hidden Faces)
|
|
UgcRender::AoOptions ao; // Bake Lighting (AO Only)
|
|
UgcRender::IconOptions icon; // from the icon_* settings (UgcIconParams); presets and overrides go over it
|
|
uint32_t maxBricks{}; // a model with more fails; 0: no limit
|
|
};
|
|
|
|
struct Outcome {
|
|
bool ok{};
|
|
std::string error; // why it failed
|
|
std::string note; // what was odd but not fatal (missing bricks, ...)
|
|
UgcStorage::Files files; // name -> bytes, when ok
|
|
bool aoBaked{};
|
|
bool empty{}; // nothing to make (no bricks): not a failure
|
|
std::string stats; // stats.json: bricks, triangles before and after per LOD, timings
|
|
};
|
|
|
|
// The client's download of `data` (`name` + ".gz" and ".checksum") added to `files`
|
|
void AddDownload(UgcStorage::Files& files, const std::string& name, const std::string& data);
|
|
|
|
// The LXFML of a ugc row's lxfml column (an sd0 stream, or plain LXFML); empty when it can't be read
|
|
std::string LxfmlFromBlob(const std::string& blob);
|
|
|
|
// A player model: the optimized .nif, its icon and its LXFML for download, and stats.json. `seed` picks the color
|
|
// variation's random numbers (the model's id, so making it again gives the same colors).
|
|
// `iconValues`: the player models' preset and this model's override (UgcIconParams), over settings.icon.
|
|
Outcome ProcessModel(const std::string& blob, UgcBricks::BrickLibrary& library, const Settings& settings, uint64_t seed = 0,
|
|
const UgcIconParams::Values& iconValues = {});
|
|
|
|
// A model's icon files (icon.png, icon.dds download) drawn from its .nif (LOD 0), its metal and glow groups by
|
|
// `tagLooks` (Shaders::TagLooks); false (and `error`) when the .nif can't be read
|
|
// (the groups drawn over others, `overlayTags` (Shaders::OverlayTags), left out)
|
|
bool IconFromNif(const std::string& nif, const UgcRender::IconOptions& options, UgcStorage::Files& files, std::string& error,
|
|
const std::map<int32_t, UgcModel::eLook>& tagLooks = {}, const std::set<int32_t>& overlayTags = {});
|
|
|
|
// The name of a group of shapes (its NiLODNode and shapes): S01_Opaque_Model, S01_Alpha_Model, S88_Metal_Model,
|
|
// S89_Brushed_Model, S46_Glow_Model, S21_Glitter_Model and S21_GlitterAlpha_Model (transparent glitter; the ids from
|
|
// the settings), at most 60 characters as LU Toolbox cuts them
|
|
std::string ShapeName(const Settings& settings, UgcModel::eLook look, bool transparent);
|
|
|
|
// The name of the glitter sparkles' group, S79_GlitterSparkle_Model (the id from the settings)
|
|
std::string SparkleName(const Settings& settings);
|
|
|
|
// How many bricks (parts) an LXFML has, counted cheaply (for the memory estimate before a job starts)
|
|
size_t CountParts(std::string_view lxfml);
|
|
|
|
// About how much memory making a model of `parts` bricks takes, in bytes
|
|
uint64_t EstimateMemory(size_t parts, const Settings& settings);
|
|
|
|
struct ModuleInput {
|
|
uint32_t lot{};
|
|
uint32_t partCode{};
|
|
std::string renderAsset; // RenderComponent.render_asset, relative to the client's res folder
|
|
std::string moduleXml; // ModuleComponent.xml
|
|
};
|
|
|
|
struct ModularInput {
|
|
std::vector<ModuleInput> modules;
|
|
std::string buildXml; // ModularBuildComponent.xml of the modules' build type
|
|
int32_t buildType{}; // 3 rocket, 6 car
|
|
std::string key; // the combination (UgcModularKey::Normalize)
|
|
UgcIconParams::Values iconValues; // the build type's preset and the combination's override (DB), over the settings
|
|
};
|
|
|
|
// The icon options for a car or rocket: the settings, then its build type's preset and the combination's override
|
|
UgcRender::IconOptions ModularIconOptions(const ModularInput& input, const Settings& settings);
|
|
|
|
// A modular build's modules' meshes put together (UgcModular::Assemble), not yet turned by the build type's
|
|
// AdditionalModelRotation (given back in `additionalRotation`); nullopt with `error` when there's nothing to draw
|
|
std::optional<UgcModel::Model> AssembleModular(const ModularInput& input, const std::filesystem::path& res, glm::mat4& additionalRotation, std::string& error, std::string& note);
|
|
|
|
// A modular build's icon (icon.png, icon.dds download, combo.json), from its modules' meshes put together
|
|
Outcome ProcessModular(const ModularInput& input, const std::filesystem::path& res, const Settings& settings);
|
|
|
|
// The assembled mesh as the icon renderer turns it (AdditionalModelRotation applied), as a .nif for the dashboard's
|
|
// pose editor; nullopt with `error` when there's nothing to draw
|
|
std::optional<std::string> AssemblyNif(const ModularInput& input, const std::filesystem::path& res, std::string& error);
|
|
}
|