mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
feat(ugc): the color variation settings apply to LU Toolbox in Blender too
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -89,7 +89,7 @@ namespace UgcToolbox {
|
||||
Worker::~Worker() = default;
|
||||
void Worker::Configure(const Config& config) { std::lock_guard lock(m_ConfigMutex); m_Config = config; }
|
||||
Config Worker::GetConfig() const { std::lock_guard lock(m_ConfigMutex); return m_Config; }
|
||||
Result Worker::Make(uint64_t, const std::string&, const std::vector<uint32_t>&, const std::function<void(double, const Pause&)>&) {
|
||||
Result Worker::Make(uint64_t, const std::string&, const std::vector<uint32_t>&, const std::function<void(double, const Pause&)>&, const UgcToolboxProtocol::ColorVariation&) {
|
||||
return { false, Problem(GetConfig()) };
|
||||
}
|
||||
void Worker::Stop() {}
|
||||
@@ -386,7 +386,8 @@ namespace UgcToolbox {
|
||||
return status;
|
||||
}
|
||||
|
||||
Result Worker::Make(uint64_t id, const std::string& lxfml, const std::vector<uint32_t>& lods, const std::function<void(double, const Pause&)>& pace) {
|
||||
Result Worker::Make(uint64_t id, const std::string& lxfml, const std::vector<uint32_t>& lods, const std::function<void(double, const Pause&)>& pace,
|
||||
const UgcToolboxProtocol::ColorVariation& variation) {
|
||||
Result result;
|
||||
const auto asked = std::chrono::steady_clock::now();
|
||||
std::unique_lock lock(m_JobMutex);
|
||||
@@ -434,7 +435,7 @@ namespace UgcToolbox {
|
||||
};
|
||||
|
||||
const auto requestId = m_NextId++;
|
||||
if (!process.Send(UgcToolboxProtocol::MakeRequest(requestId, input, output, lods))) {
|
||||
if (!process.Send(UgcToolboxProtocol::MakeRequest(requestId, input, output, lods, variation))) {
|
||||
result.error = "Blender isn't taking work (" + process.ExitReason() + ")";
|
||||
StopLocked(true);
|
||||
cleanUp();
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace UgcToolbox {
|
||||
*/
|
||||
using Pause = std::function<void(bool)>;
|
||||
Result Make(uint64_t id, const std::string& lxfml, const std::vector<uint32_t>& lods,
|
||||
const std::function<void(double cpuSeconds, const Pause& pause)>& pace = {});
|
||||
const std::function<void(double cpuSeconds, const Pause& pause)>& pace = {}, const UgcToolboxProtocol::ColorVariation& variation = {});
|
||||
|
||||
// Stops Blender (asks it to quit, then kills it); the next Make starts it again
|
||||
void Stop();
|
||||
|
||||
@@ -80,10 +80,14 @@ namespace UgcJobs {
|
||||
// LU Toolbox makes the .nif. Blender's CPU time is this thread's (the budget, the make's CPU time), and the
|
||||
// budget is kept by pausing Blender.
|
||||
step = std::chrono::steady_clock::now();
|
||||
UgcToolboxProtocol::ColorVariation variation;
|
||||
variation.mode = settings.build.variationMode == UgcModel::BuildOptions::eVariation::LIVE ? "live" : "toolbox";
|
||||
variation.percent = settings.build.colorVariation;
|
||||
variation.seed = id;
|
||||
const auto made = toolbox.Make(id, lxfml, lods, [](double cpuSeconds, const UgcToolbox::Worker::Pause& pause) {
|
||||
UgcThrottle::Charge(cpuSeconds);
|
||||
UgcThrottle::Checkpoint(pause);
|
||||
});
|
||||
}, variation);
|
||||
const double toolboxMs = Since(step);
|
||||
if (!made.ok) {
|
||||
outcome.error = made.error;
|
||||
|
||||
@@ -7,8 +7,10 @@ namespace UgcToolboxProtocol {
|
||||
return message.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace) + "\n";
|
||||
}
|
||||
|
||||
nlohmann::json MakeRequest(uint64_t id, const std::string& input, const std::string& output, const std::vector<uint32_t>& lods) {
|
||||
return { { "cmd", "make" }, { "id", id }, { "input", input }, { "output", output }, { "lods", lods } };
|
||||
nlohmann::json MakeRequest(uint64_t id, const std::string& input, const std::string& output, const std::vector<uint32_t>& lods,
|
||||
const ColorVariation& variation) {
|
||||
return { { "cmd", "make" }, { "id", id }, { "input", input }, { "output", output }, { "lods", lods },
|
||||
{ "colorVariation", { { "mode", variation.mode }, { "percent", variation.percent }, { "seed", variation.seed } } } };
|
||||
}
|
||||
|
||||
void LineReader::Feed(std::string_view data) {
|
||||
|
||||
@@ -21,9 +21,18 @@ namespace UgcToolboxProtocol {
|
||||
// A message as its line: compact JSON with "dlutb" set, and a newline
|
||||
std::string Frame(nlohmann::json message);
|
||||
|
||||
// The color variation a make uses (the color_variation* settings): "toolbox" (LU Toolbox's Apply Color Variation at
|
||||
// `percent`; 0: off) or "live" (live accurate: -6 to 6 added to each 0-255 channel of opaque bricks, from `seed`)
|
||||
struct ColorVariation {
|
||||
std::string mode{ "toolbox" };
|
||||
float percent{ 5.0f };
|
||||
uint64_t seed{};
|
||||
};
|
||||
|
||||
// The request to make one model: `input` the LXFML file, `output` the .nif to write, `lods` the brickprimitives
|
||||
// levels to import (LU Toolbox's importLOD0..3)
|
||||
nlohmann::json MakeRequest(uint64_t id, const std::string& input, const std::string& output, const std::vector<uint32_t>& lods);
|
||||
// levels to import (LU Toolbox's importLOD0..3), and its color variation
|
||||
nlohmann::json MakeRequest(uint64_t id, const std::string& input, const std::string& output, const std::vector<uint32_t>& lods,
|
||||
const ColorVariation& variation = {});
|
||||
|
||||
/**
|
||||
* Splits what is read from the worker into lines, however it arrives (parts of lines, many at once). A line longer
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
#
|
||||
# Each model goes through LU Toolbox itself, as LU-Toolbox-Standalone's lu_batch_driver.py runs it (its functions are
|
||||
# used, not copied): the LU Toolbox importer, Process Model, Bake Lighting and the niftools .nif export for LEGO
|
||||
# Universe. Nothing of LU Toolbox is changed or re-implemented here; this file only keeps Blender up between models.
|
||||
# Universe. Nothing of LU Toolbox is changed or re-implemented here; this file only keeps Blender up between models,
|
||||
# and sets the make's color variation (set_color_variation: the percent, or the live accurate one in place of LU
|
||||
# Toolbox's for that make).
|
||||
#
|
||||
# The protocol: one JSON object per line. Requests come on stdin; replies go to the original stdout, which only this
|
||||
# script writes to (everything Blender and the add-ons print is moved to stderr). Every message has "dlutb": 1.
|
||||
# -> {"dlutb":1,"cmd":"make","id":7,"input":"/x/7.lxfml","output":"/x/7.nif","lods":[0,2]}
|
||||
# -> {"dlutb":1,"cmd":"make","id":7,"input":"/x/7.lxfml","output":"/x/7.nif","lods":[0,2],
|
||||
# "colorVariation":{"mode":"toolbox"|"live","percent":5,"seed":..}}
|
||||
# <- {"dlutb":1,"type":"done","id":7,"ok":true,"ms":{"reset":..,"import":..,"process":..,"bake":..,"export":..}}
|
||||
# <- {"dlutb":1,"type":"done","id":7,"ok":false,"error":"..."}
|
||||
# -> {"dlutb":1,"cmd":"ping","id":8} <- {"dlutb":1,"type":"pong","id":8}
|
||||
@@ -171,6 +174,7 @@ class Worker:
|
||||
lod_kwargs = {f"importLOD{n}": n in lods for n in range(4)} if lods else None
|
||||
self.driver.try_import_lxf(os.path.abspath(request["input"]), op_override=None, lod_kwargs=lod_kwargs)
|
||||
lap("import")
|
||||
set_color_variation(request.get("colorVariation") or {})
|
||||
self.driver.call_op("lutb.process_model", "Process Model")
|
||||
lap("process")
|
||||
self.driver.ensure_vertex_colors_exist()
|
||||
@@ -186,6 +190,52 @@ class Worker:
|
||||
return ms
|
||||
|
||||
|
||||
def srgb_to_linear(c):
|
||||
return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4
|
||||
|
||||
|
||||
def linear_to_srgb(c):
|
||||
return c * 12.92 if c <= 0.0031308 else 1.055 * c ** (1 / 2.4) - 0.055
|
||||
|
||||
|
||||
def set_color_variation(variation):
|
||||
"""The make's color variation (the UGC server's color_variation_mode and color_variation settings) on LU Toolbox's
|
||||
Process Model. toolbox: its own Apply Color Variation at that percent. live (live accurate): instead of its
|
||||
variation, a whole number from -6 to 6 is added to each 0-255 sRGB channel of each material of each opaque object,
|
||||
transparent ones left alone; the numbers come from the model's id and repeat for every LOD, as LU Toolbox's do."""
|
||||
import bpy
|
||||
import random
|
||||
from lu_toolbox import process_model
|
||||
operator = process_model.LUTB_OT_process_model
|
||||
if "_dlu_toolbox_variation" not in operator.__dict__:
|
||||
operator._dlu_toolbox_variation = operator.apply_color_variation
|
||||
scene = bpy.context.scene
|
||||
if variation.get("mode") != "live":
|
||||
operator.apply_color_variation = operator._dlu_toolbox_variation
|
||||
percent = float(variation.get("percent", 5.0))
|
||||
scene.lutb_use_color_variation = percent > 0
|
||||
if percent > 0:
|
||||
scene.lutb_color_variation = percent
|
||||
return
|
||||
seed = int(variation.get("seed", 0))
|
||||
|
||||
def live_variation(self, context, collections):
|
||||
for collection in collections:
|
||||
for lod_collection in collection.children:
|
||||
rng = random.Random(seed)
|
||||
for obj in list(lod_collection.objects):
|
||||
if obj.type != "MESH" or obj.get(process_model.IS_TRANSPARENT):
|
||||
continue
|
||||
for material in obj.data.materials:
|
||||
step = rng.randint(-6, 6)
|
||||
color = [srgb_to_linear(min(max(round(linear_to_srgb(c) * 255) + step, 0), 255) / 255)
|
||||
for c in material.diffuse_color[:3]]
|
||||
material.diffuse_color = (*color, 1.0)
|
||||
|
||||
operator.apply_color_variation = live_variation
|
||||
scene.lutb_use_color_variation = True
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--standalone", required=True)
|
||||
|
||||
@@ -136,6 +136,10 @@ and the notices (see Threads). Cars and rockets are made once per combination of
|
||||
0.025) either way, times the color's own amount (black 0.4, orange 1.5, ...), clamped and taken back; hue and
|
||||
saturation stay. The random number comes from the model's id, the brick's index and the material, so making a
|
||||
model again gives the same colors, and every LOD the same (LU Toolbox restarts its random sequence per LOD).
|
||||
With `color_variation_mode=live` ("Live accurate") instead: a whole number from -6 to 6 (each equally likely, from
|
||||
the same random number) is added to each of the color's 0-255 sRGB channels, clamped; transparent bricks keep their
|
||||
color, and `color_variation` isn't used. Both kinds apply to either processor: with `toolbox-blender` the worker
|
||||
sets LU Toolbox's own variation to `color_variation`, or puts the live accurate one in its place for that make.
|
||||
4. Faces nobody can see are removed from the opaque bricks (they're rendered from 42 directions and triangles that
|
||||
never show are dropped; transparent bricks hide nothing and aren't touched; see "Hidden faces" below).
|
||||
`hsr_ground_plane=1` also drops what can only be seen from below.
|
||||
|
||||
@@ -197,6 +197,10 @@ TEST(UgcToolboxProtocol, FramesOneMessagePerLine) {
|
||||
const auto request = UgcToolboxProtocol::MakeRequest(7, "/w/7.lxfml", "/w/7.nif", { 0, 2 });
|
||||
EXPECT_EQ(request["cmd"], "make");
|
||||
EXPECT_EQ(request["lods"], nlohmann::json::array({ 0, 2 }));
|
||||
EXPECT_EQ(request["colorVariation"]["mode"], "toolbox");
|
||||
const auto live = UgcToolboxProtocol::MakeRequest(8, "/w/8.lxfml", "/w/8.nif", { 0 }, { "live", 5.0f, 1234 });
|
||||
EXPECT_EQ(live["colorVariation"]["mode"], "live");
|
||||
EXPECT_EQ(live["colorVariation"]["seed"], 1234u);
|
||||
|
||||
// Lines arrive in pieces and several at once
|
||||
UgcToolboxProtocol::LineReader reader;
|
||||
|
||||
Reference in New Issue
Block a user