diff --git a/dCommon/Lxfml.cpp b/dCommon/Lxfml.cpp index 5899169fb..6248189cf 100644 --- a/dCommon/Lxfml.cpp +++ b/dCommon/Lxfml.cpp @@ -127,16 +127,14 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin auto* lxfml = doc.FirstChildElement("LXFML"); if (!lxfml) return toReturn; - // Every bone of every part (flexible parts have several), and every rigid of every rigid system: both are - // positioned in the same space, so both move + // Every bone of every part (flexible parts have several). Only bones move, as the live server did: the rigids keep + // the transformations the client sent. std::vector bones; if (auto* bricks = lxfml->FirstChildElement("Bricks")) { for (auto* brick = bricks->FirstChildElement("Brick"); brick; brick = brick->NextSiblingElement("Brick")) { ForEachGrandchild(brick, "Part", "Bone", [&bones](tinyxml2::XMLElement* bone) { bones.push_back(bone); }); } } - std::vector rigids; - ForEachGrandchild(lxfml->FirstChildElement("RigidSystems"), "RigidSystem", "Rigid", [&rigids](tinyxml2::XMLElement* rigid) { rigids.push_back(rigid); }); // The new origin: the middle of the bricks' origins, on the floor of the lowest one. x and z are snapped to the // LEGO grid (0.8) so the model's position stays on it; the bricks don't move in the world either way, only the @@ -166,17 +164,15 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin rootX = GeneralUtils::RountToNearestEven(rootX, 0.8); rootZ = GeneralUtils::RountToNearestEven(rootZ, 0.8); - // Everything moves by the same amount: onto the new origin, then by the given position + // Every bone moves by the same amount: onto the new origin, then by the given position const double offsetX = curPosition.x - rootX, offsetY = curPosition.y - rootY, offsetZ = curPosition.z - rootZ; - for (auto* elements : { &bones, &rigids }) { - for (auto* element : *elements) { - auto transformation = ParseTransformation(element->Attribute("transformation")); - if (!transformation) continue; - transformation->x += offsetX; - transformation->y += offsetY; - transformation->z += offsetZ; - element->SetAttribute("transformation", FormatTransformation(*transformation).c_str()); - } + for (auto* bone : bones) { + auto transformation = ParseTransformation(bone->Attribute("transformation")); + if (!transformation) continue; + transformation->x += offsetX; + transformation->y += offsetY; + transformation->z += offsetZ; + bone->SetAttribute("transformation", FormatTransformation(*transformation).c_str()); } tinyxml2::XMLPrinter printer; diff --git a/tests/dCommonTests/LxfmlTests.cpp b/tests/dCommonTests/LxfmlTests.cpp index a4eabcf95..42503c42b 100644 --- a/tests/dCommonTests/LxfmlTests.cpp +++ b/tests/dCommonTests/LxfmlTests.cpp @@ -29,7 +29,7 @@ std::string SerializeElement(tinyxml2::XMLElement* elem) { return std::string(p.CStr()); }; -// A rigid system by what it holds (its rigids' refIDs and boneRefs): splitting moves its transformations +// A rigid system by what it holds (its rigids' refIDs and boneRefs) std::string RigidSystemKey(tinyxml2::XMLElement* rigidSystem) { std::string key; for (auto* rigid = rigidSystem->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) { @@ -424,25 +424,6 @@ TEST(LxfmlTests, DeepCloneDepthProtection) { } } -TEST(LxfmlTests, ReadContentsListsBricksAndTheirBox) { - const std::string lxfml = R"( - - - - - -)"; - const auto contents = Lxfml::ReadContents(lxfml); - EXPECT_EQ(contents.designIds, (std::vector{ 3710, 3001 })); - EXPECT_EQ(contents.boxMin, NiPoint3(-1.2f, 0.0f, -0.4f)); - EXPECT_EQ(contents.boxMax, NiPoint3(0.8f, 0.96f, 0.4f)); - - const auto nothing = Lxfml::ReadContents(""); - EXPECT_TRUE(nothing.designIds.empty()); - EXPECT_EQ(nothing.boxMin, NiPoint3Constant::ZERO); - EXPECT_TRUE(Lxfml::ReadContents("").designIds.empty()); -} - namespace { const std::string PROBE_HEAD = R"()"; @@ -482,15 +463,15 @@ namespace { } } -TEST(LxfmlTests, SplitMovesRigidsWithTheirBones) { +TEST(LxfmlTests, SplitMovesOnlyTheBones) { const auto results = Lxfml::Split(TWO_MODELS); ASSERT_EQ(results.size(), 2); EXPECT_EQ(results[0].center, NiPoint3(100.8f, 433.92f, -62.4f)); const auto bones = Transformations(results[0].lxfml, "Bone"); EXPECT_EQ(bones.at("0"), "1,0,0,0,1,0,0,0,1,-0.4,0,-0.4"); EXPECT_EQ(bones.at("1"), "1,0,0,0,1,0,0,0,1,-0.4,0.96,-0.4"); - // The rigid system sits where its first bone does, as the game writes it - EXPECT_EQ(Transformations(results[0].lxfml, "Rigid").at("0"), bones.at("0")); + // The rigid keeps the transformation the client sent, as the live server left it + EXPECT_EQ(Transformations(results[0].lxfml, "Rigid").at("0"), "1,0,0,0,1,0,0,0,1,100.4,433.92,-62.8"); } TEST(LxfmlTests, SplitMovesEveryBoneOfAFlexiblePart) { @@ -503,8 +484,8 @@ TEST(LxfmlTests, SplitMovesEveryBoneOfAFlexiblePart) { EXPECT_EQ(bones.at("3"), "1,0,0,0,1,0,0,0,1,13.6,0,0.4"); EXPECT_EQ(bones.at("4"), "1,0,0,0,-1,0,0,0,-1,-13.2,0,0.4"); const auto rigids = Transformations(results[1].lxfml, "Rigid"); - EXPECT_EQ(rigids.at("1"), bones.at("2")); - EXPECT_EQ(rigids.at("2"), bones.at("4")); + EXPECT_EQ(rigids.at("1"), "1,0,0,0,1,0,0,0,1,1234.4,433.92,-10.8"); + EXPECT_EQ(rigids.at("2"), "1,0,0,0,-1,0,0,0,-1,1210,433.92,-10.8"); } TEST(LxfmlTests, SplitKeepsTheFileOrder) { @@ -532,5 +513,24 @@ TEST(LxfmlTests, NormalizeAtAPositionKeepsTheBricksInPlace) { const auto result = Lxfml::NormalizePosition(lxfml, NiPoint3(10.5f, 5.0f, -3.0f)); EXPECT_EQ(result.center, NiPoint3(10.4f, 5.0f, -3.2f)); EXPECT_EQ(Transformations(result.lxfml, "Bone").at("0"), "1,0,0,0,1,0,0,0,1,0.1,0,0.2"); - EXPECT_EQ(Transformations(result.lxfml, "Rigid").at("0"), "1,0,0,0,1,0,0,0,1,0.1,0,0.2"); + EXPECT_EQ(Transformations(result.lxfml, "Rigid").at("0"), "1,0,0,0,1,0,0,0,1,0,0,0"); +} + +TEST(LxfmlTests, ReadContentsListsBricksAndTheirBox) { + const std::string lxfml = R"( + + + + + +)"; + const auto contents = Lxfml::ReadContents(lxfml); + EXPECT_EQ(contents.designIds, (std::vector{ 3710, 3001 })); + EXPECT_EQ(contents.boxMin, NiPoint3(-1.2f, 0.0f, -0.4f)); + EXPECT_EQ(contents.boxMax, NiPoint3(0.8f, 0.96f, 0.4f)); + + const auto nothing = Lxfml::ReadContents(""); + EXPECT_TRUE(nothing.designIds.empty()); + EXPECT_EQ(nothing.boxMin, NiPoint3Constant::ZERO); + EXPECT_TRUE(Lxfml::ReadContents("").designIds.empty()); }