Skip to content

Commit

Permalink
Simply support 3MF Production Extension.
Browse files Browse the repository at this point in the history
  • Loading branch information
cmguo committed Jun 16, 2023
1 parent 25b422a commit d765d92
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 11 deletions.
4 changes: 2 additions & 2 deletions include/Savitar/Scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
8 changes: 8 additions & 0 deletions include/Savitar/SceneNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -74,6 +81,7 @@ class SceneNode
std::vector<SceneNode*> children_;
MeshData mesh_data_;
std::map<std::string, MetadataEntry> settings_;
std::string path_;
std::string id_;
std::string name_;
std::string type_{ "model" };
Expand Down
75 changes: 69 additions & 6 deletions src/Scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -44,16 +44,54 @@ 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<SceneNode*> 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"))
{
// Found a item in the build. The items are linked to objects by objectid.
SceneNode* temp_scene_node = nullptr;
pugi::xml_node object_node = resources.find_child_by_attribute("object", "id", item.attribute("objectid").value());
if (object_node != nullptr)

std::string path = item.attribute("p:path").value();
if (!path.empty())
{
auto id = item.attribute("objectid").value();
bool found = false;
for (auto node : scene_nodes_)
{
if (node->getPath() == path && node->getId() == id)
{
temp_scene_node = new SceneNode();
temp_scene_node->setMeshData(node->getMeshData());
temp_scene_node->setTransformation(item.attribute("transform").as_string());
found = true;
break;
}
}
if (!found)
{
std::cout << "sub component not found :( " << path << ":" << id << std::endl;
}
}
else if (object_node != nullptr)
{
SceneNode* temp_scene_node = createSceneNodeFromObject(xml_node, object_node);
temp_scene_node = createSceneNodeFromObject(path, xml_node, object_node);
temp_scene_node->setTransformation(item.attribute("transform").as_string());
}

if (temp_scene_node)
{
// Get all metadata from the item and update that.
const pugi::xml_node metadatagroup_node = item.child("metadatagroup");
if (metadatagroup_node != nullptr)
Expand All @@ -73,21 +111,23 @@ void Scene::fillByXMLNode(pugi::xml_node xml_node)
}
}

scene_nodes_.push_back(temp_scene_node);
scene_nodes.push_back(temp_scene_node);
}
else
{
// TODO: add proper error handling
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<std::string, std::string>::iterator it;
const bool has_mesh_node = scene_node->getSettings().find("mesh_node_objectid") != scene_node->getSettings().end();
Expand All @@ -106,10 +146,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
Expand Down
10 changes: 10 additions & 0 deletions src/SceneNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
28 changes: 25 additions & 3 deletions src/ThreeMFParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit d765d92

Please sign in to comment.