rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 Open Asset Import Library (assimp)
|
nuclear@0
|
3 ----------------------------------------------------------------------
|
nuclear@0
|
4
|
nuclear@0
|
5 Copyright (c) 2006-2012, assimp team
|
nuclear@0
|
6 All rights reserved.
|
nuclear@0
|
7
|
nuclear@0
|
8 Redistribution and use of this software in source and binary forms,
|
nuclear@0
|
9 with or without modification, are permitted provided that the
|
nuclear@0
|
10 following conditions are met:
|
nuclear@0
|
11
|
nuclear@0
|
12 * Redistributions of source code must retain the above
|
nuclear@0
|
13 copyright notice, this list of conditions and the
|
nuclear@0
|
14 following disclaimer.
|
nuclear@0
|
15
|
nuclear@0
|
16 * Redistributions in binary form must reproduce the above
|
nuclear@0
|
17 copyright notice, this list of conditions and the
|
nuclear@0
|
18 following disclaimer in the documentation and/or other
|
nuclear@0
|
19 materials provided with the distribution.
|
nuclear@0
|
20
|
nuclear@0
|
21 * Neither the name of the assimp team, nor the names of its
|
nuclear@0
|
22 contributors may be used to endorse or promote products
|
nuclear@0
|
23 derived from this software without specific prior
|
nuclear@0
|
24 written permission of the assimp team.
|
nuclear@0
|
25
|
nuclear@0
|
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
nuclear@0
|
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
nuclear@0
|
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
nuclear@0
|
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
nuclear@0
|
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
nuclear@0
|
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
nuclear@0
|
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
nuclear@0
|
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
nuclear@0
|
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
nuclear@0
|
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
nuclear@0
|
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
nuclear@0
|
37
|
nuclear@0
|
38 ----------------------------------------------------------------------
|
nuclear@0
|
39 */
|
nuclear@0
|
40
|
nuclear@0
|
41 /** @file FBXConverter.cpp
|
nuclear@0
|
42 * @brief Implementation of the FBX DOM -> aiScene converter
|
nuclear@0
|
43 */
|
nuclear@0
|
44 #include "AssimpPCH.h"
|
nuclear@0
|
45
|
nuclear@0
|
46 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
|
nuclear@0
|
47
|
nuclear@0
|
48 #include <iterator>
|
nuclear@0
|
49 #include <boost/tuple/tuple.hpp>
|
nuclear@0
|
50
|
nuclear@0
|
51 #include "FBXParser.h"
|
nuclear@0
|
52 #include "FBXConverter.h"
|
nuclear@0
|
53 #include "FBXDocument.h"
|
nuclear@0
|
54 #include "FBXUtil.h"
|
nuclear@0
|
55 #include "FBXProperties.h"
|
nuclear@0
|
56 #include "FBXImporter.h"
|
nuclear@0
|
57
|
nuclear@0
|
58 namespace Assimp {
|
nuclear@0
|
59 namespace FBX {
|
nuclear@0
|
60
|
nuclear@0
|
61 using namespace Util;
|
nuclear@0
|
62
|
nuclear@0
|
63
|
nuclear@0
|
64 #define MAGIC_NODE_TAG "_$AssimpFbx$"
|
nuclear@0
|
65 #define MAGIC_NULL_TAG "_$AssimpFbxNull$"
|
nuclear@0
|
66
|
nuclear@0
|
67 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
|
nuclear@0
|
68
|
nuclear@0
|
69 // XXX vc9's debugger won't step into anonymous namespaces
|
nuclear@0
|
70 //namespace {
|
nuclear@0
|
71
|
nuclear@0
|
72 /** Dummy class to encapsulate the conversion process */
|
nuclear@0
|
73 class Converter
|
nuclear@0
|
74 {
|
nuclear@0
|
75 public:
|
nuclear@0
|
76
|
nuclear@0
|
77 /** the different parts that make up the final local transformation of a fbx node */
|
nuclear@0
|
78 enum TransformationComp
|
nuclear@0
|
79 {
|
nuclear@0
|
80 TransformationComp_Translation = 0,
|
nuclear@0
|
81 TransformationComp_RotationOffset,
|
nuclear@0
|
82 TransformationComp_RotationPivot,
|
nuclear@0
|
83 TransformationComp_PreRotation,
|
nuclear@0
|
84 TransformationComp_Rotation,
|
nuclear@0
|
85 TransformationComp_PostRotation,
|
nuclear@0
|
86 TransformationComp_RotationPivotInverse,
|
nuclear@0
|
87 TransformationComp_ScalingOffset,
|
nuclear@0
|
88 TransformationComp_ScalingPivot,
|
nuclear@0
|
89 TransformationComp_Scaling,
|
nuclear@0
|
90 TransformationComp_ScalingPivotInverse,
|
nuclear@0
|
91
|
nuclear@0
|
92 TransformationComp_MAXIMUM
|
nuclear@0
|
93 };
|
nuclear@0
|
94
|
nuclear@0
|
95
|
nuclear@0
|
96
|
nuclear@0
|
97 public:
|
nuclear@0
|
98
|
nuclear@0
|
99 Converter(aiScene* out, const Document& doc)
|
nuclear@0
|
100 : defaultMaterialIndex()
|
nuclear@0
|
101 , out(out)
|
nuclear@0
|
102 , doc(doc)
|
nuclear@0
|
103 {
|
nuclear@0
|
104 // animations need to be converted first since this will
|
nuclear@0
|
105 // populate the node_anim_chain_bits map, which is needed
|
nuclear@0
|
106 // to determine which nodes need to be generated.
|
nuclear@0
|
107 ConvertAnimations();
|
nuclear@0
|
108 ConvertRootNode();
|
nuclear@0
|
109
|
nuclear@0
|
110 if(doc.Settings().readAllMaterials) {
|
nuclear@0
|
111 // unfortunately this means we have to evaluate all objects
|
nuclear@0
|
112 BOOST_FOREACH(const ObjectMap::value_type& v,doc.Objects()) {
|
nuclear@0
|
113
|
nuclear@0
|
114 const Object* ob = v.second->Get();
|
nuclear@0
|
115 if(!ob) {
|
nuclear@0
|
116 continue;
|
nuclear@0
|
117 }
|
nuclear@0
|
118
|
nuclear@0
|
119 const Material* mat = dynamic_cast<const Material*>(ob);
|
nuclear@0
|
120 if(mat) {
|
nuclear@0
|
121
|
nuclear@0
|
122 if (materials_converted.find(mat) == materials_converted.end()) {
|
nuclear@0
|
123 ConvertMaterial(*mat);
|
nuclear@0
|
124 }
|
nuclear@0
|
125 }
|
nuclear@0
|
126 }
|
nuclear@0
|
127 }
|
nuclear@0
|
128
|
nuclear@0
|
129 TransferDataToScene();
|
nuclear@0
|
130
|
nuclear@0
|
131 // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
|
nuclear@0
|
132 // to make sure the scene passes assimp's validation. FBX files
|
nuclear@0
|
133 // need not contain geometry (i.e. camera animations, raw armatures).
|
nuclear@0
|
134 if (out->mNumMeshes == 0) {
|
nuclear@0
|
135 out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
|
nuclear@0
|
136 }
|
nuclear@0
|
137 }
|
nuclear@0
|
138
|
nuclear@0
|
139
|
nuclear@0
|
140 ~Converter()
|
nuclear@0
|
141 {
|
nuclear@0
|
142 std::for_each(meshes.begin(),meshes.end(),Util::delete_fun<aiMesh>());
|
nuclear@0
|
143 std::for_each(materials.begin(),materials.end(),Util::delete_fun<aiMaterial>());
|
nuclear@0
|
144 std::for_each(animations.begin(),animations.end(),Util::delete_fun<aiAnimation>());
|
nuclear@0
|
145 std::for_each(lights.begin(),lights.end(),Util::delete_fun<aiLight>());
|
nuclear@0
|
146 std::for_each(cameras.begin(),cameras.end(),Util::delete_fun<aiCamera>());
|
nuclear@0
|
147 }
|
nuclear@0
|
148
|
nuclear@0
|
149
|
nuclear@0
|
150 private:
|
nuclear@0
|
151
|
nuclear@0
|
152 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
153 // find scene root and trigger recursive scene conversion
|
nuclear@0
|
154 void ConvertRootNode()
|
nuclear@0
|
155 {
|
nuclear@0
|
156 out->mRootNode = new aiNode();
|
nuclear@0
|
157 out->mRootNode->mName.Set("RootNode");
|
nuclear@0
|
158
|
nuclear@0
|
159 // root has ID 0
|
nuclear@0
|
160 ConvertNodes(0L, *out->mRootNode);
|
nuclear@0
|
161 }
|
nuclear@0
|
162
|
nuclear@0
|
163
|
nuclear@0
|
164 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
165 // collect and assign child nodes
|
nuclear@0
|
166 void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4())
|
nuclear@0
|
167 {
|
nuclear@0
|
168 const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
|
nuclear@0
|
169
|
nuclear@0
|
170 std::vector<aiNode*> nodes;
|
nuclear@0
|
171 nodes.reserve(conns.size());
|
nuclear@0
|
172
|
nuclear@0
|
173 std::vector<aiNode*> nodes_chain;
|
nuclear@0
|
174
|
nuclear@0
|
175 try {
|
nuclear@0
|
176 BOOST_FOREACH(const Connection* con, conns) {
|
nuclear@0
|
177
|
nuclear@0
|
178 // ignore object-property links
|
nuclear@0
|
179 if(con->PropertyName().length()) {
|
nuclear@0
|
180 continue;
|
nuclear@0
|
181 }
|
nuclear@0
|
182
|
nuclear@0
|
183 const Object* const object = con->SourceObject();
|
nuclear@0
|
184 if(!object) {
|
nuclear@0
|
185 FBXImporter::LogWarn("failed to convert source object for Model link");
|
nuclear@0
|
186 continue;
|
nuclear@0
|
187 }
|
nuclear@0
|
188
|
nuclear@0
|
189 const Model* const model = dynamic_cast<const Model*>(object);
|
nuclear@0
|
190
|
nuclear@0
|
191 if(model) {
|
nuclear@0
|
192 nodes_chain.clear();
|
nuclear@0
|
193
|
nuclear@0
|
194 aiMatrix4x4 new_abs_transform = parent_transform;
|
nuclear@0
|
195
|
nuclear@0
|
196 // even though there is only a single input node, the design of
|
nuclear@0
|
197 // assimp (or rather: the complicated transformation chain that
|
nuclear@0
|
198 // is employed by fbx) means that we may need multiple aiNode's
|
nuclear@0
|
199 // to represent a fbx node's transformation.
|
nuclear@0
|
200 GenerateTransformationNodeChain(*model,nodes_chain);
|
nuclear@0
|
201
|
nuclear@0
|
202 ai_assert(nodes_chain.size());
|
nuclear@0
|
203
|
nuclear@0
|
204 const std::string& original_name = FixNodeName(model->Name());
|
nuclear@0
|
205
|
nuclear@0
|
206 // check if any of the nodes in the chain has the name the fbx node
|
nuclear@0
|
207 // is supposed to have. If there is none, add another node to
|
nuclear@0
|
208 // preserve the name - people might have scripts etc. that rely
|
nuclear@0
|
209 // on specific node names.
|
nuclear@0
|
210 aiNode* name_carrier = NULL;
|
nuclear@0
|
211 BOOST_FOREACH(aiNode* prenode, nodes_chain) {
|
nuclear@0
|
212 if ( !strcmp(prenode->mName.C_Str(), original_name.c_str()) ) {
|
nuclear@0
|
213 name_carrier = prenode;
|
nuclear@0
|
214 break;
|
nuclear@0
|
215 }
|
nuclear@0
|
216 }
|
nuclear@0
|
217
|
nuclear@0
|
218 if(!name_carrier) {
|
nuclear@0
|
219 nodes_chain.push_back(new aiNode(original_name));
|
nuclear@0
|
220 name_carrier = nodes_chain.back();
|
nuclear@0
|
221 }
|
nuclear@0
|
222
|
nuclear@0
|
223 // link all nodes in a row
|
nuclear@0
|
224 aiNode* last_parent = &parent;
|
nuclear@0
|
225 BOOST_FOREACH(aiNode* prenode, nodes_chain) {
|
nuclear@0
|
226 ai_assert(prenode);
|
nuclear@0
|
227
|
nuclear@0
|
228 if(last_parent != &parent) {
|
nuclear@0
|
229 last_parent->mNumChildren = 1;
|
nuclear@0
|
230 last_parent->mChildren = new aiNode*[1];
|
nuclear@0
|
231 last_parent->mChildren[0] = prenode;
|
nuclear@0
|
232 }
|
nuclear@0
|
233
|
nuclear@0
|
234 prenode->mParent = last_parent;
|
nuclear@0
|
235 last_parent = prenode;
|
nuclear@0
|
236
|
nuclear@0
|
237 new_abs_transform *= prenode->mTransformation;
|
nuclear@0
|
238 }
|
nuclear@0
|
239
|
nuclear@0
|
240 // attach geometry
|
nuclear@0
|
241 ConvertModel(*model, *nodes_chain.back(), new_abs_transform);
|
nuclear@0
|
242
|
nuclear@0
|
243 // attach sub-nodes
|
nuclear@0
|
244 ConvertNodes(model->ID(), *nodes_chain.back(), new_abs_transform);
|
nuclear@0
|
245
|
nuclear@0
|
246 if(doc.Settings().readLights) {
|
nuclear@0
|
247 ConvertLights(*model);
|
nuclear@0
|
248 }
|
nuclear@0
|
249
|
nuclear@0
|
250 if(doc.Settings().readCameras) {
|
nuclear@0
|
251 ConvertCameras(*model);
|
nuclear@0
|
252 }
|
nuclear@0
|
253
|
nuclear@0
|
254 // preserve the info that a node was marked as Null node
|
nuclear@0
|
255 // in the original file.
|
nuclear@0
|
256 if(model->IsNull()) {
|
nuclear@0
|
257 const std::string& new_name = original_name + MAGIC_NULL_TAG;
|
nuclear@0
|
258 RenameNode(original_name, new_name);
|
nuclear@0
|
259 name_carrier->mName.Set( new_name.c_str() );
|
nuclear@0
|
260 }
|
nuclear@0
|
261
|
nuclear@0
|
262 nodes.push_back(nodes_chain.front());
|
nuclear@0
|
263 nodes_chain.clear();
|
nuclear@0
|
264 }
|
nuclear@0
|
265 }
|
nuclear@0
|
266
|
nuclear@0
|
267 if(nodes.size()) {
|
nuclear@0
|
268 parent.mChildren = new aiNode*[nodes.size()]();
|
nuclear@0
|
269 parent.mNumChildren = static_cast<unsigned int>(nodes.size());
|
nuclear@0
|
270
|
nuclear@0
|
271 std::swap_ranges(nodes.begin(),nodes.end(),parent.mChildren);
|
nuclear@0
|
272 }
|
nuclear@0
|
273 }
|
nuclear@0
|
274 catch(std::exception&) {
|
nuclear@0
|
275 Util::delete_fun<aiNode> deleter;
|
nuclear@0
|
276 std::for_each(nodes.begin(),nodes.end(),deleter);
|
nuclear@0
|
277 std::for_each(nodes_chain.begin(),nodes_chain.end(),deleter);
|
nuclear@0
|
278 }
|
nuclear@0
|
279 }
|
nuclear@0
|
280
|
nuclear@0
|
281
|
nuclear@0
|
282 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
283 void ConvertLights(const Model& model)
|
nuclear@0
|
284 {
|
nuclear@0
|
285 const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
|
nuclear@0
|
286 BOOST_FOREACH(const NodeAttribute* attr, node_attrs) {
|
nuclear@0
|
287 const Light* const light = dynamic_cast<const Light*>(attr);
|
nuclear@0
|
288 if(light) {
|
nuclear@0
|
289 ConvertLight(model, *light);
|
nuclear@0
|
290 }
|
nuclear@0
|
291 }
|
nuclear@0
|
292 }
|
nuclear@0
|
293
|
nuclear@0
|
294
|
nuclear@0
|
295 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
296 void ConvertCameras(const Model& model)
|
nuclear@0
|
297 {
|
nuclear@0
|
298 const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
|
nuclear@0
|
299 BOOST_FOREACH(const NodeAttribute* attr, node_attrs) {
|
nuclear@0
|
300 const Camera* const cam = dynamic_cast<const Camera*>(attr);
|
nuclear@0
|
301 if(cam) {
|
nuclear@0
|
302 ConvertCamera(model, *cam);
|
nuclear@0
|
303 }
|
nuclear@0
|
304 }
|
nuclear@0
|
305 }
|
nuclear@0
|
306
|
nuclear@0
|
307
|
nuclear@0
|
308 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
309 void ConvertLight(const Model& model, const Light& light)
|
nuclear@0
|
310 {
|
nuclear@0
|
311 lights.push_back(new aiLight());
|
nuclear@0
|
312 aiLight* const out_light = lights.back();
|
nuclear@0
|
313
|
nuclear@0
|
314 out_light->mName.Set(FixNodeName(model.Name()));
|
nuclear@0
|
315
|
nuclear@0
|
316 const float intensity = light.Intensity();
|
nuclear@0
|
317 const aiVector3D& col = light.Color();
|
nuclear@0
|
318
|
nuclear@0
|
319 out_light->mColorDiffuse = aiColor3D(col.x,col.y,col.z);
|
nuclear@0
|
320 out_light->mColorDiffuse.r *= intensity;
|
nuclear@0
|
321 out_light->mColorDiffuse.g *= intensity;
|
nuclear@0
|
322 out_light->mColorDiffuse.b *= intensity;
|
nuclear@0
|
323
|
nuclear@0
|
324 out_light->mColorSpecular = out_light->mColorDiffuse;
|
nuclear@0
|
325
|
nuclear@0
|
326 switch(light.LightType())
|
nuclear@0
|
327 {
|
nuclear@0
|
328 case Light::Type_Point:
|
nuclear@0
|
329 out_light->mType = aiLightSource_POINT;
|
nuclear@0
|
330 break;
|
nuclear@0
|
331
|
nuclear@0
|
332 case Light::Type_Directional:
|
nuclear@0
|
333 out_light->mType = aiLightSource_DIRECTIONAL;
|
nuclear@0
|
334 break;
|
nuclear@0
|
335
|
nuclear@0
|
336 case Light::Type_Spot:
|
nuclear@0
|
337 out_light->mType = aiLightSource_SPOT;
|
nuclear@0
|
338 out_light->mAngleOuterCone = AI_DEG_TO_RAD(light.OuterAngle());
|
nuclear@0
|
339 out_light->mAngleInnerCone = AI_DEG_TO_RAD(light.InnerAngle());
|
nuclear@0
|
340 break;
|
nuclear@0
|
341
|
nuclear@0
|
342 case Light::Type_Area:
|
nuclear@0
|
343 FBXImporter::LogWarn("cannot represent area light, set to UNDEFINED");
|
nuclear@0
|
344 out_light->mType = aiLightSource_UNDEFINED;
|
nuclear@0
|
345 break;
|
nuclear@0
|
346
|
nuclear@0
|
347 case Light::Type_Volume:
|
nuclear@0
|
348 FBXImporter::LogWarn("cannot represent volume light, set to UNDEFINED");
|
nuclear@0
|
349 out_light->mType = aiLightSource_UNDEFINED;
|
nuclear@0
|
350 break;
|
nuclear@0
|
351 default:
|
nuclear@0
|
352 ai_assert(false);
|
nuclear@0
|
353 }
|
nuclear@0
|
354
|
nuclear@0
|
355 // XXX: how to best convert the near and far decay ranges?
|
nuclear@0
|
356 switch(light.DecayType())
|
nuclear@0
|
357 {
|
nuclear@0
|
358 case Light::Decay_None:
|
nuclear@0
|
359 out_light->mAttenuationConstant = 1.0f;
|
nuclear@0
|
360 break;
|
nuclear@0
|
361 case Light::Decay_Linear:
|
nuclear@0
|
362 out_light->mAttenuationLinear = 1.0f;
|
nuclear@0
|
363 break;
|
nuclear@0
|
364 case Light::Decay_Quadratic:
|
nuclear@0
|
365 out_light->mAttenuationQuadratic = 1.0f;
|
nuclear@0
|
366 break;
|
nuclear@0
|
367 case Light::Decay_Cubic:
|
nuclear@0
|
368 FBXImporter::LogWarn("cannot represent cubic attenuation, set to Quadratic");
|
nuclear@0
|
369 out_light->mAttenuationQuadratic = 1.0f;
|
nuclear@0
|
370 break;
|
nuclear@0
|
371 default:
|
nuclear@0
|
372 ai_assert(false);
|
nuclear@0
|
373 }
|
nuclear@0
|
374 }
|
nuclear@0
|
375
|
nuclear@0
|
376
|
nuclear@0
|
377 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
378 void ConvertCamera(const Model& model, const Camera& cam)
|
nuclear@0
|
379 {
|
nuclear@0
|
380 cameras.push_back(new aiCamera());
|
nuclear@0
|
381 aiCamera* const out_camera = cameras.back();
|
nuclear@0
|
382
|
nuclear@0
|
383 out_camera->mName.Set(FixNodeName(model.Name()));
|
nuclear@0
|
384
|
nuclear@0
|
385 out_camera->mAspect = cam.AspectWidth();
|
nuclear@0
|
386 out_camera->mPosition = cam.Position();
|
nuclear@0
|
387 out_camera->mLookAt = cam.InterestPosition() - out_camera->mPosition;
|
nuclear@0
|
388
|
nuclear@0
|
389 out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
|
nuclear@0
|
390 }
|
nuclear@0
|
391
|
nuclear@0
|
392
|
nuclear@0
|
393 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
394 // this returns unified names usable within assimp identifiers (i.e. no space characters -
|
nuclear@0
|
395 // while these would be allowed, they are a potential trouble spot so better not use them).
|
nuclear@0
|
396 const char* NameTransformationComp(TransformationComp comp)
|
nuclear@0
|
397 {
|
nuclear@0
|
398 switch(comp)
|
nuclear@0
|
399 {
|
nuclear@0
|
400 case TransformationComp_Translation:
|
nuclear@0
|
401 return "Translation";
|
nuclear@0
|
402 case TransformationComp_RotationOffset:
|
nuclear@0
|
403 return "RotationOffset";
|
nuclear@0
|
404 case TransformationComp_RotationPivot:
|
nuclear@0
|
405 return "RotationPivot";
|
nuclear@0
|
406 case TransformationComp_PreRotation:
|
nuclear@0
|
407 return "PreRotation";
|
nuclear@0
|
408 case TransformationComp_Rotation:
|
nuclear@0
|
409 return "Rotation";
|
nuclear@0
|
410 case TransformationComp_PostRotation:
|
nuclear@0
|
411 return "PostRotation";
|
nuclear@0
|
412 case TransformationComp_RotationPivotInverse:
|
nuclear@0
|
413 return "RotationPivotInverse";
|
nuclear@0
|
414 case TransformationComp_ScalingOffset:
|
nuclear@0
|
415 return "ScalingOffset";
|
nuclear@0
|
416 case TransformationComp_ScalingPivot:
|
nuclear@0
|
417 return "ScalingPivot";
|
nuclear@0
|
418 case TransformationComp_Scaling:
|
nuclear@0
|
419 return "Scaling";
|
nuclear@0
|
420 case TransformationComp_ScalingPivotInverse:
|
nuclear@0
|
421 return "ScalingPivotInverse";
|
nuclear@0
|
422 case TransformationComp_MAXIMUM: // this is to silence compiler warnings
|
nuclear@0
|
423 break;
|
nuclear@0
|
424 }
|
nuclear@0
|
425
|
nuclear@0
|
426 ai_assert(false);
|
nuclear@0
|
427 return NULL;
|
nuclear@0
|
428 }
|
nuclear@0
|
429
|
nuclear@0
|
430
|
nuclear@0
|
431 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
432 // note: this returns the REAL fbx property names
|
nuclear@0
|
433 const char* NameTransformationCompProperty(TransformationComp comp)
|
nuclear@0
|
434 {
|
nuclear@0
|
435 switch(comp)
|
nuclear@0
|
436 {
|
nuclear@0
|
437 case TransformationComp_Translation:
|
nuclear@0
|
438 return "Lcl Translation";
|
nuclear@0
|
439 case TransformationComp_RotationOffset:
|
nuclear@0
|
440 return "RotationOffset";
|
nuclear@0
|
441 case TransformationComp_RotationPivot:
|
nuclear@0
|
442 return "RotationPivot";
|
nuclear@0
|
443 case TransformationComp_PreRotation:
|
nuclear@0
|
444 return "PreRotation";
|
nuclear@0
|
445 case TransformationComp_Rotation:
|
nuclear@0
|
446 return "Lcl Rotation";
|
nuclear@0
|
447 case TransformationComp_PostRotation:
|
nuclear@0
|
448 return "PostRotation";
|
nuclear@0
|
449 case TransformationComp_RotationPivotInverse:
|
nuclear@0
|
450 return "RotationPivotInverse";
|
nuclear@0
|
451 case TransformationComp_ScalingOffset:
|
nuclear@0
|
452 return "ScalingOffset";
|
nuclear@0
|
453 case TransformationComp_ScalingPivot:
|
nuclear@0
|
454 return "ScalingPivot";
|
nuclear@0
|
455 case TransformationComp_Scaling:
|
nuclear@0
|
456 return "Lcl Scaling";
|
nuclear@0
|
457 case TransformationComp_ScalingPivotInverse:
|
nuclear@0
|
458 return "ScalingPivotInverse";
|
nuclear@0
|
459 case TransformationComp_MAXIMUM: // this is to silence compiler warnings
|
nuclear@0
|
460 break;
|
nuclear@0
|
461 }
|
nuclear@0
|
462
|
nuclear@0
|
463 ai_assert(false);
|
nuclear@0
|
464 return NULL;
|
nuclear@0
|
465 }
|
nuclear@0
|
466
|
nuclear@0
|
467
|
nuclear@0
|
468 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
469 aiVector3D TransformationCompDefaultValue(TransformationComp comp)
|
nuclear@0
|
470 {
|
nuclear@0
|
471 // XXX a neat way to solve the never-ending special cases for scaling
|
nuclear@0
|
472 // would be to do everything in log space!
|
nuclear@0
|
473 return comp == TransformationComp_Scaling ? aiVector3D(1.f,1.f,1.f) : aiVector3D();
|
nuclear@0
|
474 }
|
nuclear@0
|
475
|
nuclear@0
|
476
|
nuclear@0
|
477 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
478 void GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out)
|
nuclear@0
|
479 {
|
nuclear@0
|
480 if(mode == Model::RotOrder_SphericXYZ) {
|
nuclear@0
|
481 FBXImporter::LogError("Unsupported RotationMode: SphericXYZ");
|
nuclear@0
|
482 out = aiMatrix4x4();
|
nuclear@0
|
483 return;
|
nuclear@0
|
484 }
|
nuclear@0
|
485
|
nuclear@0
|
486 const float angle_epsilon = 1e-6f;
|
nuclear@0
|
487
|
nuclear@0
|
488 out = aiMatrix4x4();
|
nuclear@0
|
489
|
nuclear@0
|
490 bool is_id[3] = { true, true, true };
|
nuclear@0
|
491
|
nuclear@0
|
492 aiMatrix4x4 temp[3];
|
nuclear@0
|
493 if(fabs(rotation.z) > angle_epsilon) {
|
nuclear@0
|
494 aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(rotation.z),temp[2]);
|
nuclear@0
|
495 is_id[2] = false;
|
nuclear@0
|
496 }
|
nuclear@0
|
497 if(fabs(rotation.y) > angle_epsilon) {
|
nuclear@0
|
498 aiMatrix4x4::RotationY(AI_DEG_TO_RAD(rotation.y),temp[1]);
|
nuclear@0
|
499 is_id[1] = false;
|
nuclear@0
|
500 }
|
nuclear@0
|
501 if(fabs(rotation.x) > angle_epsilon) {
|
nuclear@0
|
502 aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x),temp[0]);
|
nuclear@0
|
503 is_id[0] = false;
|
nuclear@0
|
504 }
|
nuclear@0
|
505
|
nuclear@0
|
506 int order[3] = {-1, -1, -1};
|
nuclear@0
|
507
|
nuclear@0
|
508 // note: rotation order is inverted since we're left multiplying as is usual in assimp
|
nuclear@0
|
509 switch(mode)
|
nuclear@0
|
510 {
|
nuclear@0
|
511 case Model::RotOrder_EulerXYZ:
|
nuclear@0
|
512 order[0] = 2;
|
nuclear@0
|
513 order[1] = 1;
|
nuclear@0
|
514 order[2] = 0;
|
nuclear@0
|
515 break;
|
nuclear@0
|
516
|
nuclear@0
|
517 case Model::RotOrder_EulerXZY:
|
nuclear@0
|
518 order[0] = 1;
|
nuclear@0
|
519 order[1] = 2;
|
nuclear@0
|
520 order[2] = 0;
|
nuclear@0
|
521 break;
|
nuclear@0
|
522
|
nuclear@0
|
523 case Model::RotOrder_EulerYZX:
|
nuclear@0
|
524 order[0] = 0;
|
nuclear@0
|
525 order[1] = 2;
|
nuclear@0
|
526 order[2] = 1;
|
nuclear@0
|
527 break;
|
nuclear@0
|
528
|
nuclear@0
|
529 case Model::RotOrder_EulerYXZ:
|
nuclear@0
|
530 order[0] = 2;
|
nuclear@0
|
531 order[1] = 0;
|
nuclear@0
|
532 order[2] = 1;
|
nuclear@0
|
533 break;
|
nuclear@0
|
534
|
nuclear@0
|
535 case Model::RotOrder_EulerZXY:
|
nuclear@0
|
536 order[0] = 1;
|
nuclear@0
|
537 order[1] = 0;
|
nuclear@0
|
538 order[2] = 2;
|
nuclear@0
|
539 break;
|
nuclear@0
|
540
|
nuclear@0
|
541 case Model::RotOrder_EulerZYX:
|
nuclear@0
|
542 order[0] = 0;
|
nuclear@0
|
543 order[1] = 1;
|
nuclear@0
|
544 order[2] = 2;
|
nuclear@0
|
545 break;
|
nuclear@0
|
546
|
nuclear@0
|
547 default:
|
nuclear@0
|
548 ai_assert(false);
|
nuclear@0
|
549 }
|
nuclear@0
|
550
|
nuclear@0
|
551 if(!is_id[order[0]]) {
|
nuclear@0
|
552 out = temp[order[0]];
|
nuclear@0
|
553 }
|
nuclear@0
|
554
|
nuclear@0
|
555 if(!is_id[order[1]]) {
|
nuclear@0
|
556 out = out * temp[order[1]];
|
nuclear@0
|
557 }
|
nuclear@0
|
558
|
nuclear@0
|
559 if(!is_id[order[2]]) {
|
nuclear@0
|
560 out = out * temp[order[2]];
|
nuclear@0
|
561 }
|
nuclear@0
|
562 }
|
nuclear@0
|
563
|
nuclear@0
|
564
|
nuclear@0
|
565 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
566 /** checks if a node has more than just scaling, rotation and translation components */
|
nuclear@0
|
567 bool NeedsComplexTransformationChain(const Model& model)
|
nuclear@0
|
568 {
|
nuclear@0
|
569 const PropertyTable& props = model.Props();
|
nuclear@0
|
570 bool ok;
|
nuclear@0
|
571
|
nuclear@0
|
572 const float zero_epsilon = 1e-6f;
|
nuclear@0
|
573 for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
nuclear@0
|
574 const TransformationComp comp = static_cast<TransformationComp>(i);
|
nuclear@0
|
575
|
nuclear@0
|
576 if(comp == TransformationComp_Rotation || comp == TransformationComp_Scaling ||
|
nuclear@0
|
577 comp == TransformationComp_Translation) {
|
nuclear@0
|
578
|
nuclear@0
|
579 continue;
|
nuclear@0
|
580 }
|
nuclear@0
|
581
|
nuclear@0
|
582 const aiVector3D& v = PropertyGet<aiVector3D>(props,NameTransformationCompProperty(comp),ok);
|
nuclear@0
|
583 if(ok && v.SquareLength() > zero_epsilon) {
|
nuclear@0
|
584 return true;
|
nuclear@0
|
585 }
|
nuclear@0
|
586 }
|
nuclear@0
|
587
|
nuclear@0
|
588 return false;
|
nuclear@0
|
589 }
|
nuclear@0
|
590
|
nuclear@0
|
591
|
nuclear@0
|
592 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
593 // note: name must be a FixNodeName() result
|
nuclear@0
|
594 std::string NameTransformationChainNode(const std::string& name, TransformationComp comp)
|
nuclear@0
|
595 {
|
nuclear@0
|
596 return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
|
nuclear@0
|
597 }
|
nuclear@0
|
598
|
nuclear@0
|
599
|
nuclear@0
|
600 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
601 /** note: memory for output_nodes will be managed by the caller */
|
nuclear@0
|
602 void GenerateTransformationNodeChain(const Model& model,
|
nuclear@0
|
603 std::vector<aiNode*>& output_nodes)
|
nuclear@0
|
604 {
|
nuclear@0
|
605 const PropertyTable& props = model.Props();
|
nuclear@0
|
606 const Model::RotOrder rot = model.RotationOrder();
|
nuclear@0
|
607
|
nuclear@0
|
608 bool ok;
|
nuclear@0
|
609
|
nuclear@0
|
610 aiMatrix4x4 chain[TransformationComp_MAXIMUM];
|
nuclear@0
|
611 std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
|
nuclear@0
|
612
|
nuclear@0
|
613 // generate transformation matrices for all the different transformation components
|
nuclear@0
|
614 const float zero_epsilon = 1e-6f;
|
nuclear@0
|
615 bool is_complex = false;
|
nuclear@0
|
616
|
nuclear@0
|
617 const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props,"PreRotation",ok);
|
nuclear@0
|
618 if(ok && PreRotation.SquareLength() > zero_epsilon) {
|
nuclear@0
|
619 is_complex = true;
|
nuclear@0
|
620
|
nuclear@0
|
621 GetRotationMatrix(rot, PreRotation, chain[TransformationComp_PreRotation]);
|
nuclear@0
|
622 }
|
nuclear@0
|
623
|
nuclear@0
|
624 const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props,"PostRotation",ok);
|
nuclear@0
|
625 if(ok && PostRotation.SquareLength() > zero_epsilon) {
|
nuclear@0
|
626 is_complex = true;
|
nuclear@0
|
627
|
nuclear@0
|
628 GetRotationMatrix(rot, PostRotation, chain[TransformationComp_PostRotation]);
|
nuclear@0
|
629 }
|
nuclear@0
|
630
|
nuclear@0
|
631 const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props,"RotationPivot",ok);
|
nuclear@0
|
632 if(ok && RotationPivot.SquareLength() > zero_epsilon) {
|
nuclear@0
|
633 is_complex = true;
|
nuclear@0
|
634
|
nuclear@0
|
635 aiMatrix4x4::Translation(RotationPivot,chain[TransformationComp_RotationPivot]);
|
nuclear@0
|
636 aiMatrix4x4::Translation(-RotationPivot,chain[TransformationComp_RotationPivotInverse]);
|
nuclear@0
|
637 }
|
nuclear@0
|
638
|
nuclear@0
|
639 const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props,"RotationOffset",ok);
|
nuclear@0
|
640 if(ok && RotationOffset.SquareLength() > zero_epsilon) {
|
nuclear@0
|
641 is_complex = true;
|
nuclear@0
|
642
|
nuclear@0
|
643 aiMatrix4x4::Translation(RotationOffset,chain[TransformationComp_RotationOffset]);
|
nuclear@0
|
644 }
|
nuclear@0
|
645
|
nuclear@0
|
646 const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props,"ScalingOffset",ok);
|
nuclear@0
|
647 if(ok && ScalingOffset.SquareLength() > zero_epsilon) {
|
nuclear@0
|
648 is_complex = true;
|
nuclear@0
|
649
|
nuclear@0
|
650 aiMatrix4x4::Translation(ScalingOffset,chain[TransformationComp_ScalingOffset]);
|
nuclear@0
|
651 }
|
nuclear@0
|
652
|
nuclear@0
|
653 const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props,"ScalingPivot",ok);
|
nuclear@0
|
654 if(ok && ScalingPivot.SquareLength() > zero_epsilon) {
|
nuclear@0
|
655 is_complex = true;
|
nuclear@0
|
656
|
nuclear@0
|
657 aiMatrix4x4::Translation(ScalingPivot,chain[TransformationComp_ScalingPivot]);
|
nuclear@0
|
658 aiMatrix4x4::Translation(-ScalingPivot,chain[TransformationComp_ScalingPivotInverse]);
|
nuclear@0
|
659 }
|
nuclear@0
|
660
|
nuclear@0
|
661 const aiVector3D& Translation = PropertyGet<aiVector3D>(props,"Lcl Translation",ok);
|
nuclear@0
|
662 if(ok && Translation.SquareLength() > zero_epsilon) {
|
nuclear@0
|
663 aiMatrix4x4::Translation(Translation,chain[TransformationComp_Translation]);
|
nuclear@0
|
664 }
|
nuclear@0
|
665
|
nuclear@0
|
666 const aiVector3D& Scaling = PropertyGet<aiVector3D>(props,"Lcl Scaling",ok);
|
nuclear@0
|
667 if(ok && fabs(Scaling.SquareLength()-1.0f) > zero_epsilon) {
|
nuclear@0
|
668 aiMatrix4x4::Scaling(Scaling,chain[TransformationComp_Scaling]);
|
nuclear@0
|
669 }
|
nuclear@0
|
670
|
nuclear@0
|
671 const aiVector3D& Rotation = PropertyGet<aiVector3D>(props,"Lcl Rotation",ok);
|
nuclear@0
|
672 if(ok && Rotation.SquareLength() > zero_epsilon) {
|
nuclear@0
|
673 GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
|
nuclear@0
|
674 }
|
nuclear@0
|
675
|
nuclear@0
|
676 // is_complex needs to be consistent with NeedsComplexTransformationChain()
|
nuclear@0
|
677 // or the interplay between this code and the animation converter would
|
nuclear@0
|
678 // not be guaranteed.
|
nuclear@0
|
679 ai_assert(NeedsComplexTransformationChain(model) == is_complex);
|
nuclear@0
|
680
|
nuclear@0
|
681 const std::string& name = FixNodeName(model.Name());
|
nuclear@0
|
682
|
nuclear@0
|
683 // now, if we have more than just Translation, Scaling and Rotation,
|
nuclear@0
|
684 // we need to generate a full node chain to accommodate for assimp's
|
nuclear@0
|
685 // lack to express pivots and offsets.
|
nuclear@0
|
686 if(is_complex && doc.Settings().preservePivots) {
|
nuclear@0
|
687 FBXImporter::LogInfo("generating full transformation chain for node: " + name);
|
nuclear@0
|
688
|
nuclear@0
|
689 // query the anim_chain_bits dictionary to find out which chain elements
|
nuclear@0
|
690 // have associated node animation channels. These can not be dropped
|
nuclear@0
|
691 // even if they have identity transform in bind pose.
|
nuclear@0
|
692 NodeAnimBitMap::const_iterator it = node_anim_chain_bits.find(name);
|
nuclear@0
|
693 const unsigned int anim_chain_bitmask = (it == node_anim_chain_bits.end() ? 0 : (*it).second);
|
nuclear@0
|
694
|
nuclear@0
|
695 unsigned int bit = 0x1;
|
nuclear@0
|
696 for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
|
nuclear@0
|
697 const TransformationComp comp = static_cast<TransformationComp>(i);
|
nuclear@0
|
698
|
nuclear@0
|
699 if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) {
|
nuclear@0
|
700 continue;
|
nuclear@0
|
701 }
|
nuclear@0
|
702
|
nuclear@0
|
703 aiNode* nd = new aiNode();
|
nuclear@0
|
704 output_nodes.push_back(nd);
|
nuclear@0
|
705
|
nuclear@0
|
706 nd->mName.Set(NameTransformationChainNode(name, comp));
|
nuclear@0
|
707 nd->mTransformation = chain[i];
|
nuclear@0
|
708 }
|
nuclear@0
|
709
|
nuclear@0
|
710 ai_assert(output_nodes.size());
|
nuclear@0
|
711 return;
|
nuclear@0
|
712 }
|
nuclear@0
|
713
|
nuclear@0
|
714 // else, we can just multiply the matrices together
|
nuclear@0
|
715 aiNode* nd = new aiNode();
|
nuclear@0
|
716 output_nodes.push_back(nd);
|
nuclear@0
|
717
|
nuclear@0
|
718 nd->mName.Set(name);
|
nuclear@0
|
719
|
nuclear@0
|
720 for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
nuclear@0
|
721 nd->mTransformation = nd->mTransformation * chain[i];
|
nuclear@0
|
722 }
|
nuclear@0
|
723 }
|
nuclear@0
|
724
|
nuclear@0
|
725
|
nuclear@0
|
726 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
727 void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform)
|
nuclear@0
|
728 {
|
nuclear@0
|
729 const std::vector<const Geometry*>& geos = model.GetGeometry();
|
nuclear@0
|
730
|
nuclear@0
|
731 std::vector<unsigned int> meshes;
|
nuclear@0
|
732 meshes.reserve(geos.size());
|
nuclear@0
|
733
|
nuclear@0
|
734 BOOST_FOREACH(const Geometry* geo, geos) {
|
nuclear@0
|
735
|
nuclear@0
|
736 const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
|
nuclear@0
|
737 if(mesh) {
|
nuclear@0
|
738 const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, node_global_transform);
|
nuclear@0
|
739 std::copy(indices.begin(),indices.end(),std::back_inserter(meshes) );
|
nuclear@0
|
740 }
|
nuclear@0
|
741 else {
|
nuclear@0
|
742 FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name());
|
nuclear@0
|
743 }
|
nuclear@0
|
744 }
|
nuclear@0
|
745
|
nuclear@0
|
746 if(meshes.size()) {
|
nuclear@0
|
747 nd.mMeshes = new unsigned int[meshes.size()]();
|
nuclear@0
|
748 nd.mNumMeshes = static_cast<unsigned int>(meshes.size());
|
nuclear@0
|
749
|
nuclear@0
|
750 std::swap_ranges(meshes.begin(),meshes.end(),nd.mMeshes);
|
nuclear@0
|
751 }
|
nuclear@0
|
752 }
|
nuclear@0
|
753
|
nuclear@0
|
754
|
nuclear@0
|
755 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
756 // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
|
nuclear@0
|
757 std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh,const Model& model,
|
nuclear@0
|
758 const aiMatrix4x4& node_global_transform)
|
nuclear@0
|
759 {
|
nuclear@0
|
760 std::vector<unsigned int> temp;
|
nuclear@0
|
761
|
nuclear@0
|
762 MeshMap::const_iterator it = meshes_converted.find(&mesh);
|
nuclear@0
|
763 if (it != meshes_converted.end()) {
|
nuclear@0
|
764 std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(temp));
|
nuclear@0
|
765 return temp;
|
nuclear@0
|
766 }
|
nuclear@0
|
767
|
nuclear@0
|
768 const std::vector<aiVector3D>& vertices = mesh.GetVertices();
|
nuclear@0
|
769 const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
|
nuclear@0
|
770 if(vertices.empty() || faces.empty()) {
|
nuclear@0
|
771 FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name());
|
nuclear@0
|
772 return temp;
|
nuclear@0
|
773 }
|
nuclear@0
|
774
|
nuclear@0
|
775 // one material per mesh maps easily to aiMesh. Multiple material
|
nuclear@0
|
776 // meshes need to be split.
|
nuclear@0
|
777 const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
nuclear@0
|
778 if (doc.Settings().readMaterials && !mindices.empty()) {
|
nuclear@0
|
779 const MatIndexArray::value_type base = mindices[0];
|
nuclear@0
|
780 BOOST_FOREACH(MatIndexArray::value_type index, mindices) {
|
nuclear@0
|
781 if(index != base) {
|
nuclear@0
|
782 return ConvertMeshMultiMaterial(mesh, model, node_global_transform);
|
nuclear@0
|
783 }
|
nuclear@0
|
784 }
|
nuclear@0
|
785 }
|
nuclear@0
|
786
|
nuclear@0
|
787 // faster codepath, just copy the data
|
nuclear@0
|
788 temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform));
|
nuclear@0
|
789 return temp;
|
nuclear@0
|
790 }
|
nuclear@0
|
791
|
nuclear@0
|
792
|
nuclear@0
|
793 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
794 aiMesh* SetupEmptyMesh(const MeshGeometry& mesh)
|
nuclear@0
|
795 {
|
nuclear@0
|
796 aiMesh* const out_mesh = new aiMesh();
|
nuclear@0
|
797 meshes.push_back(out_mesh);
|
nuclear@0
|
798 meshes_converted[&mesh].push_back(static_cast<unsigned int>(meshes.size()-1));
|
nuclear@0
|
799
|
nuclear@0
|
800 // set name
|
nuclear@0
|
801 std::string name = mesh.Name();
|
nuclear@0
|
802 if (name.substr(0,10) == "Geometry::") {
|
nuclear@0
|
803 name = name.substr(10);
|
nuclear@0
|
804 }
|
nuclear@0
|
805
|
nuclear@0
|
806 if(name.length()) {
|
nuclear@0
|
807 out_mesh->mName.Set(name);
|
nuclear@0
|
808 }
|
nuclear@0
|
809
|
nuclear@0
|
810 return out_mesh;
|
nuclear@0
|
811 }
|
nuclear@0
|
812
|
nuclear@0
|
813
|
nuclear@0
|
814 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
815 unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
|
nuclear@0
|
816 const aiMatrix4x4& node_global_transform)
|
nuclear@0
|
817 {
|
nuclear@0
|
818 const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
nuclear@0
|
819 aiMesh* const out_mesh = SetupEmptyMesh(mesh);
|
nuclear@0
|
820
|
nuclear@0
|
821 const std::vector<aiVector3D>& vertices = mesh.GetVertices();
|
nuclear@0
|
822 const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
|
nuclear@0
|
823
|
nuclear@0
|
824 // copy vertices
|
nuclear@0
|
825 out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
|
nuclear@0
|
826 out_mesh->mVertices = new aiVector3D[vertices.size()];
|
nuclear@0
|
827 std::copy(vertices.begin(),vertices.end(),out_mesh->mVertices);
|
nuclear@0
|
828
|
nuclear@0
|
829 // generate dummy faces
|
nuclear@0
|
830 out_mesh->mNumFaces = static_cast<unsigned int>(faces.size());
|
nuclear@0
|
831 aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()]();
|
nuclear@0
|
832
|
nuclear@0
|
833 unsigned int cursor = 0;
|
nuclear@0
|
834 BOOST_FOREACH(unsigned int pcount, faces) {
|
nuclear@0
|
835 aiFace& f = *fac++;
|
nuclear@0
|
836 f.mNumIndices = pcount;
|
nuclear@0
|
837 f.mIndices = new unsigned int[pcount];
|
nuclear@0
|
838 switch(pcount)
|
nuclear@0
|
839 {
|
nuclear@0
|
840 case 1:
|
nuclear@0
|
841 out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
|
nuclear@0
|
842 break;
|
nuclear@0
|
843 case 2:
|
nuclear@0
|
844 out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
|
nuclear@0
|
845 break;
|
nuclear@0
|
846 case 3:
|
nuclear@0
|
847 out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
|
nuclear@0
|
848 break;
|
nuclear@0
|
849 default:
|
nuclear@0
|
850 out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
|
nuclear@0
|
851 break;
|
nuclear@0
|
852 }
|
nuclear@0
|
853 for (unsigned int i = 0; i < pcount; ++i) {
|
nuclear@0
|
854 f.mIndices[i] = cursor++;
|
nuclear@0
|
855 }
|
nuclear@0
|
856 }
|
nuclear@0
|
857
|
nuclear@0
|
858 // copy normals
|
nuclear@0
|
859 const std::vector<aiVector3D>& normals = mesh.GetNormals();
|
nuclear@0
|
860 if(normals.size()) {
|
nuclear@0
|
861 ai_assert(normals.size() == vertices.size());
|
nuclear@0
|
862
|
nuclear@0
|
863 out_mesh->mNormals = new aiVector3D[vertices.size()];
|
nuclear@0
|
864 std::copy(normals.begin(),normals.end(),out_mesh->mNormals);
|
nuclear@0
|
865 }
|
nuclear@0
|
866
|
nuclear@0
|
867 // copy tangents - assimp requires both tangents and bitangents (binormals)
|
nuclear@0
|
868 // to be present, or neither of them. Compute binormals from normals
|
nuclear@0
|
869 // and tangents if needed.
|
nuclear@0
|
870 const std::vector<aiVector3D>& tangents = mesh.GetTangents();
|
nuclear@0
|
871 const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
|
nuclear@0
|
872
|
nuclear@0
|
873 if(tangents.size()) {
|
nuclear@0
|
874 std::vector<aiVector3D> tempBinormals;
|
nuclear@0
|
875 if (!binormals->size()) {
|
nuclear@0
|
876 if (normals.size()) {
|
nuclear@0
|
877 tempBinormals.resize(normals.size());
|
nuclear@0
|
878 for (unsigned int i = 0; i < tangents.size(); ++i) {
|
nuclear@0
|
879 tempBinormals[i] = normals[i] ^ tangents[i];
|
nuclear@0
|
880 }
|
nuclear@0
|
881
|
nuclear@0
|
882 binormals = &tempBinormals;
|
nuclear@0
|
883 }
|
nuclear@0
|
884 else {
|
nuclear@0
|
885 binormals = NULL;
|
nuclear@0
|
886 }
|
nuclear@0
|
887 }
|
nuclear@0
|
888
|
nuclear@0
|
889 if(binormals) {
|
nuclear@0
|
890 ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
|
nuclear@0
|
891
|
nuclear@0
|
892 out_mesh->mTangents = new aiVector3D[vertices.size()];
|
nuclear@0
|
893 std::copy(tangents.begin(),tangents.end(),out_mesh->mTangents);
|
nuclear@0
|
894
|
nuclear@0
|
895 out_mesh->mBitangents = new aiVector3D[vertices.size()];
|
nuclear@0
|
896 std::copy(binormals->begin(),binormals->end(),out_mesh->mBitangents);
|
nuclear@0
|
897 }
|
nuclear@0
|
898 }
|
nuclear@0
|
899
|
nuclear@0
|
900 // copy texture coords
|
nuclear@0
|
901 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
|
nuclear@0
|
902 const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
|
nuclear@0
|
903 if(uvs.empty()) {
|
nuclear@0
|
904 break;
|
nuclear@0
|
905 }
|
nuclear@0
|
906
|
nuclear@0
|
907 aiVector3D* out_uv = out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
|
nuclear@0
|
908 BOOST_FOREACH(const aiVector2D& v, uvs) {
|
nuclear@0
|
909 *out_uv++ = aiVector3D(v.x,v.y,0.0f);
|
nuclear@0
|
910 }
|
nuclear@0
|
911
|
nuclear@0
|
912 out_mesh->mNumUVComponents[i] = 2;
|
nuclear@0
|
913 }
|
nuclear@0
|
914
|
nuclear@0
|
915 // copy vertex colors
|
nuclear@0
|
916 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
|
nuclear@0
|
917 const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
|
nuclear@0
|
918 if(colors.empty()) {
|
nuclear@0
|
919 break;
|
nuclear@0
|
920 }
|
nuclear@0
|
921
|
nuclear@0
|
922 out_mesh->mColors[i] = new aiColor4D[vertices.size()];
|
nuclear@0
|
923 std::copy(colors.begin(),colors.end(),out_mesh->mColors[i]);
|
nuclear@0
|
924 }
|
nuclear@0
|
925
|
nuclear@0
|
926 if(!doc.Settings().readMaterials || mindices.empty()) {
|
nuclear@0
|
927 FBXImporter::LogError("no material assigned to mesh, setting default material");
|
nuclear@0
|
928 out_mesh->mMaterialIndex = GetDefaultMaterial();
|
nuclear@0
|
929 }
|
nuclear@0
|
930 else {
|
nuclear@0
|
931 ConvertMaterialForMesh(out_mesh,model,mesh,mindices[0]);
|
nuclear@0
|
932 }
|
nuclear@0
|
933
|
nuclear@0
|
934 if(doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
|
nuclear@0
|
935 ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION);
|
nuclear@0
|
936 }
|
nuclear@0
|
937
|
nuclear@0
|
938 return static_cast<unsigned int>(meshes.size() - 1);
|
nuclear@0
|
939 }
|
nuclear@0
|
940
|
nuclear@0
|
941
|
nuclear@0
|
942 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
943 std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
|
nuclear@0
|
944 const aiMatrix4x4& node_global_transform)
|
nuclear@0
|
945 {
|
nuclear@0
|
946 const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
nuclear@0
|
947 ai_assert(mindices.size());
|
nuclear@0
|
948
|
nuclear@0
|
949 std::set<MatIndexArray::value_type> had;
|
nuclear@0
|
950 std::vector<unsigned int> indices;
|
nuclear@0
|
951
|
nuclear@0
|
952 BOOST_FOREACH(MatIndexArray::value_type index, mindices) {
|
nuclear@0
|
953 if(had.find(index) == had.end()) {
|
nuclear@0
|
954
|
nuclear@0
|
955 indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform));
|
nuclear@0
|
956 had.insert(index);
|
nuclear@0
|
957 }
|
nuclear@0
|
958 }
|
nuclear@0
|
959
|
nuclear@0
|
960 return indices;
|
nuclear@0
|
961 }
|
nuclear@0
|
962
|
nuclear@0
|
963
|
nuclear@0
|
964 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
965 unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
|
nuclear@0
|
966 MatIndexArray::value_type index,
|
nuclear@0
|
967 const aiMatrix4x4& node_global_transform)
|
nuclear@0
|
968 {
|
nuclear@0
|
969 aiMesh* const out_mesh = SetupEmptyMesh(mesh);
|
nuclear@0
|
970
|
nuclear@0
|
971 const MatIndexArray& mindices = mesh.GetMaterialIndices();
|
nuclear@0
|
972 const std::vector<aiVector3D>& vertices = mesh.GetVertices();
|
nuclear@0
|
973 const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
|
nuclear@0
|
974
|
nuclear@0
|
975 const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL;
|
nuclear@0
|
976
|
nuclear@0
|
977 unsigned int count_faces = 0;
|
nuclear@0
|
978 unsigned int count_vertices = 0;
|
nuclear@0
|
979
|
nuclear@0
|
980 // count faces
|
nuclear@0
|
981 std::vector<unsigned int>::const_iterator itf = faces.begin();
|
nuclear@0
|
982 for(MatIndexArray::const_iterator it = mindices.begin(),
|
nuclear@0
|
983 end = mindices.end(); it != end; ++it, ++itf)
|
nuclear@0
|
984 {
|
nuclear@0
|
985 if ((*it) != index) {
|
nuclear@0
|
986 continue;
|
nuclear@0
|
987 }
|
nuclear@0
|
988 ++count_faces;
|
nuclear@0
|
989 count_vertices += *itf;
|
nuclear@0
|
990 }
|
nuclear@0
|
991
|
nuclear@0
|
992 ai_assert(count_faces);
|
nuclear@0
|
993 ai_assert(count_vertices);
|
nuclear@0
|
994
|
nuclear@0
|
995 // mapping from output indices to DOM indexing, needed to resolve weights
|
nuclear@0
|
996 std::vector<unsigned int> reverseMapping;
|
nuclear@0
|
997
|
nuclear@0
|
998 if (process_weights) {
|
nuclear@0
|
999 reverseMapping.resize(count_vertices);
|
nuclear@0
|
1000 }
|
nuclear@0
|
1001
|
nuclear@0
|
1002 // allocate output data arrays, but don't fill them yet
|
nuclear@0
|
1003 out_mesh->mNumVertices = count_vertices;
|
nuclear@0
|
1004 out_mesh->mVertices = new aiVector3D[count_vertices];
|
nuclear@0
|
1005
|
nuclear@0
|
1006 out_mesh->mNumFaces = count_faces;
|
nuclear@0
|
1007 aiFace* fac = out_mesh->mFaces = new aiFace[count_faces]();
|
nuclear@0
|
1008
|
nuclear@0
|
1009
|
nuclear@0
|
1010 // allocate normals
|
nuclear@0
|
1011 const std::vector<aiVector3D>& normals = mesh.GetNormals();
|
nuclear@0
|
1012 if(normals.size()) {
|
nuclear@0
|
1013 ai_assert(normals.size() == vertices.size());
|
nuclear@0
|
1014 out_mesh->mNormals = new aiVector3D[vertices.size()];
|
nuclear@0
|
1015 }
|
nuclear@0
|
1016
|
nuclear@0
|
1017 // allocate tangents, binormals.
|
nuclear@0
|
1018 const std::vector<aiVector3D>& tangents = mesh.GetTangents();
|
nuclear@0
|
1019 const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
|
nuclear@0
|
1020
|
nuclear@0
|
1021 if(tangents.size()) {
|
nuclear@0
|
1022 std::vector<aiVector3D> tempBinormals;
|
nuclear@0
|
1023 if (!binormals->size()) {
|
nuclear@0
|
1024 if (normals.size()) {
|
nuclear@0
|
1025 // XXX this computes the binormals for the entire mesh, not only
|
nuclear@0
|
1026 // the part for which we need them.
|
nuclear@0
|
1027 tempBinormals.resize(normals.size());
|
nuclear@0
|
1028 for (unsigned int i = 0; i < tangents.size(); ++i) {
|
nuclear@0
|
1029 tempBinormals[i] = normals[i] ^ tangents[i];
|
nuclear@0
|
1030 }
|
nuclear@0
|
1031
|
nuclear@0
|
1032 binormals = &tempBinormals;
|
nuclear@0
|
1033 }
|
nuclear@0
|
1034 else {
|
nuclear@0
|
1035 binormals = NULL;
|
nuclear@0
|
1036 }
|
nuclear@0
|
1037 }
|
nuclear@0
|
1038
|
nuclear@0
|
1039 if(binormals) {
|
nuclear@0
|
1040 ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
|
nuclear@0
|
1041
|
nuclear@0
|
1042 out_mesh->mTangents = new aiVector3D[vertices.size()];
|
nuclear@0
|
1043 out_mesh->mBitangents = new aiVector3D[vertices.size()];
|
nuclear@0
|
1044 }
|
nuclear@0
|
1045 }
|
nuclear@0
|
1046
|
nuclear@0
|
1047 // allocate texture coords
|
nuclear@0
|
1048 unsigned int num_uvs = 0;
|
nuclear@0
|
1049 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) {
|
nuclear@0
|
1050 const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
|
nuclear@0
|
1051 if(uvs.empty()) {
|
nuclear@0
|
1052 break;
|
nuclear@0
|
1053 }
|
nuclear@0
|
1054
|
nuclear@0
|
1055 out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
|
nuclear@0
|
1056 out_mesh->mNumUVComponents[i] = 2;
|
nuclear@0
|
1057 }
|
nuclear@0
|
1058
|
nuclear@0
|
1059 // allocate vertex colors
|
nuclear@0
|
1060 unsigned int num_vcs = 0;
|
nuclear@0
|
1061 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) {
|
nuclear@0
|
1062 const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
|
nuclear@0
|
1063 if(colors.empty()) {
|
nuclear@0
|
1064 break;
|
nuclear@0
|
1065 }
|
nuclear@0
|
1066
|
nuclear@0
|
1067 out_mesh->mColors[i] = new aiColor4D[vertices.size()];
|
nuclear@0
|
1068 }
|
nuclear@0
|
1069
|
nuclear@0
|
1070 unsigned int cursor = 0, in_cursor = 0;
|
nuclear@0
|
1071
|
nuclear@0
|
1072 itf = faces.begin();
|
nuclear@0
|
1073 for(MatIndexArray::const_iterator it = mindices.begin(),
|
nuclear@0
|
1074 end = mindices.end(); it != end; ++it, ++itf)
|
nuclear@0
|
1075 {
|
nuclear@0
|
1076 const unsigned int pcount = *itf;
|
nuclear@0
|
1077 if ((*it) != index) {
|
nuclear@0
|
1078 in_cursor += pcount;
|
nuclear@0
|
1079 continue;
|
nuclear@0
|
1080 }
|
nuclear@0
|
1081
|
nuclear@0
|
1082 aiFace& f = *fac++;
|
nuclear@0
|
1083
|
nuclear@0
|
1084 f.mNumIndices = pcount;
|
nuclear@0
|
1085 f.mIndices = new unsigned int[pcount];
|
nuclear@0
|
1086 switch(pcount)
|
nuclear@0
|
1087 {
|
nuclear@0
|
1088 case 1:
|
nuclear@0
|
1089 out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
|
nuclear@0
|
1090 break;
|
nuclear@0
|
1091 case 2:
|
nuclear@0
|
1092 out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
|
nuclear@0
|
1093 break;
|
nuclear@0
|
1094 case 3:
|
nuclear@0
|
1095 out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
|
nuclear@0
|
1096 break;
|
nuclear@0
|
1097 default:
|
nuclear@0
|
1098 out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
|
nuclear@0
|
1099 break;
|
nuclear@0
|
1100 }
|
nuclear@0
|
1101 for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) {
|
nuclear@0
|
1102 f.mIndices[i] = cursor;
|
nuclear@0
|
1103
|
nuclear@0
|
1104 if(reverseMapping.size()) {
|
nuclear@0
|
1105 reverseMapping[cursor] = in_cursor;
|
nuclear@0
|
1106 }
|
nuclear@0
|
1107
|
nuclear@0
|
1108 out_mesh->mVertices[cursor] = vertices[in_cursor];
|
nuclear@0
|
1109
|
nuclear@0
|
1110 if(out_mesh->mNormals) {
|
nuclear@0
|
1111 out_mesh->mNormals[cursor] = normals[in_cursor];
|
nuclear@0
|
1112 }
|
nuclear@0
|
1113
|
nuclear@0
|
1114 if(out_mesh->mTangents) {
|
nuclear@0
|
1115 out_mesh->mTangents[cursor] = tangents[in_cursor];
|
nuclear@0
|
1116 out_mesh->mBitangents[cursor] = (*binormals)[in_cursor];
|
nuclear@0
|
1117 }
|
nuclear@0
|
1118
|
nuclear@0
|
1119 for (unsigned int i = 0; i < num_uvs; ++i) {
|
nuclear@0
|
1120 const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
|
nuclear@0
|
1121 out_mesh->mTextureCoords[i][cursor] = aiVector3D(uvs[in_cursor].x,uvs[in_cursor].y, 0.0f);
|
nuclear@0
|
1122 }
|
nuclear@0
|
1123
|
nuclear@0
|
1124 for (unsigned int i = 0; i < num_vcs; ++i) {
|
nuclear@0
|
1125 const std::vector<aiColor4D>& cols = mesh.GetVertexColors(i);
|
nuclear@0
|
1126 out_mesh->mColors[i][cursor] = cols[in_cursor];
|
nuclear@0
|
1127 }
|
nuclear@0
|
1128 }
|
nuclear@0
|
1129 }
|
nuclear@0
|
1130
|
nuclear@0
|
1131 ConvertMaterialForMesh(out_mesh,model,mesh,index);
|
nuclear@0
|
1132
|
nuclear@0
|
1133 if(process_weights) {
|
nuclear@0
|
1134 ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping);
|
nuclear@0
|
1135 }
|
nuclear@0
|
1136
|
nuclear@0
|
1137 return static_cast<unsigned int>(meshes.size() - 1);
|
nuclear@0
|
1138 }
|
nuclear@0
|
1139
|
nuclear@0
|
1140 static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
|
nuclear@0
|
1141 static_cast<unsigned int>(-1);
|
nuclear@0
|
1142
|
nuclear@0
|
1143
|
nuclear@0
|
1144 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1145 /** - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into
|
nuclear@0
|
1146 * account when determining which weights to include.
|
nuclear@0
|
1147 * - outputVertStartIndices is only used when a material index is specified, it gives for
|
nuclear@0
|
1148 * each output vertex the DOM index it maps to. */
|
nuclear@0
|
1149 void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo,
|
nuclear@0
|
1150 const aiMatrix4x4& node_global_transform = aiMatrix4x4(),
|
nuclear@0
|
1151 unsigned int materialIndex = NO_MATERIAL_SEPARATION,
|
nuclear@0
|
1152 std::vector<unsigned int>* outputVertStartIndices = NULL)
|
nuclear@0
|
1153 {
|
nuclear@0
|
1154 ai_assert(geo.DeformerSkin());
|
nuclear@0
|
1155
|
nuclear@0
|
1156 std::vector<size_t> out_indices;
|
nuclear@0
|
1157 std::vector<size_t> index_out_indices;
|
nuclear@0
|
1158 std::vector<size_t> count_out_indices;
|
nuclear@0
|
1159
|
nuclear@0
|
1160 const Skin& sk = *geo.DeformerSkin();
|
nuclear@0
|
1161
|
nuclear@0
|
1162 std::vector<aiBone*> bones;
|
nuclear@0
|
1163 bones.reserve(sk.Clusters().size());
|
nuclear@0
|
1164
|
nuclear@0
|
1165 const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
|
nuclear@0
|
1166 ai_assert(no_mat_check || outputVertStartIndices);
|
nuclear@0
|
1167
|
nuclear@0
|
1168 try {
|
nuclear@0
|
1169
|
nuclear@0
|
1170 BOOST_FOREACH(const Cluster* cluster, sk.Clusters()) {
|
nuclear@0
|
1171 ai_assert(cluster);
|
nuclear@0
|
1172
|
nuclear@0
|
1173 const WeightIndexArray& indices = cluster->GetIndices();
|
nuclear@0
|
1174
|
nuclear@0
|
1175 if(indices.empty()) {
|
nuclear@0
|
1176 continue;
|
nuclear@0
|
1177 }
|
nuclear@0
|
1178
|
nuclear@0
|
1179 const MatIndexArray& mats = geo.GetMaterialIndices();
|
nuclear@0
|
1180
|
nuclear@0
|
1181 bool ok = false;
|
nuclear@0
|
1182
|
nuclear@0
|
1183 const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
|
nuclear@0
|
1184
|
nuclear@0
|
1185 count_out_indices.clear();
|
nuclear@0
|
1186 index_out_indices.clear();
|
nuclear@0
|
1187 out_indices.clear();
|
nuclear@0
|
1188
|
nuclear@0
|
1189 // now check if *any* of these weights is contained in the output mesh,
|
nuclear@0
|
1190 // taking notes so we don't need to do it twice.
|
nuclear@0
|
1191 BOOST_FOREACH(WeightIndexArray::value_type index, indices) {
|
nuclear@0
|
1192
|
nuclear@0
|
1193 unsigned int count;
|
nuclear@0
|
1194 const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count);
|
nuclear@0
|
1195
|
nuclear@0
|
1196 index_out_indices.push_back(no_index_sentinel);
|
nuclear@0
|
1197 count_out_indices.push_back(0);
|
nuclear@0
|
1198
|
nuclear@0
|
1199 for(unsigned int i = 0; i < count; ++i) {
|
nuclear@0
|
1200 if (no_mat_check || static_cast<size_t>(mats[geo.FaceForVertexIndex(out_idx[i])]) == materialIndex) {
|
nuclear@0
|
1201
|
nuclear@0
|
1202 if (index_out_indices.back() == no_index_sentinel) {
|
nuclear@0
|
1203 index_out_indices.back() = out_indices.size();
|
nuclear@0
|
1204
|
nuclear@0
|
1205 }
|
nuclear@0
|
1206
|
nuclear@0
|
1207 if (no_mat_check) {
|
nuclear@0
|
1208 out_indices.push_back(out_idx[i]);
|
nuclear@0
|
1209 }
|
nuclear@0
|
1210 else {
|
nuclear@0
|
1211 // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
|
nuclear@0
|
1212 const std::vector<unsigned int>::iterator it = std::lower_bound(
|
nuclear@0
|
1213 outputVertStartIndices->begin(),
|
nuclear@0
|
1214 outputVertStartIndices->end(),
|
nuclear@0
|
1215 out_idx[i]
|
nuclear@0
|
1216 );
|
nuclear@0
|
1217
|
nuclear@0
|
1218 out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
|
nuclear@0
|
1219 }
|
nuclear@0
|
1220
|
nuclear@0
|
1221 ++count_out_indices.back();
|
nuclear@0
|
1222 ok = true;
|
nuclear@0
|
1223 }
|
nuclear@0
|
1224 }
|
nuclear@0
|
1225 }
|
nuclear@0
|
1226
|
nuclear@0
|
1227 // if we found at least one, generate the output bones
|
nuclear@0
|
1228 // XXX this could be heavily simplified by collecting the bone
|
nuclear@0
|
1229 // data in a single step.
|
nuclear@0
|
1230 if (ok) {
|
nuclear@0
|
1231 ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
|
nuclear@0
|
1232 count_out_indices, node_global_transform);
|
nuclear@0
|
1233 }
|
nuclear@0
|
1234 }
|
nuclear@0
|
1235 }
|
nuclear@0
|
1236 catch (std::exception&) {
|
nuclear@0
|
1237 std::for_each(bones.begin(),bones.end(),Util::delete_fun<aiBone>());
|
nuclear@0
|
1238 throw;
|
nuclear@0
|
1239 }
|
nuclear@0
|
1240
|
nuclear@0
|
1241 if(bones.empty()) {
|
nuclear@0
|
1242 return;
|
nuclear@0
|
1243 }
|
nuclear@0
|
1244
|
nuclear@0
|
1245 out->mBones = new aiBone*[bones.size()]();
|
nuclear@0
|
1246 out->mNumBones = static_cast<unsigned int>(bones.size());
|
nuclear@0
|
1247
|
nuclear@0
|
1248 std::swap_ranges(bones.begin(),bones.end(),out->mBones);
|
nuclear@0
|
1249 }
|
nuclear@0
|
1250
|
nuclear@0
|
1251
|
nuclear@0
|
1252
|
nuclear@0
|
1253 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1254 void ConvertCluster(std::vector<aiBone*>& bones, const Model& model, const Cluster& cl,
|
nuclear@0
|
1255 std::vector<size_t>& out_indices,
|
nuclear@0
|
1256 std::vector<size_t>& index_out_indices,
|
nuclear@0
|
1257 std::vector<size_t>& count_out_indices,
|
nuclear@0
|
1258 const aiMatrix4x4& node_global_transform)
|
nuclear@0
|
1259 {
|
nuclear@0
|
1260
|
nuclear@0
|
1261 aiBone* const bone = new aiBone();
|
nuclear@0
|
1262 bones.push_back(bone);
|
nuclear@0
|
1263
|
nuclear@0
|
1264 bone->mName = FixNodeName(cl.TargetNode()->Name());
|
nuclear@0
|
1265
|
nuclear@0
|
1266 bone->mOffsetMatrix = cl.TransformLink();
|
nuclear@0
|
1267 bone->mOffsetMatrix.Inverse();
|
nuclear@0
|
1268
|
nuclear@0
|
1269 bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform;
|
nuclear@0
|
1270
|
nuclear@0
|
1271 bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
|
nuclear@0
|
1272 aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
|
nuclear@0
|
1273
|
nuclear@0
|
1274 const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
|
nuclear@0
|
1275 const WeightArray& weights = cl.GetWeights();
|
nuclear@0
|
1276
|
nuclear@0
|
1277 const size_t c = index_out_indices.size();
|
nuclear@0
|
1278 for (size_t i = 0; i < c; ++i) {
|
nuclear@0
|
1279 const size_t index_index = index_out_indices[i];
|
nuclear@0
|
1280
|
nuclear@0
|
1281 if (index_index == no_index_sentinel) {
|
nuclear@0
|
1282 continue;
|
nuclear@0
|
1283 }
|
nuclear@0
|
1284
|
nuclear@0
|
1285 const size_t cc = count_out_indices[i];
|
nuclear@0
|
1286 for (size_t j = 0; j < cc; ++j) {
|
nuclear@0
|
1287 aiVertexWeight& out_weight = *cursor++;
|
nuclear@0
|
1288
|
nuclear@0
|
1289 out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
|
nuclear@0
|
1290 out_weight.mWeight = weights[i];
|
nuclear@0
|
1291 }
|
nuclear@0
|
1292 }
|
nuclear@0
|
1293 }
|
nuclear@0
|
1294
|
nuclear@0
|
1295
|
nuclear@0
|
1296 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1297 void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
|
nuclear@0
|
1298 MatIndexArray::value_type materialIndex)
|
nuclear@0
|
1299 {
|
nuclear@0
|
1300 // locate source materials for this mesh
|
nuclear@0
|
1301 const std::vector<const Material*>& mats = model.GetMaterials();
|
nuclear@0
|
1302 if (static_cast<unsigned int>(materialIndex) >= mats.size() || materialIndex < 0) {
|
nuclear@0
|
1303 FBXImporter::LogError("material index out of bounds, setting default material");
|
nuclear@0
|
1304 out->mMaterialIndex = GetDefaultMaterial();
|
nuclear@0
|
1305 return;
|
nuclear@0
|
1306 }
|
nuclear@0
|
1307
|
nuclear@0
|
1308 const Material* const mat = mats[materialIndex];
|
nuclear@0
|
1309 MaterialMap::const_iterator it = materials_converted.find(mat);
|
nuclear@0
|
1310 if (it != materials_converted.end()) {
|
nuclear@0
|
1311 out->mMaterialIndex = (*it).second;
|
nuclear@0
|
1312 return;
|
nuclear@0
|
1313 }
|
nuclear@0
|
1314
|
nuclear@0
|
1315 out->mMaterialIndex = ConvertMaterial(*mat);
|
nuclear@0
|
1316 materials_converted[mat] = out->mMaterialIndex;
|
nuclear@0
|
1317 }
|
nuclear@0
|
1318
|
nuclear@0
|
1319
|
nuclear@0
|
1320 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1321 unsigned int GetDefaultMaterial()
|
nuclear@0
|
1322 {
|
nuclear@0
|
1323 if (defaultMaterialIndex) {
|
nuclear@0
|
1324 return defaultMaterialIndex - 1;
|
nuclear@0
|
1325 }
|
nuclear@0
|
1326
|
nuclear@0
|
1327 aiMaterial* out_mat = new aiMaterial();
|
nuclear@0
|
1328 materials.push_back(out_mat);
|
nuclear@0
|
1329
|
nuclear@0
|
1330 const aiColor3D diffuse = aiColor3D(0.8f,0.8f,0.8f);
|
nuclear@0
|
1331 out_mat->AddProperty(&diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
|
nuclear@0
|
1332
|
nuclear@0
|
1333 aiString s;
|
nuclear@0
|
1334 s.Set(AI_DEFAULT_MATERIAL_NAME);
|
nuclear@0
|
1335
|
nuclear@0
|
1336 out_mat->AddProperty(&s,AI_MATKEY_NAME);
|
nuclear@0
|
1337
|
nuclear@0
|
1338 defaultMaterialIndex = static_cast<unsigned int>(materials.size());
|
nuclear@0
|
1339 return defaultMaterialIndex - 1;
|
nuclear@0
|
1340 }
|
nuclear@0
|
1341
|
nuclear@0
|
1342
|
nuclear@0
|
1343 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1344 // Material -> aiMaterial
|
nuclear@0
|
1345 unsigned int ConvertMaterial(const Material& material)
|
nuclear@0
|
1346 {
|
nuclear@0
|
1347 const PropertyTable& props = material.Props();
|
nuclear@0
|
1348
|
nuclear@0
|
1349 // generate empty output material
|
nuclear@0
|
1350 aiMaterial* out_mat = new aiMaterial();
|
nuclear@0
|
1351 materials_converted[&material] = static_cast<unsigned int>(materials.size());
|
nuclear@0
|
1352
|
nuclear@0
|
1353 materials.push_back(out_mat);
|
nuclear@0
|
1354
|
nuclear@0
|
1355 aiString str;
|
nuclear@0
|
1356
|
nuclear@0
|
1357 // stip Material:: prefix
|
nuclear@0
|
1358 std::string name = material.Name();
|
nuclear@0
|
1359 if(name.substr(0,10) == "Material::") {
|
nuclear@0
|
1360 name = name.substr(10);
|
nuclear@0
|
1361 }
|
nuclear@0
|
1362
|
nuclear@0
|
1363 // set material name if not empty - this could happen
|
nuclear@0
|
1364 // and there should be no key for it in this case.
|
nuclear@0
|
1365 if(name.length()) {
|
nuclear@0
|
1366 str.Set(name);
|
nuclear@0
|
1367 out_mat->AddProperty(&str,AI_MATKEY_NAME);
|
nuclear@0
|
1368 }
|
nuclear@0
|
1369
|
nuclear@0
|
1370 // shading stuff and colors
|
nuclear@0
|
1371 SetShadingPropertiesCommon(out_mat,props);
|
nuclear@0
|
1372
|
nuclear@0
|
1373 // texture assignments
|
nuclear@0
|
1374 SetTextureProperties(out_mat,material.Textures());
|
nuclear@0
|
1375
|
nuclear@0
|
1376 return static_cast<unsigned int>(materials.size() - 1);
|
nuclear@0
|
1377 }
|
nuclear@0
|
1378
|
nuclear@0
|
1379
|
nuclear@0
|
1380 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1381 void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
|
nuclear@0
|
1382 const std::string& propName,
|
nuclear@0
|
1383 aiTextureType target)
|
nuclear@0
|
1384 {
|
nuclear@0
|
1385 TextureMap::const_iterator it = textures.find(propName);
|
nuclear@0
|
1386 if(it == textures.end()) {
|
nuclear@0
|
1387 return;
|
nuclear@0
|
1388 }
|
nuclear@0
|
1389
|
nuclear@0
|
1390 const Texture* const tex = (*it).second;
|
nuclear@0
|
1391
|
nuclear@0
|
1392 aiString path;
|
nuclear@0
|
1393 path.Set(tex->RelativeFilename());
|
nuclear@0
|
1394
|
nuclear@0
|
1395 out_mat->AddProperty(&path,_AI_MATKEY_TEXTURE_BASE,target,0);
|
nuclear@0
|
1396
|
nuclear@0
|
1397 aiUVTransform uvTrafo;
|
nuclear@0
|
1398 // XXX handle all kinds of UV transformations
|
nuclear@0
|
1399 uvTrafo.mScaling = tex->UVScaling();
|
nuclear@0
|
1400 uvTrafo.mTranslation = tex->UVTranslation();
|
nuclear@0
|
1401 out_mat->AddProperty(&uvTrafo,1,_AI_MATKEY_UVTRANSFORM_BASE,target,0);
|
nuclear@0
|
1402
|
nuclear@0
|
1403 const PropertyTable& props = tex->Props();
|
nuclear@0
|
1404
|
nuclear@0
|
1405 int uvIndex = 0;
|
nuclear@0
|
1406
|
nuclear@0
|
1407 bool ok;
|
nuclear@0
|
1408 const std::string& uvSet = PropertyGet<std::string>(props,"UVSet",ok);
|
nuclear@0
|
1409 if(ok) {
|
nuclear@0
|
1410 // "default" is the name which usually appears in the FbxFileTexture template
|
nuclear@0
|
1411 if(uvSet != "default" && uvSet.length()) {
|
nuclear@0
|
1412 // this is a bit awkward - we need to find a mesh that uses this
|
nuclear@0
|
1413 // material and scan its UV channels for the given UV name because
|
nuclear@0
|
1414 // assimp references UV channels by index, not by name.
|
nuclear@0
|
1415
|
nuclear@0
|
1416 // XXX: the case that UV channels may appear in different orders
|
nuclear@0
|
1417 // in meshes is unhandled. A possible solution would be to sort
|
nuclear@0
|
1418 // the UV channels alphabetically, but this would have the side
|
nuclear@0
|
1419 // effect that the primary (first) UV channel would sometimes
|
nuclear@0
|
1420 // be moved, causing trouble when users read only the first
|
nuclear@0
|
1421 // UV channel and ignore UV channel assignments altogether.
|
nuclear@0
|
1422
|
nuclear@0
|
1423 const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(),
|
nuclear@0
|
1424 std::find(materials.begin(),materials.end(),out_mat)
|
nuclear@0
|
1425 ));
|
nuclear@0
|
1426
|
nuclear@0
|
1427 uvIndex = -1;
|
nuclear@0
|
1428 BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) {
|
nuclear@0
|
1429 const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first);
|
nuclear@0
|
1430 if(!mesh) {
|
nuclear@0
|
1431 continue;
|
nuclear@0
|
1432 }
|
nuclear@0
|
1433
|
nuclear@0
|
1434 const MatIndexArray& mats = mesh->GetMaterialIndices();
|
nuclear@0
|
1435 if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) {
|
nuclear@0
|
1436 continue;
|
nuclear@0
|
1437 }
|
nuclear@0
|
1438
|
nuclear@0
|
1439 int index = -1;
|
nuclear@0
|
1440 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
|
nuclear@0
|
1441 if(mesh->GetTextureCoords(i).empty()) {
|
nuclear@0
|
1442 break;
|
nuclear@0
|
1443 }
|
nuclear@0
|
1444 const std::string& name = mesh->GetTextureCoordChannelName(i);
|
nuclear@0
|
1445 if(name == uvSet) {
|
nuclear@0
|
1446 index = static_cast<int>(i);
|
nuclear@0
|
1447 break;
|
nuclear@0
|
1448 }
|
nuclear@0
|
1449 }
|
nuclear@0
|
1450 if(index == -1) {
|
nuclear@0
|
1451 FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
|
nuclear@0
|
1452 continue;
|
nuclear@0
|
1453 }
|
nuclear@0
|
1454
|
nuclear@0
|
1455 if(uvIndex == -1) {
|
nuclear@0
|
1456 uvIndex = index;
|
nuclear@0
|
1457 }
|
nuclear@0
|
1458 else {
|
nuclear@0
|
1459 FBXImporter::LogWarn("the UV channel named " + uvSet +
|
nuclear@0
|
1460 " appears at different positions in meshes, results will be wrong");
|
nuclear@0
|
1461 }
|
nuclear@0
|
1462 }
|
nuclear@0
|
1463
|
nuclear@0
|
1464 if(uvIndex == -1) {
|
nuclear@0
|
1465 FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
|
nuclear@0
|
1466 uvIndex = 0;
|
nuclear@0
|
1467 }
|
nuclear@0
|
1468 }
|
nuclear@0
|
1469 }
|
nuclear@0
|
1470
|
nuclear@0
|
1471 out_mat->AddProperty(&uvIndex,1,_AI_MATKEY_UVWSRC_BASE,target,0);
|
nuclear@0
|
1472 }
|
nuclear@0
|
1473
|
nuclear@0
|
1474
|
nuclear@0
|
1475 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1476 void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures)
|
nuclear@0
|
1477 {
|
nuclear@0
|
1478 TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE);
|
nuclear@0
|
1479 TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT);
|
nuclear@0
|
1480 TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE);
|
nuclear@0
|
1481 TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR);
|
nuclear@0
|
1482 TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY);
|
nuclear@0
|
1483 TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION);
|
nuclear@0
|
1484 TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT);
|
nuclear@0
|
1485 TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS);
|
nuclear@0
|
1486 TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT);
|
nuclear@0
|
1487 }
|
nuclear@0
|
1488
|
nuclear@0
|
1489
|
nuclear@0
|
1490
|
nuclear@0
|
1491 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1492 aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName,
|
nuclear@0
|
1493 bool& result)
|
nuclear@0
|
1494 {
|
nuclear@0
|
1495 result = true;
|
nuclear@0
|
1496
|
nuclear@0
|
1497 bool ok;
|
nuclear@0
|
1498 const aiVector3D& Diffuse = PropertyGet<aiVector3D>(props,baseName,ok);
|
nuclear@0
|
1499 if(ok) {
|
nuclear@0
|
1500 return aiColor3D(Diffuse.x,Diffuse.y,Diffuse.z);
|
nuclear@0
|
1501 }
|
nuclear@0
|
1502 else {
|
nuclear@0
|
1503 aiVector3D DiffuseColor = PropertyGet<aiVector3D>(props,baseName + "Color",ok);
|
nuclear@0
|
1504 if(ok) {
|
nuclear@0
|
1505 float DiffuseFactor = PropertyGet<float>(props,baseName + "Factor",ok);
|
nuclear@0
|
1506 if(ok) {
|
nuclear@0
|
1507 DiffuseColor *= DiffuseFactor;
|
nuclear@0
|
1508 }
|
nuclear@0
|
1509
|
nuclear@0
|
1510 return aiColor3D(DiffuseColor.x,DiffuseColor.y,DiffuseColor.z);
|
nuclear@0
|
1511 }
|
nuclear@0
|
1512 }
|
nuclear@0
|
1513 result = false;
|
nuclear@0
|
1514 return aiColor3D(0.0f,0.0f,0.0f);
|
nuclear@0
|
1515 }
|
nuclear@0
|
1516
|
nuclear@0
|
1517
|
nuclear@0
|
1518 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1519 void SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props)
|
nuclear@0
|
1520 {
|
nuclear@0
|
1521 // set shading properties. There are various, redundant ways in which FBX materials
|
nuclear@0
|
1522 // specify their shading settings (depending on shading models, prop
|
nuclear@0
|
1523 // template etc.). No idea which one is right in a particular context.
|
nuclear@0
|
1524 // Just try to make sense of it - there's no spec to verify this against,
|
nuclear@0
|
1525 // so why should we.
|
nuclear@0
|
1526 bool ok;
|
nuclear@0
|
1527 const aiColor3D& Diffuse = GetColorPropertyFromMaterial(props,"Diffuse",ok);
|
nuclear@0
|
1528 if(ok) {
|
nuclear@0
|
1529 out_mat->AddProperty(&Diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
|
nuclear@0
|
1530 }
|
nuclear@0
|
1531
|
nuclear@0
|
1532 const aiColor3D& Emissive = GetColorPropertyFromMaterial(props,"Emissive",ok);
|
nuclear@0
|
1533 if(ok) {
|
nuclear@0
|
1534 out_mat->AddProperty(&Emissive,1,AI_MATKEY_COLOR_EMISSIVE);
|
nuclear@0
|
1535 }
|
nuclear@0
|
1536
|
nuclear@0
|
1537 const aiColor3D& Ambient = GetColorPropertyFromMaterial(props,"Ambient",ok);
|
nuclear@0
|
1538 if(ok) {
|
nuclear@0
|
1539 out_mat->AddProperty(&Ambient,1,AI_MATKEY_COLOR_AMBIENT);
|
nuclear@0
|
1540 }
|
nuclear@0
|
1541
|
nuclear@0
|
1542 const aiColor3D& Specular = GetColorPropertyFromMaterial(props,"Specular",ok);
|
nuclear@0
|
1543 if(ok) {
|
nuclear@0
|
1544 out_mat->AddProperty(&Specular,1,AI_MATKEY_COLOR_SPECULAR);
|
nuclear@0
|
1545 }
|
nuclear@0
|
1546
|
nuclear@0
|
1547 const float Opacity = PropertyGet<float>(props,"Opacity",ok);
|
nuclear@0
|
1548 if(ok) {
|
nuclear@0
|
1549 out_mat->AddProperty(&Opacity,1,AI_MATKEY_OPACITY);
|
nuclear@0
|
1550 }
|
nuclear@0
|
1551
|
nuclear@0
|
1552 const float Reflectivity = PropertyGet<float>(props,"Reflectivity",ok);
|
nuclear@0
|
1553 if(ok) {
|
nuclear@0
|
1554 out_mat->AddProperty(&Reflectivity,1,AI_MATKEY_REFLECTIVITY);
|
nuclear@0
|
1555 }
|
nuclear@0
|
1556
|
nuclear@0
|
1557 const float Shininess = PropertyGet<float>(props,"Shininess",ok);
|
nuclear@0
|
1558 if(ok) {
|
nuclear@0
|
1559 out_mat->AddProperty(&Shininess,1,AI_MATKEY_SHININESS_STRENGTH);
|
nuclear@0
|
1560 }
|
nuclear@0
|
1561
|
nuclear@0
|
1562 const float ShininessExponent = PropertyGet<float>(props,"ShininessExponent",ok);
|
nuclear@0
|
1563 if(ok) {
|
nuclear@0
|
1564 out_mat->AddProperty(&ShininessExponent,1,AI_MATKEY_SHININESS);
|
nuclear@0
|
1565 }
|
nuclear@0
|
1566 }
|
nuclear@0
|
1567
|
nuclear@0
|
1568
|
nuclear@0
|
1569 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1570 // get the number of fps for a FrameRate enumerated value
|
nuclear@0
|
1571 static double FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal = -1.0)
|
nuclear@0
|
1572 {
|
nuclear@0
|
1573 switch(fp) {
|
nuclear@0
|
1574 case FileGlobalSettings::FrameRate_DEFAULT:
|
nuclear@0
|
1575 return 1.0;
|
nuclear@0
|
1576
|
nuclear@0
|
1577 case FileGlobalSettings::FrameRate_120:
|
nuclear@0
|
1578 return 120.0;
|
nuclear@0
|
1579
|
nuclear@0
|
1580 case FileGlobalSettings::FrameRate_100:
|
nuclear@0
|
1581 return 100.0;
|
nuclear@0
|
1582
|
nuclear@0
|
1583 case FileGlobalSettings::FrameRate_60:
|
nuclear@0
|
1584 return 60.0;
|
nuclear@0
|
1585
|
nuclear@0
|
1586 case FileGlobalSettings::FrameRate_50:
|
nuclear@0
|
1587 return 50.0;
|
nuclear@0
|
1588
|
nuclear@0
|
1589 case FileGlobalSettings::FrameRate_48:
|
nuclear@0
|
1590 return 48.0;
|
nuclear@0
|
1591
|
nuclear@0
|
1592 case FileGlobalSettings::FrameRate_30:
|
nuclear@0
|
1593 case FileGlobalSettings::FrameRate_30_DROP:
|
nuclear@0
|
1594 return 30.0;
|
nuclear@0
|
1595
|
nuclear@0
|
1596 case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME:
|
nuclear@0
|
1597 case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME:
|
nuclear@0
|
1598 return 29.9700262;
|
nuclear@0
|
1599
|
nuclear@0
|
1600 case FileGlobalSettings::FrameRate_PAL:
|
nuclear@0
|
1601 return 25.0;
|
nuclear@0
|
1602
|
nuclear@0
|
1603 case FileGlobalSettings::FrameRate_CINEMA:
|
nuclear@0
|
1604 return 24.0;
|
nuclear@0
|
1605
|
nuclear@0
|
1606 case FileGlobalSettings::FrameRate_1000:
|
nuclear@0
|
1607 return 1000.0;
|
nuclear@0
|
1608
|
nuclear@0
|
1609 case FileGlobalSettings::FrameRate_CINEMA_ND:
|
nuclear@0
|
1610 return 23.976;
|
nuclear@0
|
1611
|
nuclear@0
|
1612 case FileGlobalSettings::FrameRate_CUSTOM:
|
nuclear@0
|
1613 return customFPSVal;
|
nuclear@0
|
1614
|
nuclear@0
|
1615 case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings
|
nuclear@0
|
1616 break;
|
nuclear@0
|
1617 }
|
nuclear@0
|
1618
|
nuclear@0
|
1619 ai_assert(false);
|
nuclear@0
|
1620 return -1.0f;
|
nuclear@0
|
1621 }
|
nuclear@0
|
1622
|
nuclear@0
|
1623
|
nuclear@0
|
1624 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1625 // convert animation data to aiAnimation et al
|
nuclear@0
|
1626 void ConvertAnimations()
|
nuclear@0
|
1627 {
|
nuclear@0
|
1628 // first of all determine framerate
|
nuclear@0
|
1629 const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode();
|
nuclear@0
|
1630 const float custom = doc.GlobalSettings().CustomFrameRate();
|
nuclear@0
|
1631 anim_fps = FrameRateToDouble(fps, custom);
|
nuclear@0
|
1632
|
nuclear@0
|
1633 const std::vector<const AnimationStack*>& animations = doc.AnimationStacks();
|
nuclear@0
|
1634 BOOST_FOREACH(const AnimationStack* stack, animations) {
|
nuclear@0
|
1635 ConvertAnimationStack(*stack);
|
nuclear@0
|
1636 }
|
nuclear@0
|
1637 }
|
nuclear@0
|
1638
|
nuclear@0
|
1639
|
nuclear@0
|
1640 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1641 // rename a node already partially converted. fixed_name is a string previously returned by
|
nuclear@0
|
1642 // FixNodeName, new_name specifies the string FixNodeName should return on all further invocations
|
nuclear@0
|
1643 // which would previously have returned the old value.
|
nuclear@0
|
1644 //
|
nuclear@0
|
1645 // this also updates names in node animations, cameras and light sources and is thus slow.
|
nuclear@0
|
1646 //
|
nuclear@0
|
1647 // NOTE: the caller is responsible for ensuring that the new name is unique and does
|
nuclear@0
|
1648 // not collide with any other identifiers. The best way to ensure this is to only
|
nuclear@0
|
1649 // append to the old name, which is guaranteed to match these requirements.
|
nuclear@0
|
1650 void RenameNode(const std::string& fixed_name, const std::string& new_name)
|
nuclear@0
|
1651 {
|
nuclear@0
|
1652 ai_assert(node_names.find(fixed_name) != node_names.end());
|
nuclear@0
|
1653 ai_assert(node_names.find(new_name) == node_names.end());
|
nuclear@0
|
1654
|
nuclear@0
|
1655 renamed_nodes[fixed_name] = new_name;
|
nuclear@0
|
1656
|
nuclear@0
|
1657 const aiString fn(fixed_name);
|
nuclear@0
|
1658
|
nuclear@0
|
1659 BOOST_FOREACH(aiCamera* cam, cameras) {
|
nuclear@0
|
1660 if (cam->mName == fn) {
|
nuclear@0
|
1661 cam->mName.Set(new_name);
|
nuclear@0
|
1662 break;
|
nuclear@0
|
1663 }
|
nuclear@0
|
1664 }
|
nuclear@0
|
1665
|
nuclear@0
|
1666 BOOST_FOREACH(aiLight* light, lights) {
|
nuclear@0
|
1667 if (light->mName == fn) {
|
nuclear@0
|
1668 light->mName.Set(new_name);
|
nuclear@0
|
1669 break;
|
nuclear@0
|
1670 }
|
nuclear@0
|
1671 }
|
nuclear@0
|
1672
|
nuclear@0
|
1673 BOOST_FOREACH(aiAnimation* anim, animations) {
|
nuclear@0
|
1674 for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
|
nuclear@0
|
1675 aiNodeAnim* const na = anim->mChannels[i];
|
nuclear@0
|
1676 if (na->mNodeName == fn) {
|
nuclear@0
|
1677 na->mNodeName.Set(new_name);
|
nuclear@0
|
1678 break;
|
nuclear@0
|
1679 }
|
nuclear@0
|
1680 }
|
nuclear@0
|
1681 }
|
nuclear@0
|
1682 }
|
nuclear@0
|
1683
|
nuclear@0
|
1684
|
nuclear@0
|
1685 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1686 // takes a fbx node name and returns the identifier to be used in the assimp output scene.
|
nuclear@0
|
1687 // the function is guaranteed to provide consistent results over multiple invocations
|
nuclear@0
|
1688 // UNLESS RenameNode() is called for a particular node name.
|
nuclear@0
|
1689 std::string FixNodeName(const std::string& name)
|
nuclear@0
|
1690 {
|
nuclear@0
|
1691 // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if
|
nuclear@0
|
1692 // this causes ambiguities, well possible between empty identifiers,
|
nuclear@0
|
1693 // such as "Model::" and ""). Make sure the behaviour is consistent
|
nuclear@0
|
1694 // across multiple calls to FixNodeName().
|
nuclear@0
|
1695 if(name.substr(0,7) == "Model::") {
|
nuclear@0
|
1696 std::string temp = name.substr(7);
|
nuclear@0
|
1697
|
nuclear@0
|
1698 const NodeNameMap::const_iterator it = node_names.find(temp);
|
nuclear@0
|
1699 if (it != node_names.end()) {
|
nuclear@0
|
1700 if (!(*it).second) {
|
nuclear@0
|
1701 return FixNodeName(name + "_");
|
nuclear@0
|
1702 }
|
nuclear@0
|
1703 }
|
nuclear@0
|
1704 node_names[temp] = true;
|
nuclear@0
|
1705
|
nuclear@0
|
1706 const NameNameMap::const_iterator rit = renamed_nodes.find(temp);
|
nuclear@0
|
1707 return rit == renamed_nodes.end() ? temp : (*rit).second;
|
nuclear@0
|
1708 }
|
nuclear@0
|
1709
|
nuclear@0
|
1710 const NodeNameMap::const_iterator it = node_names.find(name);
|
nuclear@0
|
1711 if (it != node_names.end()) {
|
nuclear@0
|
1712 if ((*it).second) {
|
nuclear@0
|
1713 return FixNodeName(name + "_");
|
nuclear@0
|
1714 }
|
nuclear@0
|
1715 }
|
nuclear@0
|
1716 node_names[name] = false;
|
nuclear@0
|
1717
|
nuclear@0
|
1718 const NameNameMap::const_iterator rit = renamed_nodes.find(name);
|
nuclear@0
|
1719 return rit == renamed_nodes.end() ? name : (*rit).second;
|
nuclear@0
|
1720 }
|
nuclear@0
|
1721
|
nuclear@0
|
1722
|
nuclear@0
|
1723 typedef std::map<const AnimationCurveNode*, const AnimationLayer*> LayerMap;
|
nuclear@0
|
1724
|
nuclear@0
|
1725 // XXX: better use multi_map ..
|
nuclear@0
|
1726 typedef std::map<std::string, std::vector<const AnimationCurveNode*> > NodeMap;
|
nuclear@0
|
1727
|
nuclear@0
|
1728
|
nuclear@0
|
1729 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1730 void ConvertAnimationStack(const AnimationStack& st)
|
nuclear@0
|
1731 {
|
nuclear@0
|
1732 const AnimationLayerList& layers = st.Layers();
|
nuclear@0
|
1733 if(layers.empty()) {
|
nuclear@0
|
1734 return;
|
nuclear@0
|
1735 }
|
nuclear@0
|
1736
|
nuclear@0
|
1737 aiAnimation* const anim = new aiAnimation();
|
nuclear@0
|
1738 animations.push_back(anim);
|
nuclear@0
|
1739
|
nuclear@0
|
1740 // strip AnimationStack:: prefix
|
nuclear@0
|
1741 std::string name = st.Name();
|
nuclear@0
|
1742 if(name.substr(0,16) == "AnimationStack::") {
|
nuclear@0
|
1743 name = name.substr(16);
|
nuclear@0
|
1744 }
|
nuclear@0
|
1745
|
nuclear@0
|
1746 anim->mName.Set(name);
|
nuclear@0
|
1747
|
nuclear@0
|
1748 // need to find all nodes for which we need to generate node animations -
|
nuclear@0
|
1749 // it may happen that we need to merge multiple layers, though.
|
nuclear@0
|
1750 NodeMap node_map;
|
nuclear@0
|
1751
|
nuclear@0
|
1752 // reverse mapping from curves to layers, much faster than querying
|
nuclear@0
|
1753 // the FBX DOM for it.
|
nuclear@0
|
1754 LayerMap layer_map;
|
nuclear@0
|
1755
|
nuclear@0
|
1756 const char* prop_whitelist[] = {
|
nuclear@0
|
1757 "Lcl Scaling",
|
nuclear@0
|
1758 "Lcl Rotation",
|
nuclear@0
|
1759 "Lcl Translation"
|
nuclear@0
|
1760 };
|
nuclear@0
|
1761
|
nuclear@0
|
1762 BOOST_FOREACH(const AnimationLayer* layer, layers) {
|
nuclear@0
|
1763 ai_assert(layer);
|
nuclear@0
|
1764
|
nuclear@0
|
1765 const AnimationCurveNodeList& nodes = layer->Nodes(prop_whitelist, 3);
|
nuclear@0
|
1766 BOOST_FOREACH(const AnimationCurveNode* node, nodes) {
|
nuclear@0
|
1767 ai_assert(node);
|
nuclear@0
|
1768
|
nuclear@0
|
1769 const Model* const model = dynamic_cast<const Model*>(node->Target());
|
nuclear@0
|
1770 // this can happen - it could also be a NodeAttribute (i.e. for camera animations)
|
nuclear@0
|
1771 if(!model) {
|
nuclear@0
|
1772 continue;
|
nuclear@0
|
1773 }
|
nuclear@0
|
1774
|
nuclear@0
|
1775 const std::string& name = FixNodeName(model->Name());
|
nuclear@0
|
1776 node_map[name].push_back(node);
|
nuclear@0
|
1777
|
nuclear@0
|
1778 layer_map[node] = layer;
|
nuclear@0
|
1779 }
|
nuclear@0
|
1780 }
|
nuclear@0
|
1781
|
nuclear@0
|
1782 // generate node animations
|
nuclear@0
|
1783 std::vector<aiNodeAnim*> node_anims;
|
nuclear@0
|
1784
|
nuclear@0
|
1785 double min_time = 1e10;
|
nuclear@0
|
1786 double max_time = -1e10;
|
nuclear@0
|
1787
|
nuclear@0
|
1788 try {
|
nuclear@0
|
1789 BOOST_FOREACH(const NodeMap::value_type& kv, node_map) {
|
nuclear@0
|
1790 GenerateNodeAnimations(node_anims,
|
nuclear@0
|
1791 kv.first,
|
nuclear@0
|
1792 kv.second,
|
nuclear@0
|
1793 layer_map,
|
nuclear@0
|
1794 max_time,
|
nuclear@0
|
1795 min_time);
|
nuclear@0
|
1796 }
|
nuclear@0
|
1797 }
|
nuclear@0
|
1798 catch(std::exception&) {
|
nuclear@0
|
1799 std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun<aiNodeAnim>());
|
nuclear@0
|
1800 throw;
|
nuclear@0
|
1801 }
|
nuclear@0
|
1802
|
nuclear@0
|
1803 if(node_anims.size()) {
|
nuclear@0
|
1804 anim->mChannels = new aiNodeAnim*[node_anims.size()]();
|
nuclear@0
|
1805 anim->mNumChannels = static_cast<unsigned int>(node_anims.size());
|
nuclear@0
|
1806
|
nuclear@0
|
1807 std::swap_ranges(node_anims.begin(),node_anims.end(),anim->mChannels);
|
nuclear@0
|
1808 }
|
nuclear@0
|
1809 else {
|
nuclear@0
|
1810 // empty animations would fail validation, so drop them
|
nuclear@0
|
1811 delete anim;
|
nuclear@0
|
1812 animations.pop_back();
|
nuclear@0
|
1813 FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name);
|
nuclear@0
|
1814 return;
|
nuclear@0
|
1815 }
|
nuclear@0
|
1816
|
nuclear@0
|
1817 // for some mysterious reason, mDuration is simply the maximum key -- the
|
nuclear@0
|
1818 // validator always assumes animations to start at zero.
|
nuclear@0
|
1819 anim->mDuration = max_time /*- min_time */;
|
nuclear@0
|
1820 anim->mTicksPerSecond = anim_fps;
|
nuclear@0
|
1821 }
|
nuclear@0
|
1822
|
nuclear@0
|
1823
|
nuclear@0
|
1824 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
1825 void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims,
|
nuclear@0
|
1826 const std::string& fixed_name,
|
nuclear@0
|
1827 const std::vector<const AnimationCurveNode*>& curves,
|
nuclear@0
|
1828 const LayerMap& layer_map,
|
nuclear@0
|
1829 double& max_time,
|
nuclear@0
|
1830 double& min_time)
|
nuclear@0
|
1831 {
|
nuclear@0
|
1832
|
nuclear@0
|
1833 NodeMap node_property_map;
|
nuclear@0
|
1834 ai_assert(curves.size());
|
nuclear@0
|
1835
|
nuclear@0
|
1836 // sanity check whether the input is ok
|
nuclear@0
|
1837 #ifdef _DEBUG
|
nuclear@0
|
1838 { const Object* target = NULL;
|
nuclear@0
|
1839 BOOST_FOREACH(const AnimationCurveNode* node, curves) {
|
nuclear@0
|
1840 if(!target) {
|
nuclear@0
|
1841 target = node->Target();
|
nuclear@0
|
1842 }
|
nuclear@0
|
1843 ai_assert(node->Target() == target);
|
nuclear@0
|
1844 }}
|
nuclear@0
|
1845 #endif
|
nuclear@0
|
1846
|
nuclear@0
|
1847 const AnimationCurveNode* curve_node;
|
nuclear@0
|
1848 BOOST_FOREACH(const AnimationCurveNode* node, curves) {
|
nuclear@0
|
1849 ai_assert(node);
|
nuclear@0
|
1850
|
nuclear@0
|
1851 if (node->TargetProperty().empty()) {
|
nuclear@0
|
1852 FBXImporter::LogWarn("target property for animation curve not set: " + node->Name());
|
nuclear@0
|
1853 continue;
|
nuclear@0
|
1854 }
|
nuclear@0
|
1855
|
nuclear@0
|
1856 curve_node = node;
|
nuclear@0
|
1857 if (node->Curves().empty()) {
|
nuclear@0
|
1858 FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name());
|
nuclear@0
|
1859 continue;
|
nuclear@0
|
1860 }
|
nuclear@0
|
1861
|
nuclear@0
|
1862 node_property_map[node->TargetProperty()].push_back(node);
|
nuclear@0
|
1863 }
|
nuclear@0
|
1864
|
nuclear@0
|
1865 ai_assert(curve_node);
|
nuclear@0
|
1866 ai_assert(curve_node->TargetAsModel());
|
nuclear@0
|
1867
|
nuclear@0
|
1868 const Model& target = *curve_node->TargetAsModel();
|
nuclear@0
|
1869
|
nuclear@0
|
1870 // check for all possible transformation components
|
nuclear@0
|
1871 NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
|
nuclear@0
|
1872
|
nuclear@0
|
1873 bool has_any = false;
|
nuclear@0
|
1874 bool has_complex = false;
|
nuclear@0
|
1875
|
nuclear@0
|
1876 for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
nuclear@0
|
1877 const TransformationComp comp = static_cast<TransformationComp>(i);
|
nuclear@0
|
1878
|
nuclear@0
|
1879 // inverse pivots don't exist in the input, we just generate them
|
nuclear@0
|
1880 if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) {
|
nuclear@0
|
1881 chain[i] = node_property_map.end();
|
nuclear@0
|
1882 continue;
|
nuclear@0
|
1883 }
|
nuclear@0
|
1884
|
nuclear@0
|
1885 chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
|
nuclear@0
|
1886 if (chain[i] != node_property_map.end()) {
|
nuclear@0
|
1887
|
nuclear@0
|
1888 // check if this curves contains redundant information by looking
|
nuclear@0
|
1889 // up the corresponding node's transformation chain.
|
nuclear@0
|
1890 if (doc.Settings().optimizeEmptyAnimationCurves &&
|
nuclear@0
|
1891 IsRedundantAnimationData(target, comp, (*chain[i]).second)) {
|
nuclear@0
|
1892
|
nuclear@0
|
1893 FBXImporter::LogDebug("dropping redundant animation channel for node " + target.Name());
|
nuclear@0
|
1894 continue;
|
nuclear@0
|
1895 }
|
nuclear@0
|
1896
|
nuclear@0
|
1897 has_any = true;
|
nuclear@0
|
1898
|
nuclear@0
|
1899 if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling &&
|
nuclear@0
|
1900 comp != TransformationComp_Translation) {
|
nuclear@0
|
1901
|
nuclear@0
|
1902 has_complex = true;
|
nuclear@0
|
1903 }
|
nuclear@0
|
1904 }
|
nuclear@0
|
1905 }
|
nuclear@0
|
1906
|
nuclear@0
|
1907 if (!has_any) {
|
nuclear@0
|
1908 FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
|
nuclear@0
|
1909 return;
|
nuclear@0
|
1910 }
|
nuclear@0
|
1911
|
nuclear@0
|
1912 // this needs to play nicely with GenerateTransformationNodeChain() which will
|
nuclear@0
|
1913 // be invoked _later_ (animations come first). If this node has only rotation,
|
nuclear@0
|
1914 // scaling and translation _and_ there are no animated other components either,
|
nuclear@0
|
1915 // we can use a single node and also a single node animation channel.
|
nuclear@0
|
1916 if (!has_complex && !NeedsComplexTransformationChain(target)) {
|
nuclear@0
|
1917
|
nuclear@0
|
1918 aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain,
|
nuclear@0
|
1919 node_property_map.end(),
|
nuclear@0
|
1920 layer_map,
|
nuclear@0
|
1921 max_time,
|
nuclear@0
|
1922 min_time,
|
nuclear@0
|
1923 true // input is TRS order, assimp is SRT
|
nuclear@0
|
1924 );
|
nuclear@0
|
1925
|
nuclear@0
|
1926 ai_assert(nd);
|
nuclear@0
|
1927 node_anims.push_back(nd);
|
nuclear@0
|
1928 return;
|
nuclear@0
|
1929 }
|
nuclear@0
|
1930
|
nuclear@0
|
1931 // otherwise, things get gruesome and we need separate animation channels
|
nuclear@0
|
1932 // for each part of the transformation chain. Remember which channels
|
nuclear@0
|
1933 // we generated and pass this information to the node conversion
|
nuclear@0
|
1934 // code to avoid nodes that have identity transform, but non-identity
|
nuclear@0
|
1935 // animations, being dropped.
|
nuclear@0
|
1936 unsigned int flags = 0, bit = 0x1;
|
nuclear@0
|
1937 for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
|
nuclear@0
|
1938 const TransformationComp comp = static_cast<TransformationComp>(i);
|
nuclear@0
|
1939
|
nuclear@0
|
1940 if (chain[i] != node_property_map.end()) {
|
nuclear@0
|
1941 flags |= bit;
|
nuclear@0
|
1942
|
nuclear@0
|
1943 ai_assert(comp != TransformationComp_RotationPivotInverse);
|
nuclear@0
|
1944 ai_assert(comp != TransformationComp_ScalingPivotInverse);
|
nuclear@0
|
1945
|
nuclear@0
|
1946 const std::string& chain_name = NameTransformationChainNode(fixed_name, comp);
|
nuclear@0
|
1947
|
nuclear@0
|
1948 aiNodeAnim* na;
|
nuclear@0
|
1949 switch(comp)
|
nuclear@0
|
1950 {
|
nuclear@0
|
1951 case TransformationComp_Rotation:
|
nuclear@0
|
1952 case TransformationComp_PreRotation:
|
nuclear@0
|
1953 case TransformationComp_PostRotation:
|
nuclear@0
|
1954 na = GenerateRotationNodeAnim(chain_name,
|
nuclear@0
|
1955 target,
|
nuclear@0
|
1956 (*chain[i]).second,
|
nuclear@0
|
1957 layer_map,
|
nuclear@0
|
1958 max_time,
|
nuclear@0
|
1959 min_time);
|
nuclear@0
|
1960
|
nuclear@0
|
1961 break;
|
nuclear@0
|
1962
|
nuclear@0
|
1963 case TransformationComp_RotationOffset:
|
nuclear@0
|
1964 case TransformationComp_RotationPivot:
|
nuclear@0
|
1965 case TransformationComp_ScalingOffset:
|
nuclear@0
|
1966 case TransformationComp_ScalingPivot:
|
nuclear@0
|
1967 case TransformationComp_Translation:
|
nuclear@0
|
1968 na = GenerateTranslationNodeAnim(chain_name,
|
nuclear@0
|
1969 target,
|
nuclear@0
|
1970 (*chain[i]).second,
|
nuclear@0
|
1971 layer_map,
|
nuclear@0
|
1972 max_time,
|
nuclear@0
|
1973 min_time);
|
nuclear@0
|
1974
|
nuclear@0
|
1975 // pivoting requires us to generate an implicit inverse channel to undo the pivot translation
|
nuclear@0
|
1976 if (comp == TransformationComp_RotationPivot) {
|
nuclear@0
|
1977 const std::string& invName = NameTransformationChainNode(fixed_name,
|
nuclear@0
|
1978 TransformationComp_RotationPivotInverse);
|
nuclear@0
|
1979
|
nuclear@0
|
1980 aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName,
|
nuclear@0
|
1981 target,
|
nuclear@0
|
1982 (*chain[i]).second,
|
nuclear@0
|
1983 layer_map,
|
nuclear@0
|
1984 max_time,
|
nuclear@0
|
1985 min_time,
|
nuclear@0
|
1986 true);
|
nuclear@0
|
1987
|
nuclear@0
|
1988 ai_assert(inv);
|
nuclear@0
|
1989 node_anims.push_back(inv);
|
nuclear@0
|
1990
|
nuclear@0
|
1991 ai_assert(TransformationComp_RotationPivotInverse > i);
|
nuclear@0
|
1992 flags |= bit << (TransformationComp_RotationPivotInverse - i);
|
nuclear@0
|
1993 }
|
nuclear@0
|
1994 else if (comp == TransformationComp_ScalingPivot) {
|
nuclear@0
|
1995 const std::string& invName = NameTransformationChainNode(fixed_name,
|
nuclear@0
|
1996 TransformationComp_ScalingPivotInverse);
|
nuclear@0
|
1997
|
nuclear@0
|
1998 aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName,
|
nuclear@0
|
1999 target,
|
nuclear@0
|
2000 (*chain[i]).second,
|
nuclear@0
|
2001 layer_map,
|
nuclear@0
|
2002 max_time,
|
nuclear@0
|
2003 min_time,
|
nuclear@0
|
2004 true);
|
nuclear@0
|
2005
|
nuclear@0
|
2006 ai_assert(inv);
|
nuclear@0
|
2007 node_anims.push_back(inv);
|
nuclear@0
|
2008
|
nuclear@0
|
2009 ai_assert(TransformationComp_RotationPivotInverse > i);
|
nuclear@0
|
2010 flags |= bit << (TransformationComp_RotationPivotInverse - i);
|
nuclear@0
|
2011 }
|
nuclear@0
|
2012
|
nuclear@0
|
2013 break;
|
nuclear@0
|
2014
|
nuclear@0
|
2015 case TransformationComp_Scaling:
|
nuclear@0
|
2016 na = GenerateScalingNodeAnim(chain_name,
|
nuclear@0
|
2017 target,
|
nuclear@0
|
2018 (*chain[i]).second,
|
nuclear@0
|
2019 layer_map,
|
nuclear@0
|
2020 max_time,
|
nuclear@0
|
2021 min_time);
|
nuclear@0
|
2022
|
nuclear@0
|
2023 break;
|
nuclear@0
|
2024
|
nuclear@0
|
2025 default:
|
nuclear@0
|
2026 ai_assert(false);
|
nuclear@0
|
2027 }
|
nuclear@0
|
2028
|
nuclear@0
|
2029 ai_assert(na);
|
nuclear@0
|
2030 node_anims.push_back(na);
|
nuclear@0
|
2031 continue;
|
nuclear@0
|
2032 }
|
nuclear@0
|
2033 }
|
nuclear@0
|
2034
|
nuclear@0
|
2035 node_anim_chain_bits[fixed_name] = flags;
|
nuclear@0
|
2036 }
|
nuclear@0
|
2037
|
nuclear@0
|
2038
|
nuclear@0
|
2039 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2040 bool IsRedundantAnimationData(const Model& target,
|
nuclear@0
|
2041 TransformationComp comp,
|
nuclear@0
|
2042 const std::vector<const AnimationCurveNode*>& curves)
|
nuclear@0
|
2043 {
|
nuclear@0
|
2044 ai_assert(curves.size());
|
nuclear@0
|
2045
|
nuclear@0
|
2046 // look for animation nodes with
|
nuclear@0
|
2047 // * sub channels for all relevant components set
|
nuclear@0
|
2048 // * one key/value pair per component
|
nuclear@0
|
2049 // * combined values match up the corresponding value in the bind pose node transformation
|
nuclear@0
|
2050 // only such nodes are 'redundant' for this function.
|
nuclear@0
|
2051
|
nuclear@0
|
2052 if (curves.size() > 1) {
|
nuclear@0
|
2053 return false;
|
nuclear@0
|
2054 }
|
nuclear@0
|
2055
|
nuclear@0
|
2056 const AnimationCurveNode& nd = *curves.front();
|
nuclear@0
|
2057 const AnimationCurveMap& sub_curves = nd.Curves();
|
nuclear@0
|
2058
|
nuclear@0
|
2059 const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X");
|
nuclear@0
|
2060 const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y");
|
nuclear@0
|
2061 const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z");
|
nuclear@0
|
2062
|
nuclear@0
|
2063 if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) {
|
nuclear@0
|
2064 return false;
|
nuclear@0
|
2065 }
|
nuclear@0
|
2066
|
nuclear@0
|
2067 const KeyValueList& vx = (*dx).second->GetValues();
|
nuclear@0
|
2068 const KeyValueList& vy = (*dy).second->GetValues();
|
nuclear@0
|
2069 const KeyValueList& vz = (*dz).second->GetValues();
|
nuclear@0
|
2070
|
nuclear@0
|
2071 if(vx.size() != 1 || vy.size() != 1 || vz.size() != 1) {
|
nuclear@0
|
2072 return false;
|
nuclear@0
|
2073 }
|
nuclear@0
|
2074
|
nuclear@0
|
2075 const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]);
|
nuclear@0
|
2076 const aiVector3D& static_val = PropertyGet<aiVector3D>(target.Props(),
|
nuclear@0
|
2077 NameTransformationCompProperty(comp),
|
nuclear@0
|
2078 TransformationCompDefaultValue(comp)
|
nuclear@0
|
2079 );
|
nuclear@0
|
2080
|
nuclear@0
|
2081 const float epsilon = 1e-6f;
|
nuclear@0
|
2082 return (dyn_val - static_val).SquareLength() < epsilon;
|
nuclear@0
|
2083 }
|
nuclear@0
|
2084
|
nuclear@0
|
2085
|
nuclear@0
|
2086 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2087 aiNodeAnim* GenerateRotationNodeAnim(const std::string& name,
|
nuclear@0
|
2088 const Model& target,
|
nuclear@0
|
2089 const std::vector<const AnimationCurveNode*>& curves,
|
nuclear@0
|
2090 const LayerMap& layer_map,
|
nuclear@0
|
2091 double& max_time,
|
nuclear@0
|
2092 double& min_time)
|
nuclear@0
|
2093 {
|
nuclear@0
|
2094 ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
|
nuclear@0
|
2095 na->mNodeName.Set(name);
|
nuclear@0
|
2096
|
nuclear@0
|
2097 ConvertRotationKeys(na, curves, layer_map, max_time,min_time, target.RotationOrder());
|
nuclear@0
|
2098
|
nuclear@0
|
2099 // dummy scaling key
|
nuclear@0
|
2100 na->mScalingKeys = new aiVectorKey[1];
|
nuclear@0
|
2101 na->mNumScalingKeys = 1;
|
nuclear@0
|
2102
|
nuclear@0
|
2103 na->mScalingKeys[0].mTime = 0.;
|
nuclear@0
|
2104 na->mScalingKeys[0].mValue = aiVector3D(1.0f,1.0f,1.0f);
|
nuclear@0
|
2105
|
nuclear@0
|
2106 // dummy position key
|
nuclear@0
|
2107 na->mPositionKeys = new aiVectorKey[1];
|
nuclear@0
|
2108 na->mNumPositionKeys = 1;
|
nuclear@0
|
2109
|
nuclear@0
|
2110 na->mPositionKeys[0].mTime = 0.;
|
nuclear@0
|
2111 na->mPositionKeys[0].mValue = aiVector3D();
|
nuclear@0
|
2112
|
nuclear@0
|
2113 return na.dismiss();
|
nuclear@0
|
2114 }
|
nuclear@0
|
2115
|
nuclear@0
|
2116
|
nuclear@0
|
2117 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2118 aiNodeAnim* GenerateScalingNodeAnim(const std::string& name,
|
nuclear@0
|
2119 const Model& target,
|
nuclear@0
|
2120 const std::vector<const AnimationCurveNode*>& curves,
|
nuclear@0
|
2121 const LayerMap& layer_map,
|
nuclear@0
|
2122 double& max_time,
|
nuclear@0
|
2123 double& min_time)
|
nuclear@0
|
2124 {
|
nuclear@0
|
2125 ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
|
nuclear@0
|
2126 na->mNodeName.Set(name);
|
nuclear@0
|
2127
|
nuclear@0
|
2128 ConvertScaleKeys(na, curves, layer_map, max_time,min_time);
|
nuclear@0
|
2129
|
nuclear@0
|
2130 // dummy rotation key
|
nuclear@0
|
2131 na->mRotationKeys = new aiQuatKey[1];
|
nuclear@0
|
2132 na->mNumRotationKeys = 1;
|
nuclear@0
|
2133
|
nuclear@0
|
2134 na->mRotationKeys[0].mTime = 0.;
|
nuclear@0
|
2135 na->mRotationKeys[0].mValue = aiQuaternion();
|
nuclear@0
|
2136
|
nuclear@0
|
2137 // dummy position key
|
nuclear@0
|
2138 na->mPositionKeys = new aiVectorKey[1];
|
nuclear@0
|
2139 na->mNumPositionKeys = 1;
|
nuclear@0
|
2140
|
nuclear@0
|
2141 na->mPositionKeys[0].mTime = 0.;
|
nuclear@0
|
2142 na->mPositionKeys[0].mValue = aiVector3D();
|
nuclear@0
|
2143
|
nuclear@0
|
2144 return na.dismiss();
|
nuclear@0
|
2145 }
|
nuclear@0
|
2146
|
nuclear@0
|
2147
|
nuclear@0
|
2148 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2149 aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name,
|
nuclear@0
|
2150 const Model& target,
|
nuclear@0
|
2151 const std::vector<const AnimationCurveNode*>& curves,
|
nuclear@0
|
2152 const LayerMap& layer_map,
|
nuclear@0
|
2153 double& max_time,
|
nuclear@0
|
2154 double& min_time,
|
nuclear@0
|
2155 bool inverse = false)
|
nuclear@0
|
2156 {
|
nuclear@0
|
2157 ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
|
nuclear@0
|
2158 na->mNodeName.Set(name);
|
nuclear@0
|
2159
|
nuclear@0
|
2160 ConvertTranslationKeys(na, curves, layer_map, max_time,min_time);
|
nuclear@0
|
2161
|
nuclear@0
|
2162 if (inverse) {
|
nuclear@0
|
2163 for (unsigned int i = 0; i < na->mNumPositionKeys; ++i) {
|
nuclear@0
|
2164 na->mPositionKeys[i].mValue *= -1.0f;
|
nuclear@0
|
2165 }
|
nuclear@0
|
2166 }
|
nuclear@0
|
2167
|
nuclear@0
|
2168 // dummy scaling key
|
nuclear@0
|
2169 na->mScalingKeys = new aiVectorKey[1];
|
nuclear@0
|
2170 na->mNumScalingKeys = 1;
|
nuclear@0
|
2171
|
nuclear@0
|
2172 na->mScalingKeys[0].mTime = 0.;
|
nuclear@0
|
2173 na->mScalingKeys[0].mValue = aiVector3D(1.0f,1.0f,1.0f);
|
nuclear@0
|
2174
|
nuclear@0
|
2175 // dummy rotation key
|
nuclear@0
|
2176 na->mRotationKeys = new aiQuatKey[1];
|
nuclear@0
|
2177 na->mNumRotationKeys = 1;
|
nuclear@0
|
2178
|
nuclear@0
|
2179 na->mRotationKeys[0].mTime = 0.;
|
nuclear@0
|
2180 na->mRotationKeys[0].mValue = aiQuaternion();
|
nuclear@0
|
2181
|
nuclear@0
|
2182 return na.dismiss();
|
nuclear@0
|
2183 }
|
nuclear@0
|
2184
|
nuclear@0
|
2185
|
nuclear@0
|
2186 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2187 // generate node anim, extracting only Rotation, Scaling and Translation from the given chain
|
nuclear@0
|
2188 aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name,
|
nuclear@0
|
2189 const Model& target,
|
nuclear@0
|
2190 NodeMap::const_iterator chain[TransformationComp_MAXIMUM],
|
nuclear@0
|
2191 NodeMap::const_iterator iter_end,
|
nuclear@0
|
2192 const LayerMap& layer_map,
|
nuclear@0
|
2193 double& max_time,
|
nuclear@0
|
2194 double& min_time,
|
nuclear@0
|
2195 bool reverse_order = false)
|
nuclear@0
|
2196
|
nuclear@0
|
2197 {
|
nuclear@0
|
2198 ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
|
nuclear@0
|
2199 na->mNodeName.Set(name);
|
nuclear@0
|
2200
|
nuclear@0
|
2201 const PropertyTable& props = target.Props();
|
nuclear@0
|
2202
|
nuclear@0
|
2203 // need to convert from TRS order to SRT?
|
nuclear@0
|
2204 if(reverse_order) {
|
nuclear@0
|
2205
|
nuclear@0
|
2206 aiVector3D def_scale, def_translate;
|
nuclear@0
|
2207 aiQuaternion def_rot;
|
nuclear@0
|
2208
|
nuclear@0
|
2209 KeyFrameListList scaling;
|
nuclear@0
|
2210 KeyFrameListList translation;
|
nuclear@0
|
2211 KeyFrameListList rotation;
|
nuclear@0
|
2212
|
nuclear@0
|
2213 if(chain[TransformationComp_Scaling] != iter_end) {
|
nuclear@0
|
2214 scaling = GetKeyframeList((*chain[TransformationComp_Scaling]).second);
|
nuclear@0
|
2215 }
|
nuclear@0
|
2216 else {
|
nuclear@0
|
2217 def_scale = PropertyGet(props,"Lcl Scaling",aiVector3D(1.f,1.f,1.f));
|
nuclear@0
|
2218 }
|
nuclear@0
|
2219
|
nuclear@0
|
2220 if(chain[TransformationComp_Translation] != iter_end) {
|
nuclear@0
|
2221 translation = GetKeyframeList((*chain[TransformationComp_Translation]).second);
|
nuclear@0
|
2222 }
|
nuclear@0
|
2223 else {
|
nuclear@0
|
2224 def_translate = PropertyGet(props,"Lcl Translation",aiVector3D(0.f,0.f,0.f));
|
nuclear@0
|
2225 }
|
nuclear@0
|
2226
|
nuclear@0
|
2227 if(chain[TransformationComp_Rotation] != iter_end) {
|
nuclear@0
|
2228 rotation = GetKeyframeList((*chain[TransformationComp_Rotation]).second);
|
nuclear@0
|
2229 }
|
nuclear@0
|
2230 else {
|
nuclear@0
|
2231 def_rot = EulerToQuaternion(PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f)),
|
nuclear@0
|
2232 target.RotationOrder());
|
nuclear@0
|
2233 }
|
nuclear@0
|
2234
|
nuclear@0
|
2235 KeyFrameListList joined;
|
nuclear@0
|
2236 joined.insert(joined.end(), scaling.begin(), scaling.end());
|
nuclear@0
|
2237 joined.insert(joined.end(), translation.begin(), translation.end());
|
nuclear@0
|
2238 joined.insert(joined.end(), rotation.begin(), rotation.end());
|
nuclear@0
|
2239
|
nuclear@0
|
2240 const KeyTimeList& times = GetKeyTimeList(joined);
|
nuclear@0
|
2241
|
nuclear@0
|
2242 aiQuatKey* out_quat = new aiQuatKey[times.size()];
|
nuclear@0
|
2243 aiVectorKey* out_scale = new aiVectorKey[times.size()];
|
nuclear@0
|
2244 aiVectorKey* out_translation = new aiVectorKey[times.size()];
|
nuclear@0
|
2245
|
nuclear@0
|
2246 ConvertTransformOrder_TRStoSRT(out_quat, out_scale, out_translation,
|
nuclear@0
|
2247 scaling,
|
nuclear@0
|
2248 translation,
|
nuclear@0
|
2249 rotation,
|
nuclear@0
|
2250 times,
|
nuclear@0
|
2251 max_time,
|
nuclear@0
|
2252 min_time,
|
nuclear@0
|
2253 target.RotationOrder(),
|
nuclear@0
|
2254 def_scale,
|
nuclear@0
|
2255 def_translate,
|
nuclear@0
|
2256 def_rot);
|
nuclear@0
|
2257
|
nuclear@0
|
2258 // XXX remove duplicates / redundant keys which this operation did
|
nuclear@0
|
2259 // likely produce if not all three channels were equally dense.
|
nuclear@0
|
2260
|
nuclear@0
|
2261 na->mNumScalingKeys = static_cast<unsigned int>(times.size());
|
nuclear@0
|
2262 na->mNumRotationKeys = na->mNumScalingKeys;
|
nuclear@0
|
2263 na->mNumPositionKeys = na->mNumScalingKeys;
|
nuclear@0
|
2264
|
nuclear@0
|
2265 na->mScalingKeys = out_scale;
|
nuclear@0
|
2266 na->mRotationKeys = out_quat;
|
nuclear@0
|
2267 na->mPositionKeys = out_translation;
|
nuclear@0
|
2268 }
|
nuclear@0
|
2269 else {
|
nuclear@0
|
2270
|
nuclear@0
|
2271 // if a particular transformation is not given, grab it from
|
nuclear@0
|
2272 // the corresponding node to meet the semantics of aiNodeAnim,
|
nuclear@0
|
2273 // which requires all of rotation, scaling and translation
|
nuclear@0
|
2274 // to be set.
|
nuclear@0
|
2275 if(chain[TransformationComp_Scaling] != iter_end) {
|
nuclear@0
|
2276 ConvertScaleKeys(na, (*chain[TransformationComp_Scaling]).second,
|
nuclear@0
|
2277 layer_map,
|
nuclear@0
|
2278 max_time,
|
nuclear@0
|
2279 min_time);
|
nuclear@0
|
2280 }
|
nuclear@0
|
2281 else {
|
nuclear@0
|
2282 na->mScalingKeys = new aiVectorKey[1];
|
nuclear@0
|
2283 na->mNumScalingKeys = 1;
|
nuclear@0
|
2284
|
nuclear@0
|
2285 na->mScalingKeys[0].mTime = 0.;
|
nuclear@0
|
2286 na->mScalingKeys[0].mValue = PropertyGet(props,"Lcl Scaling",
|
nuclear@0
|
2287 aiVector3D(1.f,1.f,1.f));
|
nuclear@0
|
2288 }
|
nuclear@0
|
2289
|
nuclear@0
|
2290 if(chain[TransformationComp_Rotation] != iter_end) {
|
nuclear@0
|
2291 ConvertRotationKeys(na, (*chain[TransformationComp_Rotation]).second,
|
nuclear@0
|
2292 layer_map,
|
nuclear@0
|
2293 max_time,
|
nuclear@0
|
2294 min_time,
|
nuclear@0
|
2295 target.RotationOrder());
|
nuclear@0
|
2296 }
|
nuclear@0
|
2297 else {
|
nuclear@0
|
2298 na->mRotationKeys = new aiQuatKey[1];
|
nuclear@0
|
2299 na->mNumRotationKeys = 1;
|
nuclear@0
|
2300
|
nuclear@0
|
2301 na->mRotationKeys[0].mTime = 0.;
|
nuclear@0
|
2302 na->mRotationKeys[0].mValue = EulerToQuaternion(
|
nuclear@0
|
2303 PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f)),
|
nuclear@0
|
2304 target.RotationOrder());
|
nuclear@0
|
2305 }
|
nuclear@0
|
2306
|
nuclear@0
|
2307 if(chain[TransformationComp_Translation] != iter_end) {
|
nuclear@0
|
2308 ConvertTranslationKeys(na, (*chain[TransformationComp_Translation]).second,
|
nuclear@0
|
2309 layer_map,
|
nuclear@0
|
2310 max_time,
|
nuclear@0
|
2311 min_time);
|
nuclear@0
|
2312 }
|
nuclear@0
|
2313 else {
|
nuclear@0
|
2314 na->mPositionKeys = new aiVectorKey[1];
|
nuclear@0
|
2315 na->mNumPositionKeys = 1;
|
nuclear@0
|
2316
|
nuclear@0
|
2317 na->mPositionKeys[0].mTime = 0.;
|
nuclear@0
|
2318 na->mPositionKeys[0].mValue = PropertyGet(props,"Lcl Translation",
|
nuclear@0
|
2319 aiVector3D(0.f,0.f,0.f));
|
nuclear@0
|
2320 }
|
nuclear@0
|
2321
|
nuclear@0
|
2322 }
|
nuclear@0
|
2323 return na.dismiss();
|
nuclear@0
|
2324 }
|
nuclear@0
|
2325
|
nuclear@0
|
2326
|
nuclear@0
|
2327
|
nuclear@0
|
2328 // key (time), value, mapto (component index)
|
nuclear@0
|
2329 typedef boost::tuple< const KeyTimeList*, const KeyValueList*, unsigned int > KeyFrameList;
|
nuclear@0
|
2330 typedef std::vector<KeyFrameList> KeyFrameListList;
|
nuclear@0
|
2331
|
nuclear@0
|
2332
|
nuclear@0
|
2333
|
nuclear@0
|
2334 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2335 KeyFrameListList GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes)
|
nuclear@0
|
2336 {
|
nuclear@0
|
2337 KeyFrameListList inputs;
|
nuclear@0
|
2338 inputs.reserve(nodes.size()*3);
|
nuclear@0
|
2339
|
nuclear@0
|
2340 BOOST_FOREACH(const AnimationCurveNode* node, nodes) {
|
nuclear@0
|
2341 ai_assert(node);
|
nuclear@0
|
2342
|
nuclear@0
|
2343 const AnimationCurveMap& curves = node->Curves();
|
nuclear@0
|
2344 BOOST_FOREACH(const AnimationCurveMap::value_type& kv, curves) {
|
nuclear@0
|
2345
|
nuclear@0
|
2346 unsigned int mapto;
|
nuclear@0
|
2347 if (kv.first == "d|X") {
|
nuclear@0
|
2348 mapto = 0;
|
nuclear@0
|
2349 }
|
nuclear@0
|
2350 else if (kv.first == "d|Y") {
|
nuclear@0
|
2351 mapto = 1;
|
nuclear@0
|
2352 }
|
nuclear@0
|
2353 else if (kv.first == "d|Z") {
|
nuclear@0
|
2354 mapto = 2;
|
nuclear@0
|
2355 }
|
nuclear@0
|
2356 else {
|
nuclear@0
|
2357 FBXImporter::LogWarn("ignoring scale animation curve, did not recognize target component");
|
nuclear@0
|
2358 continue;
|
nuclear@0
|
2359 }
|
nuclear@0
|
2360
|
nuclear@0
|
2361 const AnimationCurve* const curve = kv.second;
|
nuclear@0
|
2362 ai_assert(curve->GetKeys().size() == curve->GetValues().size() && curve->GetKeys().size());
|
nuclear@0
|
2363
|
nuclear@0
|
2364 inputs.push_back(boost::make_tuple(&curve->GetKeys(), &curve->GetValues(), mapto));
|
nuclear@0
|
2365 }
|
nuclear@0
|
2366 }
|
nuclear@0
|
2367 return inputs; // pray for NRVO :-)
|
nuclear@0
|
2368 }
|
nuclear@0
|
2369
|
nuclear@0
|
2370
|
nuclear@0
|
2371 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2372 KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs)
|
nuclear@0
|
2373 {
|
nuclear@0
|
2374 ai_assert(inputs.size());
|
nuclear@0
|
2375
|
nuclear@0
|
2376 // reserve some space upfront - it is likely that the keyframe lists
|
nuclear@0
|
2377 // have matching time values, so max(of all keyframe lists) should
|
nuclear@0
|
2378 // be a good estimate.
|
nuclear@0
|
2379 KeyTimeList keys;
|
nuclear@0
|
2380
|
nuclear@0
|
2381 size_t estimate = 0;
|
nuclear@0
|
2382 BOOST_FOREACH(const KeyFrameList& kfl, inputs) {
|
nuclear@0
|
2383 estimate = std::max(estimate, kfl.get<0>()->size());
|
nuclear@0
|
2384 }
|
nuclear@0
|
2385
|
nuclear@0
|
2386 keys.reserve(estimate);
|
nuclear@0
|
2387
|
nuclear@0
|
2388 std::vector<unsigned int> next_pos;
|
nuclear@0
|
2389 next_pos.resize(inputs.size(),0);
|
nuclear@0
|
2390
|
nuclear@0
|
2391 const size_t count = inputs.size();
|
nuclear@0
|
2392 while(true) {
|
nuclear@0
|
2393
|
nuclear@0
|
2394 uint64_t min_tick = std::numeric_limits<uint64_t>::max();
|
nuclear@0
|
2395 for (size_t i = 0; i < count; ++i) {
|
nuclear@0
|
2396 const KeyFrameList& kfl = inputs[i];
|
nuclear@0
|
2397
|
nuclear@0
|
2398 if (kfl.get<0>()->size() > next_pos[i] && kfl.get<0>()->at(next_pos[i]) < min_tick) {
|
nuclear@0
|
2399 min_tick = kfl.get<0>()->at(next_pos[i]);
|
nuclear@0
|
2400 }
|
nuclear@0
|
2401 }
|
nuclear@0
|
2402
|
nuclear@0
|
2403 if (min_tick == std::numeric_limits<uint64_t>::max()) {
|
nuclear@0
|
2404 break;
|
nuclear@0
|
2405 }
|
nuclear@0
|
2406 keys.push_back(min_tick);
|
nuclear@0
|
2407
|
nuclear@0
|
2408 for (size_t i = 0; i < count; ++i) {
|
nuclear@0
|
2409 const KeyFrameList& kfl = inputs[i];
|
nuclear@0
|
2410
|
nuclear@0
|
2411
|
nuclear@0
|
2412 while(kfl.get<0>()->size() > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == min_tick) {
|
nuclear@0
|
2413 ++next_pos[i];
|
nuclear@0
|
2414 }
|
nuclear@0
|
2415 }
|
nuclear@0
|
2416 }
|
nuclear@0
|
2417
|
nuclear@0
|
2418 return keys;
|
nuclear@0
|
2419 }
|
nuclear@0
|
2420
|
nuclear@0
|
2421
|
nuclear@0
|
2422 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2423 void InterpolateKeys(aiVectorKey* valOut,const KeyTimeList& keys, const KeyFrameListList& inputs,
|
nuclear@0
|
2424 const bool geom,
|
nuclear@0
|
2425 double& max_time,
|
nuclear@0
|
2426 double& min_time)
|
nuclear@0
|
2427
|
nuclear@0
|
2428 {
|
nuclear@0
|
2429 ai_assert(keys.size());
|
nuclear@0
|
2430 ai_assert(valOut);
|
nuclear@0
|
2431
|
nuclear@0
|
2432 std::vector<unsigned int> next_pos;
|
nuclear@0
|
2433 const size_t count = inputs.size();
|
nuclear@0
|
2434
|
nuclear@0
|
2435 next_pos.resize(inputs.size(),0);
|
nuclear@0
|
2436
|
nuclear@0
|
2437 BOOST_FOREACH(KeyTimeList::value_type time, keys) {
|
nuclear@0
|
2438 float result[3] = {0.0f, 0.0f, 0.0f};
|
nuclear@0
|
2439 if(geom) {
|
nuclear@0
|
2440 result[0] = result[1] = result[2] = 1.0f;
|
nuclear@0
|
2441 }
|
nuclear@0
|
2442
|
nuclear@0
|
2443 for (size_t i = 0; i < count; ++i) {
|
nuclear@0
|
2444 const KeyFrameList& kfl = inputs[i];
|
nuclear@0
|
2445
|
nuclear@0
|
2446 const size_t ksize = kfl.get<0>()->size();
|
nuclear@0
|
2447 if (ksize > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == time) {
|
nuclear@0
|
2448 ++next_pos[i];
|
nuclear@0
|
2449 }
|
nuclear@0
|
2450
|
nuclear@0
|
2451 const size_t id0 = next_pos[i]>0 ? next_pos[i]-1 : 0;
|
nuclear@0
|
2452 const size_t id1 = next_pos[i]==ksize ? ksize-1 : next_pos[i];
|
nuclear@0
|
2453
|
nuclear@0
|
2454 // use lerp for interpolation
|
nuclear@0
|
2455 const KeyValueList::value_type valueA = kfl.get<1>()->at(id0);
|
nuclear@0
|
2456 const KeyValueList::value_type valueB = kfl.get<1>()->at(id1);
|
nuclear@0
|
2457
|
nuclear@0
|
2458 const KeyTimeList::value_type timeA = kfl.get<0>()->at(id0);
|
nuclear@0
|
2459 const KeyTimeList::value_type timeB = kfl.get<0>()->at(id1);
|
nuclear@0
|
2460
|
nuclear@0
|
2461 // do the actual interpolation in double-precision arithmetics
|
nuclear@0
|
2462 // because it is a bit sensitive to rounding errors.
|
nuclear@0
|
2463 const double factor = timeB == timeA ? 0. : static_cast<double>((time - timeA) / (timeB - timeA));
|
nuclear@0
|
2464 const float interpValue = static_cast<float>(valueA + (valueB - valueA) * factor);
|
nuclear@0
|
2465
|
nuclear@0
|
2466 if(geom) {
|
nuclear@0
|
2467 result[kfl.get<2>()] *= interpValue;
|
nuclear@0
|
2468 }
|
nuclear@0
|
2469 else {
|
nuclear@0
|
2470 result[kfl.get<2>()] += interpValue;
|
nuclear@0
|
2471 }
|
nuclear@0
|
2472 }
|
nuclear@0
|
2473
|
nuclear@0
|
2474 // magic value to convert fbx times to seconds
|
nuclear@0
|
2475 valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps;
|
nuclear@0
|
2476
|
nuclear@0
|
2477 min_time = std::min(min_time, valOut->mTime);
|
nuclear@0
|
2478 max_time = std::max(max_time, valOut->mTime);
|
nuclear@0
|
2479
|
nuclear@0
|
2480 valOut->mValue.x = result[0];
|
nuclear@0
|
2481 valOut->mValue.y = result[1];
|
nuclear@0
|
2482 valOut->mValue.z = result[2];
|
nuclear@0
|
2483
|
nuclear@0
|
2484 ++valOut;
|
nuclear@0
|
2485 }
|
nuclear@0
|
2486 }
|
nuclear@0
|
2487
|
nuclear@0
|
2488
|
nuclear@0
|
2489 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2490 void InterpolateKeys(aiQuatKey* valOut,const KeyTimeList& keys, const KeyFrameListList& inputs,
|
nuclear@0
|
2491 const bool geom,
|
nuclear@0
|
2492 double& maxTime,
|
nuclear@0
|
2493 double& minTime,
|
nuclear@0
|
2494 Model::RotOrder order)
|
nuclear@0
|
2495 {
|
nuclear@0
|
2496 ai_assert(keys.size());
|
nuclear@0
|
2497 ai_assert(valOut);
|
nuclear@0
|
2498
|
nuclear@0
|
2499 boost::scoped_array<aiVectorKey> temp(new aiVectorKey[keys.size()]);
|
nuclear@0
|
2500 InterpolateKeys(temp.get(),keys,inputs,geom,maxTime, minTime);
|
nuclear@0
|
2501
|
nuclear@0
|
2502 aiMatrix4x4 m;
|
nuclear@0
|
2503
|
nuclear@0
|
2504 aiQuaternion lastq;
|
nuclear@0
|
2505
|
nuclear@0
|
2506 for (size_t i = 0, c = keys.size(); i < c; ++i) {
|
nuclear@0
|
2507
|
nuclear@0
|
2508 valOut[i].mTime = temp[i].mTime;
|
nuclear@0
|
2509
|
nuclear@0
|
2510
|
nuclear@0
|
2511 GetRotationMatrix(order, temp[i].mValue, m);
|
nuclear@0
|
2512 aiQuaternion quat = aiQuaternion(aiMatrix3x3(m));
|
nuclear@0
|
2513
|
nuclear@0
|
2514 // take shortest path by checking the inner product
|
nuclear@0
|
2515 // http://www.3dkingdoms.com/weekly/weekly.php?a=36
|
nuclear@0
|
2516 if (quat.x * lastq.x + quat.y * lastq.y + quat.z * lastq.z + quat.w * lastq.w < 0)
|
nuclear@0
|
2517 {
|
nuclear@0
|
2518 quat.x = -quat.x;
|
nuclear@0
|
2519 quat.y = -quat.y;
|
nuclear@0
|
2520 quat.z = -quat.z;
|
nuclear@0
|
2521 quat.w = -quat.w;
|
nuclear@0
|
2522 }
|
nuclear@0
|
2523 lastq = quat;
|
nuclear@0
|
2524
|
nuclear@0
|
2525 valOut[i].mValue = quat;
|
nuclear@0
|
2526 }
|
nuclear@0
|
2527 }
|
nuclear@0
|
2528
|
nuclear@0
|
2529
|
nuclear@0
|
2530 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2531 void ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale,
|
nuclear@0
|
2532 aiVectorKey* out_translation,
|
nuclear@0
|
2533 const KeyFrameListList& scaling,
|
nuclear@0
|
2534 const KeyFrameListList& translation,
|
nuclear@0
|
2535 const KeyFrameListList& rotation,
|
nuclear@0
|
2536 const KeyTimeList& times,
|
nuclear@0
|
2537 double& maxTime,
|
nuclear@0
|
2538 double& minTime,
|
nuclear@0
|
2539 Model::RotOrder order,
|
nuclear@0
|
2540 const aiVector3D& def_scale,
|
nuclear@0
|
2541 const aiVector3D& def_translate,
|
nuclear@0
|
2542 const aiQuaternion& def_rotation)
|
nuclear@0
|
2543 {
|
nuclear@0
|
2544 if (rotation.size()) {
|
nuclear@0
|
2545 InterpolateKeys(out_quat, times, rotation, false, maxTime, minTime, order);
|
nuclear@0
|
2546 }
|
nuclear@0
|
2547 else {
|
nuclear@0
|
2548 for (size_t i = 0; i < times.size(); ++i) {
|
nuclear@0
|
2549 out_quat[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
|
nuclear@0
|
2550 out_quat[i].mValue = def_rotation;
|
nuclear@0
|
2551 }
|
nuclear@0
|
2552 }
|
nuclear@0
|
2553
|
nuclear@0
|
2554 if (scaling.size()) {
|
nuclear@0
|
2555 InterpolateKeys(out_scale, times, scaling, true, maxTime, minTime);
|
nuclear@0
|
2556 }
|
nuclear@0
|
2557 else {
|
nuclear@0
|
2558 for (size_t i = 0; i < times.size(); ++i) {
|
nuclear@0
|
2559 out_scale[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
|
nuclear@0
|
2560 out_scale[i].mValue = def_scale;
|
nuclear@0
|
2561 }
|
nuclear@0
|
2562 }
|
nuclear@0
|
2563
|
nuclear@0
|
2564 if (translation.size()) {
|
nuclear@0
|
2565 InterpolateKeys(out_translation, times, translation, false, maxTime, minTime);
|
nuclear@0
|
2566 }
|
nuclear@0
|
2567 else {
|
nuclear@0
|
2568 for (size_t i = 0; i < times.size(); ++i) {
|
nuclear@0
|
2569 out_translation[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
|
nuclear@0
|
2570 out_translation[i].mValue = def_translate;
|
nuclear@0
|
2571 }
|
nuclear@0
|
2572 }
|
nuclear@0
|
2573
|
nuclear@0
|
2574 const size_t count = times.size();
|
nuclear@0
|
2575 for (size_t i = 0; i < count; ++i) {
|
nuclear@0
|
2576 aiQuaternion& r = out_quat[i].mValue;
|
nuclear@0
|
2577 aiVector3D& s = out_scale[i].mValue;
|
nuclear@0
|
2578 aiVector3D& t = out_translation[i].mValue;
|
nuclear@0
|
2579
|
nuclear@0
|
2580 aiMatrix4x4 mat, temp;
|
nuclear@0
|
2581 aiMatrix4x4::Translation(t, mat);
|
nuclear@0
|
2582 mat *= aiMatrix4x4( r.GetMatrix() );
|
nuclear@0
|
2583 mat *= aiMatrix4x4::Scaling(s, temp);
|
nuclear@0
|
2584
|
nuclear@0
|
2585 mat.Decompose(s, r, t);
|
nuclear@0
|
2586 }
|
nuclear@0
|
2587 }
|
nuclear@0
|
2588
|
nuclear@0
|
2589
|
nuclear@0
|
2590 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2591 // euler xyz -> quat
|
nuclear@0
|
2592 aiQuaternion EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order)
|
nuclear@0
|
2593 {
|
nuclear@0
|
2594 aiMatrix4x4 m;
|
nuclear@0
|
2595 GetRotationMatrix(order, rot, m);
|
nuclear@0
|
2596
|
nuclear@0
|
2597 return aiQuaternion(aiMatrix3x3(m));
|
nuclear@0
|
2598 }
|
nuclear@0
|
2599
|
nuclear@0
|
2600
|
nuclear@0
|
2601 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2602 void ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& layers,
|
nuclear@0
|
2603 double& maxTime,
|
nuclear@0
|
2604 double& minTime)
|
nuclear@0
|
2605 {
|
nuclear@0
|
2606 ai_assert(nodes.size());
|
nuclear@0
|
2607
|
nuclear@0
|
2608 // XXX for now, assume scale should be blended geometrically (i.e. two
|
nuclear@0
|
2609 // layers should be multiplied with each other). There is a FBX
|
nuclear@0
|
2610 // property in the layer to specify the behaviour, though.
|
nuclear@0
|
2611
|
nuclear@0
|
2612 const KeyFrameListList& inputs = GetKeyframeList(nodes);
|
nuclear@0
|
2613 const KeyTimeList& keys = GetKeyTimeList(inputs);
|
nuclear@0
|
2614
|
nuclear@0
|
2615 na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
|
nuclear@0
|
2616 na->mScalingKeys = new aiVectorKey[keys.size()];
|
nuclear@0
|
2617 InterpolateKeys(na->mScalingKeys, keys, inputs, true, maxTime, minTime);
|
nuclear@0
|
2618 }
|
nuclear@0
|
2619
|
nuclear@0
|
2620
|
nuclear@0
|
2621 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2622 void ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
|
nuclear@0
|
2623 const LayerMap& layers,
|
nuclear@0
|
2624 double& maxTime,
|
nuclear@0
|
2625 double& minTime)
|
nuclear@0
|
2626 {
|
nuclear@0
|
2627 ai_assert(nodes.size());
|
nuclear@0
|
2628
|
nuclear@0
|
2629 // XXX see notes in ConvertScaleKeys()
|
nuclear@0
|
2630 const KeyFrameListList& inputs = GetKeyframeList(nodes);
|
nuclear@0
|
2631 const KeyTimeList& keys = GetKeyTimeList(inputs);
|
nuclear@0
|
2632
|
nuclear@0
|
2633 na->mNumPositionKeys = static_cast<unsigned int>(keys.size());
|
nuclear@0
|
2634 na->mPositionKeys = new aiVectorKey[keys.size()];
|
nuclear@0
|
2635 InterpolateKeys(na->mPositionKeys, keys, inputs, false, maxTime, minTime);
|
nuclear@0
|
2636 }
|
nuclear@0
|
2637
|
nuclear@0
|
2638
|
nuclear@0
|
2639 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2640 void ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
|
nuclear@0
|
2641 const LayerMap& layers,
|
nuclear@0
|
2642 double& maxTime,
|
nuclear@0
|
2643 double& minTime,
|
nuclear@0
|
2644 Model::RotOrder order)
|
nuclear@0
|
2645 {
|
nuclear@0
|
2646 ai_assert(nodes.size());
|
nuclear@0
|
2647
|
nuclear@0
|
2648 // XXX see notes in ConvertScaleKeys()
|
nuclear@0
|
2649 const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes);
|
nuclear@0
|
2650 const KeyTimeList& keys = GetKeyTimeList(inputs);
|
nuclear@0
|
2651
|
nuclear@0
|
2652 na->mNumRotationKeys = static_cast<unsigned int>(keys.size());
|
nuclear@0
|
2653 na->mRotationKeys = new aiQuatKey[keys.size()];
|
nuclear@0
|
2654 InterpolateKeys(na->mRotationKeys, keys, inputs, false, maxTime, minTime, order);
|
nuclear@0
|
2655 }
|
nuclear@0
|
2656
|
nuclear@0
|
2657
|
nuclear@0
|
2658 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2659 // copy generated meshes, animations, lights, cameras and textures to the output scene
|
nuclear@0
|
2660 void TransferDataToScene()
|
nuclear@0
|
2661 {
|
nuclear@0
|
2662 ai_assert(!out->mMeshes && !out->mNumMeshes);
|
nuclear@0
|
2663
|
nuclear@0
|
2664 // note: the trailing () ensures initialization with NULL - not
|
nuclear@0
|
2665 // many C++ users seem to know this, so pointing it out to avoid
|
nuclear@0
|
2666 // confusion why this code works.
|
nuclear@0
|
2667
|
nuclear@0
|
2668 if(meshes.size()) {
|
nuclear@0
|
2669 out->mMeshes = new aiMesh*[meshes.size()]();
|
nuclear@0
|
2670 out->mNumMeshes = static_cast<unsigned int>(meshes.size());
|
nuclear@0
|
2671
|
nuclear@0
|
2672 std::swap_ranges(meshes.begin(),meshes.end(),out->mMeshes);
|
nuclear@0
|
2673 }
|
nuclear@0
|
2674
|
nuclear@0
|
2675 if(materials.size()) {
|
nuclear@0
|
2676 out->mMaterials = new aiMaterial*[materials.size()]();
|
nuclear@0
|
2677 out->mNumMaterials = static_cast<unsigned int>(materials.size());
|
nuclear@0
|
2678
|
nuclear@0
|
2679 std::swap_ranges(materials.begin(),materials.end(),out->mMaterials);
|
nuclear@0
|
2680 }
|
nuclear@0
|
2681
|
nuclear@0
|
2682 if(animations.size()) {
|
nuclear@0
|
2683 out->mAnimations = new aiAnimation*[animations.size()]();
|
nuclear@0
|
2684 out->mNumAnimations = static_cast<unsigned int>(animations.size());
|
nuclear@0
|
2685
|
nuclear@0
|
2686 std::swap_ranges(animations.begin(),animations.end(),out->mAnimations);
|
nuclear@0
|
2687 }
|
nuclear@0
|
2688
|
nuclear@0
|
2689 if(lights.size()) {
|
nuclear@0
|
2690 out->mLights = new aiLight*[lights.size()]();
|
nuclear@0
|
2691 out->mNumLights = static_cast<unsigned int>(lights.size());
|
nuclear@0
|
2692
|
nuclear@0
|
2693 std::swap_ranges(lights.begin(),lights.end(),out->mLights);
|
nuclear@0
|
2694 }
|
nuclear@0
|
2695
|
nuclear@0
|
2696 if(cameras.size()) {
|
nuclear@0
|
2697 out->mCameras = new aiCamera*[cameras.size()]();
|
nuclear@0
|
2698 out->mNumCameras = static_cast<unsigned int>(cameras.size());
|
nuclear@0
|
2699
|
nuclear@0
|
2700 std::swap_ranges(cameras.begin(),cameras.end(),out->mCameras);
|
nuclear@0
|
2701 }
|
nuclear@0
|
2702 }
|
nuclear@0
|
2703
|
nuclear@0
|
2704
|
nuclear@0
|
2705 private:
|
nuclear@0
|
2706
|
nuclear@0
|
2707 // 0: not assigned yet, others: index is value - 1
|
nuclear@0
|
2708 unsigned int defaultMaterialIndex;
|
nuclear@0
|
2709
|
nuclear@0
|
2710 std::vector<aiMesh*> meshes;
|
nuclear@0
|
2711 std::vector<aiMaterial*> materials;
|
nuclear@0
|
2712 std::vector<aiAnimation*> animations;
|
nuclear@0
|
2713 std::vector<aiLight*> lights;
|
nuclear@0
|
2714 std::vector<aiCamera*> cameras;
|
nuclear@0
|
2715
|
nuclear@0
|
2716 typedef std::map<const Material*, unsigned int> MaterialMap;
|
nuclear@0
|
2717 MaterialMap materials_converted;
|
nuclear@0
|
2718
|
nuclear@0
|
2719 typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap;
|
nuclear@0
|
2720 MeshMap meshes_converted;
|
nuclear@0
|
2721
|
nuclear@0
|
2722 // fixed node name -> which trafo chain components have animations?
|
nuclear@0
|
2723 typedef std::map<std::string, unsigned int> NodeAnimBitMap;
|
nuclear@0
|
2724 NodeAnimBitMap node_anim_chain_bits;
|
nuclear@0
|
2725
|
nuclear@0
|
2726 // name -> has had its prefix_stripped?
|
nuclear@0
|
2727 typedef std::map<std::string, bool> NodeNameMap;
|
nuclear@0
|
2728 NodeNameMap node_names;
|
nuclear@0
|
2729
|
nuclear@0
|
2730 typedef std::map<std::string, std::string> NameNameMap;
|
nuclear@0
|
2731 NameNameMap renamed_nodes;
|
nuclear@0
|
2732
|
nuclear@0
|
2733 double anim_fps;
|
nuclear@0
|
2734
|
nuclear@0
|
2735 aiScene* const out;
|
nuclear@0
|
2736 const FBX::Document& doc;
|
nuclear@0
|
2737 };
|
nuclear@0
|
2738
|
nuclear@0
|
2739 //} // !anon
|
nuclear@0
|
2740
|
nuclear@0
|
2741 // ------------------------------------------------------------------------------------------------
|
nuclear@0
|
2742 void ConvertToAssimpScene(aiScene* out, const Document& doc)
|
nuclear@0
|
2743 {
|
nuclear@0
|
2744 Converter converter(out,doc);
|
nuclear@0
|
2745 }
|
nuclear@0
|
2746
|
nuclear@0
|
2747 } // !FBX
|
nuclear@0
|
2748 } // !Assimp
|
nuclear@0
|
2749
|
nuclear@0
|
2750 #endif
|