Files
DarkflameServer/dCommon/Lxfml.h
Aaron Kimbrell d870207a9f fix: place brick by brick models by the bottom middle of their bricks' box
When a brick by brick save is split into models, each model was centred on
the middle of its bones' origins, on the lowest origin. A brick's origin is at
a corner of it, not its middle, so a single 2x2 came back offset by half a
brick, and the model's floor was taken from the origins rather than from the
bricks.

Now each model is placed as the live server placed it: at the bottom middle
of the box around its bricks. Each brick's own box comes from the client's
brickdb by its design ID and is turned and moved by its bone (all 8 corners),
and the model's box holds every brick's. Its x and z middle and its lowest y
are the new origin, still snapped to the 0.8 grid on x and z, and every bone
moves by that; rigids and rotations are untouched. A design the brickdb has no
box for counts its bones' origins, as before, so no brick is left out.

Lxfml::NormalizePosition and Split take the boxes by design ID, and the brick
by brick save passes the ones read from the client (read on the first save,
then kept).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 11:52:30 -06:00

43 lines
1.7 KiB
C++

// Darkflame Universe
// Copyright 2025
#ifndef LXFML_H
#define LXFML_H
#include <string>
#include <string_view>
#include <vector>
#include "BrickBounds.h"
#include "NiPoint3.h"
namespace Lxfml {
struct Result {
std::string lxfml;
NiPoint3 center;
};
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
// Returns a struct of its new center and the updated LXFML containing these edits.
// The new center is the bottom middle of the box around the model's bricks, each brick's box taken from `bounds` by
// its design ID (a brick with no box there counts as its bones' origins).
[[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO, const BrickBounds::DesignBounds& bounds = {});
[[nodiscard]] std::vector<Result> Split(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO, const BrickBounds::DesignBounds& bounds = {});
// What a model is made of: every brick's design id (one per Brick, in file order) and the box around the bricks'
// origins (their bones' positions; zero with no bricks). Brick shapes aren't known here, so the box is the
// smallest one holding every brick's origin.
struct Contents {
std::vector<uint32_t> designIds;
NiPoint3 boxMin{};
NiPoint3 boxMax{};
};
[[nodiscard]] Contents ReadContents(const std::string_view data);
// these are only for the migrations due to a bug in one of the implementations.
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);
[[nodiscard]] Result NormalizePositionAfterFirstPart(const std::string_view data, const NiPoint3& position);
};
#endif //!LXFML_H