Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simply support 3MF Production Extension. #48

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading