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. |
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 | |
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. |
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 | |
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. |
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 | |
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 |
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 | |
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 | |