Skip to content

Commit d765d92

Browse files
committed
Simply support 3MF Production Extension.
1 parent 25b422a commit d765d92

File tree

5 files changed

+114
-11
lines changed

5 files changed

+114
-11
lines changed

include/Savitar/Scene.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Scene
4040
/**
4141
* Set the data of this SceneNode by giving it a xml node
4242
*/
43-
void fillByXMLNode(pugi::xml_node xml_node);
43+
void fillByXMLNode(const std::string& path, pugi::xml_node xml_node);
4444

4545
/**
4646
* Store a metadata entry as metadata.
@@ -86,7 +86,7 @@ class Scene
8686
* Because 3mf uses references, we also need to provide the root_node, so it's know what the reference points to
8787
* \returns The created SceneNode.
8888
*/
89-
SceneNode* createSceneNodeFromObject(pugi::xml_node root_node, pugi::xml_node object_node);
89+
SceneNode* createSceneNodeFromObject(const std::string& path, pugi::xml_node root_node, pugi::xml_node object_node);
9090
};
9191
} // namespace Savitar
9292
#endif

include/Savitar/SceneNode.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ class SceneNode
3636
*/
3737
void fillByXMLNode(pugi::xml_node xml_node);
3838

39+
/**
40+
* Get the (unique) identifier of the node.
41+
*/
42+
[[nodiscard]] std::string getPath();
43+
44+
void setPath(std::string id);
45+
3946
/**
4047
* Get the (unique) identifier of the node.
4148
*/
@@ -74,6 +81,7 @@ class SceneNode
7481
std::vector<SceneNode*> children_;
7582
MeshData mesh_data_;
7683
std::map<std::string, MetadataEntry> settings_;
84+
std::string path_;
7785
std::string id_;
7886
std::string name_;
7987
std::string type_{ "model" };

src/Scene.cpp

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ void Scene::addSceneNode(SceneNode* node)
2323
}
2424

2525

26-
void Scene::fillByXMLNode(pugi::xml_node xml_node)
26+
void Scene::fillByXMLNode(const std::string& path, pugi::xml_node xml_node)
2727
{
2828
unit_ = xml_node.attribute("unit").as_string();
2929

@@ -44,16 +44,54 @@ void Scene::fillByXMLNode(pugi::xml_node xml_node)
4444
setMetaDataEntry(key, value, type, preserve);
4545
}
4646

47+
if (!path.empty())
48+
{
49+
for (pugi::xml_node object = resources.child("object"); object != nullptr; object = object.next_sibling("object"))
50+
{
51+
SceneNode* temp_scene_node = createSceneNodeFromObject(path, xml_node, object);
52+
std::cout << "sub component " << temp_scene_node->getPath() << ":" << temp_scene_node->getId() << std::endl;
53+
scene_nodes_.push_back(temp_scene_node);
54+
}
55+
return;
56+
}
57+
58+
std::vector<SceneNode*> scene_nodes;
4759
pugi::xml_node build = xml_node.child("build");
4860
for (pugi::xml_node item = build.child("item"); item != nullptr; item = item.next_sibling("item"))
4961
{
5062
// Found a item in the build. The items are linked to objects by objectid.
63+
SceneNode* temp_scene_node = nullptr;
5164
pugi::xml_node object_node = resources.find_child_by_attribute("object", "id", item.attribute("objectid").value());
52-
if (object_node != nullptr)
65+
66+
std::string path = item.attribute("p:path").value();
67+
if (!path.empty())
68+
{
69+
auto id = item.attribute("objectid").value();
70+
bool found = false;
71+
for (auto node : scene_nodes_)
72+
{
73+
if (node->getPath() == path && node->getId() == id)
74+
{
75+
temp_scene_node = new SceneNode();
76+
temp_scene_node->setMeshData(node->getMeshData());
77+
temp_scene_node->setTransformation(item.attribute("transform").as_string());
78+
found = true;
79+
break;
80+
}
81+
}
82+
if (!found)
83+
{
84+
std::cout << "sub component not found :( " << path << ":" << id << std::endl;
85+
}
86+
}
87+
else if (object_node != nullptr)
5388
{
54-
SceneNode* temp_scene_node = createSceneNodeFromObject(xml_node, object_node);
89+
temp_scene_node = createSceneNodeFromObject(path, xml_node, object_node);
5590
temp_scene_node->setTransformation(item.attribute("transform").as_string());
91+
}
5692

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

76-
scene_nodes_.push_back(temp_scene_node);
114+
scene_nodes.push_back(temp_scene_node);
77115
}
78116
else
79117
{
80118
// TODO: add proper error handling
81119
std::cout << "Could not find object by given ID" << std::endl;
82120
}
83121
}
122+
scene_nodes.swap(scene_nodes_);
84123
}
85124

