1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4
5 Copyright (c) 2006-2017, assimp team
6
7 All rights reserved.
8
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following conditions are met:
12
13 * Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17 * Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22 * Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39 ----------------------------------------------------------------------
40 */
41 /// \file X3DImporter_Group.cpp
42 /// \brief Parsing data from nodes of "Grouping" set of X3D.
43 /// \date 2015-2016
44 /// \author smal.root@gmail.com
45
46 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
47
48 #include "X3DImporter.hpp"
49 #include "X3DImporter_Macro.hpp"
50
51 namespace Assimp
52 {
53
54 // <Group
55 // DEF="" ID
56 // USE="" IDREF
57 // bboxCenter="0 0 0" SFVec3f [initializeOnly]
58 // bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
59 // >
60 // <!-- ChildContentModel -->
61 // ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
62 // other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
63 // precise palette of legal nodes that are available depends on assigned profile and components.
64 // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
65 // </Group>
66 // A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform.
ParseNode_Grouping_Group()67 void X3DImporter::ParseNode_Grouping_Group()
68 {
69 std::string def, use;
70
71 MACRO_ATTRREAD_LOOPBEG;
72 MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
73 MACRO_ATTRREAD_LOOPEND;
74
75 // if "USE" defined then find already defined element.
76 if(!use.empty())
77 {
78 CX3DImporter_NodeElement* ne;
79
80 MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
81 }
82 else
83 {
84 ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
85 // at this place new group mode created and made current, so we can name it.
86 if(!def.empty()) NodeElement_Cur->ID = def;
87 // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
88
89 // for empty element exit from node in that place
90 if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
91 }// if(!use.empty()) else
92 }
93
ParseNode_Grouping_GroupEnd()94 void X3DImporter::ParseNode_Grouping_GroupEnd()
95 {
96 ParseHelper_Node_Exit();// go up in scene graph
97 }
98
99 // <StaticGroup
100 // DEF="" ID
101 // USE="" IDREF
102 // bboxCenter="0 0 0" SFVec3f [initializeOnly]
103 // bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
104 // >
105 // <!-- ChildContentModel -->
106 // ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
107 // other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
108 // precise palette of legal nodes that are available depends on assigned profile and components.
109 // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
110 // </StaticGroup>
111 // The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or
112 // contain any USE references outside the StaticGroup.
ParseNode_Grouping_StaticGroup()113 void X3DImporter::ParseNode_Grouping_StaticGroup()
114 {
115 std::string def, use;
116
117 MACRO_ATTRREAD_LOOPBEG;
118 MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
119 MACRO_ATTRREAD_LOOPEND;
120
121 // if "USE" defined then find already defined element.
122 if(!use.empty())
123 {
124 CX3DImporter_NodeElement* ne;
125
126 MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
127 }
128 else
129 {
130 ParseHelper_Group_Begin(true);// create new grouping element and go deeper if node has children.
131 // at this place new group mode created and made current, so we can name it.
132 if(!def.empty()) NodeElement_Cur->ID = def;
133 // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
134
135 // for empty element exit from node in that place
136 if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
137 }// if(!use.empty()) else
138 }
139
ParseNode_Grouping_StaticGroupEnd()140 void X3DImporter::ParseNode_Grouping_StaticGroupEnd()
141 {
142 ParseHelper_Node_Exit();// go up in scene graph
143 }
144
145 // <Switch
146 // DEF="" ID
147 // USE="" IDREF
148 // bboxCenter="0 0 0" SFVec3f [initializeOnly]
149 // bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
150 // whichChoice="-1" SFInt32 [inputOutput]
151 // >
152 // <!-- ChildContentModel -->
153 // ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
154 // other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
155 // precise palette of legal nodes that are available depends on assigned profile and components.
156 // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
157 // </Switch>
158 // The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child
159 // to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing
160 // is chosen.
ParseNode_Grouping_Switch()161 void X3DImporter::ParseNode_Grouping_Switch()
162 {
163 std::string def, use;
164 int32_t whichChoice = -1;
165
166 MACRO_ATTRREAD_LOOPBEG;
167 MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
168 MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32);
169 MACRO_ATTRREAD_LOOPEND;
170
171 // if "USE" defined then find already defined element.
172 if(!use.empty())
173 {
174 CX3DImporter_NodeElement* ne;
175
176 MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
177 }
178 else
179 {
180 ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
181 // at this place new group mode created and made current, so we can name it.
182 if(!def.empty()) NodeElement_Cur->ID = def;
183
184 // also set values specific to this type of group
185 ((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->UseChoice = true;
186 ((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->Choice = whichChoice;
187 // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
188
189 // for empty element exit from node in that place
190 if(mReader->isEmptyElement()) ParseHelper_Node_Exit();
191 }// if(!use.empty()) else
192 }
193
ParseNode_Grouping_SwitchEnd()194 void X3DImporter::ParseNode_Grouping_SwitchEnd()
195 {
196 // just exit from node. Defined choice will be accepted at postprocessing stage.
197 ParseHelper_Node_Exit();// go up in scene graph
198 }
199
200 // <Transform
201 // DEF="" ID
202 // USE="" IDREF
203 // bboxCenter="0 0 0" SFVec3f [initializeOnly]
204 // bboxSize="-1 -1 -1" SFVec3f [initializeOnly]
205 // center="0 0 0" SFVec3f [inputOutput]
206 // rotation="0 0 1 0" SFRotation [inputOutput]
207 // scale="1 1 1" SFVec3f [inputOutput]
208 // scaleOrientation="0 0 1 0" SFRotation [inputOutput]
209 // translation="0 0 0" SFVec3f [inputOutput]
210 // >
211 // <!-- ChildContentModel -->
212 // ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes,
213 // other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the
214 // precise palette of legal nodes that are available depends on assigned profile and components.
215 // A ProtoInstance node (with the proper node type) can be substituted for any node in this content model.
216 // </Transform>
217 // The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors.
218 // Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate
219 // transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the
220 // equivalent transformation matrices,
221 // P' = T * C * R * SR * S * -SR * -C * P
ParseNode_Grouping_Transform()222 void X3DImporter::ParseNode_Grouping_Transform()
223 {
224 aiVector3D center(0, 0, 0);
225 float rotation[4] = {0, 0, 1, 0};
226 aiVector3D scale(1, 1, 1);// A value of zero indicates that any child geometry shall not be displayed
227 float scale_orientation[4] = {0, 0, 1, 0};
228 aiVector3D translation(0, 0, 0);
229 aiMatrix4x4 matr, tmatr;
230 std::string use, def;
231
232 MACRO_ATTRREAD_LOOPBEG;
233 MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
234 MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f);
235 MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f);
236 MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f);
237 if(an == "rotation")
238 {
239 std::vector<float> tvec;
240
241 XML_ReadNode_GetAttrVal_AsArrF(idx, tvec);
242 if(tvec.size() != 4) throw DeadlyImportError("<Transform>: rotation vector must have 4 elements.");
243
244 memcpy(rotation, tvec.data(), sizeof(rotation));
245
246 continue;
247 }
248
249 if(an == "scaleOrientation")
250 {
251 std::vector<float> tvec;
252 XML_ReadNode_GetAttrVal_AsArrF(idx, tvec);
253 if ( tvec.size() != 4 )
254 {
255 throw DeadlyImportError( "<Transform>: scaleOrientation vector must have 4 elements." );
256 }
257
258 ::memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation) );
259
260 continue;
261 }
262
263 MACRO_ATTRREAD_LOOPEND;
264
265 // if "USE" defined then find already defined element.
266 if(!use.empty())
267 {
268 CX3DImporter_NodeElement* ne( nullptr );
269
270 MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne);
271 }
272 else
273 {
274 ParseHelper_Group_Begin();// create new grouping element and go deeper if node has children.
275 // at this place new group mode created and made current, so we can name it.
276 if ( !def.empty() )
277 {
278 NodeElement_Cur->ID = def;
279 }
280
281 //
282 // also set values specific to this type of group
283 //
284 // calculate transformation matrix
285 aiMatrix4x4::Translation(translation, matr);// T
286 aiMatrix4x4::Translation(center, tmatr);// C
287 matr *= tmatr;
288 aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr);// R
289 matr *= tmatr;
290 aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr);// SR
291 matr *= tmatr;
292 aiMatrix4x4::Scaling(scale, tmatr);// S
293 matr *= tmatr;
294 aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr);// -SR
295 matr *= tmatr;
296 aiMatrix4x4::Translation(-center, tmatr);// -C
297 matr *= tmatr;
298 // and assign it
299 ((CX3DImporter_NodeElement_Group*)NodeElement_Cur)->Transformation = matr;
300 // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in <Scene> parser function.
301
302 // for empty element exit from node in that place
303 if ( mReader->isEmptyElement() )
304 {
305 ParseHelper_Node_Exit();
306 }
307 }// if(!use.empty()) else
308 }
309
ParseNode_Grouping_TransformEnd()310 void X3DImporter::ParseNode_Grouping_TransformEnd()
311 {
312 ParseHelper_Node_Exit();// go up in scene graph
313 }
314
315 }// namespace Assimp
316
317 #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
318