Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Paradox.Importer.FBX.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 #include "stdafx.h"
4 
5 #include <algorithm>
6 #include <string>
7 #include <map>
8 
9 using namespace System;
10 using namespace System::IO;
11 using namespace System::Collections::Generic;
12 using namespace System::Runtime::InteropServices;
13 using namespace SiliconStudio::BuildEngine;
14 using namespace SiliconStudio::Core::Diagnostics;
15 using namespace SiliconStudio::Core::IO;
16 using namespace SiliconStudio::Core::Mathematics;
17 using namespace SiliconStudio::Core::Serialization;
18 using namespace SiliconStudio::Core::Serialization::Assets;
19 using namespace SiliconStudio::Core::Serialization::Contents;
20 using namespace SiliconStudio::Paradox::Assets::Materials;
21 using namespace SiliconStudio::Paradox::Assets::Materials::Nodes;
22 using namespace SiliconStudio::Paradox::DataModel;
23 using namespace SiliconStudio::Paradox::EntityModel;
24 using namespace SiliconStudio::Paradox::EntityModel::Data;
25 using namespace SiliconStudio::Paradox::Effects;
26 using namespace SiliconStudio::Paradox::Effects::Data;
27 using namespace SiliconStudio::Paradox::Effects::Modules;
28 using namespace SiliconStudio::Paradox::Engine;
29 using namespace SiliconStudio::Paradox::Engine::Data;
30 using namespace SiliconStudio::Paradox::Extensions;
31 using namespace SiliconStudio::Paradox::Graphics;
32 using namespace SiliconStudio::Paradox::Graphics::Data;
33 using namespace SiliconStudio::Paradox::Shaders;
34 
35 using namespace SiliconStudio::Paradox::Importer::Common;
36 
37 namespace SiliconStudio { namespace Paradox { namespace Importer { namespace FBX {
38 
39 public ref class MaterialInstances
40 {
41 public:
43  {
44  Instances = gcnew List<MaterialInstanciation^>();
45  }
46 
47  FbxSurfaceMaterial* SourceMaterial;
48  List<MaterialInstanciation^>^ Instances;
49  String^ MaterialsName;
50 };
51 
52 public ref class MeshConverter
53 {
54 public:
55  property bool InverseNormals;
57 
59 
61 
63 
64 internal:
65  FbxManager* lSdkManager;
66  FbxImporter* lImporter;
67  FbxScene* scene;
71 
72  String^ inputFilename;
74  String^ inputPath;
75 
77 
78  Dictionary<IntPtr, int> nodeMapping;
79  List<ModelNodeDefinition> nodes;
80 
81  static array<Byte>^ currentBuffer;
82 
83 public:
85  {
86  if(logger == nullptr)
87  logger = Core::Diagnostics::GlobalLogger::GetLogger("Importer FBX");
88 
89  polygonSwap = false;
90  exportedFromMaya = false;
91  logger = Logger;
92  lSdkManager = NULL;
93  lImporter = NULL;
94  }
95 
96  void Destroy()
97  {
98  //Marshal::FreeHGlobal((IntPtr)lFilename);
99  currentBuffer = nullptr;
100 
101  // The file has been imported; we can get rid of the importer.
102  lImporter->Destroy();
103 
104  // Destroy the sdk manager and all other objects it was handling.
105  lSdkManager->Destroy();
106 
107  // -----------------------------------------------------
108  // TODO: Workaround with FBX SDK not being multithreaded.
109  // We protect the whole usage of this class with a monitor
110  //
111  // Lock the whole class between Initialize/Destroy
112  // -----------------------------------------------------
113  System::Threading::Monitor::Exit( globalLock );
114  // -----------------------------------------------------
115  }
116 
117  static bool WeightGreater(const std::pair<short, float>& elem1, const std::pair<short, float>& elem2)
118  {
119  return elem1.second > elem2.second;
120  }
121 
122  void ProcessMesh(FbxMesh* pMesh, std::map<FbxMesh*, std::string> meshNames)
123  {
124  FbxVector4* controlPoints = pMesh->GetControlPoints();
125  FbxGeometryElementNormal* normalElement = pMesh->GetElementNormal();
126 
127  // UV set name mapping
128  std::map<std::string, int> uvElementMapping;
129  std::vector<FbxGeometryElementUV*> uvElements;
130 
131  for (int i = 0; i < pMesh->GetElementUVCount(); ++i)
132  {
133  uvElements.push_back(pMesh->GetElementUV(i));
134  }
135 
136  bool hasSkinningPosition = false;
137  bool hasSkinningNormal = false;
138  int totalClusterCount = 0;
139  std::vector<std::vector<std::pair<short, float> > > controlPointWeights;
140 
141  List<MeshBoneDefinition>^ bones = nullptr;
142 
143  // Dump skinning information
144  if (pMesh->GetDeformerCount() > 0)
145  {
146  if (pMesh->GetDeformerCount() != 1)
147  {
148  logger->Error("Multiple mesh deformers are not supported yet. Mesh '{0}' will not be properly deformed.", gcnew String(meshNames[pMesh].c_str()));
149  }
150 
151  FbxDeformer* deformer = pMesh->GetDeformer(0);
152  FbxDeformer::EDeformerType deformerType = deformer->GetDeformerType();
153  if (deformerType == FbxDeformer::eSkin)
154  {
155  auto lPose = scene->GetPose(0);
156 
157  FbxSkin* skin = FbxCast<FbxSkin>(deformer);
158 
159  FbxSkin::EType lSkinningType = skin->GetSkinningType();
160 
161  controlPointWeights.resize(pMesh->GetControlPointsCount());
162 
163  bones = gcnew List<MeshBoneDefinition>();
164 
165  totalClusterCount = skin->GetClusterCount();
166  for (int clusterIndex = 0 ; clusterIndex < totalClusterCount; ++clusterIndex)
167  {
168  FbxCluster* cluster = skin->GetCluster(clusterIndex);
169  FbxNode* link = cluster->GetLink();
170  FbxCluster::ELinkMode lClusterMode = cluster->GetLinkMode();
171  const char* boneName = link->GetName();
172 
173  int indexCount = cluster->GetControlPointIndicesCount();
174  int *indices = cluster->GetControlPointIndices();
175  double *weights = cluster->GetControlPointWeights();
176 
177  FbxAMatrix lReferenceGlobalInitPosition;
178  FbxAMatrix lClusterGlobalInitPosition;
179  FbxAMatrix lClusterRelativeInitPosition;
180 
181  cluster->GetTransformLinkMatrix(lClusterGlobalInitPosition);
182  cluster->GetTransformMatrix(lReferenceGlobalInitPosition);
183  //lReferenceGlobalInitPosition *= GetGeometry(pMesh->GetNode());
184 
185  bool test = cluster->IsTransformParentSet();
186 
187  auto linkMatrix = FBXMatrixToMatrix(lClusterGlobalInitPosition);
188  auto meshMatrix = FBXMatrixToMatrix(lReferenceGlobalInitPosition);
189 
190  if (swapHandedness)
191  {
192  logger->Warning("Bones transformation from left handed to right handed need to be checked.", nullptr, CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
193 
194  // TODO: Check if still necessary?
195  //linkMatrix.M13 = -linkMatrix.M13;
196  //linkMatrix.M23 = -linkMatrix.M23;
197  //linkMatrix.M43 = -linkMatrix.M43;
198  //linkMatrix.M31 = -linkMatrix.M31;
199  //linkMatrix.M32 = -linkMatrix.M32;
200  //linkMatrix.M34 = -linkMatrix.M34;
201  //
202  //meshMatrix.M13 = -meshMatrix.M13;
203  //meshMatrix.M23 = -meshMatrix.M23;
204  //meshMatrix.M43 = -meshMatrix.M43;
205  //meshMatrix.M31 = -meshMatrix.M31;
206  //meshMatrix.M32 = -meshMatrix.M32;
207  //meshMatrix.M34 = -meshMatrix.M34;
208  }
209 
210  auto nodeIndex = nodeMapping[(IntPtr)link];
211 
212  MeshBoneDefinition bone;
213  bone.NodeIndex = nodeMapping[(IntPtr)link];
214  bone.LinkToMeshMatrix = meshMatrix * Matrix::Invert(linkMatrix);
215  bones->Add(bone);
216 
217  for (int j = 0 ; j < indexCount; j++)
218  {
219  int controlPointIndex = indices[j];
220  controlPointWeights[controlPointIndex].push_back(std::pair<short, float>((short)clusterIndex, (float)weights[j]));
221  }
222  }
223 
224  // look for position/normals skinning
225  if (pMesh->GetControlPointsCount() > 0)
226  {
227  hasSkinningPosition = true;
228  hasSkinningNormal = (pMesh->GetElementNormal() != NULL);
229  }
230 
231  for (int i = 0 ; i < pMesh->GetControlPointsCount(); i++)
232  {
233  std::sort(controlPointWeights[i].begin(), controlPointWeights[i].end(), WeightGreater);
234  controlPointWeights[i].resize(4, std::pair<short, float>(0, 0.0f));
235  float totalWeight = 0.0f;
236  for (int j = 0; j < 4; ++j)
237  totalWeight += controlPointWeights[i][j].second;
238  if (totalWeight == 0.0f)
239  {
240  for (int j = 0; j < 4; ++j)
241  controlPointWeights[i][j].second = (j == 0) ? 1.0f : 0.0f;
242  }
243  else
244  {
245  totalWeight = 1.0f / totalWeight;
246  for (int j = 0; j < 4; ++j)
247  controlPointWeights[i][j].second *= totalWeight;
248  }
249  }
250  }
251  }
252 
253  // Build the vertex declaration
254  auto vertexElements = gcnew List<VertexElement>();
255 
256  int vertexStride = 0;
257  int positionOffset = vertexStride;
258  vertexElements->Add(VertexElement::Position<Vector3>(0, vertexStride));
259  vertexStride += 12;
260 
261  int normalOffset = vertexStride;
262  if (normalElement != NULL)
263  {
264  vertexElements->Add(VertexElement::Normal<Vector3>(0, vertexStride));
265  vertexStride += 12;
266  }
267 
268  std::vector<int> uvOffsets;
269  for (int i = 0; i < (int)uvElements.size(); ++i)
270  {
271  uvOffsets.push_back(vertexStride);
272  vertexElements->Add(VertexElement::TextureCoordinate<Vector2>(i, vertexStride));
273  vertexStride += 8;
274  uvElementMapping[pMesh->GetElementUV(i)->GetName()] = i;
275  }
276 
277  int blendIndicesOffset = vertexStride;
278  bool controlPointIndices16 = (AllowUnsignedBlendIndices && totalClusterCount > 256) || (!AllowUnsignedBlendIndices && totalClusterCount > 128);
279  if (!controlPointWeights.empty())
280  {
281  if (controlPointIndices16)
282  {
283  if (AllowUnsignedBlendIndices)
284  {
285  vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R16G16B16A16_UInt, vertexStride));
286  vertexStride += sizeof(unsigned short) * 4;
287  }
288  else
289  {
290  vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R16G16B16A16_SInt, vertexStride));
291  vertexStride += sizeof(short) * 4;
292  }
293  }
294  else
295  {
296  if (AllowUnsignedBlendIndices)
297  {
298  vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R8G8B8A8_UInt, vertexStride));
299  vertexStride += sizeof(unsigned char) * 4;
300  }
301  else
302  {
303  vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R8G8B8A8_SInt, vertexStride));
304  vertexStride += sizeof(char) * 4;
305  }
306  }
307  }
308 
309  int blendWeightOffset = vertexStride;
310  if (!controlPointWeights.empty())
311  {
312  vertexElements->Add(VertexElement("BLENDWEIGHT", 0, PixelFormat::R32G32B32A32_Float, vertexStride));
313  vertexStride += sizeof(float) * 4;
314  }
315 
316  int polygonCount = pMesh->GetPolygonCount();
317 
318  FbxGeometryElement::EMappingMode materialMappingMode = FbxGeometryElement::eNone;
319  FbxLayerElementArrayTemplate<int>* materialIndices = NULL;
320 
321  if (pMesh->GetElementMaterial())
322  {
323  materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode();
324  materialIndices = &pMesh->GetElementMaterial()->GetIndexArray();
325  }
326 
327  auto buildMeshes = gcnew List<BuildMesh^>();
328 
329  // Count polygon per materials
330  for (int i = 0; i < polygonCount; i++)
331  {
332  int materialIndex = 0;
333  if (materialMappingMode == FbxGeometryElement::eByPolygon)
334  {
335  materialIndex = materialIndices->GetAt(i);
336  }
337 
338  // Equivalent to std::vector::resize()
339  while (materialIndex >= buildMeshes->Count)
340  {
341  buildMeshes->Add(nullptr);
342  }
343 
344  if (buildMeshes[materialIndex] == nullptr)
345  buildMeshes[materialIndex] = gcnew BuildMesh();
346 
347  int polygonSize = pMesh->GetPolygonSize(i) - 2;
348  if (polygonSize > 0)
349  buildMeshes[materialIndex]->polygonCount += polygonSize;
350  }
351 
352  // Create arrays
353  for each(BuildMesh^ buildMesh in buildMeshes)
354  {
355  if (buildMesh == nullptr)
356  continue;
357 
358  buildMesh->buffer = gcnew array<Byte>(vertexStride * buildMesh->polygonCount * 3);
359  }
360 
361  // Build polygons
362  int polygonVertexStartIndex = 0;
363  for (int i = 0; i < polygonCount; i++)
364  {
365  int materialIndex = 0;
366  if (materialMappingMode == FbxGeometryElement::eByPolygon)
367  {
368  materialIndex = materialIndices->GetAt(i);
369  }
370 
371  auto buildMesh = buildMeshes[materialIndex];
372  auto buffer = buildMesh->buffer;
373 
374  int polygonSize = pMesh->GetPolygonSize(i);
375 
376  for (int polygonFanIndex = 2; polygonFanIndex < polygonSize; ++polygonFanIndex)
377  {
378  pin_ptr<Byte> vbPointer = &buffer[buildMesh->bufferOffset];
379  buildMesh->bufferOffset += vertexStride * 3;
380 
381  int vertexInPolygon[3] = { 0, polygonFanIndex - 1, polygonFanIndex };
382  if (polygonSwap)
383  {
384  int temp = vertexInPolygon[1];
385  vertexInPolygon[1] = vertexInPolygon[2];
386  vertexInPolygon[2] = temp;
387  }
388  int controlPointIndices[3] = { pMesh->GetPolygonVertex(i, vertexInPolygon[0]), pMesh->GetPolygonVertex(i, vertexInPolygon[1]), pMesh->GetPolygonVertex(i, vertexInPolygon[2]) };
389 
390  for (int polygonFanVertex = 0; polygonFanVertex < 3; ++polygonFanVertex)
391  {
392  int j = vertexInPolygon[polygonFanVertex];
393  int vertexIndex = polygonVertexStartIndex + j;
394  int controlPointIndex = controlPointIndices[polygonFanVertex];
395 
396  FbxVector4 controlPoint = controlPoints[controlPointIndex];
397 
398  if (swapHandedness)
399  controlPoint[2] = -controlPoint[2];
400 
401  ((float*)(vbPointer + positionOffset))[0] = (float)controlPoint[0];
402  ((float*)(vbPointer + positionOffset))[1] = (float)controlPoint[1];
403  ((float*)(vbPointer + positionOffset))[2] = (float)controlPoint[2];
404 
405  if (normalElement != NULL)
406  {
407  FbxVector4 normal;
408  if (normalElement->GetMappingMode() == FbxLayerElement::eByControlPoint)
409  {
410  int normalIndex = (normalElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect)
411  ? normalElement->GetIndexArray().GetAt(controlPointIndex)
412  : controlPointIndex;
413  normal = normalElement->GetDirectArray().GetAt(normalIndex);
414  }
415  else if (normalElement->GetMappingMode() == FbxLayerElement::eByPolygonVertex)
416  {
417  int normalIndex = (normalElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect)
418  ? normalElement->GetIndexArray().GetAt(vertexIndex)
419  : vertexIndex;
420  normal = normalElement->GetDirectArray().GetAt(normalIndex);
421  }
422 
423  if (swapHandedness)
424  normal[2] = -normal[2];
425  if(InverseNormals)
426  normal = - normal;
427 
428  ((float*)(vbPointer + normalOffset))[0] = (float)normal[0];
429  ((float*)(vbPointer + normalOffset))[1] = (float)normal[1];
430  ((float*)(vbPointer + normalOffset))[2] = (float)normal[2];
431  }
432 
433  for (int uvGroupIndex = 0; uvGroupIndex < (int)uvElements.size(); ++uvGroupIndex)
434  {
435  auto uvElement = uvElements[uvGroupIndex];
436  FbxVector2 uv;
437  if (uvElement->GetMappingMode() == FbxLayerElement::eByControlPoint)
438  {
439  int uvIndex = (uvElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect)
440  ? uvElement->GetIndexArray().GetAt(controlPointIndex)
441  : controlPointIndex;
442  uv = uvElement->GetDirectArray().GetAt(uvIndex);
443  }
444  else if (uvElement->GetMappingMode() == FbxLayerElement::eByPolygonVertex)
445  {
446  int uvIndex = (uvElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect)
447  ? uvElement->GetIndexArray().GetAt(vertexIndex)
448  : vertexIndex;
449  uv = uvElement->GetDirectArray().GetAt(uvIndex);
450  }
451  else
452  {
453  logger->Error("The texture mapping mode '{0}' is not supported yet by the FBX importer "
454  + "(currently only mapping by control point and by polygon vertex are supported). "
455  + "Texture mapping will not be correct for mesh '{1}'.", gcnew Int32(uvElement->GetMappingMode()), gcnew String(meshNames[pMesh].c_str()));
456  }
457 
458  ((float*)(vbPointer + uvOffsets[uvGroupIndex]))[0] = (float)uv[0];
459  ((float*)(vbPointer + uvOffsets[uvGroupIndex]))[1] = 1.0f - (float)uv[1];
460  }
461 
462  if (!controlPointWeights.empty())
463  {
464  const auto& blendWeights = controlPointWeights[controlPointIndex];
465  for (int i = 0; i < 4; ++i)
466  {
467  if (controlPointIndices16)
468  {
469  if (AllowUnsignedBlendIndices)
470  ((unsigned short*)(vbPointer + blendIndicesOffset))[i] = (unsigned short)blendWeights[i].first;
471  else
472  ((short*)(vbPointer + blendIndicesOffset))[i] = (short)blendWeights[i].first;
473  }
474  else
475  {
476  if (AllowUnsignedBlendIndices)
477  ((unsigned char*)(vbPointer + blendIndicesOffset))[i] = (unsigned char)blendWeights[i].first;
478  else
479  ((char*)(vbPointer + blendIndicesOffset))[i] = (char)blendWeights[i].first;
480  }
481  ((float*)(vbPointer + blendWeightOffset))[i] = blendWeights[i].second;
482  }
483  }
484 
485  vbPointer += vertexStride;
486  }
487  }
488 
489  polygonVertexStartIndex += polygonSize;
490  }
491 
492 
493  // Create submeshes
494  for (int i = 0; i < buildMeshes->Count; ++i)
495  {
496  auto buildMesh = buildMeshes[i];
497  if (buildMesh == nullptr)
498  continue;
499 
500  auto buffer = buildMesh->buffer;
501  auto vertexBufferBinding = gcnew VertexBufferBindingData(ContentReference::Create(gcnew BufferData(BufferFlags::VertexBuffer, buffer)), gcnew VertexDeclaration(vertexElements->ToArray()), buildMesh->polygonCount * 3, 0, 0);
502 
503  auto drawData = gcnew MeshDrawData();
504  auto vbb = gcnew List<VertexBufferBindingData^>();
505  vbb->Add(vertexBufferBinding);
506  drawData->VertexBuffers = vbb->ToArray();
507  drawData->PrimitiveType = PrimitiveType::TriangleList;
508  drawData->DrawCount = buildMesh->polygonCount * 3;
509 
510  // Generate index buffer
511  // For now, if user requests 16 bits indices but it doesn't fit, it
512  // won't generate an index buffer, but ideally it should just split it in multiple render calls
513  IndexExtensions::GenerateIndexBuffer(drawData);
514  /*if (drawData->DrawCount < 65536)
515  {
516  IndexExtensions::GenerateIndexBuffer(drawData);
517  }
518  else
519  {
520  logger->Warning("The index buffer could not be generated with --force-compact-indices because it would use more than 16 bits per index.", nullptr, CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
521  }*/
522 
523  auto lMaterial = pMesh->GetNode()->GetMaterial(i);
524 
525  // Generate TNB
526  if (normalElement != NULL && uvElements.size() > 0)
527  TNBExtensions::GenerateTangentBinormal(drawData);
528 
529  auto meshData = gcnew MeshData();
530  meshData->NodeIndex = nodeMapping[(IntPtr)pMesh->GetNode()];
531  meshData->Draw = drawData;
532  if (!controlPointWeights.empty())
533  {
534  meshData->Skinning = gcnew MeshSkinningDefinition();
535  meshData->Skinning->Bones = bones->ToArray();
536  }
537 
538  // Dump materials/textures
539  FbxGeometryElementMaterial* lMaterialElement = pMesh->GetElementMaterial();
540  if (lMaterialElement != NULL && lMaterial != NULL)
541  {
542  auto isTransparent = IsTransparent(lMaterial);
543 
544  auto meshName = meshNames[pMesh];
545  if (buildMeshes->Count > 1)
546  meshName = meshName + "_" + std::to_string(i+1);
547  meshData->Name = gcnew String(meshName.c_str());
548 
549  bool sortTransparentMeshes = true; // TODO transform into importer parameter
550  if (isTransparent && sortTransparentMeshes)
551  {
552  PolySortExtensions::SortMeshPolygons(drawData, ViewDirectionForTransparentZSort);
553  }
554 
555  if (hasSkinningPosition || hasSkinningNormal || totalClusterCount > 0)
556  {
557  meshData->Parameters = gcnew ParameterCollectionData();
558 
559  if (hasSkinningPosition)
560  meshData->Parameters->Set(MaterialParameters::HasSkinningPosition, true);
561  if (hasSkinningNormal)
562  meshData->Parameters->Set(MaterialParameters::HasSkinningNormal, true);
563  if (totalClusterCount > 0)
564  meshData->Parameters->Set(MaterialParameters::SkinningBones, totalClusterCount);
565  }
566 
567  modelData->Meshes->Add(meshData);
568  }
569  }
570  }
571 
572  // return a boolean indicating whether the built material is transparent or not
573  MaterialDescription^ ProcessMeshMaterialAsset(FbxSurfaceMaterial* lMaterial, std::map<std::string, int>& uvElementMapping)
574  {
575  auto uvEltMappingOverride = uvElementMapping;
576  std::map<FbxFileTexture*, std::string> textureMap;
577  std::map<std::string, int> textureNameCount;
578 
580 
581  auto phongSurface = FbxCast<FbxSurfacePhong>(lMaterial);
582  auto lambertSurface = FbxCast<FbxSurfaceLambert>(lMaterial);
583 
584  { // The diffuse color
585  auto diffuseTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor, finalMaterial);
586  if(lambertSurface || diffuseTree != nullptr)
587  {
588  if(diffuseTree == nullptr)
589  {
590  auto diffuseColor = lambertSurface->Diffuse.Get();
591  auto diffuseFactor = lambertSurface->DiffuseFactor.Get();
592  auto diffuseColorValue = diffuseFactor * diffuseColor;
593 
594  // Create diffuse value even if the color is black
595  diffuseTree = gcnew MaterialColorNode(FbxDouble3ToColor4(diffuseColorValue));
596  ((MaterialColorNode^)diffuseTree)->Key = MaterialKeys::DiffuseColorValue;
597  ((MaterialColorNode^)diffuseTree)->AutoAssignKey = false;
598  ((MaterialColorNode^)diffuseTree)->IsReducible = false;
599  }
600 
601  if(diffuseTree != nullptr)
602  finalMaterial->AddColorNode(MaterialParameters::AlbedoDiffuse, "diffuse", diffuseTree);
603  }
604  }
605  { // The emissive color
606  auto emissiveTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor, finalMaterial);
607  if(lambertSurface || emissiveTree != nullptr)
608  {
609  if(emissiveTree == nullptr)
610  {
611  auto emissiveColor = lambertSurface->Emissive.Get();
612  auto emissiveFactor = lambertSurface->EmissiveFactor.Get();
613  auto emissiveColorValue = emissiveFactor * emissiveColor;
614 
615  // Do not create the node if the value has not been explicitly specified by the user.
616  if(emissiveColorValue != FbxDouble3(0))
617  {
618  emissiveTree = gcnew MaterialColorNode(FbxDouble3ToColor4(emissiveColorValue));
619  ((MaterialColorNode^)emissiveTree)->Key = MaterialKeys::EmissiveColorValue;
620  ((MaterialColorNode^)emissiveTree)->AutoAssignKey = false;
621  ((MaterialColorNode^)emissiveTree)->IsReducible = false;
622  }
623  }
624 
625  if(emissiveTree != nullptr)
626  finalMaterial->AddColorNode(MaterialParameters::EmissiveMap, "emissive", emissiveTree);
627  }
628  }
629  { // The ambient color
630  auto ambientTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor, finalMaterial);
631  if(lambertSurface || ambientTree != nullptr)
632  {
633  if(ambientTree == nullptr)
634  {
635  auto ambientColor = lambertSurface->Emissive.Get();
636  auto ambientFactor = lambertSurface->EmissiveFactor.Get();
637  auto ambientColorValue = ambientFactor * ambientColor;
638 
639  // Do not create the node if the value has not been explicitly specified by the user.
640  if(ambientColorValue != FbxDouble3(0))
641  {
642  ambientTree = gcnew MaterialColorNode(FbxDouble3ToColor4(ambientColorValue));
643  ((MaterialColorNode^)ambientTree)->Key = MaterialKeys::AmbientColorValue;
644  ((MaterialColorNode^)ambientTree)->AutoAssignKey = false;
645  ((MaterialColorNode^)ambientTree)->IsReducible = false;
646  }
647  }
648 
649  if(ambientTree != nullptr)
650  finalMaterial->AddColorNode(MaterialParameters::AmbientMap, "ambient", ambientTree);
651  }
652  }
653  { // The normal map
654  auto normalMapTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sNormalMap, NULL, finalMaterial);
655  if(lambertSurface || normalMapTree != nullptr)
656  {
657  if(normalMapTree == nullptr)
658  {
659  auto normalMapValue = lambertSurface->NormalMap.Get();
660 
661  // Do not create the node if the value has not been explicitly specified by the user.
662  if(normalMapValue != FbxDouble3(0))
663  {
664  normalMapTree = gcnew MaterialFloat4Node(FbxDouble3ToVector4(normalMapValue));
665  ((MaterialFloat4Node^)normalMapTree)->Key = MaterialKeys::NormalMapValue;
666  ((MaterialFloat4Node^)normalMapTree)->AutoAssignKey = false;
667  ((MaterialFloat4Node^)normalMapTree)->IsReducible = false;
668  }
669  }
670 
671  if(normalMapTree != nullptr)
672  finalMaterial->AddColorNode(MaterialParameters::NormalMap, "normalMap", normalMapTree);
673  }
674  }
675  { // The bump map
676  auto bumpMapTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sBump, FbxSurfaceMaterial::sBumpFactor, finalMaterial);
677  if(lambertSurface || bumpMapTree != nullptr)
678  {
679  if(bumpMapTree == nullptr)
680  {
681  auto bumpValue = lambertSurface->Bump.Get();
682  auto bumpFactor = lambertSurface->BumpFactor.Get();
683  auto bumpMapValue = bumpFactor * bumpValue;
684 
685  // Do not create the node if the value has not been explicitly specified by the user.
686  if(bumpMapValue != FbxDouble3(0))
687  {
688  bumpMapTree = gcnew MaterialFloat4Node(FbxDouble3ToVector4(bumpMapValue));
689  ((MaterialFloat4Node^)bumpMapTree)->Key = MaterialKeys::BumpValue;
690  ((MaterialFloat4Node^)bumpMapTree)->AutoAssignKey = false;
691  ((MaterialFloat4Node^)bumpMapTree)->IsReducible = false;
692  }
693  }
694 
695  if(bumpMapTree != nullptr)
696  finalMaterial->AddColorNode(MaterialParameters::BumpMap, "bumpMap", bumpMapTree);
697  }
698  }
699  { // The transparency
700  auto transparencyTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor, finalMaterial);
701  if(lambertSurface || transparencyTree != nullptr)
702  {
703  if(transparencyTree == nullptr)
704  {
705  auto transparencyColor = lambertSurface->TransparentColor.Get();
706  auto transparencyFactor = lambertSurface->TransparencyFactor.Get();
707  auto transparencyValue = transparencyFactor * transparencyColor;
708  auto opacityValue = std::min(1.0f, std::max(0.0f, 1-(float)transparencyValue[0]));
709 
710  // Do not create the node if the value has not been explicitly specified by the user.
711  if(opacityValue < 1)
712  {
713  transparencyTree = gcnew MaterialFloatNode(opacityValue);
714  ((MaterialFloatNode^)transparencyTree)->Key = MaterialKeys::TransparencyValue;
715  ((MaterialFloatNode^)transparencyTree)->AutoAssignKey = false;
716  ((MaterialFloatNode^)transparencyTree)->IsReducible = false;
717  }
718  }
719 
720  if(transparencyTree != nullptr)
721  finalMaterial->AddColorNode(MaterialParameters::TransparencyMap, "transparencyMap", transparencyTree);
722  }
723  }
724  { // The displacement map
725  auto displacementColorTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sDisplacementColor, FbxSurfaceMaterial::sDisplacementFactor, finalMaterial);
726  if(lambertSurface || displacementColorTree != nullptr)
727  {
728  if(displacementColorTree == nullptr)
729  {
730  auto displacementColor = lambertSurface->DisplacementColor.Get();
731  auto displacementFactor = lambertSurface->DisplacementFactor.Get();
732  auto displacementValue = displacementFactor * displacementColor;
733 
734  // Do not create the node if the value has not been explicitly specified by the user.
735  if(displacementValue != FbxDouble3(0))
736  {
737  displacementColorTree = gcnew MaterialFloat4Node(FbxDouble3ToVector4(displacementValue));
738  ((MaterialFloat4Node^)displacementColorTree)->Key = MaterialKeys::DisplacementValue;
739  ((MaterialFloat4Node^)displacementColorTree)->AutoAssignKey = false;
740  ((MaterialFloat4Node^)displacementColorTree)->IsReducible = false;
741  }
742  }
743 
744  if(displacementColorTree != nullptr)
745  finalMaterial->AddColorNode(MaterialParameters::DisplacementMap, "displacementMap", displacementColorTree);
746  }
747  }
748  { // The specular color
749  auto specularTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sSpecular, NULL, finalMaterial);
750  if(phongSurface || specularTree != nullptr)
751  {
752  if(specularTree == nullptr)
753  {
754  auto specularColor = phongSurface->Specular.Get();
755 
756  // Do not create the node if the value has not been explicitly specified by the user.
757  if(specularColor != FbxDouble3(0))
758  {
759  specularTree = gcnew MaterialColorNode(FbxDouble3ToColor4(specularColor));
760  ((MaterialColorNode^)specularTree)->Key = MaterialKeys::SpecularColorValue;
761  ((MaterialColorNode^)specularTree)->AutoAssignKey = false;
762  ((MaterialColorNode^)specularTree)->IsReducible = false;
763  }
764  }
765 
766  if(specularTree != nullptr)
767  finalMaterial->AddColorNode(MaterialParameters::AlbedoSpecular, "specular", specularTree);
768  }
769  }
770  { // The specular intensity map
771  auto specularIntensityTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sSpecularFactor, NULL, finalMaterial);
772  if(phongSurface || specularIntensityTree != nullptr)
773  {
774  if(specularIntensityTree == nullptr)
775  {
776  auto specularIntensity = phongSurface->SpecularFactor.Get();
777 
778  // Do not create the node if the value has not been explicitly specified by the user.
779  if(specularIntensity > 0)
780  {
781  specularIntensityTree = gcnew MaterialFloatNode((float)specularIntensity);
782  ((MaterialFloatNode^)specularIntensityTree)->Key = MaterialKeys::SpecularIntensity;
783  ((MaterialFloatNode^)specularIntensityTree)->AutoAssignKey = false;
784  ((MaterialFloatNode^)specularIntensityTree)->IsReducible = false;
785  }
786  }
787 
788  if(specularIntensityTree != nullptr)
789  finalMaterial->AddColorNode(MaterialParameters::SpecularIntensityMap, "specularIntensity", specularIntensityTree);
790  }
791  }
792  { // The specular power map
793  auto specularPowerTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sShininess, NULL, finalMaterial);
794  if(phongSurface || specularPowerTree != nullptr)
795  {
796  if(specularPowerTree == nullptr)
797  {
798  auto specularPower = phongSurface->Shininess.Get();
799 
800  // Do not create the node if the value has not been explicitly specified by the user.
801  if(specularPower > 0)
802  {
803  specularPowerTree = gcnew MaterialFloatNode((float)specularPower);
804  ((MaterialFloatNode^)specularPowerTree)->Key = MaterialKeys::SpecularPower;
805  ((MaterialFloatNode^)specularPowerTree)->AutoAssignKey = false;
806  ((MaterialFloatNode^)specularPowerTree)->IsReducible = false;
807  }
808  }
809 
810  if(specularPowerTree != nullptr)
811  finalMaterial->AddColorNode(MaterialParameters::SpecularPowerMap, "specularPower", specularPowerTree);
812  }
813  }
814  { // The reflection map
815  auto reflectionMapTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sReflection, FbxSurfaceMaterial::sReflectionFactor, finalMaterial);
816  if(phongSurface || reflectionMapTree != nullptr)
817  {
818  if(reflectionMapTree == nullptr)
819  {
820  auto reflectionColor = lambertSurface->DisplacementColor.Get();
821  auto reflectionFactor = lambertSurface->DisplacementFactor.Get();
822  auto reflectionValue = reflectionFactor * reflectionColor;
823 
824  // Do not create the node if the value has not been explicitly specified by the user.
825  if(reflectionValue != FbxDouble3(0))
826  {
827  reflectionMapTree = gcnew MaterialColorNode(FbxDouble3ToColor4(reflectionValue));
828  ((MaterialColorNode^)reflectionMapTree)->Key = MaterialKeys::ReflectionColorValue;
829  ((MaterialColorNode^)reflectionMapTree)->AutoAssignKey = false;
830  ((MaterialColorNode^)reflectionMapTree)->IsReducible = false;
831  }
832  }
833 
834  if(reflectionMapTree != nullptr)
835  finalMaterial->AddColorNode(MaterialParameters::ReflectionMap, "reflectionMap", reflectionMapTree);
836  }
837  }
838  return finalMaterial;
839  }
840 
841  bool IsTransparent(FbxSurfaceMaterial* lMaterial)
842  {
843  for (int i = 0; i < 2; ++i)
844  {
845  auto propertyName = i == 0 ? FbxSurfaceMaterial::sTransparentColor : FbxSurfaceMaterial::sTransparencyFactor;
846  if (propertyName == NULL)
847  continue;
848 
849  FbxProperty lProperty = lMaterial->FindProperty(propertyName);
850  if (lProperty.IsValid())
851  {
852  const int lTextureCount = lProperty.GetSrcObjectCount<FbxTexture>();
853  for (int j = 0; j < lTextureCount; ++j)
854  {
855  FbxLayeredTexture *lLayeredTexture = FbxCast<FbxLayeredTexture>(lProperty.GetSrcObject<FbxTexture>(j));
856  FbxFileTexture *lFileTexture = FbxCast<FbxFileTexture>(lProperty.GetSrcObject<FbxTexture>(j));
857  if (lLayeredTexture)
858  {
859  int lNbTextures = lLayeredTexture->GetSrcObjectCount<FbxFileTexture>();
860  if (lNbTextures > 0)
861  return true;
862  }
863  else if (lFileTexture)
864  return true;
865  }
866  if (lTextureCount == 0)
867  {
868  auto val = FbxDouble3ToVector3(lProperty.Get<FbxDouble3>());
869  if (val == Vector3::Zero || val != Vector3::One)
870  return true;
871  }
872  }
873  }
874  return false;
875  }
876 
877  IMaterialNode^ GenerateSurfaceTextureTree( FbxSurfaceMaterial* lMaterial, std::map<std::string, int>& uvElementMapping, std::map<FbxFileTexture*, std::string>& textureMap,
878  std::map<std::string, int>& textureNameCount, char const* surfaceMaterial, char const* surfaceMaterialFactor,
880  {
881  auto compositionTrees = gcnew cli::array<IMaterialNode^>(2);
882 
883  for (int i = 0; i < 2; ++i)
884  {
885  // Scan first for component name, then its factor (i.e. sDiffuse, then sDiffuseFactor)
886  auto propertyName = i == 0 ? surfaceMaterial : surfaceMaterialFactor;
887  if (propertyName == NULL)
888  continue;
889 
890  int compositionCount = 0;
891 
892  FbxProperty lProperty = lMaterial->FindProperty(propertyName);
893  if (lProperty.IsValid())
894  {
895  IMaterialNode^ previousNode = nullptr;
896  const int lTextureCount = lProperty.GetSrcObjectCount<FbxTexture>();
897  for (int j = 0; j < lTextureCount; ++j)
898  {
899  FbxLayeredTexture *lLayeredTexture = FbxCast<FbxLayeredTexture>(lProperty.GetSrcObject<FbxTexture>(j));
900  FbxFileTexture *lFileTexture = FbxCast<FbxFileTexture>(lProperty.GetSrcObject<FbxTexture>(j));
901  if (lLayeredTexture)
902  {
903  int lNbTextures = lLayeredTexture->GetSrcObjectCount<FbxFileTexture>();
904  for (int k = 0; k < lNbTextures; ++k)
905  {
906  FbxFileTexture* lSubTexture = FbxCast<FbxFileTexture>(lLayeredTexture->GetSrcObject<FbxFileTexture>(k));
907 
908  auto uvName = std::string(lSubTexture->UVSet.Get());
909  if (uvElementMapping.find(uvName) == uvElementMapping.end())
910  uvElementMapping[uvName] = uvElementMapping.size();
911 
912  auto currentMaterialReference = GenerateMaterialTextureNodeFBX(lSubTexture, uvElementMapping, textureMap, textureNameCount, finalMaterial);
913 
914  if (lNbTextures == 1 || compositionCount == 0)
915  {
916  if (previousNode == nullptr)
917  previousNode = currentMaterialReference;
918  else
919  previousNode = gcnew MaterialBinaryNode(previousNode, currentMaterialReference, MaterialBinaryOperand::Add); // not sure
920  }
921  else
922  {
923  auto newNode = gcnew MaterialBinaryNode(previousNode, currentMaterialReference, MaterialBinaryOperand::Add);
924  previousNode = newNode;
925 
926  FbxLayeredTexture::EBlendMode blendMode;
927  lLayeredTexture->GetTextureBlendMode(k, blendMode);
928  newNode->Operand = BlendModeToBlendOperand(blendMode);
929  }
930 
931  compositionCount++;
932  }
933  }
934  else if (lFileTexture)
935  {
936  compositionCount++;
937 
938  auto newMaterialReference = GenerateMaterialTextureNodeFBX(lFileTexture, uvElementMapping, textureMap, textureNameCount, finalMaterial);
939 
940  if (previousNode == nullptr)
941  previousNode = newMaterialReference;
942  else
943  previousNode = gcnew MaterialBinaryNode(previousNode, newMaterialReference, MaterialBinaryOperand::Add); // not sure
944  }
945  }
946 
947  compositionTrees[i] = previousNode;
948  }
949  }
950 
951  // If we only have one of either Color or Factor, use directly, otherwise multiply them together
952  IMaterialNode^ compositionTree;
953  if (compositionTrees[0] == nullptr) // TODO do we want only the factor??? -> delete
954  {
955  compositionTree = compositionTrees[1];
956  }
957  else if (compositionTrees[1] == nullptr)
958  {
959  compositionTree = compositionTrees[0];
960  }
961  else
962  {
963  compositionTree = gcnew MaterialBinaryNode(compositionTrees[0], compositionTrees[1], MaterialBinaryOperand::Multiply);
964  }
965 
966  return compositionTree;
967  }
968 
969  MaterialBinaryOperand BlendModeToBlendOperand(FbxLayeredTexture::EBlendMode blendMode)
970  {
971  switch (blendMode)
972  {
973  case FbxLayeredTexture::eOver:
974  return MaterialBinaryOperand::Over;
975  case FbxLayeredTexture::eAdditive:
976  return MaterialBinaryOperand::Add;
977  case FbxLayeredTexture::eModulate:
978  return MaterialBinaryOperand::Multiply;
979  //case FbxLayeredTexture::eTranslucent:
980  // return MaterialBinaryOperand::Multiply;
981  //case FbxLayeredTexture::eModulate2:
982  // return MaterialBinaryOperand::Multiply;
983  //case FbxLayeredTexture::eNormal:
984  // return MaterialBinaryOperand::Multiply;
985  //case FbxLayeredTexture::eDissolve:
986  // return MaterialBinaryOperand::Multiply;
987  case FbxLayeredTexture::eDarken:
988  return MaterialBinaryOperand::Darken;
989  case FbxLayeredTexture::eColorBurn:
990  return MaterialBinaryOperand::ColorBurn;
991  case FbxLayeredTexture::eLinearBurn:
992  return MaterialBinaryOperand::LinearBurn;
993  //case FbxLayeredTexture::eDarkerColor:
994  // return MaterialBinaryOperand::Multiply;
995  case FbxLayeredTexture::eLighten:
996  return MaterialBinaryOperand::Lighten;
997  case FbxLayeredTexture::eScreen:
998  return MaterialBinaryOperand::Screen;
999  case FbxLayeredTexture::eColorDodge:
1000  return MaterialBinaryOperand::ColorDodge;
1001  case FbxLayeredTexture::eLinearDodge:
1002  return MaterialBinaryOperand::LinearDodge;
1003  //case FbxLayeredTexture::eLighterColor:
1004  // return MaterialBinaryOperand::Multiply;
1005  case FbxLayeredTexture::eSoftLight:
1006  return MaterialBinaryOperand::SoftLight;
1007  case FbxLayeredTexture::eHardLight:
1008  return MaterialBinaryOperand::HardLight;
1009  //case FbxLayeredTexture::eVividLight:
1010  // return MaterialBinaryOperand::Multiply;
1011  //case FbxLayeredTexture::eLinearLight:
1012  // return MaterialBinaryOperand::Multiply;
1013  case FbxLayeredTexture::ePinLight:
1014  return MaterialBinaryOperand::PinLight;
1015  case FbxLayeredTexture::eHardMix:
1016  return MaterialBinaryOperand::HardMix;
1017  case FbxLayeredTexture::eDifference:
1018  return MaterialBinaryOperand::Difference;
1019  case FbxLayeredTexture::eExclusion:
1020  return MaterialBinaryOperand::Exclusion;
1021  case FbxLayeredTexture::eSubtract:
1022  return MaterialBinaryOperand::Subtract;
1023  case FbxLayeredTexture::eDivide:
1024  return MaterialBinaryOperand::Divide;
1025  case FbxLayeredTexture::eHue:
1026  return MaterialBinaryOperand::Hue;
1027  case FbxLayeredTexture::eSaturation:
1028  return MaterialBinaryOperand::Saturation;
1029  //case FbxLayeredTexture::eColor:
1030  // return MaterialBinaryOperand::Multiply;
1031  //case FbxLayeredTexture::eLuminosity:
1032  // return MaterialBinaryOperand::Multiply;
1033  case FbxLayeredTexture::eOverlay:
1034  return MaterialBinaryOperand::Overlay;
1035  default:
1036  logger->Error("Material blending mode '{0}' is not supported yet. Multiplying blending mode will be used instead.", gcnew Int32(blendMode));
1037  return MaterialBinaryOperand::Multiply;
1038  }
1039  }
1040 
1041  ShaderClassSource^ GenerateTextureLayerFBX(FbxFileTexture* lFileTexture, std::map<std::string, int>& uvElementMapping, MeshData^ meshData, int& textureCount, ParameterKey<Texture^>^ surfaceMaterialKey)
1042  {
1043  auto texScale = lFileTexture->GetUVScaling();
1044  auto texturePath = FindFilePath(lFileTexture);
1045 
1046  return TextureLayerGenerator::GenerateTextureLayer(vfsOutputFilename, texturePath, uvElementMapping[std::string(lFileTexture->UVSet.Get())], Vector2((float)texScale[0], (float)texScale[1]) ,
1047  textureCount, surfaceMaterialKey,
1048  meshData,
1049  nullptr);
1050  }
1051 
1052  String^ FindFilePath(FbxFileTexture* lFileTexture)
1053  {
1054  auto relFileName = gcnew String(lFileTexture->GetRelativeFileName());
1055  auto absFileName = gcnew String(lFileTexture->GetFileName());
1056 
1057  // First try to get the texture filename by relative path, if not valid then use absolute path
1058  // (According to FBX doc, resolved first by absolute name, and relative name if absolute name is not valid)
1059  auto fileNameToUse = Path::Combine(inputPath, relFileName);
1060  if(fileNameToUse->StartsWith("\\\\"))
1061  {
1062  logger->Warning("Importer detected a network address in referenced assets. This may temporary block the build if the file does not exist. [Address='{0}']", fileNameToUse);
1063  }
1064  if (!File::Exists(fileNameToUse))
1065  {
1066  fileNameToUse = absFileName;
1067  }
1068 
1069  return fileNameToUse;
1070  }
1071 
1072  MaterialReferenceNode^ GenerateMaterialTextureNodeFBX(FbxFileTexture* lFileTexture, std::map<std::string, int>& uvElementMapping, std::map<FbxFileTexture*, std::string>& textureMap, std::map<std::string, int>& textureNameCount, SiliconStudio::Paradox::Assets::Materials::MaterialDescription^ finalMaterial)
1073  {
1074  auto texScale = lFileTexture->GetUVScaling();
1075  auto texturePath = FindFilePath(lFileTexture);
1076  auto wrapModeU = lFileTexture->GetWrapModeU();
1077  auto wrapModeV = lFileTexture->GetWrapModeV();
1078  bool wrapTextureU = (wrapModeU == FbxTexture::EWrapMode::eRepeat);
1079  bool wrapTextureV = (wrapModeV == FbxTexture::EWrapMode::eRepeat);
1080 
1081  auto index = textureMap.find(lFileTexture);
1082  if (index != textureMap.end())
1083  {
1084  auto textureName = textureMap[lFileTexture];
1085  auto materialReference = gcnew MaterialReferenceNode(gcnew String(textureName.c_str()));
1086  return materialReference;
1087  }
1088  else
1089  {
1090  auto textureValue = TextureLayerGenerator::GenerateMaterialTextureNode(vfsOutputFilename, texturePath, uvElementMapping[std::string(lFileTexture->UVSet.Get())], Vector2((float)texScale[0], (float)texScale[1]), wrapTextureU, wrapTextureV, nullptr);
1091 
1092  auto textureNamePtr = Marshal::StringToHGlobalAnsi(textureValue->TextureName);
1093  std::string textureName = std::string((char*)textureNamePtr.ToPointer());
1094  Marshal:: FreeHGlobal(textureNamePtr);
1095 
1096  auto textureCount = GetTextureNameCount(textureNameCount, textureName);
1097  if (textureCount > 1)
1098  textureName = textureName + "_" + std::to_string(textureCount - 1);
1099 
1100  auto referenceName = gcnew String(textureName.c_str());
1101  auto materialReference = gcnew MaterialReferenceNode(referenceName);
1102  finalMaterial->AddNode(referenceName, textureValue);
1103  textureMap[lFileTexture] = textureName;
1104  return materialReference;
1105  }
1106 
1107  return nullptr;
1108  }
1109 
1110  int GetTextureNameCount(std::map<std::string, int>& textureNameCount, std::string textureName)
1111  {
1112  auto textureFound = textureNameCount.find(textureName);
1113  if (textureFound == textureNameCount.end())
1114  textureNameCount[textureName] = 1;
1115  else
1116  textureNameCount[textureName] = textureNameCount[textureName] + 1;
1117  return textureNameCount[textureName];
1118  }
1119 
1120  void ProcessCamera(List<CameraInfo^>^ cameras, FbxNode* pNode, FbxCamera* pCamera, std::map<FbxNode*, std::string>& nodeNames)
1121  {
1122  auto cameraInfo = gcnew CameraInfo();
1123  auto cameraData = gcnew CameraComponentData();
1124  cameraInfo->Data = cameraData;
1125 
1126  cameraInfo->NodeName = gcnew String(nodeNames[pNode].c_str());
1127 
1128  if (pCamera->FilmAspectRatio.IsValid())
1129  {
1130  cameraData->AspectRatio = (float)pCamera->FilmAspectRatio.Get();
1131  }
1132 
1133  // TODO: Check vertical FOV formulas
1134  if (!exportedFromMaya && pCamera->FieldOfView.IsValid() && pCamera->FilmAspectRatio.IsValid()) // Only Focal Length is valid when exported from maya
1135  {
1136  auto aspectRatio = pCamera->FilmAspectRatio.Get();
1137  auto diagonalFov = pCamera->FieldOfView.Get();
1138  cameraData->VerticalFieldOfView = (float)(2.0 * Math::Atan(Math::Tan(diagonalFov * Math::PI / 180.0 / 2.0) / Math::Sqrt(1 + aspectRatio * aspectRatio)));
1139  }
1140  else if (pCamera->FocalLength.IsValid() && pCamera->FilmHeight.IsValid())
1141  {
1142  cameraData->VerticalFieldOfView = (float)FocalLengthToVerticalFov(pCamera->FilmHeight.Get(), pCamera->FocalLength.Get());
1143  }
1144 
1145  if (pNode->GetTarget() != NULL)
1146  {
1147  auto targetNodeIndex = nodeMapping[(IntPtr)pNode->GetTarget()];
1148  cameraInfo->TargetNodeName = nodes[targetNodeIndex].Name;
1149  }
1150  if (pCamera->NearPlane.IsValid())
1151  {
1152  cameraData->NearPlane = (float)pCamera->NearPlane.Get();
1153  }
1154  if (pCamera->FarPlane.IsValid())
1155  {
1156  cameraData->FarPlane = (float)pCamera->FarPlane.Get();
1157  }
1158 
1159  cameras->Add(cameraInfo);
1160  }
1161 
1162  void ProcessLight(List<LightInfo^>^ lights, FbxNode* pNode, FbxLight* pLight, std::map<FbxNode*, std::string>& nodeNames)
1163  {
1164  auto lightInfo = gcnew LightInfo();
1165  auto lightData = gcnew LightComponentData();
1166  lightInfo->Data = lightData;
1167 
1168  lightInfo->NodeName = gcnew String(nodeNames[pNode].c_str());
1169 
1170  // A FbxLight points along negative Y axis.
1171  lightData->LightDirection = Vector3(0.0f, -1.0f, 0.0f);
1172 
1173  switch (pLight->LightType.Get())
1174  {
1175  case FbxLight::ePoint:
1176  lightData->Deferred = true;
1177  lightData->Type = LightType::Point;
1178  lightData->DecayStart = (float)pLight->DecayStart.Get();
1179  // We support only spherical light (radius > 0)
1180  if (lightData->DecayStart == 0.0)
1181  return;
1182  break;
1183  case FbxLight::eDirectional:
1184  lightData->Deferred = false;
1185  lightData->Type = LightType::Directional;
1186  break;
1187  default:
1188  logger->Error("The light type '{0}' is not supported yet. The light will be ignored.", gcnew Int32(pLight->LightType.Get()));
1189  return;
1190  }
1191  auto lightColor = pLight->Color.IsValid() ? pLight->Color.Get() : FbxDouble3(1.0, 1.0, 1.0);
1192  lightData->Color = Color3((float)lightColor[0], (float)lightColor[1], (float)lightColor[2]);
1193  lightData->Intensity = (float)(pLight->Intensity.IsValid() ? pLight->Intensity.Get() * 0.01 : 1.0);
1194  lightData->Layers = RenderLayers::RenderLayerAll;
1195  lights->Add(lightInfo);
1196  }
1197 
1198  void ProcessAttribute(FbxNode* pNode, FbxNodeAttribute* pAttribute, std::map<FbxMesh*, std::string> meshNames)
1199  {
1200  if(!pAttribute) return;
1201 
1202  if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh)
1203  {
1204  ProcessMesh((FbxMesh*)pAttribute, meshNames);
1205  }
1206  }
1207 
1208  void RegisterNode(FbxNode* pNode, int parentIndex, std::map<FbxNode*, std::string>& nodeNames)
1209  {
1210  auto resultNode = gcnew EntityData();
1211  resultNode->Components->Add(TransformationComponent::Key, gcnew TransformationComponentData());
1212 
1213  int currentIndex = nodes.Count;
1214 
1215  nodeMapping[(IntPtr)pNode] = currentIndex;
1216 
1217  // Create node
1218  ModelNodeDefinition modelNodeDefinition;
1219  modelNodeDefinition.ParentIndex = parentIndex;
1220  modelNodeDefinition.Transform.Scaling = Vector3::One;
1221  modelNodeDefinition.Name = gcnew String(nodeNames[pNode].c_str());
1222  modelNodeDefinition.Flags = ModelNodeFlags::Default;
1223  nodes.Add(modelNodeDefinition);
1224 
1225  // Recursively process the children nodes.
1226  for(int j = 0; j < pNode->GetChildCount(); j++)
1227  {
1228  RegisterNode(pNode->GetChild(j), currentIndex, nodeNames);
1229  }
1230  }
1231 
1232  void ProcessNode(FbxNode* pNode, std::map<FbxMesh*, std::string> meshNames)
1233  {
1234  auto resultNode = nodeMapping[(IntPtr)pNode];
1235  auto node = &modelData->Hierarchy->Nodes[resultNode];
1236 
1237  auto localTransform = pNode->EvaluateLocalTransform();
1238 
1239  auto translation = FbxDouble4ToVector4(localTransform.GetT());
1240  auto rotation = FbxDouble3ToVector3(localTransform.GetR()) * (float)Math::PI / 180.0f;
1241  auto scaling = FbxDouble3ToVector3(localTransform.GetS());
1242 
1243  if (swapHandedness)
1244  {
1245  translation.Z = -translation.Z;
1246  rotation.Y = -rotation.Y;
1247  rotation.X = -rotation.X;
1248  }
1249 
1250  Quaternion quatX, quatY, quatZ;
1251 
1252  Quaternion::RotationX(rotation.X, quatX);
1253  Quaternion::RotationY(rotation.Y, quatY);
1254  Quaternion::RotationZ(rotation.Z, quatZ);
1255 
1256  auto rotationQuaternion = quatX * quatY * quatZ;
1257 
1258  node->Transform.Translation = (Vector3)translation;
1259  node->Transform.Rotation = rotationQuaternion;
1260  node->Transform.Scaling = (Vector3)scaling;
1261 
1262  const char* nodeName = pNode->GetName();
1263 
1264  // Process the node's attributes.
1265  for(int i = 0; i < pNode->GetNodeAttributeCount(); i++)
1266  ProcessAttribute(pNode, pNode->GetNodeAttributeByIndex(i), meshNames);
1267 
1268  // Recursively process the children nodes.
1269  for(int j = 0; j < pNode->GetChildCount(); j++)
1270  {
1271  ProcessNode(pNode->GetChild(j), meshNames);
1272  }
1273  }
1274 
1275  ref class CurveEvaluator
1276  {
1277  FbxAnimCurve* curve;
1278  int index;
1279 
1280  public:
1281  CurveEvaluator(FbxAnimCurve* curve)
1282  : curve(curve), index(0)
1283  {
1284  }
1285 
1287  {
1288  auto fbxTime = FbxTime((long long)time.Ticks * FBXSDK_TIME_ONE_SECOND.Get() / (long long)CompressedTimeSpan::TicksPerSecond);
1289  int currentIndex = index;
1290  auto result = curve->Evaluate(fbxTime, &currentIndex);
1291  index = currentIndex;
1292 
1293  return result;
1294  }
1295  };
1296 
1297  template <class T>
1298  AnimationCurve<T>^ ProcessAnimationCurveVector(AnimationClip^ animationClip, int nodeData, String^ name, int numCurves, FbxAnimCurve** curves, float maxErrorThreshold)
1299  {
1300  auto keyFrames = ProcessAnimationCurveFloatsHelper<T>(curves, numCurves);
1301  if (keyFrames == nullptr)
1302  return nullptr;
1303 
1304  // Add curve
1305  auto animationCurve = gcnew AnimationCurve<T>();
1306 
1307  // Switch to cubic implicit interpolation mode for Vector3
1308  animationCurve->InterpolationType = AnimationCurveInterpolationType::Cubic;
1309 
1310  // Create keys
1311  for (int i = 0; i < keyFrames->Count; ++i)
1312  {
1313  animationCurve->KeyFrames->Add(keyFrames[i]);
1314  }
1315 
1316  animationClip->AddCurve(name, animationCurve);
1317 
1318  if (keyFrames->Count > 0)
1319  {
1320  auto curveDuration = keyFrames[keyFrames->Count - 1].Time;
1321  if (animationClip->Duration < curveDuration)
1322  animationClip->Duration = curveDuration;
1323  }
1324 
1325  return animationCurve;
1326  }
1327 
1328  void ProcessAnimationCurveRotation(AnimationClip^ animationClip, int nodeData, String^ name, FbxAnimCurve** curves, float maxErrorThreshold)
1329  {
1330  auto keyFrames = ProcessAnimationCurveFloatsHelper<Vector3>(curves, 3);
1331  if (keyFrames == nullptr)
1332  return;
1333 
1334  // Convert euler angles to radians
1335  for (int i = 0; i < keyFrames->Count; ++i)
1336  {
1337  auto keyFrame = keyFrames[i];
1338  keyFrame.Value *= (float)Math::PI / 180.0f;
1339  keyFrames[i] = keyFrame;
1340  }
1341 
1342  // Add curve
1343  auto animationCurve = gcnew AnimationCurve<Quaternion>();
1344 
1345  // Create keys
1346  for (int i = 0; i < keyFrames->Count; ++i)
1347  {
1348  auto keyFrame = keyFrames[i];
1349  if (swapHandedness)
1350  {
1351  keyFrame.Value.X = -keyFrame.Value.X;
1352  keyFrame.Value.Y = -keyFrame.Value.Y;
1353  }
1354 
1355  Quaternion quatX, quatY, quatZ;
1356 
1357  Quaternion::RotationX(keyFrame.Value.X, quatX);
1358  Quaternion::RotationY(keyFrame.Value.Y, quatY);
1359  Quaternion::RotationZ(keyFrame.Value.Z, quatZ);
1360 
1361  auto rotationQuaternion = quatX * quatY * quatZ;
1362 
1363  KeyFrameData<Quaternion> newKeyFrame;
1364  newKeyFrame.Time = keyFrame.Time;
1365  newKeyFrame.Value = rotationQuaternion;
1366  animationCurve->KeyFrames->Add(newKeyFrame);
1367  }
1368 
1369  animationClip->AddCurve(name, animationCurve);
1370 
1371  if (keyFrames->Count > 0)
1372  {
1373  auto curveDuration = keyFrames[keyFrames->Count - 1].Time;
1374  if (animationClip->Duration < curveDuration)
1375  animationClip->Duration = curveDuration;
1376  }
1377  }
1378 
1379  template <typename T>
1380  List<KeyFrameData<T>>^ ProcessAnimationCurveFloatsHelper(FbxAnimCurve** curves, int numCurves)
1381  {
1382  FbxTime startTime = FBXSDK_TIME_INFINITE;
1383  FbxTime endTime = FBXSDK_TIME_MINUS_INFINITE;
1384  for (int i = 0; i < numCurves; ++i)
1385  {
1386  auto curve = curves[i];
1387 
1388  // If one of the expected channel is null, the group is skipped.
1389  // Ideally, we would still want to use default values
1390  // (i.e. in the unlikely situation where X and Y have animation channels but not Z, it should still be processed with default Z values).
1391  if (curve == NULL)
1392  return nullptr;
1393 
1394  FbxTimeSpan timeSpan;
1395  curve->GetTimeInterval(timeSpan);
1396 
1397  if (curve != NULL && curve->KeyGetCount() > 0)
1398  {
1399  auto firstKeyTime = curve->KeyGetTime(0);
1400  auto lastKeyTime = curve->KeyGetTime(curve->KeyGetCount() - 1);
1401  if (startTime > firstKeyTime)
1402  startTime = firstKeyTime;
1403  if (endTime < lastKeyTime)
1404  endTime = lastKeyTime;
1405  }
1406  }
1407 
1408  if (startTime == FBXSDK_TIME_INFINITE
1409  || endTime == FBXSDK_TIME_MINUS_INFINITE)
1410  {
1411  // No animation
1412  return nullptr;
1413  }
1414 
1415  auto keyFrames = gcnew List<KeyFrameData<T>>();
1416 
1417  const float framerate = static_cast<float>(FbxTime::GetFrameRate(scene->GetGlobalSettings().GetTimeMode()));
1418  auto oneFrame = FbxTime::GetOneFrameValue(scene->GetGlobalSettings().GetTimeMode());
1419 
1420  // Step1: Pregenerate curve with discontinuities
1421  int index = 0;
1422  bool discontinuity = false;
1423 
1424  int currentKeyIndices[4];
1425  int currentEvaluationIndices[4];
1426  bool isConstant[4];
1427  bool hasDiscontinuity[4];
1428 
1429  for (int i = 0; i < numCurves; ++i)
1430  {
1431  auto curve = curves[i];
1432  currentKeyIndices[i] = 0;
1433  currentEvaluationIndices[i] = 0;
1434  isConstant[i] = false;
1435  hasDiscontinuity[i] = false;
1436  }
1437 
1438  //float values[4];
1439  auto key = KeyFrameData<T>();
1440  float* values = (float*)&key.Value;
1441 
1442  FbxTime time;
1443  bool lastFrame = false;
1444  for (time = startTime; time < endTime || !lastFrame; time += oneFrame)
1445  {
1446  // Last frame with time = endTime
1447  if (time >= endTime)
1448  {
1449  lastFrame = true;
1450  time = endTime;
1451  }
1452 
1453  key.Time = FBXTimeToTimeSpane(time);
1454 
1455  bool hasDiscontinuity = false;
1456  bool needUpdate = false;
1457 
1458  for (int i = 0; i < numCurves; ++i)
1459  {
1460  auto curve = curves[i];
1461  int currentIndex = currentKeyIndices[i];
1462 
1463  FbxAnimCurveKey curveKey;
1464 
1465  // Advance to appropriate key that should be active during this frame
1466  while (curve->KeyGetTime(currentIndex) <= time && currentIndex + 1 < curve->KeyGetCount())
1467  {
1468  ++currentIndex;
1469 
1470  // If new key over constant, there is a discontinuity
1471  bool wasConstant = isConstant[i];
1472  hasDiscontinuity |= wasConstant;
1473 
1474  auto interpolation = curve->KeyGetInterpolation(currentIndex);
1475  isConstant[i] = interpolation == FbxAnimCurveDef::eInterpolationConstant;
1476  }
1477 
1478  currentKeyIndices[i] = currentIndex;
1479 
1480  // Update non-constant values
1481  if (!isConstant[i])
1482  {
1483  values[i] = curve->Evaluate(time, &currentEvaluationIndices[i]);
1484  needUpdate = true;
1485  }
1486  }
1487 
1488  // No need to update values, they are same as previous frame
1489  //if (!needUpdate && !hasDiscontinuity)
1490  // continue;
1491 
1492  // If discontinuity, we need to add previous values twice (with updated time), and new values twice (with updated time) to ignore any implicit tangents
1493  if (hasDiscontinuity)
1494  {
1495  keyFrames->Add(key);
1496  keyFrames->Add(key);
1497  }
1498 
1499  // Update constant values
1500  for (int i = 0; i < numCurves; ++i)
1501  {
1502  auto curve = curves[i];
1503  if (isConstant[i])
1504  values[i] = curve->Evaluate(time, &currentEvaluationIndices[i]);
1505  }
1506 
1507  keyFrames->Add(key);
1508  if (hasDiscontinuity)
1509  keyFrames->Add(key);
1510  }
1511 
1512  return keyFrames;
1513  }
1514 
1516  {
1517  for (int i = 0; i < channel->KeyFrames->Count; ++i)
1518  {
1519  auto keyFrame = channel->KeyFrames[i];
1520  keyFrame.Value *= (float)Math::PI / 180.0f;
1521  channel->KeyFrames[i] = keyFrame;
1522  }
1523  }
1524 
1526  {
1527  // Used for handedness conversion
1528  for (int i = 0; i < channel->KeyFrames->Count; ++i)
1529  {
1530  auto keyFrame = channel->KeyFrames[i];
1531  keyFrame.Value.Z = -keyFrame.Value.Z;
1532  channel->KeyFrames[i] = keyFrame;
1533  }
1534  }
1535 
1536  void ComputeFovFromFL(AnimationCurve<float>^ channel, FbxCamera* pCamera)
1537  {
1538  // Used for handedness conversion
1539  for (int i = 0; i < channel->KeyFrames->Count; ++i)
1540  {
1541  auto keyFrame = channel->KeyFrames[i];
1542  keyFrame.Value = (float)FocalLengthToVerticalFov(pCamera->FilmHeight.Get(), keyFrame.Value);
1543  channel->KeyFrames[i] = keyFrame;
1544  }
1545  }
1546 
1547  void MultiplyChannel(AnimationCurve<float>^ channel, double factor)
1548  {
1549  // Used for handedness conversion
1550  for (int i = 0; i < channel->KeyFrames->Count; ++i)
1551  {
1552  auto keyFrame = channel->KeyFrames[i];
1553  keyFrame.Value = (float)(factor * keyFrame.Value);
1554  channel->KeyFrames[i] = keyFrame;
1555  }
1556  }
1557 
1558  void ProcessAnimation(AnimationClip^ animationClip, FbxAnimLayer* animLayer, FbxNode* node)
1559  {
1560  auto nodeData = nodeMapping[(IntPtr)node];
1561 
1562  // Directly interpolate matrix frame per frame (test)
1563  /*auto animationChannel = gcnew array<AnimationChannel^>(16);
1564  for (int i = 0; i < 16; ++i)
1565  {
1566  animationChannel[i] = gcnew AnimationChannel();
1567  animationChannel[i]->TargetNode = nodeData;
1568  animationChannel[i]->TargetProperty = i.ToString();
1569  animationData->AnimationChannels->Add(animationChannel[i]);
1570  }
1571  for (int i = 0; i < 200; ++i)
1572  {
1573  FbxTime evalTime;
1574  evalTime.SetMilliSeconds(10 * i);
1575  FbxXMatrix matrix = node->EvaluateLocalTransform(evalTime);
1576  for (int i = 0; i < 16; ++i)
1577  {
1578  auto key2 = KeyFrameData<float>();
1579  key2.Value = ((double*)matrix)[i];
1580  double time = (double)evalTime.Get();
1581  time *= (double)TimeSpan::TicksPerSecond / (double)FBXSDK_TIME_ONE_SECOND.Get();
1582  key2.Time = TimeSpan((long long)time);
1583 
1584  animationChannel[i]->Add(key2);
1585  }
1586  }
1587  FbxXMatrix localMatrix = node->EvaluateLocalTransform(FbxTime(0));
1588 
1589  FbxVector4 t = localMatrix.GetT();
1590  FbxVector4 r = localMatrix.GetR();*/
1591 
1592  auto rotationOffset = node->RotationOffset.Get();
1593  auto rotationPivot = node->RotationPivot.Get();
1594  auto quatInterpolate = node->QuaternionInterpolate.Get();
1595  auto rotationOrder = node->RotationOrder.Get();
1596 
1597  FbxAnimCurve* curves[3];
1598 
1599  auto nodeName = nodes[nodeData].Name;
1600 
1601  curves[0] = node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X);
1602  curves[1] = node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y);
1603  curves[2] = node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z);
1604  auto translation = ProcessAnimationCurveVector<Vector3>(animationClip, nodeData, String::Format("Transformation.Translation[{0}]", nodeName), 3, curves, 0.005f);
1605 
1606  curves[0] = node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X);
1607  curves[1] = node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y);
1608  curves[2] = node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z);
1609  ProcessAnimationCurveRotation(animationClip, nodeData, String::Format("Transformation.Rotation[{0}]", nodeName), curves, 0.01f);
1610 
1611  curves[0] = node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X);
1612  curves[1] = node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y);
1613  curves[2] = node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z);
1614  auto scaling = ProcessAnimationCurveVector<Vector3>(animationClip, nodeData, String::Format("Transformation.Scaling[{0}]", nodeName), 3, curves, 0.005f);
1615 
1616  if (swapHandedness)
1617  {
1618  if (translation != nullptr)
1619  ReverseChannelZ(translation);
1620  }
1621 
1622  // Change Y scaling for "root" nodes, if necessary
1623  /*if (node == scene->GetRootNode() && scalingY != nullptr && swapHandedness == true)
1624  {
1625  ReverseChannelY(scaling);
1626  }*/
1627 
1628  FbxCamera* camera = node->GetCamera();
1629  if (camera != NULL)
1630  {
1631  if(camera->FieldOfViewY.GetCurve(animLayer))
1632  {
1633  curves[0] = camera->FieldOfViewY.GetCurve(animLayer);
1634  auto FovAnimChannel = ProcessAnimationCurveVector<float>(animationClip, nodeData, "Camera.FieldOfViewVertical", 1, curves, 0.01f);
1635  ConvertDegreeToRadians(FovAnimChannel);
1636 
1637  if(!exportedFromMaya)
1638  MultiplyChannel(FovAnimChannel, 0.6); // Random factor to match what we see in 3dsmax, need to check why!
1639  }
1640 
1641 
1642  if(camera->FocalLength.GetCurve(animLayer))
1643  {
1644  curves[0] = camera->FocalLength.GetCurve(animLayer);
1645  auto flAnimChannel = ProcessAnimationCurveVector<float>(animationClip, nodeData, "Camera.FieldOfViewVertical", 1, curves, 0.01f);
1646  ComputeFovFromFL(flAnimChannel, camera);
1647  }
1648  }
1649 
1650  for(int i = 0; i < node->GetChildCount(); ++i)
1651  {
1652  ProcessAnimation(animationClip, animLayer, node->GetChild(i));
1653  }
1654  }
1655 
1656  void SetPivotStateRecursive(FbxNode* node)
1657  {
1658  node->SetPivotState(FbxNode::eSourcePivot, FbxNode::ePivotActive);
1659  node->SetPivotState(FbxNode::eDestinationPivot, FbxNode::ePivotActive);
1660 
1661  for(int i = 0; i < node->GetChildCount(); ++i)
1662  {
1663  SetPivotStateRecursive(node->GetChild(i));
1664  }
1665  }
1666 
1668  {
1669  auto animationClip = gcnew AnimationClip();
1670 
1671  int animStackCount = scene->GetMemberCount<FbxAnimStack>();
1672 
1673  // TODO: We probably don't support more than one anim stack count.
1674  for (int i = 0; i < animStackCount; ++i)
1675  {
1676  FbxAnimStack* animStack = scene->GetMember<FbxAnimStack>(i);
1677  int animLayerCount = animStack->GetMemberCount<FbxAnimLayer>();
1678  FbxAnimLayer* animLayer = animStack->GetMember<FbxAnimLayer>(0);
1679 
1680  // From http://www.the-area.com/forum/autodesk-fbx/fbx-sdk/resetpivotsetandconvertanimation-issue/page-1/
1681  scene->GetRootNode()->ResetPivotSet(FbxNode::eDestinationPivot);
1682  SetPivotStateRecursive(scene->GetRootNode());
1683  scene->GetRootNode()->ConvertPivotAnimationRecursive(animStack, FbxNode::eDestinationPivot, 30.0f);
1684 
1685  ProcessAnimation(animationClip, animLayer, scene->GetRootNode());
1686 
1687  scene->GetRootNode()->ResetPivotSet(FbxNode::eSourcePivot);
1688  }
1689 
1690  if (animationClip->Curves->Count == 0)
1691  animationClip = nullptr;
1692 
1693  return animationClip;
1694  }
1695 
1696  ref class BuildMesh
1697  {
1698  public:
1699  array<Byte>^ buffer;
1702  };
1703 
1705  {
1706  public:
1707  property bool ImportTemplates;
1708  property bool ImportPivots;
1709  property bool ImportGlobalSettings;
1710  property bool ImportCharacters;
1711  property bool ImportConstraints;
1712  property bool ImportGobos;
1713  property bool ImportShapes;
1714  property bool ImportLinks;
1715  property bool ImportMaterials;
1716  property bool ImportTextures;
1717  property bool ImportModels;
1718  property bool ImportAnimations;
1719  property bool ExtractEmbeddedData;
1720 
1721  public:
1723  {
1724  auto config = gcnew ImportConfiguration();
1725 
1726  config->ImportTemplates = true;
1727  config->ImportPivots = true;
1728  config->ImportGlobalSettings = true;
1729  config->ImportCharacters = true;
1730  config->ImportConstraints = true;
1731  config->ImportGobos = true;
1732  config->ImportShapes = true;
1733  config->ImportLinks = true;
1734  config->ImportMaterials = true;
1735  config->ImportTextures = true;
1736  config->ImportModels = true;
1737  config->ImportAnimations = true;
1738  config->ExtractEmbeddedData = true;
1739 
1740  return config;
1741  }
1742 
1744  {
1745  auto config = gcnew ImportConfiguration();
1746 
1747  config->ImportTemplates = false;
1748  config->ImportPivots = false;
1749  config->ImportGlobalSettings = false;
1750  config->ImportCharacters = false;
1751  config->ImportConstraints = false;
1752  config->ImportGobos = false;
1753  config->ImportShapes = false;
1754  config->ImportLinks = false;
1755  config->ImportMaterials = true;
1756  config->ImportTextures = false;
1757  config->ImportModels = true;
1758  config->ImportAnimations = false;
1759  config->ExtractEmbeddedData = false;
1760 
1761  return config;
1762  }
1763 
1765  {
1766  auto config = gcnew ImportConfiguration();
1767 
1768  config->ImportTemplates = false;
1769  config->ImportPivots = false;
1770  config->ImportGlobalSettings = false;
1771  config->ImportCharacters = false;
1772  config->ImportConstraints = false;
1773  config->ImportGobos = false;
1774  config->ImportShapes = false;
1775  config->ImportLinks = false;
1776  config->ImportMaterials = true;
1777  config->ImportTextures = false;
1778  config->ImportModels = false;
1779  config->ImportAnimations = false;
1780  config->ExtractEmbeddedData = false;
1781 
1782  return config;
1783  }
1784 
1786  {
1787  auto config = gcnew ImportConfiguration();
1788 
1789  config->ImportTemplates = false;
1790  config->ImportPivots = false;
1791  config->ImportGlobalSettings = false;
1792  config->ImportCharacters = false;
1793  config->ImportConstraints = false;
1794  config->ImportGobos = false;
1795  config->ImportShapes = false;
1796  config->ImportLinks = false;
1797  config->ImportMaterials = false;
1798  config->ImportTextures = false;
1799  config->ImportModels = false;
1800  config->ImportAnimations = true;
1801  config->ExtractEmbeddedData = false;
1802 
1803  return config;
1804  }
1805 
1807  {
1808  auto config = gcnew ImportConfiguration();
1809 
1810  config->ImportTemplates = false;
1811  config->ImportPivots = false;
1812  config->ImportGlobalSettings = false;
1813  config->ImportCharacters = false;
1814  config->ImportConstraints = false;
1815  config->ImportGobos = false;
1816  config->ImportShapes = false;
1817  config->ImportLinks = false;
1818  config->ImportMaterials = false;
1819  config->ImportTextures = true;
1820  config->ImportModels = false;
1821  config->ImportAnimations = false;
1822  config->ExtractEmbeddedData = false;
1823 
1824  return config;
1825  }
1826 
1828  {
1829  auto config = gcnew ImportConfiguration();
1830 
1831  config->ImportTemplates = false;
1832  config->ImportPivots = false;
1833  config->ImportGlobalSettings = false;
1834  config->ImportCharacters = false;
1835  config->ImportConstraints = false;
1836  config->ImportGobos = false;
1837  config->ImportShapes = false;
1838  config->ImportLinks = false;
1839  config->ImportMaterials = true;
1840  config->ImportTextures = true;
1841  config->ImportModels = true;
1842  config->ImportAnimations = true;
1843  config->ExtractEmbeddedData = false;
1844 
1845  return config;
1846  }
1847 
1849  {
1850  auto config = gcnew ImportConfiguration();
1851 
1852  config->ImportGlobalSettings = true;
1853 
1854  return config;
1855  }
1856  };
1857 
1858 private:
1859  static System::Object^ globalLock = gcnew System::Object();
1860 
1861  void Initialize(String^ inputFilename, String^ vfsOutputFilename, ImportConfiguration^ importConfig)
1862  {
1863  // -----------------------------------------------------
1864  // TODO: Workaround with FBX SDK not being multithreaded.
1865  // We protect the whole usage of this class with a monitor
1866  //
1867  // Lock the whole class between Initialize/Destroy
1868  // -----------------------------------------------------
1869  System::Threading::Monitor::Enter( globalLock );
1870  // -----------------------------------------------------
1871 
1872  this->inputFilename = inputFilename;
1873  this->vfsOutputFilename = vfsOutputFilename;
1874  this->inputPath = Path::GetDirectoryName(inputFilename);
1875 
1876  polygonSwap = false;
1877 
1878  // Initialize the sdk manager. This object handles all our memory management.
1879  lSdkManager = FbxManager::Create();
1880 
1881  // Create the io settings object.
1882  FbxIOSettings *ios = FbxIOSettings::Create(lSdkManager, IOSROOT);
1883  ios->SetBoolProp(IMP_FBX_TEMPLATE, importConfig->ImportTemplates);
1884  ios->SetBoolProp(IMP_FBX_PIVOT, importConfig->ImportPivots);
1885  ios->SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, importConfig->ImportGlobalSettings);
1886  ios->SetBoolProp(IMP_FBX_CHARACTER, importConfig->ImportCharacters);
1887  ios->SetBoolProp(IMP_FBX_CONSTRAINT, importConfig->ImportConstraints);
1888  ios->SetBoolProp(IMP_FBX_GOBO, importConfig->ImportGobos);
1889  ios->SetBoolProp(IMP_FBX_SHAPE, importConfig->ImportShapes);
1890  ios->SetBoolProp(IMP_FBX_LINK, importConfig->ImportLinks);
1891  ios->SetBoolProp(IMP_FBX_MATERIAL, importConfig->ImportMaterials);
1892  ios->SetBoolProp(IMP_FBX_TEXTURE, importConfig->ImportTextures);
1893  ios->SetBoolProp(IMP_FBX_MODEL, importConfig->ImportModels);
1894  ios->SetBoolProp(IMP_FBX_ANIMATION, importConfig->ImportAnimations);
1895  ios->SetBoolProp(IMP_FBX_EXTRACT_EMBEDDED_DATA, importConfig->ExtractEmbeddedData);
1896  lSdkManager->SetIOSettings(ios);
1897 
1898  // Create an importer using our sdk manager.
1899  lImporter = FbxImporter::Create(lSdkManager,"");
1900 
1901  auto inputFilenameUtf8 = System::Text::Encoding::UTF8->GetBytes(inputFilename);
1902  pin_ptr<Byte> inputFilenameUtf8Ptr = &inputFilenameUtf8[0];
1903 
1904  if(!lImporter->Initialize((const char*)inputFilenameUtf8Ptr, -1, lSdkManager->GetIOSettings()))
1905  {
1906  throw gcnew InvalidOperationException(String::Format("Call to FbxImporter::Initialize() failed.\n"
1907  "Error returned: {0}\n\n", gcnew String(lImporter->GetStatus().GetErrorString())));
1908  }
1909 
1910  // Create a new scene so it can be populated by the imported file.
1911  scene = FbxScene::Create(lSdkManager, "myScene");
1912 
1913  // Import the contents of the file into the scene.
1914  lImporter->Import(scene);
1915 
1916  auto documentInfo = scene->GetDocumentInfo();
1917  auto appliWhichExported = gcnew String(std::string(documentInfo->Original_ApplicationName.Get()).c_str());
1918  if(appliWhichExported == "Maya")
1919  exportedFromMaya = true;
1920 
1921  const float framerate = static_cast<float>(FbxTime::GetFrameRate(scene->GetGlobalSettings().GetTimeMode()));
1922  scene->GetRootNode()->ResetPivotSetAndConvertAnimation(framerate, false, false);
1923 
1924  // For some reason ConvertScene doesn't seem to work well in some cases with no animation
1925  //FbxAxisSystem ourAxisSystem(FbxAxisSystem::ZAxis, (FbxAxisSystem::eFrontVector)FbxAxisSystem::ParityOdd, FbxAxisSystem::LeftHanded);
1926  //ourAxisSystem.ConvertScene(scene);
1927 
1928  auto sceneAxisSystem = scene->GetGlobalSettings().GetAxisSystem();
1929 
1930  swapHandedness = sceneAxisSystem.GetCoorSystem() == FbxAxisSystem::eLeftHanded;
1931  polygonSwap = !swapHandedness;
1932 
1933  // Not sure if handedness is really what requires it to be rotated by 180?
1934  // Maybe we should swap Y instead of swapping Z?
1935  auto rotationAngle = swapHandedness ? 180 : 0;
1936 
1937  // Y axis is up, swap some stuff! -- ConvertScene doesn't seem to take care of it, maybe only because it was a case with no animation?
1938  // TODO: Maybe it would be better to do it on inner nodes instead of root node?
1939  //int upVectorSign;
1940  //if (sceneAxisSystem.GetUpVector(upVectorSign) == FbxAxisSystem::eYAxis)
1941  // rotationAngle += 90;
1942 
1943  auto sceneRootNode = scene->GetRootNode();
1944 
1945  // Add the root rotation
1946  sceneRootNode->SetRotationActive(true);
1947  sceneRootNode->SetPreRotation(FbxNode::eSourcePivot, FbxVector4(rotationAngle, 0, 0));
1948 
1949  std::map<FbxNode*, std::string> nodeNames;
1950  GenerateNodesName(nodeNames);
1951 
1952  RegisterNode(scene->GetRootNode(), -1, nodeNames);
1953  }
1954 
1955  bool CheckAnimationData(FbxAnimLayer* animLayer, FbxNode* node)
1956  {
1957  if ((node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X) != NULL
1958  && node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y) != NULL
1959  && node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z) != NULL)
1960  ||
1961  (node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X) != NULL
1962  && node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y) != NULL
1963  && node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z) != NULL)
1964  ||
1965  (node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X) != NULL
1966  && node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y) != NULL
1967  && node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z) != NULL))
1968  return true;
1969 
1970  FbxCamera* camera = node->GetCamera();
1971  if (camera != NULL)
1972  {
1973  if(camera->FieldOfViewY.GetCurve(animLayer))
1974  return true;
1975 
1976  if(camera->FocalLength.GetCurve(animLayer))
1977  return true;
1978  }
1979 
1980  for(int i = 0; i < node->GetChildCount(); ++i)
1981  {
1982  if (CheckAnimationData(animLayer, node->GetChild(i)))
1983  return true;
1984  }
1985 
1986  return false;
1987  }
1988 
1989  bool HasAnimationData(String^ inputFile)
1990  {
1991  try
1992  {
1993  Initialize(inputFile, nullptr, ImportConfiguration::ImportAnimationsOnly());
1994 
1995  int animStackCount = scene->GetMemberCount<FbxAnimStack>();
1996 
1997  if (animStackCount > 0)
1998  {
1999  bool check = true;
2000  for (int i = 0; i < animStackCount && check; ++i)
2001  {
2002  FbxAnimStack* animStack = scene->GetMember<FbxAnimStack>(i);
2003  int animLayerCount = animStack->GetMemberCount<FbxAnimLayer>();
2004  FbxAnimLayer* animLayer = animStack->GetMember<FbxAnimLayer>(0);
2005 
2006  check = check && CheckAnimationData(animLayer, scene->GetRootNode());
2007  }
2008 
2009  return check;
2010  }
2011 
2012  return false;
2013  }
2014  finally
2015  {
2016  Destroy();
2017  }
2018  }
2019 
2020  void GetAnimationNodes(FbxAnimLayer* animLayer, FbxNode* node, List<String^>^ animationNodes)
2021  {
2022  auto nodeData = nodeMapping[(IntPtr)node];
2023  auto nodeName = nodes[nodeData].Name;
2024 
2025  bool checkTranslation = true;
2026  checkTranslation = checkTranslation && node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X) != NULL;
2027  checkTranslation = checkTranslation && node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y) != NULL;
2028  checkTranslation = checkTranslation && node->LclTranslation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z) != NULL;
2029 
2030  bool checkRotation = true;
2031  checkRotation = checkRotation && node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X) != NULL;
2032  checkRotation = checkRotation && node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y) != NULL;
2033  checkRotation = checkRotation && node->LclRotation.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z) != NULL;
2034 
2035  bool checkScale = true;
2036  checkScale = checkScale && node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_X) != NULL;
2037  checkScale = checkScale && node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Y) != NULL;
2038  checkScale = checkScale && node->LclScaling.GetCurve(animLayer, FBXSDK_CURVENODE_COMPONENT_Z) != NULL;
2039 
2040  if (checkTranslation || checkRotation || checkScale)
2041  {
2042  animationNodes->Add(nodeName);
2043  }
2044  else
2045  {
2046  bool checkCamera = true;
2047  FbxCamera* camera = node->GetCamera();
2048  if (camera != NULL)
2049  {
2050  if(camera->FieldOfViewY.GetCurve(animLayer))
2051  checkCamera = checkCamera && camera->FieldOfViewY.GetCurve(animLayer) != NULL;
2052 
2053  if(camera->FocalLength.GetCurve(animLayer))
2054  checkCamera = checkCamera && camera->FocalLength.GetCurve(animLayer) != NULL;
2055 
2056  if (checkCamera)
2057  animationNodes->Add(nodeName);
2058  }
2059  }
2060 
2061  for(int i = 0; i < node->GetChildCount(); ++i)
2062  {
2063  GetAnimationNodes(animLayer, node->GetChild(i), animationNodes);
2064  }
2065  }
2066 
2067  void GenerateMaterialNames(std::map<FbxSurfaceMaterial*, std::string>& materialNames)
2068  {
2069  auto materials = gcnew List<MaterialDescription^>();
2070  std::map<std::string, int> materialNameTotalCount;
2071  std::map<std::string, int> materialNameCurrentCount;
2072  std::map<FbxSurfaceMaterial*, std::string> tempNames;
2073  auto materialCount = scene->GetMaterialCount();
2074 
2075  for (int i = 0; i < materialCount; i++)
2076  {
2077  auto lMaterial = scene->GetMaterial(i);
2078  auto materialName = std::string(lMaterial->GetName());
2079  auto materialPart = std::string();
2080 
2081  int materialNameSplitPosition = materialName.find('#');
2082  if (materialNameSplitPosition != std::string::npos)
2083  {
2084  materialPart = materialName.substr(materialNameSplitPosition + 1);
2085  materialName = materialName.substr(0, materialNameSplitPosition);
2086  }
2087 
2088  materialNameSplitPosition = materialNameSplitPosition = materialName.find("__");
2089  if (materialNameSplitPosition != std::string::npos)
2090  {
2091  materialPart = materialName.substr(materialNameSplitPosition + 2);
2092  materialName = materialName.substr(0, materialNameSplitPosition);
2093  }
2094 
2095  // TODO: remove all bad characters
2096  int nextCharacterPos = materialName.find(':');
2097  while (nextCharacterPos != std::string::npos)
2098  {
2099  materialName.replace(nextCharacterPos, 1, 1, '_');
2100  nextCharacterPos = materialName.find(':', nextCharacterPos);
2101  }
2102  tempNames[lMaterial] = materialName;
2103 
2104  if (materialNameTotalCount.count(materialName) == 0)
2105  materialNameTotalCount[materialName] = 1;
2106  else
2107  materialNameTotalCount[materialName] = materialNameTotalCount[materialName] + 1;
2108  }
2109 
2110  for (int i = 0; i < materialCount; i++)
2111  {
2112  auto lMaterial = scene->GetMaterial(i);
2113  auto materialName = tempNames[lMaterial];
2114  int currentCount = 0;
2115 
2116  if (materialNameCurrentCount.count(materialName) == 0)
2117  materialNameCurrentCount[materialName] = 1;
2118  else
2119  materialNameCurrentCount[materialName] = materialNameCurrentCount[materialName] + 1;
2120 
2121  if(materialNameTotalCount[materialName] > 1)
2122  materialName = materialName + "_" + std::to_string(materialNameCurrentCount[materialName]);
2123 
2124  materialNames[lMaterial] = materialName;
2125  }
2126  }
2127 
2128  void GetMeshes(FbxNode* pNode, std::vector<FbxMesh*>& meshes)
2129  {
2130  // Process the node's attributes.
2131  for(int i = 0; i < pNode->GetNodeAttributeCount(); i++)
2132  {
2133  auto pAttribute = pNode->GetNodeAttributeByIndex(i);
2134 
2135  if(!pAttribute) return;
2136 
2137  if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh)
2138  {
2139  auto pMesh = (FbxMesh*)pAttribute;
2140  meshes.push_back(pMesh);
2141  }
2142  }
2143 
2144  // Recursively process the children nodes.
2145  for(int j = 0; j < pNode->GetChildCount(); j++)
2146  {
2147  GetMeshes(pNode->GetChild(j), meshes);
2148  }
2149  }
2150 
2151  void GenerateMeshesName(std::map<FbxMesh*, std::string>& meshNames)
2152  {
2153  std::vector<FbxMesh*> meshes;
2154  GetMeshes(scene->GetRootNode(), meshes);
2155 
2156  std::map<std::string, int> meshNameTotalCount;
2157  std::map<std::string, int> meshNameCurrentCount;
2158  std::map<FbxMesh*, std::string> tempNames;
2159 
2160  for (auto iter = meshes.begin(); iter != meshes.end(); ++iter)
2161  {
2162  auto pMesh = *iter;
2163  auto meshName = std::string(pMesh->GetNode()->GetName());
2164  tempNames[pMesh] = meshName;
2165 
2166  if (meshNameTotalCount.count(meshName) == 0)
2167  meshNameTotalCount[meshName] = 1;
2168  else
2169  meshNameTotalCount[meshName] = meshNameTotalCount[meshName] + 1;
2170  }
2171 
2172  for (auto iter = meshes.begin(); iter != meshes.end(); ++iter)
2173  {
2174  auto pMesh = *iter;
2175  auto meshName = tempNames[pMesh];
2176  int currentCount = 0;
2177 
2178  if (meshNameCurrentCount.count(meshName) == 0)
2179  meshNameCurrentCount[meshName] = 1;
2180  else
2181  meshNameCurrentCount[meshName] = meshNameCurrentCount[meshName] + 1;
2182 
2183  if(meshNameTotalCount[meshName] > 1)
2184  meshName = meshName + "_" + std::to_string(meshNameCurrentCount[meshName]);
2185 
2186  meshNames[pMesh] = meshName;
2187  }
2188  }
2189 
2190  void GetNodes(FbxNode* pNode, std::vector<FbxNode*>& nodes)
2191  {
2192  nodes.push_back(pNode);
2193 
2194  // Recursively process the children nodes.
2195  for(int j = 0; j < pNode->GetChildCount(); j++)
2196  GetNodes(pNode->GetChild(j), nodes);
2197  }
2198 
2199  void GenerateNodesName(std::map<FbxNode*, std::string>& nodeNames)
2200  {
2201  std::vector<FbxNode*> nodes;
2202  GetNodes(scene->GetRootNode(), nodes);
2203 
2204  std::map<std::string, int> nodeNameTotalCount;
2205  std::map<std::string, int> nodeNameCurrentCount;
2206  std::map<FbxNode*, std::string> tempNames;
2207 
2208  for (auto iter = nodes.begin(); iter != nodes.end(); ++iter)
2209  {
2210  auto pNode = *iter;
2211  auto nodeName = std::string(pNode->GetName());
2212  auto subBegin = nodeName.find_last_of(':');
2213  if (subBegin != std::string::npos)
2214  nodeName = nodeName.substr(subBegin + 1);
2215  tempNames[pNode] = nodeName;
2216 
2217  if (nodeNameTotalCount.count(nodeName) == 0)
2218  nodeNameTotalCount[nodeName] = 1;
2219  else
2220  nodeNameTotalCount[nodeName] = nodeNameTotalCount[nodeName] + 1;
2221  }
2222 
2223  for (auto iter = nodes.begin(); iter != nodes.end(); ++iter)
2224  {
2225  auto pNode = *iter;
2226  auto nodeName = tempNames[pNode];
2227  int currentCount = 0;
2228 
2229  if (nodeNameCurrentCount.count(nodeName) == 0)
2230  nodeNameCurrentCount[nodeName] = 1;
2231  else
2232  nodeNameCurrentCount[nodeName] = nodeNameCurrentCount[nodeName] + 1;
2233 
2234  if(nodeNameTotalCount[nodeName] > 1)
2235  nodeName = nodeName + "_" + std::to_string(nodeNameCurrentCount[nodeName]);
2236 
2237  nodeNames[pNode] = nodeName;
2238  }
2239  }
2240 
2241  MaterialInstances^ GetOrCreateInstances(FbxSurfaceMaterial* lMaterial, List<MaterialInstances^>^ instances, std::map<FbxSurfaceMaterial*, std::string>& materialNames)
2242  {
2243  for (int i = 0; i < instances->Count; ++i)
2244  {
2245  if (lMaterial == instances[i]->SourceMaterial)
2246  return instances[i];
2247  }
2248 
2249  auto newInstance = gcnew MaterialInstances();
2250  newInstance->SourceMaterial = lMaterial;
2251  newInstance->MaterialsName = gcnew String(materialNames[lMaterial].c_str());
2252  instances->Add(newInstance);
2253  return newInstance;
2254  }
2255 
2256  MaterialInstanciation^ GetOrCreateMaterial(FbxSurfaceMaterial* lMaterial, List<String^>^ uvNames, List<MaterialInstances^>^ instances, std::map<std::string, int>& uvElements, std::map<FbxSurfaceMaterial*, std::string>& materialNames)
2257  {
2258  auto materialInstances = GetOrCreateInstances(lMaterial, instances, materialNames);
2259 
2260  for (int i = 0; i < materialInstances->Instances->Count; ++i)
2261  {
2262  auto parameters = materialInstances->Instances[i]->Parameters;
2263  if (uvNames->Count == parameters->Count)
2264  {
2265  bool equals = true;
2266  for (int j = 0; j < parameters->Count; ++j)
2267  {
2268  equals = equals && (parameters[j] == uvNames[j]);
2269  }
2270 
2271  if (equals)
2272  return materialInstances->Instances[i];
2273  }
2274  }
2275 
2276  auto newInstanciation = gcnew MaterialInstanciation();
2277  newInstanciation->Parameters = uvNames;
2278 
2279  if (materialInstances->Instances->Count > 0)
2280  newInstanciation->MaterialName = materialInstances->MaterialsName + "_" + materialInstances->Instances->Count;
2281  else
2282  newInstanciation->MaterialName = materialInstances->MaterialsName;
2283 
2284  newInstanciation->Material = ProcessMeshMaterialAsset(lMaterial, uvElements);
2285  materialInstances->Instances->Add(newInstanciation);
2286  return newInstanciation;
2287  }
2288 
2289  void SearchMeshInAttribute(FbxNode* pNode, FbxNodeAttribute* pAttribute, std::map<FbxSurfaceMaterial*, std::string> materialNames, std::map<FbxMesh*, std::string> meshNames, std::map<FbxNode*, std::string>& nodeNames, List<MeshParameters^>^ models, List<MaterialInstances^>^ materialInstances, List<CameraInfo^>^ cameras, List<LightInfo^>^ lights)
2290  {
2291  if(!pAttribute) return;
2292 
2293  if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh)
2294  {
2295  auto pMesh = (FbxMesh*)pAttribute;
2296  int polygonCount = pMesh->GetPolygonCount();
2297  FbxGeometryElement::EMappingMode materialMappingMode = FbxGeometryElement::eNone;
2298  FbxLayerElementArrayTemplate<int>* materialIndices = NULL;
2299 
2300  if (pMesh->GetElementMaterial())
2301  {
2302  materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode();
2303  materialIndices = &pMesh->GetElementMaterial()->GetIndexArray();
2304  }
2305 
2306  auto buildMeshes = gcnew List<BuildMesh^>();
2307 
2308  // Count polygon per materials
2309  for (int i = 0; i < polygonCount; i++)
2310  {
2311  int materialIndex = 0;
2312  if (materialMappingMode == FbxGeometryElement::eByPolygon)
2313  {
2314  materialIndex = materialIndices->GetAt(i);
2315  }
2316 
2317  // Equivalent to std::vector::resize()
2318  while (materialIndex >= buildMeshes->Count)
2319  {
2320  buildMeshes->Add(nullptr);
2321  }
2322 
2323  if (buildMeshes[materialIndex] == nullptr)
2324  buildMeshes[materialIndex] = gcnew BuildMesh();
2325 
2326  int polygonSize = pMesh->GetPolygonSize(i) - 2;
2327  if (polygonSize > 0)
2328  buildMeshes[materialIndex]->polygonCount += polygonSize;
2329  }
2330 
2331  for (int i = 0; i < buildMeshes->Count; ++i)
2332  {
2333  FbxGeometryElementMaterial* lMaterialElement = pMesh->GetElementMaterial();
2334  if (lMaterialElement != NULL)
2335  {
2336  auto meshParams = gcnew MeshParameters();
2337 
2338  FbxSurfaceMaterial* lMaterial = pNode->GetMaterial(i);
2339  std::map<std::string, int> uvElements;
2340  auto uvNames = gcnew List<String^>();
2341  for (int j = 0; j < pMesh->GetElementUVCount(); ++j)
2342  {
2343  uvElements[pMesh->GetElementUV(j)->GetName()] = j;
2344  uvNames->Add(gcnew String(pMesh->GetElementUV(j)->GetName()));
2345  }
2346 
2347  auto material = GetOrCreateMaterial(lMaterial, uvNames, materialInstances, uvElements, materialNames);
2348  auto meshName = meshNames[pMesh];
2349  if (buildMeshes->Count > 1)
2350  meshName = meshName + "_" + std::to_string(i+1);
2351 
2352  meshParams->MeshName = gcnew String(meshName.c_str());
2353  meshParams->MaterialName = material->MaterialName;
2354  meshParams->NodeName = gcnew String(nodeNames[pNode].c_str());
2355  models->Add(meshParams);
2356  }
2357  }
2358  }
2359  else if (pAttribute->GetAttributeType() == FbxNodeAttribute::eCamera)
2360  {
2361  auto pCamera = (FbxCamera*)pAttribute;
2362  ProcessCamera(cameras, pNode, pCamera, nodeNames);
2363  }
2364  else if (pAttribute->GetAttributeType() == FbxNodeAttribute::eLight)
2365  {
2366  auto pLight = (FbxLight*)pAttribute;
2367  ProcessLight(lights, pNode, pLight, nodeNames);
2368  }
2369  }
2370 
2371  void SearchMesh(FbxNode* pNode, std::map<FbxSurfaceMaterial*, std::string> materialNames, std::map<FbxMesh*, std::string> meshNames, std::map<FbxNode*, std::string>& nodeNames, List<MeshParameters^>^ models, List<MaterialInstances^>^ materialInstances, List<CameraInfo^>^ cameras, List<LightInfo^>^ lights)
2372  {
2373  // Process the node's attributes.
2374  for(int i = 0; i < pNode->GetNodeAttributeCount(); i++)
2375  SearchMeshInAttribute(pNode, pNode->GetNodeAttributeByIndex(i), materialNames, meshNames, nodeNames, models, materialInstances, cameras, lights);
2376 
2377  // Recursively process the children nodes.
2378  for(int j = 0; j < pNode->GetChildCount(); j++)
2379  {
2380  SearchMesh(pNode->GetChild(j), materialNames, meshNames, nodeNames, models, materialInstances, cameras, lights);
2381  }
2382  }
2383 
2384  Dictionary<String^, MaterialDescription^>^ ExtractMaterialsNoInit()
2385  {
2386  std::map<FbxSurfaceMaterial*, std::string> materialNames;
2387  GenerateMaterialNames(materialNames);
2388 
2389  auto materials = gcnew Dictionary<String^, MaterialDescription^>();
2390  for (int i = 0; i < scene->GetMaterialCount(); i++)
2391  {
2392  std::map<std::string, int> dict;
2393  auto lMaterial = scene->GetMaterial(i);
2394  auto materialName = materialNames[lMaterial];
2395  materials->Add(gcnew String(materialName.c_str()), ProcessMeshMaterialAsset(lMaterial, dict));
2396  }
2397  return materials;
2398  }
2399 
2400  MeshMaterials^ ExtractModelNoInit(std::map<FbxNode*, std::string>& nodeNames)
2401  {
2402  std::map<FbxSurfaceMaterial*, std::string> materialNames;
2403  GenerateMaterialNames(materialNames);
2404 
2405  std::map<FbxMesh*, std::string> meshNames;
2406  GenerateMeshesName(meshNames);
2407 
2408  std::map<std::string, FbxSurfaceMaterial*> materialPerMesh;
2409  auto models = gcnew List<MeshParameters^>();
2410  auto materialInstances = gcnew List<MaterialInstances^>();
2411  auto cameras = gcnew List<CameraInfo^>();
2412  auto lights = gcnew List<LightInfo^>();
2413  SearchMesh(scene->GetRootNode(), materialNames, meshNames, nodeNames, models, materialInstances, cameras, lights);
2414 
2415  auto ret = gcnew MeshMaterials();
2416  ret->Models = models;
2417  ret->Cameras = cameras;
2418  ret->Lights = lights;
2419  ret->Materials = gcnew Dictionary<String^, MaterialDescription^>();
2420  for (int i = 0; i < materialInstances->Count; ++i)
2421  {
2422  for (int j = 0; j < materialInstances[i]->Instances->Count; ++j)
2423  {
2424  ret->Materials->Add(materialInstances[i]->Instances[j]->MaterialName, materialInstances[i]->Instances[j]->Material);
2425  }
2426  }
2427 
2428  // patch lights count
2429  int numPointLights = 0;
2430  int numSpotLights = 0;
2431  int numDirectionalLights = 0;
2432  for (int i = 0; i < lights->Count; ++i)
2433  {
2434  auto lightType = lights[i]->Data->Type;
2435  if (lightType == LightType::Point)
2436  ++numPointLights;
2437  else if (lightType == LightType::Directional)
2438  ++numDirectionalLights;
2439  else if (lightType == LightType::Spot)
2440  ++numSpotLights;
2441  }
2442 
2443  for (int i = 0; i < models->Count; ++i)
2444  {
2445  models[i]->Parameters->Add(LightingKeys::MaxPointLights, numPointLights);
2446  models[i]->Parameters->Add(LightingKeys::MaxDirectionalLights, numDirectionalLights);
2447  models[i]->Parameters->Add(LightingKeys::MaxSpotLights, numSpotLights);
2448  }
2449 
2450  return ret;
2451  }
2452 
2453  List<String^>^ ExtractTextureDependenciesNoInit()
2454  {
2455  auto textureNames = gcnew List<String^>();
2456 
2457  auto textureCount = scene->GetTextureCount();
2458  for(int i=0; i<textureCount; ++i)
2459  {
2460  auto texture = FbxCast<FbxFileTexture>(scene->GetTexture(i));
2461 
2462  if(texture == nullptr)
2463  continue;
2464 
2465  auto texturePath = FindFilePath(texture);
2466  if(!String::IsNullOrEmpty(texturePath)
2467  && File::Exists(texturePath))
2468  textureNames->Add(texturePath);
2469  }
2470 
2471  return textureNames;
2472  }
2473 
2474  List<String^>^ ExtractAnimationNodesNoInit()
2475  {
2476  int animStackCount = scene->GetMemberCount<FbxAnimStack>();
2477  List<String^>^ animationNodes = nullptr;
2478 
2479  if (animStackCount > 0)
2480  {
2481  animationNodes = gcnew List<String^>();
2482  for (int i = 0; i < animStackCount; ++i)
2483  {
2484  FbxAnimStack* animStack = scene->GetMember<FbxAnimStack>(i);
2485  int animLayerCount = animStack->GetMemberCount<FbxAnimLayer>();
2486  FbxAnimLayer* animLayer = animStack->GetMember<FbxAnimLayer>(0);
2487  GetAnimationNodes(animLayer, scene->GetRootNode(), animationNodes);
2488  }
2489  }
2490 
2491  return animationNodes;
2492  }
2493 
2494  List<String^>^ GetAllAnimationNodes(String^ inputFile)
2495  {
2496  try
2497  {
2498  Initialize(inputFile, nullptr, ImportConfiguration::ImportAnimationsOnly());
2499  return ExtractAnimationNodesNoInit();
2500  }
2501  finally
2502  {
2503  Destroy();
2504  }
2505  return nullptr;
2506  }
2507 
2508  List<String^>^ ExtractTextureDependencies(String^ inputFile)
2509  {
2510  try
2511  {
2512  Initialize(inputFile, nullptr, ImportConfiguration::ImportTexturesOnly());
2513  return ExtractTextureDependenciesNoInit();
2514  }
2515  finally
2516  {
2517  Destroy();
2518  }
2519  return nullptr;
2520  }
2521 
2522  Dictionary<String^, MaterialDescription^>^ ExtractMaterials(String^ inputFilename)
2523  {
2524  try
2525  {
2526  Initialize(inputFilename, nullptr, ImportConfiguration::ImportMaterialsOnly());
2527  return ExtractMaterialsNoInit();
2528  }
2529  finally
2530  {
2531  Destroy();
2532  }
2533  return nullptr;
2534  }
2535 
2536  void GetNodes(FbxNode* node, int depth, std::map<FbxNode*, std::string>& nodeNames, List<NodeInfo^>^ allNodes)
2537  {
2538  auto newNodeInfo = gcnew NodeInfo();
2539  newNodeInfo->Name = gcnew String(nodeNames[node].c_str());
2540  newNodeInfo->Depth = depth;
2541  newNodeInfo->Preserve = false;
2542 
2543  allNodes->Add(newNodeInfo);
2544  for (int i = 0; i < node->GetChildCount(); ++i)
2545  GetNodes(node->GetChild(i), depth + 1, nodeNames, allNodes);
2546  }
2547 
2548  List<NodeInfo^>^ ExtractNodeHierarchy(std::map<FbxNode*, std::string>& nodeNames)
2549  {
2550  auto allNodes = gcnew List<NodeInfo^>();
2551  GetNodes(scene->GetRootNode(), 0, nodeNames, allNodes);
2552  return allNodes;
2553  }
2554 
2555 public:
2556  EntityInfo^ ExtractEntity(String^ inputFileName)
2557  {
2558  try
2559  {
2560  Initialize(inputFileName, nullptr, ImportConfiguration::ImportEntityConfig());
2561 
2562  auto index = scene->GetGlobalSettings().GetOriginalUpAxis();
2563  auto originalUpAxis = Vector3::Zero;
2564  if (index < 0 || index > 2) // Default up vector is Z
2565  originalUpAxis[2] = 1;
2566  else
2567  originalUpAxis[index] = 1;
2568 
2569  std::map<FbxNode*, std::string> nodeNames;
2570  GenerateNodesName(nodeNames);
2571 
2572  auto entityInfo = gcnew EntityInfo();
2573  entityInfo->TextureDependencies = ExtractTextureDependenciesNoInit();
2574  entityInfo->AnimationNodes = ExtractAnimationNodesNoInit();
2575  auto models = ExtractModelNoInit(nodeNames);
2576  entityInfo->Models = models->Models;
2577  entityInfo->Materials = models->Materials;
2578  entityInfo->Nodes = ExtractNodeHierarchy(nodeNames);
2579  entityInfo->Lights = models->Lights;
2580  entityInfo->Cameras = models->Cameras;
2581  entityInfo->UpAxis = originalUpAxis;
2582 
2583  return entityInfo;
2584  }
2585  finally
2586  {
2587  Destroy();
2588  }
2589  return nullptr;
2590  }
2591 
2592  ModelData^ Convert(String^ inputFilename, String^ vfsOutputFilename)
2593  {
2594  try
2595  {
2596  Initialize(inputFilename, vfsOutputFilename, ImportConfiguration::ImportAll());
2597 
2598  // Create default ModelViewData
2599  modelData = gcnew ModelData();
2600  modelData->Hierarchy = gcnew ModelViewHierarchyDefinition();
2601  modelData->Hierarchy->Nodes = nodes.ToArray();
2602 
2603  //auto sceneName = scene->GetName();
2604  //if (sceneName != NULL && strlen(sceneName) > 0)
2605  //{
2606  // entity->Name = gcnew String(sceneName);
2607  //}
2608  //else
2609  //{
2610  // // Build scene name from file name
2611  // entity->Name = Path::GetFileName(this->inputFilename);
2612  //}
2613 
2614  std::map<FbxMesh*, std::string> meshNames;
2615  GenerateMeshesName(meshNames);
2616 
2617  // Process and add root entity
2618  ProcessNode(scene->GetRootNode(), meshNames);
2619 
2620  // Process animation
2621  //sceneData->Animation = ProcessAnimation(scene);
2622 
2623  return modelData;
2624  }
2625  finally
2626  {
2627  Destroy();
2628  }
2629 
2630  return nullptr;
2631  }
2632 
2633  AnimationClip^ ConvertAnimation(String^ inputFilename, String^ vfsOutputFilename)
2634  {
2635  try
2636  {
2637  Initialize(inputFilename, vfsOutputFilename, ImportConfiguration::ImportAnimationsOnly());
2638 
2639  // Process animation
2640  auto animationClip = ProcessAnimation(scene);
2641 
2642  return animationClip;
2643  }
2644  finally
2645  {
2646  Destroy();
2647  }
2648 
2649  return nullptr;
2650  }
2651 };
2652 
2653 } } } }
void ConvertDegreeToRadians(AnimationCurve< float >^channel)
Key of an effect parameter.
Definition: ParameterKey.cs:15
CompressedTimeSpan FBXTimeToTimeSpane(const FbxTime &time)
Vector3 FbxDouble3ToVector3(FbxDouble3 vector)
TimeSpan Duration
Gets or sets the duration of this clip.
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
The layout of a vertex buffer with a set of VertexElement.
Data type for SiliconStudio.Paradox.Engine.TransformationComponent.
Definition: EngineData.cs:701
ModelData Convert(String^inputFilename, String^vfsOutputFilename)
A node that describe a binary operation between two IMaterialNode
Vector4 FbxDouble4ToVector4(FbxDouble4 vector)
ComponentBase.Destroy() event.
Describes hiderarchical nodes in a flattened array.
Represents a color in the form of rgb.
Definition: Color3.cs:41
void AddCurve(string propertyName, AnimationCurve curve)
Adds a named curve.
Vector4 FbxDouble3ToVector4(FbxDouble3 vector, float wValue)
void MultiplyChannel(AnimationCurve< float >^channel, double factor)
List< KeyFrameData< T > > ProcessAnimationCurveFloatsHelper(FbxAnimCurve **curves, int numCurves)
Describes skinning for a Mesh, through a collection of MeshBoneDefinition.
void ProcessAnimation(AnimationClip^animationClip, FbxAnimLayer *animLayer, FbxNode *node)
double FocalLengthToVerticalFov(double filmHeight, double focalLength)
static bool WeightGreater(const std::pair< short, float > &elem1, const std::pair< short, float > &elem2)
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
int NodeIndex
The node index in ModelViewHierarchyUpdater.NodeTransformations.
AnimationCurveInterpolationType InterpolationType
Gets or sets the interpolation type.
Data type for SiliconStudio.Paradox.Effects.ParameterCollection.
Definition: ParadoxData.cs:31
void ProcessNode(FbxNode *pNode, std::map< FbxMesh *, std::string > meshNames)
An aggregation of AnimationCurve with their channel names.
Base implementation for ILogger.
Definition: Logger.cs:10
MaterialBinaryOperand
Operands of the MaterialNode.
IMaterialNode GenerateSurfaceTextureTree(FbxSurfaceMaterial *lMaterial, std::map< std::string, int > &uvElementMapping, std::map< FbxFileTexture *, std::string > &textureMap, std::map< std::string, int > &textureNameCount, char const *surfaceMaterial, char const *surfaceMaterialFactor, SiliconStudio::Paradox::Assets::Materials::MaterialDescription^finalMaterial)
Represents a four dimensional mathematical quaternion.
Definition: Quaternion.cs:45
void RegisterNode(FbxNode *pNode, int parentIndex, std::map< FbxNode *, std::string > &nodeNames)
Data type for SiliconStudio.Paradox.Graphics.VertexBufferBinding.
void AddNode(string referenceName, IMaterialNode node)
Inserts a new tree in the material
Data type for SiliconStudio.Paradox.EntityModel.Entity.
Definition: EngineData.cs:739
void ProcessAttribute(FbxNode *pNode, FbxNodeAttribute *pAttribute, std::map< FbxMesh *, std::string > meshNames)
MaterialReferenceNode GenerateMaterialTextureNodeFBX(FbxFileTexture *lFileTexture, std::map< std::string, int > &uvElementMapping, std::map< FbxFileTexture *, std::string > &textureMap, std::map< std::string, int > &textureNameCount, SiliconStudio::Paradox::Assets::Materials::MaterialDescription^finalMaterial)
Data type for SiliconStudio.Paradox.Effects.Mesh.
Definition: EngineData.cs:197
Data type for SiliconStudio.Paradox.Effects.MeshDraw.
Definition: EngineData.cs:165
int GetTextureNameCount(std::map< std::string, int > &textureNameCount, std::string textureName)
Untyped base class for animation curves.
void ProcessAnimationCurveRotation(AnimationClip^animationClip, int nodeData, String^name, FbxAnimCurve **curves, float maxErrorThreshold)
Data type for SiliconStudio.Paradox.Effects.Model.
Definition: EngineData.cs:241
static void Add(ref Quaternion left, ref Quaternion right, out Quaternion result)
Adds two quaternions.
Definition: Quaternion.cs:343
void ProcessLight(List< LightInfo^>^lights, FbxNode *pNode, FbxLight *pLight, std::map< FbxNode *, std::string > &nodeNames)
Matrix FBXMatrixToMatrix(FbxAMatrix &matrix)
AnimationClip ConvertAnimation(String^inputFilename, String^vfsOutputFilename)
Describes a single transformation node, usually in a Model node hierarchy.
Color4 FbxDouble3ToColor4(FbxDouble3 vector, float alphaValue)
SiliconStudio.Core.Mathematics.Vector3 Vector3
Describes a bone cluster inside a Mesh.
void ReverseChannelZ(AnimationCurve< Vector3 >^channel)
void ComputeFovFromFL(AnimationCurve< float >^channel, FbxCamera *pCamera)
AnimationCurve< T > ProcessAnimationCurveVector(AnimationClip^animationClip, int nodeData, String^name, int numCurves, FbxAnimCurve **curves, float maxErrorThreshold)
void ProcessMesh(FbxMesh *pMesh, std::map< FbxMesh *, std::string > meshNames)
Content of a GPU buffer (vertex buffer, index buffer, etc...).
Definition: BufferData.cs:10
ShaderClassSource GenerateTextureLayerFBX(FbxFileTexture *lFileTexture, std::map< std::string, int > &uvElementMapping, MeshData^meshData, int &textureCount, ParameterKey< Texture^>^surfaceMaterialKey)
float Z
The Z component of the vector.
Definition: Vector3.cs:90
void ProcessCamera(List< CameraInfo^>^cameras, FbxNode *pNode, FbxCamera *pCamera, std::map< FbxNode *, std::string > &nodeNames)
Base interface for all nodes in the material tree
A description of a single element for the input-assembler stage. This structure is related to Direct3...
MaterialBinaryOperand BlendModeToBlendOperand(FbxLayeredTexture::EBlendMode blendMode)
Data type for SiliconStudio.Paradox.Engine.LightComponent.
Definition: EngineData.cs:524
Data type for SiliconStudio.Paradox.Engine.CameraComponent.
System.Windows.Point Point
Definition: ColorPicker.cs:15
MaterialDescription ProcessMeshMaterialAsset(FbxSurfaceMaterial *lMaterial, std::map< std::string, int > &uvElementMapping)