1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "BsFBXImporter.h"
4#include "Resources/BsResource.h"
5#include "BsCoreApplication.h"
6#include "Debug/BsDebug.h"
7#include "FileSystem/BsDataStream.h"
8#include "Mesh/BsMeshData.h"
9#include "Mesh/BsMesh.h"
10#include "Math/BsVector2.h"
11#include "Math/BsVector3.h"
12#include "Math/BsVector4.h"
13#include "RenderAPI/BsVertexDataDesc.h"
14#include "BsFBXUtility.h"
15#include "Mesh/BsMeshUtility.h"
16#include "Renderer/BsRendererMeshData.h"
17#include "Importer/BsMeshImportOptions.h"
18#include "Physics/BsPhysicsMesh.h"
19#include "Animation/BsAnimationCurve.h"
20#include "Animation/BsAnimationClip.h"
21#include "Animation/BsAnimationUtility.h"
22#include "Animation/BsSkeleton.h"
23#include "Animation/BsMorphShapes.h"
24#include "Physics/BsPhysics.h"
25#include "FileSystem/BsFileSystem.h"
26
27namespace bs
28{
29 Matrix4 FBXToNativeType(const FbxAMatrix& value)
30 {
31 Matrix4 native;
32 for (UINT32 row = 0; row < 4; row++)
33 for (UINT32 col = 0; col < 4; col++)
34 native[row][col] = (float)value[col][row];
35
36 return native;
37 }
38
39 Vector3 FBXToNativeType(const FbxVector4& value)
40 {
41 return Vector3((float)value[0], (float)value[1], (float)value[2]);
42 }
43
44 Vector3 FBXToNativeType(const FbxDouble3& value)
45 {
46 return Vector3((float)value[0], (float)value[1], (float)value[2]);
47 }
48
49 Vector2 FBXToNativeType(const FbxVector2& value)
50 {
51 return Vector2((float)value[0], (float)value[1]);
52 }
53
54 RGBA FBXToNativeType(const FbxColor& value)
55 {
56 Color native;
57 native.r = (float)value[0];
58 native.g = (float)value[1];
59 native.b = (float)value[2];
60 native.a = (float)value[3];
61
62 return native.getAsRGBA();
63 }
64
65 FbxSurfaceMaterial* FBXToNativeType(FbxSurfaceMaterial* const& value)
66 {
67 return value;
68 }
69
70 int FBXToNativeType(const int & value)
71 {
72 return value;
73 }
74
75 FBXImporter::FBXImporter()
76 {
77 mExtensions.push_back(u8"fbx");
78 mExtensions.push_back(u8"obj");
79 mExtensions.push_back(u8"dae");
80 }
81
82 bool FBXImporter::isExtensionSupported(const String& ext) const
83 {
84 String lowerCaseExt = ext;
85 StringUtil::toLowerCase(lowerCaseExt);
86
87 return find(mExtensions.begin(), mExtensions.end(), lowerCaseExt) != mExtensions.end();
88 }
89
90 bool FBXImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
91 {
92 return true; // FBX files can be plain-text so I don't even check for magic number
93 }
94
95 SPtr<ImportOptions> FBXImporter::createImportOptions() const
96 {
97 return bs_shared_ptr_new<MeshImportOptions>();
98 }
99
100 SPtr<Resource> FBXImporter::import(const Path& filePath, SPtr<const ImportOptions> importOptions)
101 {
102 MESH_DESC desc;
103
104 Vector<FBXAnimationClipData> dummy;
105 SPtr<RendererMeshData> rendererMeshData = importMeshData(filePath, importOptions, desc.subMeshes, dummy,
106 desc.skeleton, desc.morphShapes);
107
108 const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
109
110 desc.usage = MU_STATIC;
111 if (meshImportOptions->cpuCached)
112 desc.usage |= MU_CPUCACHED;
113
114 SPtr<Mesh> mesh = Mesh::_createPtr(rendererMeshData->getData(), desc);
115
116 const String fileName = filePath.getFilename(false);
117 mesh->setName(fileName);
118
119 return mesh;
120 }
121
122 Vector<SubResourceRaw> FBXImporter::importAll(const Path& filePath, SPtr<const ImportOptions> importOptions)
123 {
124 MESH_DESC desc;
125
126 Vector<FBXAnimationClipData> animationClips;
127 SPtr<RendererMeshData> rendererMeshData = importMeshData(filePath, importOptions, desc.subMeshes, animationClips,
128 desc.skeleton, desc.morphShapes);
129
130 const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
131
132 desc.usage = MU_STATIC;
133 if (meshImportOptions->cpuCached)
134 desc.usage |= MU_CPUCACHED;
135
136 SPtr<Mesh> mesh = Mesh::_createPtr(rendererMeshData->getData(), desc);
137
138 const String fileName = filePath.getFilename(false);
139 mesh->setName(fileName);
140
141 Vector<SubResourceRaw> output;
142 if(mesh != nullptr)
143 {
144 output.push_back({ u8"primary", mesh });
145
146 CollisionMeshType collisionMeshType = meshImportOptions->collisionMeshType;
147 if(collisionMeshType != CollisionMeshType::None)
148 {
149 if(Physics::isStarted())
150 {
151 PhysicsMeshType type = collisionMeshType == CollisionMeshType::Convex ?
152 PhysicsMeshType::Convex : PhysicsMeshType::Triangle;
153
154 SPtr<PhysicsMesh> physicsMesh = PhysicsMesh::_createPtr(rendererMeshData->getData(), type);
155
156 output.push_back({ u8"collision", physicsMesh });
157 }
158 else
159 {
160 LOGWRN("Cannot generate a collision mesh as the physics module was not started.");
161 }
162 }
163
164 Vector<ImportedAnimationEvents> events = meshImportOptions->animationEvents;
165 for(auto& entry : animationClips)
166 {
167 SPtr<AnimationClip> clip = AnimationClip::_createPtr(entry.curves, entry.isAdditive, entry.sampleRate,
168 entry.rootMotion);
169
170 for(auto& eventsEntry : events)
171 {
172 if(entry.name == eventsEntry.name)
173 {
174 clip->setEvents(eventsEntry.events);
175 break;
176 }
177 }
178
179 output.push_back({ entry.name, clip });
180 }
181 }
182
183 return output;
184 }
185
186 SPtr<RendererMeshData> FBXImporter::importMeshData(const Path& filePath, SPtr<const ImportOptions> importOptions,
187 Vector<SubMesh>& subMeshes, Vector<FBXAnimationClipData>& animation, SPtr<Skeleton>& skeleton,
188 SPtr<MorphShapes>& morphShapes)
189 {
190 FbxScene* fbxScene = nullptr;
191
192 if (!startUpSdk(fbxScene))
193 return nullptr;
194
195 if (!loadFBXFile(fbxScene, filePath))
196 return nullptr;
197
198 const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
199 FBXImportOptions fbxImportOptions;
200 fbxImportOptions.importNormals = meshImportOptions->importNormals;
201 fbxImportOptions.importTangents = meshImportOptions->importTangents;
202 fbxImportOptions.importAnimation = meshImportOptions->importAnimation;
203 fbxImportOptions.importBlendShapes = meshImportOptions->importBlendShapes;
204 fbxImportOptions.importSkin = meshImportOptions->importSkin;
205 fbxImportOptions.importScale = meshImportOptions->importScale;
206 fbxImportOptions.reduceKeyframes = meshImportOptions->reduceKeyFrames;
207
208 FBXImportScene importedScene;
209 bakeTransforms(fbxScene);
210 parseScene(fbxScene, fbxImportOptions, importedScene);
211
212 if (fbxImportOptions.importBlendShapes)
213 importBlendShapes(importedScene, fbxImportOptions);
214
215 if (fbxImportOptions.importSkin)
216 importSkin(importedScene, fbxImportOptions);
217
218 if (fbxImportOptions.importAnimation)
219 importAnimations(fbxScene, fbxImportOptions, importedScene);
220
221 splitMeshVertices(importedScene);
222 generateMissingTangentSpace(importedScene, fbxImportOptions);
223
224 SPtr<RendererMeshData> rendererMeshData = generateMeshData(importedScene, fbxImportOptions, subMeshes);
225
226 skeleton = createSkeleton(importedScene, subMeshes.size() > 1);
227 morphShapes = createMorphShapes(importedScene);
228
229 // Import animation clips
230 if (!importedScene.clips.empty())
231 {
232 const Vector<AnimationSplitInfo>& splits = meshImportOptions->animationSplits;
233 convertAnimations(importedScene.clips, splits, skeleton, meshImportOptions->importRootMotion, animation);
234 }
235
236 // TODO - Later: Optimize mesh: Remove bad and degenerate polygons, weld nearby vertices, optimize for vertex cache
237
238 shutDownSdk();
239
240 return rendererMeshData;
241 }
242
243 SPtr<Skeleton> FBXImporter::createSkeleton(const FBXImportScene& scene, bool sharedRoot)
244 {
245 Vector<BONE_DESC> allBones;
246 UnorderedMap<FBXImportNode*, UINT32> boneMap;
247
248 for (auto& mesh : scene.meshes)
249 {
250 // Create bones
251 for (auto& fbxBone : mesh->bones)
252 {
253 UINT32 boneIdx = (UINT32)allBones.size();
254
255 auto iterFind = boneMap.find(fbxBone.node);
256 if(iterFind != boneMap.end())
257 continue; // Duplicate
258
259 boneMap[fbxBone.node] = boneIdx;
260
261 allBones.push_back(BONE_DESC());
262 BONE_DESC& bone = allBones.back();
263
264 bone.name = fbxBone.node->name;
265 bone.localTfrm = fbxBone.localTfrm;
266 bone.invBindPose = fbxBone.bindPose;
267 }
268 }
269
270 // Generate skeleton
271 if (allBones.size() > 0)
272 {
273 // Find bone parents
274 UINT32 numProcessedBones = 0;
275
276 // Generate common root bone for all meshes
277 UINT32 rootBoneIdx = (UINT32)-1;
278 if (sharedRoot)
279 {
280 rootBoneIdx = (UINT32)allBones.size();
281
282 allBones.push_back(BONE_DESC());
283 BONE_DESC& bone = allBones.back();
284
285 bone.name = "MultiMeshRoot";
286 bone.localTfrm = Transform();
287 bone.invBindPose = Matrix4::IDENTITY;
288 bone.parent = (UINT32)-1;
289
290 numProcessedBones++;
291 }
292
293 Stack<std::pair<FBXImportNode*, UINT32>> todo;
294 todo.push({ scene.rootNode, rootBoneIdx });
295
296 while (!todo.empty())
297 {
298 auto entry = todo.top();
299 todo.pop();
300
301 FBXImportNode* node = entry.first;
302 UINT32 parentBoneIdx = entry.second;
303
304 auto boneIter = boneMap.find(node);
305 if (boneIter != boneMap.end())
306 {
307 UINT32 boneIdx = boneIter->second;
308 allBones[boneIdx].parent = parentBoneIdx;
309
310 parentBoneIdx = boneIdx;
311 numProcessedBones++;
312 }
313 else
314 {
315 // Node is not a bone, but it still needs to be part of the hierarchy. It wont be animated, nor will
316 // it directly influence any vertices, but its transform must be applied to any child bones.
317 UINT32 boneIdx = (UINT32)allBones.size();
318
319 allBones.push_back(BONE_DESC());
320 BONE_DESC& bone = allBones.back();
321
322 bone.name = node->name;
323 bone.localTfrm = node->localTransform;
324 bone.invBindPose = Matrix4::IDENTITY;
325 bone.parent = parentBoneIdx;
326
327 parentBoneIdx = boneIdx;
328 numProcessedBones++;
329 }
330
331 for (auto& child : node->children)
332 todo.push({ child, parentBoneIdx });
333 }
334
335 UINT32 numAllBones = (UINT32)allBones.size();
336 if (numProcessedBones == numAllBones)
337 return Skeleton::create(allBones.data(), numAllBones);
338
339 LOGERR("Not all bones were found in the node hierarchy. Skeleton invalid.");
340 }
341
342 return nullptr;
343 }
344
345 SPtr<MorphShapes> FBXImporter::createMorphShapes(const FBXImportScene& scene)
346 {
347 // Combine morph shapes from all sub-meshes, and transform them
348 struct RawMorphShape
349 {
350 String name;
351 float weight;
352 Vector<MorphVertex> vertices;
353 };
354
355 UnorderedMap<String, UnorderedMap<String, RawMorphShape>> allRawMorphShapes;
356 UINT32 totalNumVertices = 0;
357
358 // Note: Order in which we combine meshes must match the order in MeshData::combine
359 for (auto& mesh : scene.meshes)
360 {
361 UINT32 numVertices = (UINT32)mesh->positions.size();
362 UINT32 numNormals = (UINT32)mesh->normals.size();
363 bool hasNormals = numVertices == numNormals;
364
365 for (auto& node : mesh->referencedBy)
366 {
367 Matrix4 worldTransform = node->worldTransform * node->geomTransform;
368 Matrix4 worldTransformIT = worldTransform.inverse();
369 worldTransformIT = worldTransformIT.transpose();
370
371 // Copy & transform positions
372 for(auto& blendShape : mesh->blendShapes)
373 {
374 UnorderedMap<String, RawMorphShape>& channelShapes = allRawMorphShapes[blendShape.name];
375
376 for(auto& blendFrame : blendShape.frames)
377 {
378 RawMorphShape& shape = channelShapes[blendFrame.name];
379 shape.name = blendFrame.name;
380 shape.weight = blendFrame.weight;
381
382 UINT32 frameNumVertices = (UINT32)blendFrame.positions.size();
383 if (frameNumVertices == numVertices)
384 {
385 for (UINT32 i = 0; i < numVertices; i++)
386 {
387 Vector3 meshPosition = worldTransform.multiplyAffine(mesh->positions[i]);
388 Vector3 blendPosition = worldTransform.multiplyAffine(blendFrame.positions[i]);
389
390 Vector3 positionDelta = blendPosition - meshPosition;
391 Vector3 normalDelta;
392 if (hasNormals)
393 {
394 Vector3 blendNormal = worldTransformIT.multiplyDirection(blendFrame.normals[i]);
395 blendNormal = Vector3::normalize(blendNormal);
396
397 Vector3 meshNormal = worldTransformIT.multiplyDirection(mesh->normals[i]);
398 meshNormal = Vector3::normalize(meshNormal);
399
400 normalDelta = blendNormal - meshNormal;
401 }
402 else
403 normalDelta = Vector3::ZERO;
404
405 if (positionDelta.squaredLength() > 0.000001f || normalDelta.squaredLength() > 0.0001f)
406 shape.vertices.push_back(MorphVertex(positionDelta, normalDelta, totalNumVertices + i));
407 }
408 }
409 else
410 {
411 LOGERR("Corrupt blend shape frame. Number of vertices doesn't match the number of mesh vertices.");
412 }
413 }
414 }
415
416 totalNumVertices += numVertices;
417 }
418 }
419
420 // Create morph shape object from combined shape data
421 SPtr<MorphShapes> morphShapes;
422 Vector<SPtr<MorphChannel>> allChannels;
423 for (auto& channel : allRawMorphShapes)
424 {
425 Vector<SPtr<MorphShape>> channelShapes;
426 for (auto& entry : channel.second)
427 {
428 RawMorphShape& shape = entry.second;
429 shape.vertices.shrink_to_fit();
430
431 SPtr<MorphShape> morphShape = MorphShape::create(shape.name, shape.weight, shape.vertices);
432 channelShapes.push_back(morphShape);
433 }
434
435 if(channelShapes.size() > 0)
436 {
437 SPtr<MorphChannel> morphChannel = MorphChannel::create(channel.first, channelShapes);
438 allChannels.push_back(morphChannel);
439 }
440 }
441
442 if (!allChannels.empty())
443 return MorphShapes::create(allChannels, totalNumVertices);
444
445 return morphShapes;
446 }
447
448 bool FBXImporter::startUpSdk(FbxScene*& scene)
449 {
450 mFBXManager = FbxManager::Create();
451 if (mFBXManager == nullptr)
452 {
453 LOGERR("FBX import failed: FBX SDK failed to initialize. FbxManager::Create() failed.");
454 return false;
455 }
456
457 FbxIOSettings* ios = FbxIOSettings::Create(mFBXManager, IOSROOT);
458 mFBXManager->SetIOSettings(ios);
459
460 scene = FbxScene::Create(mFBXManager, "Import Scene");
461 if (scene == nullptr)
462 {
463 LOGWRN("FBX import failed: Failed to create FBX scene.");
464 return false;
465 }
466
467 return true;
468 }
469
470 void FBXImporter::shutDownSdk()
471 {
472 mFBXManager->Destroy();
473 mFBXManager = nullptr;
474 }
475
476 bool FBXImporter::loadFBXFile(FbxScene* scene, const Path& filePath)
477 {
478 int lFileMajor, lFileMinor, lFileRevision;
479 int lSDKMajor, lSDKMinor, lSDKRevision;
480 FbxManager::GetFileFormatVersion(lSDKMajor, lSDKMinor, lSDKRevision);
481
482 Lock fileLock = FileScheduler::getLock(filePath);
483 FbxImporter* importer = FbxImporter::Create(mFBXManager, "");
484 bool importStatus = importer->Initialize(filePath.toString().c_str(), -1, mFBXManager->GetIOSettings());
485
486 importer->GetFileVersion(lFileMajor, lFileMinor, lFileRevision);
487
488 if(!importStatus)
489 {
490 LOGERR("FBX import failed: Call to FbxImporter::Initialize() failed.\n" +
491 String("Error returned: %s\n\n") + String(importer->GetStatus().GetErrorString()));
492 return false;
493 }
494
495 mFBXManager->GetIOSettings()->SetBoolProp(IMP_FBX_TEXTURE, false);
496 mFBXManager->GetIOSettings()->SetBoolProp(IMP_FBX_GOBO, false);
497
498 importStatus = importer->Import(scene);
499
500 if(!importStatus)
501 {
502 importer->Destroy();
503
504 LOGERR("FBX import failed: Call to FbxImporter::Import() failed.\n" +
505 String("Error returned: %s\n\n") + String(importer->GetStatus().GetErrorString()));
506 return false;
507 }
508
509 FbxAxisSystem fileCoordSystem = scene->GetGlobalSettings().GetAxisSystem();
510 FbxAxisSystem bsCoordSystem(FbxAxisSystem::eYAxis, FbxAxisSystem::eParityOdd, FbxAxisSystem::eRightHanded);
511 if (fileCoordSystem != bsCoordSystem)
512 bsCoordSystem.ConvertScene(scene);
513
514 importer->Destroy();
515 return true;
516 }
517
518 void FBXImporter::parseScene(FbxScene* scene, const FBXImportOptions& options, FBXImportScene& outputScene)
519 {
520 // Scale from file units to engine units, and apply optional user scale
521 float importScale = 1.0f;
522 if (options.importScale > 0.0001f)
523 importScale = options.importScale;
524
525 FbxSystemUnit units = scene->GetGlobalSettings().GetSystemUnit();
526 FbxSystemUnit bsScaledUnits(100.0f / importScale);
527
528 const FbxSystemUnit::ConversionOptions convOptions = {
529 false,
530 true,
531 true,
532 true,
533 true,
534 true
535 };
536
537 bsScaledUnits.ConvertScene(scene, convOptions);
538
539 outputScene.rootNode = createImportNode(outputScene, scene->GetRootNode(), nullptr);
540
541 Stack<FbxNode*> todo;
542 todo.push(scene->GetRootNode());
543
544 while(!todo.empty())
545 {
546 FbxNode* curNode = todo.top();
547 FBXImportNode* curImportNode = outputScene.nodeMap[curNode];
548 todo.pop();
549
550 FbxNodeAttribute* attrib = curNode->GetNodeAttribute();
551 if(attrib != nullptr)
552 {
553 FbxNodeAttribute::EType attribType = attrib->GetAttributeType();
554
555 switch(attribType)
556 {
557 case FbxNodeAttribute::eNurbs:
558 case FbxNodeAttribute::eNurbsSurface:
559 case FbxNodeAttribute::ePatch:
560 {
561 FbxGeometryConverter geomConverter(mFBXManager);
562 attrib = geomConverter.Triangulate(attrib, true);
563
564 if (attrib->GetAttributeType() == FbxNodeAttribute::eMesh)
565 {
566 FbxMesh* mesh = static_cast<FbxMesh*>(attrib);
567 mesh->RemoveBadPolygons();
568
569 parseMesh(mesh, curImportNode, options, outputScene);
570 }
571 }
572 break;
573 case FbxNodeAttribute::eMesh:
574 {
575 FbxMesh* mesh = static_cast<FbxMesh*>(attrib);
576 mesh->RemoveBadPolygons();
577
578 if(!mesh->IsTriangleMesh())
579 {
580 FbxGeometryConverter geomConverter(mFBXManager);
581 geomConverter.Triangulate(mesh, true);
582 attrib = curNode->GetNodeAttribute();
583 mesh = static_cast<FbxMesh*>(attrib);
584 }
585
586 parseMesh(mesh, curImportNode, options, outputScene);
587 }
588 break;
589 default:
590 break;
591 }
592 }
593
594 for (int i = 0; i < curNode->GetChildCount(); i++)
595 {
596 FbxNode* childNode = curNode->GetChild(i);
597 createImportNode(outputScene, childNode, curImportNode);
598
599 todo.push(childNode);
600 }
601 }
602 }
603
604 FBXImportNode* FBXImporter::createImportNode(FBXImportScene& scene, FbxNode* fbxNode, FBXImportNode* parent)
605 {
606 FBXImportNode* node = bs_new<FBXImportNode>();
607
608 Vector3 translation = FBXToNativeType(fbxNode->EvaluateLocalTranslation(FbxTime(0)));
609 Vector3 rotationEuler = FBXToNativeType(fbxNode->EvaluateLocalRotation(FbxTime(0)));
610 Vector3 scale = FBXToNativeType(fbxNode->EvaluateLocalScaling(FbxTime(0)));
611
612 Quaternion rotation((Degree)rotationEuler.x, (Degree)rotationEuler.y, (Degree)rotationEuler.z,
613 EulerAngleOrder::XYZ);
614
615 node->name = fbxNode->GetNameWithoutNameSpacePrefix().Buffer();
616 node->fbxNode = fbxNode;
617 node->localTransform = Transform(translation, rotation, scale);
618
619 if (parent != nullptr)
620 {
621 node->worldTransform = parent->worldTransform * node->localTransform.getMatrix();
622
623 parent->children.push_back(node);
624 }
625 else
626 node->worldTransform = node->localTransform.getMatrix();
627
628 // Geometry transform is applied to geometry (mesh data) only, it is not inherited by children, so we store it
629 // separately
630 Vector3 geomTrans = FBXToNativeType(fbxNode->GeometricTranslation.Get());
631 Vector3 geomRotEuler = FBXToNativeType(fbxNode->GeometricRotation.Get());
632 Vector3 geomScale = FBXToNativeType(fbxNode->GeometricScaling.Get());
633
634 Quaternion geomRotation((Degree)geomRotEuler.x, (Degree)geomRotEuler.y, (Degree)geomRotEuler.z, EulerAngleOrder::XYZ);
635 node->geomTransform = Matrix4::TRS(geomTrans, geomRotation, geomScale);
636
637 scene.nodeMap.insert(std::make_pair(fbxNode, node));
638
639 // Determine if geometry winding needs to be flipped to match the engine convention. This is true by default, but
640 // each negative scaling factor changes the winding.
641 if (parent != nullptr)
642 node->flipWinding = parent->flipWinding;
643 else
644 node->flipWinding = true;
645
646 for (UINT32 i = 0; i < 3; i++)
647 {
648 if (scale[i] < 0.0f) node->flipWinding = !node->flipWinding;
649 if (geomScale[i] < 0.0f) node->flipWinding = !node->flipWinding;
650 }
651
652 return node;
653 }
654
655 void FBXImporter::splitMeshVertices(FBXImportScene& scene)
656 {
657 Vector<FBXImportMesh*> splitMeshes;
658
659 for (auto& mesh : scene.meshes)
660 {
661 FBXImportMesh* splitMesh = bs_new<FBXImportMesh>();
662 splitMesh->fbxMesh = mesh->fbxMesh;
663 splitMesh->referencedBy = mesh->referencedBy;
664 splitMesh->bones = mesh->bones;
665
666 FBXUtility::splitVertices(*mesh, *splitMesh);
667 splitMeshes.push_back(splitMesh);
668
669 bs_delete(mesh);
670 }
671
672 scene.meshes = splitMeshes;
673 }
674
675 void FBXImporter::convertAnimations(const Vector<FBXAnimationClip>& clips, const Vector<AnimationSplitInfo>& splits,
676 const SPtr<Skeleton>& skeleton, bool importRootMotion, Vector<FBXAnimationClipData>& output)
677 {
678 UnorderedSet<String> names;
679
680 String rootBoneName;
681 if (skeleton == nullptr)
682 importRootMotion = false;
683 else
684 {
685 UINT32 rootBoneIdx = skeleton->getRootBoneIndex();
686 if (rootBoneIdx == (UINT32)-1)
687 importRootMotion = false;
688 else
689 rootBoneName = skeleton->getBoneInfo(rootBoneIdx).name;
690 }
691
692 bool isFirstClip = true;
693 for (auto& clip : clips)
694 {
695 SPtr<AnimationCurves> curves = bs_shared_ptr_new<AnimationCurves>();
696 SPtr<RootMotion> rootMotion;
697
698 // Find offset so animations start at time 0
699 float animStart = std::numeric_limits<float>::infinity();
700
701 for (auto& bone : clip.boneAnimations)
702 {
703 if(bone.translation.getNumKeyFrames() > 0)
704 animStart = std::min(bone.translation.getKeyFrame(0).time, animStart);
705
706 if (bone.rotation.getNumKeyFrames() > 0)
707 animStart = std::min(bone.rotation.getKeyFrame(0).time, animStart);
708
709 if (bone.scale.getNumKeyFrames() > 0)
710 animStart = std::min(bone.scale.getKeyFrame(0).time, animStart);
711 }
712
713 for (auto& anim : clip.blendShapeAnimations)
714 {
715 if (anim.curve.getNumKeyFrames() > 0)
716 animStart = std::min(anim.curve.getKeyFrame(0).time, animStart);
717 }
718
719 AnimationCurveFlags blendShapeFlags = AnimationCurveFlag::ImportedCurve | AnimationCurveFlag::MorphFrame;
720 if (animStart != 0.0f && animStart != std::numeric_limits<float>::infinity())
721 {
722 for (auto& bone : clip.boneAnimations)
723 {
724 TAnimationCurve<Vector3> translation = AnimationUtility::offsetCurve(bone.translation, -animStart);
725 TAnimationCurve<Quaternion> rotation = AnimationUtility::offsetCurve(bone.rotation, -animStart);
726 TAnimationCurve<Vector3> scale = AnimationUtility::offsetCurve(bone.scale, -animStart);
727
728 if(importRootMotion && bone.node->name == rootBoneName)
729 rootMotion = bs_shared_ptr_new<RootMotion>(translation, rotation);
730 else
731 {
732 curves->position.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, translation });
733 curves->rotation.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, rotation });
734 curves->scale.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, scale });
735 }
736 }
737
738 for (auto& anim : clip.blendShapeAnimations)
739 {
740 TAnimationCurve<float> curve = AnimationUtility::offsetCurve(anim.curve, -animStart);
741 curves->generic.push_back({ anim.blendShape, blendShapeFlags, curve });
742 }
743 }
744 else
745 {
746 for (auto& bone : clip.boneAnimations)
747 {
748 if (importRootMotion && bone.node->name == rootBoneName)
749 rootMotion = bs_shared_ptr_new<RootMotion>(bone.translation, bone.rotation);
750 else
751 {
752 curves->position.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.translation });
753 curves->rotation.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.rotation });
754 curves->scale.push_back({ bone.node->name, AnimationCurveFlag::ImportedCurve, bone.scale });
755 }
756 }
757
758 for (auto& anim : clip.blendShapeAnimations)
759 curves->generic.push_back({ anim.blendShape, blendShapeFlags, anim.curve });
760 }
761
762 // See if any splits are required. We only split the first clip as it is assumed if FBX has multiple clips the
763 // user has the ability to split them externally.
764 if(isFirstClip && !splits.empty())
765 {
766 float secondsPerFrame = 1.0f / clip.sampleRate;
767
768 for(auto& split : splits)
769 {
770 SPtr<AnimationCurves> splitClipCurve = bs_shared_ptr_new<AnimationCurves>();
771 SPtr<RootMotion> splitRootMotion;
772
773 auto splitCurves = [&](auto& inCurves, auto& outCurves)
774 {
775 UINT32 numCurves = (UINT32)inCurves.size();
776 outCurves.resize(numCurves);
777
778 for (UINT32 i = 0; i < numCurves; i++)
779 {
780 auto& animCurve = inCurves[i].curve;
781 outCurves[i].name = inCurves[i].name;
782
783 UINT32 numFrames = animCurve.getNumKeyFrames();
784 if (numFrames == 0)
785 continue;
786
787 float startTime = split.startFrame * secondsPerFrame;
788 float endTime = split.endFrame * secondsPerFrame;
789
790 outCurves[i].curve = inCurves[i].curve.split(startTime, endTime);
791
792 if (split.isAdditive)
793 outCurves[i].curve.makeAdditive();
794 }
795 };
796
797 splitCurves(curves->position, splitClipCurve->position);
798 splitCurves(curves->rotation, splitClipCurve->rotation);
799 splitCurves(curves->scale, splitClipCurve->scale);
800 splitCurves(curves->generic, splitClipCurve->generic);
801
802 if(rootMotion != nullptr)
803 {
804 auto splitCurve = [&](auto& inCurve, auto& outCurve)
805 {
806 UINT32 numFrames = inCurve.getNumKeyFrames();
807 if (numFrames > 0)
808 {
809 float startTime = split.startFrame * secondsPerFrame;
810 float endTime = split.endFrame * secondsPerFrame;
811
812 outCurve = inCurve.split(startTime, endTime);
813
814 if (split.isAdditive)
815 outCurve.makeAdditive();
816 }
817 };
818
819 splitRootMotion = bs_shared_ptr_new<RootMotion>();
820 splitCurve(rootMotion->position, splitRootMotion->position);
821 splitCurve(rootMotion->rotation, splitRootMotion->rotation);
822 }
823
824 // Search for a unique name
825 String name = split.name;
826 UINT32 attemptIdx = 0;
827 while (names.find(name) != names.end())
828 {
829 name = clip.name + "_" + toString(attemptIdx);
830 attemptIdx++;
831 }
832
833 names.insert(name);
834 output.push_back(FBXAnimationClipData(name, split.isAdditive, clip.sampleRate, splitClipCurve,
835 splitRootMotion));
836 }
837 }
838 else
839 {
840 // Search for a unique name
841 String name = clip.name;
842 UINT32 attemptIdx = 0;
843 while(names.find(name) != names.end())
844 {
845 name = clip.name + "_" + toString(attemptIdx);
846 attemptIdx++;
847 }
848
849 names.insert(name);
850 output.push_back(FBXAnimationClipData(name, false, clip.sampleRate, curves, rootMotion));
851 }
852
853 isFirstClip = false;
854 }
855 }
856
857 SPtr<RendererMeshData> FBXImporter::generateMeshData(const FBXImportScene& scene, const FBXImportOptions& options,
858 Vector<SubMesh>& outputSubMeshes)
859 {
860 Vector<SPtr<MeshData>> allMeshData;
861 Vector<Vector<SubMesh>> allSubMeshes;
862 UINT32 boneIndexOffset = 0;
863
864 // Generate unique indices for all the bones. This is mirrored in createSkeleton().
865 UnorderedMap<FBXImportNode*, UINT32> boneMap;
866 for (auto& mesh : scene.meshes)
867 {
868 // Create bones
869 for (auto& fbxBone : mesh->bones)
870 {
871 UINT32 boneIdx = (UINT32)boneMap.size();
872
873 auto iterFind = boneMap.find(fbxBone.node);
874 if(iterFind != boneMap.end())
875 continue; // Duplicate
876
877 boneMap[fbxBone.node] = boneIdx;
878 }
879 }
880
881 for (auto& mesh : scene.meshes)
882 {
883 Vector<Vector<UINT32>> indicesPerMaterial;
884 for (UINT32 i = 0; i < (UINT32)mesh->indices.size(); i++)
885 {
886 while ((UINT32)mesh->materials[i] >= (UINT32)indicesPerMaterial.size())
887 indicesPerMaterial.push_back(Vector<UINT32>());
888
889 indicesPerMaterial[mesh->materials[i]].push_back(mesh->indices[i]);
890 }
891
892 UINT32* orderedIndices = (UINT32*)bs_alloc((UINT32)mesh->indices.size() * sizeof(UINT32));
893 Vector<SubMesh> subMeshes;
894 UINT32 currentIndex = 0;
895
896 for (auto& subMeshIndices : indicesPerMaterial)
897 {
898 UINT32 indexCount = (UINT32)subMeshIndices.size();
899 UINT32* dest = orderedIndices + currentIndex;
900 memcpy(dest, subMeshIndices.data(), indexCount * sizeof(UINT32));
901
902 subMeshes.push_back(SubMesh(currentIndex, indexCount, DOT_TRIANGLE_LIST));
903
904 currentIndex += indexCount;
905 }
906
907 UINT32 vertexLayout = (UINT32)VertexLayout::Position;
908
909 size_t numVertices = mesh->positions.size();
910 bool hasColors = mesh->colors.size() == numVertices;
911 bool hasNormals = mesh->normals.size() == numVertices;
912 bool hasBoneInfluences = mesh->boneInfluences.size() == numVertices;
913
914 if (hasColors)
915 vertexLayout |= (UINT32)VertexLayout::Color;
916
917 bool hasTangents = false;
918 if (hasNormals)
919 {
920 vertexLayout |= (UINT32)VertexLayout::Normal;
921
922 if (mesh->tangents.size() == numVertices &&
923 mesh->bitangents.size() == numVertices)
924 {
925 vertexLayout |= (UINT32)VertexLayout::Tangent;
926 hasTangents = true;
927 }
928 }
929
930 if (hasBoneInfluences)
931 vertexLayout |= (UINT32)VertexLayout::BoneWeights;
932
933 for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++)
934 {
935 if (mesh->UV[i].size() == numVertices)
936 {
937 if (i == 0)
938 vertexLayout |= (UINT32)VertexLayout::UV0;
939 else if (i == 1)
940 vertexLayout |= (UINT32)VertexLayout::UV1;
941 }
942 }
943
944 UINT32 numIndices = (UINT32)mesh->indices.size();
945 for (auto& node : mesh->referencedBy)
946 {
947 Matrix4 worldTransform = node->worldTransform * node->geomTransform;
948 Matrix4 worldTransformIT = worldTransform.inverse();
949 worldTransformIT = worldTransformIT.transpose();
950
951 SPtr<RendererMeshData> meshData = RendererMeshData::create((UINT32)numVertices, numIndices, (VertexLayout)vertexLayout);
952
953 // Copy indices
954 if(!node->flipWinding)
955 meshData->setIndices(orderedIndices, numIndices * sizeof(UINT32));
956 else
957 {
958 UINT32* flippedIndices = bs_stack_alloc<UINT32>(numIndices);
959
960 for (UINT32 i = 0; i < numIndices; i += 3)
961 {
962 flippedIndices[i + 0] = orderedIndices[i + 0];
963 flippedIndices[i + 1] = orderedIndices[i + 2];
964 flippedIndices[i + 2] = orderedIndices[i + 1];
965 }
966
967 meshData->setIndices(flippedIndices, numIndices * sizeof(UINT32));
968 bs_stack_free(flippedIndices);
969 }
970
971 // Copy & transform positions
972 UINT32 positionsSize = sizeof(Vector3) * (UINT32)numVertices;
973 Vector3* transformedPositions = (Vector3*)bs_stack_alloc(positionsSize);
974
975 for (UINT32 i = 0; i < (UINT32)numVertices; i++)
976 transformedPositions[i] = worldTransform.multiplyAffine((Vector3)mesh->positions[i]);
977
978 meshData->setPositions(transformedPositions, positionsSize);
979 bs_stack_free(transformedPositions);
980
981 // Copy & transform normals
982 if (hasNormals)
983 {
984 UINT32 normalsSize = sizeof(Vector3) * (UINT32)numVertices;
985 Vector3* transformedNormals = (Vector3*)bs_stack_alloc(normalsSize);
986
987 // Copy, convert & transform tangents & bitangents
988 if (hasTangents)
989 {
990 UINT32 tangentsSize = sizeof(Vector4) * (UINT32)numVertices;
991 Vector4* transformedTangents = (Vector4*)bs_stack_alloc(tangentsSize);
992
993 for (UINT32 i = 0; i < (UINT32)numVertices; i++)
994 {
995 Vector3 normal = (Vector3)mesh->normals[i];
996 normal = worldTransformIT.multiplyDirection(normal);
997 transformedNormals[i] = Vector3::normalize(normal);
998
999 Vector3 tangent = (Vector3)mesh->tangents[i];
1000 tangent = Vector3::normalize(worldTransformIT.multiplyDirection(tangent));
1001
1002 Vector3 bitangent = (Vector3)mesh->bitangents[i];
1003 bitangent = worldTransformIT.multiplyDirection(bitangent);
1004
1005 Vector3 engineBitangent = Vector3::cross(normal, tangent);
1006 float sign = Vector3::dot(engineBitangent, bitangent);
1007
1008 transformedTangents[i] = Vector4(tangent.x, tangent.y, tangent.z, sign > 0 ? 1.0f : -1.0f);
1009 }
1010
1011 meshData->setTangents(transformedTangents, tangentsSize);
1012 bs_stack_free(transformedTangents);
1013 }
1014 else // Just normals
1015 {
1016 for (UINT32 i = 0; i < (UINT32)numVertices; i++)
1017 transformedNormals[i] = Vector3::normalize(worldTransformIT.multiplyDirection((Vector3)mesh->normals[i]));
1018 }
1019
1020 meshData->setNormals(transformedNormals, normalsSize);
1021 bs_stack_free(transformedNormals);
1022 }
1023
1024 // Copy colors
1025 if (hasColors)
1026 {
1027 meshData->setColors(mesh->colors.data(), sizeof(UINT32) * (UINT32)numVertices);
1028 }
1029
1030 // Copy UV
1031 int writeUVIDx = 0;
1032 for (auto& uvLayer : mesh->UV)
1033 {
1034 if (uvLayer.size() == numVertices)
1035 {
1036 UINT32 size = sizeof(Vector2) * (UINT32)numVertices;
1037 Vector2* transformedUV = (Vector2*)bs_stack_alloc(size);
1038
1039 UINT32 i = 0;
1040 for (auto& uv : uvLayer)
1041 {
1042 transformedUV[i] = uv;
1043 transformedUV[i].y = 1.0f - uv.y;
1044
1045 i++;
1046 }
1047
1048 if (writeUVIDx == 0)
1049 meshData->setUV0(transformedUV, size);
1050 else if (writeUVIDx == 1)
1051 meshData->setUV1(transformedUV, size);
1052
1053 bs_stack_free(transformedUV);
1054
1055 writeUVIDx++;
1056 }
1057 }
1058
1059 // Copy bone influences & remap bone indices
1060 if(hasBoneInfluences)
1061 {
1062 UINT32 bufferSize = sizeof(BoneWeight) * (UINT32)numVertices;
1063 BoneWeight* weights = (BoneWeight*)bs_stack_alloc(bufferSize);
1064 for(UINT32 i = 0; i < (UINT32)numVertices; i++)
1065 {
1066 int* indices[] = { &weights[i].index0, &weights[i].index1, &weights[i].index2, &weights[i].index3};
1067 float* amounts[] = { &weights[i].weight0, &weights[i].weight1, &weights[i].weight2, &weights[i].weight3};
1068
1069 for(UINT32 j = 0; j < 4; j++)
1070 {
1071 int boneIdx = mesh->boneInfluences[i].indices[j];
1072 if(boneIdx != -1)
1073 {
1074 FBXImportNode* boneNode = mesh->bones[boneIdx].node;
1075
1076 auto iterFind = boneMap.find(boneNode);
1077 if(iterFind != boneMap.end())
1078 *indices[j] = iterFind->second;
1079 else
1080 *indices[j] = -1;
1081 }
1082 else
1083 *indices[j] = boneIdx;
1084
1085 *amounts[j] = mesh->boneInfluences[i].weights[j];
1086 }
1087 }
1088
1089 meshData->setBoneWeights(weights, bufferSize);
1090 bs_stack_free(weights);
1091 }
1092
1093 allMeshData.push_back(meshData->getData());
1094 allSubMeshes.push_back(subMeshes);
1095 }
1096
1097 bs_free(orderedIndices);
1098
1099 UINT32 numBones = (UINT32)mesh->bones.size();
1100 boneIndexOffset += numBones;
1101 }
1102
1103 if (allMeshData.size() > 1)
1104 {
1105 return RendererMeshData::create(MeshData::combine(allMeshData, allSubMeshes, outputSubMeshes));
1106 }
1107 else if (allMeshData.size() == 1)
1108 {
1109 outputSubMeshes = allSubMeshes[0];
1110 return RendererMeshData::create(allMeshData[0]);
1111 }
1112
1113 return nullptr;
1114 }
1115
1116 template<class TFBX, class TNative>
1117 class FBXDirectIndexer
1118 {
1119 public:
1120 FBXDirectIndexer(const FbxLayerElementTemplate<TFBX>& layer)
1121 :mElementArray(layer.GetDirectArray()),
1122 mElementCount(mElementArray.GetCount())
1123 {}
1124
1125 bool get(int index, TNative& output) const
1126 {
1127 if (index < 0 || index >= mElementCount)
1128 return false;
1129
1130 output = FBXToNativeType(mElementArray.GetAt(index));
1131 return true;
1132 }
1133
1134 bool isEmpty() const
1135 {
1136 return mElementCount == 0;
1137 }
1138
1139 private:
1140 const FbxLayerElementArrayTemplate<TFBX>& mElementArray;
1141 int mElementCount;
1142 };
1143
1144 template<class TFBX, class TNative>
1145 class FBXIndexIndexer
1146 {
1147 public:
1148 FBXIndexIndexer(const FbxLayerElementTemplate<TFBX>& layer)
1149 :mElementArray(layer.GetDirectArray()),
1150 mIndexArray(layer.GetIndexArray()),
1151 mElementCount(mElementArray.GetCount()),
1152 mIndexCount(mIndexArray.GetCount())
1153 {}
1154
1155 bool get(int index, TNative& output) const
1156 {
1157 if (index < 0 || index >= mIndexCount)
1158 return false;
1159
1160 int actualIndex = mIndexArray.GetAt(index);
1161
1162 if (actualIndex < 0 || actualIndex >= mElementCount)
1163 return false;
1164
1165 output = FBXToNativeType(mElementArray.GetAt(actualIndex));
1166 return true;
1167 }
1168
1169 bool isEmpty() const
1170 {
1171 return mElementCount == 0 || mIndexCount == 0;
1172 }
1173
1174 private:
1175 const FbxLayerElementArrayTemplate<TFBX>& mElementArray;
1176 const FbxLayerElementArrayTemplate<int>& mIndexArray;
1177 int mElementCount;
1178 int mIndexCount;
1179 };
1180
1181 template<class TFBX, class TNative, class TIndexer>
1182 void readLayerData(FbxLayerElementTemplate<TFBX>& layer, Vector<TNative>& output, const Vector<int>& indices)
1183 {
1184 TIndexer indexer(layer);
1185 if (indexer.isEmpty())
1186 return;
1187
1188 output.resize(indices.size());
1189
1190 FbxLayerElement::EMappingMode mappingMode = layer.GetMappingMode();
1191
1192 UINT32 indexCount = (UINT32)indices.size();
1193 switch (mappingMode)
1194 {
1195 case FbxLayerElement::eByControlPoint:
1196 for (UINT32 i = 0; i < indexCount; i++)
1197 {
1198 int index = indices[i];
1199 indexer.get(index, output[i]);
1200 }
1201 break;
1202 case FbxLayerElement::eByPolygonVertex:
1203 for (UINT32 i = 0; i < indexCount; i++)
1204 indexer.get(i, output[i]);
1205 break;
1206 case FbxLayerElement::eByPolygon:
1207 // We expect mesh to be triangulated here
1208 {
1209 UINT32 polygonCount = indexCount / 3;
1210 UINT32 index = 0;
1211
1212 for (UINT32 i = 0; i < polygonCount; i++)
1213 {
1214 TNative value{};
1215 indexer.get(i, value);
1216
1217 output[index++] = value;
1218 output[index++] = value;
1219 output[index++] = value;
1220 }
1221 }
1222 break;
1223 case FbxLayerElement::eAllSame:
1224 {
1225 TNative value{};
1226 indexer.get(0, value);
1227
1228 for (UINT32 i = 0; i < indexCount; i++)
1229 output[i] = value;
1230 }
1231 break;
1232 default:
1233 LOGWRN("FBX Import: Unsupported layer mapping mode.");
1234 break;
1235 }
1236 }
1237
1238 template<class TFBX, class TNative>
1239 void readLayerData(FbxLayerElementTemplate<TFBX>& layer, Vector<TNative>& output, const Vector<int>& indices)
1240 {
1241 FbxLayerElement::EReferenceMode refMode = layer.GetReferenceMode();
1242
1243 if (refMode == FbxLayerElement::eDirect)
1244 readLayerData<TFBX, TNative, FBXDirectIndexer<TFBX, TNative> >(layer, output, indices);
1245 else if (refMode == FbxLayerElement::eIndexToDirect)
1246 readLayerData<TFBX, TNative, FBXIndexIndexer<TFBX, TNative> >(layer, output, indices);
1247 else
1248 LOGWRN("FBX Import: Unsupported layer reference mode.");
1249 }
1250
1251 void FBXImporter::parseMesh(FbxMesh* mesh, FBXImportNode* parentNode, const FBXImportOptions& options, FBXImportScene& outputScene)
1252 {
1253 // Check if valid
1254 if (!mesh->IsTriangleMesh())
1255 return;
1256
1257 UINT32 vertexCount = mesh->GetControlPointsCount();
1258 UINT32 triangleCount = mesh->GetPolygonCount();
1259
1260 if (vertexCount == 0 || triangleCount == 0)
1261 return;
1262
1263 // Register in global mesh array
1264 FBXImportMesh* importMesh = nullptr;
1265
1266 auto iterFindMesh = outputScene.meshMap.find(mesh);
1267 if (iterFindMesh != outputScene.meshMap.end())
1268 {
1269 UINT32 meshIdx = iterFindMesh->second;
1270 outputScene.meshes[meshIdx]->referencedBy.push_back(parentNode);
1271
1272 return;
1273 }
1274 else
1275 {
1276 importMesh = bs_new<FBXImportMesh>();
1277 outputScene.meshes.push_back(importMesh);
1278
1279 importMesh->referencedBy.push_back(parentNode);
1280 importMesh->fbxMesh = mesh;
1281
1282 outputScene.meshMap[mesh] = (UINT32)outputScene.meshes.size() - 1;
1283 }
1284
1285 // Import vertices
1286 importMesh->positions.resize(vertexCount);
1287 FbxVector4* controlPoints = mesh->GetControlPoints();
1288
1289 for (UINT32 i = 0; i < vertexCount; i++)
1290 importMesh->positions[i] = FBXToNativeType(controlPoints[i]);
1291
1292 // Import triangles
1293 UINT32 indexCount = triangleCount * 3;
1294 importMesh->indices.resize(indexCount);
1295
1296 int* fbxIndices = mesh->GetPolygonVertices();
1297 importMesh->indices.assign(fbxIndices, fbxIndices + indexCount);
1298
1299 // Import UVs
1300 Vector<FbxLayerElementUV*> fbxUVLayers;
1301
1302 //// Search the diffuse layers first
1303 for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++)
1304 {
1305 FbxLayer* layer = mesh->GetLayer(i, FbxLayerElement::eUV);
1306 if (layer == nullptr)
1307 continue;
1308
1309 for (int j = FbxLayerElement::eTextureDiffuse; j < FbxLayerElement::eTypeCount; j++)
1310 {
1311 FbxLayerElementUV* uvLayer = layer->GetUVs((FbxLayerElement::EType)j);
1312 if (uvLayer == nullptr)
1313 continue;
1314
1315 fbxUVLayers.push_back(uvLayer);
1316
1317 if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
1318 break;
1319 }
1320
1321 if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
1322 break;
1323 }
1324
1325 //// If there's room, search all others too
1326 if (fbxUVLayers.size() < FBX_IMPORT_MAX_UV_LAYERS)
1327 {
1328 UINT32 numLayers = mesh->GetLayerCount();
1329 for (UINT32 i = 0; i < numLayers; i++)
1330 {
1331 FbxLayer* layer = mesh->GetLayer(i);
1332 if (layer == nullptr)
1333 continue;
1334
1335 for (int j = FbxLayerElement::eTextureDiffuse; j < FbxLayerElement::eTypeCount; j++)
1336 {
1337 FbxLayerElementUV* uvLayer = layer->GetUVs((FbxLayerElement::EType)j);
1338 if (uvLayer == nullptr)
1339 continue;
1340
1341 auto iterFind = std::find(fbxUVLayers.begin(), fbxUVLayers.end(), uvLayer);
1342 if (iterFind != fbxUVLayers.end())
1343 continue;
1344
1345 fbxUVLayers.push_back(uvLayer);
1346
1347 if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
1348 break;
1349 }
1350
1351 if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
1352 break;
1353 }
1354 }
1355
1356 for (size_t i = 0; i < fbxUVLayers.size(); i++)
1357 readLayerData(*fbxUVLayers[i], importMesh->UV[i], importMesh->indices);
1358
1359 FbxLayer* mainLayer = mesh->GetLayer(0);
1360 if (mainLayer != nullptr)
1361 {
1362 // Import colors
1363 if (mainLayer->GetVertexColors() != nullptr)
1364 readLayerData(*mainLayer->GetVertexColors(), importMesh->colors, importMesh->indices);
1365
1366 // Import normals
1367 if (options.importNormals)
1368 {
1369 bool hasNormals = mainLayer->GetNormals() != nullptr;
1370
1371 if (!hasNormals)
1372 {
1373 if (mainLayer->GetSmoothing() != nullptr)
1374 {
1375 FbxLayerElementSmoothing* smoothing = mainLayer->GetSmoothing();
1376
1377 if (smoothing->GetMappingMode() == FbxLayerElement::eByEdge)
1378 {
1379 FbxGeometryConverter converter(mFBXManager);
1380 converter.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0);
1381 }
1382
1383 readLayerData(*smoothing, importMesh->smoothingGroups, importMesh->indices);
1384
1385 if (!importMesh->smoothingGroups.empty())
1386 {
1387 FBXUtility::normalsFromSmoothing(importMesh->positions, importMesh->indices,
1388 importMesh->smoothingGroups, importMesh->normals);
1389 }
1390 }
1391 }
1392 else
1393 readLayerData(*mainLayer->GetNormals(), importMesh->normals, importMesh->indices);
1394 }
1395
1396 // Import tangents
1397 if (options.importTangents)
1398 {
1399 bool hasTangents = mainLayer->GetTangents() != nullptr && mainLayer->GetBinormals() != nullptr;
1400
1401 if (!hasTangents)
1402 {
1403 if (fbxUVLayers.size() > 0)
1404 hasTangents = mesh->GenerateTangentsData(0, false);
1405 }
1406
1407 if (hasTangents)
1408 {
1409 readLayerData(*mainLayer->GetTangents(), importMesh->tangents, importMesh->indices);
1410 readLayerData(*mainLayer->GetBinormals(), importMesh->bitangents, importMesh->indices);
1411 }
1412 }
1413
1414 // Import material indexes
1415 if (mainLayer->GetMaterials() != nullptr)
1416 {
1417 Vector<FbxSurfaceMaterial*> fbxMaterials;
1418
1419 readLayerData(*mainLayer->GetMaterials(), fbxMaterials, importMesh->indices);
1420
1421 UnorderedMap<FbxSurfaceMaterial*, int> materialLookup;
1422 int nextMaterialIdx = 0;
1423 for (UINT32 i = 0; i < (UINT32)fbxMaterials.size(); i++)
1424 {
1425 auto iterFind = materialLookup.find(fbxMaterials[i]);
1426
1427 int materialIdx = 0;
1428 if (iterFind != materialLookup.end())
1429 materialIdx = iterFind->second;
1430 else
1431 {
1432 materialIdx = nextMaterialIdx++;
1433 materialLookup[fbxMaterials[i]] = materialIdx;
1434 }
1435
1436 importMesh->materials.push_back(materialIdx);
1437 }
1438 }
1439 else
1440 {
1441 importMesh->materials.resize(importMesh->indices.size(), 0);
1442 }
1443 }
1444 }
1445
1446 void FBXImporter::importBlendShapes(FBXImportScene& scene, const FBXImportOptions& options)
1447 {
1448 for (auto& mesh : scene.meshes)
1449 {
1450 FbxMesh* fbxMesh = mesh->fbxMesh;
1451
1452 UINT32 deformerCount = (UINT32)fbxMesh->GetDeformerCount(FbxDeformer::eBlendShape);
1453 for (UINT32 i = 0; i < deformerCount; i++)
1454 {
1455 FbxBlendShape* deformer = static_cast<FbxBlendShape*>(fbxMesh->GetDeformer(i, FbxDeformer::eBlendShape));
1456
1457 UINT32 blendShapeChannelCount = (UINT32)deformer->GetBlendShapeChannelCount();
1458 for (UINT32 j = 0; j < blendShapeChannelCount; ++j)
1459 {
1460 FbxBlendShapeChannel* channel = deformer->GetBlendShapeChannel(j);
1461 double* weights = channel->GetTargetShapeFullWeights();
1462
1463 UINT32 frameCount = channel->GetTargetShapeCount();
1464 if (frameCount == 0)
1465 continue;
1466
1467 mesh->blendShapes.push_back(FBXBlendShape());
1468 FBXBlendShape& blendShape = mesh->blendShapes.back();
1469 blendShape.name = channel->GetName();
1470 blendShape.frames.resize(frameCount);
1471
1472 // Get name without invalid characters
1473 blendShape.name = StringUtil::replaceAll(blendShape.name, ".", "_");
1474 blendShape.name = StringUtil::replaceAll(blendShape.name, "/", "_");
1475
1476 for (UINT32 k = 0; k < frameCount; k++)
1477 {
1478 FbxShape* fbxShape = channel->GetTargetShape(k);
1479
1480 FBXBlendShapeFrame& frame = blendShape.frames[k];
1481 frame.name = fbxShape->GetName();
1482 frame.weight = (float)(weights[k] / 100.0);
1483
1484 // Get name without invalid characters
1485 frame.name = StringUtil::replaceAll(frame.name, ".", "_");
1486 frame.name = StringUtil::replaceAll(frame.name, "/", "_");
1487
1488 importBlendShapeFrame(fbxShape, *mesh, options, frame);
1489 }
1490 }
1491 }
1492 }
1493 }
1494
1495 void FBXImporter::importBlendShapeFrame(FbxShape* shape, const FBXImportMesh& mesh, const FBXImportOptions& options, FBXBlendShapeFrame& outFrame)
1496 {
1497 UINT32 vertexCount = (UINT32)shape->GetControlPointsCount();
1498 outFrame.positions.resize(vertexCount);
1499 FbxVector4* controlPoints = shape->GetControlPoints();
1500
1501 for (UINT32 i = 0; i < vertexCount; i++)
1502 outFrame.positions[i] = FBXToNativeType(controlPoints[i]);
1503
1504 FbxLayer* mainLayer = shape->GetLayer(0);
1505 if (options.importNormals)
1506 {
1507 bool hasNormals = mainLayer->GetNormals() != nullptr;
1508
1509 if (!hasNormals)
1510 {
1511 if (!mesh.smoothingGroups.empty())
1512 {
1513 FBXUtility::normalsFromSmoothing(outFrame.positions, mesh.indices,
1514 mesh.smoothingGroups, outFrame.normals);
1515 }
1516 }
1517 else
1518 readLayerData(*mainLayer->GetNormals(), outFrame.normals, mesh.indices);
1519 }
1520
1521 if (options.importTangents)
1522 {
1523 bool hasTangents = mainLayer->GetTangents() != nullptr && mainLayer->GetBinormals() != nullptr;
1524
1525 if (hasTangents)
1526 {
1527 readLayerData(*mainLayer->GetTangents(), outFrame.tangents, mesh.indices);
1528 readLayerData(*mainLayer->GetBinormals(), outFrame.bitangents, mesh.indices);
1529 }
1530 }
1531 }
1532
1533 void FBXImporter::importSkin(FBXImportScene& scene, const FBXImportOptions& options)
1534 {
1535 for (auto& mesh : scene.meshes)
1536 {
1537 FbxMesh* fbxMesh = mesh->fbxMesh;
1538
1539 UINT32 deformerCount = (UINT32)fbxMesh->GetDeformerCount(FbxDeformer::eSkin);
1540 if (deformerCount > 0)
1541 {
1542 // We ignore other deformers if there's more than one
1543 FbxSkin* deformer = static_cast<FbxSkin*>(fbxMesh->GetDeformer(0, FbxDeformer::eSkin));
1544 UINT32 boneCount = (UINT32)deformer->GetClusterCount();
1545
1546 if (boneCount == 0)
1547 continue;
1548
1549 // If only one bone and it links to itself, ignore the bone
1550 if (boneCount == 1)
1551 {
1552 FbxCluster* cluster = deformer->GetCluster(0);
1553 if (mesh->referencedBy.size() == 1 && mesh->referencedBy[0]->fbxNode == cluster->GetLink())
1554 continue;
1555 }
1556
1557 importSkin(scene, deformer, *mesh, options);
1558 }
1559 }
1560 }
1561
1562 void FBXImporter::importSkin(FBXImportScene& scene, FbxSkin* skin, FBXImportMesh& mesh, const FBXImportOptions& options)
1563 {
1564 Vector<FBXBoneInfluence>& influences = mesh.boneInfluences;
1565 influences.resize(mesh.positions.size());
1566
1567 UnorderedSet<FbxNode*> existingBones;
1568 UINT32 boneCount = (UINT32)skin->GetClusterCount();
1569 for (UINT32 i = 0; i < boneCount; i++)
1570 {
1571 FbxCluster* cluster = skin->GetCluster(i);
1572 FbxNode* link = cluster->GetLink();
1573
1574 // The bone node doesn't exist, skip it
1575 auto iterFind = scene.nodeMap.find(link);
1576 if (iterFind == scene.nodeMap.end())
1577 continue;
1578
1579 mesh.bones.push_back(FBXBone());
1580
1581 FBXBone& bone = mesh.bones.back();
1582 bone.node = iterFind->second;
1583
1584 if(mesh.referencedBy.size() > 1)
1585 {
1586 // Note: If this becomes a relevant issue (unlikely), then I will have to duplicate skeleton bones for
1587 // each such mesh, since they will all require their own bind poses. Animation curves will also need to be
1588 // handled specially (likely by allowing them to be applied to multiple bones at once). The other option is
1589 // not to bake the node transform into mesh vertices and handle it on a Scene Object level.
1590 LOGWRN("Skinned mesh has multiple different instances. This is not supported.");
1591 }
1592
1593 FBXImportNode* parentNode = mesh.referencedBy[0];
1594
1595 // Calculate bind pose
1596 //// Grab the transform of the node linked to this cluster (should be equivalent to bone.node->worldTransform)
1597 FbxAMatrix linkTransform;
1598 cluster->GetTransformLinkMatrix(linkTransform);
1599
1600 FbxAMatrix clusterTransform;
1601 cluster->GetTransformMatrix(clusterTransform);
1602
1603 bone.localTfrm = bone.node->localTransform;
1604
1605 FbxAMatrix invLinkTransform = linkTransform.Inverse();
1606 bone.bindPose = FBXToNativeType(invLinkTransform * clusterTransform);
1607
1608 // Undo the transform we baked into the mesh
1609 bone.bindPose = bone.bindPose * (parentNode->worldTransform * parentNode->geomTransform).inverseAffine();
1610
1611 bool isDuplicate = !existingBones.insert(link).second;
1612 bool isAdditive = cluster->GetLinkMode() == FbxCluster::eAdditive;
1613
1614 // We avoid importing weights twice for duplicate bones and we don't
1615 // support additive link mode.
1616 bool importWeights = !isDuplicate && !isAdditive;
1617 if (!importWeights)
1618 continue;
1619
1620 double* weights = cluster->GetControlPointWeights();
1621 INT32* indices = cluster->GetControlPointIndices();
1622 UINT32 numIndices = (UINT32)cluster->GetControlPointIndicesCount();
1623 INT32 numVertices = (INT32)influences.size();
1624
1625 // Add new weights while keeping them in order and removing the smallest ones
1626 // if number of influences exceeds the set maximum value
1627 for (UINT32 j = 0; j < numIndices; j++)
1628 {
1629 INT32 vertexIndex = indices[j];
1630 float weight = (float)weights[j];
1631
1632 for (INT32 k = 0; k < FBX_IMPORT_MAX_BONE_INFLUENCES; k++)
1633 {
1634 if (vertexIndex < 0 || vertexIndex >= numVertices)
1635 continue;
1636
1637 if (weight >= influences[vertexIndex].weights[k])
1638 {
1639 for (INT32 l = FBX_IMPORT_MAX_BONE_INFLUENCES - 2; l >= k; l--)
1640 {
1641 influences[vertexIndex].weights[l + 1] = influences[vertexIndex].weights[l];
1642 influences[vertexIndex].indices[l + 1] = influences[vertexIndex].indices[l];
1643 }
1644
1645 influences[vertexIndex].weights[k] = weight;
1646 influences[vertexIndex].indices[k] = i;
1647 break;
1648 }
1649 }
1650 }
1651 }
1652
1653 if (mesh.bones.empty())
1654 mesh.boneInfluences.clear();
1655
1656 UINT32 numBones = (UINT32)mesh.bones.size();
1657 if (numBones > 256)
1658 LOGWRN("A maximum of 256 bones per skeleton are supported. Imported skeleton has " + toString(numBones) + " bones");
1659
1660 // Normalize weights
1661 UINT32 numInfluences = (UINT32)mesh.boneInfluences.size();
1662 for (UINT32 i = 0; i < numInfluences; i++)
1663 {
1664 float sum = 0.0f;
1665 for (UINT32 j = 0; j < FBX_IMPORT_MAX_BONE_INFLUENCES; j++)
1666 sum += influences[i].weights[j];
1667
1668 float invSum = 1.0f / sum;
1669 for (UINT32 j = 0; j < FBX_IMPORT_MAX_BONE_INFLUENCES; j++)
1670 influences[i].weights[j] *= invSum;
1671 }
1672 }
1673
1674 void FBXImporter::generateMissingTangentSpace(FBXImportScene& scene, const FBXImportOptions& options)
1675 {
1676 for (auto& mesh : scene.meshes)
1677 {
1678 UINT32 numVertices = (UINT32)mesh->positions.size();
1679 UINT32 numIndices = (UINT32)mesh->indices.size();
1680
1681 if ((options.importNormals || options.importTangents) && mesh->normals.empty())
1682 {
1683 mesh->normals.resize(numVertices);
1684
1685 MeshUtility::calculateNormals(mesh->positions.data(), (UINT8*)mesh->indices.data(), numVertices, numIndices, mesh->normals.data());
1686 }
1687
1688 if (options.importTangents && !mesh->UV[0].empty() && (mesh->tangents.empty() || mesh->bitangents.empty()))
1689 {
1690 mesh->tangents.resize(numVertices);
1691 mesh->bitangents.resize(numVertices);
1692
1693 MeshUtility::calculateTangents(mesh->positions.data(), mesh->normals.data(), mesh->UV[0].data(), (UINT8*)mesh->indices.data(),
1694 numVertices, numIndices, mesh->tangents.data(), mesh->bitangents.data());
1695 }
1696
1697 for (auto& shape : mesh->blendShapes)
1698 {
1699 for (auto& frame : shape.frames)
1700 {
1701 if ((options.importNormals || options.importTangents) && frame.normals.empty())
1702 {
1703 frame.normals.resize(numVertices);
1704
1705 MeshUtility::calculateNormals(mesh->positions.data(), (UINT8*)mesh->indices.data(), numVertices, numIndices, frame.normals.data());
1706 }
1707
1708 if (options.importTangents && !mesh->UV[0].empty() && (frame.tangents.empty() || frame.bitangents.empty()))
1709 {
1710 frame.tangents.resize(numVertices);
1711 frame.bitangents.resize(numVertices);
1712
1713 MeshUtility::calculateTangents(mesh->positions.data(), frame.normals.data(), mesh->UV[0].data(), (UINT8*)mesh->indices.data(),
1714 numVertices, numIndices, frame.tangents.data(), frame.bitangents.data());
1715 }
1716 }
1717 }
1718 }
1719 }
1720
1721 void FBXImporter::importAnimations(FbxScene* scene, FBXImportOptions& importOptions, FBXImportScene& importScene)
1722 {
1723 FbxNode* root = scene->GetRootNode();
1724
1725 UINT32 numAnimStacks = (UINT32)scene->GetSrcObjectCount<FbxAnimStack>();
1726 for (UINT32 i = 0; i < numAnimStacks; i++)
1727 {
1728 FbxAnimStack* animStack = scene->GetSrcObject<FbxAnimStack>(i);
1729
1730 importScene.clips.push_back(FBXAnimationClip());
1731 FBXAnimationClip& clip = importScene.clips.back();
1732 clip.name = animStack->GetName();
1733
1734 FbxTimeSpan timeSpan = animStack->GetLocalTimeSpan();
1735 clip.start = (float)timeSpan.GetStart().GetSecondDouble();
1736 clip.end = (float)timeSpan.GetStop().GetSecondDouble();
1737
1738 clip.sampleRate = (UINT32)FbxTime::GetFrameRate(scene->GetGlobalSettings().GetTimeMode());
1739
1740 UINT32 layerCount = animStack->GetMemberCount<FbxAnimLayer>();
1741 if (layerCount > 1)
1742 {
1743 FbxAnimEvaluator* evaluator = scene->GetAnimationEvaluator();
1744
1745 FbxTime startTime;
1746 startTime.SetSecondDouble(clip.start);
1747
1748 FbxTime endTime;
1749 endTime.SetSecondDouble(clip.end);
1750
1751 FbxTime sampleRate;
1752
1753 if (importOptions.animResample)
1754 sampleRate.SetSecondDouble(importOptions.animSampleRate);
1755 else
1756 {
1757 FbxTime::EMode timeMode = scene->GetGlobalSettings().GetTimeMode();
1758 sampleRate.SetSecondDouble(1.0f / FbxTime::GetFrameRate(timeMode));
1759 }
1760
1761 if (!animStack->BakeLayers(evaluator, startTime, endTime, sampleRate))
1762 continue;
1763
1764 layerCount = animStack->GetMemberCount<FbxAnimLayer>();
1765 }
1766
1767 if (layerCount == 1)
1768 {
1769 FbxAnimLayer* animLayer = animStack->GetMember<FbxAnimLayer>(0);
1770
1771 importAnimations(animLayer, root, importOptions, clip, importScene);
1772 }
1773 }
1774 }
1775
1776 void FBXImporter::importAnimations(FbxAnimLayer* layer, FbxNode* node, FBXImportOptions& importOptions,
1777 FBXAnimationClip& clip, FBXImportScene& importScene)
1778 {
1779 FbxAnimCurve* translation[3];
1780 translation[0] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
1781 translation[1] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
1782 translation[2] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
1783
1784 FbxAnimCurve* rotation[3];
1785 rotation[0] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
1786 rotation[1] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
1787 rotation[2] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
1788
1789 FbxAnimCurve* scale[3];
1790 scale[0] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
1791 scale[1] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
1792 scale[2] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
1793
1794 Vector3 defaultTranslation = FBXToNativeType(node->LclTranslation.Get());
1795 Vector3 defaultRotation = FBXToNativeType(node->LclRotation.Get());
1796 Vector3 defaultScale = FBXToNativeType(node->LclScaling.Get());
1797
1798 auto hasCurveValues = [](FbxAnimCurve* curves[3])
1799 {
1800 for (UINT32 i = 0; i < 3; i++)
1801 {
1802 if (curves[i] != nullptr && curves[i]->KeyGetCount() > 0)
1803 return true;
1804 }
1805
1806 return false;
1807 };
1808
1809 bool hasBoneAnimation = hasCurveValues(translation) || hasCurveValues(rotation) || hasCurveValues(scale);
1810 if (hasBoneAnimation)
1811 {
1812 clip.boneAnimations.push_back(FBXBoneAnimation());
1813 FBXBoneAnimation& boneAnim = clip.boneAnimations.back();
1814 boneAnim.node = importScene.nodeMap[node];
1815
1816 if (hasCurveValues(translation))
1817 {
1818 float defaultValues[3];
1819 memcpy(defaultValues, &defaultTranslation, sizeof(defaultValues));
1820
1821 boneAnim.translation = importCurve<Vector3, 3>(translation, defaultValues, importOptions,
1822 clip.start, clip.end);
1823 }
1824 else
1825 {
1826 Vector<TKeyframe<Vector3>> keyframes(1);
1827 keyframes[0].value = defaultTranslation;
1828 keyframes[0].inTangent = Vector3::ZERO;
1829 keyframes[0].outTangent = Vector3::ZERO;
1830
1831 boneAnim.translation = TAnimationCurve<Vector3>(keyframes);
1832 }
1833
1834 if (hasCurveValues(scale))
1835 {
1836 float defaultValues[3];
1837 memcpy(defaultValues, &defaultScale, sizeof(defaultValues));
1838
1839 boneAnim.scale = importCurve<Vector3, 3>(scale, defaultValues, importOptions, clip.start, clip.end);
1840 }
1841 else
1842 {
1843 Vector<TKeyframe<Vector3>> keyframes(1);
1844 keyframes[0].value = defaultScale;
1845 keyframes[0].inTangent = Vector3::ZERO;
1846 keyframes[0].outTangent = Vector3::ZERO;
1847
1848 boneAnim.scale = TAnimationCurve<Vector3>(keyframes);
1849 }
1850
1851 SPtr<TAnimationCurve<Vector3>> eulerAnimation = bs_shared_ptr_new<TAnimationCurve<Vector3>>();
1852 if (hasCurveValues(rotation))
1853 {
1854 float defaultValues[3];
1855 memcpy(defaultValues, &defaultRotation, sizeof(defaultValues));
1856
1857 *eulerAnimation = importCurve<Vector3, 3>(rotation, defaultValues, importOptions, clip.start, clip.end);
1858 }
1859 else
1860 {
1861 Vector<TKeyframe<Vector3>> keyframes(1);
1862 keyframes[0].value = defaultRotation;
1863 keyframes[0].inTangent = Vector3::ZERO;
1864 keyframes[0].outTangent = Vector3::ZERO;
1865
1866 *eulerAnimation = TAnimationCurve<Vector3>(keyframes);
1867 }
1868
1869 if(importOptions.reduceKeyframes)
1870 {
1871 boneAnim.translation = reduceKeyframes(boneAnim.translation);
1872 boneAnim.scale = reduceKeyframes(boneAnim.scale);
1873 *eulerAnimation = reduceKeyframes(*eulerAnimation);
1874 }
1875
1876 boneAnim.rotation = *AnimationUtility::eulerToQuaternionCurve(eulerAnimation, EulerAngleOrder::XYZ);
1877 }
1878
1879 if (importOptions.importBlendShapes)
1880 {
1881 FbxMesh* fbxMesh = node->GetMesh();
1882 if (fbxMesh != nullptr)
1883 {
1884 INT32 deformerCount = fbxMesh->GetDeformerCount(FbxDeformer::eBlendShape);
1885 for (INT32 i = 0; i < deformerCount; i++)
1886 {
1887 FbxBlendShape* deformer = static_cast<FbxBlendShape*>(fbxMesh->GetDeformer(i, FbxDeformer::eBlendShape));
1888
1889 INT32 channelCount = deformer->GetBlendShapeChannelCount();
1890 for (INT32 j = 0; j < channelCount; j++)
1891 {
1892 FbxBlendShapeChannel* channel = deformer->GetBlendShapeChannel(j);
1893
1894 FbxAnimCurve* curve = fbxMesh->GetShapeChannel(i, j, layer);
1895 if (curve != nullptr && curve->KeyGetCount() > 0)
1896 {
1897 clip.blendShapeAnimations.push_back(FBXBlendShapeAnimation());
1898 FBXBlendShapeAnimation& blendShapeAnim = clip.blendShapeAnimations.back();
1899 blendShapeAnim.blendShape = channel->GetName();
1900
1901 // Get name without invalid characters
1902 blendShapeAnim.blendShape = StringUtil::replaceAll(blendShapeAnim.blendShape, ".", "_");
1903 blendShapeAnim.blendShape = StringUtil::replaceAll(blendShapeAnim.blendShape, "/", "_");
1904
1905 FbxAnimCurve* curves[1] = { curve };
1906 float defaultValues[1] = { 0.0f };
1907 blendShapeAnim.curve = importCurve<float, 1>(curves, defaultValues, importOptions, clip.start,
1908 clip.end);
1909
1910 // FBX contains data in [0, 100] range, but we need it in [0, 1] range
1911 blendShapeAnim.curve = AnimationUtility::scaleCurve(blendShapeAnim.curve, 0.01f);
1912 }
1913 }
1914 }
1915 }
1916 }
1917
1918 UINT32 childCount = (UINT32)node->GetChildCount();
1919 for (UINT32 i = 0; i < childCount; i++)
1920 {
1921 FbxNode* child = node->GetChild(i);
1922 importAnimations(layer, child, importOptions, clip, importScene);
1923 }
1924 }
1925
1926 void FBXImporter::bakeTransforms(FbxScene* scene)
1927 {
1928 // FBX stores transforms in a more complex way than just translation-rotation-scale as used by the framework.
1929 // Instead they also support rotations offsets and pivots, scaling pivots and more. We wish to bake all this data
1930 // into a standard transform so we can access it using node's local TRS properties (e.g. FbxNode::LclTranslation).
1931
1932 double frameRate = FbxTime::GetFrameRate(scene->GetGlobalSettings().GetTimeMode());
1933
1934 bs_frame_mark();
1935 {
1936 FrameStack<FbxNode*> todo;
1937 todo.push(scene->GetRootNode());
1938
1939 while(todo.size() > 0)
1940 {
1941 FbxNode* node = todo.top();
1942 todo.pop();
1943
1944 FbxVector4 zero(0, 0, 0);
1945 FbxVector4 one(1, 1, 1);
1946
1947 // Activate pivot converting
1948 node->SetPivotState(FbxNode::eSourcePivot, FbxNode::ePivotActive);
1949 node->SetPivotState(FbxNode::eDestinationPivot, FbxNode::ePivotActive);
1950
1951 // We want to set all these to 0 (1 for scale) and bake them into the transforms
1952 node->SetPostRotation(FbxNode::eDestinationPivot, zero);
1953 node->SetPreRotation(FbxNode::eDestinationPivot, zero);
1954 node->SetRotationOffset(FbxNode::eDestinationPivot, zero);
1955 node->SetScalingOffset(FbxNode::eDestinationPivot, zero);
1956 node->SetRotationPivot(FbxNode::eDestinationPivot, zero);
1957 node->SetScalingPivot(FbxNode::eDestinationPivot, zero);
1958
1959 // We account for geometric properties separately during node traversal
1960 node->SetGeometricTranslation(FbxNode::eDestinationPivot, node->GetGeometricTranslation(FbxNode::eSourcePivot));
1961 node->SetGeometricRotation(FbxNode::eDestinationPivot, node->GetGeometricRotation(FbxNode::eSourcePivot));
1962 node->SetGeometricScaling(FbxNode::eDestinationPivot, node->GetGeometricScaling(FbxNode::eSourcePivot));
1963
1964 // Use XYZ as that appears to be the default for FBX (other orders sometimes have artifacts)
1965 node->SetRotationOrder(FbxNode::eDestinationPivot, FbxEuler::eOrderXYZ);
1966
1967 // Keep interpolation as is
1968 node->SetQuaternionInterpolation(FbxNode::eDestinationPivot, node->GetQuaternionInterpolation(FbxNode::eSourcePivot));
1969
1970 for (int i = 0; i < node->GetChildCount(); i++)
1971 {
1972 FbxNode* childNode = node->GetChild(i);
1973 todo.push(childNode);
1974 }
1975 }
1976
1977 scene->GetRootNode()->ConvertPivotAnimationRecursive(nullptr, FbxNode::eDestinationPivot, frameRate, false);
1978 }
1979 bs_frame_clear();
1980 }
1981
1982 TAnimationCurve<Vector3> FBXImporter::reduceKeyframes(TAnimationCurve<Vector3>& curve)
1983 {
1984 UINT32 keyCount = curve.getNumKeyFrames();
1985
1986 Vector<TKeyframe<Vector3>> newKeyframes;
1987
1988 bool lastWasEqual = false;
1989 for (UINT32 i = 0; i < keyCount; i++)
1990 {
1991 bool isEqual = true;
1992
1993 const TKeyframe<Vector3>& curKey = curve.getKeyFrame(i);
1994 if (i > 0)
1995 {
1996 TKeyframe<Vector3>& prevKey = newKeyframes.back();
1997
1998 isEqual = Math::approxEquals(prevKey.value, curKey.value) &&
1999 Math::approxEquals(prevKey.outTangent, curKey.inTangent) && isEqual;
2000 }
2001 else
2002 isEqual = false;
2003
2004 // More than two keys in a row are equal, remove previous key by replacing it with this one
2005 if (lastWasEqual && isEqual)
2006 {
2007 TKeyframe<Vector3>& prevKey = newKeyframes.back();
2008
2009 // Other properties are guaranteed unchanged
2010 prevKey.time = curKey.time;
2011 prevKey.outTangent = curKey.outTangent;
2012
2013 continue;
2014 }
2015
2016 newKeyframes.push_back(curKey);
2017 lastWasEqual = isEqual;
2018 }
2019
2020 return TAnimationCurve<Vector3>(newKeyframes);
2021 }
2022
2023 template<class T>
2024 void setKeyframeValues(TKeyframe<T>& keyFrame, int idx, float value, float inTangent, float outTangent)
2025 {
2026 keyFrame.value = value;
2027 keyFrame.inTangent = inTangent;
2028 keyFrame.outTangent = outTangent;
2029 }
2030
2031 template<>
2032 void setKeyframeValues<Vector3>(TKeyframe<Vector3>& keyFrame, int idx, float value, float inTangent, float outTangent)
2033 {
2034 keyFrame.value[idx] = value;
2035 keyFrame.inTangent[idx] = inTangent;
2036 keyFrame.outTangent[idx] = outTangent;
2037 }
2038
2039 template<class T, int C>
2040 TAnimationCurve<T> FBXImporter::importCurve(FbxAnimCurve*(&fbxCurve)[C], float (&defaultValues)[C],
2041 FBXImportOptions& importOptions, float clipStart, float clipEnd)
2042 {
2043 int keyCounts[C];
2044 for (int i = 0; i < C; i++)
2045 {
2046 if (fbxCurve[i] != nullptr)
2047 keyCounts[i] = fbxCurve[i]->KeyGetCount();
2048 else
2049 keyCounts[i] = 0;
2050 }
2051
2052 // If curve key-counts don't match, we need to force resampling
2053 bool forceResample = false;
2054 if (!forceResample)
2055 {
2056 for (int i = 1; i < C; i++)
2057 {
2058 forceResample |= keyCounts[i - 1] != keyCounts[i];
2059 if (forceResample)
2060 break;
2061 }
2062 }
2063
2064 // Determine curve length
2065 float curveStart = std::numeric_limits<float>::infinity();
2066 float curveEnd = -std::numeric_limits<float>::infinity();
2067
2068 for (INT32 i = 0; i < C; i++)
2069 {
2070 if(fbxCurve[i] == nullptr)
2071 {
2072 curveStart = std::min(0.0f, curveStart);
2073 curveEnd = std::max(0.0f, curveEnd);
2074
2075 continue;
2076 }
2077
2078 int keyCount = keyCounts[i];
2079 for (INT32 j = 0; j < keyCount; j++)
2080 {
2081 FbxTime fbxTime = fbxCurve[i]->KeyGetTime(j);
2082 float time = (float)fbxTime.GetSecondDouble();
2083
2084 curveStart = std::min(time, curveStart);
2085 curveEnd = std::max(time, curveEnd);
2086 }
2087 }
2088
2089 // Read keys directly
2090 if(!importOptions.animResample && !forceResample)
2091 {
2092 bool foundMismatch = false;
2093 int keyCount = keyCounts[0];
2094 Vector<TKeyframe<T>> keyframes;
2095
2096 // All curves must match the length of the clip, so add a keyframe if first keyframe doesn't match the start time
2097 if(curveStart > clipStart)
2098 {
2099 keyframes.push_back(TKeyframe<T>());
2100 TKeyframe<T>& keyFrame = keyframes.back();
2101
2102 keyFrame.time = clipStart;
2103
2104 FbxTime fbxSampleTime;
2105 fbxSampleTime.SetSecondDouble(clipStart);
2106
2107 for (int j = 0; j < C; j++)
2108 {
2109 setKeyframeValues(keyFrame, j,
2110 fbxCurve[j]->Evaluate(fbxSampleTime),
2111 fbxCurve[j]->EvaluateLeftDerivative(fbxSampleTime),
2112 fbxCurve[j]->EvaluateRightDerivative(fbxSampleTime));
2113 }
2114 }
2115
2116 for (int i = 0; i < keyCount; i++)
2117 {
2118 FbxTime fbxTime = fbxCurve[0]->KeyGetTime(i);
2119 float time = (float)fbxTime.GetSecondDouble();
2120
2121 // Ensure times from other curves match
2122 for (int j = 1; j < C; j++)
2123 {
2124 fbxTime = fbxCurve[j]->KeyGetTime(i);
2125 float otherTime = (float)fbxTime.GetSecondDouble();
2126
2127 if (!Math::approxEquals(time, otherTime))
2128 {
2129 foundMismatch = true;
2130 break;
2131 }
2132 }
2133
2134 if(foundMismatch)
2135 break;
2136
2137 if (time < clipStart || time > clipEnd)
2138 continue;
2139
2140 keyframes.push_back(TKeyframe<T>());
2141 TKeyframe<T>& keyFrame = keyframes.back();
2142
2143 keyFrame.time = time;
2144
2145 for (int j = 0; j < C; j++)
2146 {
2147 setKeyframeValues(keyFrame, j,
2148 fbxCurve[j]->KeyGetValue(i),
2149 fbxCurve[j]->KeyGetLeftDerivative(i),
2150 fbxCurve[j]->KeyGetRightDerivative(i));
2151 }
2152 }
2153
2154 // All curves must match the length of the clip, so add a keyframe if last keyframe doesn't match the end time
2155 if(curveEnd < clipEnd)
2156 {
2157 keyframes.push_back(TKeyframe<T>());
2158 TKeyframe<T>& keyFrame = keyframes.back();
2159
2160 keyFrame.time = clipEnd;
2161
2162 FbxTime fbxSampleTime;
2163 fbxSampleTime.SetSecondDouble(clipEnd);
2164
2165 for (int j = 0; j < C; j++)
2166 {
2167 setKeyframeValues(keyFrame, j,
2168 fbxCurve[j]->Evaluate(fbxSampleTime),
2169 fbxCurve[j]->EvaluateLeftDerivative(fbxSampleTime),
2170 fbxCurve[j]->EvaluateRightDerivative(fbxSampleTime));
2171 }
2172 }
2173
2174 if (!foundMismatch)
2175 return TAnimationCurve<T>(keyframes);
2176 else
2177 forceResample = true;
2178 }
2179
2180 // Resample keys
2181 if (!importOptions.animResample && forceResample)
2182 LOGWRN_VERBOSE("Animation has different keyframes for different curve components, forcing resampling.");
2183
2184 // Make sure to resample along the length of the entire clip
2185 curveStart = std::min(curveStart, clipStart);
2186 curveEnd = std::max(curveEnd, clipEnd);
2187
2188 float curveLength = curveEnd - curveStart;
2189 INT32 numSamples = Math::ceilToInt(curveLength / importOptions.animSampleRate) + 1;
2190
2191 // We don't use the exact provided sample rate but instead modify it slightly so it
2192 // completely covers the curve range including start/end points while maintaining
2193 // constant time step between keyframes.
2194 float dt = curveLength / (float)(numSamples - 1);
2195
2196 INT32 lastKeyframe[] = { 0, 0, 0 };
2197 INT32 lastLeftTangent[] = { 0, 0, 0 };
2198 INT32 lastRightTangent[] = { 0, 0, 0 };
2199
2200 Vector<TKeyframe<T>> keyframes(numSamples);
2201 for (INT32 i = 0; i < numSamples; i++)
2202 {
2203 float sampleTime = std::min(curveStart + i * dt, curveEnd);
2204 FbxTime fbxSampleTime;
2205 fbxSampleTime.SetSecondDouble(sampleTime);
2206
2207 TKeyframe<T>& keyFrame = keyframes[i];
2208 keyFrame.time = sampleTime;
2209
2210 for (int j = 0; j < C; j++)
2211 {
2212 if (fbxCurve[j] != nullptr)
2213 {
2214 setKeyframeValues(keyFrame, j,
2215 fbxCurve[j]->Evaluate(fbxSampleTime, &lastKeyframe[j]),
2216 fbxCurve[j]->EvaluateLeftDerivative(fbxSampleTime, &lastLeftTangent[j]),
2217 fbxCurve[j]->EvaluateRightDerivative(fbxSampleTime, &lastRightTangent[j]));
2218 }
2219 else
2220 {
2221 setKeyframeValues(keyFrame, j, defaultValues[j], 0.0f, 0.0f);
2222 }
2223 }
2224 }
2225
2226 return TAnimationCurve<T>(keyframes);
2227 }
2228}
2229