From 34b313502f83fcfac833bfe1411e49a133bce4b1 Mon Sep 17 00:00:00 2001 From: noa7 Date: Mon, 13 Nov 2023 21:43:49 +0100 Subject: [PATCH 1/2] Base Commit to read blendshapes Base commit with following changes 1. New classes on at Stride.Rendering that are BlendShape and Shape 2. Update to model class to store the belndshape 3. Update Meshconveter.cpp at ProcessMesh sequence with new Method ProcessBlendShape --- .../Stride.Rendering/Rendering/BlendShape.cs | 50 +++++++ .../Stride.Rendering/Rendering/Model.cs | 12 +- .../Stride.Rendering/Rendering/Shape.cs | 29 ++++ .../Stride.Importer.FBX/MeshConverter.cpp | 136 +++++++++++++++++- .../Stride.Importer.FBX.vcxproj | 9 +- 5 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 sources/engine/Stride.Rendering/Rendering/BlendShape.cs create mode 100644 sources/engine/Stride.Rendering/Rendering/Shape.cs diff --git a/sources/engine/Stride.Rendering/Rendering/BlendShape.cs b/sources/engine/Stride.Rendering/Rendering/BlendShape.cs new file mode 100644 index 0000000000..651e399b2a --- /dev/null +++ b/sources/engine/Stride.Rendering/Rendering/BlendShape.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Stride.Rendering +{ + public class BlendShape + { + + + public string Name { get; private set; } + + public BlendShape(string name) + { + Name = name; + } + + public Dictionary Shapes { get; private set; } + + public void AddShape(Shape shape, float weight) + { + if (shape != null) + { (Shapes ??= new Dictionary())[Math.Clamp(0f, 1f, weight)] = shape; + } + } + + + + public int GetShapeCount() + { + return Shapes==null ? 0 : Shapes.Count; + } + + public Shape GetShapeAtIndex(int index) + { + return Shapes==null? null : Shapes[index]; + } + + public float[] GetShapeWeightsArray() + { + if( Shapes==null) return null; + float[] weights = new float[Shapes.Count]; + Shapes.Keys.CopyTo(weights, 0); + return weights; + } + + } +} diff --git a/sources/engine/Stride.Rendering/Rendering/Model.cs b/sources/engine/Stride.Rendering/Rendering/Model.cs index 9e5cc455d8..dfdffddb8e 100644 --- a/sources/engine/Stride.Rendering/Rendering/Model.cs +++ b/sources/engine/Stride.Rendering/Rendering/Model.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; - +using System.Runtime.CompilerServices; using Stride.Core; using Stride.Core.Annotations; using Stride.Core.Collections; @@ -14,7 +14,6 @@ using Stride.Core.Serialization.Contents; using Stride.Graphics; using Stride.Graphics.Data; - using Buffer = Stride.Graphics.Buffer; namespace Stride.Rendering @@ -146,6 +145,8 @@ public Model Instantiate() return result; } + + private void Children_CollectionChanged(object sender, TrackingCollectionChangedEventArgs e) { var child = (Model)e.Item; @@ -163,5 +164,12 @@ private void Children_CollectionChanged(object sender, TrackingCollectionChanged break; } } + + List blendShapes = null; + + public void AddBlendShape(BlendShape blendShape) + { + (blendShapes??=new()).Add(blendShape); + } } } diff --git a/sources/engine/Stride.Rendering/Rendering/Shape.cs b/sources/engine/Stride.Rendering/Rendering/Shape.cs new file mode 100644 index 0000000000..3befe4b74e --- /dev/null +++ b/sources/engine/Stride.Rendering/Rendering/Shape.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; + +namespace Stride.Rendering +{ + // Represet one instance of blend shape and control points + public class Shape + { + public string Name { get; private set; } + + //Index Of control points in shape + public int[] Indices { get; private set; } + + //Array of position of each of the control point in mesh with index that set in array Indices of same length + public Vector4[] Positions { get; private set; } + + + public Shape(string name, int[] indices, Vector4[] positions) + { + this.Name = name; + this.Indices = indices; + this.Positions = positions; + } + } +} diff --git a/sources/tools/Stride.Importer.FBX/MeshConverter.cpp b/sources/tools/Stride.Importer.FBX/MeshConverter.cpp index 97e2e5d5d0..3f58c08d3b 100644 --- a/sources/tools/Stride.Importer.FBX/MeshConverter.cpp +++ b/sources/tools/Stride.Importer.FBX/MeshConverter.cpp @@ -709,8 +709,70 @@ public ref class MeshConverter } modelData->Meshes->Add(meshData); } + + ProcessBlendShapes(pMesh); + } + +#pragma region BLENDSHAPES + void ProcessBlendShapes(FbxMesh* pMesh) { + int blendShapeDeformerCount = pMesh->GetDeformerCount(FbxDeformer::eBlendShape); + + for (int i = 0; i < blendShapeDeformerCount; ++i) { + FbxBlendShape* pBlendShape = static_cast(pMesh->GetDeformer(i, FbxDeformer::eBlendShape)); + + int blendShapeChannelCount = pBlendShape->GetBlendShapeChannelCount(); + + for (int j = 0; j < blendShapeChannelCount; ++j) { + FbxBlendShapeChannel* pBlendShapeChannel = pBlendShape->GetBlendShapeChannel(j); + String^ channelBlendShapeName = Marshal::PtrToStringAnsi(static_cast(const_cast(pBlendShapeChannel->GetName()))); + + + Stride::Rendering::BlendShape^ blendShape = gcnew BlendShape(channelBlendShapeName); + + int blenShapeChannelTargetCountr = pBlendShapeChannel->GetTargetShapeCount(); + + for (int k = 0; k < blenShapeChannelTargetCountr; ++k) + { + FbxShape* fbxShape=pBlendShapeChannel->GetTargetShape(k); + + + + String^ shapeName = Marshal::PtrToStringAnsi(static_cast(const_cast(fbxShape->GetName()))); + + + // array^ indices=gcnew array^ indices = gcnew cli::array(fbxShape->GetControlPointsCount()); + + cli::array^ controlPoints = gcnew cli::array(fbxShape->GetControlPointsCount()); + + for (int h = 0; h < fbxShape->GetControlPointsCount(); ++h) + { + indices[h]= fbxShape->GetControlPointIndices()[h]; + System::Numerics::Vector4^ controlPoint = gcnew System::Numerics::Vector4(fbxShape->GetControlPointAt(h)[1], fbxShape->GetControlPointAt(h)[2], fbxShape->GetControlPointAt(h)[3], fbxShape->GetControlPointAt(h)[4]); + + controlPoints[h] = *controlPoint; + } + + + Stride::Rendering::Shape^ shape = gcnew + + Stride::Rendering::Shape(shapeName, indices, controlPoints); + + double weight= pBlendShapeChannel->GetTargetShapeFullWeights()[k]; + + + + blendShape->AddShape(shape, weight); + + + } + + modelData->AddBlendShape(blendShape); + } + } } +#pragma endregion // return a boolean indicating whether the built material is transparent or not MaterialAsset^ ProcessMeshMaterialAsset(FbxSurfaceMaterial* lMaterial, std::map& uvElementMapping) { @@ -1037,7 +1099,9 @@ public ref class MeshConverter IComputeNode^ GenerateSurfaceTextureTree(FbxSurfaceMaterial* lMaterial, std::map& uvElementMapping, Dictionary^ textureMap, std::map& textureNameCount, char const* surfaceMaterial, char const* surfaceMaterialFactor, - Stride::Assets::Materials::MaterialAsset^ finalMaterial) + + + Stride::Assets::Materials::MaterialAsset^ finalMaterial) { auto compositionTrees = gcnew cli::array(2); @@ -1984,6 +2048,76 @@ public ref class MeshConverter return allNodes; } +#pragma region BLENDSHAPES + + + + void ProcessBlendShapeTarget(fbxsdk::FbxShape* targetShape) { + // Get the number of control points in the target shape + int controlPointCount = targetShape->GetControlPointsCount(); + + // Get a pointer to the array of control points + fbxsdk::FbxVector4* controlPoints = targetShape->GetControlPoints(); + + // Access individual control points and their coordinates + for (int i = 0; i < controlPointCount; ++i) { + fbxsdk::FbxVector4 controlPoint = controlPoints[i]; + double x = controlPoint[0]; + double y = controlPoint[1]; + double z = controlPoint[2]; + + + } + } + + void ProcessBlendShapeChannel(fbxsdk::FbxBlendShapeChannel* channel) { + // Process each blend shape target + int targetShapeCount = channel->GetTargetShapeCount(); + for (int k = 0; k < targetShapeCount; ++k) { + fbxsdk::FbxShape* targetShape = channel->GetTargetShape(k); + const char* targetShapeName = targetShape->GetName(); + + // Process control points of the blend shape target + ProcessBlendShapeTarget(targetShape); + } + } + + void ProcessBlendShape(fbxsdk::FbxBlendShape* blendShape) { + // Process each blend shape channel + int blendShapeChannelCount = blendShape->GetBlendShapeChannelCount(); + for (int j = 0; j < blendShapeChannelCount; ++j) { + fbxsdk::FbxBlendShapeChannel* channel = blendShape->GetBlendShapeChannel(j); + const char* channelName = channel->GetName(); + + // Process control points of the blend shape channel + ProcessBlendShapeChannel(channel); + } + } + + void ProcessNode(fbxsdk::FbxNode* pNode) { + // Check if the node has blend shapes + int blendShapeCount = pNode->GetGeometry()->GetDeformerCount(FbxDeformer::eBlendShape); + if (blendShapeCount > 0) { + for (int i = 0; i < blendShapeCount; ++i) { + fbxsdk::FbxBlendShape* blendShape = static_cast(pNode->GetGeometry()->GetDeformer(i, FbxDeformer::eBlendShape)); + + // Process control points of the blend shape + ProcessBlendShape(blendShape); + } + } + + // Recursively process child nodes + int childCount = pNode->GetChildCount(); + for (int i = 0; i < childCount; ++i) { + ProcessNode(pNode->GetChild(i)); + } + } + + +#pragma endregion + + + public: EntityInfo^ ExtractEntity(String^ inputFileName, bool extractTextureDependencies) { diff --git a/sources/tools/Stride.Importer.FBX/Stride.Importer.FBX.vcxproj b/sources/tools/Stride.Importer.FBX/Stride.Importer.FBX.vcxproj index f1b6046d26..f9a0810895 100644 --- a/sources/tools/Stride.Importer.FBX/Stride.Importer.FBX.vcxproj +++ b/sources/tools/Stride.Importer.FBX/Stride.Importer.FBX.vcxproj @@ -203,10 +203,6 @@ {c121a566-555e-42b9-9b0a-1696529a9088} False - - {ad4fdc24-b64d-4ed7-91aa-62c9eda12fa4} - False - {0e916ab7-5a6c-4820-8ab1-aa492fe66d68} False @@ -251,6 +247,9 @@ ..\..\..\deps\BulletPhysics\BulletSharp.NetStandard.dll + + ..\..\engine\Stride.Rendering\bin\Debug\net6.0\Stride.Rendering.dll + @@ -259,4 +258,4 @@ - + \ No newline at end of file From 2575e1353d8fc9552b5011cd95ddec21ff901493 Mon Sep 17 00:00:00 2001 From: noa7 Date: Tue, 5 Dec 2023 15:25:54 +0100 Subject: [PATCH 2/2] Shader pure application --- .../Compositing/GraphicsCompositorHelper.cs | 1 + .../BlendShapeRenderFeature.cs | 57 + .../Stride.Rendering/Rendering/BlendShape.cs | 54 + .../BlendShape/TransformationBlendShape.sdsl | 38 + .../TransformationBlendShape.sdsl.cs | 0 .../Materials/Shaders/MaterialKeys.cs | 4 + .../Rendering/MeshRenderFeature.cs | 1 - .../Stride.Rendering/Rendering/Model.cs | 12 +- .../Stride.Rendering/Rendering/Shape.cs | 31 + .../Rendering/SkinningRenderFeature.cs | 3 + .../Rendering/StrideEffectBase.sdfx | 10 + .../Rendering/StrideEffectBase.sdfx.cs | 11 +- .../Compiler/EffectCompilerCache.cs | 2 +- .../Stride.Importer.FBX/MeshConverter.cpp | 3873 +++++++++-------- .../Stride.Importer.FBX.vcxproj | 9 +- 15 files changed, 2231 insertions(+), 1875 deletions(-) create mode 100644 sources/engine/Stride.Rendering/BlendShapeRenderFeature.cs create mode 100644 sources/engine/Stride.Rendering/Rendering/BlendShape.cs create mode 100644 sources/engine/Stride.Rendering/Rendering/BlendShape/TransformationBlendShape.sdsl create mode 100644 sources/engine/Stride.Rendering/Rendering/BlendShape/TransformationBlendShape.sdsl.cs create mode 100644 sources/engine/Stride.Rendering/Rendering/Shape.cs diff --git a/sources/engine/Stride.Engine/Rendering/Compositing/GraphicsCompositorHelper.cs b/sources/engine/Stride.Engine/Rendering/Compositing/GraphicsCompositorHelper.cs index f30afbaa91..7223195bdc 100644 --- a/sources/engine/Stride.Engine/Rendering/Compositing/GraphicsCompositorHelper.cs +++ b/sources/engine/Stride.Engine/Rendering/Compositing/GraphicsCompositorHelper.cs @@ -130,6 +130,7 @@ public static GraphicsCompositor CreateDefault(bool enablePostEffects, string mo { new TransformRenderFeature(), new SkinningRenderFeature(), + new BlendShapeRenderFeature(), new MaterialRenderFeature(), new ShadowCasterRenderFeature(), forwardLighting, diff --git a/sources/engine/Stride.Rendering/BlendShapeRenderFeature.cs b/sources/engine/Stride.Rendering/BlendShapeRenderFeature.cs new file mode 100644 index 0000000000..3f18d59a5d --- /dev/null +++ b/sources/engine/Stride.Rendering/BlendShapeRenderFeature.cs @@ -0,0 +1,57 @@ +// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +using System; +using System.Runtime.CompilerServices; +using Stride.Core; +using Stride.Core.Diagnostics; +using Stride.Core.Mathematics; +using Stride.Core.Threading; +using Stride.Rendering.Materials; + +namespace Stride.Rendering +{ + /// + /// Computes and uploads skinning info. + /// + public class BlendShapeRenderFeature : SubRenderFeature + { + private StaticObjectPropertyKey renderEffectKey; + + + + /// + protected override void InitializeCore() + { + // renderEffectKey = ((RootEffectRenderFeature)RootRenderFeature).RenderEffectKey; + } + + /// + public override void PrepareEffectPermutations(RenderDrawContext context) + { + // var renderEffects = RootRenderFeature.RenderData.GetData(renderEffectKey); + int effectSlotCount = ((RootEffectRenderFeature)RootRenderFeature).EffectPermutationSlotCount; + + Dispatcher.ForEach(((RootEffectRenderFeature)RootRenderFeature).ObjectNodeReferences, objectNodeReference => + { + var objectNode = RootRenderFeature.GetObjectNode(objectNodeReference); + var renderMesh = (RenderMesh)objectNode.RenderObject; + var modelData = ((RenderMesh)objectNode.RenderObject).RenderModel?.Model; + var staticObjectNode = renderMesh.StaticObjectNode; + if (modelData.blendShapes != null) + { + } + }); + } + + /// + public override void Extract() + { + } + + /// + public override unsafe void Prepare(RenderDrawContext context) + { + + } + } +} diff --git a/sources/engine/Stride.Rendering/Rendering/BlendShape.cs b/sources/engine/Stride.Rendering/Rendering/BlendShape.cs new file mode 100644 index 0000000000..5c9131fda1 --- /dev/null +++ b/sources/engine/Stride.Rendering/Rendering/BlendShape.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Stride.Core; + +namespace Stride.Rendering +{ + // Each instace of class represents one blendshape in an FBXChannel in the mesh + [DataContract] + public class BlendShape + { + //Name as in source model file + public string Name { get; set; } + + public Dictionary Shapes { get; set; } + + public BlendShape() + { + // Name = name; + } + + + + public void AddShape(Shape shape, float weight) + { + if (shape != null) + { + (Shapes ??= new Dictionary())[Math.Clamp(0f, 1f, weight)] = shape; + } + } + + + public int GetShapeCount() + { + return Shapes == null ? 0 : Shapes.Count; + } + + public Shape GetShapeAtIndex(int index) + { + return Shapes == null ? null : Shapes[index]; + } + + public float[] GetShapeWeightsArray() + { + if (Shapes == null) return null; + float[] weights = new float[Shapes.Count]; + Shapes.Keys.CopyTo(weights, 0); + return weights; + } + + } +} diff --git a/sources/engine/Stride.Rendering/Rendering/BlendShape/TransformationBlendShape.sdsl b/sources/engine/Stride.Rendering/Rendering/BlendShape/TransformationBlendShape.sdsl new file mode 100644 index 0000000000..13efdc317d --- /dev/null +++ b/sources/engine/Stride.Rendering/Rendering/BlendShape/TransformationBlendShape.sdsl @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp) +// Distributed under the MIT license. See the LICENSE.md file in the project root for more information. +/// +/// Performs skinning on the position. +/// +/// +/// SkinningMaxBones: Macro - number of threads on the X axis. +/// STRIDE_GRAPHICS_API_OPENGLES: Macro - flag to activate on for opengl es 2.0 platforms (int4 attributes like blend indices are not supported). +/// +#ifndef SkinningMaxBones +# define SkinningMaxBones 4 +#endif + +// TODO: use STRIDE_GRAPHICS_API_OPENGLES20 because opengl es 3.0 supports int4 atributes. +#ifndef STRIDE_GRAPHICS_API_OPENGLES +# define STRIDE_GRAPHICS_API_OPENGLES false +#endif + +shader TransformationBlendShape : TransformationBase, PositionStream4, Transformation +{ + cbuffer PerDraw + { + stage float4 BlendWeight; + } + + stage stream float4 BlendShape1 : BLENDSHAPE1; + stage stream float4 BlendShape2 : BELNDSHAPE2; + + + + + + override stage void PreTransformPosition() + { + base.PreTransformPosition(); + // streams.PositionWS =float4(100,100,100,100); + } +}; diff --git a/sources/engine/Stride.Rendering/Rendering/BlendShape/TransformationBlendShape.sdsl.cs b/sources/engine/Stride.Rendering/Rendering/BlendShape/TransformationBlendShape.sdsl.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sources/engine/Stride.Rendering/Rendering/Materials/Shaders/MaterialKeys.cs b/sources/engine/Stride.Rendering/Rendering/Materials/Shaders/MaterialKeys.cs index 47be865a36..99faeb4c13 100644 --- a/sources/engine/Stride.Rendering/Rendering/Materials/Shaders/MaterialKeys.cs +++ b/sources/engine/Stride.Rendering/Rendering/Materials/Shaders/MaterialKeys.cs @@ -130,6 +130,10 @@ public class MaterialKeys public static readonly PermutationParameterKey UseDitheredShadows = ParameterKeys.NewPermutation(); + + public static readonly PermutationParameterKey HasBlendShape = ParameterKeys.NewPermutation(); + + static MaterialKeys() { //SpecularPowerScaled = ParameterKeys.NewDynamic(ParameterDynamicValue.New(SpecularPower, ScaleSpecularPower)); diff --git a/sources/engine/Stride.Rendering/Rendering/MeshRenderFeature.cs b/sources/engine/Stride.Rendering/Rendering/MeshRenderFeature.cs index 58d9ea471e..adb1d3862d 100644 --- a/sources/engine/Stride.Rendering/Rendering/MeshRenderFeature.cs +++ b/sources/engine/Stride.Rendering/Rendering/MeshRenderFeature.cs @@ -121,7 +121,6 @@ public override void Prepare(RenderDrawContext context) { base.Prepare(context); - // Prepare each sub render feature foreach (var renderFeature in RenderFeatures) { renderFeature.Prepare(context); diff --git a/sources/engine/Stride.Rendering/Rendering/Model.cs b/sources/engine/Stride.Rendering/Rendering/Model.cs index 9e5cc455d8..e1ef018359 100644 --- a/sources/engine/Stride.Rendering/Rendering/Model.cs +++ b/sources/engine/Stride.Rendering/Rendering/Model.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; - +using System.Runtime.CompilerServices; using Stride.Core; using Stride.Core.Annotations; using Stride.Core.Collections; @@ -14,7 +14,6 @@ using Stride.Core.Serialization.Contents; using Stride.Graphics; using Stride.Graphics.Data; - using Buffer = Stride.Graphics.Buffer; namespace Stride.Rendering @@ -146,6 +145,8 @@ public Model Instantiate() return result; } + + private void Children_CollectionChanged(object sender, TrackingCollectionChangedEventArgs e) { var child = (Model)e.Item; @@ -163,5 +164,12 @@ private void Children_CollectionChanged(object sender, TrackingCollectionChanged break; } } + + public List blendShapes { get; set; } + + public void AddBlendShape(BlendShape blendShape) + { + (blendShapes??=new()).Add(blendShape); + } } } diff --git a/sources/engine/Stride.Rendering/Rendering/Shape.cs b/sources/engine/Stride.Rendering/Rendering/Shape.cs new file mode 100644 index 0000000000..e9059af07e --- /dev/null +++ b/sources/engine/Stride.Rendering/Rendering/Shape.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Stride.Core; + +namespace Stride.Rendering +{ + [DataContract] + // Represet one instance of blend shape and control points + public class Shape + { + public string Name { get; set; } + + //Index Of control points in shape + public int[] Indices { get; set; } + + //Array of position of each of the control point in mesh with index that set in array Indices of same length + public Vector4[] Positions { get; set; } + + + public Shape() + { + // this.Name = name; + // this.Indices = indices; + // this.Positions = positions; + } + } +} diff --git a/sources/engine/Stride.Rendering/Rendering/SkinningRenderFeature.cs b/sources/engine/Stride.Rendering/Rendering/SkinningRenderFeature.cs index e05af8ed36..4ed54dc338 100644 --- a/sources/engine/Stride.Rendering/Rendering/SkinningRenderFeature.cs +++ b/sources/engine/Stride.Rendering/Rendering/SkinningRenderFeature.cs @@ -60,6 +60,9 @@ public override void PrepareEffectPermutations(RenderDrawContext context) { var objectNode = RootRenderFeature.GetObjectNode(objectNodeReference); var renderMesh = (RenderMesh)objectNode.RenderObject; + + + var staticObjectNode = renderMesh.StaticObjectNode; ref var skinningInfo = ref skinningInfos[staticObjectNode]; diff --git a/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx b/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx index e706737b01..fd07eb52c1 100644 --- a/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx +++ b/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx @@ -135,6 +135,16 @@ namespace Stride.Rendering } } + + + + if (MaterialKeys.HasBlendShape) + { + mixin TransformationBlendShape; + } + + + // -------------------------------------------- // Mix material tessellation for Domain effect //--------------------------------------------- diff --git a/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx.cs b/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx.cs index f36c51b60d..0bfb714af8 100644 --- a/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx.cs +++ b/sources/engine/Stride.Rendering/Rendering/StrideEffectBase.sdfx.cs @@ -1,4 +1,4 @@ -// +// // Do not edit this file yourself! // // This code was generated by Stride Shader Mixin Code Generator. @@ -127,6 +127,15 @@ public void Generate(ShaderMixinSource mixin, ShaderMixinContext context) } } } + + + if (context.GetParam(MaterialKeys.HasBlendShape)) + { + context.Mixin(mixin, "TransformationBlendShape"); + } + + + if (extensionTessellationShader != null) { context.Mixin(mixin, (extensionTessellationShader)); diff --git a/sources/engine/Stride.Shaders/Compiler/EffectCompilerCache.cs b/sources/engine/Stride.Shaders/Compiler/EffectCompilerCache.cs index d1ac942159..0a141325d0 100644 --- a/sources/engine/Stride.Shaders/Compiler/EffectCompilerCache.cs +++ b/sources/engine/Stride.Shaders/Compiler/EffectCompilerCache.cs @@ -93,7 +93,7 @@ public override TaskOrResult Compile(ShaderMixinSo // ------------------------------------------------------------------------------------------------------------ // 2) Try to load from database cache // ------------------------------------------------------------------------------------------------------------ - if (bytecode.Key == null && database.ObjectDatabase.Exists(mixinObjectId)) + if (bytecode.Key == null && database.ObjectDatabase.Exists(mixinObjectId) && false) { using (var stream = database.ObjectDatabase.OpenStream(mixinObjectId)) { diff --git a/sources/tools/Stride.Importer.FBX/MeshConverter.cpp b/sources/tools/Stride.Importer.FBX/MeshConverter.cpp index 02905f303e..5a6e49356e 100644 --- a/sources/tools/Stride.Importer.FBX/MeshConverter.cpp +++ b/sources/tools/Stride.Importer.FBX/MeshConverter.cpp @@ -28,2099 +28,2242 @@ using namespace Stride::Shaders; using namespace Stride::Importer::Common; -namespace Stride { namespace Importer { namespace FBX { +namespace Stride { + namespace Importer { + namespace FBX { -static const char* MappingModeName[] = { "None", "ByControlPoint", "ByPolygonVertex", "ByPolygon", "ByEdge", "AllSame" }; -static const char* MappingModeSuggestion[] = { "", "", "", "", " Try using ByPolygon mapping instead.", "" }; + static const char* MappingModeName[] = { "None", "ByControlPoint", "ByPolygonVertex", "ByPolygon", "ByEdge", "AllSame" }; + static const char* MappingModeSuggestion[] = { "", "", "", "", " Try using ByPolygon mapping instead.", "" }; -public ref class MaterialInstantiation -{ -public: - MaterialInstantiation() - { - } - - FbxSurfaceMaterial* SourceMaterial; - MaterialAsset^ Material; - String^ MaterialName; -}; - - -public ref class MeshConverter -{ -public: - property bool AllowUnsignedBlendIndices; - - Logger^ logger; - -internal: - FbxManager* lSdkManager; - FbxImporter* lImporter; - FbxScene* scene; - - String^ inputFilename; - String^ vfsOutputFilename; - String^ inputPath; - - Model^ modelData; - - SceneMapping^ sceneMapping; - - static array^ currentBuffer; - - static bool WeightGreater(const std::pair& elem1, const std::pair& elem2) - { - return elem1.second > elem2.second; - } - - bool IsGroupMappingModeByEdge(FbxLayerElement* layerElement) - { - return layerElement->GetMappingMode() == FbxLayerElement::eByEdge; - } - - template - int GetGroupIndexForLayerElementTemplate(FbxLayerElementTemplate* layerElement, int controlPointIndex, int vertexIndex, int edgeIndex, int polygonIndex, String^ meshName, bool& firstTimeError) - { - int groupIndex = 0; - if (layerElement->GetMappingMode() == FbxLayerElement::eByControlPoint) - { - groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) - ? layerElement->GetIndexArray().GetAt(controlPointIndex) - : controlPointIndex; - } - else if (layerElement->GetMappingMode() == FbxLayerElement::eByPolygonVertex) - { - groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) - ? layerElement->GetIndexArray().GetAt(vertexIndex) - : vertexIndex; - } - else if (layerElement->GetMappingMode() == FbxLayerElement::eByPolygon) - { - groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) - ? layerElement->GetIndexArray().GetAt(polygonIndex) - : polygonIndex; - } - else if (layerElement->GetMappingMode() == FbxLayerElement::eByEdge) - { - groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) - ? layerElement->GetIndexArray().GetAt(edgeIndex) - : edgeIndex; - } - else if (layerElement->GetMappingMode() == FbxLayerElement::eAllSame) - { - groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) - ? layerElement->GetIndexArray().GetAt(0) - : 0; - } - else if (firstTimeError) - { - firstTimeError = false; - int mappingMode = layerElement->GetMappingMode(); - if (mappingMode > (int)FbxLayerElement::eAllSame) - mappingMode = (int)FbxLayerElement::eAllSame; - const char* layerName = layerElement->GetName(); - logger->Warning(String::Format("'{0}' mapping mode for layer '{1}' in mesh '{2}' is not supported by the FBX importer.{3}", - gcnew String(MappingModeName[mappingMode]), - strlen(layerName) > 0 ? gcnew String(layerName) : gcnew String("Unknown"), - meshName, - gcnew String(MappingModeSuggestion[mappingMode])), (CallerInfo^)nullptr); - } - - return groupIndex; - } + public ref class MaterialInstantiation + { + public: + MaterialInstantiation() + { + } + FbxSurfaceMaterial* SourceMaterial; + MaterialAsset^ Material; + String^ MaterialName; + }; -public: - MeshConverter(Logger^ Logger) - { - if(Logger == nullptr) - Logger = Core::Diagnostics::GlobalLogger::GetLogger("Importer FBX"); - logger = Logger; - lSdkManager = NULL; - lImporter = NULL; - } + public ref class MeshConverter + { + public: + property bool AllowUnsignedBlendIndices; - void Destroy() - { - //Marshal::FreeHGlobal((IntPtr)lFilename); - currentBuffer = nullptr; - - // The file has been imported; we can get rid of the importer. - lImporter->Destroy(); - - // Destroy the sdk manager and all other objects it was handling. - lSdkManager->Destroy(); - - // ----------------------------------------------------- - // TODO: Workaround with FBX SDK not being multithreaded. - // We protect the whole usage of this class with a monitor - // - // Lock the whole class between Initialize/Destroy - // ----------------------------------------------------- - System::Threading::Monitor::Exit( globalLock ); - // ----------------------------------------------------- - } + Logger^ logger; - void ProcessMesh(FbxMesh* pMesh, std::map meshNames, std::map materials) - { - // Checks normals availability. - bool has_normals = pMesh->GetElementNormalCount() > 0 && pMesh->GetElementNormal(0)->GetMappingMode() != FbxLayerElement::eNone; - bool needEdgeIndexing = false; + internal: + FbxManager* lSdkManager; + FbxImporter* lImporter; + FbxScene* scene; - // Regenerate normals if necessary - if (!has_normals) - { - pMesh->GenerateNormals(true, false, false); - } + String^ inputFilename; + String^ vfsOutputFilename; + String^ inputPath; - FbxVector4* controlPoints = pMesh->GetControlPoints(); - FbxGeometryElementNormal* normalElement = pMesh->GetElementNormal(); - FbxGeometryElementTangent* tangentElement = pMesh->GetElementTangent(); - FbxGeometryElementBinormal* binormalElement = pMesh->GetElementBinormal(); - FbxGeometryElementSmoothing* smoothingElement = pMesh->GetElementSmoothing(); - - // UV set name mapping - std::map uvElementMapping; - std::vector uvElements; - - for (int i = 0; i < pMesh->GetElementUVCount(); ++i) - { - auto uvElement = pMesh->GetElementUV(i); - uvElements.push_back(uvElement); - needEdgeIndexing |= IsGroupMappingModeByEdge(uvElement); - } + Model^ modelData; - auto meshName = gcnew String(meshNames[pMesh].c_str()); + SceneMapping^ sceneMapping; - bool hasSkinningPosition = false; - bool hasSkinningNormal = false; - int totalClusterCount = 0; - std::vector > > controlPointWeights; + static array^ currentBuffer; - List^ bones = nullptr; + static bool WeightGreater(const std::pair& elem1, const std::pair& elem2) + { + return elem1.second > elem2.second; + } - // Dump skinning information - int skinDeformerCount = pMesh->GetDeformerCount(FbxDeformer::eSkin); - if (skinDeformerCount > 0) - { - bones = gcnew List(); - for (int deformerIndex = 0; deformerIndex < skinDeformerCount; deformerIndex++) - { - FbxSkin* skin = FbxCast(pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); - controlPointWeights.resize(pMesh->GetControlPointsCount()); + bool IsGroupMappingModeByEdge(FbxLayerElement* layerElement) + { + return layerElement->GetMappingMode() == FbxLayerElement::eByEdge; + } - totalClusterCount = skin->GetClusterCount(); - for (int clusterIndex = 0 ; clusterIndex < totalClusterCount; ++clusterIndex) + template + int GetGroupIndexForLayerElementTemplate(FbxLayerElementTemplate* layerElement, int controlPointIndex, int vertexIndex, int edgeIndex, int polygonIndex, String^ meshName, bool& firstTimeError) { - FbxCluster* cluster = skin->GetCluster(clusterIndex); - int indexCount = cluster->GetControlPointIndicesCount(); - if (indexCount == 0) + int groupIndex = 0; + if (layerElement->GetMappingMode() == FbxLayerElement::eByControlPoint) { - continue; + groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) + ? layerElement->GetIndexArray().GetAt(controlPointIndex) + : controlPointIndex; } - - FbxNode* link = cluster->GetLink(); - const char* boneName = link->GetName(); - int *indices = cluster->GetControlPointIndices(); - double *weights = cluster->GetControlPointWeights(); - - FbxAMatrix transformMatrix; - FbxAMatrix transformLinkMatrix; - - cluster->GetTransformMatrix(transformMatrix); - cluster->GetTransformLinkMatrix(transformLinkMatrix); - auto globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix; - - MeshBoneDefinition bone; - int boneIndex = bones->Count; - bone.NodeIndex = sceneMapping->FindNodeIndex(link); - bone.LinkToMeshMatrix = sceneMapping->ConvertMatrixFromFbx(globalBindposeInverseMatrix); - - // Check if the bone was not already there, else update it - // TODO: this is not the correct way to handle multiple deformers (additive...etc.) - bool isBoneAlreadyFound = false; - for (int i = 0; i < bones->Count; i++) + else if (layerElement->GetMappingMode() == FbxLayerElement::eByPolygonVertex) { - if (bones[i].NodeIndex == bone.NodeIndex) - { - bones[i] = bone; - boneIndex = i; - isBoneAlreadyFound = true; - break; - } + groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) + ? layerElement->GetIndexArray().GetAt(vertexIndex) + : vertexIndex; } - - // Gather skin indices and weights - for (int j = 0 ; j < indexCount; j++) + else if (layerElement->GetMappingMode() == FbxLayerElement::eByPolygon) { - int controlPointIndex = indices[j]; - controlPointWeights[controlPointIndex].push_back(std::pair((short)boneIndex, (float)weights[j])); + groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) + ? layerElement->GetIndexArray().GetAt(polygonIndex) + : polygonIndex; } - - // Find an existing bone and update it - // TODO: this is probably not correct to do this (we should handle cluster additive...etc. more correctly here) - if (!isBoneAlreadyFound) + else if (layerElement->GetMappingMode() == FbxLayerElement::eByEdge) { - bones->Add(bone); + groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) + ? layerElement->GetIndexArray().GetAt(edgeIndex) + : edgeIndex; } - } - - // look for position/normals skinning - if (pMesh->GetControlPointsCount() > 0) - { - hasSkinningPosition = true; - hasSkinningNormal = (pMesh->GetElementNormal() != NULL); - } - - for (int i = 0 ; i < pMesh->GetControlPointsCount(); i++) - { - std::sort(controlPointWeights[i].begin(), controlPointWeights[i].end(), WeightGreater); - controlPointWeights[i].resize(4, std::pair(0, 0.0f)); - float totalWeight = 0.0f; - for (int j = 0; j < 4; ++j) - totalWeight += controlPointWeights[i][j].second; - if (totalWeight == 0.0f) + else if (layerElement->GetMappingMode() == FbxLayerElement::eAllSame) { - for (int j = 0; j < 4; ++j) - controlPointWeights[i][j].second = (j == 0) ? 1.0f : 0.0f; + groupIndex = (layerElement->GetReferenceMode() == FbxLayerElement::eIndexToDirect) + ? layerElement->GetIndexArray().GetAt(0) + : 0; } - else + else if (firstTimeError) { - totalWeight = 1.0f / totalWeight; - for (int j = 0; j < 4; ++j) - controlPointWeights[i][j].second *= totalWeight; + firstTimeError = false; + int mappingMode = layerElement->GetMappingMode(); + if (mappingMode > (int)FbxLayerElement::eAllSame) + mappingMode = (int)FbxLayerElement::eAllSame; + const char* layerName = layerElement->GetName(); + logger->Warning(String::Format("'{0}' mapping mode for layer '{1}' in mesh '{2}' is not supported by the FBX importer.{3}", + gcnew String(MappingModeName[mappingMode]), + strlen(layerName) > 0 ? gcnew String(layerName) : gcnew String("Unknown"), + meshName, + gcnew String(MappingModeSuggestion[mappingMode])), (CallerInfo^)nullptr); } - } - } - } - - // ********************************************************************************* - // Build the vertex declaration - // ********************************************************************************* - auto vertexElements = gcnew List(); - - // POSITION - int vertexStride = 0; - int positionOffset = vertexStride; - vertexElements->Add(VertexElement::Position(0, vertexStride)); - vertexStride += 12; - - // NORMAL - int normalOffset = vertexStride; - if (normalElement != NULL) - { - vertexElements->Add(VertexElement::Normal(0, vertexStride)); - vertexStride += 12; - - needEdgeIndexing |= IsGroupMappingModeByEdge(normalElement); - } - - int tangentOffset = vertexStride; - if (tangentElement != NULL) - { - vertexElements->Add(VertexElement::Tangent(0, vertexStride)); - vertexStride += 16; - needEdgeIndexing |= IsGroupMappingModeByEdge(tangentElement); - } + return groupIndex; + } - // TEXCOORD - std::vector uvOffsets; - for (int i = 0; i < (int)uvElements.size(); ++i) - { - uvOffsets.push_back(vertexStride); - vertexElements->Add(VertexElement::TextureCoordinate(i, vertexStride)); - vertexStride += 8; - uvElementMapping[pMesh->GetElementUV(i)->GetName()] = i; - } - // BLENDINDICES - int blendIndicesOffset = vertexStride; - bool controlPointIndices16 = (AllowUnsignedBlendIndices && totalClusterCount > 256) || (!AllowUnsignedBlendIndices && totalClusterCount > 128); - if (!controlPointWeights.empty()) - { - if (controlPointIndices16) - { - if (AllowUnsignedBlendIndices) - { - vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R16G16B16A16_UInt, vertexStride)); - vertexStride += sizeof(unsigned short) * 4; - } - else - { - vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R16G16B16A16_SInt, vertexStride)); - vertexStride += sizeof(short) * 4; - } - } - else - { - if (AllowUnsignedBlendIndices) + public: + MeshConverter(Logger^ Logger) { - vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R8G8B8A8_UInt, vertexStride)); - vertexStride += sizeof(unsigned char) * 4; + if (Logger == nullptr) + Logger = Core::Diagnostics::GlobalLogger::GetLogger("Importer FBX"); + + logger = Logger; + lSdkManager = NULL; + lImporter = NULL; } - else + + void Destroy() { - vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R8G8B8A8_SInt, vertexStride)); - vertexStride += sizeof(char) * 4; + //Marshal::FreeHGlobal((IntPtr)lFilename); + currentBuffer = nullptr; + + // The file has been imported; we can get rid of the importer. + lImporter->Destroy(); + + // Destroy the sdk manager and all other objects it was handling. + lSdkManager->Destroy(); + + // ----------------------------------------------------- + // TODO: Workaround with FBX SDK not being multithreaded. + // We protect the whole usage of this class with a monitor + // + // Lock the whole class between Initialize/Destroy + // ----------------------------------------------------- + System::Threading::Monitor::Exit(globalLock); + // ----------------------------------------------------- } - } - } - - // BLENDWEIGHT - int blendWeightOffset = vertexStride; - if (!controlPointWeights.empty()) - { - vertexElements->Add(VertexElement("BLENDWEIGHT", 0, PixelFormat::R32G32B32A32_Float, vertexStride)); - vertexStride += sizeof(float) * 4; - } - - // COLOR - auto elementVertexColorCount = pMesh->GetElementVertexColorCount(); - std::vector vertexColorElements; - int colorOffset = vertexStride; - for (int i = 0; i < elementVertexColorCount; i++) - { - auto vertexColorElement = pMesh->GetElementVertexColor(i); - vertexColorElements.push_back(vertexColorElement); - vertexElements->Add(VertexElement::Color(i, vertexStride)); - vertexStride += sizeof(Color); - needEdgeIndexing |= IsGroupMappingModeByEdge(vertexColorElement); - } - - // USERDATA - // TODO: USERData how to handle then? - //auto userDataCount = pMesh->GetElementUserDataCount(); - //for (int i = 0; i < userDataCount; i++) - //{ - // auto userData = pMesh->GetElementUserData(i); - // auto dataType = userData->GetDataName(0); - // Console::WriteLine("DataName {0}", gcnew String(dataType)); - //} - - // Add the smoothing group information at the end of the vertex declaration - // ************************************************************************* - // WARNING - DONT PUT ANY VertexElement after SMOOTHINGGROUP - // ************************************************************************* - // Iit is important that to be the LAST ELEMENT of the declaration because it is dropped later in the process by partial memcopys - // SMOOTHINGGROUP - int smoothingOffset = vertexStride; - if (smoothingElement != NULL) - { - vertexElements->Add(VertexElement("SMOOTHINGGROUP", 0, PixelFormat::R32_UInt, vertexStride)); - vertexStride += sizeof(int); - - needEdgeIndexing |= IsGroupMappingModeByEdge(smoothingElement); - } - - int polygonCount = pMesh->GetPolygonCount(); - - FbxGeometryElement::EMappingMode materialMappingMode = FbxGeometryElement::eNone; - FbxLayerElementArrayTemplate* materialIndices = NULL; - - if (pMesh->GetElementMaterial()) - { - materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode(); - materialIndices = &pMesh->GetElementMaterial()->GetIndexArray(); - } - - auto buildMeshes = gcnew List(); - - // Count polygon per materials - for (int i = 0; i < polygonCount; i++) - { - int materialIndex = 0; - if (materialMappingMode == FbxGeometryElement::eByPolygon) - { - materialIndex = materialIndices->GetAt(i); - } - - // Equivalent to std::vector::resize() - while (materialIndex >= buildMeshes->Count) - { - buildMeshes->Add(nullptr); - } - - if (buildMeshes[materialIndex] == nullptr) - buildMeshes[materialIndex] = gcnew BuildMesh(); - - int polygonSize = pMesh->GetPolygonSize(i) - 2; - if (polygonSize > 0) - buildMeshes[materialIndex]->polygonCount += polygonSize; - } - - // Create arrays - for each(BuildMesh^ buildMesh in buildMeshes) - { - if (buildMesh == nullptr) - continue; - - buildMesh->buffer = gcnew array(vertexStride * buildMesh->polygonCount * 3); - } - bool layerIndexFirstTimeError = true; + void ProcessMesh(FbxMesh* pMesh, std::map meshNames, std::map materials) + { + // Checks normals availability. + bool has_normals = pMesh->GetElementNormalCount() > 0 && pMesh->GetElementNormal(0)->GetMappingMode() != FbxLayerElement::eNone; + bool needEdgeIndexing = false; - if (needEdgeIndexing) - pMesh->BeginGetMeshEdgeIndexForPolygon(); + // Regenerate normals if necessary + if (!has_normals) + { + pMesh->GenerateNormals(true, false, false); + } - // Build polygons - int polygonVertexStartIndex = 0; - for (int i = 0; i < polygonCount; i++) - { - int materialIndex = 0; - if (materialMappingMode == FbxGeometryElement::eByPolygon) - { - materialIndex = materialIndices->GetAt(i); - } + FbxVector4* controlPoints = pMesh->GetControlPoints(); + FbxGeometryElementNormal* normalElement = pMesh->GetElementNormal(); + FbxGeometryElementTangent* tangentElement = pMesh->GetElementTangent(); + FbxGeometryElementBinormal* binormalElement = pMesh->GetElementBinormal(); + FbxGeometryElementSmoothing* smoothingElement = pMesh->GetElementSmoothing(); - auto buildMesh = buildMeshes[materialIndex]; - auto buffer = buildMesh->buffer; + // UV set name mapping + std::map uvElementMapping; + std::vector uvElements; - int polygonSize = pMesh->GetPolygonSize(i); + for (int i = 0; i < pMesh->GetElementUVCount(); ++i) + { + auto uvElement = pMesh->GetElementUV(i); + uvElements.push_back(uvElement); + needEdgeIndexing |= IsGroupMappingModeByEdge(uvElement); + } - for (int polygonFanIndex = 2; polygonFanIndex < polygonSize; ++polygonFanIndex) - { - pin_ptr vbPointer = &buffer[buildMesh->bufferOffset]; - buildMesh->bufferOffset += vertexStride * 3; + auto meshName = gcnew String(meshNames[pMesh].c_str()); - int vertexInPolygon[3] = { 0, polygonFanIndex, polygonFanIndex - 1}; - int edgesInPolygon[3]; + bool hasSkinningPosition = false; + bool hasSkinningNormal = false; + int totalClusterCount = 0; + std::vector > > controlPointWeights; - if (needEdgeIndexing) - { - // Default case for polygon of size 3 - // Since our polygon order is 0,2,1, edge order is 2 (edge from 0 to 2),1 (edge from 2 to 1),0 (edge from 1 to 0) - // Note: all that code computing edge should change if vertexInPolygon changes - edgesInPolygon[0] = polygonFanIndex; - edgesInPolygon[1] = polygonFanIndex - 1; - edgesInPolygon[2] = 0; + List^ bones = nullptr; - if (polygonSize > 3) + // Dump skinning information + int skinDeformerCount = pMesh->GetDeformerCount(FbxDeformer::eSkin); + if (skinDeformerCount > 0) { - // Since we create non-existing edges inside the fan, we might have to use another edge in those cases - // If edge doesn't exist, we have to use edge from (polygonFanIndex-1) to polygonFanIndex (only one that always exists) + bones = gcnew List(); + for (int deformerIndex = 0; deformerIndex < skinDeformerCount; deformerIndex++) + { + FbxSkin* skin = FbxCast(pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); + controlPointWeights.resize(pMesh->GetControlPointsCount()); - // Let's say polygon is 0,4,3,2,1 + totalClusterCount = skin->GetClusterCount(); + for (int clusterIndex = 0; clusterIndex < totalClusterCount; ++clusterIndex) + { + FbxCluster* cluster = skin->GetCluster(clusterIndex); + int indexCount = cluster->GetControlPointIndicesCount(); + if (indexCount == 0) + { + continue; + } + + FbxNode* link = cluster->GetLink(); + const char* boneName = link->GetName(); + int* indices = cluster->GetControlPointIndices(); + double* weights = cluster->GetControlPointWeights(); + + FbxAMatrix transformMatrix; + FbxAMatrix transformLinkMatrix; + + cluster->GetTransformMatrix(transformMatrix); + cluster->GetTransformLinkMatrix(transformLinkMatrix); + auto globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix; + + MeshBoneDefinition bone; + int boneIndex = bones->Count; + bone.NodeIndex = sceneMapping->FindNodeIndex(link); + bone.LinkToMeshMatrix = sceneMapping->ConvertMatrixFromFbx(globalBindposeInverseMatrix); + + // Check if the bone was not already there, else update it + // TODO: this is not the correct way to handle multiple deformers (additive...etc.) + bool isBoneAlreadyFound = false; + for (int i = 0; i < bones->Count; i++) + { + if (bones[i].NodeIndex == bone.NodeIndex) + { + bones[i] = bone; + boneIndex = i; + isBoneAlreadyFound = true; + break; + } + } + + // Gather skin indices and weights + for (int j = 0; j < indexCount; j++) + { + int controlPointIndex = indices[j]; + controlPointWeights[controlPointIndex].push_back(std::pair((short)boneIndex, (float)weights[j])); + } + + // Find an existing bone and update it + // TODO: this is probably not correct to do this (we should handle cluster additive...etc. more correctly here) + if (!isBoneAlreadyFound) + { + bones->Add(bone); + } + } - // First polygons (except last): 0,2,1 (edge doesn't exist, use the one from 2 to 1 so edge 1) - // Last polygon : 0,4,3 (edge exists:4, from 0 to 4) - if (polygonFanIndex != polygonSize - 1) - edgesInPolygon[0] = polygonFanIndex - 1; + // look for position/normals skinning + if (pMesh->GetControlPointsCount() > 0) + { + hasSkinningPosition = true; + hasSkinningNormal = (pMesh->GetElementNormal() != NULL); + } - // First polygon: 0,2,1 (edge exists:0, from 1 to 0) - // Last polygons: 0,4,3 (edge doesn't exist, use the one from 4 to 3 so edge 3) - if (polygonFanIndex != 2) - edgesInPolygon[2] = polygonFanIndex - 1; + for (int i = 0; i < pMesh->GetControlPointsCount(); i++) + { + std::sort(controlPointWeights[i].begin(), controlPointWeights[i].end(), WeightGreater); + controlPointWeights[i].resize(4, std::pair(0, 0.0f)); + float totalWeight = 0.0f; + for (int j = 0; j < 4; ++j) + totalWeight += controlPointWeights[i][j].second; + if (totalWeight == 0.0f) + { + for (int j = 0; j < 4; ++j) + controlPointWeights[i][j].second = (j == 0) ? 1.0f : 0.0f; + } + else + { + totalWeight = 1.0f / totalWeight; + for (int j = 0; j < 4; ++j) + controlPointWeights[i][j].second *= totalWeight; + } + } + } } - } - - //if (polygonSwap) - //{ - // int temp = vertexInPolygon[1]; - // vertexInPolygon[1] = vertexInPolygon[2]; - // vertexInPolygon[2] = temp; - //} - int controlPointIndices[3] = { pMesh->GetPolygonVertex(i, vertexInPolygon[0]), pMesh->GetPolygonVertex(i, vertexInPolygon[1]), pMesh->GetPolygonVertex(i, vertexInPolygon[2]) }; - for (int polygonFanVertex = 0; polygonFanVertex < 3; ++polygonFanVertex) - { - int j = vertexInPolygon[polygonFanVertex]; - int vertexIndex = polygonVertexStartIndex + j; - int jNext = vertexInPolygon[(polygonFanVertex + 1) % 3]; - int vertexIndexNext = polygonVertexStartIndex + jNext; - int controlPointIndex = controlPointIndices[polygonFanVertex]; - int edgeIndex = needEdgeIndexing ? pMesh->GetMeshEdgeIndexForPolygon(i, edgesInPolygon[polygonFanVertex]) : 0; + // ********************************************************************************* + // Build the vertex declaration + // ********************************************************************************* + auto vertexElements = gcnew List(); // POSITION - auto controlPoint = sceneMapping->ConvertPointFromFbx(controlPoints[controlPointIndex]); - *(Vector3*)(vbPointer + positionOffset) = controlPoint; + int vertexStride = 0; + int positionOffset = vertexStride; + vertexElements->Add(VertexElement::Position(0, vertexStride)); + vertexStride += 12; // NORMAL - Vector3 normal = Vector3(1, 0, 0); + int normalOffset = vertexStride; if (normalElement != NULL) { - int normalIndex = GetGroupIndexForLayerElementTemplate(normalElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); - auto src_normal = normalElement->GetDirectArray().GetAt(normalIndex); - auto normalPointer = ((Vector3*)(vbPointer + normalOffset)); - normal = sceneMapping->ConvertNormalFromFbx(src_normal); - if (isnan(normal.X) || isnan(normal.Y) || isnan(normal.Z)) - normal = Vector3(1, 0, 0); - normal = Vector3::Normalize(normal); - *normalPointer = normal; + vertexElements->Add(VertexElement::Normal(0, vertexStride)); + vertexStride += 12; + + needEdgeIndexing |= IsGroupMappingModeByEdge(normalElement); } - // UV - for (int uvGroupIndex = 0; uvGroupIndex < (int)uvElements.size(); ++uvGroupIndex) + int tangentOffset = vertexStride; + if (tangentElement != NULL) { - auto uvElement = uvElements[uvGroupIndex]; - int uvIndex = GetGroupIndexForLayerElementTemplate(uvElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); - auto uv = uvElement->GetDirectArray().GetAt(uvIndex); + vertexElements->Add(VertexElement::Tangent(0, vertexStride)); + vertexStride += 16; - ((float*)(vbPointer + uvOffsets[uvGroupIndex]))[0] = (float)uv[0]; - ((float*)(vbPointer + uvOffsets[uvGroupIndex]))[1] = 1.0f - (float)uv[1]; + needEdgeIndexing |= IsGroupMappingModeByEdge(tangentElement); } - // TANGENT - if (tangentElement != NULL) + // TEXCOORD + std::vector uvOffsets; + for (int i = 0; i < (int)uvElements.size(); ++i) { - int tangentIndex = GetGroupIndexForLayerElementTemplate(tangentElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); - auto src_tangent = tangentElement->GetDirectArray().GetAt(tangentIndex); - auto tangentPointer = ((Vector4*)(vbPointer + tangentOffset)); - Vector3 tangent = sceneMapping->ConvertNormalFromFbx(src_tangent); - if (isnan(tangent.X) || isnan(tangent.Y) || isnan(tangent.Z)) - { - *tangentPointer = Vector4(1, 0, 0, 1); - } - else - { - tangent = Vector3::Normalize(tangent); - - int binormalIndex = GetGroupIndexForLayerElementTemplate(binormalElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); - auto src_binormal = binormalElement->GetDirectArray().GetAt(binormalIndex); - Vector3 binormal = sceneMapping->ConvertNormalFromFbx(src_tangent); - // See GenerateTangentBinormal() - *tangentPointer = Vector4(tangent.X, tangent.Y, tangent.Z, Vector3::Dot(Vector3::Cross(normal, tangent), binormal) < 0.0f ? -1.0f : 1.0f); - } + uvOffsets.push_back(vertexStride); + vertexElements->Add(VertexElement::TextureCoordinate(i, vertexStride)); + vertexStride += 8; + uvElementMapping[pMesh->GetElementUV(i)->GetName()] = i; } - // BLENDINDICES and BLENDWEIGHT + // BLENDINDICES + int blendIndicesOffset = vertexStride; + bool controlPointIndices16 = (AllowUnsignedBlendIndices && totalClusterCount > 256) || (!AllowUnsignedBlendIndices && totalClusterCount > 128); if (!controlPointWeights.empty()) { - const auto& blendWeights = controlPointWeights[controlPointIndex]; - for (int i = 0; i < 4; ++i) + if (controlPointIndices16) { - if (controlPointIndices16) + if (AllowUnsignedBlendIndices) { - if (AllowUnsignedBlendIndices) - ((unsigned short*)(vbPointer + blendIndicesOffset))[i] = (unsigned short)blendWeights[i].first; - else - ((short*)(vbPointer + blendIndicesOffset))[i] = (short)blendWeights[i].first; + vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R16G16B16A16_UInt, vertexStride)); + vertexStride += sizeof(unsigned short) * 4; } else { - if (AllowUnsignedBlendIndices) - ((unsigned char*)(vbPointer + blendIndicesOffset))[i] = (unsigned char)blendWeights[i].first; - else - ((char*)(vbPointer + blendIndicesOffset))[i] = (char)blendWeights[i].first; + vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R16G16B16A16_SInt, vertexStride)); + vertexStride += sizeof(short) * 4; + } + } + else + { + if (AllowUnsignedBlendIndices) + { + vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R8G8B8A8_UInt, vertexStride)); + vertexStride += sizeof(unsigned char) * 4; + } + else + { + vertexElements->Add(VertexElement("BLENDINDICES", 0, PixelFormat::R8G8B8A8_SInt, vertexStride)); + vertexStride += sizeof(char) * 4; } - ((float*)(vbPointer + blendWeightOffset))[i] = blendWeights[i].second; } } + // BLENDWEIGHT + int blendWeightOffset = vertexStride; + if (!controlPointWeights.empty()) + { + vertexElements->Add(VertexElement("BLENDWEIGHT", 0, PixelFormat::R32G32B32A32_Float, vertexStride)); + vertexStride += sizeof(float) * 4; + } + // COLOR - for (int elementColorIndex = 0; elementColorIndex < elementVertexColorCount; elementColorIndex++) + auto elementVertexColorCount = pMesh->GetElementVertexColorCount(); + std::vector vertexColorElements; + int colorOffset = vertexStride; + for (int i = 0; i < elementVertexColorCount; i++) { - auto vertexColorElement = vertexColorElements[elementColorIndex]; - auto groupIndex = GetGroupIndexForLayerElementTemplate(vertexColorElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); - auto color = vertexColorElement->GetDirectArray().GetAt(groupIndex); - ((Color*)(vbPointer + colorOffset))[elementColorIndex] = Color((float)color.mRed, (float)color.mGreen, (float)color.mBlue, (float)color.mAlpha); + auto vertexColorElement = pMesh->GetElementVertexColor(i); + vertexColorElements.push_back(vertexColorElement); + vertexElements->Add(VertexElement::Color(i, vertexStride)); + vertexStride += sizeof(Color); + needEdgeIndexing |= IsGroupMappingModeByEdge(vertexColorElement); } // USERDATA - // TODO HANDLE USERDATA HERE - + // TODO: USERData how to handle then? + //auto userDataCount = pMesh->GetElementUserDataCount(); + //for (int i = 0; i < userDataCount; i++) + //{ + // auto userData = pMesh->GetElementUserData(i); + // auto dataType = userData->GetDataName(0); + // Console::WriteLine("DataName {0}", gcnew String(dataType)); + //} + + // Add the smoothing group information at the end of the vertex declaration + // ************************************************************************* + // WARNING - DONT PUT ANY VertexElement after SMOOTHINGGROUP + // ************************************************************************* + // Iit is important that to be the LAST ELEMENT of the declaration because it is dropped later in the process by partial memcopys // SMOOTHINGGROUP + int smoothingOffset = vertexStride; if (smoothingElement != NULL) { - auto groupIndex = GetGroupIndexForLayerElementTemplate(smoothingElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); - auto group = smoothingElement->GetDirectArray().GetAt(groupIndex); - ((int*)(vbPointer + smoothingOffset))[0] = (int)group; - } - - vbPointer += vertexStride; - } - } - - polygonVertexStartIndex += polygonSize; - } - - if (needEdgeIndexing) - pMesh->EndGetMeshEdgeIndexForPolygon(); - - // Create submeshes - for (int i = 0; i < buildMeshes->Count; ++i) - { - auto buildMesh = buildMeshes[i]; - if (buildMesh == nullptr) - continue; - - auto buffer = buildMesh->buffer; - auto vertexBufferBinding = VertexBufferBinding(GraphicsSerializerExtensions::ToSerializableVersion(gcnew BufferData(BufferFlags::VertexBuffer, buffer)), gcnew VertexDeclaration(vertexElements->ToArray()), buildMesh->polygonCount * 3, 0, 0); - - auto drawData = gcnew MeshDraw(); - auto vbb = gcnew List(); - vbb->Add(vertexBufferBinding); - drawData->VertexBuffers = vbb->ToArray(); - drawData->PrimitiveType = PrimitiveType::TriangleList; - drawData->DrawCount = buildMesh->polygonCount * 3; - - // build the final VertexDeclaration removing the declaration element needed only for the buffer's correct construction - auto finalVertexElements = gcnew List(); - for each (VertexElement element in vertexElements) - { - if (element.SemanticName != "SMOOTHINGGROUP") - finalVertexElements->Add(element); - } - auto finalDeclaration = gcnew VertexDeclaration(finalVertexElements->ToArray()); - - // Generate index buffer - // For now, if user requests 16 bits indices but it doesn't fit, it - // won't generate an index buffer, but ideally it should just split it in multiple render calls - IndexExtensions::GenerateIndexBuffer(drawData, finalDeclaration); - /*if (drawData->DrawCount < 65536) - { - IndexExtensions::GenerateIndexBuffer(drawData); - } - else - { - 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__)); - }*/ - - auto lMaterial = pMesh->GetNode()->GetMaterial(i); - - // Generate TNB - if (tangentElement == NULL && normalElement != NULL && uvElements.size() > 0) - TNBExtensions::GenerateTangentBinormal(drawData); - - auto meshData = gcnew Mesh(); - meshData->NodeIndex = sceneMapping->FindNodeIndex(pMesh->GetNode()); - meshData->Draw = drawData; - if (!controlPointWeights.empty()) - { - meshData->Skinning = gcnew MeshSkinningDefinition(); - meshData->Skinning->Bones = bones->ToArray(); - } - - auto materialIndex = materials.find(lMaterial); - meshData->MaterialIndex = (materialIndex != materials.end()) ? materialIndex->second : 0; - - auto meshName = meshNames[pMesh]; - if (buildMeshes->Count > 1) - meshName = meshName + "_" + std::to_string(i + 1); - meshData->Name = gcnew String(meshName.c_str()); - - if (hasSkinningPosition || hasSkinningNormal || totalClusterCount > 0) - { - if (hasSkinningPosition) - meshData->Parameters->Set(MaterialKeys::HasSkinningPosition, true); - if (hasSkinningNormal) - meshData->Parameters->Set(MaterialKeys::HasSkinningNormal, true); - } - modelData->Meshes->Add(meshData); - } - } + vertexElements->Add(VertexElement("SMOOTHINGGROUP", 0, PixelFormat::R32_UInt, vertexStride)); + vertexStride += sizeof(int); - // return a boolean indicating whether the built material is transparent or not - MaterialAsset^ ProcessMeshMaterialAsset(FbxSurfaceMaterial* lMaterial, std::map& uvElementMapping) - { - auto uvEltMappingOverride = uvElementMapping; - auto textureMap = gcnew Dictionary(); - std::map textureNameCount; - - auto finalMaterial = gcnew Stride::Assets::Materials::MaterialAsset(); - - auto phongSurface = FbxCast(lMaterial); - auto lambertSurface = FbxCast(lMaterial); - - { // The diffuse color - auto diffuseTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor, finalMaterial); - if(lambertSurface || diffuseTree != nullptr) - { - if(diffuseTree == nullptr) - { - auto diffuseColor = lambertSurface->Diffuse.Get(); - auto diffuseFactor = lambertSurface->DiffuseFactor.Get(); - auto diffuseColorValue = diffuseFactor * diffuseColor; + needEdgeIndexing |= IsGroupMappingModeByEdge(smoothingElement); + } - // Create diffuse value even if the color is black - diffuseTree = gcnew ComputeColor(FbxDouble3ToColor4(diffuseColorValue)); - } + int polygonCount = pMesh->GetPolygonCount(); - if (diffuseTree != nullptr) - { - finalMaterial->Attributes->Diffuse = gcnew MaterialDiffuseMapFeature(diffuseTree); - finalMaterial->Attributes->DiffuseModel = gcnew MaterialDiffuseLambertModelFeature(); - } - } - } - { // The emissive color - auto emissiveTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor, finalMaterial); - if(lambertSurface || emissiveTree != nullptr) - { - if(emissiveTree == nullptr) - { - auto emissiveColor = lambertSurface->Emissive.Get(); - auto emissiveFactor = lambertSurface->EmissiveFactor.Get(); - auto emissiveColorValue = emissiveFactor * emissiveColor; + FbxGeometryElement::EMappingMode materialMappingMode = FbxGeometryElement::eNone; + FbxLayerElementArrayTemplate* materialIndices = NULL; - // Do not create the node if the value has not been explicitly specified by the user. - if(emissiveColorValue != FbxDouble3(0)) + if (pMesh->GetElementMaterial()) { - emissiveTree = gcnew ComputeColor(FbxDouble3ToColor4(emissiveColorValue)); + materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode(); + materialIndices = &pMesh->GetElementMaterial()->GetIndexArray(); } - } - if (emissiveTree != nullptr) - { - finalMaterial->Attributes->Emissive = gcnew MaterialEmissiveMapFeature(emissiveTree); - } - } - } - // TODO: Check if we want to support Ambient Color - //{ // The ambient color - // auto ambientTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor, finalMaterial); - // if(lambertSurface || ambientTree != nullptr) - // { - // if(ambientTree == nullptr) - // { - // auto ambientColor = lambertSurface->Emissive.Get(); - // auto ambientFactor = lambertSurface->EmissiveFactor.Get(); - // auto ambientColorValue = ambientFactor * ambientColor; - - // // Do not create the node if the value has not been explicitly specified by the user. - // if(ambientColorValue != FbxDouble3(0)) - // { - // ambientTree = gcnew ComputeColor(FbxDouble3ToColor4(ambientColorValue)); - // } - // } - - // if(ambientTree != nullptr) - // finalMaterial->AddColorNode(MaterialParameters::AmbientMap, "ambient", ambientTree); - // } - //} - { // The normal map - auto normalMapTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sNormalMap, NULL, finalMaterial); - if(lambertSurface || normalMapTree != nullptr) - { - if(normalMapTree == nullptr) - { - auto normalMapValue = lambertSurface->NormalMap.Get(); + auto buildMeshes = gcnew List(); - // Do not create the node if the value has not been explicitly specified by the user. - if(normalMapValue != FbxDouble3(0)) - { - normalMapTree = gcnew ComputeFloat4(FbxDouble3ToVector4(normalMapValue)); - } - } - - if (normalMapTree != nullptr) - { - finalMaterial->Attributes->Surface = gcnew MaterialNormalMapFeature(normalMapTree); - } - } - } - // TODO: Support for BumpMap - //{ // The bump map - // auto bumpMapTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sBump, FbxSurfaceMaterial::sBumpFactor, finalMaterial); - // if(lambertSurface || bumpMapTree != nullptr) - // { - // if(bumpMapTree == nullptr) - // { - // auto bumpValue = lambertSurface->Bump.Get(); - // auto bumpFactor = lambertSurface->BumpFactor.Get(); - // auto bumpMapValue = bumpFactor * bumpValue; - - // // Do not create the node if the value has not been explicitly specified by the user. - // if(bumpMapValue != FbxDouble3(0)) - // { - // bumpMapTree = gcnew MaterialFloat4ComputeColor(FbxDouble3ToVector4(bumpMapValue)); - // } - // } - // - // if (bumpMapTree != nullptr) - // { - // finalMaterial->AddColorNode(MaterialParameters::BumpMap, "bumpMap", bumpMapTree); - // } - // } - //} - // TODO: Support for Transparency - //{ // The transparency - // auto transparencyTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor, finalMaterial); - // if(lambertSurface || transparencyTree != nullptr) - // { - // if(transparencyTree == nullptr) - // { - // auto transparencyColor = lambertSurface->TransparentColor.Get(); - // auto transparencyFactor = lambertSurface->TransparencyFactor.Get(); - // auto transparencyValue = transparencyFactor * transparencyColor; - // auto opacityValue = std::min(1.0f, std::max(0.0f, 1-(float)transparencyValue[0])); - - // // Do not create the node if the value has not been explicitly specified by the user. - // if(opacityValue < 1) - // { - // transparencyTree = gcnew MaterialFloatComputeColor(opacityValue); - // } - // } - - // if(transparencyTree != nullptr) - // finalMaterial->AddColorNode(MaterialParameters::TransparencyMap, "transparencyMap", transparencyTree); - // } - //} - //// TODO: Support for displacement map - //{ // The displacement map - // auto displacementColorTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sDisplacementColor, FbxSurfaceMaterial::sDisplacementFactor, finalMaterial); - // if(lambertSurface || displacementColorTree != nullptr) - // { - // if(displacementColorTree == nullptr) - // { - // auto displacementColor = lambertSurface->DisplacementColor.Get(); - // auto displacementFactor = lambertSurface->DisplacementFactor.Get(); - // auto displacementValue = displacementFactor * displacementColor; - - // // Do not create the node if the value has not been explicitly specified by the user. - // if(displacementValue != FbxDouble3(0)) - // { - // displacementColorTree = gcnew MaterialFloat4ComputeColor(FbxDouble3ToVector4(displacementValue)); - // } - // } - // - // if(displacementColorTree != nullptr) - // finalMaterial->AddColorNode(MaterialParameters::DisplacementMap, "displacementMap", displacementColorTree); - // } - //} - { // The specular color - auto specularTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sSpecular, NULL, finalMaterial); - if(phongSurface || specularTree != nullptr) - { - if(specularTree == nullptr) - { - auto specularColor = phongSurface->Specular.Get(); - - // Do not create the node if the value has not been explicitly specified by the user. - if(specularColor != FbxDouble3(0)) + // Count polygon per materials + for (int i = 0; i < polygonCount; i++) { - specularTree = gcnew ComputeColor(FbxDouble3ToColor4(specularColor)); - } - } - - if (specularTree != nullptr) - { - auto specularFeature = gcnew MaterialSpecularMapFeature(); - specularFeature->SpecularMap = specularTree; - finalMaterial->Attributes->Specular = specularFeature; + int materialIndex = 0; + if (materialMappingMode == FbxGeometryElement::eByPolygon) + { + materialIndex = materialIndices->GetAt(i); + } + + // Equivalent to std::vector::resize() + while (materialIndex >= buildMeshes->Count) + { + buildMeshes->Add(nullptr); + } - auto specularModel = gcnew MaterialSpecularMicrofacetModelFeature(); - specularModel->Fresnel = gcnew MaterialSpecularMicrofacetFresnelSchlick(); - specularModel->Visibility = gcnew MaterialSpecularMicrofacetVisibilityImplicit(); - specularModel->NormalDistribution = gcnew MaterialSpecularMicrofacetNormalDistributionBlinnPhong(); + if (buildMeshes[materialIndex] == nullptr) + buildMeshes[materialIndex] = gcnew BuildMesh(); - finalMaterial->Attributes->SpecularModel = specularModel; - } - } - } - // TODO REPLUG SPECULAR INTENSITY - //{ // The specular intensity map - // auto specularIntensityTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sSpecularFactor, NULL, finalMaterial); - // if(phongSurface || specularIntensityTree != nullptr) - // { - // if(specularIntensityTree == nullptr) - // { - // auto specularIntensity = phongSurface->SpecularFactor.Get(); - // - // // Do not create the node if the value has not been explicitly specified by the user. - // if(specularIntensity > 0) - // { - // specularIntensityTree = gcnew MaterialFloatComputeNode((float)specularIntensity); - // } - // } - // - // if (specularIntensityTree != nullptr) - // { - // MaterialSpecularMapFeature^ specularFeature; - // if (finalMaterial->Attributes->Specular == nullptr || finalMaterial->Attributes->Specular->GetType() != MaterialSpecularMapFeature::typeid) - // { - // specularFeature = gcnew MaterialSpecularMapFeature(); - // } - // else - // { - // specularFeature = (MaterialSpecularMapFeature^)finalMaterial->Attributes->Specular; - // } - // // TODO: Check Specular Intensity and Power - // specularFeature->Intensity = specularIntensityTree; - // finalMaterial->Attributes->Specular = specularFeature; - // } - // } - // } - /* { // The specular power map - auto specularPowerTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sShininess, NULL, finalMaterial); - if(phongSurface || specularPowerTree != nullptr) - { - if(specularPowerTree == nullptr) - { - auto specularPower = phongSurface->Shininess.Get(); - - // Do not create the node if the value has not been explicitly specified by the user. - if(specularPower > 0) - { - specularPowerTree = gcnew MaterialFloatComputeColor((float)specularPower); - } - } - - if (specularPowerTree != nullptr) - { - MaterialSpecularMapFeature^ specularFeature; - if (finalMaterial->Attributes->Specular == nullptr || finalMaterial->Attributes->Specular->GetType() != MaterialSpecularMapFeature::typeid) - { - specularFeature = gcnew MaterialSpecularMapFeature(); + int polygonSize = pMesh->GetPolygonSize(i) - 2; + if (polygonSize > 0) + buildMeshes[materialIndex]->polygonCount += polygonSize; } - else + + // Create arrays + for each (BuildMesh ^ buildMesh in buildMeshes) { - specularFeature = (MaterialSpecularMapFeature^)finalMaterial->Attributes->Specular; + if (buildMesh == nullptr) + continue; + + buildMesh->buffer = gcnew array(vertexStride * buildMesh->polygonCount * 3); } - // TODO: Check Specular Intensity and Power - specularFeature->Intensity = specularPowerTree; - finalMaterial->Attributes->Specular = specularFeature; - } - } - }*/ - //// TODO: Support for reflection map - //{ // The reflection map - // auto reflectionMapTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sReflection, FbxSurfaceMaterial::sReflectionFactor, finalMaterial); - // if(phongSurface || reflectionMapTree != nullptr) - // { - // if(reflectionMapTree == nullptr) - // { - // auto reflectionColor = lambertSurface->DisplacementColor.Get(); - // auto reflectionFactor = lambertSurface->DisplacementFactor.Get(); - // auto reflectionValue = reflectionFactor * reflectionColor; - - // // Do not create the node if the value has not been explicitly specified by the user. - // if(reflectionValue != FbxDouble3(0)) - // { - // reflectionMapTree = gcnew ComputeColor(FbxDouble3ToColor4(reflectionValue)); - // } - // } - // - // if(reflectionMapTree != nullptr) - // finalMaterial->AddColorNode(MaterialParameters::ReflectionMap, "reflectionMap", reflectionMapTree); - // } - //} - return finalMaterial; - } - bool IsTransparent(FbxSurfaceMaterial* lMaterial) - { - for (int i = 0; i < 2; ++i) - { - auto propertyName = i == 0 ? FbxSurfaceMaterial::sTransparentColor : FbxSurfaceMaterial::sTransparencyFactor; - if (propertyName == NULL) - continue; + bool layerIndexFirstTimeError = true; - FbxProperty lProperty = lMaterial->FindProperty(propertyName); - if (lProperty.IsValid()) - { - const int lTextureCount = lProperty.GetSrcObjectCount(); - for (int j = 0; j < lTextureCount; ++j) - { - FbxLayeredTexture *lLayeredTexture = FbxCast(lProperty.GetSrcObject(j)); - FbxFileTexture *lFileTexture = FbxCast(lProperty.GetSrcObject(j)); - if (lLayeredTexture) - { - int lNbTextures = lLayeredTexture->GetSrcObjectCount(); - if (lNbTextures > 0) - return true; - } - else if (lFileTexture) - return true; - } - if (lTextureCount == 0) - { - auto val = FbxDouble3ToVector3(lProperty.Get()); - if (val == Vector3::Zero || val != Vector3::One) - return true; - } - } - } - return false; - } + if (needEdgeIndexing) + pMesh->BeginGetMeshEdgeIndexForPolygon(); - IComputeNode^ GenerateSurfaceTextureTree(FbxSurfaceMaterial* lMaterial, std::map& uvElementMapping, Dictionary^ textureMap, - std::map& textureNameCount, char const* surfaceMaterial, char const* surfaceMaterialFactor, - Stride::Assets::Materials::MaterialAsset^ finalMaterial) - { - auto compositionTrees = gcnew cli::array(2); - - for (int i = 0; i < 2; ++i) - { - // Scan first for component name, then its factor (i.e. sDiffuse, then sDiffuseFactor) - auto propertyName = i == 0 ? surfaceMaterial : surfaceMaterialFactor; - if (propertyName == NULL) - continue; - - int compositionCount = 0; - - FbxProperty lProperty = lMaterial->FindProperty(propertyName); - if (lProperty.IsValid()) - { - IComputeColor^ previousNode = nullptr; - const int lTextureCount = lProperty.GetSrcObjectCount(); - for (int j = 0; j < lTextureCount; ++j) - { - FbxLayeredTexture *lLayeredTexture = FbxCast(lProperty.GetSrcObject(j)); - FbxFileTexture *lFileTexture = FbxCast(lProperty.GetSrcObject(j)); - if (lLayeredTexture) + // Build polygons + int polygonVertexStartIndex = 0; + for (int i = 0; i < polygonCount; i++) { - int lNbTextures = lLayeredTexture->GetSrcObjectCount(); - for (int k = 0; k < lNbTextures; ++k) + int materialIndex = 0; + if (materialMappingMode == FbxGeometryElement::eByPolygon) { - FbxFileTexture* lSubTexture = FbxCast(lLayeredTexture->GetSrcObject(k)); + materialIndex = materialIndices->GetAt(i); + } - auto uvName = std::string(lSubTexture->UVSet.Get()); - if (uvElementMapping.find(uvName) == uvElementMapping.end()) - uvElementMapping[uvName] = uvElementMapping.size(); + auto buildMesh = buildMeshes[materialIndex]; + auto buffer = buildMesh->buffer; - auto currentMaterialReference = GenerateMaterialTextureNodeFBX(lSubTexture, uvElementMapping, textureMap, textureNameCount, finalMaterial); - - if (lNbTextures == 1 || compositionCount == 0) + int polygonSize = pMesh->GetPolygonSize(i); + + for (int polygonFanIndex = 2; polygonFanIndex < polygonSize; ++polygonFanIndex) + { + pin_ptr vbPointer = &buffer[buildMesh->bufferOffset]; + buildMesh->bufferOffset += vertexStride * 3; + + int vertexInPolygon[3] = { 0, polygonFanIndex, polygonFanIndex - 1 }; + int edgesInPolygon[3]; + + if (needEdgeIndexing) { - if (previousNode == nullptr) - previousNode = currentMaterialReference; - else - previousNode = gcnew ComputeBinaryColor(previousNode, currentMaterialReference, BinaryOperator::Add); // not sure + // Default case for polygon of size 3 + // Since our polygon order is 0,2,1, edge order is 2 (edge from 0 to 2),1 (edge from 2 to 1),0 (edge from 1 to 0) + // Note: all that code computing edge should change if vertexInPolygon changes + edgesInPolygon[0] = polygonFanIndex; + edgesInPolygon[1] = polygonFanIndex - 1; + edgesInPolygon[2] = 0; + + if (polygonSize > 3) + { + // Since we create non-existing edges inside the fan, we might have to use another edge in those cases + // If edge doesn't exist, we have to use edge from (polygonFanIndex-1) to polygonFanIndex (only one that always exists) + + // Let's say polygon is 0,4,3,2,1 + + // First polygons (except last): 0,2,1 (edge doesn't exist, use the one from 2 to 1 so edge 1) + // Last polygon : 0,4,3 (edge exists:4, from 0 to 4) + if (polygonFanIndex != polygonSize - 1) + edgesInPolygon[0] = polygonFanIndex - 1; + + // First polygon: 0,2,1 (edge exists:0, from 1 to 0) + // Last polygons: 0,4,3 (edge doesn't exist, use the one from 4 to 3 so edge 3) + if (polygonFanIndex != 2) + edgesInPolygon[2] = polygonFanIndex - 1; + } } - else + + //if (polygonSwap) + //{ + // int temp = vertexInPolygon[1]; + // vertexInPolygon[1] = vertexInPolygon[2]; + // vertexInPolygon[2] = temp; + //} + int controlPointIndices[3] = { pMesh->GetPolygonVertex(i, vertexInPolygon[0]), pMesh->GetPolygonVertex(i, vertexInPolygon[1]), pMesh->GetPolygonVertex(i, vertexInPolygon[2]) }; + + for (int polygonFanVertex = 0; polygonFanVertex < 3; ++polygonFanVertex) { - auto newNode = gcnew ComputeBinaryColor(previousNode, currentMaterialReference, BinaryOperator::Add); - previousNode = newNode; - - FbxLayeredTexture::EBlendMode blendMode; - lLayeredTexture->GetTextureBlendMode(k, blendMode); - newNode->Operator = BlendModeToBlendOperand(blendMode); + int j = vertexInPolygon[polygonFanVertex]; + int vertexIndex = polygonVertexStartIndex + j; + int jNext = vertexInPolygon[(polygonFanVertex + 1) % 3]; + int vertexIndexNext = polygonVertexStartIndex + jNext; + int controlPointIndex = controlPointIndices[polygonFanVertex]; + int edgeIndex = needEdgeIndexing ? pMesh->GetMeshEdgeIndexForPolygon(i, edgesInPolygon[polygonFanVertex]) : 0; + + // POSITION + auto controlPoint = sceneMapping->ConvertPointFromFbx(controlPoints[controlPointIndex]); + *(Vector3*)(vbPointer + positionOffset) = controlPoint; + + // NORMAL + Vector3 normal = Vector3(1, 0, 0); + if (normalElement != NULL) + { + int normalIndex = GetGroupIndexForLayerElementTemplate(normalElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); + auto src_normal = normalElement->GetDirectArray().GetAt(normalIndex); + auto normalPointer = ((Vector3*)(vbPointer + normalOffset)); + normal = sceneMapping->ConvertNormalFromFbx(src_normal); + if (isnan(normal.X) || isnan(normal.Y) || isnan(normal.Z)) + normal = Vector3(1, 0, 0); + normal = Vector3::Normalize(normal); + *normalPointer = normal; + } + + // UV + for (int uvGroupIndex = 0; uvGroupIndex < (int)uvElements.size(); ++uvGroupIndex) + { + auto uvElement = uvElements[uvGroupIndex]; + int uvIndex = GetGroupIndexForLayerElementTemplate(uvElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); + auto uv = uvElement->GetDirectArray().GetAt(uvIndex); + + ((float*)(vbPointer + uvOffsets[uvGroupIndex]))[0] = (float)uv[0]; + ((float*)(vbPointer + uvOffsets[uvGroupIndex]))[1] = 1.0f - (float)uv[1]; + } + + // TANGENT + if (tangentElement != NULL) + { + int tangentIndex = GetGroupIndexForLayerElementTemplate(tangentElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); + auto src_tangent = tangentElement->GetDirectArray().GetAt(tangentIndex); + auto tangentPointer = ((Vector4*)(vbPointer + tangentOffset)); + Vector3 tangent = sceneMapping->ConvertNormalFromFbx(src_tangent); + if (isnan(tangent.X) || isnan(tangent.Y) || isnan(tangent.Z)) + { + *tangentPointer = Vector4(1, 0, 0, 1); + } + else + { + tangent = Vector3::Normalize(tangent); + + int binormalIndex = GetGroupIndexForLayerElementTemplate(binormalElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); + auto src_binormal = binormalElement->GetDirectArray().GetAt(binormalIndex); + Vector3 binormal = sceneMapping->ConvertNormalFromFbx(src_tangent); + // See GenerateTangentBinormal() + *tangentPointer = Vector4(tangent.X, tangent.Y, tangent.Z, Vector3::Dot(Vector3::Cross(normal, tangent), binormal) < 0.0f ? -1.0f : 1.0f); + } + } + + // BLENDINDICES and BLENDWEIGHT + if (!controlPointWeights.empty()) + { + const auto& blendWeights = controlPointWeights[controlPointIndex]; + for (int i = 0; i < 4; ++i) + { + if (controlPointIndices16) + { + if (AllowUnsignedBlendIndices) + ((unsigned short*)(vbPointer + blendIndicesOffset))[i] = (unsigned short)blendWeights[i].first; + else + ((short*)(vbPointer + blendIndicesOffset))[i] = (short)blendWeights[i].first; + } + else + { + if (AllowUnsignedBlendIndices) + ((unsigned char*)(vbPointer + blendIndicesOffset))[i] = (unsigned char)blendWeights[i].first; + else + ((char*)(vbPointer + blendIndicesOffset))[i] = (char)blendWeights[i].first; + } + ((float*)(vbPointer + blendWeightOffset))[i] = blendWeights[i].second; + } + } + + // COLOR + for (int elementColorIndex = 0; elementColorIndex < elementVertexColorCount; elementColorIndex++) + { + auto vertexColorElement = vertexColorElements[elementColorIndex]; + auto groupIndex = GetGroupIndexForLayerElementTemplate(vertexColorElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); + auto color = vertexColorElement->GetDirectArray().GetAt(groupIndex); + ((Color*)(vbPointer + colorOffset))[elementColorIndex] = Color((float)color.mRed, (float)color.mGreen, (float)color.mBlue, (float)color.mAlpha); + } + + // USERDATA + // TODO HANDLE USERDATA HERE + + // SMOOTHINGGROUP + if (smoothingElement != NULL) + { + auto groupIndex = GetGroupIndexForLayerElementTemplate(smoothingElement, controlPointIndex, vertexIndex, edgeIndex, i, meshName, layerIndexFirstTimeError); + auto group = smoothingElement->GetDirectArray().GetAt(groupIndex); + ((int*)(vbPointer + smoothingOffset))[0] = (int)group; + } + + vbPointer += vertexStride; } - - compositionCount++; } + + polygonVertexStartIndex += polygonSize; } - else if (lFileTexture) + + if (needEdgeIndexing) + pMesh->EndGetMeshEdgeIndexForPolygon(); + + // Create submeshes + for (int i = 0; i < buildMeshes->Count; ++i) { - compositionCount++; + auto buildMesh = buildMeshes[i]; + if (buildMesh == nullptr) + continue; + + auto buffer = buildMesh->buffer; + auto vertexBufferBinding = VertexBufferBinding(GraphicsSerializerExtensions::ToSerializableVersion(gcnew BufferData(BufferFlags::VertexBuffer, buffer)), gcnew VertexDeclaration(vertexElements->ToArray()), buildMesh->polygonCount * 3, 0, 0); + + auto drawData = gcnew MeshDraw(); + auto vbb = gcnew List(); + vbb->Add(vertexBufferBinding); + drawData->VertexBuffers = vbb->ToArray(); + drawData->PrimitiveType = PrimitiveType::TriangleList; + drawData->DrawCount = buildMesh->polygonCount * 3; + + // build the final VertexDeclaration removing the declaration element needed only for the buffer's correct construction + auto finalVertexElements = gcnew List(); + for each (VertexElement element in vertexElements) + { + if (element.SemanticName != "SMOOTHINGGROUP") + finalVertexElements->Add(element); + } + auto finalDeclaration = gcnew VertexDeclaration(finalVertexElements->ToArray()); - auto newMaterialReference = GenerateMaterialTextureNodeFBX(lFileTexture, uvElementMapping, textureMap, textureNameCount, finalMaterial); - - if (previousNode == nullptr) - previousNode = newMaterialReference; + // Generate index buffer + // For now, if user requests 16 bits indices but it doesn't fit, it + // won't generate an index buffer, but ideally it should just split it in multiple render calls + IndexExtensions::GenerateIndexBuffer(drawData, finalDeclaration); + /*if (drawData->DrawCount < 65536) + { + IndexExtensions::GenerateIndexBuffer(drawData); + } else - previousNode = gcnew ComputeBinaryColor(previousNode, newMaterialReference, BinaryOperator::Add); // not sure - } - } + { + 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__)); + }*/ - compositionTrees[i] = previousNode; - } - } + auto lMaterial = pMesh->GetNode()->GetMaterial(i); - // If we only have one of either Color or Factor, use directly, otherwise multiply them together - IComputeColor^ compositionTree; - if (compositionTrees[0] == nullptr) // TODO do we want only the factor??? -> delete - { - compositionTree = compositionTrees[1]; - } - else if (compositionTrees[1] == nullptr) - { - compositionTree = compositionTrees[0]; - } - else - { - compositionTree = gcnew ComputeBinaryColor(compositionTrees[0], compositionTrees[1], BinaryOperator::Multiply); - } + // Generate TNB + if (tangentElement == NULL && normalElement != NULL && uvElements.size() > 0) + TNBExtensions::GenerateTangentBinormal(drawData); - return compositionTree; - } + auto meshData = gcnew Mesh(); + meshData->NodeIndex = sceneMapping->FindNodeIndex(pMesh->GetNode()); + meshData->Draw = drawData; + if (!controlPointWeights.empty()) + { + meshData->Skinning = gcnew MeshSkinningDefinition(); + meshData->Skinning->Bones = bones->ToArray(); + } - BinaryOperator BlendModeToBlendOperand(FbxLayeredTexture::EBlendMode blendMode) - { - switch (blendMode) - { - case FbxLayeredTexture::eOver: - return BinaryOperator::Over; - case FbxLayeredTexture::eAdditive: - return BinaryOperator::Add; - case FbxLayeredTexture::eModulate: - return BinaryOperator::Multiply; - //case FbxLayeredTexture::eTranslucent: - // return BinaryOperator::Multiply; - //case FbxLayeredTexture::eModulate2: - // return BinaryOperator::Multiply; - //case FbxLayeredTexture::eNormal: - // return BinaryOperator::Multiply; - //case FbxLayeredTexture::eDissolve: - // return BinaryOperator::Multiply; - case FbxLayeredTexture::eDarken: - return BinaryOperator::Darken; - case FbxLayeredTexture::eColorBurn: - return BinaryOperator::ColorBurn; - case FbxLayeredTexture::eLinearBurn: - return BinaryOperator::LinearBurn; - //case FbxLayeredTexture::eDarkerColor: - // return BinaryOperator::Multiply; - case FbxLayeredTexture::eLighten: - return BinaryOperator::Lighten; - case FbxLayeredTexture::eScreen: - return BinaryOperator::Screen; - case FbxLayeredTexture::eColorDodge: - return BinaryOperator::ColorDodge; - case FbxLayeredTexture::eLinearDodge: - return BinaryOperator::LinearDodge; - //case FbxLayeredTexture::eLighterColor: - // return BinaryOperator::Multiply; - case FbxLayeredTexture::eSoftLight: - return BinaryOperator::SoftLight; - case FbxLayeredTexture::eHardLight: - return BinaryOperator::HardLight; - //case FbxLayeredTexture::eVividLight: - // return BinaryOperator::Multiply; - //case FbxLayeredTexture::eLinearLight: - // return BinaryOperator::Multiply; - case FbxLayeredTexture::ePinLight: - return BinaryOperator::PinLight; - case FbxLayeredTexture::eHardMix: - return BinaryOperator::HardMix; - case FbxLayeredTexture::eDifference: - return BinaryOperator::Difference; - case FbxLayeredTexture::eExclusion: - return BinaryOperator::Exclusion; - case FbxLayeredTexture::eSubtract: - return BinaryOperator::Subtract; - case FbxLayeredTexture::eDivide: - return BinaryOperator::Divide; - case FbxLayeredTexture::eHue: - return BinaryOperator::Hue; - case FbxLayeredTexture::eSaturation: - return BinaryOperator::Saturation; - //case FbxLayeredTexture::eColor: - // return BinaryOperator::Multiply; - //case FbxLayeredTexture::eLuminosity: - // return BinaryOperator::Multiply; - case FbxLayeredTexture::eOverlay: - return BinaryOperator::Overlay; - default: - logger->Error(String::Format("Material blending mode '{0}' is not supported yet. Multiplying blending mode will be used instead.", gcnew Int32(blendMode)), (CallerInfo^)nullptr); - return BinaryOperator::Multiply; - } - } + auto materialIndex = materials.find(lMaterial); + meshData->MaterialIndex = (materialIndex != materials.end()) ? materialIndex->second : 0; - ShaderClassSource^ GenerateTextureLayerFBX(FbxFileTexture* lFileTexture, std::map& uvElementMapping, Mesh^ meshData, int& textureCount, ParameterKey^ surfaceMaterialKey) - { - auto texScale = lFileTexture->GetUVScaling(); - auto texturePath = FindFilePath(lFileTexture); + auto meshName = meshNames[pMesh]; + if (buildMeshes->Count > 1) + meshName = meshName + "_" + std::to_string(i + 1); + meshData->Name = gcnew String(meshName.c_str()); - return TextureLayerGenerator::GenerateTextureLayer(vfsOutputFilename, texturePath, uvElementMapping[std::string(lFileTexture->UVSet.Get())], Vector2((float)texScale[0], (float)texScale[1]) , - textureCount, surfaceMaterialKey, - meshData, - nullptr); - } + if (hasSkinningPosition || hasSkinningNormal || totalClusterCount > 0) + { + if (hasSkinningPosition) + meshData->Parameters->Set(MaterialKeys::HasSkinningPosition, true); + if (hasSkinningNormal) + meshData->Parameters->Set(MaterialKeys::HasSkinningNormal, true); + } + modelData->Meshes->Add(meshData); + } - String^ FindFilePath(FbxFileTexture* lFileTexture) - { - auto relFileName = gcnew String(lFileTexture->GetRelativeFileName()); - auto absFileName = gcnew String(lFileTexture->GetFileName()); - - // First try to get the texture filename by relative path, if not valid then use absolute path - // (According to FBX doc, resolved first by absolute name, and relative name if absolute name is not valid) - auto fileNameToUse = Path::Combine(inputPath, relFileName); - if(fileNameToUse->StartsWith("\\\\", StringComparison::Ordinal)) - { - logger->Warning(String::Format("Importer detected a network address in referenced assets. This may temporary block the build if the file does not exist. [Address='{0}']", fileNameToUse), (CallerInfo^)nullptr); - } - if (!File::Exists(fileNameToUse) && !String::IsNullOrEmpty(absFileName)) - { - fileNameToUse = absFileName; - } + ProcessBlendShapes(pMesh); + } - // Make sure path is absolute - if (!(gcnew UFile(fileNameToUse))->IsAbsolute) - { - fileNameToUse = Path::Combine(inputPath, fileNameToUse); - } +#pragma region BLENDSHAPES + void ProcessBlendShapes(FbxMesh* pMesh) { - return fileNameToUse; - } + int blendShapeDeformerCount = pMesh->GetDeformerCount(FbxDeformer::eBlendShape); - ComputeTextureColor^ GenerateMaterialTextureNodeFBX(FbxFileTexture* lFileTexture, std::map& uvElementMapping, Dictionary^ textureMap, std::map& textureNameCount, Stride::Assets::Materials::MaterialAsset^ finalMaterial) - { - auto texScale = lFileTexture->GetUVScaling(); - auto texturePath = FindFilePath(lFileTexture); - auto wrapModeU = lFileTexture->GetWrapModeU(); - auto wrapModeV = lFileTexture->GetWrapModeV(); - auto wrapTextureU = (wrapModeU == FbxTexture::EWrapMode::eRepeat) ? TextureAddressMode::Wrap : TextureAddressMode::Clamp; - auto wrapTextureV = (wrapModeV == FbxTexture::EWrapMode::eRepeat) ? TextureAddressMode::Wrap : TextureAddressMode::Clamp; - - ComputeTextureColor^ textureValue; - - if (textureMap->TryGetValue(IntPtr(lFileTexture), textureValue)) - { - return textureValue; - } - else - { - textureValue = TextureLayerGenerator::GenerateMaterialTextureNode(vfsOutputFilename, texturePath, uvElementMapping[std::string(lFileTexture->UVSet.Get())], Vector2((float)texScale[0], (float)texScale[1]), wrapTextureU, wrapTextureV, nullptr); + for (int i = 0; i < blendShapeDeformerCount; ++i) { + FbxBlendShape* pBlendShape = static_cast(pMesh->GetDeformer(i, FbxDeformer::eBlendShape)); - auto attachedReference = AttachedReferenceManager::GetAttachedReference(textureValue->Texture); + int blendShapeChannelCount = pBlendShape->GetBlendShapeChannelCount(); - auto textureNamePtr = Marshal::StringToHGlobalAnsi(attachedReference->Url); - std::string textureName = std::string((char*)textureNamePtr.ToPointer()); - Marshal:: FreeHGlobal(textureNamePtr); + for (int j = 0; j < blendShapeChannelCount; ++j) { + FbxBlendShapeChannel* pBlendShapeChannel = pBlendShape->GetBlendShapeChannel(j); + String^ channelBlendShapeName = Marshal::PtrToStringAnsi(static_cast(const_cast(pBlendShapeChannel->GetName()))); - auto textureCount = GetTextureNameCount(textureNameCount, textureName); - if (textureCount > 1) - textureName = textureName + "_" + std::to_string(textureCount - 1); - auto referenceName = gcnew String(textureName.c_str()); - //auto materialReference = gcnew MaterialReferenceNode(referenceName); - //finalMaterial->AddNode(referenceName, textureValue); - textureMap[IntPtr(lFileTexture)] = textureValue; - return textureValue; - } - - return nullptr; - } + Stride::Rendering::BlendShape^ blendShape = gcnew BlendShape(); + blendShape->Name = channelBlendShapeName; - int GetTextureNameCount(std::map& textureNameCount, std::string textureName) - { - auto textureFound = textureNameCount.find(textureName); - if (textureFound == textureNameCount.end()) - textureNameCount[textureName] = 1; - else - textureNameCount[textureName] = textureNameCount[textureName] + 1; - return textureNameCount[textureName]; - } + int blenShapeChannelTargetCountr = pBlendShapeChannel->GetTargetShapeCount(); - void ProcessAttribute(FbxNode* pNode, FbxNodeAttribute* pAttribute, std::map meshNames, std::map materials) - { - if(!pAttribute) return; - - if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) - { - ProcessMesh((FbxMesh*)pAttribute, meshNames, materials); - } - } + for (int k = 0; k < blenShapeChannelTargetCountr; ++k) + { + FbxShape* fbxShape = pBlendShapeChannel->GetTargetShape(k); - void ProcessNodeTransformation(FbxNode* pNode) - { - auto nodeIndex = sceneMapping->FindNodeIndex(pNode); - auto nodes = sceneMapping->Nodes; - auto node = &nodes[nodeIndex]; - // Use GlobalTransform instead of LocalTransform - auto fbxMatrix = pNode->EvaluateLocalTransform(FBXSDK_TIME_ZERO); - auto matrix = sceneMapping->ConvertMatrixFromFbx(fbxMatrix); + String^ shapeName = Marshal::PtrToStringAnsi(static_cast(const_cast(fbxShape->GetName()))); - // Extract the translation and scaling - Vector3 translation; - Quaternion rotation; - Vector3 scaling; - matrix.Decompose(scaling, rotation, translation); - // Apply rotation on top level nodes only - if (node->ParentIndex == 0) - { - Vector3::TransformCoordinate(translation, sceneMapping->AxisSystemRotationMatrix, translation); - rotation = Quaternion::Multiply(rotation, Quaternion::RotationMatrix(sceneMapping->AxisSystemRotationMatrix)); - } + // array^ indices=gcnew array^ indices = gcnew cli::array(fbxShape->GetControlPointsCount()); - // Setup the transform for this node - node->Transform.Position = translation; - node->Transform.Rotation = rotation; - node->Transform.Scale = scaling; + cli::array^ controlPoints = gcnew cli::array(fbxShape->GetControlPointsCount()); - // Recursively process the children nodes. - for (int j = 0; j < pNode->GetChildCount(); j++) - { - ProcessNodeTransformation(pNode->GetChild(j)); - } - } + for (int h = 0; h < fbxShape->GetControlPointsCount(); ++h) + { + indices[h] = fbxShape->GetControlPointIndices()[h]; + System::Numerics::Vector4^ controlPoint = gcnew System::Numerics::Vector4(fbxShape->GetControlPointAt(h)[1], fbxShape->GetControlPointAt(h)[2], fbxShape->GetControlPointAt(h)[3], fbxShape->GetControlPointAt(h)[4]); - void ProcessNodeAttributes(FbxNode* pNode, std::map meshNames, std::map materials) - { - // Process the node's attributes. - for(int i = 0; i < pNode->GetNodeAttributeCount(); i++) - ProcessAttribute(pNode, pNode->GetNodeAttributeByIndex(i), meshNames, materials); + controlPoints[h] = *controlPoint; + } - // Recursively process the children nodes. - for(int j = 0; j < pNode->GetChildCount(); j++) - { - ProcessNodeAttributes(pNode->GetChild(j), meshNames, materials); - } - } - ref class BuildMesh - { - public: - array^ buffer; - int bufferOffset; - int polygonCount; - }; - - ref struct ImportConfiguration - { - public: - property bool ImportTemplates; - property bool ImportPivots; - property bool ImportGlobalSettings; - property bool ImportCharacters; - property bool ImportConstraints; - property bool ImportGobos; - property bool ImportShapes; - property bool ImportLinks; - property bool ImportMaterials; - property bool ImportTextures; - property bool ImportModels; - property bool ImportAnimations; - property bool ExtractEmbeddedData; - - public: - static ImportConfiguration^ ImportAll() - { - auto config = gcnew ImportConfiguration(); - - config->ImportTemplates = true; - config->ImportPivots = true; - config->ImportGlobalSettings = true; - config->ImportCharacters = true; - config->ImportConstraints = true; - config->ImportGobos = true; - config->ImportShapes = true; - config->ImportLinks = true; - config->ImportMaterials = true; - config->ImportTextures = true; - config->ImportModels = true; - config->ImportAnimations = true; - config->ExtractEmbeddedData = true; - - return config; - } + Stride::Rendering::Shape^ shape = gcnew - static ImportConfiguration^ ImportModelOnly() - { - auto config = gcnew ImportConfiguration(); - - config->ImportTemplates = false; - config->ImportPivots = false; - config->ImportGlobalSettings = true; - config->ImportCharacters = false; - config->ImportConstraints = false; - config->ImportGobos = false; - config->ImportShapes = false; - config->ImportLinks = false; - config->ImportMaterials = true; - config->ImportTextures = false; - config->ImportModels = true; - config->ImportAnimations = false; - config->ExtractEmbeddedData = false; - - return config; - } + Stride::Rendering::Shape(); + shape->Name = shapeName; + shape->Indices = indices; shape->Positions = controlPoints; - static ImportConfiguration^ ImportMaterialsOnly() - { - auto config = gcnew ImportConfiguration(); - - config->ImportTemplates = false; - config->ImportPivots = false; - config->ImportGlobalSettings = true; - config->ImportCharacters = false; - config->ImportConstraints = false; - config->ImportGobos = false; - config->ImportShapes = false; - config->ImportLinks = false; - config->ImportMaterials = true; - config->ImportTextures = false; - config->ImportModels = false; - config->ImportAnimations = false; - config->ExtractEmbeddedData = false; - - return config; - } + - static ImportConfiguration^ ImportAnimationsOnly() - { - auto config = gcnew ImportConfiguration(); - - config->ImportTemplates = false; - config->ImportPivots = false; - config->ImportGlobalSettings = true; - config->ImportCharacters = false; - config->ImportConstraints = false; - config->ImportGobos = false; - config->ImportShapes = false; - config->ImportLinks = false; - config->ImportMaterials = false; - config->ImportTextures = false; - config->ImportModels = false; - config->ImportAnimations = true; - config->ExtractEmbeddedData = false; - - return config; - } + double weight = pBlendShapeChannel->GetTargetShapeFullWeights()[k]; - static ImportConfiguration^ ImportSkeletonOnly() - { - auto config = gcnew ImportConfiguration(); - - config->ImportTemplates = false; - config->ImportPivots = false; - config->ImportGlobalSettings = true; - config->ImportCharacters = false; - config->ImportConstraints = false; - config->ImportGobos = false; - config->ImportShapes = false; - config->ImportLinks = false; - config->ImportMaterials = false; - config->ImportTextures = false; - config->ImportModels = false; - config->ImportAnimations = false; - config->ExtractEmbeddedData = false; - - return config; - } - static ImportConfiguration^ ImportTexturesOnly() - { - auto config = gcnew ImportConfiguration(); - - config->ImportTemplates = false; - config->ImportPivots = false; - config->ImportGlobalSettings = false; - config->ImportCharacters = false; - config->ImportConstraints = false; - config->ImportGobos = false; - config->ImportShapes = false; - config->ImportLinks = false; - config->ImportMaterials = false; - config->ImportTextures = true; - config->ImportModels = false; - config->ImportAnimations = false; - config->ExtractEmbeddedData = true; - - return config; - } - static ImportConfiguration^ ImportEntityConfig() - { - auto config = gcnew ImportConfiguration(); - - config->ImportTemplates = false; - config->ImportPivots = false; - config->ImportGlobalSettings = true; - config->ImportCharacters = false; - config->ImportConstraints = false; - config->ImportGobos = false; - config->ImportShapes = false; - config->ImportLinks = false; - config->ImportMaterials = true; - config->ImportTextures = true; - config->ImportModels = true; - config->ImportAnimations = true; - config->ExtractEmbeddedData = true; - - return config; - } + blendShape->AddShape(shape, weight); - static ImportConfiguration^ ImportGlobalSettingsOnly() - { - auto config = gcnew ImportConfiguration(); - config->ImportGlobalSettings = true; + } - return config; - } - }; - -private: - static System::Object^ globalLock = gcnew System::Object(); - - void Initialize(String^ inputFilename, String^ vfsOutputFilename, ImportConfiguration^ importConfig) - { - // ----------------------------------------------------- - // TODO: Workaround with FBX SDK not being multithreaded. - // We protect the whole usage of this class with a monitor - // - // Lock the whole class between Initialize/Destroy - // ----------------------------------------------------- - System::Threading::Monitor::Enter( globalLock ); - // ----------------------------------------------------- - - this->inputFilename = inputFilename; - this->vfsOutputFilename = vfsOutputFilename; - this->inputPath = Path::GetDirectoryName(inputFilename); - - // Initialize the sdk manager. This object handles all our memory management. - lSdkManager = FbxManager::Create(); - - // Create the io settings object. - FbxIOSettings *ios = FbxIOSettings::Create(lSdkManager, IOSROOT); - ios->SetBoolProp(IMP_FBX_TEMPLATE, importConfig->ImportTemplates); - ios->SetBoolProp(IMP_FBX_PIVOT, importConfig->ImportPivots); - ios->SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, importConfig->ImportGlobalSettings); - ios->SetBoolProp(IMP_FBX_CHARACTER, importConfig->ImportCharacters); - ios->SetBoolProp(IMP_FBX_CONSTRAINT, importConfig->ImportConstraints); - ios->SetBoolProp(IMP_FBX_GOBO, importConfig->ImportGobos); - ios->SetBoolProp(IMP_FBX_SHAPE, importConfig->ImportShapes); - ios->SetBoolProp(IMP_FBX_LINK, importConfig->ImportLinks); - ios->SetBoolProp(IMP_FBX_MATERIAL, importConfig->ImportMaterials); - ios->SetBoolProp(IMP_FBX_TEXTURE, importConfig->ImportTextures); - ios->SetBoolProp(IMP_FBX_MODEL, importConfig->ImportModels); - ios->SetBoolProp(IMP_FBX_ANIMATION, importConfig->ImportAnimations); - ios->SetBoolProp(IMP_FBX_EXTRACT_EMBEDDED_DATA, importConfig->ExtractEmbeddedData); - lSdkManager->SetIOSettings(ios); - - // Create an importer using our sdk manager. - lImporter = FbxImporter::Create(lSdkManager,""); - - auto inputFilenameUtf8 = System::Text::Encoding::UTF8->GetBytes(inputFilename); - pin_ptr inputFilenameUtf8Ptr = &inputFilenameUtf8[0]; - - if(!lImporter->Initialize((const char*)inputFilenameUtf8Ptr, -1, lSdkManager->GetIOSettings())) - { - throw gcnew InvalidOperationException(String::Format("Call to FbxImporter::Initialize() failed.\n" - "Error returned: {0}\n\n", gcnew String(lImporter->GetStatus().GetErrorString()))); - } + modelData->AddBlendShape(blendShape); + } + } + } - // Create a new scene so it can be populated by the imported file. - scene = FbxScene::Create(lSdkManager, "myScene"); +#pragma endregion + // return a boolean indicating whether the built material is transparent or not + MaterialAsset^ ProcessMeshMaterialAsset(FbxSurfaceMaterial* lMaterial, std::map& uvElementMapping) + { + auto uvEltMappingOverride = uvElementMapping; + auto textureMap = gcnew Dictionary(); + std::map textureNameCount; - // Import the contents of the file into the scene. - lImporter->Import(scene); + auto finalMaterial = gcnew Stride::Assets::Materials::MaterialAsset(); - const float framerate = static_cast(FbxTime::GetFrameRate(scene->GetGlobalSettings().GetTimeMode())); - scene->GetRootNode()->ResetPivotSetAndConvertAnimation(framerate, false, false); + auto phongSurface = FbxCast(lMaterial); + auto lambertSurface = FbxCast(lMaterial); - // Initialize the node mapping - sceneMapping = gcnew SceneMapping(scene); - } - - bool HasAnimationData(String^ inputFile) - { - try - { - Initialize(inputFile, nullptr, ImportConfiguration::ImportAnimationsOnly()); - auto animConverter = gcnew AnimationConverter(logger, sceneMapping); - return animConverter->HasAnimationData(); - } - finally - { - Destroy(); - } - } - - void GenerateMaterialNames(std::map& materialNames) - { - auto materials = gcnew List(); - std::map materialNameTotalCount; - std::map materialNameCurrentCount; - std::map tempNames; - auto materialCount = scene->GetMaterialCount(); - - for (int i = 0; i < materialCount; i++) - { - auto lMaterial = scene->GetMaterial(i); - auto materialName = std::string(lMaterial->GetName()); - auto materialPart = std::string(); - - size_t materialNameSplitPosition = materialName.find('#'); - if (materialNameSplitPosition != std::string::npos) - { - materialPart = materialName.substr(materialNameSplitPosition + 1); - materialName = materialName.substr(0, materialNameSplitPosition); - } + { // The diffuse color + auto diffuseTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor, finalMaterial); + if (lambertSurface || diffuseTree != nullptr) + { + if (diffuseTree == nullptr) + { + auto diffuseColor = lambertSurface->Diffuse.Get(); + auto diffuseFactor = lambertSurface->DiffuseFactor.Get(); + auto diffuseColorValue = diffuseFactor * diffuseColor; - materialNameSplitPosition = materialName.find("__"); - if (materialNameSplitPosition != std::string::npos) - { - materialPart = materialName.substr(materialNameSplitPosition + 2); - materialName = materialName.substr(0, materialNameSplitPosition); - } - - // remove all bad characters - ReplaceCharacter(materialName, ':', '_'); - ReplaceCharacter(materialName, '/', '_'); - RemoveCharacter(materialName, ' '); - tempNames[lMaterial] = materialName; - - if (materialNameTotalCount.count(materialName) == 0) - materialNameTotalCount[materialName] = 1; - else - materialNameTotalCount[materialName] = materialNameTotalCount[materialName] + 1; - } + // Create diffuse value even if the color is black + diffuseTree = gcnew ComputeColor(FbxDouble3ToColor4(diffuseColorValue)); + } - for (int i = 0; i < materialCount; i++) - { - auto lMaterial = scene->GetMaterial(i); - auto materialName = tempNames[lMaterial]; - int currentCount = 0; + if (diffuseTree != nullptr) + { + finalMaterial->Attributes->Diffuse = gcnew MaterialDiffuseMapFeature(diffuseTree); + finalMaterial->Attributes->DiffuseModel = gcnew MaterialDiffuseLambertModelFeature(); + } + } + } + { // The emissive color + auto emissiveTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor, finalMaterial); + if (lambertSurface || emissiveTree != nullptr) + { + if (emissiveTree == nullptr) + { + auto emissiveColor = lambertSurface->Emissive.Get(); + auto emissiveFactor = lambertSurface->EmissiveFactor.Get(); + auto emissiveColorValue = emissiveFactor * emissiveColor; + + // Do not create the node if the value has not been explicitly specified by the user. + if (emissiveColorValue != FbxDouble3(0)) + { + emissiveTree = gcnew ComputeColor(FbxDouble3ToColor4(emissiveColorValue)); + } + } - if (materialNameCurrentCount.count(materialName) == 0) - materialNameCurrentCount[materialName] = 1; - else - materialNameCurrentCount[materialName] = materialNameCurrentCount[materialName] + 1; + if (emissiveTree != nullptr) + { + finalMaterial->Attributes->Emissive = gcnew MaterialEmissiveMapFeature(emissiveTree); + } + } + } + // TODO: Check if we want to support Ambient Color + //{ // The ambient color + // auto ambientTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor, finalMaterial); + // if(lambertSurface || ambientTree != nullptr) + // { + // if(ambientTree == nullptr) + // { + // auto ambientColor = lambertSurface->Emissive.Get(); + // auto ambientFactor = lambertSurface->EmissiveFactor.Get(); + // auto ambientColorValue = ambientFactor * ambientColor; + + // // Do not create the node if the value has not been explicitly specified by the user. + // if(ambientColorValue != FbxDouble3(0)) + // { + // ambientTree = gcnew ComputeColor(FbxDouble3ToColor4(ambientColorValue)); + // } + // } + + // if(ambientTree != nullptr) + // finalMaterial->AddColorNode(MaterialParameters::AmbientMap, "ambient", ambientTree); + // } + //} + { // The normal map + auto normalMapTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sNormalMap, NULL, finalMaterial); + if (lambertSurface || normalMapTree != nullptr) + { + if (normalMapTree == nullptr) + { + auto normalMapValue = lambertSurface->NormalMap.Get(); - if(materialNameTotalCount[materialName] > 1) - materialName = materialName + "_" + std::to_string(materialNameCurrentCount[materialName]); + // Do not create the node if the value has not been explicitly specified by the user. + if (normalMapValue != FbxDouble3(0)) + { + normalMapTree = gcnew ComputeFloat4(FbxDouble3ToVector4(normalMapValue)); + } + } - materialNames[lMaterial] = materialName; - } - } + if (normalMapTree != nullptr) + { + finalMaterial->Attributes->Surface = gcnew MaterialNormalMapFeature(normalMapTree); + } + } + } + // TODO: Support for BumpMap + //{ // The bump map + // auto bumpMapTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sBump, FbxSurfaceMaterial::sBumpFactor, finalMaterial); + // if(lambertSurface || bumpMapTree != nullptr) + // { + // if(bumpMapTree == nullptr) + // { + // auto bumpValue = lambertSurface->Bump.Get(); + // auto bumpFactor = lambertSurface->BumpFactor.Get(); + // auto bumpMapValue = bumpFactor * bumpValue; + + // // Do not create the node if the value has not been explicitly specified by the user. + // if(bumpMapValue != FbxDouble3(0)) + // { + // bumpMapTree = gcnew MaterialFloat4ComputeColor(FbxDouble3ToVector4(bumpMapValue)); + // } + // } + // + // if (bumpMapTree != nullptr) + // { + // finalMaterial->AddColorNode(MaterialParameters::BumpMap, "bumpMap", bumpMapTree); + // } + // } + //} + // TODO: Support for Transparency + //{ // The transparency + // auto transparencyTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor, finalMaterial); + // if(lambertSurface || transparencyTree != nullptr) + // { + // if(transparencyTree == nullptr) + // { + // auto transparencyColor = lambertSurface->TransparentColor.Get(); + // auto transparencyFactor = lambertSurface->TransparencyFactor.Get(); + // auto transparencyValue = transparencyFactor * transparencyColor; + // auto opacityValue = std::min(1.0f, std::max(0.0f, 1-(float)transparencyValue[0])); + + // // Do not create the node if the value has not been explicitly specified by the user. + // if(opacityValue < 1) + // { + // transparencyTree = gcnew MaterialFloatComputeColor(opacityValue); + // } + // } + + // if(transparencyTree != nullptr) + // finalMaterial->AddColorNode(MaterialParameters::TransparencyMap, "transparencyMap", transparencyTree); + // } + //} + //// TODO: Support for displacement map + //{ // The displacement map + // auto displacementColorTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sDisplacementColor, FbxSurfaceMaterial::sDisplacementFactor, finalMaterial); + // if(lambertSurface || displacementColorTree != nullptr) + // { + // if(displacementColorTree == nullptr) + // { + // auto displacementColor = lambertSurface->DisplacementColor.Get(); + // auto displacementFactor = lambertSurface->DisplacementFactor.Get(); + // auto displacementValue = displacementFactor * displacementColor; + + // // Do not create the node if the value has not been explicitly specified by the user. + // if(displacementValue != FbxDouble3(0)) + // { + // displacementColorTree = gcnew MaterialFloat4ComputeColor(FbxDouble3ToVector4(displacementValue)); + // } + // } + // + // if(displacementColorTree != nullptr) + // finalMaterial->AddColorNode(MaterialParameters::DisplacementMap, "displacementMap", displacementColorTree); + // } + //} + { // The specular color + auto specularTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sSpecular, NULL, finalMaterial); + if (phongSurface || specularTree != nullptr) + { + if (specularTree == nullptr) + { + auto specularColor = phongSurface->Specular.Get(); - void GetMeshes(FbxNode* pNode, std::vector& meshes) - { - // Process the node's attributes. - for(int i = 0; i < pNode->GetNodeAttributeCount(); i++) - { - auto pAttribute = pNode->GetNodeAttributeByIndex(i); + // Do not create the node if the value has not been explicitly specified by the user. + if (specularColor != FbxDouble3(0)) + { + specularTree = gcnew ComputeColor(FbxDouble3ToColor4(specularColor)); + } + } - if(!pAttribute) return; - - if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) - { - auto pMesh = (FbxMesh*)pAttribute; - meshes.push_back(pMesh); - } - } + if (specularTree != nullptr) + { + auto specularFeature = gcnew MaterialSpecularMapFeature(); + specularFeature->SpecularMap = specularTree; + finalMaterial->Attributes->Specular = specularFeature; - // Recursively process the children nodes. - for(int j = 0; j < pNode->GetChildCount(); j++) - { - GetMeshes(pNode->GetChild(j), meshes); - } - } - - void GenerateMeshesName(std::map& meshNames) - { - std::vector meshes; - GetMeshes(scene->GetRootNode(), meshes); - - std::map meshNameTotalCount; - std::map meshNameCurrentCount; - std::map tempNames; - - for (auto iter = meshes.begin(); iter != meshes.end(); ++iter) - { - auto pMesh = *iter; - auto meshName = std::string(pMesh->GetNode()->GetName()); - - // remove all bad characters - RemoveCharacter(meshName, ' '); - tempNames[pMesh] = meshName; - - if (meshNameTotalCount.count(meshName) == 0) - meshNameTotalCount[meshName] = 1; - else - meshNameTotalCount[meshName] = meshNameTotalCount[meshName] + 1; - } + auto specularModel = gcnew MaterialSpecularMicrofacetModelFeature(); + specularModel->Fresnel = gcnew MaterialSpecularMicrofacetFresnelSchlick(); + specularModel->Visibility = gcnew MaterialSpecularMicrofacetVisibilityImplicit(); + specularModel->NormalDistribution = gcnew MaterialSpecularMicrofacetNormalDistributionBlinnPhong(); - for (auto iter = meshes.begin(); iter != meshes.end(); ++iter) - { - auto pMesh = *iter; - auto meshName = tempNames[pMesh]; - int currentCount = 0; + finalMaterial->Attributes->SpecularModel = specularModel; + } + } + } + // TODO REPLUG SPECULAR INTENSITY + //{ // The specular intensity map + // auto specularIntensityTree = (IComputeColor^)GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sSpecularFactor, NULL, finalMaterial); + // if(phongSurface || specularIntensityTree != nullptr) + // { + // if(specularIntensityTree == nullptr) + // { + // auto specularIntensity = phongSurface->SpecularFactor.Get(); + // + // // Do not create the node if the value has not been explicitly specified by the user. + // if(specularIntensity > 0) + // { + // specularIntensityTree = gcnew MaterialFloatComputeNode((float)specularIntensity); + // } + // } + // + // if (specularIntensityTree != nullptr) + // { + // MaterialSpecularMapFeature^ specularFeature; + // if (finalMaterial->Attributes->Specular == nullptr || finalMaterial->Attributes->Specular->GetType() != MaterialSpecularMapFeature::typeid) + // { + // specularFeature = gcnew MaterialSpecularMapFeature(); + // } + // else + // { + // specularFeature = (MaterialSpecularMapFeature^)finalMaterial->Attributes->Specular; + // } + // // TODO: Check Specular Intensity and Power + // specularFeature->Intensity = specularIntensityTree; + // finalMaterial->Attributes->Specular = specularFeature; + // } + // } + // } + /* { // The specular power map + auto specularPowerTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sShininess, NULL, finalMaterial); + if(phongSurface || specularPowerTree != nullptr) + { + if(specularPowerTree == nullptr) + { + auto specularPower = phongSurface->Shininess.Get(); - if (meshNameCurrentCount.count(meshName) == 0) - meshNameCurrentCount[meshName] = 1; - else - meshNameCurrentCount[meshName] = meshNameCurrentCount[meshName] + 1; + // Do not create the node if the value has not been explicitly specified by the user. + if(specularPower > 0) + { + specularPowerTree = gcnew MaterialFloatComputeColor((float)specularPower); + } + } - if(meshNameTotalCount[meshName] > 1) - meshName = meshName + "_" + std::to_string(meshNameCurrentCount[meshName]); + if (specularPowerTree != nullptr) + { + MaterialSpecularMapFeature^ specularFeature; + if (finalMaterial->Attributes->Specular == nullptr || finalMaterial->Attributes->Specular->GetType() != MaterialSpecularMapFeature::typeid) + { + specularFeature = gcnew MaterialSpecularMapFeature(); + } + else + { + specularFeature = (MaterialSpecularMapFeature^)finalMaterial->Attributes->Specular; + } + // TODO: Check Specular Intensity and Power + specularFeature->Intensity = specularPowerTree; + finalMaterial->Attributes->Specular = specularFeature; + } + } + }*/ + //// TODO: Support for reflection map + //{ // The reflection map + // auto reflectionMapTree = GenerateSurfaceTextureTree(lMaterial, uvEltMappingOverride, textureMap, textureNameCount, FbxSurfaceMaterial::sReflection, FbxSurfaceMaterial::sReflectionFactor, finalMaterial); + // if(phongSurface || reflectionMapTree != nullptr) + // { + // if(reflectionMapTree == nullptr) + // { + // auto reflectionColor = lambertSurface->DisplacementColor.Get(); + // auto reflectionFactor = lambertSurface->DisplacementFactor.Get(); + // auto reflectionValue = reflectionFactor * reflectionColor; + + // // Do not create the node if the value has not been explicitly specified by the user. + // if(reflectionValue != FbxDouble3(0)) + // { + // reflectionMapTree = gcnew ComputeColor(FbxDouble3ToColor4(reflectionValue)); + // } + // } + // + // if(reflectionMapTree != nullptr) + // finalMaterial->AddColorNode(MaterialParameters::ReflectionMap, "reflectionMap", reflectionMapTree); + // } + //} + return finalMaterial; + } - meshNames[pMesh] = meshName; - } - } + bool IsTransparent(FbxSurfaceMaterial* lMaterial) + { + for (int i = 0; i < 2; ++i) + { + auto propertyName = i == 0 ? FbxSurfaceMaterial::sTransparentColor : FbxSurfaceMaterial::sTransparencyFactor; + if (propertyName == NULL) + continue; - MaterialInstantiation^ GetOrCreateMaterial(FbxSurfaceMaterial* lMaterial, List^ uvNames, List^ instances, std::map& uvElements, std::map& materialNames) - { - for (int i = 0; i < instances->Count; ++i) - { - if (lMaterial == instances[i]->SourceMaterial) - return instances[i]; - } + FbxProperty lProperty = lMaterial->FindProperty(propertyName); + if (lProperty.IsValid()) + { + const int lTextureCount = lProperty.GetSrcObjectCount(); + for (int j = 0; j < lTextureCount; ++j) + { + FbxLayeredTexture* lLayeredTexture = FbxCast(lProperty.GetSrcObject(j)); + FbxFileTexture* lFileTexture = FbxCast(lProperty.GetSrcObject(j)); + if (lLayeredTexture) + { + int lNbTextures = lLayeredTexture->GetSrcObjectCount(); + if (lNbTextures > 0) + return true; + } + else if (lFileTexture) + return true; + } + if (lTextureCount == 0) + { + auto val = FbxDouble3ToVector3(lProperty.Get()); + if (val == Vector3::Zero || val != Vector3::One) + return true; + } + } + } + return false; + } - auto newMaterialInstantiation = gcnew MaterialInstantiation(); - newMaterialInstantiation->SourceMaterial = lMaterial; - newMaterialInstantiation->MaterialName = gcnew String(materialNames[lMaterial].c_str()); + IComputeNode^ GenerateSurfaceTextureTree(FbxSurfaceMaterial* lMaterial, std::map& uvElementMapping, Dictionary^ textureMap, + std::map& textureNameCount, char const* surfaceMaterial, char const* surfaceMaterialFactor, - // TODO: We currently use UV mapping of first requesting mesh. - // However, we probably need to reverse everything: mesh describes what they have, materials what they need, and an appropriate input layout is created at runtime? - // Such a mechanism would also be able to handle missing streams gracefully. - newMaterialInstantiation->Material = ProcessMeshMaterialAsset(lMaterial, uvElements); - instances->Add(newMaterialInstantiation); - return newMaterialInstantiation; - } - void SearchMeshInAttribute(FbxNode* pNode, FbxNodeAttribute* pAttribute, std::map materialNames, std::map meshNames, List^ models, List^ materialInstantiations) - { - if(!pAttribute) return; - - if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) - { - auto pMesh = (FbxMesh*)pAttribute; - int polygonCount = pMesh->GetPolygonCount(); - FbxGeometryElement::EMappingMode materialMappingMode = FbxGeometryElement::eNone; - FbxLayerElementArrayTemplate* materialIndices = NULL; - - if (pMesh->GetElementMaterial()) - { - materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode(); - materialIndices = &pMesh->GetElementMaterial()->GetIndexArray(); - } + Stride::Assets::Materials::MaterialAsset^ finalMaterial) + { + auto compositionTrees = gcnew cli::array(2); + + for (int i = 0; i < 2; ++i) + { + // Scan first for component name, then its factor (i.e. sDiffuse, then sDiffuseFactor) + auto propertyName = i == 0 ? surfaceMaterial : surfaceMaterialFactor; + if (propertyName == NULL) + continue; - auto buildMeshes = gcnew List(); + int compositionCount = 0; - // Count polygon per materials - for (int i = 0; i < polygonCount; i++) - { - int materialIndex = 0; - if (materialMappingMode == FbxGeometryElement::eByPolygon) + FbxProperty lProperty = lMaterial->FindProperty(propertyName); + if (lProperty.IsValid()) + { + IComputeColor^ previousNode = nullptr; + const int lTextureCount = lProperty.GetSrcObjectCount(); + for (int j = 0; j < lTextureCount; ++j) + { + FbxLayeredTexture* lLayeredTexture = FbxCast(lProperty.GetSrcObject(j)); + FbxFileTexture* lFileTexture = FbxCast(lProperty.GetSrcObject(j)); + if (lLayeredTexture) + { + int lNbTextures = lLayeredTexture->GetSrcObjectCount(); + for (int k = 0; k < lNbTextures; ++k) + { + FbxFileTexture* lSubTexture = FbxCast(lLayeredTexture->GetSrcObject(k)); + + auto uvName = std::string(lSubTexture->UVSet.Get()); + if (uvElementMapping.find(uvName) == uvElementMapping.end()) + uvElementMapping[uvName] = uvElementMapping.size(); + + auto currentMaterialReference = GenerateMaterialTextureNodeFBX(lSubTexture, uvElementMapping, textureMap, textureNameCount, finalMaterial); + + if (lNbTextures == 1 || compositionCount == 0) + { + if (previousNode == nullptr) + previousNode = currentMaterialReference; + else + previousNode = gcnew ComputeBinaryColor(previousNode, currentMaterialReference, BinaryOperator::Add); // not sure + } + else + { + auto newNode = gcnew ComputeBinaryColor(previousNode, currentMaterialReference, BinaryOperator::Add); + previousNode = newNode; + + FbxLayeredTexture::EBlendMode blendMode; + lLayeredTexture->GetTextureBlendMode(k, blendMode); + newNode->Operator = BlendModeToBlendOperand(blendMode); + } + + compositionCount++; + } + } + else if (lFileTexture) + { + compositionCount++; + + auto newMaterialReference = GenerateMaterialTextureNodeFBX(lFileTexture, uvElementMapping, textureMap, textureNameCount, finalMaterial); + + if (previousNode == nullptr) + previousNode = newMaterialReference; + else + previousNode = gcnew ComputeBinaryColor(previousNode, newMaterialReference, BinaryOperator::Add); // not sure + } + } + + compositionTrees[i] = previousNode; + } + } + + // If we only have one of either Color or Factor, use directly, otherwise multiply them together + IComputeColor^ compositionTree; + if (compositionTrees[0] == nullptr) // TODO do we want only the factor??? -> delete + { + compositionTree = compositionTrees[1]; + } + else if (compositionTrees[1] == nullptr) + { + compositionTree = compositionTrees[0]; + } + else + { + compositionTree = gcnew ComputeBinaryColor(compositionTrees[0], compositionTrees[1], BinaryOperator::Multiply); + } + + return compositionTree; + } + + BinaryOperator BlendModeToBlendOperand(FbxLayeredTexture::EBlendMode blendMode) { - materialIndex = materialIndices->GetAt(i); + switch (blendMode) + { + case FbxLayeredTexture::eOver: + return BinaryOperator::Over; + case FbxLayeredTexture::eAdditive: + return BinaryOperator::Add; + case FbxLayeredTexture::eModulate: + return BinaryOperator::Multiply; + //case FbxLayeredTexture::eTranslucent: + // return BinaryOperator::Multiply; + //case FbxLayeredTexture::eModulate2: + // return BinaryOperator::Multiply; + //case FbxLayeredTexture::eNormal: + // return BinaryOperator::Multiply; + //case FbxLayeredTexture::eDissolve: + // return BinaryOperator::Multiply; + case FbxLayeredTexture::eDarken: + return BinaryOperator::Darken; + case FbxLayeredTexture::eColorBurn: + return BinaryOperator::ColorBurn; + case FbxLayeredTexture::eLinearBurn: + return BinaryOperator::LinearBurn; + //case FbxLayeredTexture::eDarkerColor: + // return BinaryOperator::Multiply; + case FbxLayeredTexture::eLighten: + return BinaryOperator::Lighten; + case FbxLayeredTexture::eScreen: + return BinaryOperator::Screen; + case FbxLayeredTexture::eColorDodge: + return BinaryOperator::ColorDodge; + case FbxLayeredTexture::eLinearDodge: + return BinaryOperator::LinearDodge; + //case FbxLayeredTexture::eLighterColor: + // return BinaryOperator::Multiply; + case FbxLayeredTexture::eSoftLight: + return BinaryOperator::SoftLight; + case FbxLayeredTexture::eHardLight: + return BinaryOperator::HardLight; + //case FbxLayeredTexture::eVividLight: + // return BinaryOperator::Multiply; + //case FbxLayeredTexture::eLinearLight: + // return BinaryOperator::Multiply; + case FbxLayeredTexture::ePinLight: + return BinaryOperator::PinLight; + case FbxLayeredTexture::eHardMix: + return BinaryOperator::HardMix; + case FbxLayeredTexture::eDifference: + return BinaryOperator::Difference; + case FbxLayeredTexture::eExclusion: + return BinaryOperator::Exclusion; + case FbxLayeredTexture::eSubtract: + return BinaryOperator::Subtract; + case FbxLayeredTexture::eDivide: + return BinaryOperator::Divide; + case FbxLayeredTexture::eHue: + return BinaryOperator::Hue; + case FbxLayeredTexture::eSaturation: + return BinaryOperator::Saturation; + //case FbxLayeredTexture::eColor: + // return BinaryOperator::Multiply; + //case FbxLayeredTexture::eLuminosity: + // return BinaryOperator::Multiply; + case FbxLayeredTexture::eOverlay: + return BinaryOperator::Overlay; + default: + logger->Error(String::Format("Material blending mode '{0}' is not supported yet. Multiplying blending mode will be used instead.", gcnew Int32(blendMode)), (CallerInfo^)nullptr); + return BinaryOperator::Multiply; + } } - else if (materialMappingMode == FbxGeometryElement::eAllSame) + + ShaderClassSource^ GenerateTextureLayerFBX(FbxFileTexture* lFileTexture, std::map& uvElementMapping, Mesh^ meshData, int& textureCount, ParameterKey^ surfaceMaterialKey) { - materialIndex = materialIndices->GetAt(0); + auto texScale = lFileTexture->GetUVScaling(); + auto texturePath = FindFilePath(lFileTexture); + + return TextureLayerGenerator::GenerateTextureLayer(vfsOutputFilename, texturePath, uvElementMapping[std::string(lFileTexture->UVSet.Get())], Vector2((float)texScale[0], (float)texScale[1]), + textureCount, surfaceMaterialKey, + meshData, + nullptr); } - // Equivalent to std::vector::resize() - while (materialIndex >= buildMeshes->Count) + String^ FindFilePath(FbxFileTexture* lFileTexture) { - buildMeshes->Add(nullptr); + auto relFileName = gcnew String(lFileTexture->GetRelativeFileName()); + auto absFileName = gcnew String(lFileTexture->GetFileName()); + + // First try to get the texture filename by relative path, if not valid then use absolute path + // (According to FBX doc, resolved first by absolute name, and relative name if absolute name is not valid) + auto fileNameToUse = Path::Combine(inputPath, relFileName); + if (fileNameToUse->StartsWith("\\\\", StringComparison::Ordinal)) + { + logger->Warning(String::Format("Importer detected a network address in referenced assets. This may temporary block the build if the file does not exist. [Address='{0}']", fileNameToUse), (CallerInfo^)nullptr); + } + if (!File::Exists(fileNameToUse) && !String::IsNullOrEmpty(absFileName)) + { + fileNameToUse = absFileName; + } + + // Make sure path is absolute + if (!(gcnew UFile(fileNameToUse))->IsAbsolute) + { + fileNameToUse = Path::Combine(inputPath, fileNameToUse); + } + + return fileNameToUse; } - if (buildMeshes[materialIndex] == nullptr) - buildMeshes[materialIndex] = gcnew BuildMesh(); + ComputeTextureColor^ GenerateMaterialTextureNodeFBX(FbxFileTexture* lFileTexture, std::map& uvElementMapping, Dictionary^ textureMap, std::map& textureNameCount, Stride::Assets::Materials::MaterialAsset^ finalMaterial) + { + auto texScale = lFileTexture->GetUVScaling(); + auto texturePath = FindFilePath(lFileTexture); + auto wrapModeU = lFileTexture->GetWrapModeU(); + auto wrapModeV = lFileTexture->GetWrapModeV(); + auto wrapTextureU = (wrapModeU == FbxTexture::EWrapMode::eRepeat) ? TextureAddressMode::Wrap : TextureAddressMode::Clamp; + auto wrapTextureV = (wrapModeV == FbxTexture::EWrapMode::eRepeat) ? TextureAddressMode::Wrap : TextureAddressMode::Clamp; - int polygonSize = pMesh->GetPolygonSize(i) - 2; - if (polygonSize > 0) - buildMeshes[materialIndex]->polygonCount += polygonSize; - } + ComputeTextureColor^ textureValue; - for (int i = 0; i < buildMeshes->Count; ++i) - { - auto meshParams = gcnew MeshParameters(); - auto meshName = meshNames[pMesh]; - if (buildMeshes->Count > 1) - meshName = meshName + "_" + std::to_string(i + 1); - meshParams->MeshName = gcnew String(meshName.c_str()); - meshParams->NodeName = sceneMapping->FindNode(pNode).Name; - - // Collect bones - int skinDeformerCount = pMesh->GetDeformerCount(FbxDeformer::eSkin); - if (skinDeformerCount > 0) + if (textureMap->TryGetValue(IntPtr(lFileTexture), textureValue)) + { + return textureValue; + } + else + { + textureValue = TextureLayerGenerator::GenerateMaterialTextureNode(vfsOutputFilename, texturePath, uvElementMapping[std::string(lFileTexture->UVSet.Get())], Vector2((float)texScale[0], (float)texScale[1]), wrapTextureU, wrapTextureV, nullptr); + + auto attachedReference = AttachedReferenceManager::GetAttachedReference(textureValue->Texture); + + auto textureNamePtr = Marshal::StringToHGlobalAnsi(attachedReference->Url); + std::string textureName = std::string((char*)textureNamePtr.ToPointer()); + Marshal::FreeHGlobal(textureNamePtr); + + auto textureCount = GetTextureNameCount(textureNameCount, textureName); + if (textureCount > 1) + textureName = textureName + "_" + std::to_string(textureCount - 1); + + auto referenceName = gcnew String(textureName.c_str()); + //auto materialReference = gcnew MaterialReferenceNode(referenceName); + //finalMaterial->AddNode(referenceName, textureValue); + textureMap[IntPtr(lFileTexture)] = textureValue; + return textureValue; + } + + return nullptr; + } + + int GetTextureNameCount(std::map& textureNameCount, std::string textureName) + { + auto textureFound = textureNameCount.find(textureName); + if (textureFound == textureNameCount.end()) + textureNameCount[textureName] = 1; + else + textureNameCount[textureName] = textureNameCount[textureName] + 1; + return textureNameCount[textureName]; + } + + void ProcessAttribute(FbxNode* pNode, FbxNodeAttribute* pAttribute, std::map meshNames, std::map materials) + { + if (!pAttribute) return; + + if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) + { + ProcessMesh((FbxMesh*)pAttribute, meshNames, materials); + } + } + + void ProcessNodeTransformation(FbxNode* pNode) + { + auto nodeIndex = sceneMapping->FindNodeIndex(pNode); + auto nodes = sceneMapping->Nodes; + auto node = &nodes[nodeIndex]; + + // Use GlobalTransform instead of LocalTransform + + auto fbxMatrix = pNode->EvaluateLocalTransform(FBXSDK_TIME_ZERO); + auto matrix = sceneMapping->ConvertMatrixFromFbx(fbxMatrix); + + // Extract the translation and scaling + Vector3 translation; + Quaternion rotation; + Vector3 scaling; + matrix.Decompose(scaling, rotation, translation); + + // Apply rotation on top level nodes only + if (node->ParentIndex == 0) + { + Vector3::TransformCoordinate(translation, sceneMapping->AxisSystemRotationMatrix, translation); + rotation = Quaternion::Multiply(rotation, Quaternion::RotationMatrix(sceneMapping->AxisSystemRotationMatrix)); + } + + // Setup the transform for this node + node->Transform.Position = translation; + node->Transform.Rotation = rotation; + node->Transform.Scale = scaling; + + // Recursively process the children nodes. + for (int j = 0; j < pNode->GetChildCount(); j++) + { + ProcessNodeTransformation(pNode->GetChild(j)); + } + } + + void ProcessNodeAttributes(FbxNode* pNode, std::map meshNames, std::map materials) + { + // Process the node's attributes. + for (int i = 0; i < pNode->GetNodeAttributeCount(); i++) + ProcessAttribute(pNode, pNode->GetNodeAttributeByIndex(i), meshNames, materials); + + // Recursively process the children nodes. + for (int j = 0; j < pNode->GetChildCount(); j++) + { + ProcessNodeAttributes(pNode->GetChild(j), meshNames, materials); + } + } + + ref class BuildMesh + { + public: + array^ buffer; + int bufferOffset; + int polygonCount; + }; + + ref struct ImportConfiguration + { + public: + property bool ImportTemplates; + property bool ImportPivots; + property bool ImportGlobalSettings; + property bool ImportCharacters; + property bool ImportConstraints; + property bool ImportGobos; + property bool ImportShapes; + property bool ImportLinks; + property bool ImportMaterials; + property bool ImportTextures; + property bool ImportModels; + property bool ImportAnimations; + property bool ExtractEmbeddedData; + + public: + static ImportConfiguration^ ImportAll() + { + auto config = gcnew ImportConfiguration(); + + config->ImportTemplates = true; + config->ImportPivots = true; + config->ImportGlobalSettings = true; + config->ImportCharacters = true; + config->ImportConstraints = true; + config->ImportGobos = true; + config->ImportShapes = true; + config->ImportLinks = true; + config->ImportMaterials = true; + config->ImportTextures = true; + config->ImportModels = true; + config->ImportAnimations = true; + config->ExtractEmbeddedData = true; + + return config; + } + + static ImportConfiguration^ ImportModelOnly() + { + auto config = gcnew ImportConfiguration(); + + config->ImportTemplates = false; + config->ImportPivots = false; + config->ImportGlobalSettings = true; + config->ImportCharacters = false; + config->ImportConstraints = false; + config->ImportGobos = false; + config->ImportShapes = false; + config->ImportLinks = false; + config->ImportMaterials = true; + config->ImportTextures = false; + config->ImportModels = true; + config->ImportAnimations = false; + config->ExtractEmbeddedData = false; + + return config; + } + + static ImportConfiguration^ ImportMaterialsOnly() + { + auto config = gcnew ImportConfiguration(); + + config->ImportTemplates = false; + config->ImportPivots = false; + config->ImportGlobalSettings = true; + config->ImportCharacters = false; + config->ImportConstraints = false; + config->ImportGobos = false; + config->ImportShapes = false; + config->ImportLinks = false; + config->ImportMaterials = true; + config->ImportTextures = false; + config->ImportModels = false; + config->ImportAnimations = false; + config->ExtractEmbeddedData = false; + + return config; + } + + static ImportConfiguration^ ImportAnimationsOnly() + { + auto config = gcnew ImportConfiguration(); + + config->ImportTemplates = false; + config->ImportPivots = false; + config->ImportGlobalSettings = true; + config->ImportCharacters = false; + config->ImportConstraints = false; + config->ImportGobos = false; + config->ImportShapes = false; + config->ImportLinks = false; + config->ImportMaterials = false; + config->ImportTextures = false; + config->ImportModels = false; + config->ImportAnimations = true; + config->ExtractEmbeddedData = false; + + return config; + } + + static ImportConfiguration^ ImportSkeletonOnly() + { + auto config = gcnew ImportConfiguration(); + + config->ImportTemplates = false; + config->ImportPivots = false; + config->ImportGlobalSettings = true; + config->ImportCharacters = false; + config->ImportConstraints = false; + config->ImportGobos = false; + config->ImportShapes = false; + config->ImportLinks = false; + config->ImportMaterials = false; + config->ImportTextures = false; + config->ImportModels = false; + config->ImportAnimations = false; + config->ExtractEmbeddedData = false; + + return config; + } + + static ImportConfiguration^ ImportTexturesOnly() + { + auto config = gcnew ImportConfiguration(); + + config->ImportTemplates = false; + config->ImportPivots = false; + config->ImportGlobalSettings = false; + config->ImportCharacters = false; + config->ImportConstraints = false; + config->ImportGobos = false; + config->ImportShapes = false; + config->ImportLinks = false; + config->ImportMaterials = false; + config->ImportTextures = true; + config->ImportModels = false; + config->ImportAnimations = false; + config->ExtractEmbeddedData = true; + + return config; + } + + static ImportConfiguration^ ImportEntityConfig() + { + auto config = gcnew ImportConfiguration(); + + config->ImportTemplates = false; + config->ImportPivots = false; + config->ImportGlobalSettings = true; + config->ImportCharacters = false; + config->ImportConstraints = false; + config->ImportGobos = false; + config->ImportShapes = false; + config->ImportLinks = false; + config->ImportMaterials = true; + config->ImportTextures = true; + config->ImportModels = true; + config->ImportAnimations = true; + config->ExtractEmbeddedData = true; + + return config; + } + + static ImportConfiguration^ ImportGlobalSettingsOnly() + { + auto config = gcnew ImportConfiguration(); + + config->ImportGlobalSettings = true; + + return config; + } + }; + + private: + static System::Object^ globalLock = gcnew System::Object(); + + void Initialize(String^ inputFilename, String^ vfsOutputFilename, ImportConfiguration^ importConfig) + { + // ----------------------------------------------------- + // TODO: Workaround with FBX SDK not being multithreaded. + // We protect the whole usage of this class with a monitor + // + // Lock the whole class between Initialize/Destroy + // ----------------------------------------------------- + System::Threading::Monitor::Enter(globalLock); + // ----------------------------------------------------- + + this->inputFilename = inputFilename; + this->vfsOutputFilename = vfsOutputFilename; + this->inputPath = Path::GetDirectoryName(inputFilename); + + // Initialize the sdk manager. This object handles all our memory management. + lSdkManager = FbxManager::Create(); + + // Create the io settings object. + FbxIOSettings* ios = FbxIOSettings::Create(lSdkManager, IOSROOT); + ios->SetBoolProp(IMP_FBX_TEMPLATE, importConfig->ImportTemplates); + ios->SetBoolProp(IMP_FBX_PIVOT, importConfig->ImportPivots); + ios->SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, importConfig->ImportGlobalSettings); + ios->SetBoolProp(IMP_FBX_CHARACTER, importConfig->ImportCharacters); + ios->SetBoolProp(IMP_FBX_CONSTRAINT, importConfig->ImportConstraints); + ios->SetBoolProp(IMP_FBX_GOBO, importConfig->ImportGobos); + ios->SetBoolProp(IMP_FBX_SHAPE, importConfig->ImportShapes); + ios->SetBoolProp(IMP_FBX_LINK, importConfig->ImportLinks); + ios->SetBoolProp(IMP_FBX_MATERIAL, importConfig->ImportMaterials); + ios->SetBoolProp(IMP_FBX_TEXTURE, importConfig->ImportTextures); + ios->SetBoolProp(IMP_FBX_MODEL, importConfig->ImportModels); + ios->SetBoolProp(IMP_FBX_ANIMATION, importConfig->ImportAnimations); + ios->SetBoolProp(IMP_FBX_EXTRACT_EMBEDDED_DATA, importConfig->ExtractEmbeddedData); + lSdkManager->SetIOSettings(ios); + + // Create an importer using our sdk manager. + lImporter = FbxImporter::Create(lSdkManager, ""); + + auto inputFilenameUtf8 = System::Text::Encoding::UTF8->GetBytes(inputFilename); + pin_ptr inputFilenameUtf8Ptr = &inputFilenameUtf8[0]; + + if (!lImporter->Initialize((const char*)inputFilenameUtf8Ptr, -1, lSdkManager->GetIOSettings())) + { + throw gcnew InvalidOperationException(String::Format("Call to FbxImporter::Initialize() failed.\n" + "Error returned: {0}\n\n", gcnew String(lImporter->GetStatus().GetErrorString()))); + } + + // Create a new scene so it can be populated by the imported file. + scene = FbxScene::Create(lSdkManager, "myScene"); + + // Import the contents of the file into the scene. + lImporter->Import(scene); + + const float framerate = static_cast(FbxTime::GetFrameRate(scene->GetGlobalSettings().GetTimeMode())); + scene->GetRootNode()->ResetPivotSetAndConvertAnimation(framerate, false, false); + + // Initialize the node mapping + sceneMapping = gcnew SceneMapping(scene); + } + + bool HasAnimationData(String^ inputFile) + { + try + { + Initialize(inputFile, nullptr, ImportConfiguration::ImportAnimationsOnly()); + auto animConverter = gcnew AnimationConverter(logger, sceneMapping); + return animConverter->HasAnimationData(); + } + finally + { + Destroy(); + } + } + + void GenerateMaterialNames(std::map& materialNames) + { + auto materials = gcnew List(); + std::map materialNameTotalCount; + std::map materialNameCurrentCount; + std::map tempNames; + auto materialCount = scene->GetMaterialCount(); + + for (int i = 0; i < materialCount; i++) + { + auto lMaterial = scene->GetMaterial(i); + auto materialName = std::string(lMaterial->GetName()); + auto materialPart = std::string(); + + size_t materialNameSplitPosition = materialName.find('#'); + if (materialNameSplitPosition != std::string::npos) + { + materialPart = materialName.substr(materialNameSplitPosition + 1); + materialName = materialName.substr(0, materialNameSplitPosition); + } + + materialNameSplitPosition = materialName.find("__"); + if (materialNameSplitPosition != std::string::npos) + { + materialPart = materialName.substr(materialNameSplitPosition + 2); + materialName = materialName.substr(0, materialNameSplitPosition); + } + + // remove all bad characters + ReplaceCharacter(materialName, ':', '_'); + RemoveCharacter(materialName, ' '); + tempNames[lMaterial] = materialName; + + if (materialNameTotalCount.count(materialName) == 0) + materialNameTotalCount[materialName] = 1; + else + materialNameTotalCount[materialName] = materialNameTotalCount[materialName] + 1; + } + + for (int i = 0; i < materialCount; i++) + { + auto lMaterial = scene->GetMaterial(i); + auto materialName = tempNames[lMaterial]; + int currentCount = 0; + + if (materialNameCurrentCount.count(materialName) == 0) + materialNameCurrentCount[materialName] = 1; + else + materialNameCurrentCount[materialName] = materialNameCurrentCount[materialName] + 1; + + if (materialNameTotalCount[materialName] > 1) + materialName = materialName + "_" + std::to_string(materialNameCurrentCount[materialName]); + + materialNames[lMaterial] = materialName; + } + } + + void GetMeshes(FbxNode* pNode, std::vector& meshes) { - meshParams->BoneNodes = gcnew HashSet(); - for (int deformerIndex = 0; deformerIndex < skinDeformerCount; deformerIndex++) + // Process the node's attributes. + for (int i = 0; i < pNode->GetNodeAttributeCount(); i++) { - FbxSkin* skin = FbxCast(pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); + auto pAttribute = pNode->GetNodeAttributeByIndex(i); + + if (!pAttribute) return; - auto totalClusterCount = skin->GetClusterCount(); - for (int clusterIndex = 0; clusterIndex < totalClusterCount; ++clusterIndex) + if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) { - FbxCluster* cluster = skin->GetCluster(clusterIndex); - int indexCount = cluster->GetControlPointIndicesCount(); - if (indexCount == 0) + auto pMesh = (FbxMesh*)pAttribute; + meshes.push_back(pMesh); + } + } + + // Recursively process the children nodes. + for (int j = 0; j < pNode->GetChildCount(); j++) + { + GetMeshes(pNode->GetChild(j), meshes); + } + } + + void GenerateMeshesName(std::map& meshNames) + { + std::vector meshes; + GetMeshes(scene->GetRootNode(), meshes); + + std::map meshNameTotalCount; + std::map meshNameCurrentCount; + std::map tempNames; + + for (auto iter = meshes.begin(); iter != meshes.end(); ++iter) + { + auto pMesh = *iter; + auto meshName = std::string(pMesh->GetNode()->GetName()); + + // remove all bad characters + RemoveCharacter(meshName, ' '); + tempNames[pMesh] = meshName; + + if (meshNameTotalCount.count(meshName) == 0) + meshNameTotalCount[meshName] = 1; + else + meshNameTotalCount[meshName] = meshNameTotalCount[meshName] + 1; + } + + for (auto iter = meshes.begin(); iter != meshes.end(); ++iter) + { + auto pMesh = *iter; + auto meshName = tempNames[pMesh]; + int currentCount = 0; + + if (meshNameCurrentCount.count(meshName) == 0) + meshNameCurrentCount[meshName] = 1; + else + meshNameCurrentCount[meshName] = meshNameCurrentCount[meshName] + 1; + + if (meshNameTotalCount[meshName] > 1) + meshName = meshName + "_" + std::to_string(meshNameCurrentCount[meshName]); + + meshNames[pMesh] = meshName; + } + } + + MaterialInstantiation^ GetOrCreateMaterial(FbxSurfaceMaterial* lMaterial, List^ uvNames, List^ instances, std::map& uvElements, std::map& materialNames) + { + for (int i = 0; i < instances->Count; ++i) + { + if (lMaterial == instances[i]->SourceMaterial) + return instances[i]; + } + + auto newMaterialInstantiation = gcnew MaterialInstantiation(); + newMaterialInstantiation->SourceMaterial = lMaterial; + newMaterialInstantiation->MaterialName = gcnew String(materialNames[lMaterial].c_str()); + + // TODO: We currently use UV mapping of first requesting mesh. + // However, we probably need to reverse everything: mesh describes what they have, materials what they need, and an appropriate input layout is created at runtime? + // Such a mechanism would also be able to handle missing streams gracefully. + newMaterialInstantiation->Material = ProcessMeshMaterialAsset(lMaterial, uvElements); + instances->Add(newMaterialInstantiation); + return newMaterialInstantiation; + } + + void SearchMeshInAttribute(FbxNode* pNode, FbxNodeAttribute* pAttribute, std::map materialNames, std::map meshNames, List^ models, List^ materialInstantiations) + { + if (!pAttribute) return; + + if (pAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) + { + auto pMesh = (FbxMesh*)pAttribute; + int polygonCount = pMesh->GetPolygonCount(); + FbxGeometryElement::EMappingMode materialMappingMode = FbxGeometryElement::eNone; + FbxLayerElementArrayTemplate* materialIndices = NULL; + + if (pMesh->GetElementMaterial()) + { + materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode(); + materialIndices = &pMesh->GetElementMaterial()->GetIndexArray(); + } + + auto buildMeshes = gcnew List(); + + // Count polygon per materials + for (int i = 0; i < polygonCount; i++) + { + int materialIndex = 0; + if (materialMappingMode == FbxGeometryElement::eByPolygon) + { + materialIndex = materialIndices->GetAt(i); + } + else if (materialMappingMode == FbxGeometryElement::eAllSame) + { + materialIndex = materialIndices->GetAt(0); + } + + // Equivalent to std::vector::resize() + while (materialIndex >= buildMeshes->Count) + { + buildMeshes->Add(nullptr); + } + + if (buildMeshes[materialIndex] == nullptr) + buildMeshes[materialIndex] = gcnew BuildMesh(); + + int polygonSize = pMesh->GetPolygonSize(i) - 2; + if (polygonSize > 0) + buildMeshes[materialIndex]->polygonCount += polygonSize; + } + + for (int i = 0; i < buildMeshes->Count; ++i) + { + auto meshParams = gcnew MeshParameters(); + auto meshName = meshNames[pMesh]; + if (buildMeshes->Count > 1) + meshName = meshName + "_" + std::to_string(i + 1); + meshParams->MeshName = gcnew String(meshName.c_str()); + meshParams->NodeName = sceneMapping->FindNode(pNode).Name; + + // Collect bones + int skinDeformerCount = pMesh->GetDeformerCount(FbxDeformer::eSkin); + if (skinDeformerCount > 0) { - continue; + meshParams->BoneNodes = gcnew HashSet(); + for (int deformerIndex = 0; deformerIndex < skinDeformerCount; deformerIndex++) + { + FbxSkin* skin = FbxCast(pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); + + auto totalClusterCount = skin->GetClusterCount(); + for (int clusterIndex = 0; clusterIndex < totalClusterCount; ++clusterIndex) + { + FbxCluster* cluster = skin->GetCluster(clusterIndex); + int indexCount = cluster->GetControlPointIndicesCount(); + if (indexCount == 0) + { + continue; + } + + FbxNode* link = cluster->GetLink(); + + MeshBoneDefinition bone; + meshParams->BoneNodes->Add(sceneMapping->FindNode(link).Name); + } + } } - FbxNode* link = cluster->GetLink(); + FbxGeometryElementMaterial* lMaterialElement = pMesh->GetElementMaterial(); + FbxSurfaceMaterial* lMaterial = pNode->GetMaterial(i); + if ((materialMappingMode == FbxGeometryElement::eByPolygon || materialMappingMode == FbxGeometryElement::eAllSame) + && lMaterialElement != NULL && lMaterial != NULL) + { + std::map uvElements; + auto uvNames = gcnew List(); + for (int j = 0; j < pMesh->GetElementUVCount(); ++j) + { + uvElements[pMesh->GetElementUV(j)->GetName()] = j; + uvNames->Add(gcnew String(pMesh->GetElementUV(j)->GetName())); + } + + auto material = GetOrCreateMaterial(lMaterial, uvNames, materialInstantiations, uvElements, materialNames); + meshParams->MaterialName = material->MaterialName; + } + else + { + logger->Warning(String::Format("Mesh {0} does not have a material. It might not be displayed.", meshParams->MeshName), (CallerInfo^)nullptr); + } - MeshBoneDefinition bone; - meshParams->BoneNodes->Add(sceneMapping->FindNode(link).Name); + models->Add(meshParams); } } } - FbxGeometryElementMaterial* lMaterialElement = pMesh->GetElementMaterial(); - FbxSurfaceMaterial* lMaterial = pNode->GetMaterial(i); - if ((materialMappingMode == FbxGeometryElement::eByPolygon || materialMappingMode == FbxGeometryElement::eAllSame) - && lMaterialElement != NULL && lMaterial != NULL) + void SearchMesh(FbxNode* pNode, std::map materialNames, std::map meshNames, List^ models, List^ materialInstantiations) { - std::map uvElements; - auto uvNames = gcnew List(); - for (int j = 0; j < pMesh->GetElementUVCount(); ++j) + // Process the node's attributes. + for (int i = 0; i < pNode->GetNodeAttributeCount(); i++) + SearchMeshInAttribute(pNode, pNode->GetNodeAttributeByIndex(i), materialNames, meshNames, models, materialInstantiations); + + // Recursively process the children nodes. + for (int j = 0; j < pNode->GetChildCount(); j++) { - uvElements[pMesh->GetElementUV(j)->GetName()] = j; - uvNames->Add(gcnew String(pMesh->GetElementUV(j)->GetName())); + SearchMesh(pNode->GetChild(j), materialNames, meshNames, models, materialInstantiations); } + } - auto material = GetOrCreateMaterial(lMaterial, uvNames, materialInstantiations, uvElements, materialNames); - meshParams->MaterialName = material->MaterialName; + Dictionary^ ExtractMaterialsNoInit() + { + std::map materialNames; + GenerateMaterialNames(materialNames); + + auto materials = gcnew Dictionary(); + for (int i = 0; i < scene->GetMaterialCount(); i++) + { + std::map dict; + auto lMaterial = scene->GetMaterial(i); + auto materialName = materialNames[lMaterial]; + materials->Add(gcnew String(materialName.c_str()), ProcessMeshMaterialAsset(lMaterial, dict)); + } + return materials; } - else + + MeshMaterials^ ExtractModelNoInit() { - logger->Warning(String::Format("Mesh {0} does not have a material. It might not be displayed.", meshParams->MeshName), (CallerInfo^)nullptr); + std::map materialNames; + GenerateMaterialNames(materialNames); + + std::map meshNames; + GenerateMeshesName(meshNames); + + std::map materialPerMesh; + auto models = gcnew List(); + auto materialInstantiations = gcnew List(); + SearchMesh(scene->GetRootNode(), materialNames, meshNames, models, materialInstantiations); + + auto ret = gcnew MeshMaterials(); + ret->Models = models; + ret->Materials = gcnew Dictionary(); + for (int i = 0; i < materialInstantiations->Count; ++i) + { + if (!ret->Materials->ContainsKey(materialInstantiations[i]->MaterialName)) + { + ret->Materials->Add(materialInstantiations[i]->MaterialName, materialInstantiations[i]->Material); + } + } + + return ret; } - models->Add(meshParams); - } - } - } + List^ ExtractTextureDependenciesNoInit() + { + auto textureNames = gcnew List(); - void SearchMesh(FbxNode* pNode, std::map materialNames, std::map meshNames, List^ models, List^ materialInstantiations) - { - // Process the node's attributes. - for(int i = 0; i < pNode->GetNodeAttributeCount(); i++) - SearchMeshInAttribute(pNode, pNode->GetNodeAttributeByIndex(i), materialNames, meshNames, models, materialInstantiations); + auto textureCount = scene->GetTextureCount(); + for (int i = 0; i < textureCount; ++i) + { + auto texture = FbxCast(scene->GetTexture(i)); - // Recursively process the children nodes. - for(int j = 0; j < pNode->GetChildCount(); j++) - { - SearchMesh(pNode->GetChild(j), materialNames, meshNames, models, materialInstantiations); - } - } + if (texture == nullptr) + continue; - Dictionary^ ExtractMaterialsNoInit() - { - std::map materialNames; - GenerateMaterialNames(materialNames); - - auto materials = gcnew Dictionary(); - for (int i = 0; i < scene->GetMaterialCount(); i++) - { - std::map dict; - auto lMaterial = scene->GetMaterial(i); - auto materialName = materialNames[lMaterial]; - materials->Add(gcnew String(materialName.c_str()), ProcessMeshMaterialAsset(lMaterial, dict)); - } - return materials; - } + auto texturePath = FindFilePath(texture); + if (!String::IsNullOrEmpty(texturePath)) + { + if (texturePath->Contains(".fbm\\")) + logger->Info(String::Format("Importer detected an embedded texture. It has been extracted at address '{0}'.", texturePath), (CallerInfo^)nullptr); + if (!File::Exists(texturePath)) + logger->Warning(String::Format("Importer detected a texture not available on disk at address '{0}'", texturePath), (CallerInfo^)nullptr); - MeshMaterials^ ExtractModelNoInit() - { - std::map materialNames; - GenerateMaterialNames(materialNames); - - std::map meshNames; - GenerateMeshesName(meshNames); - - std::map materialPerMesh; - auto models = gcnew List(); - auto materialInstantiations = gcnew List(); - SearchMesh(scene->GetRootNode(), materialNames, meshNames, models, materialInstantiations); - - auto ret = gcnew MeshMaterials(); - ret->Models = models; - ret->Materials = gcnew Dictionary(); - for (int i = 0; i < materialInstantiations->Count; ++i) - { - if (!ret->Materials->ContainsKey(materialInstantiations[i]->MaterialName)) - { - ret->Materials->Add(materialInstantiations[i]->MaterialName, materialInstantiations[i]->Material); - } - } - - return ret; - } + textureNames->Add(texturePath); + } + } - List^ ExtractTextureDependenciesNoInit() - { - auto textureNames = gcnew List(); - - auto textureCount = scene->GetTextureCount(); - for(int i=0; i(scene->GetTexture(i)); - - if(texture == nullptr) - continue; - - auto texturePath = FindFilePath(texture); - if (!String::IsNullOrEmpty(texturePath)) - { - if (texturePath->Contains(".fbm\\")) - logger->Info(String::Format("Importer detected an embedded texture. It has been extracted at address '{0}'.", texturePath), (CallerInfo^)nullptr); - if (!File::Exists(texturePath)) - logger->Warning(String::Format("Importer detected a texture not available on disk at address '{0}'", texturePath), (CallerInfo^)nullptr); + return textureNames; + } - textureNames->Add(texturePath); - } - } + List^ ExtractTextureDependencies(String^ inputFile) + { + try + { + Initialize(inputFile, nullptr, ImportConfiguration::ImportTexturesOnly()); + return ExtractTextureDependenciesNoInit(); + } + finally + { + Destroy(); + } + return nullptr; + } - return textureNames; - } + Dictionary^ ExtractMaterials(String^ inputFilename) + { + try + { + Initialize(inputFilename, nullptr, ImportConfiguration::ImportMaterialsOnly()); + return ExtractMaterialsNoInit(); + } + finally + { + Destroy(); + } + return nullptr; + } - List^ ExtractTextureDependencies(String^ inputFile) - { - try - { - Initialize(inputFile, nullptr, ImportConfiguration::ImportTexturesOnly()); - return ExtractTextureDependenciesNoInit(); - } - finally - { - Destroy(); - } - return nullptr; - } + void GetNodes(FbxNode* node, int depth, List^ allNodes) + { + auto newNodeInfo = gcnew NodeInfo(); + newNodeInfo->Name = sceneMapping->FindNode(node).Name; + newNodeInfo->Depth = depth; + newNodeInfo->Preserve = true; + + allNodes->Add(newNodeInfo); + for (int i = 0; i < node->GetChildCount(); ++i) + GetNodes(node->GetChild(i), depth + 1, allNodes); + } - Dictionary^ ExtractMaterials(String^ inputFilename) - { - try - { - Initialize(inputFilename, nullptr, ImportConfiguration::ImportMaterialsOnly()); - return ExtractMaterialsNoInit(); - } - finally - { - Destroy(); - } - return nullptr; - } + List^ ExtractNodeHierarchy() + { + auto allNodes = gcnew List(); + GetNodes(scene->GetRootNode(), 0, allNodes); + return allNodes; + } - void GetNodes(FbxNode* node, int depth, List^ allNodes) - { - auto newNodeInfo = gcnew NodeInfo(); - newNodeInfo->Name = sceneMapping->FindNode(node).Name; - newNodeInfo->Depth = depth; - newNodeInfo->Preserve = true; - - allNodes->Add(newNodeInfo); - for (int i = 0; i < node->GetChildCount(); ++i) - GetNodes(node->GetChild(i), depth + 1, allNodes); - } +#pragma region BLENDSHAPES - List^ ExtractNodeHierarchy() - { - auto allNodes = gcnew List(); - GetNodes(scene->GetRootNode(), 0, allNodes); - return allNodes; - } -public: - EntityInfo^ ExtractEntity(String^ inputFileName, bool extractTextureDependencies) - { - try - { - Initialize(inputFileName, nullptr, ImportConfiguration::ImportEntityConfig()); - - auto animationConverter = gcnew AnimationConverter(logger, sceneMapping); - - auto entityInfo = gcnew EntityInfo(); - if (extractTextureDependencies) - entityInfo->TextureDependencies = ExtractTextureDependenciesNoInit(); - entityInfo->AnimationNodes = animationConverter->ExtractAnimationNodesNoInit(); - auto models = ExtractModelNoInit(); - entityInfo->Models = models->Models; - entityInfo->Materials = models->Materials; - entityInfo->Nodes = ExtractNodeHierarchy(); - - return entityInfo; - } - finally - { - Destroy(); - } - return nullptr; - } - double GetAnimationDuration(String^ inputFileName, int animationStack) - { - try - { - Initialize(inputFileName, nullptr, ImportConfiguration::ImportEntityConfig()); + void ProcessBlendShapeTarget(fbxsdk::FbxShape* targetShape) { + // Get the number of control points in the target shape + int controlPointCount = targetShape->GetControlPointsCount(); - auto animationConverter = gcnew AnimationConverter(logger, sceneMapping); - auto animationData = animationConverter->ProcessAnimation(inputFilename, "", true, animationStack); + // Get a pointer to the array of control points + fbxsdk::FbxVector4* controlPoints = targetShape->GetControlPoints(); - return animationData->Duration.TotalSeconds; - } - finally - { - Destroy(); - } + // Access individual control points and their coordinates + for (int i = 0; i < controlPointCount; ++i) { + fbxsdk::FbxVector4 controlPoint = controlPoints[i]; + double x = controlPoint[0]; + double y = controlPoint[1]; + double z = controlPoint[2]; - return 0; - } - Model^ Convert(String^ inputFilename, String^ vfsOutputFilename, Dictionary^ materialIndices) - { - try - { - Initialize(inputFilename, vfsOutputFilename, ImportConfiguration::ImportAll()); - - // Create default ModelViewData - modelData = gcnew Model(); - - //auto sceneName = scene->GetName(); - //if (sceneName != NULL && strlen(sceneName) > 0) - //{ - // entity->Name = gcnew String(sceneName); - //} - //else - //{ - // // Build scene name from file name - // entity->Name = Path::GetFileName(this->inputFilename); - //} - - std::map meshNames; - GenerateMeshesName(meshNames); - - std::map materialNames; - GenerateMaterialNames(materialNames); - - std::map materials; - for (auto it = materialNames.begin(); it != materialNames.end(); ++it) - { - auto materialName = gcnew String(it->second.c_str()); - int materialIndex; - if (materialIndices->TryGetValue(materialName, materialIndex)) + } + } + + void ProcessBlendShapeChannel(fbxsdk::FbxBlendShapeChannel* channel) { + // Process each blend shape target + int targetShapeCount = channel->GetTargetShapeCount(); + for (int k = 0; k < targetShapeCount; ++k) { + fbxsdk::FbxShape* targetShape = channel->GetTargetShape(k); + const char* targetShapeName = targetShape->GetName(); + + // Process control points of the blend shape target + ProcessBlendShapeTarget(targetShape); + } + } + + void ProcessBlendShape(fbxsdk::FbxBlendShape* blendShape) { + // Process each blend shape channel + int blendShapeChannelCount = blendShape->GetBlendShapeChannelCount(); + for (int j = 0; j < blendShapeChannelCount; ++j) { + fbxsdk::FbxBlendShapeChannel* channel = blendShape->GetBlendShapeChannel(j); + const char* channelName = channel->GetName(); + + // Process control points of the blend shape channel + ProcessBlendShapeChannel(channel); + } + } + + void ProcessNode(fbxsdk::FbxNode* pNode) { + // Check if the node has blend shapes + int blendShapeCount = pNode->GetGeometry()->GetDeformerCount(FbxDeformer::eBlendShape); + if (blendShapeCount > 0) { + for (int i = 0; i < blendShapeCount; ++i) { + fbxsdk::FbxBlendShape* blendShape = static_cast(pNode->GetGeometry()->GetDeformer(i, FbxDeformer::eBlendShape)); + + // Process control points of the blend shape + ProcessBlendShape(blendShape); + } + } + + // Recursively process child nodes + int childCount = pNode->GetChildCount(); + for (int i = 0; i < childCount; ++i) { + ProcessNode(pNode->GetChild(i)); + } + } + + +#pragma endregion + + + + public: + EntityInfo^ ExtractEntity(String^ inputFileName, bool extractTextureDependencies) { - materials[it->first] = materialIndex; + try + { + Initialize(inputFileName, nullptr, ImportConfiguration::ImportEntityConfig()); + + auto animationConverter = gcnew AnimationConverter(logger, sceneMapping); + + auto entityInfo = gcnew EntityInfo(); + if (extractTextureDependencies) + entityInfo->TextureDependencies = ExtractTextureDependenciesNoInit(); + entityInfo->AnimationNodes = animationConverter->ExtractAnimationNodesNoInit(); + auto models = ExtractModelNoInit(); + entityInfo->Models = models->Models; + entityInfo->Materials = models->Materials; + entityInfo->Nodes = ExtractNodeHierarchy(); + + return entityInfo; + } + finally + { + Destroy(); + } + return nullptr; } - else + + double GetAnimationDuration(String^ inputFileName, int animationStack) { - logger->Warning(String::Format("Model references material '{0}', but it was not defined in the ModelAsset.", materialName), (CallerInfo^)nullptr); + try + { + Initialize(inputFileName, nullptr, ImportConfiguration::ImportEntityConfig()); + + auto animationConverter = gcnew AnimationConverter(logger, sceneMapping); + auto animationData = animationConverter->ProcessAnimation(inputFilename, "", true, animationStack); + + return animationData->Duration.TotalSeconds; + } + finally + { + Destroy(); + } + + return 0; } - } - // Process and add root entity - ProcessNodeTransformation(scene->GetRootNode()); - ProcessNodeAttributes(scene->GetRootNode(), meshNames, materials); + Model^ Convert(String^ inputFilename, String^ vfsOutputFilename, Dictionary^ materialIndices) + { + try + { + Initialize(inputFilename, vfsOutputFilename, ImportConfiguration::ImportAll()); + + // Create default ModelViewData + modelData = gcnew Model(); + + //auto sceneName = scene->GetName(); + //if (sceneName != NULL && strlen(sceneName) > 0) + //{ + // entity->Name = gcnew String(sceneName); + //} + //else + //{ + // // Build scene name from file name + // entity->Name = Path::GetFileName(this->inputFilename); + //} + + std::map meshNames; + GenerateMeshesName(meshNames); + + std::map materialNames; + GenerateMaterialNames(materialNames); + + std::map materials; + for (auto it = materialNames.begin(); it != materialNames.end(); ++it) + { + auto materialName = gcnew String(it->second.c_str()); + int materialIndex; + if (materialIndices->TryGetValue(materialName, materialIndex)) + { + materials[it->first] = materialIndex; + } + else + { + logger->Warning(String::Format("Model references material '{0}', but it was not defined in the ModelAsset.", materialName), (CallerInfo^)nullptr); + } + } - return modelData; - } - finally - { - Destroy(); - } + // Process and add root entity + ProcessNodeTransformation(scene->GetRootNode()); + ProcessNodeAttributes(scene->GetRootNode(), meshNames, materials); - return nullptr; - } + return modelData; + } + finally + { + Destroy(); + } - AnimationInfo^ ConvertAnimation(String^ inputFilename, String^ vfsOutputFilename, bool importCustomAttributeAnimations, int animationStack) - { - try - { - Initialize(inputFilename, vfsOutputFilename, ImportConfiguration::ImportAnimationsOnly()); + return nullptr; + } - auto animationConverter = gcnew AnimationConverter(logger, sceneMapping); - return animationConverter->ProcessAnimation(inputFilename, vfsOutputFilename, importCustomAttributeAnimations, animationStack); - } - finally - { - Destroy(); - } + AnimationInfo^ ConvertAnimation(String^ inputFilename, String^ vfsOutputFilename, bool importCustomAttributeAnimations, int animationStack) + { + try + { + Initialize(inputFilename, vfsOutputFilename, ImportConfiguration::ImportAnimationsOnly()); - return nullptr; - } + auto animationConverter = gcnew AnimationConverter(logger, sceneMapping); + return animationConverter->ProcessAnimation(inputFilename, vfsOutputFilename, importCustomAttributeAnimations, animationStack); + } + finally + { + Destroy(); + } - Skeleton^ ConvertSkeleton(String^ inputFilename, String^ vfsOutputFilename) - { - try - { - Initialize(inputFilename, vfsOutputFilename, ImportConfiguration::ImportSkeletonOnly()); - ProcessNodeTransformation(scene->GetRootNode()); + return nullptr; + } - auto skeleton = gcnew Skeleton(); - skeleton->Nodes = sceneMapping->Nodes; - return skeleton; - } - finally - { - Destroy(); - } + Skeleton^ ConvertSkeleton(String^ inputFilename, String^ vfsOutputFilename) + { + try + { + Initialize(inputFilename, vfsOutputFilename, ImportConfiguration::ImportSkeletonOnly()); + ProcessNodeTransformation(scene->GetRootNode()); - return nullptr; - } -}; + auto skeleton = gcnew Skeleton(); + skeleton->Nodes = sceneMapping->Nodes; + return skeleton; + } + finally + { + Destroy(); + } + + return nullptr; + } + }; -} } } + } + } +} diff --git a/sources/tools/Stride.Importer.FBX/Stride.Importer.FBX.vcxproj b/sources/tools/Stride.Importer.FBX/Stride.Importer.FBX.vcxproj index f1b6046d26..f5199e5f1c 100644 --- a/sources/tools/Stride.Importer.FBX/Stride.Importer.FBX.vcxproj +++ b/sources/tools/Stride.Importer.FBX/Stride.Importer.FBX.vcxproj @@ -203,10 +203,6 @@ {c121a566-555e-42b9-9b0a-1696529a9088} False - - {ad4fdc24-b64d-4ed7-91aa-62c9eda12fa4} - False - {0e916ab7-5a6c-4820-8ab1-aa492fe66d68} False @@ -223,6 +219,9 @@ {5210fb81-b807-49bb-af0d-31fb6a83a572} False + + {ad4fdc24-b64d-4ed7-91aa-62c9eda12fa4} + {f2d52edb-bc17-4243-b06d-33cd20f87a7f} False @@ -259,4 +258,4 @@ - + \ No newline at end of file