diff --git a/include/Savitar/Scene.h b/include/Savitar/Scene.h index 50d2cef..d318355 100644 --- a/include/Savitar/Scene.h +++ b/include/Savitar/Scene.h @@ -40,7 +40,7 @@ class Scene /** * Set the data of this SceneNode by giving it a xml node */ - void fillByXMLNode(pugi::xml_node xml_node); + void fillByXMLNode(const std::string& path, pugi::xml_node xml_node); /** * Store a metadata entry as metadata. @@ -86,7 +86,7 @@ class Scene * Because 3mf uses references, we also need to provide the root_node, so it's know what the reference points to * \returns The created SceneNode. */ - SceneNode* createSceneNodeFromObject(pugi::xml_node root_node, pugi::xml_node object_node); + SceneNode* createSceneNodeFromObject(const std::string& path, pugi::xml_node root_node, pugi::xml_node object_node); }; } // namespace Savitar #endif \ No newline at end of file diff --git a/include/Savitar/SceneNode.h b/include/Savitar/SceneNode.h index 9f8f19c..a99d29a 100644 --- a/include/Savitar/SceneNode.h +++ b/include/Savitar/SceneNode.h @@ -36,6 +36,13 @@ class SceneNode */ void fillByXMLNode(pugi::xml_node xml_node); + /** + * Get the (unique) identifier of the node. + */ + [[nodiscard]] std::string getPath(); + + void setPath(std::string id); + /** * Get the (unique) identifier of the node. */ @@ -74,6 +81,7 @@ class SceneNode std::vector children_; MeshData mesh_data_; std::map settings_; + std::string path_; std::string id_; std::string name_; std::string type_{ "model" }; diff --git a/src/Scene.cpp b/src/Scene.cpp index 28ea2f3..a6c783e 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -23,7 +23,7 @@ void Scene::addSceneNode(SceneNode* node) } -void Scene::fillByXMLNode(pugi::xml_node xml_node) +void Scene::fillByXMLNode(const std::string& path, pugi::xml_node xml_node) { unit_ = xml_node.attribute("unit").as_string(); @@ -44,6 +44,18 @@ void Scene::fillByXMLNode(pugi::xml_node xml_node) setMetaDataEntry(key, value, type, preserve); } + if (!path.empty()) + { + for (pugi::xml_node object = resources.child("object"); object != nullptr; object = object.next_sibling("object")) + { + SceneNode* temp_scene_node = createSceneNodeFromObject(path, xml_node, object); + std::cout << "sub component " << temp_scene_node->getPath() << ":" << temp_scene_node->getId() << std::endl; + scene_nodes_.push_back(temp_scene_node); + } + return; + } + + std::vector scene_nodes; pugi::xml_node build = xml_node.child("build"); for (pugi::xml_node item = build.child("item"); item != nullptr; item = item.next_sibling("item")) { @@ -51,7 +63,7 @@ void Scene::fillByXMLNode(pugi::xml_node xml_node) pugi::xml_node object_node = resources.find_child_by_attribute("object", "id", item.attribute("objectid").value()); if (object_node != nullptr) { - SceneNode* temp_scene_node = createSceneNodeFromObject(xml_node, object_node); + SceneNode* temp_scene_node = createSceneNodeFromObject(path, xml_node, object_node); temp_scene_node->setTransformation(item.attribute("transform").as_string()); // Get all metadata from the item and update that. @@ -73,7 +85,7 @@ void Scene::fillByXMLNode(pugi::xml_node xml_node) } } - scene_nodes_.push_back(temp_scene_node); + scene_nodes.push_back(temp_scene_node); } else { @@ -81,13 +93,15 @@ void Scene::fillByXMLNode(pugi::xml_node xml_node) std::cout << "Could not find object by given ID" << std::endl; } } + scene_nodes.swap(scene_nodes_); } -SceneNode* Scene::createSceneNodeFromObject(pugi::xml_node root_node, pugi::xml_node object_node) +SceneNode* Scene::createSceneNodeFromObject(const std::string& path, pugi::xml_node root_node, pugi::xml_node object_node) { pugi::xml_node components = object_node.child("components"); auto* scene_node = new SceneNode(); scene_node->fillByXMLNode(object_node); + scene_node->setPath(path); std::map::iterator it; const bool has_mesh_node = scene_node->getSettings().find("mesh_node_objectid") != scene_node->getSettings().end(); @@ -106,10 +120,33 @@ SceneNode* Scene::createSceneNodeFromObject(pugi::xml_node root_node, pugi::xml_ for (pugi::xml_node component = components.child("component"); component != nullptr; component = component.next_sibling("component")) { // This node has children. Add them one by one. + std::string path = component.attribute("p:path").value(); + if (!path.empty()) + { + auto id = component.attribute("objectid").value(); + bool found = false; + for (auto node : scene_nodes_) + { + if (node->getPath() == path && node->getId() == id) + { + auto* child_node = new SceneNode(); + child_node->setMeshData(node->getMeshData()); + child_node->setTransformation(component.attribute("transform").as_string()); + scene_node->addChild(child_node); + found = true; + break; + } + } + if (!found) + { + std::cout << "sub component not found :( " << path << ":" << id << std::endl; + } + continue; + } pugi::xml_node child_object_node = root_node.child("resources").find_child_by_attribute("object", "id", component.attribute("objectid").value()); if (child_object_node != nullptr) { - SceneNode* child_node = createSceneNodeFromObject(root_node, child_object_node); + SceneNode* child_node = createSceneNodeFromObject(path, root_node, child_object_node); if (has_mesh_node && mesh_node_object_id == component.attribute("objectid").as_string()) { // Don't add a node with the mesh_node_objectid metadata. Store it until last so we can copy it's mesh to the parent node diff --git a/src/SceneNode.cpp b/src/SceneNode.cpp index 99f396d..2b45ec2 100644 --- a/src/SceneNode.cpp +++ b/src/SceneNode.cpp @@ -127,6 +127,16 @@ void SceneNode::fillByXMLNode(pugi::xml_node xml_node) } } +std::string SceneNode::getPath() +{ + return path_; +} + +void SceneNode::setPath(std::string path) +{ + path_ = path; +} + std::string SceneNode::getId() { return id_; diff --git a/src/ThreeMFParser.cpp b/src/ThreeMFParser.cpp index 7dcb591..05d2ecc 100644 --- a/src/ThreeMFParser.cpp +++ b/src/ThreeMFParser.cpp @@ -17,10 +17,32 @@ ThreeMFParser::ThreeMFParser() Scene ThreeMFParser::parse(const std::string& xml_string) { + size_t xml_start = 0; + std::string path; + if (!xml_string.empty() && xml_string.front() == '/') + { + xml_start = xml_string.find(':'); + if (xml_start != std::string::npos) + { + path = xml_string.substr(0, xml_start); + ++xml_start; + } + } + + static Scene g_scene; pugi::xml_document document; - document.load_string(xml_string.c_str()); - Scene scene; - scene.fillByXMLNode(document.child("model")); + document.load_string(xml_string.c_str() + xml_start); + Scene scene = g_scene; + scene.fillByXMLNode(path, document.child("model")); + + if (path.empty()) + { + g_scene = Scene(); + } + else + { + g_scene = scene; + } return scene; }