mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-10-31 04:32:06 +00:00 
			
		
		
		
	LXFML SPLITTING
Included test file
This commit is contained in:
		| @@ -5,6 +5,10 @@ | ||||
| #include "TinyXmlUtils.h" | ||||
|  | ||||
| #include <ranges> | ||||
| #include <unordered_map> | ||||
| #include <unordered_set> | ||||
| #include <functional> | ||||
| #include <sstream> | ||||
|  | ||||
| Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) { | ||||
| 	Result toReturn; | ||||
| @@ -128,3 +132,270 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin | ||||
| 	toReturn.center = newRootPos; | ||||
| 	return toReturn; | ||||
| } | ||||
|  | ||||
| // Deep-clone an XMLElement (attributes, text, and child elements) into a target document | ||||
| static tinyxml2::XMLElement* CloneElementDeep(const tinyxml2::XMLElement* src, tinyxml2::XMLDocument& dstDoc) { | ||||
| 	if (!src) return nullptr; | ||||
| 	auto* dst = dstDoc.NewElement(src->Name()); | ||||
|  | ||||
| 	// copy attributes | ||||
| 	for (const tinyxml2::XMLAttribute* attr = src->FirstAttribute(); attr; attr = attr->Next()) { | ||||
| 		dst->SetAttribute(attr->Name(), attr->Value()); | ||||
| 	} | ||||
|  | ||||
| 	// copy children (elements and text) | ||||
| 	for (const tinyxml2::XMLNode* child = src->FirstChild(); child; child = child->NextSibling()) { | ||||
| 		if (const tinyxml2::XMLElement* childElem = child->ToElement()) { | ||||
| 			dst->InsertEndChild(CloneElementDeep(childElem, dstDoc)); | ||||
| 		} else if (const tinyxml2::XMLText* txt = child->ToText()) { | ||||
| 			auto* n = dstDoc.NewText(txt->Value()); | ||||
| 			dst->InsertEndChild(n); | ||||
| 		} else if (const tinyxml2::XMLComment* c = child->ToComment()) { | ||||
| 			auto* n = dstDoc.NewComment(c->Value()); | ||||
| 			dst->InsertEndChild(n); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return dst; | ||||
| } | ||||
|  | ||||
| std::vector<Lxfml::Result> Lxfml::Split(const std::string_view data, const NiPoint3& curPosition) { | ||||
| 	std::vector<Result> results; | ||||
| 	tinyxml2::XMLDocument doc; | ||||
| 	const auto err = doc.Parse(data.data()); | ||||
| 	if (err != tinyxml2::XML_SUCCESS) { | ||||
| 		LOG("Failed to parse xml %s.", StringifiedEnum::ToString(err).data()); | ||||
| 		return results; | ||||
| 	} | ||||
|  | ||||
| 	auto* lxfml = doc.FirstChildElement("LXFML"); | ||||
| 	if (!lxfml) { | ||||
| 		LOG("Failed to find LXFML element."); | ||||
| 		return results; | ||||
| 	} | ||||
|  | ||||
| 	// Build maps: partRef -> Part element, partRef -> Brick element, boneRef -> partRef, brickRef -> Brick element | ||||
| 	std::unordered_map<std::string, tinyxml2::XMLElement*> partRefToPart; | ||||
| 	std::unordered_map<std::string, tinyxml2::XMLElement*> partRefToBrick; | ||||
| 	std::unordered_map<std::string, std::string> boneRefToPartRef; | ||||
| 	std::unordered_map<std::string, tinyxml2::XMLElement*> brickByRef; | ||||
|  | ||||
| 	auto* bricksParent = lxfml->FirstChildElement("Bricks"); | ||||
| 	if (bricksParent) { | ||||
| 		for (auto* brick = bricksParent->FirstChildElement("Brick"); brick; brick = brick->NextSiblingElement("Brick")) { | ||||
| 			const char* brickRef = brick->Attribute("refID"); | ||||
| 			if (brickRef) brickByRef.emplace(std::string(brickRef), brick); | ||||
| 			for (auto* part = brick->FirstChildElement("Part"); part; part = part->NextSiblingElement("Part")) { | ||||
| 				const char* partRef = part->Attribute("refID"); | ||||
| 				if (partRef) { | ||||
| 					partRefToPart.emplace(std::string(partRef), part); | ||||
| 					partRefToBrick.emplace(std::string(partRef), brick); | ||||
| 				} | ||||
| 				auto* bone = part->FirstChildElement("Bone"); | ||||
| 				if (bone) { | ||||
| 					const char* boneRef = bone->Attribute("refID"); | ||||
| 					if (boneRef) boneRefToPartRef.emplace(std::string(boneRef), partRef ? std::string(partRef) : std::string()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Collect RigidSystem elements | ||||
| 	std::vector<tinyxml2::XMLElement*> rigidSystems; | ||||
| 	auto* rigidSystemsParent = lxfml->FirstChildElement("RigidSystems"); | ||||
| 	if (rigidSystemsParent) { | ||||
| 		for (auto* rs = rigidSystemsParent->FirstChildElement("RigidSystem"); rs; rs = rs->NextSiblingElement("RigidSystem")) { | ||||
| 			rigidSystems.push_back(rs); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Collect top-level groups (immediate children of GroupSystem) | ||||
| 	std::vector<tinyxml2::XMLElement*> groupRoots; | ||||
| 	auto* groupSystemsParent = lxfml->FirstChildElement("GroupSystems"); | ||||
| 	if (groupSystemsParent) { | ||||
| 		for (auto* gs = groupSystemsParent->FirstChildElement("GroupSystem"); gs; gs = gs->NextSiblingElement("GroupSystem")) { | ||||
| 			for (auto* group = gs->FirstChildElement("Group"); group; group = group->NextSiblingElement("Group")) { | ||||
| 				groupRoots.push_back(group); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Track used bricks and rigidsystems | ||||
| 	std::unordered_set<std::string> usedBrickRefs; | ||||
| 	std::unordered_set<tinyxml2::XMLElement*> usedRigidSystems; | ||||
|  | ||||
| 	auto splitListAttr = [](const char* attr) { | ||||
| 		std::vector<std::string> out; | ||||
| 		if (!attr) return out; | ||||
| 		std::stringstream ss(attr); | ||||
| 		std::string token; | ||||
| 		while (std::getline(ss, token, ',')) { | ||||
| 			if (!token.empty()) out.push_back(token); | ||||
| 		} | ||||
| 		return out; | ||||
| 	}; | ||||
|  | ||||
| 	// Helper to create output document from sets of brick refs and rigidsystem pointers | ||||
| 	auto makeOutput = [&](const std::unordered_set<std::string>& bricksToInclude, const std::vector<tinyxml2::XMLElement*>& rigidSystemsToInclude, const std::vector<tinyxml2::XMLElement*>& groupsToInclude = {}) { | ||||
| 		tinyxml2::XMLDocument outDoc; | ||||
| 		outDoc.Parse(Lxfml::base.c_str()); | ||||
| 		auto* outRoot = outDoc.FirstChildElement("LXFML"); | ||||
| 		auto* outBricks = outRoot->FirstChildElement("Bricks"); | ||||
| 		auto* outRigidSystems = outRoot->FirstChildElement("RigidSystems"); | ||||
| 		auto* outGroupSystems = outRoot->FirstChildElement("GroupSystems"); | ||||
|  | ||||
| 		// clone and insert bricks | ||||
| 		for (const auto& bref : bricksToInclude) { | ||||
| 			auto it = brickByRef.find(bref); | ||||
| 			if (it == brickByRef.end()) continue; | ||||
| 			tinyxml2::XMLElement* cloned = CloneElementDeep(it->second, outDoc); | ||||
| 			if (cloned) outBricks->InsertEndChild(cloned); | ||||
| 		} | ||||
|  | ||||
| 		// clone and insert rigidsystems | ||||
| 		for (auto* rsPtr : rigidSystemsToInclude) { | ||||
| 			tinyxml2::XMLElement* cloned = CloneElementDeep(rsPtr, outDoc); | ||||
| 			if (cloned) outRigidSystems->InsertEndChild(cloned); | ||||
| 		} | ||||
|  | ||||
| 		// clone and insert group(s) if requested | ||||
| 		if (outGroupSystems && !groupsToInclude.empty()) { | ||||
| 			// clear default children | ||||
| 			while (outGroupSystems->FirstChild()) outGroupSystems->DeleteChild(outGroupSystems->FirstChild()); | ||||
| 			// create a GroupSystem element and append requested groups | ||||
| 			auto* newGS = outDoc.NewElement("GroupSystem"); | ||||
| 			for (auto* gptr : groupsToInclude) { | ||||
| 				tinyxml2::XMLElement* clonedG = CloneElementDeep(gptr, outDoc); | ||||
| 				if (clonedG) newGS->InsertEndChild(clonedG); | ||||
| 			} | ||||
| 			outGroupSystems->InsertEndChild(newGS); | ||||
| 		} | ||||
|  | ||||
| 		// Print to string | ||||
| 		tinyxml2::XMLPrinter printer; | ||||
| 		outDoc.Print(&printer); | ||||
| 		// Normalize position and compute center using existing helper | ||||
| 		auto normalized = NormalizePosition(printer.CStr(), curPosition); | ||||
| 		return normalized; | ||||
| 	}; | ||||
|  | ||||
| 	// 1) Process groups (each top-level Group becomes one output; nested groups are included) | ||||
| 	for (auto* groupRoot : groupRoots) { | ||||
| 		// collect all partRefs in this group's subtree | ||||
| 		std::unordered_set<std::string> partRefs; | ||||
| 		std::function<void(const tinyxml2::XMLElement*)> collectParts = [&](const tinyxml2::XMLElement* g) { | ||||
| 			if (!g) return; | ||||
| 			const char* partAttr = g->Attribute("partRefs"); | ||||
| 			if (partAttr) { | ||||
| 				for (auto& tok : splitListAttr(partAttr)) partRefs.insert(tok); | ||||
| 			} | ||||
| 			for (auto* child = g->FirstChildElement("Group"); child; child = child->NextSiblingElement("Group")) collectParts(child); | ||||
| 		}; | ||||
| 		collectParts(groupRoot); | ||||
|  | ||||
| 		// Build initial sets of bricks and boneRefs | ||||
| 		std::unordered_set<std::string> bricksIncluded; | ||||
| 		std::unordered_set<std::string> boneRefsIncluded; | ||||
| 		for (const auto& pref : partRefs) { | ||||
| 			auto pit = partRefToBrick.find(pref); | ||||
| 			if (pit != partRefToBrick.end()) { | ||||
| 				const char* bref = pit->second->Attribute("refID"); | ||||
| 				if (bref) bricksIncluded.insert(std::string(bref)); | ||||
| 			} | ||||
| 			auto partIt = partRefToPart.find(pref); | ||||
| 			if (partIt != partRefToPart.end()) { | ||||
| 				auto* bone = partIt->second->FirstChildElement("Bone"); | ||||
| 				if (bone) { | ||||
| 					const char* bref = bone->Attribute("refID"); | ||||
| 					if (bref) boneRefsIncluded.insert(std::string(bref)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Iteratively include any RigidSystems that reference any boneRefsIncluded | ||||
| 		bool changed = true; | ||||
| 		std::vector<tinyxml2::XMLElement*> rigidSystemsToInclude; | ||||
| 		while (changed) { | ||||
| 			changed = false; | ||||
| 			for (auto* rs : rigidSystems) { | ||||
| 				if (usedRigidSystems.find(rs) != usedRigidSystems.end()) continue; | ||||
| 				// parse boneRefs of this rigid system (from its <Rigid> children) | ||||
| 				bool intersects = false; | ||||
| 				std::vector<std::string> rsBoneRefs; | ||||
| 				for (auto* rigid = rs->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) { | ||||
| 					const char* battr = rigid->Attribute("boneRefs"); | ||||
| 					if (!battr) continue; | ||||
| 					for (auto& tok : splitListAttr(battr)) { | ||||
| 						rsBoneRefs.push_back(tok); | ||||
| 						if (boneRefsIncluded.find(tok) != boneRefsIncluded.end()) intersects = true; | ||||
| 					} | ||||
| 				} | ||||
| 				if (!intersects) continue; | ||||
| 				// include this rigid system and all boneRefs it references | ||||
| 				usedRigidSystems.insert(rs); | ||||
| 				rigidSystemsToInclude.push_back(rs); | ||||
| 				for (const auto& br : rsBoneRefs) { | ||||
| 					boneRefsIncluded.insert(br); | ||||
| 					auto bpIt = boneRefToPartRef.find(br); | ||||
| 					if (bpIt != boneRefToPartRef.end()) { | ||||
| 						auto partRef = bpIt->second; | ||||
| 						auto pbIt = partRefToBrick.find(partRef); | ||||
| 						if (pbIt != partRefToBrick.end()) { | ||||
| 							const char* bref = pbIt->second->Attribute("refID"); | ||||
| 							if (bref && bricksIncluded.insert(std::string(bref)).second) changed = true; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				// also include bricks for any newly discovered parts | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// include bricks from bricksIncluded into used set | ||||
| 		for (const auto& b : bricksIncluded) usedBrickRefs.insert(b); | ||||
|  | ||||
| 		// make output doc and push result (include this group's XML) | ||||
| 		std::vector<tinyxml2::XMLElement*> groupsVec{ groupRoot }; | ||||
| 		auto normalized = makeOutput(bricksIncluded, rigidSystemsToInclude, groupsVec); | ||||
| 			results.push_back(normalized); | ||||
| 	} | ||||
|  | ||||
| 	// 2) Process remaining RigidSystems (each becomes its own file) | ||||
| 	for (auto* rs : rigidSystems) { | ||||
| 		if (usedRigidSystems.find(rs) != usedRigidSystems.end()) continue; | ||||
| 		std::unordered_set<std::string> bricksIncluded; | ||||
| 		// collect boneRefs referenced by this rigid system | ||||
| 		for (auto* rigid = rs->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) { | ||||
| 			const char* battr = rigid->Attribute("boneRefs"); | ||||
| 			if (!battr) continue; | ||||
| 			for (auto& tok : splitListAttr(battr)) { | ||||
| 				auto bpIt = boneRefToPartRef.find(tok); | ||||
| 				if (bpIt != boneRefToPartRef.end()) { | ||||
| 					auto partRef = bpIt->second; | ||||
| 					auto pbIt = partRefToBrick.find(partRef); | ||||
| 					if (pbIt != partRefToBrick.end()) { | ||||
| 						const char* bref = pbIt->second->Attribute("refID"); | ||||
| 						if (bref) bricksIncluded.insert(std::string(bref)); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// mark used | ||||
| 		for (const auto& b : bricksIncluded) usedBrickRefs.insert(b); | ||||
| 		usedRigidSystems.insert(rs); | ||||
|  | ||||
| 		std::vector<tinyxml2::XMLElement*> rsVec{ rs }; | ||||
| 		auto normalized = makeOutput(bricksIncluded, rsVec); | ||||
| 		results.push_back(normalized); | ||||
| 	} | ||||
|  | ||||
| 	// 3) Any remaining bricks not included become their own files | ||||
| 	for (const auto& [bref, brickPtr] : brickByRef) { | ||||
| 		if (usedBrickRefs.find(bref) != usedBrickRefs.end()) continue; | ||||
| 		std::unordered_set<std::string> bricksIncluded{ bref }; | ||||
| 		auto normalized = makeOutput(bricksIncluded, {}); | ||||
| 		results.push_back(normalized); | ||||
| 		usedBrickRefs.insert(bref); | ||||
| 	} | ||||
|  | ||||
| 	return results; | ||||
| } | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| #include <string> | ||||
| #include <string_view> | ||||
| #include <vector> | ||||
|  | ||||
| #include "NiPoint3.h" | ||||
|  | ||||
| @@ -18,6 +19,25 @@ namespace Lxfml { | ||||
| 	// 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. | ||||
| 	[[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO); | ||||
| 	[[nodiscard]] std::vector<Result> Split(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO); | ||||
|  | ||||
| 	// The base LXFML xml file to use when creating new models. | ||||
| 	static inline std::string base = R"(<?xml version="1.0" encoding="UTF-8" standalone="no" ?> | ||||
| <LXFML versionMajor="5" versionMinor="0"> | ||||
| <Meta> | ||||
|     <Application name="LEGO Universe" versionMajor="0" versionMinor="0"/> | ||||
|     <Brand name="LEGOUniverse"/> | ||||
|     <BrickSet version="457"/> | ||||
| </Meta> | ||||
| <Bricks> | ||||
| </Bricks> | ||||
| <RigidSystems> | ||||
| </RigidSystems> | ||||
| <GroupSystems> | ||||
|     <GroupSystem> | ||||
|     </GroupSystem> | ||||
| </GroupSystems> | ||||
| </LXFML>)"; | ||||
|  | ||||
| 	// these are only for the migrations due to a bug in one of the implementations. | ||||
| 	[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data); | ||||
|   | ||||
| @@ -10,6 +10,7 @@ set(DCOMMONTEST_SOURCES | ||||
| 	"TestLUString.cpp" | ||||
| 	"TestLUWString.cpp" | ||||
| 	"dCommonDependencies.cpp" | ||||
| 	"lxfml_split_tests.cpp" | ||||
| ) | ||||
|  | ||||
| add_subdirectory(dEnumsTests) | ||||
|   | ||||
							
								
								
									
										145
									
								
								tests/dCommonTests/lxfml_split_tests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								tests/dCommonTests/lxfml_split_tests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| #include "gtest/gtest.h" | ||||
|  | ||||
| #include "Lxfml.h" | ||||
| #include "TinyXmlUtils.h" | ||||
|  | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
| #include <unordered_set> | ||||
| #include <filesystem> | ||||
|  | ||||
| using namespace TinyXmlUtils; | ||||
|  | ||||
| static std::string ReadFile(const std::string& path) { | ||||
|     std::ifstream in(path, std::ios::in | std::ios::binary); | ||||
|     std::ostringstream ss; | ||||
|     ss << in.rdbuf(); | ||||
|     return ss.str(); | ||||
| } | ||||
|  | ||||
| TEST(LxfmlSplitTests, SplitUsesAllBricksAndNoDuplicates) { | ||||
|     // Read the sample test.lxfml included in tests. Resolve path relative to this source file. | ||||
|     std::filesystem::path srcDir = std::filesystem::path(__FILE__).parent_path(); | ||||
|     std::filesystem::path filePath = srcDir / "test.lxfml"; | ||||
|     std::string data = ReadFile(filePath.string()); | ||||
|     ASSERT_FALSE(data.empty()) << "Failed to read " << filePath.string(); | ||||
|      | ||||
|  | ||||
|     auto results = Lxfml::Split(data); | ||||
|     ASSERT_GT(results.size(), 0); | ||||
|  | ||||
|     // Write split outputs to disk for manual inspection | ||||
|     std::filesystem::path outDir = srcDir / "lxfml_splits"; | ||||
|     std::error_code ec; | ||||
|     std::filesystem::create_directories(outDir, ec); | ||||
|     for (size_t i = 0; i < results.size(); ++i) { | ||||
|         auto outPath = outDir / ("split_" + std::to_string(i) + ".lxfml"); | ||||
|         std::ofstream ofs(outPath, std::ios::out | std::ios::binary); | ||||
|         ASSERT_TRUE(ofs) << "Failed to open output file: " << outPath.string(); | ||||
|         ofs << results[i].lxfml; | ||||
|         ofs.close(); | ||||
|     } | ||||
|  | ||||
|     // parse original to count bricks | ||||
|     tinyxml2::XMLDocument doc; | ||||
|     ASSERT_EQ(doc.Parse(data.c_str()), tinyxml2::XML_SUCCESS); | ||||
|     DocumentReader reader(doc); | ||||
|     auto lxfml = reader["LXFML"]; | ||||
|     ASSERT_TRUE(lxfml); | ||||
|  | ||||
|     // Collect original RigidSystems and Groups (serialize each element string) | ||||
|     auto serializeElement = [](tinyxml2::XMLElement* elem) { | ||||
|         tinyxml2::XMLPrinter p; | ||||
|         elem->Accept(&p); | ||||
|         return std::string(p.CStr()); | ||||
|     }; | ||||
|  | ||||
|     std::unordered_set<std::string> originalRigidSet; | ||||
|     if (auto* rsParent = doc.FirstChildElement("LXFML")->FirstChildElement("RigidSystems")) { | ||||
|         for (auto* rs = rsParent->FirstChildElement("RigidSystem"); rs; rs = rs->NextSiblingElement("RigidSystem")) { | ||||
|             originalRigidSet.insert(serializeElement(rs)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::unordered_set<std::string> originalGroupSet; | ||||
|     if (auto* gsParent = doc.FirstChildElement("LXFML")->FirstChildElement("GroupSystems")) { | ||||
|         for (auto* gs = gsParent->FirstChildElement("GroupSystem"); gs; gs = gs->NextSiblingElement("GroupSystem")) { | ||||
|             for (auto* g = gs->FirstChildElement("Group"); g; g = g->NextSiblingElement("Group")) { | ||||
|                 // collect this group and nested groups | ||||
|                 std::function<void(tinyxml2::XMLElement*)> collectGroups = [&](tinyxml2::XMLElement* grp) { | ||||
|                     originalGroupSet.insert(serializeElement(grp)); | ||||
|                     for (auto* child = grp->FirstChildElement("Group"); child; child = child->NextSiblingElement("Group")) collectGroups(child); | ||||
|                 }; | ||||
|                 collectGroups(g); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::unordered_set<std::string> originalBricks; | ||||
|     for (const auto& brick : lxfml["Bricks"]) { | ||||
|         const auto* ref = brick.Attribute("refID"); | ||||
|         if (ref) originalBricks.insert(ref); | ||||
|     } | ||||
|     ASSERT_GT(originalBricks.size(), 0); | ||||
|  | ||||
|     // Collect bricks across all results and ensure no duplicates and all used | ||||
|     std::unordered_set<std::string> usedBricks; | ||||
|     // Track used rigid systems and groups (serialized strings) | ||||
|     std::unordered_set<std::string> usedRigidSet; | ||||
|     std::unordered_set<std::string> usedGroupSet; | ||||
|     for (const auto& res : results) { | ||||
|         tinyxml2::XMLDocument outDoc; | ||||
|         ASSERT_EQ(outDoc.Parse(res.lxfml.c_str()), tinyxml2::XML_SUCCESS); | ||||
|         DocumentReader outReader(outDoc); | ||||
|         auto outLxfml = outReader["LXFML"]; | ||||
|         ASSERT_TRUE(outLxfml); | ||||
|         // collect rigid systems in this output | ||||
|         if (auto* rsParent = outDoc.FirstChildElement("LXFML")->FirstChildElement("RigidSystems")) { | ||||
|             for (auto* rs = rsParent->FirstChildElement("RigidSystem"); rs; rs = rs->NextSiblingElement("RigidSystem")) { | ||||
|                 auto s = serializeElement(rs); | ||||
|                 // no duplicate allowed across outputs | ||||
|                 ASSERT_EQ(usedRigidSet.find(s), usedRigidSet.end()) << "Duplicate RigidSystem across splits"; | ||||
|                 usedRigidSet.insert(s); | ||||
|             } | ||||
|         } | ||||
|         // collect groups in this output | ||||
|         if (auto* gsParent = outDoc.FirstChildElement("LXFML")->FirstChildElement("GroupSystems")) { | ||||
|             for (auto* gs = gsParent->FirstChildElement("GroupSystem"); gs; gs = gs->NextSiblingElement("GroupSystem")) { | ||||
|                 for (auto* g = gs->FirstChildElement("Group"); g; g = g->NextSiblingElement("Group")) { | ||||
|                     std::function<void(tinyxml2::XMLElement*)> collectGroupsOut = [&](tinyxml2::XMLElement* grp) { | ||||
|                         auto s = serializeElement(grp); | ||||
|                         ASSERT_EQ(usedGroupSet.find(s), usedGroupSet.end()) << "Duplicate Group across splits"; | ||||
|                         usedGroupSet.insert(s); | ||||
|                         for (auto* child = grp->FirstChildElement("Group"); child; child = child->NextSiblingElement("Group")) collectGroupsOut(child); | ||||
|                     }; | ||||
|                     collectGroupsOut(g); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         for (const auto& brick : outLxfml["Bricks"]) { | ||||
|             const auto* ref = brick.Attribute("refID"); | ||||
|             if (ref) { | ||||
|                 // no duplicate allowed | ||||
|                 ASSERT_EQ(usedBricks.find(ref), usedBricks.end()) << "Duplicate brick ref across splits: " << ref; | ||||
|                 usedBricks.insert(ref); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Every original brick must be used in one of the outputs | ||||
|     for (const auto& bref : originalBricks) { | ||||
|         ASSERT_NE(usedBricks.find(bref), usedBricks.end()) << "Brick not used in splits: " << bref; | ||||
|     } | ||||
|  | ||||
|     // And usedBricks should not contain anything outside original | ||||
|     for (const auto& ub : usedBricks) { | ||||
|         ASSERT_NE(originalBricks.find(ub), originalBricks.end()) << "Split produced unknown brick: " << ub; | ||||
|     } | ||||
|  | ||||
|     // Ensure all original rigid systems and groups were used exactly once | ||||
|     ASSERT_EQ(originalRigidSet.size(), usedRigidSet.size()) << "RigidSystem count mismatch"; | ||||
|     for (const auto& s : originalRigidSet) ASSERT_NE(usedRigidSet.find(s), usedRigidSet.end()) << "RigidSystem missing in splits"; | ||||
|  | ||||
|     ASSERT_EQ(originalGroupSet.size(), usedGroupSet.size()) << "Group count mismatch"; | ||||
|     for (const auto& s : originalGroupSet) ASSERT_NE(usedGroupSet.find(s), usedGroupSet.end()) << "Group missing in splits"; | ||||
| } | ||||
							
								
								
									
										336
									
								
								tests/dCommonTests/test.lxfml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								tests/dCommonTests/test.lxfml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,336 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no" ?> | ||||
| <LXFML versionMajor="5" versionMinor="0"> | ||||
|     <Meta> | ||||
|         <Application name="LEGO Universe" versionMajor="0" versionMinor="0"/> | ||||
|         <Brand name="LEGOUniverse"/> | ||||
|         <BrickSet version="457"/> | ||||
|     </Meta> | ||||
|     <Bricks> | ||||
|         <Brick refID="0" designID="74340"> | ||||
|             <Part refID="0" designID="3679" materials="23"> | ||||
|                 <Bone refID="0" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,10.7959,4.83179,1.36732"/> | ||||
|             </Part> | ||||
|             <Part refID="1" designID="3680" materials="23"> | ||||
|                 <Bone refID="1" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,10.8444,4.54236,1.49497"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="1" designID="41533"> | ||||
|             <Part refID="2" designID="41533" materials="23"> | ||||
|                 <Bone refID="2" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,11.5689,3.28748,3.18812"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="2" designID="74340"> | ||||
|             <Part refID="3" designID="3679" materials="23"> | ||||
|                 <Bone refID="3" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,11.5689,3.28748,3.18812"/> | ||||
|             </Part> | ||||
|             <Part refID="4" designID="3680" materials="23"> | ||||
|                 <Bone refID="4" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,11.6174,2.99808,3.31576"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="3" designID="41533"> | ||||
|             <Part refID="5" designID="41533" materials="23"> | ||||
|                 <Bone refID="5" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,12.3419,1.74316,5.0089"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="4" designID="3614"> | ||||
|             <Part refID="6" designID="3614" materials="23"> | ||||
|                 <Bone refID="6" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,13.1227,1.67822,5.36752"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="5" designID="3004"> | ||||
|             <Part refID="7" designID="3004" materials="23,0,0,0" decoration="0,0,0"> | ||||
|                 <Bone refID="7" transformation="0,0,-1,0,1,0,1,0,0,22,0.320038,10.8"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="6" designID="3036"> | ||||
|             <Part refID="8" designID="3036" materials="23"> | ||||
|                 <Bone refID="8" transformation="1,0,0,0,1,0,0,0,1,18,3.05176e-05,12.4"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="7" designID="3036"> | ||||
|             <Part refID="9" designID="3036" materials="23"> | ||||
|                 <Bone refID="9" transformation="1,0,0,0,1,0,0,0,1,14.8,0.320038,13.2"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="8" designID="3036"> | ||||
|             <Part refID="10" designID="3036" materials="23"> | ||||
|                 <Bone refID="10" transformation="1,0,0,0,1,0,0,0,1,13.2,0.640045,11.6"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="9" designID="6106"> | ||||
|             <Part refID="11" designID="6106" materials="23"> | ||||
|                 <Bone refID="11" transformation="1,0,0,0,1.0000001192092896,0,0,0,1,12.4,0.960052,6.8"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="10" designID="6106"> | ||||
|             <Part refID="12" designID="6106" materials="23"> | ||||
|                 <Bone refID="12" transformation="1,0,0,0,1.0000001192092896,0,0,0,1,13.2,1.28006,6.8"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="11" designID="6106"> | ||||
|             <Part refID="13" designID="6106" materials="23"> | ||||
|                 <Bone refID="13" transformation="1,0,0,0,1.0000001192092896,0,0,0,1,12.4,1.60007,6.8"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="12" designID="3730"> | ||||
|             <Part refID="14" designID="3730" materials="23"> | ||||
|                 <Bone refID="14" transformation="-1,0,0,0,1,0,0,0,-1,13.2,1.92007,6.8"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="13" designID="73587"> | ||||
|             <Part refID="15" designID="4592" materials="23"> | ||||
|                 <Bone refID="15" transformation="1,0,0,0,1,0,0,0,1,15.6,1.92007,6.8"/> | ||||
|             </Part> | ||||
|             <Part refID="16" designID="4593" materials="23"> | ||||
|                 <Bone refID="16" transformation="0.70092326402664185,-0.71323668956756592,0,0.71323668956756592,0.70092326402664185,0,0,0,1,15.6,2.34009,6.8"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="14" designID="3688"> | ||||
|             <Part refID="17" designID="3688" materials="23"> | ||||
|                 <Bone refID="17" transformation="1,0,0,0,1,0,0,0,1,3.6,0.960022,15.6"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="15" designID="4085"> | ||||
|             <Part refID="18" designID="4085" materials="23"> | ||||
|                 <Bone refID="18" transformation="1,0,0,0,1,0,0,0,1,5.2,0.960022,15.6"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="16" designID="3046"> | ||||
|             <Part refID="19" designID="3046" materials="23"> | ||||
|                 <Bone refID="19" transformation="1,0,0,0,1,0,0,0,1,4.4,3.05176e-05,14.8"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="17" designID="73587"> | ||||
|             <Part refID="20" designID="4592" materials="23"> | ||||
|                 <Bone refID="20" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-21.2,3.05176e-05,-9.2"/> | ||||
|             </Part> | ||||
|             <Part refID="21" designID="4593" materials="23"> | ||||
|                 <Bone refID="21" transformation="0,-0.71323662996292114,-0.70092326402664185,0,0.70092320442199707,-0.71323668956756592,1,0,0,-21.2,0.420044,-9.2"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="18" designID="74340"> | ||||
|             <Part refID="22" designID="3679" materials="23"> | ||||
|                 <Bone refID="22" transformation="0.28873360157012939,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140815079212189,0.87035715579986572,0.3214133083820343,0.37305739521980286,-16.0119,3.28745,-5.96892"/> | ||||
|             </Part> | ||||
|             <Part refID="23" designID="3680" materials="23"> | ||||
|                 <Bone refID="23" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-15.8842,2.99805,-6.01737"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="19" designID="41533"> | ||||
|             <Part refID="24" designID="41533" materials="23"> | ||||
|                 <Bone refID="24" transformation="0.28873360157012939,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.3214133083820343,0.37305739521980286,-16.0119,3.28745,-5.96892"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="20" designID="41533"> | ||||
|             <Part refID="25" designID="41533" materials="23"> | ||||
|                 <Bone refID="25" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-14.1911,1.74313,-6.74193"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="21" designID="3730"> | ||||
|             <Part refID="26" designID="3730" materials="23"> | ||||
|                 <Bone refID="26" transformation="0,0,1,0,0.99999994039535522,0,-1,0,0,-12.4,1.92004,-7.60001"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="22" designID="6106"> | ||||
|             <Part refID="27" designID="6106" materials="23"> | ||||
|                 <Bone refID="27" transformation="0,0,-1,0,1,0,1,0,0,-12.4,1.60004,-6.80001"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="23" designID="6106"> | ||||
|             <Part refID="28" designID="6106" materials="23"> | ||||
|                 <Bone refID="28" transformation="0,0,-1,0,1,0,1,0,0,-12.4,1.28003,-7.60001"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="24" designID="6106"> | ||||
|             <Part refID="29" designID="6106" materials="23"> | ||||
|                 <Bone refID="29" transformation="0,0,-1,0,1,0,1,0,0,-12.4,0.960022,-6.80001"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="25" designID="3036"> | ||||
|             <Part refID="30" designID="3036" materials="23"> | ||||
|                 <Bone refID="30" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-7.6,0.640015,-7.60001"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="26" designID="3036"> | ||||
|             <Part refID="31" designID="3036" materials="23"> | ||||
|                 <Bone refID="31" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-6,0.320007,-9.2"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="27" designID="3036"> | ||||
|             <Part refID="32" designID="3036" materials="23"> | ||||
|                 <Bone refID="32" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-6.8,0,-12.4"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="28" designID="3004"> | ||||
|             <Part refID="33" designID="3004" materials="23,0,0,0" decoration="0,0,0"> | ||||
|                 <Bone refID="33" transformation="-1,0,0,0,0.99999994039535522,0,0,0,-1,-8.40001,0.320007,-16.4"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="29" designID="73587"> | ||||
|             <Part refID="34" designID="4592" materials="23"> | ||||
|                 <Bone refID="34" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-12.4,1.92004,-10"/> | ||||
|             </Part> | ||||
|             <Part refID="35" designID="4593" materials="23"> | ||||
|                 <Bone refID="35" transformation="0,-0.71323662996292114,-0.70092326402664185,0,0.70092320442199707,-0.71323668956756592,1,0,0,-12.4,2.34006,-10"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="30" designID="3614"> | ||||
|             <Part refID="36" designID="3614" materials="23"> | ||||
|                 <Bone refID="36" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-13.8325,1.67819,-7.52268"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="31" designID="74340"> | ||||
|             <Part refID="37" designID="3679" materials="23"> | ||||
|                 <Bone refID="37" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-17.8327,4.83176,-5.19592"/> | ||||
|             </Part> | ||||
|             <Part refID="38" designID="3680" materials="23"> | ||||
|                 <Bone refID="38" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-17.705,4.54233,-5.24437"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="32" designID="3046"> | ||||
|             <Part refID="39" designID="3046" materials="23"> | ||||
|                 <Bone refID="39" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-4.4,0,1.2"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="33" designID="4085"> | ||||
|             <Part refID="40" designID="4085" materials="23"> | ||||
|                 <Bone refID="40" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-3.6,0.959991,0.399994"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="34" designID="3688"> | ||||
|             <Part refID="41" designID="3688" materials="23"> | ||||
|                 <Bone refID="41" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-3.60001,0.959991,2"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|         <Brick refID="35" designID="73587"> | ||||
|             <Part refID="42" designID="4592" materials="23"> | ||||
|                 <Bone refID="42" transformation="1,0,0,0,1,0,0,0,1,7.60001,6.10352e-05,-9.2"/> | ||||
|             </Part> | ||||
|             <Part refID="43" designID="4593" materials="23"> | ||||
|                 <Bone refID="43" transformation="0.70092326402664185,-0.71323668956756592,0,0.71323668956756592,0.70092326402664185,0,0,0,1,7.6,0.420074,-9.2"/> | ||||
|             </Part> | ||||
|         </Brick> | ||||
|     </Bricks> | ||||
|     <RigidSystems> | ||||
|         <RigidSystem> | ||||
|             <Rigid refID="0" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,5.9959182739257812,459.87173461914062,69.367317199707031" boneRefs="0"/> | ||||
|             <Rigid refID="1" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,6.0443649291992187,459.58230590820312,69.494972229003906" boneRefs="1"/> | ||||
|             <Rigid refID="2" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,6.7689208984375,458.32742309570312,71.188117980957031" boneRefs="2,3"/> | ||||
|             <Rigid refID="3" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,6.8173675537109375,458.03802490234375,71.315757751464844" boneRefs="4"/> | ||||
|             <Rigid refID="4" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,7.54193115234375,456.78311157226562,73.008895874023438" boneRefs="5"/> | ||||
|             <Rigid refID="5" transformation="0.91537082195281982,0.28058713674545288,0.28873366117477417,-0.15140815079212189,0.90441381931304932,-0.39888754487037659,-0.37305742502212524,0.32141336798667908,0.87035715579986572,8.3226776123046875,456.71817016601562,73.367523193359375" boneRefs="6"/> | ||||
|             <Rigid refID="6" transformation="0,0,-1,0,1,0,1,0,0,17.19999885559082,455.3599853515625,78.799995422363281" boneRefs="7,8,9,10,11,12,13,14,15"/> | ||||
|             <Rigid refID="7" transformation="0.70092326402664185,-0.71323668956756592,0,0.71323668956756592,0.70092326402664185,0,0,0,1,10.800004959106445,457.38003540039062,74.800003051757813" boneRefs="16"/> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="0" a="0,-1,0" z="1,0,0" t="0.40000000596046448,-0.21002300083637238,-0.40000000596046448"/> | ||||
|                 <RigidRef rigidRef="1" a="0,-1,0" z="-1,0,0" t="0.40000000596046448,0.10999999940395355,-0.40000000596046448"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="1" a="0,1,0" z="0,0,1" t="0.80000007152557373,0,0"/> | ||||
|                 <RigidRef rigidRef="2" a="0,1,0" z="0,0,1" t="0,1.9199999570846558,-0.80000007152557373"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="2" a="0,-1,0" z="1,0,0" t="0.40000000596046448,-0.21002300083637238,-0.40000000596046448"/> | ||||
|                 <RigidRef rigidRef="3" a="0,-1,0" z="-1,0,0" t="0.40000000596046448,0.10999999940395355,-0.40000000596046448"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="4" a="0,1,0" z="0,0,1" t="0,1.9199999570846558,-0.80000007152557373"/> | ||||
|                 <RigidRef rigidRef="3" a="0,1,0" z="0,0,1" t="0.80000007152557373,0,0"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="5" a="0,1,0" z="0,0,1" t="0,0.31999999284744263,0"/> | ||||
|                 <RigidRef rigidRef="4" a="0,1,0" z="0,0,1" t="0.80000007152557373,0,0"/> | ||||
|             </Joint> | ||||
|             <Joint type="ball"> | ||||
|                 <RigidRef rigidRef="6" a="-1,0,0" z="0,1,0" t="4.799992561340332,1.7600365877151489,-9.1999912261962891"/> | ||||
|                 <RigidRef rigidRef="5" a="0,0,1" z="0,-1,0" t="0,0.15999999642372131,0.80000001192092896"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="6" a="1,0,0" z="0,1,0" t="3.9999923706054687,2.0200366973876953,-6.3999929428100586"/> | ||||
|                 <RigidRef rigidRef="7" a="0,0,-1" z="-1,0,0" t="0,0,0"/> | ||||
|             </Joint> | ||||
|         </RigidSystem> | ||||
|         <RigidSystem> | ||||
|             <Rigid refID="8" transformation="1,0,0,0,1,0,0,0,1,-1.1999999284744263,455.99996948242187,83.599998474121094" boneRefs="17"/> | ||||
|             <Rigid refID="9" transformation="1,0,0,0,1,0,0,0,1,0.40000009536743164,455.99996948242187,83.600006103515625" boneRefs="18,19"/> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="9" a="0,1,0" z="0,0,1" t="-0.80000007152557373,0,-0.8000030517578125"/> | ||||
|                 <RigidRef rigidRef="8" a="0,1,0" z="0,0,1" t="0.80000007152557373,0,-0.80000007152557373"/> | ||||
|             </Joint> | ||||
|         </RigidSystem> | ||||
|         <RigidSystem> | ||||
|             <Rigid refID="10" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-26.000003814697266,455.03997802734375,58.799995422363281" boneRefs="20"/> | ||||
|             <Rigid refID="11" transformation="0,-0.71323662996292114,-0.70092326402664185,0,0.70092320442199707,-0.71323668956756592,1,0,0,-26.000003814697266,455.45999145507812,58.799995422363281" boneRefs="21"/> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="10" a="0,0,-1" z="0,1,0" t="0,0.41999998688697815,0"/> | ||||
|                 <RigidRef rigidRef="11" a="0,0,-1" z="-1,0,0" t="0,0,0"/> | ||||
|             </Joint> | ||||
|         </RigidSystem> | ||||
|         <RigidSystem> | ||||
|             <Rigid refID="12" transformation="0.28873360157012939,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140815079212189,0.87035715579986572,0.3214133083820343,0.37305739521980286,-20.811885833740234,458.327392578125,62.031078338623047" boneRefs="22,24"/> | ||||
|             <Rigid refID="13" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-20.684246063232422,458.03799438476562,61.982631683349609" boneRefs="23"/> | ||||
|             <Rigid refID="14" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-18.991107940673828,456.7830810546875,61.258068084716797" boneRefs="25"/> | ||||
|             <Rigid refID="15" transformation="0,0,1,0,0.99999994039535522,0,-1,0,0,-17.200000762939453,456.95999145507812,60.399990081787109" boneRefs="26,27,28,29,30,31,32,33,34"/> | ||||
|             <Rigid refID="16" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-18.632480621337891,456.7181396484375,60.477321624755859" boneRefs="36"/> | ||||
|             <Rigid refID="17" transformation="0,-0.71323662996292114,-0.70092326402664185,0,0.70092320442199707,-0.71323668956756592,1,0,0,-17.200000762939453,457.3800048828125,57.999996185302734" boneRefs="35"/> | ||||
|             <Rigid refID="18" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-22.632686614990234,459.8717041015625,62.804080963134766" boneRefs="37"/> | ||||
|             <Rigid refID="19" transformation="0.28873363137245178,0.28058710694313049,-0.91537082195281982,-0.39888754487037659,0.90441375970840454,0.15140816569328308,0.87035715579986572,0.32141333818435669,0.37305739521980286,-22.505031585693359,459.582275390625,62.755634307861328" boneRefs="38"/> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="12" a="0,-1,0" z="1,0,0" t="0.40000000596046448,-0.21002300083637238,-0.40000000596046448"/> | ||||
|                 <RigidRef rigidRef="13" a="0,-1,0" z="-1,0,0" t="0.40000000596046448,0.10999999940395355,-0.40000000596046448"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="12" a="0,1,0" z="0,0,1" t="0,1.9199999570846558,-0.80000007152557373"/> | ||||
|                 <RigidRef rigidRef="19" a="0,1,0" z="0,0,1" t="0.80000007152557373,0,0"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="13" a="0,1,0" z="0,0,1" t="0.80000007152557373,0,0"/> | ||||
|                 <RigidRef rigidRef="14" a="0,1,0" z="0,0,1" t="0,1.9199999570846558,-0.80000007152557373"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="16" a="0,1,0" z="0,0,1" t="0,0.31999999284744263,0"/> | ||||
|                 <RigidRef rigidRef="14" a="0,1,0" z="0,0,1" t="0.80000007152557373,0,0"/> | ||||
|             </Joint> | ||||
|             <Joint type="ball"> | ||||
|                 <RigidRef rigidRef="15" a="0,0,-1" z="0,1,0" t="0.40000000596046448,0.15999999642372131,0.80000001192092896"/> | ||||
|                 <RigidRef rigidRef="16" a="0,0,1" z="0,-1,0" t="0,0.15999999642372131,0.80000001192092896"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="15" a="0,0,1" z="0,1,0" t="-2.3999977111816406,0.41999998688697815,0"/> | ||||
|                 <RigidRef rigidRef="17" a="0,0,-1" z="-1,0,0" t="0,0,0"/> | ||||
|             </Joint> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="18" a="0,-1,0" z="1,0,0" t="0.40000000596046448,-0.21002300083637238,-0.40000000596046448"/> | ||||
|                 <RigidRef rigidRef="19" a="0,-1,0" z="-1,0,0" t="0.40000000596046448,0.10999999940395355,-0.40000000596046448"/> | ||||
|             </Joint> | ||||
|         </RigidSystem> | ||||
|         <RigidSystem> | ||||
|             <Rigid refID="20" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-9.2000007629394531,455.03994750976562,69.199996948242188" boneRefs="39,40"/> | ||||
|             <Rigid refID="21" transformation="0,0,-1,0,0.99999994039535522,0,1,0,0,-8.4000053405761719,455.99993896484375,70" boneRefs="41"/> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="20" a="0,1,0" z="0,0,1" t="0,0.95999997854232788,0"/> | ||||
|                 <RigidRef rigidRef="21" a="0,1,0" z="0,0,1" t="0.80000007152557373,0,-0.80000007152557373"/> | ||||
|             </Joint> | ||||
|         </RigidSystem> | ||||
|         <RigidSystem> | ||||
|             <Rigid refID="22" transformation="1,0,0,0,1,0,0,0,1,2.8000054359436035,455.04000854492187,58.799999237060547" boneRefs="42"/> | ||||
|             <Rigid refID="23" transformation="0.70092326402664185,-0.71323668956756592,0,0.71323668956756592,0.70092326402664185,0,0,0,1,2.8000044822692871,455.46002197265625,58.799999237060547" boneRefs="43"/> | ||||
|             <Joint type="hinge"> | ||||
|                 <RigidRef rigidRef="22" a="0,0,-1" z="0,1,0" t="0,0.41999998688697815,0"/> | ||||
|                 <RigidRef rigidRef="23" a="0,0,-1" z="-1,0,0" t="0,0,0"/> | ||||
|             </Joint> | ||||
|         </RigidSystem> | ||||
|     </RigidSystems> | ||||
|     <GroupSystems> | ||||
|         <GroupSystem> | ||||
|             <Group transformation="1,0,0,0,1,0,0,0,1,0,0,0" pivot="0,0,0" partRefs="15,16"> | ||||
|                 <Group transformation="1,0,0,0,1,0,0,0,1,0,0,0" pivot="0,0,0" partRefs="19,18,17"> | ||||
|                     <Group transformation="1,0,0,0,1,0,0,0,1,0,0,0" pivot="0,0,0" partRefs="11,12,13"> | ||||
|                         <Group transformation="1,0,0,0,1,0,0,0,1,0,0,0" pivot="0,0,0" partRefs="10,9,7,8"/> | ||||
|                     </Group> | ||||
|                 </Group> | ||||
|             </Group> | ||||
|             <Group transformation="1,0,0,0,1,0,0,0,1,0,0,0" pivot="0,0,0" partRefs="25,34,35,20,21,31,37,38,30,24,39,22,23,32,41,26,28,27,36,40,29,33"/> | ||||
|         </GroupSystem> | ||||
|     </GroupSystems> | ||||
| </LXFML> | ||||
		Reference in New Issue
	
	Block a user
	 Aaron Kimbrell
					Aaron Kimbrell