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