mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-24 21:52:48 +00:00
fix(ugc): glitter sparkles that move on placed models
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>
This commit is contained in:
@@ -128,6 +128,17 @@ namespace UgcJobs {
|
||||
return looks;
|
||||
}
|
||||
|
||||
std::set<int32_t> Shaders::OverlayTags() const {
|
||||
std::set<int32_t> tags{ 79 };
|
||||
if (sparkle != 0) tags.insert(static_cast<int32_t>(sparkle));
|
||||
return tags;
|
||||
}
|
||||
|
||||
std::string SparkleName(const Settings& settings) {
|
||||
const auto tag = std::to_string(settings.shaders.sparkle);
|
||||
return ("S" + std::string(tag.size() < 2 ? "0" : "") + tag + "_GlitterSparkle_Model").substr(0, 60);
|
||||
}
|
||||
|
||||
std::string ShapeName(const Settings& settings, UgcModel::eLook look, bool transparent) {
|
||||
if (transparent && look != UgcModel::eLook::GLITTER) return "S01_Alpha_Model";
|
||||
if (look == UgcModel::eLook::PLASTIC) return ("S" + settings.shaderOpaque + "_Opaque_Model").substr(0, 60);
|
||||
@@ -138,10 +149,10 @@ namespace UgcJobs {
|
||||
}
|
||||
|
||||
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::map<int32_t, UgcModel::eLook>& tagLooks, const std::set<int32_t>& overlayTags) {
|
||||
const auto readBack = NifFile::Parse(nif, 0, error);
|
||||
if (!readBack) return false;
|
||||
AddIcon(files, UgcModel::FromNif(*readBack, tagLooks), options);
|
||||
AddIcon(files, UgcModel::FromNif(*readBack, tagLooks, overlayTags), options);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -189,6 +200,8 @@ namespace UgcJobs {
|
||||
std::array<bool, UgcModel::LOOK_COUNT> separate{};
|
||||
for (size_t look = 1; look < UgcModel::LOOK_COUNT; look++) separate[look] = settings.shaders.TagOf(static_cast<UgcModel::eLook>(look)) != 0;
|
||||
const bool glowApart = separate[static_cast<size_t>(UgcModel::eLook::GLOW)];
|
||||
// The glitter bricks' sparkles, a group over both glitter groups
|
||||
const bool sparkles = separate[static_cast<size_t>(UgcModel::eLook::GLITTER)] && settings.shaders.sparkle != 0;
|
||||
UgcModel::Model preview; // LOD 0 before the lighting bake, for the dashboard
|
||||
for (size_t i = 0; i < lods.size(); i++) {
|
||||
auto options = settings.build;
|
||||
@@ -251,6 +264,12 @@ namespace UgcJobs {
|
||||
for (const auto& piece : transparentPieces.back()[kind]) triangles += piece.TriangleCount();
|
||||
if (triangles > 0) byGroup[ShapeName(settings, kind ? UgcModel::eLook::GLITTER : UgcModel::eLook::PLASTIC, true)] = triangles;
|
||||
}
|
||||
if (sparkles) {
|
||||
size_t triangles = 0;
|
||||
for (const auto& piece : opaquePieces.back()[static_cast<size_t>(UgcModel::eLook::GLITTER)]) triangles += piece.TriangleCount();
|
||||
for (const auto& piece : transparentPieces.back()[1]) triangles += piece.TriangleCount();
|
||||
if (triangles > 0) byGroup[SparkleName(settings)] = triangles;
|
||||
}
|
||||
}
|
||||
lodStats.push_back(entry);
|
||||
}
|
||||
@@ -277,6 +296,21 @@ namespace UgcJobs {
|
||||
}
|
||||
if (any) out.push_back(std::move(group));
|
||||
}
|
||||
// Last, over everything: the sparkles over the opaque and the transparent glitter bricks
|
||||
if (sparkles) {
|
||||
UgcFormats::NifLodGroup group{ SparkleName(settings), false, {} };
|
||||
group.glitter = &settings.shaders.glitterParams;
|
||||
group.sparkle = true;
|
||||
bool any = false;
|
||||
for (size_t i = 0; i < levels; i++) {
|
||||
UgcFormats::NifLod lod{ ranges[i].first, ranges[i].second, "LOD_" + std::to_string(lods[i]), {} };
|
||||
for (const auto& piece : opaque[i][static_cast<size_t>(UgcModel::eLook::GLITTER)]) lod.pieces.push_back(&piece);
|
||||
for (const auto& piece : transparent[i][1]) lod.pieces.push_back(&piece);
|
||||
any = any || !lod.pieces.empty();
|
||||
group.lods.push_back(std::move(lod));
|
||||
}
|
||||
if (any) out.push_back(std::move(group));
|
||||
}
|
||||
return out;
|
||||
};
|
||||
const auto nif = UgcFormats::WriteLodNif("SceneNode_Model", groups(lods.size(), opaquePieces, transparentPieces));
|
||||
@@ -295,7 +329,7 @@ namespace UgcJobs {
|
||||
auto iconOptions = settings.icon;
|
||||
UgcIconParams::Apply(iconOptions, iconValues);
|
||||
std::string nifError;
|
||||
if (!IconFromNif(nif, iconOptions, outcome.files, nifError, settings.shaders.TagLooks())) {
|
||||
if (!IconFromNif(nif, iconOptions, outcome.files, nifError, settings.shaders.TagLooks(), settings.shaders.OverlayTags())) {
|
||||
outcome.error = "the .nif made can't be read back for the icon: " + nifError;
|
||||
return outcome;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -33,8 +34,11 @@ namespace UgcJobs {
|
||||
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_size, glitter_density, glitter_speed
|
||||
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;
|
||||
@@ -42,6 +46,9 @@ namespace UgcJobs {
|
||||
// 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 {
|
||||
@@ -81,14 +88,18 @@ namespace UgcJobs {
|
||||
|
||||
// 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::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);
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ void UgcProcessor::Worker() {
|
||||
const auto nif = m_Storage.ReadNif(Kind::MODEL, job.id, "model.nif");
|
||||
auto options = settings.icon;
|
||||
UgcIconParams::Apply(options, job.iconValues);
|
||||
outcome.ok = nif && UgcJobs::IconFromNif(*nif, options, outcome.files, outcome.error, settings.shaders.TagLooks());
|
||||
outcome.ok = nif && UgcJobs::IconFromNif(*nif, options, outcome.files, outcome.error, settings.shaders.TagLooks(), settings.shaders.OverlayTags());
|
||||
if (!nif) outcome.error = "the model has no stored .nif yet";
|
||||
}
|
||||
if (outcome.ok && outcome.files.contains("assembly.nif")) {
|
||||
@@ -289,7 +289,7 @@ void UgcProcessor::Worker() {
|
||||
const auto nif = m_Storage.ReadNif(Kind::MODEL, job.id, "model.nif");
|
||||
auto options = settings.icon;
|
||||
UgcIconParams::Apply(options, job.iconValues);
|
||||
done.outcome.ok = nif && UgcJobs::IconFromNif(*nif, options, done.outcome.files, done.outcome.error, settings.shaders.TagLooks());
|
||||
done.outcome.ok = nif && UgcJobs::IconFromNif(*nif, options, done.outcome.files, done.outcome.error, settings.shaders.TagLooks(), settings.shaders.OverlayTags());
|
||||
if (!nif) done.outcome.error = "no stored .nif";
|
||||
} else {
|
||||
done.outcome = job.kind == Kind::MODEL
|
||||
|
||||
Reference in New Issue
Block a user