Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Paradox.Importer.Assimp.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 #include "../SiliconStudio.Paradox.Assimp.Translation/Extension.h"
5 
6 #include <string>
7 #include <map>
8 #include <set>
9 
10 using namespace System;
11 using namespace System::Collections;
12 using namespace System::Collections::Generic;
13 using namespace System::Diagnostics;
14 using namespace System::IO;
15 using namespace SiliconStudio;
16 using namespace SiliconStudio::Core;
17 using namespace SiliconStudio::Core::Diagnostics;
18 using namespace SiliconStudio::Core::IO;
19 using namespace SiliconStudio::Core::Mathematics;
20 using namespace SiliconStudio::Core::Serialization;
21 using namespace SiliconStudio::Core::Serialization::Assets;
22 using namespace SiliconStudio::Core::Serialization::Contents;
23 using namespace SiliconStudio::Paradox::Assets::Materials;
24 using namespace SiliconStudio::Paradox::Assets::Materials::Nodes;
25 using namespace SiliconStudio::Paradox::AssimpNet;
26 using namespace SiliconStudio::Paradox::DataModel;
27 using namespace SiliconStudio::Paradox::Engine;
28 using namespace SiliconStudio::Paradox::EntityModel;
29 using namespace SiliconStudio::Paradox::Effects;
30 using namespace SiliconStudio::Paradox::Effects::Data;
31 using namespace SiliconStudio::Paradox::Effects::Modules;
32 using namespace SiliconStudio::Paradox::Extensions;
33 using namespace SiliconStudio::Paradox::Graphics;
34 using namespace SiliconStudio::Paradox::Graphics::Data;
35 using namespace SiliconStudio::Paradox::Shaders;
36 
37 using namespace Assimp;
38 using namespace SiliconStudio::Paradox::Importer::Common;
39 
40 namespace SiliconStudio { namespace Paradox { namespace Importer { namespace AssimpNET {
41 
42 public ref class MaterialInstances
43 {
44 public:
46  {
47  Instances = gcnew List<MaterialInstanciation^>();
48  }
49 
50  aiMaterial* SourceMaterial;
51  List<MaterialInstanciation^>^ Instances;
52  String^ MaterialsName;
53 };
54 
55 public ref class MeshInfo
56 {
57 public:
59  {
60  HasSkinningPosition = false;
61  HasSkinningNormal = false;
62  TotalClusterCount = 0;
63  }
64 
66  List<MeshBoneDefinition>^ Bones;
67  String^ Name;
71 };
72 
73 public ref class MeshConverter
74 {
75 public:
76  property Logger^ Logger;
79 
80 private:
81  Stopwatch^ internalStopwatch;
82 
83  // ----- From here: convertion-wise data ----- //
84 
85  String^ vfsInputFilename;
86  String^ vfsOutputFilename;
87  String^ vfsInputPath;
88 
89  ModelData^ modelData;
90  List<ModelNodeDefinition> nodes;
91  Dictionary<IntPtr, int> nodeMapping;
92  Dictionary<String^, int>^ textureNameCount;
93 
94  List<MeshData^>^ effectMeshes; // array of EffectMeshes built from the aiMeshes (same order)
95  Dictionary<String^, List<EntityData^ >^ >^ nodeNameToNodeData; // access to the built nodeData via their String ID (may not be unique)
96  Dictionary<IntPtr, EntityData^>^ nodePtrToNodeData; // access to the built nodeData via their corresponding aiNode
97  Dictionary<int, List<EntityData^>^ >^ meshIndexToReferingNodes; // access all the nodes that reference a Mesh Index
98 
99 public:
101  {
102  if(logger == nullptr)
103  logger = Core::Diagnostics::GlobalLogger::GetLogger("Importer Assimp");
104 
105  internalStopwatch = gcnew Stopwatch();
106 
107  Logger = logger;
108  }
109 
110 private:
111  const aiScene* Initialize(String^ inputFilename, String^ outputFilename, Assimp::Importer* importer, unsigned int flags)
112  {
113  // reset all cached data for this new stream convertion
114  ResetConvertionData();
115 
116  // TODO: correct this hack
117  auto splitString = inputFilename->Split(':');
118  auto unixStyleInputFilename = "/" + splitString[0] + splitString[1];
119 
120  this->vfsInputFilename = inputFilename;
121  this->vfsOutputFilename = outputFilename;
122  this->vfsInputPath = VirtualFileSystem::GetParentFolder(inputFilename);
123 
124  auto stream = VirtualFileSystem::Drive->OpenStream(unixStyleInputFilename, VirtualFileMode::Open, VirtualFileAccess::Read, VirtualFileShare::Read, StreamFlags::None);
125 
126  // check and possibly adjust the input parameters
127  if(stream == nullptr)
128  throw gcnew ArgumentNullException("Input Stream");
129 
130  // set file wise info
131  //absolutePath = absPath;
132  //relativePath = dstSrcRelativePath;
133 
134  // copies the all the stream in local memory
135  // Not the best memory-wise, need to be optimized
136  internalStopwatch->StartNew();
137  auto memoryStream = gcnew MemoryStream();
138  stream->CopyTo(memoryStream);
139  auto buffer = memoryStream->ToArray();
140  Logger->Verbose("File Stream copied - Time taken: {0} ms.", nullptr, internalStopwatch->ElapsedMilliseconds);
141 
142  // load the mesh from its original format to Assimp data structures
143  internalStopwatch->StartNew();
144  cli::pin_ptr<System::Byte> bufferStart = &buffer[0];
145  return importer->ReadFileFromMemory(bufferStart, buffer->Length, flags);
146  }
147 
148  // Reset all data related to one specific file conversion
149  void ResetConvertionData()
150  {
151  effectMeshes = gcnew List<MeshData^>();
152  nodeNameToNodeData = gcnew Dictionary<String^, List<EntityData^ >^ >();
153  nodePtrToNodeData = gcnew Dictionary<IntPtr, EntityData^>();
154  meshIndexToReferingNodes = gcnew Dictionary<int, List<EntityData^>^ >();
155  textureNameCount = gcnew Dictionary<String^, int>();
156  }
157 
158  void ExtractEmbededTexture(aiTexture* texture)
159  {
160  Logger->Warning("The input file contains embeded textures. Embeded textures are not currently supported. This texture will be ignored",
161  gcnew NotImplementedException("Embeded textures extraction"),
162  CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
163  }
164 
165  void NormalizeVertexWeights(std::vector<std::vector<std::pair<short,float> > >& controlPts, int nbBoneByVertex)
166  {
167  for(unsigned int vertexId=0; vertexId<controlPts.size(); ++vertexId)
168  {
169  auto& curVertexWeights = controlPts[vertexId];
170 
171  // check that one vertex has not more than 'nbBoneByVertex' associated bones
172  if((int) curVertexWeights.size() > nbBoneByVertex)
173  Logger->Warning("The input file contains vertices that are associated to more than {0} bones. In current version of the system, a single vertex can only be associated to {0} bones. Extra bones will be ignored",
174  gcnew ArgumentOutOfRangeException("To much bones influencing a single vertex"),
175  CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
176 
177  // resize the weights so that they contains exactly the number of bone weights required
178  curVertexWeights.resize(nbBoneByVertex, std::pair<short, float>(0, 0.0f));
179 
180  float totalWeight = 0.f;
181  for(int boneId=0; boneId<nbBoneByVertex; ++boneId)
182  totalWeight += curVertexWeights[boneId].second;
183 
184  if(totalWeight == 0.f) // Assimp weights are positive, so in this case all weights are nulls
185  continue;
186 
187  for(int boneId=0; boneId<nbBoneByVertex; ++boneId)
188  curVertexWeights[boneId].second /= totalWeight;
189  }
190  }
191 
192  bool IsTransparent(aiMaterial* pMaterial)
193  {
194  float opacity;
195  if(pMaterial->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS)
196  return (opacity != 1.0);
197  return false;
198  }
199 
200  MeshInfo^ ProcessMesh(const aiScene* scene, aiMesh* mesh, std::map<aiMesh*, std::string>& meshNames)
201  {
202  // constant declaration
203  const int nbBonesByVertex = 4;
204 
205  List<MeshBoneDefinition>^ bones = nullptr;
206  bool hasSkinningPosition = false;
207  bool hasSkinningNormal = false;
208  int totalClusterCount = 0;
209 
210  // Build the bone's indices/weights and attach bones to NodeData
211  //(bones info are present in the mesh so that is why we have to perform that here)
212  std::vector<std::vector<std::pair<short, float> > > vertexIndexToBoneIdWeight;
213  if(mesh->HasBones())
214  {
215  bones = gcnew List<MeshBoneDefinition>();
216 
217  // TODO: change this to support shared meshes across nodes
218 
219  // size of the array is already known
220  vertexIndexToBoneIdWeight.resize(mesh->mNumVertices);
221 
222  // Build skinning clusters and fill controls points data stutcture
223  for(unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId)
224  {
225  auto bone = mesh->mBones[boneId];
226 
227  // Fill controlPts with bone controls on the mesh
228  for(unsigned int vtxWeightId=0; vtxWeightId<bone->mNumWeights; ++vtxWeightId)
229  {
230  auto vtxWeight = bone->mWeights[vtxWeightId];
231  vertexIndexToBoneIdWeight[vtxWeight.mVertexId].push_back(std::make_pair(boneId, vtxWeight.mWeight));
232  }
233 
234  // find the node where the bone is mapped - based on the name(?)
235  int nodeIndex = -1;
236  auto boneName = gcnew String(bone->mName.C_Str());
237  for (int nodeDefId = 0; nodeDefId < nodes.Count; ++nodeDefId)
238  {
239  auto nodeDef = nodes[nodeDefId];
240  if (nodeDef.Name->Equals(boneName))
241  {
242  nodeIndex = nodeDefId;
243  break;
244  }
245  }
246  if (nodeIndex == -1)
247  {
248  // TODO: log an error
249  nodeIndex = 0;
250  }
251 
252  MeshBoneDefinition boneDef;
253  boneDef.NodeIndex = nodeIndex;
254  auto bindPoseMatrix = aiMatrixToMatrix(bone->mOffsetMatrix);
255 
256  boneDef.LinkToMeshMatrix = bindPoseMatrix;
257  bones->Add(boneDef);
258  }
259  NormalizeVertexWeights(vertexIndexToBoneIdWeight, nbBonesByVertex);
260 
261  totalClusterCount = mesh->mNumBones;
262  if (totalClusterCount > 0)
263  hasSkinningPosition = true;
264  }
265 
266  // Build the vertex declaration
267  auto vertexElements = gcnew List<VertexElement>();
268  int vertexStride = 0;
269 
270  int positionOffset = vertexStride;
271  vertexElements->Add(VertexElement::Position<Vector3>(0, vertexStride));
272  vertexStride += sizeof(Vector3);
273 
274  int normalOffset = vertexStride;
275  if (mesh->HasNormals())
276  {
277  vertexElements->Add(VertexElement::Normal<Vector3>(0, vertexStride));
278  vertexStride += sizeof(Vector3);
279  }
280 
281  int uvOffset = vertexStride;
282  const int sizeUV = sizeof(Vector2); // 3D uv not supported
283  for (unsigned int uvChannel = 0; uvChannel < mesh->GetNumUVChannels(); ++uvChannel)
284  {
285  vertexElements->Add(VertexElement::TextureCoordinate<Vector2>(uvChannel, vertexStride));
286  vertexStride += sizeUV;
287  }
288 
289  int colorOffset = vertexStride;
290  const int sizeColor = sizeof(Color);
291  for (unsigned int colorChannel=0; colorChannel< mesh->GetNumColorChannels(); ++colorChannel)
292  {
293  vertexElements->Add(VertexElement::Color<Color>(colorChannel, vertexStride));
294  vertexStride += sizeColor;
295  }
296 
297  int tangentOffset = vertexStride;
298  if (mesh->HasTangentsAndBitangents())
299  {
300  vertexElements->Add(VertexElement::Tangent<Vector3>(0, vertexStride));
301  vertexStride += sizeof(Vector3);
302  }
303 
304  int bitangentOffset = vertexStride;
305  if (mesh->HasTangentsAndBitangents())
306  {
307  vertexElements->Add(VertexElement::BiTangent<Vector3>(0, vertexStride));
308  vertexStride += sizeof(Vector3);
309  }
310 
311  int blendIndicesOffset = vertexStride;
312  bool controlPointIndices16 = (AllowUnsignedBlendIndices && totalClusterCount > 256) || (!AllowUnsignedBlendIndices && totalClusterCount > 128);
313  if (vertexIndexToBoneIdWeight.size() > 0)
314  {
315  if (controlPointIndices16)
316  {
317  if (AllowUnsignedBlendIndices)
318  {
319  vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R16G16B16A16_UInt, vertexStride));
320  vertexStride += sizeof(unsigned short) * 4;
321  }
322  else
323  {
324  vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R16G16B16A16_SInt, vertexStride));
325  vertexStride += sizeof(short) * 4;
326  }
327  }
328  else
329  {
330  if (AllowUnsignedBlendIndices)
331  {
332  vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R8G8B8A8_UInt, vertexStride));
333  vertexStride += sizeof(unsigned char) * 4;
334  }
335  else
336  {
337  vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R8G8B8A8_SInt, vertexStride));
338  vertexStride += sizeof(char) * 4;
339  }
340  }
341  }
342 
343  int blendWeightOffset = vertexStride;
344  if (vertexIndexToBoneIdWeight.size() > 0)
345  {
346  vertexElements->Add(VertexElement("BLENDWEIGHT", 0, PixelFormat::R32G32B32A32_Float, vertexStride));
347  vertexStride += sizeof(float) * 4;
348  }
349 
350  // Build the vertices data buffer
351  auto vertexBuffer = gcnew array<Byte>(vertexStride * mesh->mNumVertices);
352  pin_ptr<Byte> vbPointer = &vertexBuffer[0];
353  for (unsigned int i = 0; i < mesh->mNumVertices; i++)
354  {
355  *((Vector3*)(vbPointer + positionOffset)) = aiVector3ToVector3(mesh->mVertices[i]);
356 
357  if (mesh->HasNormals())
358  *((Vector3*)(vbPointer + normalOffset)) = aiVector3ToVector3(mesh->mNormals[i]);
359 
360  for (unsigned int uvChannel = 0; uvChannel < mesh->GetNumUVChannels(); ++uvChannel)
361  {
362  auto textureCoord = mesh->mTextureCoords[uvChannel][i];
363  *((Vector2*)(vbPointer + uvOffset + sizeUV*uvChannel)) = Vector2(textureCoord.x, textureCoord.y); // 3D uv not supported
364  }
365 
366  for (unsigned int colorChannel=0; colorChannel< mesh->GetNumColorChannels(); ++colorChannel)
367  {
368  auto color = aiColor4ToColor(mesh->mColors[colorChannel][i]);
369  *((Color*)(vbPointer + colorOffset + sizeColor*colorChannel)) = color;
370  }
371 
372  if (mesh->HasTangentsAndBitangents())
373  {
374  *((Vector3*)(vbPointer + tangentOffset)) = aiVector3ToVector3(mesh->mTangents[i]);
375  *((Vector3*)(vbPointer + bitangentOffset)) = aiVector3ToVector3(mesh->mBitangents[i]);
376  }
377 
378  if (vertexIndexToBoneIdWeight.size() > 0)
379  {
380  for(int bone=0; bone<nbBonesByVertex; ++bone)
381  {
382  if (controlPointIndices16)
383  {
384  if (AllowUnsignedBlendIndices)
385  ((unsigned short*)(vbPointer + blendIndicesOffset))[bone] = (unsigned short)vertexIndexToBoneIdWeight[i][bone].first;
386  else
387  ((short*)(vbPointer + blendIndicesOffset))[bone] = (short)vertexIndexToBoneIdWeight[i][bone].first;
388  }
389  else
390  {
391  if (AllowUnsignedBlendIndices)
392  ((unsigned char*)(vbPointer + blendIndicesOffset))[bone] = (unsigned char)vertexIndexToBoneIdWeight[i][bone].first;
393  else
394  ((char*)(vbPointer + blendIndicesOffset))[bone] = (char)vertexIndexToBoneIdWeight[i][bone].first;
395  }
396  ((float*)(vbPointer + blendWeightOffset))[bone] = vertexIndexToBoneIdWeight[i][bone].second;
397  }
398  }
399  vbPointer += vertexStride;
400  }
401 
402  // Build the indices data buffer
403  const int nbIndices = 3 * mesh->mNumFaces;
404  array<Byte>^ indexBuffer;
405  bool is32BitIndex = mesh->mNumVertices > 65535;
406  if (is32BitIndex)
407  indexBuffer = gcnew array<Byte>(sizeof(unsigned int) * nbIndices);
408  else
409  indexBuffer = gcnew array<Byte>(sizeof(unsigned short) * nbIndices);
410 
411  pin_ptr<Byte> ibPointer = &indexBuffer[0];
412  for (unsigned int i = 0; i < mesh->mNumFaces; i++)
413  {
414  if (is32BitIndex)
415  {
416  for (int j = 0; j < 3; ++j)
417  {
418  *((unsigned int*)ibPointer) = mesh->mFaces[i].mIndices[j];
419  ibPointer += sizeof(unsigned int);
420  }
421  }
422  else
423  {
424  for (int j = 0; j < 3; ++j)
425  {
426  *((unsigned short*)ibPointer) = (unsigned short)(mesh->mFaces[i].mIndices[j]);
427  ibPointer += sizeof(unsigned short);
428  }
429  }
430  }
431 
432  // Build the mesh data
433  auto vertexBufferBinding = gcnew VertexBufferBindingData(gcnew BufferData(BufferFlags::VertexBuffer, vertexBuffer), gcnew VertexDeclaration(vertexElements->ToArray()), mesh->mNumVertices, 0, 0);
434  auto indexBufferBinding = gcnew IndexBufferBindingData(gcnew BufferData(BufferFlags::IndexBuffer, indexBuffer), is32BitIndex, nbIndices, 0);
435 
436  auto drawData = gcnew MeshDrawData();
437  auto vbb = gcnew List<VertexBufferBindingData^>();
438  vbb->Add(vertexBufferBinding);
439  drawData->VertexBuffers = vbb->ToArray();
440  drawData->IndexBuffer = indexBufferBinding;
441  drawData->PrimitiveType = PrimitiveType::TriangleList;
442  drawData->DrawCount = nbIndices;
443 
444  bool isTransparent = IsTransparent(scene->mMaterials[mesh->mMaterialIndex]);
445  bool sortTransparentMeshes = true; // TODO transform into importer parameter
446  if (isTransparent && sortTransparentMeshes)
447  {
448  PolySortExtensions::SortMeshPolygons(drawData, ViewDirectionForTransparentZSort);
449  }
450 
451  auto meshInfo = gcnew MeshInfo();
452  meshInfo->Draw = drawData;
453  meshInfo->Name = gcnew String(meshNames[mesh].c_str());
454  meshInfo->Bones = bones;
455  meshInfo->HasSkinningPosition = hasSkinningPosition;
456  meshInfo->HasSkinningNormal = hasSkinningNormal;
457  meshInfo->TotalClusterCount = totalClusterCount;
458 
459  return meshInfo;
460  }
461 
462  // Register all the nodes in dictionnaries
463  void RegisterNodes(aiNode* fromNode, int parentIndex, std::map<aiNode*, std::string>& nodeNames, std::map<int, std::vector<int>*>& meshIndexToNodeIndex)
464  {
465  int nodeIndex = nodes.Count;
466 
467  // assign the index of the node to the index of the mesh
468  for(unsigned int m=0; m<fromNode->mNumMeshes; ++m)
469  {
470  int meshIndex = fromNode->mMeshes[m];
471 
472  if (meshIndexToNodeIndex.find(meshIndex) == meshIndexToNodeIndex.end())
473  meshIndexToNodeIndex[meshIndex] = new std::vector<int>();
474  meshIndexToNodeIndex[meshIndex]->push_back(nodeIndex);
475  }
476 
477  // get node transformation
478  aiVector3t<float> aiTranslation;
479  aiVector3t<float> aiScaling;
480  aiQuaterniont<float> aiOrientation;
481  fromNode->mTransformation.Decompose(aiScaling, aiOrientation, aiTranslation);
482 
483  // Create node
484  ModelNodeDefinition modelNodeDefinition;
485  modelNodeDefinition.ParentIndex = parentIndex;
486  modelNodeDefinition.Transform.Translation = Vector3(aiTranslation.x, aiTranslation.y, aiTranslation.z);
487  modelNodeDefinition.Transform.Rotation = aiQuaternionToQuaternion(aiOrientation);
488 
489  if (parentIndex == -1)
490  modelNodeDefinition.Transform.Scaling = Vector3::One;
491  else
492  modelNodeDefinition.Transform.Scaling = Vector3(aiScaling.x, aiScaling.y, aiScaling.z);
493 
494  modelNodeDefinition.Name = gcnew String(nodeNames[fromNode].c_str());
495  modelNodeDefinition.Flags = ModelNodeFlags::Default;
496  nodes.Add(modelNodeDefinition);
497 
498  // register the children
499  for(unsigned int child=0; child<fromNode->mNumChildren; ++child)
500  {
501  RegisterNodes(fromNode->mChildren[child], nodeIndex, nodeNames, meshIndexToNodeIndex);
502  }
503  }
504 
505  // Register all the nodes in dictionnaries
506  void RegisterNodes_old(aiNode* fromNode)
507  {
508  auto curNode = gcnew EntityData();
509 
510  // the name
511  curNode->Name = aiStringToString(fromNode->mName);
512 
513  // register the children
514  for(unsigned int child=0; child<fromNode->mNumChildren; ++child)
515  RegisterNodes_old(fromNode->mChildren[child]);
516 
517  // add the current node to the hash tables in order to be able to find it easily when processing attributes
518  if(!nodeNameToNodeData->ContainsKey(curNode->Name))
519  nodeNameToNodeData->Add(curNode->Name, gcnew List<EntityData^>());
520  nodeNameToNodeData[curNode->Name]->Add(curNode);
521  nodePtrToNodeData->Add((IntPtr)fromNode, curNode);
522 
523  // add the meshes refered to the dictionnary (needed for bones animation in processMesh)
524  for(unsigned int m=0; m<fromNode->mNumMeshes; ++m)
525  {
526  int index = fromNode->mMeshes[m];
527  if(!meshIndexToReferingNodes->ContainsKey(index))
528  meshIndexToReferingNodes->Add(index, gcnew List<EntityData^>());
529  meshIndexToReferingNodes[index]->Add(curNode);
530  }
531  }
532 
533  void ProcessCamera(const aiCamera* assimpCam)
534  {
535  // In Assimp the cameras are part of the node hierarchy,
536  // but unfortunatelly the camera property structures have no direct reference to their corresponding hierarchy node.
537  // This correspondance can only be found by evaluating the correspondance between the camera node and property names.
538 
539  // Find the camera's name of the current camera property
540  auto camName = aiStringToString(assimpCam->mName);
541  // Find its corresponding nodes in the hierarchy
542  if(!(nodeNameToNodeData->ContainsKey(camName)))
543  {
544  Logger->Error( "The node '{0}' is missing in the node hierarchy. Impossible to properly locate the camera.",
545  gcnew ArgumentException("Camera node missing in the node hierarchy"), camName,
546  CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
547  return;
548  }
549  auto camNodes = nodeNameToNodeData[camName];
550 
551  // Build the camera attribute data
552  auto camData = gcnew CameraComponentData();
553  camData->NearPlane = assimpCam->mClipPlaneNear;
554  camData->FarPlane = assimpCam->mClipPlaneFar;
555  camData->AspectRatio = assimpCam->mAspect;
556  camData->VerticalFieldOfView = assimpCam->mHorizontalFOV / assimpCam->mAspect;
557  auto camTargetNodeName = camName + ".Target";
558  if(nodeNameToNodeData->ContainsKey(camTargetNodeName))
559  {
560  auto camTargetNodes = nodeNameToNodeData[camTargetNodeName];
561  if(camTargetNodes->Count > 1)
562  {
563  Logger->Error( "The camera target '{0}' has several corresponding nodes in the hierarchy. First correspondance will be used.",
564  gcnew ArgumentException("Several camera's node correspondance found"), camTargetNodeName,
565  CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
566  }
567  Logger->Error( "Camera targets are not implemented in current version.",
568  gcnew NotImplementedException("Camera targets not implemented"), camTargetNodeName,
569  CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
570  //camData->Target = camTargetNodes[0];
571  }
572 
573  // Attach the camera attribute to the Camera nodes
574  for each (EntityData^ camNode in camNodes)
575  camNode->Components->Add(CameraComponent::Key, camData);
576  }
577 
578  void ProcessLight(const aiLight* assimpLight)
579  {
580  // In Assimp the lights are part of the node hierarchy,
581  // but unfortunatelly the light property structures have no direct reference to their corresponding hierarchy node.
582  // This correspondance can only be found by evaluating the correspondance between the light node and property names.
583 
584  // Find the light's name of the current light property
585  auto lightName = aiStringToString(assimpLight->mName);
586  // Find its corresponding nodes in the hierarchy
587  if(!(nodeNameToNodeData->ContainsKey(lightName)))
588  {
589  Logger->Error( "The node '{0}' is missing in the node hierarchy. Impossible to properly locate the light.",
590  gcnew ArgumentException("Light node missing in the node hierarchy"), lightName,
591  CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
592  return;
593  }
594  auto lightNodes = nodeNameToNodeData[lightName];
595 
596  // Build the camera attribute data
597  auto lightData = gcnew LightComponentData();
598  lightData->LightDirection = Vector3(assimpLight->mDirection.x, assimpLight->mDirection.y, assimpLight->mDirection.z);
599  lightData->Type = aiLightTypeToPdxLightType(assimpLight->mType, lightName, Logger);
600 
601  // --- Temporary --- TODO expand LightData members and fill them properly
602  lightData->Color = aiColor3ToColor3(assimpLight->mColorDiffuse);
603  lightData->Deferred = lightData->Type == LightType::Point;
604  lightData->Intensity = 1.f;
605  lightData->DecayStart = 1.f;
606  // --- End Temporary ---
607 
608  lightData->Layers = RenderLayers::RenderLayerAll;
609 
610  // Attach the camera attribute to the Light nodes
611  for each (EntityData^ lightNode in lightNodes)
612  lightNode->Components->Add(LightComponent::Key, lightData);
613  }
614 
615  void ProcessAnimationCurveVector(AnimationClip^ animationClip, const aiVectorKey* keys, unsigned int nbKeys, String^ partialTargetName, double ticksPerSec)
616  {
617  auto animationCurve = gcnew AnimationCurve<Vector3>();
618 
619  // Switch to cubic implicit interpolation mode for Vector3
620  animationCurve->InterpolationType = AnimationCurveInterpolationType::Cubic;
621 
622  CompressedTimeSpan lastKeyTime;
623 
624  for(unsigned int keyId=0; keyId<nbKeys; ++keyId)
625  {
626  auto aiKey = keys[keyId];
627  KeyFrameData<Vector3> key;
628 
629  auto time = aiTimeToPdxTimeSpan(aiKey.mTime, ticksPerSec);
630  auto value = aiVector3ToVector3(aiKey.mValue);
631 
632  key.Time = time;
633  lastKeyTime = time;
634 
635  key.Value.X = value.X;
636  key.Value.Y = value.Y;
637  key.Value.Z = value.Z;
638 
639  animationCurve->KeyFrames->Add(key);
640  if(keyId == 0 || keyId == nbKeys-1) // discontinuity at animation first and last frame
641  animationCurve->KeyFrames->Add(key); // add 2 times the same frame at discontinuities to have null gradient
642  }
643 
644  animationClip->AddCurve(partialTargetName, animationCurve);
645 
646  if (nbKeys > 0)
647  {
648  if (animationClip->Duration < lastKeyTime)
649  animationClip->Duration = lastKeyTime;
650  }
651  }
652 
653  void ProcessAnimationCurveQuaternion(AnimationClip^ animationClip, const aiQuatKey* keys, unsigned int nbKeys, String^ partialTargetName, double ticksPerSec)
654  {
655  auto animationCurve = gcnew AnimationCurve<Quaternion>();
656 
657  CompressedTimeSpan lastKeyTime;
658 
659  for(unsigned int keyId=0; keyId<nbKeys; ++keyId)
660  {
661  auto aiKey = keys[keyId];
662  KeyFrameData<Quaternion> key;
663 
664  auto time = aiTimeToPdxTimeSpan(aiKey.mTime, ticksPerSec);
665  auto value = aiQuaternionToQuaternion(aiKey.mValue);
666 
667  key.Time = time;
668  lastKeyTime = time;
669 
670  key.Value.X = value.X;
671  key.Value.Y = value.Y;
672  key.Value.Z = value.Z;
673  key.Value.W = value.W;
674 
675  animationCurve->KeyFrames->Add(key);
676  }
677 
678  animationClip->AddCurve(partialTargetName, animationCurve);
679 
680  if (nbKeys > 0)
681  {
682  if (animationClip->Duration < lastKeyTime)
683  animationClip->Duration = lastKeyTime;
684  }
685  }
686 
687  void ProcessNodeAnimation(AnimationClip^ animationClip, const aiNodeAnim* nodeAnim, double ticksPerSec)
688  {
689  // Find the nodes on which the animation is performed
690  auto nodeName = aiStringToString(nodeAnim->mNodeName);
691 
692  // The scales
693  ProcessAnimationCurveVector(animationClip, nodeAnim->mScalingKeys, nodeAnim->mNumScalingKeys, String::Format("Transformation.Scaling[{0}]", nodeName), ticksPerSec);
694  // The rotation
695  ProcessAnimationCurveQuaternion(animationClip, nodeAnim->mRotationKeys, nodeAnim->mNumRotationKeys, String::Format("Transformation.Rotation[{0}]", nodeName), ticksPerSec);
696  // The translation
697  ProcessAnimationCurveVector(animationClip, nodeAnim->mPositionKeys, nodeAnim->mNumPositionKeys, String::Format("Transformation.Translation[{0}]", nodeName), ticksPerSec);
698  }
699 
700  AnimationClip^ ProcessAnimation(const aiScene* scene)
701  {
702  auto animationClip = gcnew AnimationClip();
703  std::set<std::string> visitedNodeNames;
704 
705  for (unsigned int i = 0; i < scene->mNumAnimations; ++i)
706  {
707  auto aiAnim = scene->mAnimations[i];
708 
709  // Assimp animations have two different channels of animations ((1) on Nodes, (2) on Meshes).
710  // Nevertheless the second one do not seems to be usable in assimp 3.0 so it will be ignored here.
711 
712  // name of the animation (dropped)
713  auto animName = aiStringToString(aiAnim->mName); // used only be the logger
714 
715  // animation speed
716  auto ticksPerSec = aiAnim->mTicksPerSecond;
717 
718  // animation using meshes (not supported)
719  for(unsigned int meshAnimId = 0; meshAnimId<aiAnim->mNumMeshChannels; ++meshAnimId)
720  {
721  auto meshName = aiStringToString(aiAnim->mMeshChannels[meshAnimId]->mName);
722  Logger->Warning("Mesh animation are not currently supported. Animation '{0}' on mesh {1} will be ignored", animName, meshName,
723  CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
724  }
725 
726  // animation on nodes
727  for(unsigned int nodeAnimId=0; nodeAnimId<aiAnim->mNumChannels; ++nodeAnimId)
728  {
729  auto nodeAnim = aiAnim->mChannels[nodeAnimId];
730  auto nodeName = std::string(nodeAnim->mNodeName.C_Str());
731  if (visitedNodeNames.find(nodeName) == visitedNodeNames.end())
732  {
733  visitedNodeNames.insert(nodeName);
734  ProcessNodeAnimation(animationClip, nodeAnim, ticksPerSec);
735  }
736  else
737  {
738  Logger->Error("Animation '{0}' uses two nodes with the same name ({1}). The animation cannot be resolved.", animName, aiStringToString(nodeAnim->mNodeName),
739  CallerInfo::Get(__FILEW__, __FUNCTIONW__, __LINE__));
740  return nullptr;
741  }
742  }
743  }
744  return animationClip;
745  }
746 
747  MaterialReferenceNode^ GetTextureReferenceNode(String^ vfsOutputPath, String^ sourceTextureFile, int textureUVSetIndex, Vector2 textureUVscaling, bool wrapTextureU, bool wrapTextureV, MaterialDescription^ finalMaterial, SiliconStudio::Core::Diagnostics::Logger^ logger)
748  {
749  // TODO: compare with FBX importer - see if there could be some conflict between texture names
750  auto textureValue = TextureLayerGenerator::GenerateMaterialTextureNode(vfsOutputPath, sourceTextureFile, textureUVSetIndex, textureUVscaling, wrapTextureU, wrapTextureV, Logger);
751  auto referenceName = textureValue->TextureName;
752 
753  // find a new and correctName
754  if (!textureNameCount->ContainsKey(referenceName))
755  textureNameCount->Add(referenceName, 1);
756  else
757  {
758  int count = textureNameCount[referenceName];
759  textureNameCount[referenceName] = count + 1;
760  referenceName = String::Concat(referenceName, "_", count);
761  }
762 
763  auto materialReference = gcnew MaterialReferenceNode(referenceName);
764  finalMaterial->AddNode(referenceName, textureValue);
765 
766  return materialReference;
767  }
768 
769  MaterialNodeBase^ GenerateOneTextureTypeLayers(aiMaterial* pMat, aiTextureType textureType, int& textureCount, SiliconStudio::Paradox::Assets::Materials::MaterialDescription^ finalMaterial)
770  {
771  AssimpNet::Material::Stack^ stack = NetTranslation::Materials::convertAssimpStackCppToCs(pMat, textureType);
772  int set;
773  System::Collections::Stack^ compositionFathers = gcnew System::Collections::Stack();
774  std::stack<int> sets;
775 
776  sets.push(0);
777  auto nbTextures = pMat->GetTextureCount(textureType);
778  MaterialNodeBase^ curComposition = nullptr,^ newCompositionFather = nullptr;
779  MaterialNodeBase^ curCompositionFather = nullptr;
780 
781  bool isRootElement = true;
782  MaterialNodeBase^ rootMaterial = nullptr;
783 
784  while (!stack->IsEmpty)
785  {
786  auto top = stack->Pop();
787 
788  if (!isRootElement)
789  {
790  if (compositionFathers->Count == 0)
791  Logger->Error(String::Format("Texture Stack Invalid : Operand without Operation."));
792 
793  curCompositionFather = (MaterialNodeBase^)compositionFathers->Pop();
794  }
795 
796  set = sets.top();
797  sets.pop();
798  auto type = top->type;
799  auto strength = top->blend;
800  auto alpha = top->alpha;
801 
803  {
804  auto realTop = (AssimpNet::Material::StackOperation^) top;
805  AssimpNet::Material::Operation op = realTop->operation;
806  auto binNode = gcnew MaterialBinaryNode(nullptr, nullptr, MaterialBinaryOperand::Add);
807 
808  switch (op)
809  {
810  case AssimpNet::Material::Operation::Add3ds:
811  case AssimpNet::Material::Operation::AddMaya:
812  binNode->Operand = MaterialBinaryOperand::Add; //MaterialBinaryOperand::Add3ds;
813  break;
814  case AssimpNet::Material::Operation::Multiply3ds:
815  case AssimpNet::Material::Operation::MultiplyMaya:
816  binNode->Operand = MaterialBinaryOperand::Multiply;
817  break;
818  default:
819  binNode->Operand = MaterialBinaryOperand::Add;
820  break;
821  }
822 
823  curComposition = binNode;
824  }
825  else if (type == AssimpNet::Material::StackType::Color)
826  {
827  auto realTop = (AssimpNet::Material::StackColor^)top;
828  Color3 col = realTop->color;
829  curComposition = gcnew MaterialColorNode(Color4(col.R, col.G, col.B, alpha));
830  }
831  else if (type == AssimpNet::Material::StackType::Texture)
832  {
833  auto realTop = (AssimpNet::Material::StackTexture^)top;
834  String ^texPath = realTop->texturePath;
835  int indexUV = realTop->channel;
836  auto textureValue = GetTextureReferenceNode(vfsOutputFilename, texPath, indexUV, Vector2::One, false, false, finalMaterial, Logger);
837  curComposition = textureValue;
838  }
839 
840  newCompositionFather = curComposition;
841 
842  if (strength != 1.f)
843  {
844  float strengthAlpha = strength;
846  strengthAlpha *= alpha;
847 
848 
849  auto factorComposition = gcnew MaterialFloat4Node(Vector4(strength, strength, strength, strengthAlpha));
850  curComposition = gcnew MaterialBinaryNode(curComposition, factorComposition, MaterialBinaryOperand::Multiply);
851  }
852  else if (alpha != 1.f && type != AssimpNet::Material::StackType::Color)
853  {
854  auto factorComposition = gcnew MaterialFloat4Node(Vector4(1.0f, 1.0f, 1.0f, alpha));
855  curComposition = gcnew MaterialBinaryNode(curComposition, factorComposition, MaterialBinaryOperand::Multiply);
856  }
857 
858  if (isRootElement)
859  {
860  rootMaterial = curComposition;
861  isRootElement = false;
862  compositionFathers->Push(curCompositionFather);
863  }
864  else
865  {
866  if (set == 0)
867  {
868  ((MaterialBinaryNode^)curCompositionFather)->LeftChild = curComposition;
869  compositionFathers->Push(curCompositionFather);
870  sets.push(1);
871  }
872  else if (set == 1)
873  {
874  ((MaterialBinaryNode^)curCompositionFather)->RightChild = curComposition;
875  }
876  else
877  {
878  Logger->Error(String::Format("Texture Stack Invalid : Invalid Operand Number {0}.", set));
879  }
880  }
881 
883  {
884  compositionFathers->Push(newCompositionFather);
885  sets.push(0);
886  }
887  }
888 
889  return rootMaterial;
890  }
891 
892  void BuildLayeredSurface(aiMaterial* pMat, bool hasBaseColor, bool hasBaseValue, Color4 baseColor, float baseValue, aiTextureType textureType, SiliconStudio::Paradox::Assets::Materials::MaterialDescription^ finalMaterial)
893  {
894  auto nbTextures = pMat->GetTextureCount(textureType);
895 
896  if(nbTextures == 0)
897  {
898  if (hasBaseColor)
899  {
900  auto colorNode = gcnew MaterialColorNode(baseColor);
901  colorNode->AutoAssignKey = false;
902  colorNode->IsReducible = false;
903  if (textureType == aiTextureType_DIFFUSE)
904  {
905  colorNode->Key = MaterialKeys::DiffuseColorValue;
906  finalMaterial->AddColorNode(MaterialParameters::AlbedoDiffuse, "diffuse", colorNode);
907  }
908  else if (textureType == aiTextureType_SPECULAR)
909  {
910  colorNode->Key = MaterialKeys::SpecularColorValue;
911  finalMaterial->AddColorNode(MaterialParameters::AlbedoSpecular, "specular", colorNode);
912  }
913  else if (textureType == aiTextureType_EMISSIVE)
914  {
915  colorNode->Key = MaterialKeys::EmissiveColorValue;
916  finalMaterial->AddColorNode(MaterialParameters::EmissiveMap, "emissive", colorNode);
917  }
918  else if (textureType == aiTextureType_AMBIENT)
919  {
920  colorNode->Key = MaterialKeys::AmbientColorValue;
921  finalMaterial->AddColorNode(MaterialParameters::AmbientMap, "ambient", colorNode);
922  }
923  else if (textureType == aiTextureType_REFLECTION)
924  {
925  colorNode->Key = MaterialKeys::ReflectionColorValue;
926  finalMaterial->AddColorNode(MaterialParameters::ReflectionMap, "reflectionMap", colorNode);
927  }
928  }
929  else if (hasBaseValue)
930  {
931  auto floatNode = gcnew MaterialFloatNode(baseValue);
932  floatNode->AutoAssignKey = false;
933  floatNode->IsReducible = false;
934  if (textureType == aiTextureType_OPACITY)
935  {
936  floatNode->Key = MaterialKeys::TransparencyValue;
937  finalMaterial->AddColorNode(MaterialParameters::TransparencyMap, "transparencyMap", floatNode);
938  }
939  else if (textureType == aiTextureType_SHININESS)
940  {
941  floatNode->Key = MaterialKeys::SpecularPower;
942  finalMaterial->AddColorNode(MaterialParameters::SpecularPowerMap, "specularPower", floatNode);
943  }
944  }
945  }
946  else
947  {
948  int textureCount = 0;
949  auto albedoNode = GenerateOneTextureTypeLayers(pMat, textureType, textureCount, finalMaterial);
950  if (albedoNode != nullptr)
951  {
952  if (textureType == aiTextureType_DIFFUSE)
953  {
954  if (pMat->GetTextureCount(aiTextureType_LIGHTMAP) > 0)
955  {
956  auto lightMap = GenerateOneTextureTypeLayers(pMat, aiTextureType_LIGHTMAP, textureCount, finalMaterial);
957  if (lightMap != nullptr)
958  albedoNode = gcnew MaterialBinaryNode(albedoNode, lightMap, MaterialBinaryOperand::Add);
959  }
960  finalMaterial->AddColorNode(MaterialParameters::AlbedoDiffuse, "diffuse", albedoNode);
961  }
962  else if (textureType == aiTextureType_SPECULAR)
963  {
964  finalMaterial->AddColorNode(MaterialParameters::AlbedoSpecular, "specular", albedoNode);
965  }
966  else if (textureType == aiTextureType_NORMALS)
967  {
968  finalMaterial->AddColorNode(MaterialParameters::NormalMap, "normalMap", albedoNode);
969  }
970  else if (textureType == aiTextureType_DISPLACEMENT)
971  {
972  finalMaterial->AddColorNode(MaterialParameters::DisplacementMap, "displacementMap", albedoNode);
973  }
974  else if (textureType == aiTextureType_AMBIENT)
975  {
976  finalMaterial->AddColorNode(MaterialParameters::AmbientMap, "ambient", albedoNode);
977  }
978  else if (textureType == aiTextureType_OPACITY)
979  {
980  finalMaterial->AddColorNode(MaterialParameters::TransparencyMap, "transparencyMap", albedoNode);
981  }
982  else if (textureType == aiTextureType_SHININESS)
983  {
984  finalMaterial->AddColorNode(MaterialParameters::SpecularPowerMap, "specularPower", albedoNode);
985  }
986  else if (textureType == aiTextureType_EMISSIVE)
987  {
988  finalMaterial->AddColorNode(MaterialParameters::EmissiveMap, "emissive", albedoNode);
989  }
990  else if (textureType == aiTextureType_HEIGHT)
991  {
992  finalMaterial->AddColorNode(MaterialParameters::BumpMap, "bumpMap", albedoNode);
993  }
994  else if (textureType == aiTextureType_REFLECTION)
995  {
996  finalMaterial->AddColorNode(MaterialParameters::ReflectionMap, "reflectionMap", albedoNode);
997  }
998  }
999  }
1000  }
1001 
1002  MaterialDescription^ ProcessMeshMaterial(aiMaterial* pMaterial)
1003  {
1004  auto finalMaterial = gcnew MaterialDescription();
1005 
1006  // Set material specular components
1007  float specIntensity;
1008  if (AI_SUCCESS == pMaterial->Get(AI_MATKEY_SHININESS_STRENGTH, specIntensity))
1009  {
1010  if (specIntensity > 0)
1011  {
1012  auto specularIntensityMap = gcnew MaterialFloatNode(specIntensity);
1013  specularIntensityMap->Key = MaterialKeys::SpecularIntensity;
1014  specularIntensityMap->AutoAssignKey = false;
1015  specularIntensityMap->IsReducible = false;
1016  finalMaterial->AddColorNode(MaterialParameters::SpecularIntensityMap, "specularIntensity", specularIntensityMap);
1017  }
1018  }
1019 
1020  // ---------------------------------------------------------------------------------
1021  // Iterate on all custom Paradox Properties and add them to the mesh.
1022  // Key must be in the format: Paradox_KeyName
1023  // ---------------------------------------------------------------------------------
1024  for(unsigned int i = 0; i<pMaterial->mNumProperties; ++i)
1025  {
1026  auto pProp = pMaterial->mProperties[i];
1027  auto propertyName = aiStringToString(pProp->mKey);
1028  if (propertyName->StartsWith("PX_"))
1029  {
1030  int index = propertyName->IndexOf('_');
1031  propertyName = propertyName->Substring(index);
1032  propertyName = propertyName->Replace('_','.');
1033  // TODO Paradox Change name
1034  propertyName = gcnew String("SiliconStudio.Paradox.Effects.Modules") + propertyName;
1035 
1036  switch (pProp->mDataLength)
1037  {
1038  case sizeof(double):
1039  {
1040  auto value = *((double*)pProp->mData);
1041  ParameterKey<float>^ key = gcnew ParameterKey<float>(propertyName, 1, nullptr);
1042  finalMaterial->SetParameter(key, (float)value);
1043  }
1044  break;
1045  case 3*sizeof(double):
1046  {
1047  auto value = (double*)pProp->mData;
1048  ParameterKey<Vector3>^ key = gcnew ParameterKey<Vector3>(propertyName, 1, nullptr);
1049  finalMaterial->SetParameter(key, Vector3((float)value[0], (float)value[1], (float)value[2]));
1050  }
1051  break;
1052  default:
1053  Console::WriteLine("Warning, Type for property [{0}] is not supported", propertyName);
1054  break;
1055  }
1056  }
1057  }
1058 
1059  // Build the material Diffuse, Specular, NormalMap and DisplacementColor surfaces.
1060  aiColor3D color;
1061  Color4 diffColor;
1062  Color4 specColor;
1063  Color4 ambientColor;
1064  Color4 emissiveColor;
1065  Color4 reflectiveColor;
1066  Color4 dummyColor;
1067  float specPower;
1068  float opacity;
1069 
1070  bool hasDiffColor = false;
1071  bool hasSpecColor = false;
1072  bool hasAmbientColor = false;
1073  bool hasEmissiveColor = false;
1074  bool hasReflectiveColor = false;
1075  bool hasSpecPower = false;
1076  bool hasOpacity = false;
1077 
1078  if(pMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) // always keep black color for diffuse
1079  {
1080  diffColor = aiColor3ToColor4(color);
1081  hasDiffColor = true;
1082  }
1083  if(pMaterial->Get(AI_MATKEY_COLOR_SPECULAR, color) == AI_SUCCESS && IsNotBlackColor(color))
1084  {
1085  specColor = aiColor3ToColor4(color);
1086  hasSpecColor = true;
1087  }
1088  if(pMaterial->Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS && IsNotBlackColor(color))
1089  {
1090  ambientColor = aiColor3ToColor4(color);
1091  hasAmbientColor = true;
1092  }
1093  if(pMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS && IsNotBlackColor(color))
1094  {
1095  emissiveColor = aiColor3ToColor4(color);
1096  hasEmissiveColor = true;
1097  }
1098  if(pMaterial->Get(AI_MATKEY_COLOR_REFLECTIVE, color) == AI_SUCCESS && IsNotBlackColor(color))
1099  {
1100  reflectiveColor = aiColor3ToColor4(color);
1101  hasReflectiveColor = true;
1102  }
1103 
1104  hasSpecPower = (AI_SUCCESS == pMaterial->Get(AI_MATKEY_SHININESS, specPower) && specPower > 0);
1105  if(pMaterial->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS && opacity < 1.0)
1106  {
1107  finalMaterial->SetParameter(MaterialParameters::UseTransparent, true);
1108  hasOpacity = true;
1109  }
1110 
1111  BuildLayeredSurface(pMaterial, hasDiffColor, false, diffColor, 0.0f, aiTextureType_DIFFUSE, finalMaterial);
1112  BuildLayeredSurface(pMaterial, hasSpecColor, false, specColor, 0.0f, aiTextureType_SPECULAR, finalMaterial);
1113  BuildLayeredSurface(pMaterial, false, false, dummyColor, 0.0f, aiTextureType_NORMALS, finalMaterial);
1114  BuildLayeredSurface(pMaterial, false, false, dummyColor, 0.0f, aiTextureType_DISPLACEMENT, finalMaterial);
1115  BuildLayeredSurface(pMaterial, hasAmbientColor, false, ambientColor, 0.0f, aiTextureType_AMBIENT, finalMaterial);
1116  BuildLayeredSurface(pMaterial, false, hasOpacity, dummyColor, opacity, aiTextureType_OPACITY, finalMaterial);
1117  BuildLayeredSurface(pMaterial, false, hasSpecPower, dummyColor, specPower, aiTextureType_SHININESS, finalMaterial);
1118  BuildLayeredSurface(pMaterial, hasEmissiveColor, false, emissiveColor, 0.0f, aiTextureType_EMISSIVE, finalMaterial);
1119  BuildLayeredSurface(pMaterial, false, false, dummyColor, 0.0f, aiTextureType_HEIGHT, finalMaterial);
1120  BuildLayeredSurface(pMaterial, hasReflectiveColor, false, reflectiveColor, 0.0f, aiTextureType_REFLECTION, finalMaterial);
1121 
1122  return finalMaterial;
1123  }
1124 
1125  bool IsNotBlackColor(aiColor3D color)
1126  {
1127  return color.r != 0 || color.g != 0 || color.b != 0;
1128  }
1129 
1130  template<class T>
1131  void GenerateUniqueNames(std::map<T*, std::string>& finalNames, std::vector<std::string>& baseNames, T** objectsToName)
1132  {
1133  std::map<std::string, int> itemNameTotalCount;
1134  std::map<std::string, int> itemNameCurrentCount;
1135  std::vector<std::string> tempNames;
1136 
1137  for (int i = 0; i < baseNames.size(); ++i)
1138  {
1139  // Clean the name by removing unwanted characters
1140  T* lItem = objectsToName[i];
1141  std::string itemName = baseNames[i];
1142 
1143  auto itemPart = std::string();
1144 
1145  int itemNameSplitPosition = itemName.find('#');
1146  if (itemNameSplitPosition != std::string::npos)
1147  {
1148  itemPart = itemName.substr(itemNameSplitPosition + 1);
1149  itemName = itemName.substr(0, itemNameSplitPosition);
1150  }
1151 
1152  itemNameSplitPosition = itemNameSplitPosition = itemName.find("__");
1153  if (itemNameSplitPosition != std::string::npos)
1154  {
1155  itemPart = itemName.substr(itemNameSplitPosition + 2);
1156  itemName = itemName.substr(0, itemNameSplitPosition);
1157  }
1158 
1159  // TODO: remove all bad characters
1160  int nextCharacterPos = itemName.find(':');
1161  while (nextCharacterPos != std::string::npos)
1162  {
1163  itemName.replace(nextCharacterPos, 1, 1, '_');
1164  nextCharacterPos = itemName.find(':', nextCharacterPos);
1165  }
1166  tempNames.push_back(itemName);
1167 
1168  // count the occurences of this name
1169  if (itemNameTotalCount.count(itemName) == 0)
1170  itemNameTotalCount[itemName] = 1;
1171  else
1172  itemNameTotalCount[itemName] = itemNameTotalCount[itemName] + 1;
1173  }
1174 
1175  for (int i = 0; i < baseNames.size(); ++i)
1176  {
1177  T* lItem = objectsToName[i];
1178  auto itemName = tempNames[i];
1179  int currentCount = 0;
1180 
1181  if (itemNameTotalCount[itemName] > 1)
1182  {
1183  if (itemNameCurrentCount.count(itemName) == 0)
1184  itemNameCurrentCount[itemName] = 1;
1185  else
1186  itemNameCurrentCount[itemName] = itemNameCurrentCount[itemName] + 1;
1187 
1188  itemName = itemName + "_" + std::to_string(itemNameCurrentCount[itemName]);
1189  }
1190 
1191  finalNames[lItem] = itemName;
1192  }
1193  }
1194 
1195  void GenerateMaterialNames(const aiScene* scene, std::map<aiMaterial*, std::string>& materialNames)
1196  {
1197  std::vector<std::string> baseNames;
1198  for (uint32_t i = 0; i < scene->mNumMaterials; i++)
1199  {
1200  auto lMaterial = scene->mMaterials[i];
1201 
1202  std::string materialName = "";
1203  aiString aiName;
1204  if (lMaterial->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS)
1205  materialName = std::string(aiName.C_Str());
1206  baseNames.push_back(materialName);
1207  }
1208 
1209  GenerateUniqueNames(materialNames, baseNames, scene->mMaterials);
1210  }
1211 
1212  void GenerateMeshNames(const aiScene* scene, std::map<aiMesh*, std::string>& meshNames)
1213  {
1214  std::vector<std::string> baseNames;
1215  for (uint32_t i = 0; i < scene->mNumMeshes; i++)
1216  {
1217  auto lMesh = scene->mMeshes[i];
1218  std::string meshName = std::string(lMesh->mName.C_Str());
1219  baseNames.push_back(meshName);
1220  }
1221 
1222  GenerateUniqueNames(meshNames, baseNames, scene->mMeshes);
1223  }
1224 
1225  void GenerateAnimationNames(const aiScene* scene, std::map<aiAnimation*, std::string>& animationNames)
1226  {
1227  std::vector<std::string> baseNames;
1228  for (uint32_t i = 0; i < scene->mNumAnimations; i++)
1229  {
1230  auto lAnimation = scene->mAnimations[i];
1231  std::string animationName = std::string(lAnimation->mName.C_Str());
1232  baseNames.push_back(animationName);
1233  }
1234 
1235  GenerateUniqueNames(animationNames, baseNames, scene->mAnimations);
1236  }
1237 
1238  void GetNodeNames(aiNode* node, std::vector<std::string>& nodeNames, std::vector<aiNode*>& orderedNodes)
1239  {
1240  nodeNames.push_back(std::string(node->mName.C_Str()));
1241  orderedNodes.push_back(node);
1242  for (uint32_t i = 0; i < node->mNumChildren; ++i)
1243  GetNodeNames(node->mChildren[i], nodeNames, orderedNodes);
1244  }
1245 
1246  void GenerateNodeNames(const aiScene* scene, std::map<aiNode*, std::string>& nodeNames)
1247  {
1248  std::vector<std::string> baseNames;
1249  std::vector<aiNode*> orderedNodes;
1250  GetNodeNames(scene->mRootNode, baseNames, orderedNodes);
1251 
1252  // Need to create the array of the nodes
1253  int nodeCount = orderedNodes.size();
1254  aiNode** nodeArray = new aiNode*[nodeCount];
1255  for (int i = 0; i < nodeCount; ++i)
1256  nodeArray[i] = orderedNodes[i];
1257 
1258  GenerateUniqueNames(nodeNames, baseNames, nodeArray);
1259 
1260  delete[] nodeArray;
1261  }
1262 
1263  List<String^>^ ExtractTextureDependencies(const aiScene *scene)
1264  {
1265  auto textureNames = gcnew List<String^>();
1266 
1267  // get internal textures (not supported)
1268  /*for(int i=0; i<scene->mNumTextures; ++i)
1269  {
1270  auto texture = scene->mTextures[i];
1271 
1272  if(texture == nullptr)
1273  continue;
1274 
1275  //TODO: add material name
1276  std::string baseName = "GenTexture";
1277  baseName.append(std::to_string(i));
1278 
1279  textureNames->Add(gcnew String(baseName.c_str()));
1280  }*/
1281 
1282  // texture search is done by type so we need to loop on them
1283  std::vector<aiTextureType> allTextureTypes;
1284  allTextureTypes.push_back(aiTextureType_DIFFUSE);
1285  allTextureTypes.push_back(aiTextureType_SPECULAR);
1286  allTextureTypes.push_back(aiTextureType_AMBIENT);
1287  allTextureTypes.push_back(aiTextureType_EMISSIVE);
1288  allTextureTypes.push_back(aiTextureType_HEIGHT);
1289  allTextureTypes.push_back(aiTextureType_NORMALS);
1290  allTextureTypes.push_back(aiTextureType_SHININESS);
1291  allTextureTypes.push_back(aiTextureType_OPACITY);
1292  allTextureTypes.push_back(aiTextureType_DISPLACEMENT);
1293  allTextureTypes.push_back(aiTextureType_LIGHTMAP);
1294  allTextureTypes.push_back(aiTextureType_REFLECTION);
1295  //allTextureTypes.push_back(aiTextureType_NONE);
1296  //allTextureTypes.push_back(aiTextureType_UNKNOWN);
1297 
1298  for (uint32_t i = 0; i < scene->mNumMaterials; i++)
1299  {
1300  for (std::vector<aiTextureType>::iterator it = allTextureTypes.begin(); it != allTextureTypes.end(); ++it)
1301  {
1302  auto lMaterial = scene->mMaterials[i];
1303  auto nbTextures = lMaterial->GetTextureCount(*it);
1304  for (uint32_t j = 0; j < nbTextures; ++j)
1305  {
1306  aiString path;
1307  aiTextureMapping mapping;
1308  unsigned int index;
1309  float blend;
1310  aiTextureOp textureOp;
1311  aiTextureMapMode mapMode;
1312  if (AI_SUCCESS == lMaterial->GetTexture(*it, 0, &path, &mapping, &index, &blend, &textureOp, &mapMode))
1313  {
1314  auto relFileName = gcnew String(path.C_Str());
1315  String^ fileNameToUse = Path::Combine(vfsInputPath, relFileName);
1316  textureNames->Add(fileNameToUse);
1317  break;
1318  }
1319  }
1320  }
1321  }
1322 
1323  return textureNames;
1324  }
1325 
1326  Dictionary<String^, MaterialDescription^>^ ExtractMaterials(const aiScene *scene, std::map<aiMaterial*, std::string>& materialNames)
1327  {
1328  GenerateMaterialNames(scene, materialNames);
1329 
1330  auto materials = gcnew Dictionary<String^, MaterialDescription^>();
1331  for (uint32_t i = 0; i < scene->mNumMaterials; i++)
1332  {
1333  std::map<std::string, int> dict;
1334  auto lMaterial = scene->mMaterials[i];
1335  auto materialName = materialNames[lMaterial];
1336  materials->Add(gcnew String(materialName.c_str()), ProcessMeshMaterial(lMaterial));
1337  }
1338  return materials;
1339  }
1340 
1341  String^ SearchMeshNode(aiNode* node, unsigned int meshIndex, std::map<aiNode*, std::string>& nodeNames)
1342  {
1343  for (uint32_t i = 0; i < node->mNumMeshes; ++i)
1344  {
1345  if (node->mMeshes[i] == meshIndex)
1346  return gcnew String(nodeNames[node].c_str());
1347  }
1348 
1349  for (uint32_t i = 0; i < node->mNumChildren; ++i)
1350  {
1351  auto res = SearchMeshNode(node->mChildren[i], meshIndex, nodeNames);
1352  if (res != nullptr)
1353  return res;
1354  }
1355 
1356  return nullptr;
1357  }
1358 
1359  List<CameraInfo^>^ ExtractCameras(const aiScene* scene, std::map<aiNode*, std::string>& nodeNames)
1360  {
1361  auto allCameras = gcnew List<CameraInfo^>();
1362  for (uint32_t cameraIndex = 0; cameraIndex < scene->mNumCameras; ++cameraIndex)
1363  {
1364  auto assimpCam = scene->mCameras[cameraIndex];
1365 
1366  // In Assimp the cameras are part of the node hierarchy,
1367  // but unfortunatelly the camera property structures have no direct reference to their corresponding hierarchy node.
1368  // This correspondance can only be found by evaluating the correspondance between the camera node and property names.
1369  auto camName = std::string(assimpCam->mName.C_Str());
1370  bool foundNode = false;
1371  bool foundTargetNode = false;
1372  for (std::map<aiNode*, std::string>::iterator iter = nodeNames.begin(); iter != nodeNames.end(); ++iter)
1373  {
1374  if (camName == iter->second)
1375  {
1376  foundNode = true;
1377  break;
1378  }
1379  }
1380  if (!foundNode)
1381  continue; // TODO: log error/warning
1382 
1383  auto cameraInfo = gcnew CameraInfo();
1384  cameraInfo->NodeName = gcnew String(assimpCam->mName.C_Str());
1385  cameraInfo->TargetNodeName = cameraInfo->NodeName + ".Target";
1386 
1387  // Build the camera attribute data
1388  auto camData = gcnew CameraComponentData();
1389  camData->NearPlane = assimpCam->mClipPlaneNear;
1390  camData->FarPlane = assimpCam->mClipPlaneFar;
1391  camData->AspectRatio = assimpCam->mAspect;
1392  camData->VerticalFieldOfView = assimpCam->mHorizontalFOV / assimpCam->mAspect;
1393  // TODO: handle mPosition
1394 
1395  cameraInfo->Data = camData;
1396  allCameras->Add(cameraInfo);
1397  }
1398  return allCameras;
1399  }
1400 
1401  List<LightInfo^>^ ExtractLights(const aiScene* scene, std::map<aiNode*, std::string>& nodeNames)
1402  {
1403  auto allLights = gcnew List<LightInfo^>();
1404  for (uint32_t lightIndex = 0; lightIndex < scene->mNumLights; ++lightIndex)
1405  {
1406  auto assimpLight = scene->mLights[lightIndex];
1407 
1408  // In Assimp the lights are part of the node hierarchy,
1409  // but unfortunatelly the light property structures have no direct reference to their corresponding hierarchy node.
1410  // This correspondance can only be found by evaluating the correspondance between the light node and property names.
1411  auto lightName = std::string(assimpLight->mName.C_Str());
1412  bool foundNode = false;
1413  bool foundTargetNode = false;
1414  for (std::map<aiNode*, std::string>::iterator iter = nodeNames.begin(); iter != nodeNames.end(); ++iter)
1415  {
1416  if (lightName == iter->second)
1417  {
1418  foundNode = true;
1419  break;
1420  }
1421  }
1422  if (!foundNode)
1423  continue; // TODO: log error/warning
1424 
1425  auto lightInfo = gcnew LightInfo();
1426  lightInfo->NodeName = gcnew String(assimpLight->mName.C_Str()); // TODO: check that the node exists
1427 
1428  // Build the light attribute data
1429  auto lightData = gcnew LightComponentData();
1430  lightData->LightDirection = Vector3(assimpLight->mDirection.x, assimpLight->mDirection.y, assimpLight->mDirection.z);
1431  lightData->Type = aiLightTypeToPdxLightType(assimpLight->mType, lightInfo->NodeName, Logger);
1432 
1433  // --- Temporary --- TODO expand LightData members and fill them properly
1434  lightData->Color = aiColor3ToColor3(assimpLight->mColorDiffuse);
1435  lightData->Deferred = lightData->Type == LightType::Point;
1436  lightData->Intensity = 1.f;
1437  lightData->DecayStart = 1.f;
1438  // --- End Temporary ---
1439 
1440  lightData->Layers = RenderLayers::RenderLayerAll;
1441 
1442  lightInfo->Data = lightData;
1443  allLights->Add(lightInfo);
1444  }
1445  return allLights;
1446  }
1447 
1448  List<MeshParameters^>^ ExtractModel(const aiScene* scene, std::map<aiMesh*, std::string>& meshNames, std::map<aiMaterial*, std::string>& materialNames, std::map<aiNode*, std::string>& nodeNames)
1449  {
1450  GenerateMeshNames(scene, meshNames);
1451 
1452  auto meshList = gcnew List<MeshParameters^>();
1453  for (uint32_t i = 0; i < scene->mNumMeshes; ++i)
1454  {
1455  auto mesh = scene->mMeshes[i];
1456  auto meshParams = gcnew MeshParameters();
1457  meshParams->MeshName = gcnew String(meshNames[mesh].c_str());
1458  auto lMaterial = scene->mMaterials[mesh->mMaterialIndex];
1459  meshParams->MaterialName = gcnew String(materialNames[lMaterial].c_str());
1460  meshParams->NodeName = SearchMeshNode(scene->mRootNode, i, nodeNames);
1461  meshList->Add(meshParams);
1462  }
1463  return meshList;
1464  }
1465 
1466  List<String^>^ ExtractAnimations(const aiScene* scene, std::map<aiAnimation*, std::string>& animationNames)
1467  {
1468  if (scene->mNumAnimations == 0)
1469  return nullptr;
1470 
1471  GenerateAnimationNames(scene, animationNames);
1472 
1473  auto animationList = gcnew List<String^>();
1474  for (std::map<aiAnimation*, std::string>::iterator it = animationNames.begin(); it != animationNames.end(); ++it)
1475  {
1476  animationList->Add(gcnew String(it->second.c_str()));
1477  }
1478  return animationList;
1479  }
1480 
1481  ModelData^ ConvertAssimpScene(const aiScene *scene)
1482  {
1483  modelData = gcnew ModelData();
1484  modelData->Hierarchy = gcnew ModelViewHierarchyDefinition();
1485 
1486  std::map<aiMesh*, std::string> meshNames;
1487  GenerateMeshNames(scene, meshNames);
1488 
1489  std::map<aiNode*, std::string> nodeNames;
1490  GenerateNodeNames(scene, nodeNames);
1491 
1492  // register the nodes and fill hierarchy
1493  std::map<int, std::vector<int>*> meshIndexToNodeIndex;
1494 
1495  RegisterNodes(scene->mRootNode, -1, nodeNames, meshIndexToNodeIndex);
1496  modelData->Hierarchy->Nodes = nodes.ToArray();
1497 
1498  // meshes
1499  for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
1500  {
1501  if (meshIndexToNodeIndex.find(i) != meshIndexToNodeIndex.end())
1502  {
1503  auto meshInfo = ProcessMesh(scene, scene->mMeshes[i], meshNames);
1504 
1505  for (std::vector<int>::iterator nodeIndexPtr = meshIndexToNodeIndex[i]->begin(); nodeIndexPtr != meshIndexToNodeIndex[i]->end(); ++nodeIndexPtr)
1506  {
1507  auto nodeMeshData = gcnew MeshData();
1508  nodeMeshData->Draw = meshInfo->Draw;
1509  nodeMeshData->Name = meshInfo->Name;
1510  nodeMeshData->NodeIndex = *nodeIndexPtr;
1511 
1512  if (meshInfo->Bones != nullptr)
1513  {
1514  nodeMeshData->Skinning = gcnew MeshSkinningDefinition();
1515  nodeMeshData->Skinning->Bones = meshInfo->Bones->ToArray();
1516  }
1517  if (meshInfo->HasSkinningPosition || meshInfo->HasSkinningNormal || meshInfo->TotalClusterCount > 0)
1518  {
1519  nodeMeshData->Parameters = gcnew ParameterCollectionData();
1520  if (meshInfo->HasSkinningPosition)
1521  nodeMeshData->Parameters->Set(MaterialParameters::HasSkinningPosition, true);
1522  if (meshInfo->HasSkinningNormal)
1523  nodeMeshData->Parameters->Set(MaterialParameters::HasSkinningNormal, true);
1524  if (meshInfo->TotalClusterCount > 0)
1525  nodeMeshData->Parameters->Set(MaterialParameters::SkinningBones, meshInfo->TotalClusterCount);
1526  }
1527  modelData->Meshes->Add(nodeMeshData);
1528  }
1529  }
1530  }
1531 
1532  // delete the vectors in the map
1533  for (std::map<int, std::vector<int>*>::iterator it = meshIndexToNodeIndex.begin(); it != meshIndexToNodeIndex.end(); ++it)
1534  {
1535  delete it->second;
1536  it->second = 0;
1537  }
1538 
1539  // embedded texture - only to log the warning for now
1540  for (unsigned int i = 0; i < scene->mNumTextures; ++i)
1541  ExtractEmbededTexture(scene->mTextures[i]);
1542 
1543  // cameras - left out
1544  //for (unsigned int i = 0; i < scene->mNumCameras; ++i)
1545  // ProcessCamera(scene->mCameras[i]);
1546 
1547  // lights - left out
1548  //for (unsigned int i = 0; i < scene->mNumLights; ++i)
1549  // ProcessLight(scene->mLights[i]);
1550 
1551  return modelData;
1552  }
1553 
1554  void GetNodes(aiNode* node, int depth, std::map<aiNode*, std::string>& nodeNames, List<NodeInfo^>^ allNodes)
1555  {
1556  auto currentIndex = allNodes->Count;
1557  auto newNodeInfo = gcnew NodeInfo();
1558  newNodeInfo->Name = gcnew String(nodeNames[node].c_str());
1559  newNodeInfo->Depth = depth;
1560  newNodeInfo->Preserve = false;
1561 
1562  allNodes->Add(newNodeInfo);
1563  for (uint32_t i = 0; i < node->mNumChildren; ++i)
1564  GetNodes(node->mChildren[i], depth + 1, nodeNames, allNodes);
1565  }
1566 
1567  Vector3 GetUpAxis(aiNode* rootNode)
1568  {
1569  if (rootNode != 0)
1570  {
1571  // get node transformation
1572  aiVector3t<float> aiTranslation;
1573  aiVector3t<float> aiScaling;
1574  aiQuaterniont<float> aiOrientation;
1575  rootNode->mTransformation.Decompose(aiScaling, aiOrientation, aiTranslation);
1576  if (aiOrientation.x != 0)
1577  return Vector3::UnitZ;
1578  if (aiOrientation.z != 0)
1579  return Vector3::UnitX;
1580  return Vector3::UnitY;
1581  }
1582  return Vector3::UnitZ;
1583  }
1584 
1585  List<NodeInfo^>^ ExtractNodeHierarchy(const aiScene *scene, std::map<aiNode*, std::string>& nodeNames)
1586  {
1587  auto allNodes = gcnew List<NodeInfo^>();
1588  GetNodes(scene->mRootNode, 0, nodeNames, allNodes);
1589  return allNodes;
1590  }
1591 
1592 public:
1593  EntityInfo^ ExtractEntity(String^ inputFilename, String^ outputFilename)
1594  {
1595  try
1596  {
1597  // the importer is kept here since it owns the scene object.
1598  //TODO: check the options
1599  Assimp::Importer importer;
1600  auto scene = Initialize(inputFilename, outputFilename, &importer,
1601  aiProcess_SortByPType
1602  | aiProcess_RemoveRedundantMaterials);
1603 
1604  std::map<aiMaterial*, std::string> materialNames;
1605  std::map<aiMesh*, std::string> meshNames;
1606  std::map<aiAnimation*, std::string> animationNames;
1607  std::map<aiNode*, std::string> nodeNames;
1608 
1609  GenerateNodeNames(scene, nodeNames);
1610 
1611  auto entityInfo = gcnew EntityInfo();
1612  entityInfo->TextureDependencies = ExtractTextureDependencies(scene);
1613  entityInfo->Materials = ExtractMaterials(scene, materialNames);
1614  entityInfo->Models = ExtractModel(scene, meshNames, materialNames, nodeNames);
1615  entityInfo->Lights = ExtractLights(scene, nodeNames);
1616  entityInfo->Cameras = ExtractCameras(scene, nodeNames);
1617  entityInfo->Nodes = ExtractNodeHierarchy(scene, nodeNames);
1618  entityInfo->AnimationNodes = ExtractAnimations(scene, animationNames);
1619  entityInfo->UpAxis = GetUpAxis(scene->mRootNode);
1620 
1621  // patch lights count
1622  int numPointLights = 0;
1623  int numSpotLights = 0;
1624  int numDirectionalLights = 0;
1625  for (int i = 0; i < entityInfo->Lights->Count; ++i)
1626  {
1627  auto lightType = entityInfo->Lights[i]->Data->Type;
1628  if (lightType == LightType::Point)
1629  ++numPointLights;
1630  else if (lightType == LightType::Directional)
1631  ++numDirectionalLights;
1632  else if (lightType == LightType::Spot)
1633  ++numSpotLights;
1634  }
1635 
1636  for (int i = 0; i < entityInfo->Models->Count; ++i)
1637  {
1638  entityInfo->Models[i]->Parameters->Add(LightingKeys::MaxPointLights, numPointLights);
1639  entityInfo->Models[i]->Parameters->Add(LightingKeys::MaxDirectionalLights, numDirectionalLights);
1640  entityInfo->Models[i]->Parameters->Add(LightingKeys::MaxSpotLights, numSpotLights);
1641  }
1642 
1643  return entityInfo;
1644  }
1645  catch (Exception^)
1646  {
1647  return nullptr;
1648  }
1649  }
1650 
1651  ModelData^ Convert(String^ inputFilename, String^ outputFilename)
1652  {
1653  // the importer is kept here since it owns the scene object.
1654  Assimp::Importer importer;
1655  auto scene = Initialize(inputFilename, outputFilename, &importer,
1656  aiProcess_CalcTangentSpace
1657  | aiProcess_RemoveRedundantMaterials
1658  | aiProcess_Triangulate
1659  | aiProcess_GenNormals
1660  | aiProcess_JoinIdenticalVertices
1661  | aiProcess_LimitBoneWeights
1662  | aiProcess_SortByPType
1663  | aiProcess_FlipWindingOrder
1664  | aiProcess_FlipUVs );
1665  return ConvertAssimpScene(scene);
1666  }
1667 
1668  AnimationClip^ ConvertAnimation(String^ inputFilename, String^ outputFilename)
1669  {
1670  // the importer is kept here since it owns the scene object.
1671  Assimp::Importer importer;
1672  auto scene = Initialize(inputFilename, outputFilename, &importer, 0);
1673  return ProcessAnimation(scene);
1674  }
1675 };
1676 
1677 }}}}
Key of an effect parameter.
Definition: ParameterKey.cs:15
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.
void SetParameter(ParameterKey parameterKey, object value)
Set the value of a compilation parameter. Creates a new entry if necessary.
Vector3 Translation
The translation.
Definition: TransformTRS.cs:22
A node that describe a binary operation between two IMaterialNode
Represents a two dimensional mathematical vector.
Definition: Vector2.cs:42
Matrix LinkToMeshMatrix
The matrix to transform from mesh space to local space of this bone.
Describes hiderarchical nodes in a flattened array.
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
Definition: DirectXTexP.h:170
Represents a color in the form of rgb.
Definition: Color3.cs:41
void AddCurve(string propertyName, AnimationCurve curve)
Adds a named curve.
float B
The blue component of the color.
Definition: Color3.cs:59
void AddColorNode(ParameterKey< ShaderMixinSource > key, string referenceName, IMaterialNode node)
Adds a tree in the model.
String aiStringToString(aiString str)
ModelNodeFlags Flags
The flags of this node.
Quaternion aiQuaternionToQuaternion(aiQuaterniont< float > quat)
Describes skinning for a Mesh, through a collection of MeshBoneDefinition.
float R
The red component of the color.
Definition: Color3.cs:47
Color aiColor4ToColor(aiColor4D color)
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
Represents a color in the form of rgba.
Definition: Color4.cs:42
An aggregation of AnimationCurve with their channel names.
Base implementation for ILogger.
Definition: Logger.cs:10
_In_ size_t count
Definition: DirectXTexP.h:174
ModelData Convert(String^inputFilename, String^outputFilename)
void Error(string message, Exception exception, CallerInfo callerInfo=null)
Logs the specified error message with an exception.
static void Add(ref Matrix left, ref Matrix right, out Matrix result)
Determines the sum of two matrices.
Definition: Matrix.cs:736
CompressedTimeSpan aiTimeToPdxTimeSpan(double time, double aiTickPerSecond)
Data type for SiliconStudio.Paradox.Graphics.IndexBufferBinding.
Represents a four dimensional mathematical vector.
Definition: Vector4.cs:42
SiliconStudio.Core.Mathematics.Color Color
Definition: ColorPicker.cs:14
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
EntityInfo ExtractEntity(String^inputFilename, String^outputFilename)
Operation
Enumeration of the different operations in the new Assimp's material stack.
Represents a 32-bit color (4 bytes) in the form of RGBA (in byte order: R, G, B, A).
Definition: Color.cs:16
Vector3 aiVector3ToVector3(aiVector3D vec)
Color3 aiColor3ToColor3(aiColor3D color)
Data type for SiliconStudio.Paradox.Effects.Mesh.
Definition: EngineData.cs:197
Data type for SiliconStudio.Paradox.Effects.MeshDraw.
Definition: EngineData.cs:165
TrackingDictionary< PropertyKey, EntityComponentData > Components
Definition: EntityData.cs:32
Untyped base class for animation curves.
Data type for SiliconStudio.Paradox.Effects.Model.
Definition: EngineData.cs:241
LightType aiLightTypeToPdxLightType(aiLightSourceType aiLightType, String^lightName, Logger^logger)
Describes a single transformation node, usually in a Model node hierarchy.
AnimationClip ConvertAnimation(String^inputFilename, String^outputFilename)
float G
The green component of the color.
Definition: Color3.cs:53
SiliconStudio.Core.Mathematics.Vector3 Vector3
Describes a bone cluster inside a Mesh.
Content of a GPU buffer (vertex buffer, index buffer, etc...).
Definition: BufferData.cs:10
Matrix aiMatrixToMatrix(aiMatrix4x4 mat)
void Verbose(string message, Exception exception, CallerInfo callerInfo=null)
Logs the specified verbose message with an exception.
void Warning(string message, Exception exception, CallerInfo callerInfo=null)
Logs the specified warning message with an exception.
A description of a single element for the input-assembler stage. This structure is related to Direct3...
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
Color4 aiColor3ToColor4(aiColor3D color)