|
|
|
|
@@ -30,6 +30,7 @@
|
|
|
|
|
#include "Engine/Tools/TextureTool/TextureTool.h"
|
|
|
|
|
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
|
|
|
|
#include "Engine/ContentImporters/CreateMaterial.h"
|
|
|
|
|
#include "Engine/ContentImporters/CreateMaterialInstance.h"
|
|
|
|
|
#include "Engine/ContentImporters/CreateCollisionData.h"
|
|
|
|
|
#include "Engine/Serialization/Serialization.h"
|
|
|
|
|
#include "Editor/Utilities/EditorUtilities.h"
|
|
|
|
|
@@ -498,11 +499,11 @@ bool ModelTool::ImportData(const String& path, ImportedModelData& data, Options&
|
|
|
|
|
if (ImportDataAssimp(importPath.Get(), data, options, errorMsg))
|
|
|
|
|
return true;
|
|
|
|
|
#elif USE_AUTODESK_FBX_SDK
|
|
|
|
|
if (ImportDataAutodeskFbxSdk(importPath.Get(), data, options, errorMsg))
|
|
|
|
|
return true;
|
|
|
|
|
if (ImportDataAutodeskFbxSdk(importPath.Get(), data, options, errorMsg))
|
|
|
|
|
return true;
|
|
|
|
|
#elif USE_OPEN_FBX
|
|
|
|
|
if (ImportDataOpenFBX(importPath.Get(), data, options, errorMsg))
|
|
|
|
|
return true;
|
|
|
|
|
if (ImportDataOpenFBX(importPath.Get(), data, options, errorMsg))
|
|
|
|
|
return true;
|
|
|
|
|
#else
|
|
|
|
|
LOG(Error, "Compiled without model importing backend.");
|
|
|
|
|
return true;
|
|
|
|
|
@@ -615,62 +616,62 @@ bool ModelTool::ImportData(const String& path, ImportedModelData& data, Options&
|
|
|
|
|
|
|
|
|
|
bool SortDepths(const Pair<int32, int32>& a, const Pair<int32, int32>& b)
|
|
|
|
|
{
|
|
|
|
|
return a.First < b.First;
|
|
|
|
|
return a.First < b.First;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CreateLinearListFromTree(Array<SkeletonNode>& nodes, Array<int32>& mapping)
|
|
|
|
|
{
|
|
|
|
|
// Customized breadth first tree algorithm (each node has no direct reference to the children so we build the cache for the nodes depth level)
|
|
|
|
|
const int32 count = nodes.Count();
|
|
|
|
|
Array<Pair<int32, int32>> depths(count); // Pair.First = Depth, Pair.Second = Node Index
|
|
|
|
|
depths.SetSize(count);
|
|
|
|
|
depths.Set(-1);
|
|
|
|
|
for (int32 i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
// Skip evaluated nodes
|
|
|
|
|
if (depths[i].First != -1)
|
|
|
|
|
continue;
|
|
|
|
|
const int32 count = nodes.Count();
|
|
|
|
|
Array<Pair<int32, int32>> depths(count); // Pair.First = Depth, Pair.Second = Node Index
|
|
|
|
|
depths.SetSize(count);
|
|
|
|
|
depths.Set(-1);
|
|
|
|
|
for (int32 i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
// Skip evaluated nodes
|
|
|
|
|
if (depths[i].First != -1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Find the first node with calculated depth and get the distance to it
|
|
|
|
|
int32 end = i;
|
|
|
|
|
int32 lastDepth;
|
|
|
|
|
int32 relativeDepth = 0;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
lastDepth = depths[end].First;
|
|
|
|
|
end = nodes[end].ParentIndex;
|
|
|
|
|
relativeDepth++;
|
|
|
|
|
} while (end != -1 && lastDepth == -1);
|
|
|
|
|
// Find the first node with calculated depth and get the distance to it
|
|
|
|
|
int32 end = i;
|
|
|
|
|
int32 lastDepth;
|
|
|
|
|
int32 relativeDepth = 0;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
lastDepth = depths[end].First;
|
|
|
|
|
end = nodes[end].ParentIndex;
|
|
|
|
|
relativeDepth++;
|
|
|
|
|
} while (end != -1 && lastDepth == -1);
|
|
|
|
|
|
|
|
|
|
// Set the depth (second item is the node index)
|
|
|
|
|
depths[i] = MakePair(lastDepth + relativeDepth, i);
|
|
|
|
|
}
|
|
|
|
|
for (int32 i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
// Strange divide by 2 but works
|
|
|
|
|
depths[i].First = depths[i].First >> 1;
|
|
|
|
|
}
|
|
|
|
|
// Set the depth (second item is the node index)
|
|
|
|
|
depths[i] = MakePair(lastDepth + relativeDepth, i);
|
|
|
|
|
}
|
|
|
|
|
for (int32 i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
// Strange divide by 2 but works
|
|
|
|
|
depths[i].First = depths[i].First >> 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Order nodes by depth O(n*log(n))
|
|
|
|
|
depths.Sort(SortDepths);
|
|
|
|
|
// Order nodes by depth O(n*log(n))
|
|
|
|
|
depths.Sort(SortDepths);
|
|
|
|
|
|
|
|
|
|
// Extract nodes mapping O(n^2)
|
|
|
|
|
mapping.EnsureCapacity(count, false);
|
|
|
|
|
mapping.SetSize(count);
|
|
|
|
|
for (int32 i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
int32 newIndex = -1;
|
|
|
|
|
for (int32 j = 0; j < count; j++)
|
|
|
|
|
{
|
|
|
|
|
if (depths[j].Second == i)
|
|
|
|
|
{
|
|
|
|
|
newIndex = j;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ASSERT(newIndex != -1);
|
|
|
|
|
mapping[i] = newIndex;
|
|
|
|
|
}
|
|
|
|
|
// Extract nodes mapping O(n^2)
|
|
|
|
|
mapping.EnsureCapacity(count, false);
|
|
|
|
|
mapping.SetSize(count);
|
|
|
|
|
for (int32 i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
int32 newIndex = -1;
|
|
|
|
|
for (int32 j = 0; j < count; j++)
|
|
|
|
|
{
|
|
|
|
|
if (depths[j].Second == i)
|
|
|
|
|
{
|
|
|
|
|
newIndex = j;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ASSERT(newIndex != -1);
|
|
|
|
|
mapping[i] = newIndex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
@@ -938,7 +939,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
|
|
|
|
case TextureEntry::TypeHint::Normals:
|
|
|
|
|
textureOptions.Type = TextureFormatType::NormalMap;
|
|
|
|
|
break;
|
|
|
|
|
default: ;
|
|
|
|
|
default:;
|
|
|
|
|
}
|
|
|
|
|
AssetsImportingManager::ImportIfEdited(texture.FilePath, assetPath, texture.AssetID, &textureOptions);
|
|
|
|
|
#endif
|
|
|
|
|
@@ -973,24 +974,47 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
|
|
|
|
importedFileNames.Add(filename);
|
|
|
|
|
#if COMPILE_WITH_ASSETS_IMPORTER
|
|
|
|
|
auto assetPath = autoImportOutput / filename + ASSET_FILES_EXTENSION_WITH_DOT;
|
|
|
|
|
CreateMaterial::Options materialOptions;
|
|
|
|
|
materialOptions.Diffuse.Color = material.Diffuse.Color;
|
|
|
|
|
if (material.Diffuse.TextureIndex != -1)
|
|
|
|
|
materialOptions.Diffuse.Texture = data.Textures[material.Diffuse.TextureIndex].AssetID;
|
|
|
|
|
materialOptions.Diffuse.HasAlphaMask = material.Diffuse.HasAlphaMask;
|
|
|
|
|
materialOptions.Emissive.Color = material.Emissive.Color;
|
|
|
|
|
if (material.Emissive.TextureIndex != -1)
|
|
|
|
|
materialOptions.Emissive.Texture = data.Textures[material.Emissive.TextureIndex].AssetID;
|
|
|
|
|
materialOptions.Opacity.Value = material.Opacity.Value;
|
|
|
|
|
if (material.Opacity.TextureIndex != -1)
|
|
|
|
|
materialOptions.Opacity.Texture = data.Textures[material.Opacity.TextureIndex].AssetID;
|
|
|
|
|
if (material.Normals.TextureIndex != -1)
|
|
|
|
|
materialOptions.Normals.Texture = data.Textures[material.Normals.TextureIndex].AssetID;
|
|
|
|
|
if (material.TwoSided || material.Diffuse.HasAlphaMask)
|
|
|
|
|
materialOptions.Info.CullMode = CullMode::TwoSided;
|
|
|
|
|
if (!Math::IsOne(material.Opacity.Value) || material.Opacity.TextureIndex != -1)
|
|
|
|
|
materialOptions.Info.BlendMode = MaterialBlendMode::Transparent;
|
|
|
|
|
AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialTag, assetPath, material.AssetID, &materialOptions);
|
|
|
|
|
if (options.ImportMaterialsAsInstances)
|
|
|
|
|
{
|
|
|
|
|
AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialInstanceTag, assetPath, material.AssetID);
|
|
|
|
|
MaterialInstance* materialInstance = (MaterialInstance*) LoadAsset(assetPath, MaterialInstance::TypeInitializer);
|
|
|
|
|
if (materialInstance->WaitForLoaded())
|
|
|
|
|
{
|
|
|
|
|
LOG(Error, "Failed to load material instance after creation. ({0})", assetPath);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MaterialBase* materialInstanceOf = (MaterialBase*) LoadAsset(options.InstanceToImportAs, MaterialBase::TypeInitializer);
|
|
|
|
|
if (materialInstanceOf->WaitForLoaded())
|
|
|
|
|
{
|
|
|
|
|
LOG(Error, "Failed to load material to create an instance of. ({0})", options.InstanceToImportAs);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
materialInstance->SetBaseMaterial(materialInstanceOf);
|
|
|
|
|
materialInstance->Save();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CreateMaterial::Options materialOptions;
|
|
|
|
|
materialOptions.Diffuse.Color = material.Diffuse.Color;
|
|
|
|
|
if (material.Diffuse.TextureIndex != -1)
|
|
|
|
|
materialOptions.Diffuse.Texture = data.Textures[material.Diffuse.TextureIndex].AssetID;
|
|
|
|
|
materialOptions.Diffuse.HasAlphaMask = material.Diffuse.HasAlphaMask;
|
|
|
|
|
materialOptions.Emissive.Color = material.Emissive.Color;
|
|
|
|
|
if (material.Emissive.TextureIndex != -1)
|
|
|
|
|
materialOptions.Emissive.Texture = data.Textures[material.Emissive.TextureIndex].AssetID;
|
|
|
|
|
materialOptions.Opacity.Value = material.Opacity.Value;
|
|
|
|
|
if (material.Opacity.TextureIndex != -1)
|
|
|
|
|
materialOptions.Opacity.Texture = data.Textures[material.Opacity.TextureIndex].AssetID;
|
|
|
|
|
if (material.Normals.TextureIndex != -1)
|
|
|
|
|
materialOptions.Normals.Texture = data.Textures[material.Normals.TextureIndex].AssetID;
|
|
|
|
|
if (material.TwoSided || material.Diffuse.HasAlphaMask)
|
|
|
|
|
materialOptions.Info.CullMode = CullMode::TwoSided;
|
|
|
|
|
if (!Math::IsOne(material.Opacity.Value) || material.Opacity.TextureIndex != -1)
|
|
|
|
|
materialOptions.Info.BlendMode = MaterialBlendMode::Transparent;
|
|
|
|
|
AssetsImportingManager::Create(AssetsImportingManager::CreateMaterialTag, assetPath, material.AssetID, &materialOptions);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1364,24 +1388,24 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
|
|
|
|
|
|
|
|
|
#if USE_SKELETON_NODES_SORTING
|
|
|
|
|
// Sort skeleton nodes and bones hierarchy (parents first)
|
|
|
|
|
// Then it can be used with a simple linear loop update
|
|
|
|
|
{
|
|
|
|
|
const int32 nodesCount = data.Skeleton.Nodes.Count();
|
|
|
|
|
const int32 bonesCount = data.Skeleton.Bones.Count();
|
|
|
|
|
Array<int32> mapping;
|
|
|
|
|
CreateLinearListFromTree(data.Skeleton.Nodes, mapping);
|
|
|
|
|
for (int32 i = 0; i < nodesCount; i++)
|
|
|
|
|
{
|
|
|
|
|
auto& node = data.Skeleton.Nodes[i];
|
|
|
|
|
node.ParentIndex = mapping[node.ParentIndex];
|
|
|
|
|
}
|
|
|
|
|
for (int32 i = 0; i < bonesCount; i++)
|
|
|
|
|
{
|
|
|
|
|
auto& bone = data.Skeleton.Bones[i];
|
|
|
|
|
bone.NodeIndex = mapping[bone.NodeIndex];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reorder_nodes_and_test_it_out!
|
|
|
|
|
// Then it can be used with a simple linear loop update
|
|
|
|
|
{
|
|
|
|
|
const int32 nodesCount = data.Skeleton.Nodes.Count();
|
|
|
|
|
const int32 bonesCount = data.Skeleton.Bones.Count();
|
|
|
|
|
Array<int32> mapping;
|
|
|
|
|
CreateLinearListFromTree(data.Skeleton.Nodes, mapping);
|
|
|
|
|
for (int32 i = 0; i < nodesCount; i++)
|
|
|
|
|
{
|
|
|
|
|
auto& node = data.Skeleton.Nodes[i];
|
|
|
|
|
node.ParentIndex = mapping[node.ParentIndex];
|
|
|
|
|
}
|
|
|
|
|
for (int32 i = 0; i < bonesCount; i++)
|
|
|
|
|
{
|
|
|
|
|
auto& bone = data.Skeleton.Bones[i];
|
|
|
|
|
bone.NodeIndex = mapping[bone.NodeIndex];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
reorder_nodes_and_test_it_out!
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else if (options.Type == ModelType::Animation)
|
|
|
|
|
|