86-
SceneNode* Scene::createSceneNodeFromObject(pugi::xml_node root_node, pugi::xml_node object_node)
125+
SceneNode* Scene::createSceneNodeFromObject(const std::string& path, pugi::xml_node root_node, pugi::xml_node object_node)
87126
{
88127
pugi::xml_node components = object_node.child("components");
89128
auto* scene_node = new SceneNode();
90129
scene_node->fillByXMLNode(object_node);
130+
scene_node->setPath(path);
91131

92132
std::map<std::string, std::string>::iterator it;
93133
const bool has_mesh_node = scene_node->getSettings().find("mesh_node_objectid") != scene_node->getSettings().end();
@@ -106,10 +146,33 @@ SceneNode* Scene::createSceneNodeFromObject(pugi::xml_node root_node, pugi::xml_
106146
for (pugi::xml_node component = components.child("component"); component != nullptr; component = component.next_sibling("component"))
107147
{
108148
// This node has children. Add them one by one.
149+
std::string path = component.attribute("p:path").value();
150+
if (!path.empty())
151+
{
152+
auto id = component.attribute("objectid").value();
153+
bool found = false;
154+
for (auto node : scene_nodes_)
155+
{
156+
if (node->getPath() == path && node->getId() == id)
157+
{
158+
auto* child_node = new SceneNode();
159+
child_node->setMeshData(node->getMeshData());
160+
child_node->setTransformation(component.attribute("transform").as_string());
161+
scene_node->addChild(child_node);
162+
found = true;
163+
break;
164+
}
165+
}
166+
if (!found)
167+
{
168+
std::cout << "sub component not found :( " << path << ":" << id << std::endl;
169+
}
170+
continue;
171+
}
109172
pugi::xml_node child_object_node = root_node.child("resources").find_child_by_attribute("object", "id", component.attribute("objectid").value());
110173
if (child_object_node != nullptr)
111174
{
112-
SceneNode* child_node = createSceneNodeFromObject(root_node, child_object_node);
175+
SceneNode* child_node = createSceneNodeFromObject(path, root_node, child_object_node);
113176
if (has_mesh_node && mesh_node_object_id == component.attribute("objectid").as_string())
114177
{
115178
// 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

src/SceneNode.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ void SceneNode::fillByXMLNode(pugi::xml_node xml_node)
127127
}
128128
}
129129

130+
std::string SceneNode::getPath()
131+
{
132+
return path_;
133+
}
134+
135+
void SceneNode::setPath(std::string path)
136+
{
137+
path_ = path;
138+
}
139+
130140
std::string SceneNode::getId()
131141
{
132142
return id_;

src/ThreeMFParser.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,32 @@ ThreeMFParser::ThreeMFParser()
1717

1818
Scene ThreeMFParser::parse(const std::string& xml_string)
1919
{
20+
size_t xml_start = 0;
21+
std::string path;
22+
if (!xml_string.empty() && xml_string.front() == '/')
23+
{
24+
xml_start = xml_string.find(':');
25+
if (xml_start != std::string::npos)
26+
{
27+
path = xml_string.substr(0, xml_start);
28+
++xml_start;
29+
}
30+
}
31+
32+
static Scene g_scene;
2033
pugi::xml_document document;
21-
document.load_string(xml_string.c_str());
22-
Scene scene;
23-
scene.fillByXMLNode(document.child("model"));
34+
document.load_string(xml_string.c_str() + xml_start);
35+
Scene scene = g_scene;
36+
scene.fillByXMLNode(path, document.child("model"));
37+
38+
if (path.empty())
39+
{
40+
g_scene = Scene();
41+
}
42+
else
43+
{
44+
g_scene = scene;
45+
}
2446

2547
return scene;
2648
}

0 commit comments

Comments
 (0)