diff --git a/sources/tools/Stride.Importer.Assimp/Material/Flags.cs b/sources/tools/Stride.Importer.Assimp/Material/Flags.cs
new file mode 100644
index 0000000000..13dd304ca4
--- /dev/null
+++ b/sources/tools/Stride.Importer.Assimp/Material/Flags.cs
@@ -0,0 +1,20 @@
+
+// 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;
+
+namespace Stride.Importer.Assimp.Material
+{
+ ///
+ /// Enumeration of the new Assimp's flags.
+ ///
+
+ [Flags]
+ public enum Flags
+ {
+ None = 0,
+ Invert = 1,
+ ReplaceAlpha = 2
+ }
+}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/MappingMode.cs b/sources/tools/Stride.Importer.Assimp/Material/MappingMode.cs
new file mode 100644
index 0000000000..c1a95b8b24
--- /dev/null
+++ b/sources/tools/Stride.Importer.Assimp/Material/MappingMode.cs
@@ -0,0 +1,16 @@
+// 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.
+
+namespace Stride.Importer.Assimp.Material
+{
+ ///
+ /// Enumeration of the different mapping modes in the new Assimp's material stack.
+ ///
+ public enum MappingMode
+ {
+ Wrap,
+ Clamp,
+ Decal,
+ Mirror
+ }
+}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/MaterialStack.cs b/sources/tools/Stride.Importer.Assimp/Material/MaterialStack.cs
index 14d2affe51..86256080e1 100644
--- a/sources/tools/Stride.Importer.Assimp/Material/MaterialStack.cs
+++ b/sources/tools/Stride.Importer.Assimp/Material/MaterialStack.cs
@@ -1,243 +1,18 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Stride.Core.Mathematics;
+// 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.Collections.Generic;
namespace Stride.Importer.Assimp.Material
{
- ///
- /// Enumeration of the different types of node in the new Assimp's material stack.
- ///
- public enum StackType
- {
- Color = 0,
- Texture,
- Operation
- }
- ///
- /// Enumeration of the new Assimp's flags.
- ///
- public enum Flags
- {
- Invert = 1,
- ReplaceAlpha = 2
- }
- ///
- /// Enumeration of the different operations in the new Assimp's material stack.
- ///
- public enum Operation
- {
- Add = 0,
- Add3ds,
- AddMaya,
- Average,
- Color,
- ColorBurn,
- ColorDodge,
- Darken3ds,
- DarkenMaya,
- Desaturate,
- Difference3ds,
- DifferenceMaya,
- Divide,
- Exclusion,
- HardLight,
- HardMix,
- Hue,
- Illuminate,
- In,
- Lighten3ds,
- LightenMaya,
- LinearBurn,
- LinearDodge,
- Multiply3ds,
- MultiplyMaya,
- None,
- Out,
- Over3ds,
- Overlay3ds,
- OverMaya,
- PinLight,
- Saturate,
- Saturation,
- Screen,
- SoftLight,
- Substract3ds,
- SubstractMaya,
- Value,
- Mask
- }
- ///
- /// Enumeration of the different mapping modes in the new Assimp's material stack.
- ///
- public enum MappingMode
- {
- Wrap,
- Clamp,
- Decal,
- Mirror
- }
- ///
- /// Class representing an element in the new Assimp's material stack.
- ///
- public abstract class StackElement
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The alpha of the node.
- /// The blending coefficient of the node.
- /// The flags of the node.
- /// The type of the node.
- public StackElement(float Alpha, float Blend, int flags, StackType Type)
- {
- alpha = Alpha;
- blend = Blend;
- type = Type;
- }
- ///
- /// Gets the alpha of the node.
- ///
- ///
- /// The alpha of the node.
- ///
- public float alpha { get; private set; }
- ///
- /// Gets the blending coefficient of the node.
- ///
- ///
- /// The blending coefficient of the node.
- ///
- public float blend { get; private set; }
- ///
- /// Gets the flags of the node.
- ///
- ///
- /// The flags of the node.
- ///
- public int flags { get; private set; }
- ///
- /// Gets the type of the node.
- ///
- ///
- /// The type of the node.
- ///
- public StackType type { get; private set; }
- }
- ///
- /// Class representing an operation in the new Assimp's material stack.
- ///
- public class StackOperation : StackElement
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The operation of the node.
- /// The alpha of the node.
- /// The blending coefficient of the node.
- /// The flags.
- public StackOperation(Operation Operation, float Alpha = 1.0f, float Blend = 1.0f, int Flags = 0)
- : base(Alpha, Blend, Flags, StackType.Operation)
- {
- operation = Operation;
- }
- ///
- /// Gets the operation of the node.
- ///
- ///
- /// The operation of the node.
- ///
- public Operation operation { get; private set; }
- }
- ///
- /// Class representing a color in the new Assimp's material stack.
- ///
- public class StackColor : StackElement
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The color of the node.
- /// The alpha of the node.
- /// The blending coefficient of the node.
- /// The flags of the node.
- public StackColor(Color3 Color, float Alpha = 1.0f, float Blend = 1.0f, int Flags = 0)
- : base(Alpha, Blend, Flags, StackType.Color)
- {
- color = Color;
- }
- ///
- /// Gets the color of the node.
- ///
- ///
- /// The color of the node.
- ///
- public Color3 color { get; private set; }
- }
- ///
- /// Class representing a texture in the new Assimp's material stack.
- ///
- public class StackTexture : StackElement
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The texture path.
- /// The uv channel used by the texture.
- /// The U mapping mode.
- /// The V mapping mode.
- /// The alpha of the node.
- /// The blending coefficient of the node.
- /// The flags of the node.
- public StackTexture(String TexturePath, int Channel, MappingMode MappingModeU, MappingMode MappingModeV, float Alpha = 1.0f, float Blend = 1.0F, int Flags = 0)
- : base(Alpha, Blend, Flags, StackType.Texture)
- {
- texturePath = TexturePath;
- channel = Channel;
- mappingModeU = MappingModeU;
- mappingModeV = MappingModeV;
- }
- ///
- /// Gets the texture path.
- ///
- ///
- /// The texture path.
- ///
- public String texturePath { get; private set; }
- ///
- /// Gets the uv channel.
- ///
- ///
- /// The uv channel.
- ///
- public int channel { get; private set; }
- ///
- /// Gets the U mapping mode.
- ///
- ///
- /// The U mapping mode.
- ///
- public MappingMode mappingModeU { get; private set; }
- ///
- /// Gets the Vmapping mode.
- ///
- ///
- /// The V mapping mode.
- ///
- public MappingMode mappingModeV { get; private set; }
- }
///
/// Class representing the new Assimp's material stack in c#.
///
- public class Stack
+ public class MaterialStack
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public Stack()
+ public MaterialStack()
{
stack = new Stack();
}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/Materials.cs b/sources/tools/Stride.Importer.Assimp/Material/Materials.cs
index 32560bc259..87a2175c50 100644
--- a/sources/tools/Stride.Importer.Assimp/Material/Materials.cs
+++ b/sources/tools/Stride.Importer.Assimp/Material/Materials.cs
@@ -1,154 +1,152 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+// 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 Silk.NET.Assimp;
using Stride.Core.Mathematics;
namespace Stride.Importer.Assimp.Material
{
- public static unsafe class Materials
- {
- public const string MatKeyTexTypeBase = "$tex.type";
- public const string MatKeyTexColorBase = "$tex.color";
- public const string MatKeyTexAlphaBase = "$tex.alpha";
+ public static unsafe class Materials
+ {
+ public const string MatKeyTexTypeBase = "$tex.type";
+ public const string MatKeyTexColorBase = "$tex.color";
+ public const string MatKeyTexAlphaBase = "$tex.alpha";
- ///
- /// Converts an Assimp's material stack operation From c++ to c#.
- ///
- public static readonly Operation[] ConvertAssimpStackOperationCppToCs = new Operation[]
- {
- Operation.Add, //aiStackOperation_Add
- Operation.Add3ds, //aiStackOperation_Add3ds
- Operation.AddMaya, //aiStackOperation_AddMaya
- Operation.Average, //aiStackOperation_Average
- Operation.Color, //aiStackOperation_Color
- Operation.ColorBurn, //aiStackOperation_ColorBurn
- Operation.ColorDodge, //aiStackOperation_ColorDodge
- Operation.Darken3ds, //aiStackOperation_Darken3ds
- Operation.DarkenMaya, //aiStackOperation_DarkenMaya
- Operation.Desaturate, //aiStackOperation_Desaturate
- Operation.Difference3ds, //aiStackOperation_Difference3ds
- Operation.DifferenceMaya,//aiStackOperation_DifferenceMaya
- Operation.Divide, //aiStackOperation_Divide
- Operation.Exclusion, //aiStackOperation_Exclusion
- Operation.HardLight, //aiStackOperation_HardLight
- Operation.HardMix, //aiStackOperation_HardMix
- Operation.Hue, //aiStackOperation_Hue
- Operation.Illuminate, //aiStackOperation_Illuminate
- Operation.In, //aiStackOperation_In
- Operation.Lighten3ds, //aiStackOperation_Lighten3ds
- Operation.LightenMaya, //aiStackOperation_LightenMaya
- Operation.LinearBurn, //aiStackOperation_LinearBurn
- Operation.LinearDodge, //aiStackOperation_LinearDodge
- Operation.Multiply3ds, //aiStackOperation_Multiply3ds
- Operation.MultiplyMaya, //aiStackOperation_MultiplyMaya
- Operation.None, //aiStackOperation_None
- Operation.Out, //aiStackOperation_Out
- Operation.Over3ds, //aiStackOperation_Over3ds
- Operation.Overlay3ds, //aiStackOperation_Overlay3ds
- Operation.OverMaya, //aiStackOperation_OverMaya
- Operation.PinLight, //aiStackOperation_PinLight
- Operation.Saturate, //aiStackOperation_Saturate
- Operation.Saturation, //aiStackOperation_Saturation
- Operation.Screen, //aiStackOperation_Screen
- Operation.SoftLight, //aiStackOperation_SoftLight
- Operation.Substract3ds, //aiStackOperation_Substract3ds
- Operation.SubstractMaya, //aiStackOperation_SubstractMaya
- Operation.Value, //aiStackOperation_Value
- Operation.Mask //aiStackOperation_Mask
- };
+ ///
+ /// Converts an Assimp's material stack operation From c++ to c#.
+ ///
+ public static readonly Operation[] ConvertAssimpStackOperationCppToCs = new Operation[]
+ {
+ Operation.Add, //aiStackOperation_Add
+ Operation.Add3ds, //aiStackOperation_Add3ds
+ Operation.AddMaya, //aiStackOperation_AddMaya
+ Operation.Average, //aiStackOperation_Average
+ Operation.Color, //aiStackOperation_Color
+ Operation.ColorBurn, //aiStackOperation_ColorBurn
+ Operation.ColorDodge, //aiStackOperation_ColorDodge
+ Operation.Darken3ds, //aiStackOperation_Darken3ds
+ Operation.DarkenMaya, //aiStackOperation_DarkenMaya
+ Operation.Desaturate, //aiStackOperation_Desaturate
+ Operation.Difference3ds, //aiStackOperation_Difference3ds
+ Operation.DifferenceMaya,//aiStackOperation_DifferenceMaya
+ Operation.Divide, //aiStackOperation_Divide
+ Operation.Exclusion, //aiStackOperation_Exclusion
+ Operation.HardLight, //aiStackOperation_HardLight
+ Operation.HardMix, //aiStackOperation_HardMix
+ Operation.Hue, //aiStackOperation_Hue
+ Operation.Illuminate, //aiStackOperation_Illuminate
+ Operation.In, //aiStackOperation_In
+ Operation.Lighten3ds, //aiStackOperation_Lighten3ds
+ Operation.LightenMaya, //aiStackOperation_LightenMaya
+ Operation.LinearBurn, //aiStackOperation_LinearBurn
+ Operation.LinearDodge, //aiStackOperation_LinearDodge
+ Operation.Multiply3ds, //aiStackOperation_Multiply3ds
+ Operation.MultiplyMaya, //aiStackOperation_MultiplyMaya
+ Operation.None, //aiStackOperation_None
+ Operation.Out, //aiStackOperation_Out
+ Operation.Over3ds, //aiStackOperation_Over3ds
+ Operation.Overlay3ds, //aiStackOperation_Overlay3ds
+ Operation.OverMaya, //aiStackOperation_OverMaya
+ Operation.PinLight, //aiStackOperation_PinLight
+ Operation.Saturate, //aiStackOperation_Saturate
+ Operation.Saturation, //aiStackOperation_Saturation
+ Operation.Screen, //aiStackOperation_Screen
+ Operation.SoftLight, //aiStackOperation_SoftLight
+ Operation.Substract3ds, //aiStackOperation_Substract3ds
+ Operation.SubstractMaya, //aiStackOperation_SubstractMaya
+ Operation.Value, //aiStackOperation_Value
+ Operation.Mask //aiStackOperation_Mask
+ };
- ///
- /// Converts an Assimp's material stack node type From c++ to c#.
- ///
- public static readonly StackType[] ConvertAssimpStackTypeCppToCs = new StackType[]
- {
- StackType.Color, // aiStackType_ColorType
- StackType.Texture, // aiStackType_TextureType
- StackType.Operation // aiStackType_BlemdOpType
- };
+ ///
+ /// Converts an Assimp's material stack node type From c++ to c#.
+ ///
+ public static readonly StackType[] ConvertAssimpStackTypeCppToCs = new StackType[]
+ {
+ StackType.Color, // aiStackType_ColorType
+ StackType.Texture, // aiStackType_TextureType
+ StackType.Operation // aiStackType_BlemdOpType
+ };
- public static readonly MappingMode[] ConvertAssimpMappingModeCppToCs = new MappingMode[]
- {
- MappingMode.Wrap, // aiTextureMapMode_Wrap
- MappingMode.Clamp, // aiTextureMapMode_Clamp
- MappingMode.Mirror, // aiTextureMapMode_Mirror
- MappingMode.Decal // aiTextureMapMode_Decal
- };
+ public static readonly MappingMode[] ConvertAssimpMappingModeCppToCs = new MappingMode[]
+ {
+ MappingMode.Wrap, // aiTextureMapMode_Wrap
+ MappingMode.Clamp, // aiTextureMapMode_Clamp
+ MappingMode.Mirror, // aiTextureMapMode_Mirror
+ MappingMode.Decal // aiTextureMapMode_Decal
+ };
- public static unsafe Stack ConvertAssimpStackCppToCs(Silk.NET.Assimp.Assimp assimp, Silk.NET.Assimp.Material* material, Silk.NET.Assimp.TextureType type)
- {
- var ret = new Stack();
- var count = (int)assimp.GetMaterialTextureCount(material, type);
+ public static unsafe MaterialStack ConvertAssimpStackCppToCs(Silk.NET.Assimp.Assimp assimp, Silk.NET.Assimp.Material* material, Silk.NET.Assimp.TextureType type)
+ {
+ var ret = new MaterialStack();
+ var count = (int)assimp.GetMaterialTextureCount(material, type);
- // Process the material stack
- for (int iEl = count - 1; iEl >= 0; --iEl)
- {
- StackElement el;
- // Common properties
- int elType = 0, elFlags = 0;
- float elAlpha = 0.0f, elBlend = 0.0f;
- // Operation-specific properties
- int elOp = 0;
- // Color-specific properties
- var elColor = new System.Numerics.Vector4();
- // Texture-specific properties
- var elTexPath = new AssimpString();
- int elTexChannel = 0, elMappingModeU = 0, elMappingModeV = 0;
- uint pMax = 0;
+ // Process the material stack
+ for (int iEl = count - 1; iEl >= 0; --iEl)
+ {
+ StackElement el;
+ // Common properties
+ int elType = 0, elFlags = 0;
+ float elAlpha = 0.0f, elBlend = 0.0f;
+ // Operation-specific properties
+ int elOp = 0;
+ // Color-specific properties
+ var elColor = new System.Numerics.Vector4();
+ // Texture-specific properties
+ var elTexPath = new AssimpString();
+ int elTexChannel = 0, elMappingModeU = 0, elMappingModeV = 0;
+ uint pMax = 0;
- if (assimp.GetMaterialFloatArray(material, MatKeyTexAlphaBase, (uint)type, (uint)iEl, ref elAlpha, ref pMax) != Return.ReturnSuccess)
- elAlpha = 1.0f; // default alpha
- if (assimp.GetMaterialFloatArray(material, Silk.NET.Assimp.Assimp.MaterialTexblendBase, (uint)type, (uint)iEl, ref elBlend, ref pMax) != Return.ReturnSuccess)
- elBlend = 1.0f; // default blend
- if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialTexflagsBase, (uint)type, (uint)iEl, ref elFlags, ref pMax) != Return.ReturnSuccess)
- elFlags = 0; // default flags (no flags)
- if (assimp.GetMaterialIntegerArray(material, MatKeyTexTypeBase, (uint)type, (uint)iEl, ref elType, ref pMax) != Return.ReturnSuccess)
- elType = (int)StackType.Texture;//continue; // error !
+ if (assimp.GetMaterialFloatArray(material, MatKeyTexAlphaBase, (uint)type, (uint)iEl, ref elAlpha, ref pMax) != Return.Success)
+ elAlpha = 1.0f; // default alpha
+ if (assimp.GetMaterialFloatArray(material, Silk.NET.Assimp.Assimp.MaterialTexblendBase, (uint)type, (uint)iEl, ref elBlend, ref pMax) != Return.Success)
+ elBlend = 1.0f; // default blend
+ if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialTexflagsBase, (uint)type, (uint)iEl, ref elFlags, ref pMax) != Return.Success)
+ elFlags = 0; // default flags (no flags)
+ if (assimp.GetMaterialIntegerArray(material, MatKeyTexTypeBase, (uint)type, (uint)iEl, ref elType, ref pMax) != Return.Success)
+ elType = (int)StackType.Texture;//continue; // error !
- switch ((StackType)elType)
- {
- case StackType.Operation:
- if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialTexopBase, (uint)type, (uint)iEl, ref elOp, ref pMax) != Return.ReturnSuccess)
- continue; // error !
+ switch ((StackType)elType)
+ {
+ case StackType.Operation:
+ if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialTexopBase, (uint)type, (uint)iEl, ref elOp, ref pMax) != Return.Success)
+ continue; // error !
- el = new StackOperation(ConvertAssimpStackOperationCppToCs[elOp], elAlpha, elBlend, elFlags);
- break;
- case StackType.Color:
- if (assimp.GetMaterialColor(material, MatKeyTexColorBase, (uint)type, (uint)iEl, ref elColor) != Return.ReturnSuccess)
- continue; // error !
- el = new StackColor(new Color3(elColor.X, elColor.Y, elColor.Z), elAlpha, elBlend, elFlags);
- break;
- case StackType.Texture:
- if (assimp.GetMaterialString(material, Silk.NET.Assimp.Assimp.MaterialTextureBase, (uint)type, (uint)iEl, ref elTexPath) != Return.ReturnSuccess)
- continue; // error !
- if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialUvwsrcBase, (uint)type, (uint)iEl, ref elTexChannel, ref pMax) != Return.ReturnSuccess)
- elTexChannel = 0; // default channel
- if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialMappingmodeUBase, (uint)type, (uint)iEl, ref elMappingModeU, ref pMax) != Return.ReturnSuccess)
- elMappingModeU = (int)TextureMapMode.TextureMapModeWrap; // default mapping mode
- if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialMappingmodeVBase, (uint)type, (uint)iEl, ref elMappingModeV, ref pMax) != Return.ReturnSuccess)
- elMappingModeV = (int)TextureMapMode.TextureMapModeWrap; // default mapping mode
+ el = new StackOperation(ConvertAssimpStackOperationCppToCs[elOp], elAlpha, elBlend, elFlags);
+ break;
+ case StackType.Color:
+ if (assimp.GetMaterialColor(material, MatKeyTexColorBase, (uint)type, (uint)iEl, ref elColor) != Return.Success)
+ continue; // error !
+ el = new StackColor(new Color3(elColor.X, elColor.Y, elColor.Z), elAlpha, elBlend, elFlags);
+ break;
+ case StackType.Texture:
+ if (assimp.GetMaterialString(material, Silk.NET.Assimp.Assimp.MaterialTextureBase, (uint)type, (uint)iEl, ref elTexPath) != Return.Success)
+ continue; // error !
+ if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialUvwsrcBase, (uint)type, (uint)iEl, ref elTexChannel, ref pMax) != Return.Success)
+ elTexChannel = 0; // default channel
+ if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialMappingmodeUBase, (uint)type, (uint)iEl, ref elMappingModeU, ref pMax) != Return.Success)
+ elMappingModeU = (int)TextureMapMode.Wrap; // default mapping mode
+ if (assimp.GetMaterialIntegerArray(material, Silk.NET.Assimp.Assimp.MaterialMappingmodeVBase, (uint)type, (uint)iEl, ref elMappingModeV, ref pMax) != Return.Success)
+ elMappingModeV = (int)TextureMapMode.Wrap; // default mapping mode
- el = new StackTexture(
- elTexPath.AsString,
- elTexChannel,
- ConvertAssimpMappingModeCppToCs[elMappingModeU],
- ConvertAssimpMappingModeCppToCs[elMappingModeV],
- elAlpha,
- elBlend,
- elFlags);
- break;
- default:
- // error !
- continue;
- }
+ el = new StackTexture(
+ elTexPath.AsString,
+ elTexChannel,
+ ConvertAssimpMappingModeCppToCs[elMappingModeU],
+ ConvertAssimpMappingModeCppToCs[elMappingModeV],
+ elAlpha,
+ elBlend,
+ elFlags);
+ break;
+ default:
+ // error !
+ continue;
+ }
- ret.Push(el);
- }
+ ret.Push(el);
+ }
- return ret;
- }
- }
+ return ret;
+ }
+ }
}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/Operation.cs b/sources/tools/Stride.Importer.Assimp/Material/Operation.cs
new file mode 100644
index 0000000000..707300b8e3
--- /dev/null
+++ b/sources/tools/Stride.Importer.Assimp/Material/Operation.cs
@@ -0,0 +1,51 @@
+// 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.
+
+namespace Stride.Importer.Assimp.Material
+{
+ ///
+ /// Enumeration of the different operations in the new Assimp's material stack.
+ ///
+ public enum Operation
+ {
+ Add = 0,
+ Add3ds,
+ AddMaya,
+ Average,
+ Color,
+ ColorBurn,
+ ColorDodge,
+ Darken3ds,
+ DarkenMaya,
+ Desaturate,
+ Difference3ds,
+ DifferenceMaya,
+ Divide,
+ Exclusion,
+ HardLight,
+ HardMix,
+ Hue,
+ Illuminate,
+ In,
+ Lighten3ds,
+ LightenMaya,
+ LinearBurn,
+ LinearDodge,
+ Multiply3ds,
+ MultiplyMaya,
+ None,
+ Out,
+ Over3ds,
+ Overlay3ds,
+ OverMaya,
+ PinLight,
+ Saturate,
+ Saturation,
+ Screen,
+ SoftLight,
+ Substract3ds,
+ SubstractMaya,
+ Value,
+ Mask
+ }
+}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/StackColor.cs b/sources/tools/Stride.Importer.Assimp/Material/StackColor.cs
new file mode 100644
index 0000000000..0acd723ffb
--- /dev/null
+++ b/sources/tools/Stride.Importer.Assimp/Material/StackColor.cs
@@ -0,0 +1,33 @@
+// 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 Stride.Core.Mathematics;
+
+namespace Stride.Importer.Assimp.Material
+{
+ ///
+ /// Class representing a color in the new Assimp's material stack.
+ ///
+ public class StackColor : StackElement
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The color of the node.
+ /// The alpha of the node.
+ /// The blending coefficient of the node.
+ /// The flags of the node.
+ public StackColor(Color3 color, float alpha = 1.0f, float blend = 1.0f, int flags = 0)
+ : base(alpha, blend, flags, StackType.Color)
+ {
+ Color = color;
+ }
+ ///
+ /// Gets the color of the node.
+ ///
+ ///
+ /// The color of the node.
+ ///
+ public Color3 Color { get; private set; }
+ }
+}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/StackElement.cs b/sources/tools/Stride.Importer.Assimp/Material/StackElement.cs
new file mode 100644
index 0000000000..e0be7e3fcc
--- /dev/null
+++ b/sources/tools/Stride.Importer.Assimp/Material/StackElement.cs
@@ -0,0 +1,53 @@
+// 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.
+
+namespace Stride.Importer.Assimp.Material
+{
+ ///
+ /// Class representing an element in the new Assimp's material stack.
+ ///
+ public abstract class StackElement
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The alpha of the node.
+ /// The blending coefficient of the node.
+ /// The flags of the node.
+ /// The type of the node.
+ public StackElement(float alpha, float blend, int flags, StackType type)
+ {
+ Alpha = alpha;
+ Blend = blend;
+ Type = type;
+ }
+ ///
+ /// Gets the alpha of the node.
+ ///
+ ///
+ /// The alpha of the node.
+ ///
+ public float Alpha { get; private set; }
+ ///
+ /// Gets the blending coefficient of the node.
+ ///
+ ///
+ /// The blending coefficient of the node.
+ ///
+ public float Blend { get; private set; }
+ ///
+ /// Gets the flags of the node.
+ ///
+ ///
+ /// The flags of the node.
+ ///
+ public int Flags { get; private set; }
+ ///
+ /// Gets the type of the node.
+ ///
+ ///
+ /// The type of the node.
+ ///
+ public StackType Type { get; private set; }
+ }
+}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/StackOperation.cs b/sources/tools/Stride.Importer.Assimp/Material/StackOperation.cs
new file mode 100644
index 0000000000..e4586d5781
--- /dev/null
+++ b/sources/tools/Stride.Importer.Assimp/Material/StackOperation.cs
@@ -0,0 +1,31 @@
+// 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.
+
+namespace Stride.Importer.Assimp.Material
+{
+ ///
+ /// Class representing an operation in the new Assimp's material stack.
+ ///
+ public class StackOperation : StackElement
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The operation of the node.
+ /// The alpha of the node.
+ /// The blending coefficient of the node.
+ /// The flags.
+ public StackOperation(Operation operation, float alpha = 1.0f, float blend = 1.0f, int flags = 0)
+ : base(alpha, blend, flags, StackType.Operation)
+ {
+ Operation = operation;
+ }
+ ///
+ /// Gets the operation of the node.
+ ///
+ ///
+ /// The operation of the node.
+ ///
+ public Operation Operation { get; private set; }
+ }
+}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/StackTexture.cs b/sources/tools/Stride.Importer.Assimp/Material/StackTexture.cs
new file mode 100644
index 0000000000..cae6eb9210
--- /dev/null
+++ b/sources/tools/Stride.Importer.Assimp/Material/StackTexture.cs
@@ -0,0 +1,58 @@
+// 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.
+
+namespace Stride.Importer.Assimp.Material
+{
+ ///
+ /// Class representing a texture in the new Assimp's material stack.
+ ///
+ public class StackTexture : StackElement
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The texture path.
+ /// The uv channel used by the texture.
+ /// The U mapping mode.
+ /// The V mapping mode.
+ /// The alpha of the node.
+ /// The blending coefficient of the node.
+ /// The flags of the node.
+ public StackTexture(string texturePath, int channel, MappingMode mappingModeU, MappingMode mappingModeV, float alpha = 1.0f, float blend = 1.0F, int flags = 0)
+ : base(alpha, blend, flags, StackType.Texture)
+ {
+ TexturePath = texturePath;
+ Channel = channel;
+ MappingModeU = mappingModeU;
+ MappingModeV = mappingModeV;
+ }
+ ///
+ /// Gets the texture path.
+ ///
+ ///
+ /// The texture path.
+ ///
+ public string TexturePath { get; private set; }
+ ///
+ /// Gets the uv channel.
+ ///
+ ///
+ /// The uv channel.
+ ///
+ public int Channel { get; private set; }
+ ///
+ /// Gets the U mapping mode.
+ ///
+ ///
+ /// The U mapping mode.
+ ///
+ public MappingMode MappingModeU { get; private set; }
+ ///
+ /// Gets the Vmapping mode.
+ ///
+ ///
+ /// The V mapping mode.
+ ///
+ public MappingMode MappingModeV { get; private set; }
+ }
+}
diff --git a/sources/tools/Stride.Importer.Assimp/Material/StackType.cs b/sources/tools/Stride.Importer.Assimp/Material/StackType.cs
new file mode 100644
index 0000000000..0ccae7ac5a
--- /dev/null
+++ b/sources/tools/Stride.Importer.Assimp/Material/StackType.cs
@@ -0,0 +1,15 @@
+// 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.
+
+namespace Stride.Importer.Assimp.Material
+{
+ ///
+ /// Enumeration of the different types of node in the new Assimp's material stack.
+ ///
+ public enum StackType
+ {
+ Color = 0,
+ Texture,
+ Operation
+ }
+}
diff --git a/sources/tools/Stride.Importer.Assimp/MeshConverter.cs b/sources/tools/Stride.Importer.Assimp/MeshConverter.cs
index 1a609497c8..e26824f3e3 100644
--- a/sources/tools/Stride.Importer.Assimp/MeshConverter.cs
+++ b/sources/tools/Stride.Importer.Assimp/MeshConverter.cs
@@ -1,1375 +1,1379 @@
-using Stride.Core.Diagnostics;
-using Stride.Core.Mathematics;
-using Stride.Rendering;
+// 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.Collections.Generic;
+using System.Globalization;
+using System.IO;
using Silk.NET.Assimp;
-using Mesh = Stride.Rendering.Mesh;
-using Stride.Core.IO;
-using Stride.Rendering.Materials;
+using Stride.Animations;
using Stride.Assets.Materials;
-using System.Globalization;
+using Stride.Core.Assets.Diagnostics;
+using Stride.Core.Diagnostics;
+using Stride.Core.IO;
+using Stride.Core.Mathematics;
+using Stride.Core.Serialization;
using Stride.Graphics;
using Stride.Graphics.Data;
-using PrimitiveType = Stride.Graphics.PrimitiveType;
using Stride.Importer.Common;
-using Stride.Animations;
+using Stride.Rendering;
+using Stride.Rendering.Materials;
using Stride.Rendering.Materials.ComputeColors;
-using System.IO;
-using Stride.Core.Serialization;
+using Mesh = Stride.Rendering.Mesh;
+using PrimitiveType = Stride.Graphics.PrimitiveType;
namespace Stride.Importer.Assimp
{
- public class MeshConverter
- {
- private const int NumberOfBonesPerVertex = 4;
-
- public Logger Logger { get; set; }
-
- private readonly Silk.NET.Assimp.Assimp assimp = Silk.NET.Assimp.Assimp.GetApi();
-
- public bool AllowUnsignedBlendIndices { get; set; }
-
- // Conversion data
-
- private string vfsInputFilename;
- private string vfsOutputFilename;
- private string vfsInputPath;
-
- private Quaternion rootOrientation;
- private Quaternion rootOrientationInverse;
- private Matrix rootTransform;
- private Matrix rootTransformInverse;
- private Model modelData;
-
- private readonly List nodes = new();
- private readonly Dictionary textureNameCount = new();
-
- public MeshConverter(Logger logger)
- {
- Logger = logger ?? GlobalLogger.GetLogger("Import Assimp");
- }
-
- private void ResetConvertionData()
- {
- textureNameCount.Clear();
- }
-
- public unsafe EntityInfo ExtractEntity(string inptuFilename, string outputFilename, bool extractTextureDependencies, bool deduplicateMaterials)
- {
- try
- {
- uint importFlags = 0;
- var postProcessFlags = PostProcessSteps.SortByPrimitiveType;
-
- if (deduplicateMaterials)
- {
- postProcessFlags |= PostProcessSteps.RemoveRedundantMaterials;
- }
-
- var scene = Initialize(inptuFilename, outputFilename, importFlags, (uint)postProcessFlags);
-
- // If scene is null, something went wrong inside Assimp
- if (scene == null)
- {
- var error = assimp.GetErrorStringS();
- if (error.Length > 0)
- {
- Logger.Error($"Assimp: {error}");
- }
-
- return null;
- }
-
- var materialNames = new Dictionary();
- var meshNames = new Dictionary();
- var animationNames = new Dictionary();
- var nodeNames = new Dictionary();
-
- GenerateNodeNames(scene, nodeNames);
-
- var entityInfo = new EntityInfo
- {
- Materials = ExtractMaterials(scene, materialNames),
- Models = ExtractModels(scene, meshNames, materialNames, nodeNames),
- Nodes = ExtractNodeHierarchy(scene, nodeNames),
- AnimationNodes = ExtractAnimations(scene, animationNames)
- };
-
- if (extractTextureDependencies)
- entityInfo.TextureDependencies = ExtractTextureDependencies(scene);
-
- return entityInfo;
- }
- catch
- {
- return null;
- }
- }
-
- public unsafe Model Convert(string inptuFilename, string outputFilename, bool deduplicateMaterials)
- {
- uint importFlags = 0;
- var postProcessFlags =
- PostProcessSteps.CalculateTangentSpace
- | PostProcessSteps.Triangulate
- | PostProcessSteps.GenerateNormals
- | PostProcessSteps.JoinIdenticalVertices
- | PostProcessSteps.LimitBoneWeights
- | PostProcessSteps.SortByPrimitiveType
- | PostProcessSteps.FlipWindingOrder
- | PostProcessSteps.FlipUVs;
-
- if (deduplicateMaterials)
- {
- postProcessFlags |= PostProcessSteps.RemoveRedundantMaterials;
- }
-
- var scene = Initialize(inptuFilename, outputFilename, importFlags, (uint)postProcessFlags);
- return ConvertAssimpScene(scene);
- }
-
- public unsafe AnimationInfo ConvertAnimation(string inputFilename, string outputFilename)
- {
- uint importFlags = 0;
- var postProcessFlags = PostProcessSteps.None;
-
- var scene = Initialize(inputFilename, outputFilename, importFlags, (uint)postProcessFlags);
-
- return ProcessAnimations(scene);
- }
-
- public unsafe Skeleton ConvertSkeleton(string inputFilename, string outputFilename)
- {
- uint importFlags = 0;
- var postProcessFlags = PostProcessSteps.None;
-
- var scene = Initialize(inputFilename, outputFilename, importFlags, (uint)postProcessFlags);
-
- return ProcessSkeleton(scene);
- }
-
- private unsafe Scene* Initialize(string inputFilename, string outputFilename, uint importFlags, uint postProcessFlags)
- {
- ResetConvertionData();
-
- vfsInputFilename = inputFilename;
- vfsOutputFilename = outputFilename;
- vfsInputPath = VirtualFileSystem.GetParentFolder(inputFilename);
-
- var scene = assimp.ImportFile(inputFilename, importFlags);
- scene = assimp.ApplyPostProcessing(scene, postProcessFlags);
-
- return scene;
- }
-
- private unsafe Model ConvertAssimpScene(Scene* scene)
- {
- modelData = new Model();
-
- var meshNames = new Dictionary();
- GenerateMeshNames(scene, meshNames);
-
- var nodeNames = new Dictionary();
- GenerateNodeNames(scene, nodeNames);
-
- // register the nodes and fill hierarchy
- var meshIndexToNodeIndex = new Dictionary>();
- RegisterNodes(scene->MRootNode, -1, nodeNames, meshIndexToNodeIndex);
-
- // meshes
- for (var i = 0; i < scene->MNumMeshes; ++i)
- {
- if (meshIndexToNodeIndex.ContainsKey(i))
- {
- var meshInfo = ProcessMesh(scene, scene->MMeshes[i], meshNames);
-
- foreach (var nodeIndex in meshIndexToNodeIndex[i])
- {
- var nodeMeshData = new Mesh
- {
- Draw = meshInfo.Draw,
- Name = meshInfo.Name,
- MaterialIndex = meshInfo.MaterialIndex,
- NodeIndex = nodeIndex
- };
-
- if (meshInfo.Bones != null)
- {
- nodeMeshData.Skinning = new MeshSkinningDefinition
- {
- Bones = meshInfo.Bones.ToArray()
- };
- }
-
- if (meshInfo.HasSkinningPosition && meshInfo.TotalClusterCount > 0)
- nodeMeshData.Parameters.Set(MaterialKeys.HasSkinningPosition, true);
-
- if (meshInfo.HasSkinningNormal && meshInfo.TotalClusterCount > 0)
- nodeMeshData.Parameters.Set(MaterialKeys.HasSkinningNormal, true);
-
- modelData.Meshes.Add(nodeMeshData);
- }
- }
- }
-
- // embedded texture - only to log the warning for now
- for (uint i = 0; i < scene->MNumTextures; ++i)
- {
- ExtractEmbededTexture(scene->MTextures[i]);
- }
-
- return modelData;
- }
-
- private unsafe Skeleton ProcessSkeleton(Scene* scene)
- {
- var nodeNames = new Dictionary();
- GenerateNodeNames(scene, nodeNames);
-
- // register the nodes and fill hierarchy
- var meshIndexToNodeIndex = new Dictionary>();
- RegisterNodes(scene->MRootNode, -1, nodeNames, meshIndexToNodeIndex);
-
- return new Skeleton
- {
- Nodes = nodes.ToArray()
- };
- }
-
- private unsafe AnimationInfo ProcessAnimations(Scene* scene)
- {
- var animationData = new AnimationInfo();
- var visitedNodeNames = new HashSet();
-
- if (scene->MNumAnimations > 1)
- Logger.Warning($"There are {scene->MNumAnimations} animations in this file, using only the first one.");
-
- var nodeNames = new Dictionary();
- GenerateNodeNames(scene, nodeNames);
-
- // register the nodes and fill hierarchy
- var meshIndexToNodeIndex = new Dictionary>();
- RegisterNodes(scene->MRootNode, -1, nodeNames, meshIndexToNodeIndex);
-
- for (uint i = 0; i < Math.Min(1, scene->MNumAnimations); ++i)
- {
- var aiAnim = scene->MAnimations[i];
-
- // animation speed
- var ticksPerSec = aiAnim->MTicksPerSecond;
-
- animationData.Duration = Utils.AiTimeToXkTimeSpan(aiAnim->MDuration, ticksPerSec); ;
-
- // Assimp animations have two different channels of animations ((1) on Nodes, (2) on Meshes).
- // Nevertheless the second one do not seems to be usable in assimp 3.0 so it will be ignored here.
-
- // name of the animation (dropped)
- var animName = aiAnim->MName.AsString; // used only be the logger
-
- // animation using meshes (not supported)
- for (uint meshAnimId = 0; meshAnimId < aiAnim->MNumMeshChannels; ++meshAnimId)
- {
- var meshName = aiAnim->MMeshChannels[meshAnimId]->MName.AsString;
- Logger.Warning($"Mesh animation are not currently supported. Animation '{animName}' on mesh {meshName} will be ignored");
- }
-
- // animation on nodes
- for (uint nodeAnimId = 0; nodeAnimId < aiAnim->MNumChannels; ++nodeAnimId)
- {
- var nodeAnim = aiAnim->MChannels[nodeAnimId];
- var nodeName = nodeAnim->MNodeName.AsString;
-
- if (!visitedNodeNames.Contains(nodeName))
- {
- visitedNodeNames.Add(nodeName);
- ProcessNodeAnimation(animationData.AnimationClips, nodeAnim, ticksPerSec);
- }
- else
- {
- Logger.Error($"Animation '{animName}' uses two nodes with the same name ({nodeAnim->MNodeName.AsString}). The animation cannot be resolved.");
- return null;
- }
- }
- }
-
- return animationData;
- }
-
- private unsafe void ProcessNodeAnimation(Dictionary animationClips, NodeAnim* nodeAnim, double ticksPerSec)
- {
- // Find the nodes on which the animation is performed
- var nodeName = nodeAnim->MNodeName.AsString;
-
- var animationClip = new AnimationClip();
-
- // The translation
- ProcessAnimationCurveVector(animationClip, nodeAnim->MPositionKeys, nodeAnim->MNumPositionKeys, "Transform.Position", ticksPerSec, true);
- // The rotation
- ProcessAnimationCurveQuaternion(animationClip, nodeAnim->MRotationKeys, nodeAnim->MNumRotationKeys, "Transform.Rotation", ticksPerSec);
- // The scales
- ProcessAnimationCurveVector(animationClip, nodeAnim->MScalingKeys, nodeAnim->MNumScalingKeys, "Transform.Scale", ticksPerSec, false);
-
- if (animationClip.Curves.Count > 0)
- animationClips.Add(nodeName, animationClip);
- }
-
- private unsafe void ProcessAnimationCurveVector(AnimationClip animationClip, VectorKey* keys, uint nbKeys, string partialTargetName, double ticksPerSec, bool isTranslation)
- {
- var animationCurve = new AnimationCurve();
-
- // Switch to cubic implicit interpolation mode for Vector3
- animationCurve.InterpolationType = AnimationCurveInterpolationType.Cubic;
-
- var lastKeyTime = new CompressedTimeSpan();
-
- for (uint keyId = 0; keyId < nbKeys; ++keyId)
- {
- var aiKey = keys[keyId];
-
- var key = new KeyFrameData
- {
- Time = lastKeyTime = Utils.AiTimeToXkTimeSpan(aiKey.MTime, ticksPerSec),
- Value = aiKey.MValue.ToStrideVector3()
- };
-
- if (isTranslation)
- {
- // Change of basis: key.Value = (rootTransformInverse * Matrix::Translation(key.Value) * rootTransform).TranslationVector;
- Vector3.TransformCoordinate(ref key.Value, ref rootTransform, out key.Value);
- }
- else
- {
- // Change of basis: key.Value = (rootTransformInverse * Matrix::Scaling(key.Value) * rootTransform).ScaleVector;
- var scale = Vector3.One;
- Vector3.TransformNormal(ref scale, ref rootTransformInverse, out scale);
- scale *= key.Value;
- Vector3.TransformNormal(ref scale, ref rootTransform, out key.Value);
- }
-
- animationCurve.KeyFrames.Add(key);
- if (keyId == 0 || keyId == nbKeys - 1) // discontinuity at animation first and last frame
- animationCurve.KeyFrames.Add(key); // add 2 times the same frame at discontinuities to have null gradient
- }
-
- animationClip.AddCurve(partialTargetName, animationCurve, false);
-
- if (nbKeys > 0 && animationClip.Duration < lastKeyTime)
- {
- animationClip.Duration = lastKeyTime;
- }
- }
-
- private unsafe void ProcessAnimationCurveQuaternion(AnimationClip animationClip, QuatKey* keys, uint nbKeys, string partialTargetName, double ticksPerSec)
- {
- var animationCurve = new AnimationCurve();
-
- var lastKeyTime = new CompressedTimeSpan();
-
- for (uint keyId = 0; keyId < nbKeys; ++keyId)
- {
- var aiKey = keys[keyId];
- var key = new KeyFrameData
- {
- Time = lastKeyTime = Utils.AiTimeToXkTimeSpan(aiKey.MTime, ticksPerSec),
- Value = aiKey.MValue.ToStrideQuaternion()
- };
-
- key.Value = rootOrientationInverse * key.Value * rootOrientation;
-
- animationCurve.KeyFrames.Add(key);
- }
-
- animationClip.AddCurve(partialTargetName, animationCurve, false);
-
- if (nbKeys > 0 && animationClip.Duration < lastKeyTime)
- {
- animationClip.Duration = lastKeyTime;
- }
- }
-
- private unsafe void GenerateUniqueNames(Dictionary finalNames, List baseNames, Func objectToName)
- {
- var itemNameTotalCount = new Dictionary();
- var itemNameCurrentCount = new Dictionary();
- var tempNames = new List();
-
- for (var i = 0; i < baseNames.Count; ++i)
- {
- // Clean the name by removing unwanted characters
- var itemName = baseNames[i];
-
- var itemNameSplitPosition = itemName.IndexOf('#');
- if (itemNameSplitPosition != -1)
- {
- itemName = itemName.Substring(0, itemNameSplitPosition);
- }
-
- itemNameSplitPosition = itemName.IndexOf("__");
- if (itemNameSplitPosition != -1)
- {
- itemName = itemName.Substring(0, itemNameSplitPosition);
- }
-
- // remove all bad characters
- itemName = itemName.Replace(':', '_');
- itemName = itemName.Replace(" ", string.Empty);
-
- tempNames.Add(itemName);
-
- // count the occurences of this name
- if (!itemNameTotalCount.ContainsKey(itemName))
- itemNameTotalCount.Add(itemName, 1);
- else
- itemNameTotalCount[itemName]++;
- }
-
- for (var i = 0; i < baseNames.Count; ++i)
- {
- var lItem = objectToName(i);
- var itemName = tempNames[i];
-
- if (itemNameTotalCount[itemName] > 1)
- {
- if (!itemNameCurrentCount.ContainsKey(itemName))
- itemNameCurrentCount.Add(itemName, 1);
- else
- itemNameCurrentCount[itemName]++;
-
- itemName = itemName + "_" + itemNameCurrentCount[itemName].ToString(CultureInfo.InvariantCulture);
- }
-
- finalNames.Add(lItem, itemName);
- }
- }
-
- private unsafe void GenerateMeshNames(Scene* scene, Dictionary meshNames)
- {
- var baseNames = new List();
- for (uint i = 0; i < scene->MNumMeshes; i++)
- {
- var lMesh = scene->MMeshes[i];
- baseNames.Add(lMesh->MName.AsString);
- }
-
- GenerateUniqueNames(meshNames, baseNames, i => (IntPtr)scene->MMeshes[i]);
- }
-
- private unsafe void GenerateAnimationNames(Scene* scene, Dictionary animationNames)
- {
- var baseNames = new List();
- for (uint i = 0; i < scene->MNumAnimations; i++)
- {
- var lAnimation = scene->MAnimations[i];
- var animationName = lAnimation->MName.AsString;
- baseNames.Add(animationName);
- }
-
- GenerateUniqueNames(animationNames, baseNames, i => (IntPtr)scene->MAnimations[i]);
- }
-
- private unsafe void GenerateNodeNames(Scene* scene, Dictionary nodeNames)
- {
- var baseNames = new List();
- var orderedNodes = new List();
-
- GetNodeNames(scene->MRootNode, baseNames, orderedNodes);
- GenerateUniqueNames(nodeNames, baseNames, i => orderedNodes[i]);
- }
-
- private unsafe void GetNodeNames(Node* node, List nodeNames, List orderedNodes)
- {
- nodeNames.Add(node->MName.AsString);
- orderedNodes.Add((IntPtr)node);
-
- for (uint i = 0; i < node->MNumChildren; ++i)
- {
- GetNodeNames(node->MChildren[i], nodeNames, orderedNodes);
- }
- }
-
- private unsafe void RegisterNodes(Node* fromNode, int parentIndex, Dictionary nodeNames, Dictionary> meshIndexToNodeIndex)
- {
- var nodeIndex = nodes.Count;
-
- // assign the index of the node to the index of the mesh
- for (uint m = 0; m < fromNode->MNumMeshes; ++m)
- {
- var meshIndex = fromNode->MMeshes[m];
-
- if (!meshIndexToNodeIndex.TryGetValue((int)meshIndex, out var nodeIndices))
- {
- nodeIndices = new List();
- meshIndexToNodeIndex.Add((int)meshIndex, nodeIndices);
- }
-
- nodeIndices.Add(nodeIndex);
- }
-
- // Create node
- var modelNodeDefinition = new ModelNodeDefinition
- {
- ParentIndex = parentIndex,
- Name = nodeNames[(IntPtr)fromNode],
- Flags = ModelNodeFlags.Default
- };
-
- // Extract scene scaling and rotation from the root node.
- // Bake scaling into all node's positions and rotation into the 1st-level nodes.
- if (parentIndex == -1)
- {
- rootTransform = fromNode->MTransformation.ToStrideMatrix();
-
- rootTransform.Decompose(out var rootScaling, out rootOrientation, out var rootTranslation);
-
- rootTransformInverse = Matrix.Invert(rootTransform);
- rootOrientationInverse = Quaternion.Invert(rootOrientation);
-
- modelNodeDefinition.Transform.Rotation = Quaternion.Identity;
- modelNodeDefinition.Transform.Scale = Vector3.One;
- }
- else
- {
- var transform = rootTransformInverse * fromNode->MTransformation.ToStrideMatrix() * rootTransform;
- transform.Decompose(out modelNodeDefinition.Transform.Scale, out modelNodeDefinition.Transform.Rotation, out modelNodeDefinition.Transform.Position);
- }
-
- nodes.Add(modelNodeDefinition);
-
- // register the children
- for (uint child = 0; child < fromNode->MNumChildren; ++child)
- {
- RegisterNodes(fromNode->MChildren[child], nodeIndex, nodeNames, meshIndexToNodeIndex);
- }
- }
-
- private unsafe MeshInfo ProcessMesh(Scene* scene, Silk.NET.Assimp.Mesh* mesh, Dictionary meshNames)
- {
- List bones = null;
- var hasSkinningPosition = false;
- var hasSkinningNormal = false;
- var totalClusterCount = 0;
-
- // Build the bone's indices/weights and attach bones to NodeData
- //(bones info are present in the mesh so that is why we have to perform that here)
-
- var vertexIndexToBoneIdWeight = new List>();
- if (mesh->MNumBones > 0)
- {
- bones = new List();
-
- // TODO: change this to support shared meshes across nodes
-
- // size of the array is already known
- vertexIndexToBoneIdWeight.Capacity = (int)mesh->MNumVertices;
- for (var i = 0; i < (int)mesh->MNumVertices; i++)
- {
- vertexIndexToBoneIdWeight.Add(new List<(short, float)>());
- }
-
- // Build skinning clusters and fill controls points data stutcture
- for (uint boneId = 0; boneId < mesh->MNumBones; ++boneId)
- {
- var bone = mesh->MBones[boneId];
-
- // Fill controlPts with bone controls on the mesh
- for (uint vtxWeightId = 0; vtxWeightId < bone->MNumWeights; ++vtxWeightId)
- {
- var vtxWeight = bone->MWeights[vtxWeightId];
- vertexIndexToBoneIdWeight[(int)vtxWeight.MVertexId].Add(((short)boneId, vtxWeight.MWeight));
- }
-
- // find the node where the bone is mapped - based on the name(?)
- var nodeIndex = -1;
- var boneName = bone->MName.AsString;
- for (var nodeDefId = 0; nodeDefId < nodes.Count; ++nodeDefId)
- {
- var nodeDef = nodes[nodeDefId];
- if (nodeDef.Name == boneName)
- {
- nodeIndex = nodeDefId;
- break;
- }
- }
-
- if (nodeIndex == -1)
- {
- Logger.Error($"No node found for none {boneId}:{boneName}");
- nodeIndex = 0;
- }
-
- bones.Add(new MeshBoneDefinition
- {
- NodeIndex = nodeIndex,
- LinkToMeshMatrix = rootTransformInverse * bone->MOffsetMatrix.ToStrideMatrix() * rootTransform
- });
- }
-
- NormalizeVertexWeights(vertexIndexToBoneIdWeight, NumberOfBonesPerVertex);
-
- totalClusterCount = (int)mesh->MNumBones;
- if (totalClusterCount > 0)
- hasSkinningPosition = true;
- }
-
- // Build the vertex declaration
- var vertexElements = new List();
- var vertexStride = 0;
-
- var positionOffset = vertexStride;
- vertexElements.Add(VertexElement.Position(0, vertexStride));
- vertexStride += sizeof(Vector3);
-
- var normalOffset = vertexStride;
- if (mesh->MNormals != null)
- {
- vertexElements.Add(VertexElement.Normal(0, vertexStride));
- vertexStride += sizeof(Vector3);
- }
-
- var uvOffset = vertexStride;
- var sizeUV = sizeof(Vector2); // 3D uv not supported
- for (uint uvChannel = 0; uvChannel < Utils.GetNumUVChannels(mesh); ++uvChannel)
- {
- vertexElements.Add(VertexElement.TextureCoordinate((int)uvChannel, vertexStride));
- vertexStride += sizeUV;
- }
-
- var colorOffset = vertexStride;
- var sizeColor = sizeof(Color);
- for (uint colorChannel = 0; colorChannel < Utils.GetNumColorChannels(mesh); ++colorChannel)
- {
- vertexElements.Add(VertexElement.Color((int)colorChannel, vertexStride));
- vertexStride += sizeColor;
- }
-
- var tangentOffset = vertexStride;
- if (mesh->MTangents != null)
- {
- vertexElements.Add(VertexElement.Tangent(0, vertexStride));
- vertexStride += sizeof(Vector3);
- }
-
- var bitangentOffset = vertexStride;
- if (mesh->MTangents != null)
- {
- vertexElements.Add(VertexElement.BiTangent(0, vertexStride));
- vertexStride += sizeof(Vector3);
- }
-
- var blendIndicesOffset = vertexStride;
- var controlPointIndices16 = (AllowUnsignedBlendIndices && totalClusterCount > 256) || (!AllowUnsignedBlendIndices && totalClusterCount > 128);
- if (vertexIndexToBoneIdWeight.Count > 0)
- {
- if (controlPointIndices16)
- {
- if (AllowUnsignedBlendIndices)
- {
- vertexElements.Add(new VertexElement("BLENDINDICES", 0, PixelFormat.R16G16B16A16_UInt, vertexStride));
- vertexStride += sizeof(ushort) * 4;
- }
- else
- {
- vertexElements.Add(new VertexElement("BLENDINDICES", 0, PixelFormat.R16G16B16A16_SInt, vertexStride));
- vertexStride += sizeof(short) * 4;
- }
- }
- else
- {
- if (AllowUnsignedBlendIndices)
- {
- vertexElements.Add(new VertexElement("BLENDINDICES", 0, PixelFormat.R8G8B8A8_UInt, vertexStride));
- vertexStride += sizeof(byte) * 4;
- }
- else
- {
- vertexElements.Add(new VertexElement("BLENDINDICES", 0, PixelFormat.R8G8B8A8_SInt, vertexStride));
- vertexStride += sizeof(sbyte) * 4;
- }
- }
- }
-
- var blendWeightOffset = vertexStride;
- if (vertexIndexToBoneIdWeight.Count > 0)
- {
- vertexElements.Add(new VertexElement("BLENDWEIGHT", 0, PixelFormat.R32G32B32A32_Float, vertexStride));
- vertexStride += sizeof(float) * 4;
- }
-
- // Build the vertices data buffer
- var vertexBuffer = new byte[vertexStride * mesh->MNumVertices];
- fixed (byte* vertexBufferPtr = &vertexBuffer[0])
- {
- var vbPointer = vertexBufferPtr;
- for (uint i = 0; i < mesh->MNumVertices; i++)
- {
- var positionPointer = (Vector3*)(vbPointer + positionOffset);
- *positionPointer = mesh->MVertices[i].ToStrideVector3();
-
- Vector3.TransformCoordinate(ref *positionPointer, ref rootTransform, out *positionPointer);
-
- if (mesh->MNormals != null)
- {
- var normalPointer = (Vector3*)(vbPointer + normalOffset);
- *normalPointer = mesh->MNormals[i].ToStrideVector3();
-
- Vector3.TransformNormal(ref *normalPointer, ref rootTransform, out *normalPointer);
-
- if (float.IsNaN(normalPointer->X) || float.IsNaN(normalPointer->Y) || float.IsNaN(normalPointer->Z))
- *normalPointer = new Vector3(1, 0, 0);
- else
- normalPointer->Normalize();
- }
-
- for (uint uvChannel = 0; uvChannel < Utils.GetNumUVChannels(mesh); ++uvChannel)
- {
- var textureCoord = mesh->MTextureCoords[(int)uvChannel][i];
- *((Vector2*)(vbPointer + uvOffset + sizeUV * uvChannel)) = new Vector2(textureCoord.X, textureCoord.Y); // 3D uv not supported
- }
-
- for (uint colorChannel = 0; colorChannel < Utils.GetNumColorChannels(mesh); ++colorChannel)
- {
- var color = mesh->MColors[(int)colorChannel][i].ToStrideColor();
- *((Color*)(vbPointer + colorOffset + sizeColor * colorChannel)) = color;
- }
-
- if (mesh->MTangents != null)
- {
- var tangentPointer = (Vector3*)(vbPointer + tangentOffset);
- var bitangentPointer = (Vector3*)(vbPointer + bitangentOffset);
- *tangentPointer = mesh->MTangents[i].ToStrideVector3();
- *bitangentPointer = mesh->MBitangents[i].ToStrideVector3();
- if (float.IsNaN(tangentPointer->X) || float.IsNaN(tangentPointer->Y) || float.IsNaN(tangentPointer->Z) ||
- float.IsNaN(bitangentPointer->X) || float.IsNaN(bitangentPointer->Y) || float.IsNaN(bitangentPointer->Z))
- {
- //assert(mesh->HasNormals());
- var normalPointer = ((Vector3*)(vbPointer + normalOffset));
- Vector3 c1 = Vector3.Cross(*normalPointer, new Vector3(0.0f, 0.0f, 1.0f));
- Vector3 c2 = Vector3.Cross(*normalPointer, new Vector3(0.0f, 1.0f, 0.0f));
-
- if (c1.LengthSquared() > c2.LengthSquared())
- *tangentPointer = c1;
- else
- *tangentPointer = c2;
- *bitangentPointer = Vector3.Cross(*normalPointer, *tangentPointer);
- }
- tangentPointer->Normalize();
- bitangentPointer->Normalize();
- }
-
- if (vertexIndexToBoneIdWeight.Count > 0)
- {
- for (var bone = 0; bone < NumberOfBonesPerVertex; ++bone)
- {
- if (controlPointIndices16)
- {
- if (AllowUnsignedBlendIndices)
- ((ushort*)(vbPointer + blendIndicesOffset))[bone] = (ushort)vertexIndexToBoneIdWeight[(int)i][bone].Item1;
- else
- ((short*)(vbPointer + blendIndicesOffset))[bone] = vertexIndexToBoneIdWeight[(int)i][bone].Item1;
- }
- else
- {
- if (AllowUnsignedBlendIndices)
- (vbPointer + blendIndicesOffset)[bone] = (byte)vertexIndexToBoneIdWeight[(int)i][bone].Item1;
- else
- ((sbyte*)(vbPointer + blendIndicesOffset))[bone] = (sbyte)vertexIndexToBoneIdWeight[(int)i][bone].Item1;
- }
-
- ((float*)(vbPointer + blendWeightOffset))[bone] = vertexIndexToBoneIdWeight[(int)i][bone].Item2;
- }
- }
-
- vbPointer += vertexStride;
- }
- }
-
- // Build the indices data buffer
- var nbIndices = 3 * mesh->MNumFaces;
- byte[] indexBuffer;
- var is32BitIndex = mesh->MNumVertices > 65535;
- if (is32BitIndex)
- indexBuffer = new byte[sizeof(uint) * nbIndices];
- else
- indexBuffer = new byte[sizeof(ushort) * nbIndices];
-
- fixed (byte* indexBufferPtr = &indexBuffer[0])
- {
- var ibPointer = indexBufferPtr;
-
- for (uint i = 0; i < mesh->MNumFaces; i++)
- {
- if (is32BitIndex)
- {
- for (int j = 0; j < 3; ++j)
- {
- *((uint*)ibPointer) = mesh->MFaces[(int)i].MIndices[j];
- ibPointer += sizeof(uint);
- }
- }
- else
- {
- for (int j = 0; j < 3; ++j)
- {
- *((ushort*)ibPointer) = (ushort)(mesh->MFaces[(int)i].MIndices[j]);
- ibPointer += sizeof(ushort);
- }
- }
- }
- }
-
- // Build the mesh data
- var vertexDeclaration = new VertexDeclaration(vertexElements.ToArray());
- var vertexBufferBinding = new VertexBufferBinding(GraphicsSerializerExtensions.ToSerializableVersion(new BufferData(BufferFlags.VertexBuffer, vertexBuffer)), vertexDeclaration, (int)mesh->MNumVertices, vertexDeclaration.VertexStride, 0);
- var indexBufferBinding = new IndexBufferBinding(GraphicsSerializerExtensions.ToSerializableVersion(new BufferData(BufferFlags.IndexBuffer, indexBuffer)), is32BitIndex, (int)nbIndices, 0);
-
- var drawData = new MeshDraw
- {
- VertexBuffers = new VertexBufferBinding[] { vertexBufferBinding },
- IndexBuffer = indexBufferBinding,
- PrimitiveType = PrimitiveType.TriangleList,
- DrawCount = (int)nbIndices
- };
-
- return new MeshInfo
- {
- Draw = drawData,
- Name = meshNames[(IntPtr)mesh],
- Bones = bones,
- MaterialIndex = (int)mesh->MMaterialIndex,
- HasSkinningPosition = hasSkinningPosition,
- HasSkinningNormal = hasSkinningNormal,
- TotalClusterCount = totalClusterCount
- };
- }
-
- private void NormalizeVertexWeights(List> controlPts, int nbBoneByVertex)
- {
- for (var vertexId = 0; vertexId < controlPts.Count; ++vertexId)
- {
- var curVertexWeights = controlPts[vertexId];
-
- // check that one vertex has not more than 'nbBoneByVertex' associated bones
- if (curVertexWeights.Count > nbBoneByVertex)
- {
- Logger.Warning(
- "The input file contains vertices that are associated to more than {0} bones. In current version of the system, a single vertex can only be associated to {0} bones. Extra bones will be ignored",
- new ArgumentOutOfRangeException("To much bones influencing a single vertex"));
- }
-
- // resize the weights so that they contains exactly the number of bone weights required
- while (curVertexWeights.Count < nbBoneByVertex)
- {
- curVertexWeights.Add((0, 0));
- }
-
- var totalWeight = 0.0f;
- for (var boneId = 0; boneId < nbBoneByVertex; ++boneId)
- totalWeight += curVertexWeights[boneId].Item2;
-
- if (totalWeight == 0.0) // Assimp weights are positive, so in this case all weights are nulls
- continue;
-
- for (var boneId = 0; boneId < nbBoneByVertex; ++boneId)
- curVertexWeights[boneId] = (curVertexWeights[boneId].Item1, curVertexWeights[boneId].Item2 / totalWeight);
- }
- }
+ public class MeshConverter
+ {
+ private const int NumberOfBonesPerVertex = 4;
+
+ public Logger Logger { get; set; }
+
+ private readonly Silk.NET.Assimp.Assimp assimp = Silk.NET.Assimp.Assimp.GetApi();
+
+ public bool AllowUnsignedBlendIndices { get; set; }
+
+ // Conversion data
+
+ private string vfsInputFilename;
+ private string vfsOutputFilename;
+ private string vfsInputPath;
+
+ private Quaternion rootOrientation;
+ private Quaternion rootOrientationInverse;
+ private Matrix rootTransform;
+ private Matrix rootTransformInverse;
+ private Model modelData;
+
+ private readonly List nodes = new();
+ private readonly Dictionary textureNameCount = new();
+
+ public MeshConverter(Logger logger)
+ {
+ Logger = logger ?? GlobalLogger.GetLogger("Import Assimp");
+ }
+
+ private void ResetConvertionData()
+ {
+ textureNameCount.Clear();
+ }
+
+ public unsafe EntityInfo ExtractEntity(string inputFilename, string outputFilename, bool extractTextureDependencies, bool deduplicateMaterials)
+ {
+ try
+ {
+ uint importFlags = 0;
+ var postProcessFlags = PostProcessSteps.SortByPrimitiveType;
+
+ if (deduplicateMaterials)
+ {
+ postProcessFlags |= PostProcessSteps.RemoveRedundantMaterials;
+ }
+
+ var scene = Initialize(inputFilename, outputFilename, importFlags, (uint)postProcessFlags);
+
+ // If scene is null, something went wrong inside Assimp
+ if (scene == null)
+ {
+ var error = assimp.GetErrorStringS();
+ if (error.Length > 0)
+ {
+ Logger.Error($"Assimp: {error}");
+ }
+
+ return null;
+ }
+
+ var materialNames = new Dictionary();
+ var meshNames = new Dictionary();
+ var animationNames = new Dictionary();
+ var nodeNames = new Dictionary();
+
+ GenerateNodeNames(scene, nodeNames);
+
+ var entityInfo = new EntityInfo
+ {
+ Materials = ExtractMaterials(scene, materialNames),
+ Models = ExtractModels(scene, meshNames, materialNames, nodeNames),
+ Nodes = ExtractNodeHierarchy(scene, nodeNames),
+ AnimationNodes = ExtractAnimations(scene, animationNames)
+ };
+
+ if (extractTextureDependencies)
+ entityInfo.TextureDependencies = ExtractTextureDependencies(scene);
+
+ return entityInfo;
+ }
+ catch(Exception ex)
+ {
+ Logger.Error($"Exception has occured during Entity extraction : {ex.Message}");
+ throw;
+ }
+ }
+
+ public unsafe Model Convert(string inptuFilename, string outputFilename, bool deduplicateMaterials)
+ {
+ uint importFlags = 0;
+ var postProcessFlags =
+ PostProcessSteps.CalculateTangentSpace
+ | PostProcessSteps.Triangulate
+ | PostProcessSteps.GenerateNormals
+ | PostProcessSteps.JoinIdenticalVertices
+ | PostProcessSteps.LimitBoneWeights
+ | PostProcessSteps.SortByPrimitiveType
+ | PostProcessSteps.FlipWindingOrder
+ | PostProcessSteps.FlipUVs;
+
+ if (deduplicateMaterials)
+ {
+ postProcessFlags |= PostProcessSteps.RemoveRedundantMaterials;
+ }
+
+ var scene = Initialize(inptuFilename, outputFilename, importFlags, (uint)postProcessFlags);
+ return ConvertAssimpScene(scene);
+ }
+
+ public unsafe AnimationInfo ConvertAnimation(string inputFilename, string outputFilename)
+ {
+ uint importFlags = 0;
+ var postProcessFlags = PostProcessSteps.None;
+
+ var scene = Initialize(inputFilename, outputFilename, importFlags, (uint)postProcessFlags);
+
+ return ProcessAnimations(scene);
+ }
+
+ public unsafe Rendering.Skeleton ConvertSkeleton(string inputFilename, string outputFilename)
+ {
+ uint importFlags = 0;
+ var postProcessFlags = PostProcessSteps.None;
+
+ var scene = Initialize(inputFilename, outputFilename, importFlags, (uint)postProcessFlags);
+
+ return ProcessSkeleton(scene);
+ }
+
+ private unsafe Scene* Initialize(string inputFilename, string outputFilename, uint importFlags, uint postProcessFlags)
+ {
+ ResetConvertionData();
+
+ vfsInputFilename = inputFilename;
+ vfsOutputFilename = outputFilename;
+ vfsInputPath = VirtualFileSystem.GetParentFolder(inputFilename);
+
+ var scene = assimp.ImportFile(inputFilename, importFlags);
+ scene = assimp.ApplyPostProcessing(scene, postProcessFlags);
+
+ return scene;
+ }
+
+ private unsafe Model ConvertAssimpScene(Scene* scene)
+ {
+ modelData = new Model();
+
+ var meshNames = new Dictionary();
+ GenerateMeshNames(scene, meshNames);
+
+ var nodeNames = new Dictionary();
+ GenerateNodeNames(scene, nodeNames);
+
+ // register the nodes and fill hierarchy
+ var meshIndexToNodeIndex = new Dictionary>();
+ RegisterNodes(scene->MRootNode, -1, nodeNames, meshIndexToNodeIndex);
+
+ // meshes
+ for (var i = 0; i < scene->MNumMeshes; ++i)
+ {
+ if (meshIndexToNodeIndex.ContainsKey(i))
+ {
+ var meshInfo = ProcessMesh(scene, scene->MMeshes[i], meshNames);
+
+ foreach (var nodeIndex in meshIndexToNodeIndex[i])
+ {
+ var nodeMeshData = new Mesh
+ {
+ Draw = meshInfo.Draw,
+ Name = meshInfo.Name,
+ MaterialIndex = meshInfo.MaterialIndex,
+ NodeIndex = nodeIndex
+ };
+
+ if (meshInfo.Bones != null)
+ {
+ nodeMeshData.Skinning = new MeshSkinningDefinition
+ {
+ Bones = meshInfo.Bones.ToArray()
+ };
+ }
+
+ if (meshInfo.HasSkinningPosition && meshInfo.TotalClusterCount > 0)
+ nodeMeshData.Parameters.Set(MaterialKeys.HasSkinningPosition, true);
+
+ if (meshInfo.HasSkinningNormal && meshInfo.TotalClusterCount > 0)
+ nodeMeshData.Parameters.Set(MaterialKeys.HasSkinningNormal, true);
+
+ modelData.Meshes.Add(nodeMeshData);
+ }
+ }
+ }
+
+ // embedded texture - only to log the warning for now
+ for (uint i = 0; i < scene->MNumTextures; ++i)
+ {
+ ExtractEmbededTexture(scene->MTextures[i]);
+ }
+
+ return modelData;
+ }
+
+ private unsafe Rendering.Skeleton ProcessSkeleton(Scene* scene)
+ {
+ var nodeNames = new Dictionary();
+ GenerateNodeNames(scene, nodeNames);
+
+ // register the nodes and fill hierarchy
+ var meshIndexToNodeIndex = new Dictionary>();
+ RegisterNodes(scene->MRootNode, -1, nodeNames, meshIndexToNodeIndex);
+
+ return new Rendering.Skeleton
+ {
+ Nodes = nodes.ToArray()
+ };
+ }
+
+ private unsafe AnimationInfo ProcessAnimations(Scene* scene)
+ {
+ var animationData = new AnimationInfo();
+ var visitedNodeNames = new HashSet();
+
+ if (scene->MNumAnimations > 1)
+ Logger.Warning($"There are {scene->MNumAnimations} animations in this file, using only the first one.");
+
+ var nodeNames = new Dictionary();
+ GenerateNodeNames(scene, nodeNames);
+
+ // register the nodes and fill hierarchy
+ var meshIndexToNodeIndex = new Dictionary>();
+ RegisterNodes(scene->MRootNode, -1, nodeNames, meshIndexToNodeIndex);
+
+ for (uint i = 0; i < Math.Min(1, scene->MNumAnimations); ++i)
+ {
+ var aiAnim = scene->MAnimations[i];
+
+ // animation speed
+ var ticksPerSec = aiAnim->MTicksPerSecond;
+
+ animationData.Duration = Utils.AiTimeToStrideTimeSpan(aiAnim->MDuration, ticksPerSec);
+
+ // Assimp animations have two different channels of animations ((1) on Nodes, (2) on Meshes).
+ // Nevertheless the second one do not seems to be usable in assimp 3.0 so it will be ignored here.
+
+ // name of the animation (dropped)
+ var animName = aiAnim->MName.AsString; // used only be the logger
+
+ // animation using meshes (not supported)
+ for (uint meshAnimId = 0; meshAnimId < aiAnim->MNumMeshChannels; ++meshAnimId)
+ {
+ var meshName = aiAnim->MMeshChannels[meshAnimId]->MName.AsString;
+ Logger.Warning($"Mesh animations are not currently supported. Animation '{animName}' on mesh {meshName} will be ignored");
+ }
+
+ // animation on nodes
+ for (uint nodeAnimId = 0; nodeAnimId < aiAnim->MNumChannels; ++nodeAnimId)
+ {
+ var nodeAnim = aiAnim->MChannels[nodeAnimId];
+ var nodeName = nodeAnim->MNodeName.AsString;
+
+ if (!visitedNodeNames.Contains(nodeName))
+ {
+ visitedNodeNames.Add(nodeName);
+ ProcessNodeAnimation(animationData.AnimationClips, nodeAnim, ticksPerSec);
+ }
+ else
+ {
+ Logger.Error($"Animation '{animName}' uses two nodes with the same name ({nodeAnim->MNodeName.AsString}). The animation cannot be resolved.");
+ return null;
+ }
+ }
+ }
+
+ return animationData;
+ }
+
+ private unsafe void ProcessNodeAnimation(Dictionary animationClips, NodeAnim* nodeAnim, double ticksPerSec)
+ {
+ // Find the nodes on which the animation is performed
+ var nodeName = nodeAnim->MNodeName.AsString;
+
+ var animationClip = new AnimationClip();
+
+ // The translation
+ ProcessAnimationCurveVector(animationClip, nodeAnim->MPositionKeys, nodeAnim->MNumPositionKeys, "Transform.Position", ticksPerSec, true);
+ // The rotation
+ ProcessAnimationCurveQuaternion(animationClip, nodeAnim->MRotationKeys, nodeAnim->MNumRotationKeys, "Transform.Rotation", ticksPerSec);
+ // The scales
+ ProcessAnimationCurveVector(animationClip, nodeAnim->MScalingKeys, nodeAnim->MNumScalingKeys, "Transform.Scale", ticksPerSec, false);
+
+ if (animationClip.Curves.Count > 0)
+ animationClips.Add(nodeName, animationClip);
+ }
+
+ private unsafe void ProcessAnimationCurveVector(AnimationClip animationClip, VectorKey* keys, uint nbKeys, string partialTargetName, double ticksPerSec, bool isTranslation)
+ {
+ var animationCurve = new AnimationCurve();
+
+ // Switch to cubic implicit interpolation mode for Vector3
+ animationCurve.InterpolationType = AnimationCurveInterpolationType.Cubic;
+
+ var lastKeyTime = new CompressedTimeSpan();
+
+ for (uint keyId = 0; keyId < nbKeys; ++keyId)
+ {
+ var aiKey = keys[keyId];
+
+ var key = new KeyFrameData
+ {
+ Time = lastKeyTime = Utils.AiTimeToStrideTimeSpan(aiKey.MTime, ticksPerSec),
+ Value = aiKey.MValue.ToStrideVector3()
+ };
+
+ if (isTranslation)
+ {
+ // Change of basis: key.Value = (rootTransformInverse * Matrix::Translation(key.Value) * rootTransform).TranslationVector;
+ Vector3.TransformCoordinate(ref key.Value, ref rootTransform, out key.Value);
+ }
+ else
+ {
+ // Change of basis: key.Value = (rootTransformInverse * Matrix::Scaling(key.Value) * rootTransform).ScaleVector;
+ var scale = Vector3.One;
+ Vector3.TransformNormal(ref scale, ref rootTransformInverse, out scale);
+ scale *= key.Value;
+ Vector3.TransformNormal(ref scale, ref rootTransform, out key.Value);
+ }
+
+ animationCurve.KeyFrames.Add(key);
+ if (keyId == 0 || keyId == nbKeys - 1) // discontinuity at animation first and last frame
+ animationCurve.KeyFrames.Add(key); // add 2 times the same frame at discontinuities to have null gradient
+ }
+
+ animationClip.AddCurve(partialTargetName, animationCurve, false);
+
+ if (nbKeys > 0 && animationClip.Duration < lastKeyTime)
+ {
+ animationClip.Duration = lastKeyTime;
+ }
+ }
+
+ private unsafe void ProcessAnimationCurveQuaternion(AnimationClip animationClip, QuatKey* keys, uint nbKeys, string partialTargetName, double ticksPerSec)
+ {
+ var animationCurve = new AnimationCurve();
+
+ var lastKeyTime = new CompressedTimeSpan();
+
+ for (uint keyId = 0; keyId < nbKeys; ++keyId)
+ {
+ var aiKey = keys[keyId];
+ var key = new KeyFrameData
+ {
+ Time = lastKeyTime = Utils.AiTimeToStrideTimeSpan(aiKey.MTime, ticksPerSec),
+ Value = aiKey.MValue.ToStrideQuaternion()
+ };
+
+ key.Value = rootOrientationInverse * key.Value * rootOrientation;
+
+ animationCurve.KeyFrames.Add(key);
+ }
+
+ animationClip.AddCurve(partialTargetName, animationCurve, false);
+
+ if (nbKeys > 0 && animationClip.Duration < lastKeyTime)
+ {
+ animationClip.Duration = lastKeyTime;
+ }
+ }
+
+ private unsafe void GenerateUniqueNames(Dictionary finalNames, List baseNames, Func objectToName)
+ {
+ var itemNameTotalCount = new Dictionary();
+ var itemNameCurrentCount = new Dictionary();
+ var tempNames = new List();
+
+ for (var i = 0; i < baseNames.Count; ++i)
+ {
+ // Clean the name by removing unwanted characters
+ var itemName = baseNames[i];
+
+ var itemNameSplitPosition = itemName.IndexOf('#');
+ if (itemNameSplitPosition != -1)
+ {
+ itemName = itemName.Substring(0, itemNameSplitPosition);
+ }
+
+ itemNameSplitPosition = itemName.IndexOf("__");
+ if (itemNameSplitPosition != -1)
+ {
+ itemName = itemName.Substring(0, itemNameSplitPosition);
+ }
+
+ // remove all bad characters
+ itemName = itemName.Replace(':', '_');
+ itemName = itemName.Replace(" ", string.Empty);
+
+ tempNames.Add(itemName);
+
+ // count the occurences of this name
+ if (!itemNameTotalCount.ContainsKey(itemName))
+ itemNameTotalCount.Add(itemName, 1);
+ else
+ itemNameTotalCount[itemName]++;
+ }
+
+ for (var i = 0; i < baseNames.Count; ++i)
+ {
+ var lItem = objectToName(i);
+ var itemName = tempNames[i];
+
+ if (itemNameTotalCount[itemName] > 1)
+ {
+ if (!itemNameCurrentCount.ContainsKey(itemName))
+ itemNameCurrentCount.Add(itemName, 1);
+ else
+ itemNameCurrentCount[itemName]++;
+
+ itemName = itemName + "_" + itemNameCurrentCount[itemName].ToString(CultureInfo.InvariantCulture);
+ }
+
+ finalNames.Add(lItem, itemName);
+ }
+ }
+
+ private unsafe void GenerateMeshNames(Scene* scene, Dictionary meshNames)
+ {
+ var baseNames = new List();
+ for (uint i = 0; i < scene->MNumMeshes; i++)
+ {
+ var lMesh = scene->MMeshes[i];
+ baseNames.Add(lMesh->MName.AsString);
+ }
+
+ GenerateUniqueNames(meshNames, baseNames, i => (IntPtr)scene->MMeshes[i]);
+ }
+
+ private unsafe void GenerateAnimationNames(Scene* scene, Dictionary animationNames)
+ {
+ var baseNames = new List();
+ for (uint i = 0; i < scene->MNumAnimations; i++)
+ {
+ var lAnimation = scene->MAnimations[i];
+ var animationName = lAnimation->MName.AsString;
+ baseNames.Add(animationName);
+ }
+
+ GenerateUniqueNames(animationNames, baseNames, i => (IntPtr)scene->MAnimations[i]);
+ }
+
+ private unsafe void GenerateNodeNames(Scene* scene, Dictionary nodeNames)
+ {
+ var baseNames = new List();
+ var orderedNodes = new List();
+
+ GetNodeNames(scene->MRootNode, baseNames, orderedNodes);
+ GenerateUniqueNames(nodeNames, baseNames, i => orderedNodes[i]);
+ }
+
+ private unsafe void GetNodeNames(Node* node, List nodeNames, List orderedNodes)
+ {
+ nodeNames.Add(node->MName.AsString);
+ orderedNodes.Add((IntPtr)node);
+
+ for (uint i = 0; i < node->MNumChildren; ++i)
+ {
+ GetNodeNames(node->MChildren[i], nodeNames, orderedNodes);
+ }
+ }
+
+ private unsafe void RegisterNodes(Node* fromNode, int parentIndex, Dictionary nodeNames, Dictionary> meshIndexToNodeIndex)
+ {
+ var nodeIndex = nodes.Count;
+
+ // assign the index of the node to the index of the mesh
+ for (uint m = 0; m < fromNode->MNumMeshes; ++m)
+ {
+ var meshIndex = fromNode->MMeshes[m];
+
+ if (!meshIndexToNodeIndex.TryGetValue((int)meshIndex, out var nodeIndices))
+ {
+ nodeIndices = new List();
+ meshIndexToNodeIndex.Add((int)meshIndex, nodeIndices);
+ }
+
+ nodeIndices.Add(nodeIndex);
+ }
+
+ // Create node
+ var modelNodeDefinition = new ModelNodeDefinition
+ {
+ ParentIndex = parentIndex,
+ Name = nodeNames[(IntPtr)fromNode],
+ Flags = ModelNodeFlags.Default
+ };
+
+ // Extract scene scaling and rotation from the root node.
+ // Bake scaling into all node's positions and rotation into the 1st-level nodes.
+ if (parentIndex == -1)
+ {
+ rootTransform = fromNode->MTransformation.ToStrideMatrix();
+
+ rootTransform.Decompose(out var rootScaling, out rootOrientation, out var rootTranslation);
+
+ rootTransformInverse = Matrix.Invert(rootTransform);
+ rootOrientationInverse = Quaternion.Invert(rootOrientation);
+
+ modelNodeDefinition.Transform.Rotation = Quaternion.Identity;
+ modelNodeDefinition.Transform.Scale = Vector3.One;
+ }
+ else
+ {
+ var transform = rootTransformInverse * fromNode->MTransformation.ToStrideMatrix() * rootTransform;
+ transform.Decompose(out modelNodeDefinition.Transform.Scale, out modelNodeDefinition.Transform.Rotation, out modelNodeDefinition.Transform.Position);
+ }
+
+ nodes.Add(modelNodeDefinition);
+
+ // register the children
+ for (uint child = 0; child < fromNode->MNumChildren; ++child)
+ {
+ RegisterNodes(fromNode->MChildren[child], nodeIndex, nodeNames, meshIndexToNodeIndex);
+ }
+ }
+
+ private unsafe MeshInfo ProcessMesh(Scene* scene, Silk.NET.Assimp.Mesh* mesh, Dictionary meshNames)
+ {
+ List bones = null;
+ var hasSkinningPosition = false;
+ var hasSkinningNormal = false;
+ var totalClusterCount = 0;
+
+ // Build the bone's indices/weights and attach bones to NodeData
+ //(bones info are present in the mesh so that is why we have to perform that here)
+
+ var vertexIndexToBoneIdWeight = new List>();
+ if (mesh->MNumBones > 0)
+ {
+ bones = new List();
+
+ // TODO: change this to support shared meshes across nodes
+
+ // size of the array is already known
+ vertexIndexToBoneIdWeight.Capacity = (int)mesh->MNumVertices;
+ for (var i = 0; i < (int)mesh->MNumVertices; i++)
+ {
+ vertexIndexToBoneIdWeight.Add(new List<(short, float)>());
+ }
+
+ // Build skinning clusters and fill controls points data stutcture
+ for (uint boneId = 0; boneId < mesh->MNumBones; ++boneId)
+ {
+ var bone = mesh->MBones[boneId];
+
+ // Fill controlPts with bone controls on the mesh
+ for (uint vtxWeightId = 0; vtxWeightId < bone->MNumWeights; ++vtxWeightId)
+ {
+ var vtxWeight = bone->MWeights[vtxWeightId];
+ vertexIndexToBoneIdWeight[(int)vtxWeight.MVertexId].Add(((short)boneId, vtxWeight.MWeight));
+ }
+
+ // find the node where the bone is mapped - based on the name(?)
+ var nodeIndex = -1;
+ var boneName = bone->MName.AsString;
+ for (var nodeDefId = 0; nodeDefId < nodes.Count; ++nodeDefId)
+ {
+ var nodeDef = nodes[nodeDefId];
+ if (nodeDef.Name == boneName)
+ {
+ nodeIndex = nodeDefId;
+ break;
+ }
+ }
+
+ if (nodeIndex == -1)
+ {
+ Logger.Error($"No node found for name {boneId}:{boneName}");
+ nodeIndex = 0;
+ }
+
+ bones.Add(new MeshBoneDefinition
+ {
+ NodeIndex = nodeIndex,
+ LinkToMeshMatrix = rootTransformInverse * bone->MOffsetMatrix.ToStrideMatrix() * rootTransform
+ });
+ }
+
+ NormalizeVertexWeights(vertexIndexToBoneIdWeight, NumberOfBonesPerVertex);
+
+ totalClusterCount = (int)mesh->MNumBones;
+ if (totalClusterCount > 0)
+ hasSkinningPosition = true;
+ }
+
+ // Build the vertex declaration
+ var vertexElements = new List();
+ var vertexStride = 0;
+
+ var positionOffset = vertexStride;
+ vertexElements.Add(VertexElement.Position(0, vertexStride));
+ vertexStride += sizeof(Vector3);
+
+ var normalOffset = vertexStride;
+ if (mesh->MNormals != null)
+ {
+ vertexElements.Add(VertexElement.Normal(0, vertexStride));
+ vertexStride += sizeof(Vector3);
+ }
+
+ var uvOffset = vertexStride;
+ var sizeUV = sizeof(Vector2); // 3D uv not supported
+ for (uint uvChannel = 0; uvChannel < Utils.GetNumUVChannels(mesh); ++uvChannel)
+ {
+ vertexElements.Add(VertexElement.TextureCoordinate((int)uvChannel, vertexStride));
+ vertexStride += sizeUV;
+ }
+
+ var colorOffset = vertexStride;
+ var sizeColor = sizeof(Color);
+ for (uint colorChannel = 0; colorChannel < Utils.GetNumColorChannels(mesh); ++colorChannel)
+ {
+ vertexElements.Add(VertexElement.Color((int)colorChannel, vertexStride));
+ vertexStride += sizeColor;
+ }
+
+ var tangentOffset = vertexStride;
+ if (mesh->MTangents != null)
+ {
+ vertexElements.Add(VertexElement.Tangent(0, vertexStride));
+ vertexStride += sizeof(Vector3);
+ }
+
+ var bitangentOffset = vertexStride;
+ if (mesh->MTangents != null)
+ {
+ vertexElements.Add(VertexElement.BiTangent(0, vertexStride));
+ vertexStride += sizeof(Vector3);
+ }
+
+ var blendIndicesOffset = vertexStride;
+ var controlPointIndices16 = (AllowUnsignedBlendIndices && totalClusterCount > 256) || (!AllowUnsignedBlendIndices && totalClusterCount > 128);
+ if (vertexIndexToBoneIdWeight.Count > 0)
+ {
+ if (controlPointIndices16)
+ {
+ if (AllowUnsignedBlendIndices)
+ {
+ vertexElements.Add(new VertexElement("BLENDINDICES", 0, PixelFormat.R16G16B16A16_UInt, vertexStride));
+ vertexStride += sizeof(ushort) * 4;
+ }
+ else
+ {
+ vertexElements.Add(new VertexElement("BLENDINDICES", 0, PixelFormat.R16G16B16A16_SInt, vertexStride));
+ vertexStride += sizeof(short) * 4;
+ }
+ }
+ else
+ {
+ if (AllowUnsignedBlendIndices)
+ {
+ vertexElements.Add(new VertexElement("BLENDINDICES", 0, PixelFormat.R8G8B8A8_UInt, vertexStride));
+ vertexStride += sizeof(byte) * 4;
+ }
+ else
+ {
+ vertexElements.Add(new VertexElement("BLENDINDICES", 0, PixelFormat.R8G8B8A8_SInt, vertexStride));
+ vertexStride += sizeof(sbyte) * 4;
+ }
+ }
+ }
+
+ var blendWeightOffset = vertexStride;
+ if (vertexIndexToBoneIdWeight.Count > 0)
+ {
+ vertexElements.Add(new VertexElement("BLENDWEIGHT", 0, PixelFormat.R32G32B32A32_Float, vertexStride));
+ vertexStride += sizeof(float) * 4;
+ }
+
+ // Build the vertices data buffer
+ var vertexBuffer = new byte[vertexStride * mesh->MNumVertices];
+ fixed (byte* vertexBufferPtr = &vertexBuffer[0])
+ {
+ var vbPointer = vertexBufferPtr;
+ for (uint i = 0; i < mesh->MNumVertices; i++)
+ {
+ var positionPointer = (Vector3*)(vbPointer + positionOffset);
+ *positionPointer = mesh->MVertices[i].ToStrideVector3();
+
+ Vector3.TransformCoordinate(ref *positionPointer, ref rootTransform, out *positionPointer);
+
+ if (mesh->MNormals != null)
+ {
+ var normalPointer = (Vector3*)(vbPointer + normalOffset);
+ *normalPointer = mesh->MNormals[i].ToStrideVector3();
+
+ Vector3.TransformNormal(ref *normalPointer, ref rootTransform, out *normalPointer);
+
+ if (float.IsNaN(normalPointer->X) || float.IsNaN(normalPointer->Y) || float.IsNaN(normalPointer->Z))
+ *normalPointer = new Vector3(1, 0, 0);
+ else
+ normalPointer->Normalize();
+ }
+
+ for (uint uvChannel = 0; uvChannel < Utils.GetNumUVChannels(mesh); ++uvChannel)
+ {
+ var textureCoord = mesh->MTextureCoords[(int)uvChannel][i];
+ *((Vector2*)(vbPointer + uvOffset + sizeUV * uvChannel)) = new Vector2(textureCoord.X, textureCoord.Y); // 3D uv not supported
+ }
+
+ for (uint colorChannel = 0; colorChannel < Utils.GetNumColorChannels(mesh); ++colorChannel)
+ {
+ var color = mesh->MColors[(int)colorChannel][i].ToStrideColor();
+ *((Color*)(vbPointer + colorOffset + sizeColor * colorChannel)) = color;
+ }
+
+ if (mesh->MTangents != null)
+ {
+ var tangentPointer = (Vector3*)(vbPointer + tangentOffset);
+ var bitangentPointer = (Vector3*)(vbPointer + bitangentOffset);
+ *tangentPointer = mesh->MTangents[i].ToStrideVector3();
+ *bitangentPointer = mesh->MBitangents[i].ToStrideVector3();
+ if (float.IsNaN(tangentPointer->X) || float.IsNaN(tangentPointer->Y) || float.IsNaN(tangentPointer->Z) ||
+ float.IsNaN(bitangentPointer->X) || float.IsNaN(bitangentPointer->Y) || float.IsNaN(bitangentPointer->Z))
+ {
+ var normalPointer = ((Vector3*)(vbPointer + normalOffset));
+ Vector3 c1 = Vector3.Cross(*normalPointer, new Vector3(0.0f, 0.0f, 1.0f));
+ Vector3 c2 = Vector3.Cross(*normalPointer, new Vector3(0.0f, 1.0f, 0.0f));
+
+ if (c1.LengthSquared() > c2.LengthSquared())
+ *tangentPointer = c1;
+ else
+ *tangentPointer = c2;
+ *bitangentPointer = Vector3.Cross(*normalPointer, *tangentPointer);
+ }
+ tangentPointer->Normalize();
+ bitangentPointer->Normalize();
+ }
+
+ if (vertexIndexToBoneIdWeight.Count > 0)
+ {
+ for (var bone = 0; bone < NumberOfBonesPerVertex; ++bone)
+ {
+ if (controlPointIndices16)
+ {
+ if (AllowUnsignedBlendIndices)
+ ((ushort*)(vbPointer + blendIndicesOffset))[bone] = (ushort)vertexIndexToBoneIdWeight[(int)i][bone].Item1;
+ else
+ ((short*)(vbPointer + blendIndicesOffset))[bone] = vertexIndexToBoneIdWeight[(int)i][bone].Item1;
+ }
+ else
+ {
+ if (AllowUnsignedBlendIndices)
+ (vbPointer + blendIndicesOffset)[bone] = (byte)vertexIndexToBoneIdWeight[(int)i][bone].Item1;
+ else
+ ((sbyte*)(vbPointer + blendIndicesOffset))[bone] = (sbyte)vertexIndexToBoneIdWeight[(int)i][bone].Item1;
+ }
+
+ ((float*)(vbPointer + blendWeightOffset))[bone] = vertexIndexToBoneIdWeight[(int)i][bone].Item2;
+ }
+ }
+
+ vbPointer += vertexStride;
+ }
+ }
+
+ // Build the indices data buffer
+ var nbIndices = 3 * mesh->MNumFaces;
+ byte[] indexBuffer;
+ var is32BitIndex = mesh->MNumVertices > 65535;
+ if (is32BitIndex)
+ indexBuffer = new byte[sizeof(uint) * nbIndices];
+ else
+ indexBuffer = new byte[sizeof(ushort) * nbIndices];
+
+ fixed (byte* indexBufferPtr = &indexBuffer[0])
+ {
+ var ibPointer = indexBufferPtr;
+
+ for (uint i = 0; i < mesh->MNumFaces; i++)
+ {
+ if (is32BitIndex)
+ {
+ for (int j = 0; j < 3; ++j)
+ {
+ *((uint*)ibPointer) = mesh->MFaces[(int)i].MIndices[j];
+ ibPointer += sizeof(uint);
+ }
+ }
+ else
+ {
+ for (int j = 0; j < 3; ++j)
+ {
+ *((ushort*)ibPointer) = (ushort)(mesh->MFaces[(int)i].MIndices[j]);
+ ibPointer += sizeof(ushort);
+ }
+ }
+ }
+ }
+
+ // Build the mesh data
+ var vertexDeclaration = new VertexDeclaration(vertexElements.ToArray());
+ var vertexBufferBinding = new VertexBufferBinding(GraphicsSerializerExtensions.ToSerializableVersion(new BufferData(BufferFlags.VertexBuffer, vertexBuffer)), vertexDeclaration, (int)mesh->MNumVertices, vertexDeclaration.VertexStride, 0);
+ var indexBufferBinding = new IndexBufferBinding(GraphicsSerializerExtensions.ToSerializableVersion(new BufferData(BufferFlags.IndexBuffer, indexBuffer)), is32BitIndex, (int)nbIndices, 0);
+
+ var drawData = new MeshDraw
+ {
+ VertexBuffers = new VertexBufferBinding[] { vertexBufferBinding },
+ IndexBuffer = indexBufferBinding,
+ PrimitiveType = PrimitiveType.TriangleList,
+ DrawCount = (int)nbIndices
+ };
+
+ return new MeshInfo
+ {
+ Draw = drawData,
+ Name = meshNames[(IntPtr)mesh],
+ Bones = bones,
+ MaterialIndex = (int)mesh->MMaterialIndex,
+ HasSkinningPosition = hasSkinningPosition,
+ HasSkinningNormal = hasSkinningNormal,
+ TotalClusterCount = totalClusterCount
+ };
+ }
+
+ private void NormalizeVertexWeights(List> controlPts, int nbBoneByVertex)
+ {
+ for (var vertexId = 0; vertexId < controlPts.Count; ++vertexId)
+ {
+ var curVertexWeights = controlPts[vertexId];
+
+ // check that one vertex has not more than 'nbBoneByVertex' associated bones
+ if (curVertexWeights.Count > nbBoneByVertex)
+ {
+ Logger.Warning(
+ $"The input file contains vertices that are associated to more than {curVertexWeights.Count} bones. In current version of the system, a single vertex can only be associated to {nbBoneByVertex} bones. Extra bones will be ignored",
+ new ArgumentOutOfRangeException("To much bones influencing a single vertex"));
+ }
+
+ // resize the weights so that they contains exactly the number of bone weights required
+ while (curVertexWeights.Count < nbBoneByVertex)
+ {
+ curVertexWeights.Add((0, 0));
+ }
+
+ var totalWeight = 0.0f;
+ for (var boneId = 0; boneId < nbBoneByVertex; ++boneId)
+ totalWeight += curVertexWeights[boneId].Item2;
+
+ if (totalWeight <= float.Epsilon) // Assimp weights are positive, so in this case all weights are nulls
+ continue;
+
+ for (var boneId = 0; boneId < nbBoneByVertex; ++boneId)
+ curVertexWeights[boneId] = (curVertexWeights[boneId].Item1, curVertexWeights[boneId].Item2 / totalWeight);
+ }
+ }
#pragma warning disable IDE0060 // Remove unused parameter
- private unsafe void ExtractEmbededTexture(Silk.NET.Assimp.Texture* texture)
+ private unsafe void ExtractEmbededTexture(Silk.NET.Assimp.Texture* texture)
#pragma warning restore IDE0060 // Remove unused parameter
- {
- Logger.Warning("The input file contains embeded textures. Embeded textures are not currently supported. This texture will be ignored",
- new NotImplementedException("Embeded textures extraction"));
- }
-
- private unsafe Dictionary ExtractMaterials(Scene* scene, Dictionary materialNames)
- {
- GenerateMaterialNames(scene, materialNames);
-
- var materials = new Dictionary();
- for (uint i = 0; i < scene->MNumMaterials; i++)
- {
- var lMaterial = scene->MMaterials[i];
- var materialName = materialNames[(IntPtr)lMaterial];
- materials.Add(materialName, ProcessMeshMaterial(lMaterial));
- }
- return materials;
- }
-
- private unsafe void GenerateMaterialNames(Scene* scene, Dictionary materialNames)
- {
- var baseNames = new List();
- for (uint i = 0; i < scene->MNumMaterials; i++)
- {
- var lMaterial = scene->MMaterials[i];
-
- var aiMaterial = new AssimpString();
- var materialName = assimp.GetMaterialString(lMaterial, Silk.NET.Assimp.Assimp.MaterialNameBase, 0, 0, ref aiMaterial) == Return.ReturnSuccess ? aiMaterial.AsString : "Material";
- baseNames.Add(materialName);
- }
-
- GenerateUniqueNames(materialNames, baseNames, i => (IntPtr)scene->MMaterials[i]);
- }
-
- private unsafe MaterialAsset ProcessMeshMaterial(Silk.NET.Assimp.Material* pMaterial)
- {
- var finalMaterial = new MaterialAsset();
-
- float specPower = 0;
- float opacity = 0;
-
- bool hasDiffColor = false;
- bool hasSpecColor = false;
- bool hasAmbientColor = false;
- bool hasEmissiveColor = false;
- bool hasReflectiveColor = false;
- bool hasSpecPower = false;
- bool hasOpacity = false;
-
- var diffColor = System.Numerics.Vector4.Zero;
- var specColor = System.Numerics.Vector4.Zero;
- var ambientColor = System.Numerics.Vector4.Zero;
- var emissiveColor = System.Numerics.Vector4.Zero;
- var reflectiveColor = System.Numerics.Vector4.Zero;
- var dummyColor = System.Numerics.Vector4.Zero;
-
- SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorDiffuseBase, ref hasDiffColor, ref diffColor, true);// always keep black color for diffuse
- SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorSpecularBase, ref hasSpecColor, ref specColor, IsNotBlackColor(specColor));
- SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorAmbientBase, ref hasAmbientColor, ref ambientColor, IsNotBlackColor(specColor));
- SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorEmissiveBase, ref hasEmissiveColor, ref emissiveColor, IsNotBlackColor(emissiveColor));
- SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorReflectiveBase, ref hasReflectiveColor, ref reflectiveColor, IsNotBlackColor(reflectiveColor));
- SetMaterialFloatArrayFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialShininessBase, ref hasSpecPower, specPower, specPower > 0);
- SetMaterialFloatArrayFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialOpacityBase, ref hasOpacity, opacity, opacity < 1.0);
-
- BuildLayeredSurface(pMaterial, hasDiffColor, false, diffColor.ToStrideColor(), 0.0f, TextureType.TextureTypeDiffuse, finalMaterial);
- BuildLayeredSurface(pMaterial, hasSpecColor, false, specColor.ToStrideColor(), 0.0f, TextureType.TextureTypeSpecular, finalMaterial);
- BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.TextureTypeNormals, finalMaterial);
- BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.TextureTypeDisplacement, finalMaterial);
- BuildLayeredSurface(pMaterial, hasAmbientColor, false, ambientColor.ToStrideColor(), 0.0f, TextureType.TextureTypeAmbient, finalMaterial);
- BuildLayeredSurface(pMaterial, false, hasOpacity, dummyColor.ToStrideColor(), opacity, TextureType.TextureTypeOpacity, finalMaterial);
- BuildLayeredSurface(pMaterial, false, hasSpecPower, dummyColor.ToStrideColor(), specPower, TextureType.TextureTypeShininess, finalMaterial);
- BuildLayeredSurface(pMaterial, hasEmissiveColor, false, emissiveColor.ToStrideColor(), 0.0f, TextureType.TextureTypeEmissive, finalMaterial);
- BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.TextureTypeHeight, finalMaterial);
- BuildLayeredSurface(pMaterial, hasReflectiveColor, false, reflectiveColor.ToStrideColor(), 0.0f, TextureType.TextureTypeReflection, finalMaterial);
-
- return finalMaterial;
- }
-
- private unsafe void SetMaterialColorFlag(Silk.NET.Assimp.Material* pMaterial, string materialColorBase, ref bool hasMatColor, ref System.Numerics.Vector4 matColor, bool condition)
- {
- if (assimp.GetMaterialColor(pMaterial, materialColorBase, 0, 0, ref matColor) == Return.ReturnSuccess && condition)
- {
- hasMatColor = true;
- }
- }
- private unsafe void SetMaterialFloatArrayFlag(Silk.NET.Assimp.Material* pMaterial, string materialBase, ref bool hasMatProperty, float matColor, bool condition)
- {
- if(assimp.GetMaterialFloatArray(pMaterial, materialBase, 0, 0, &matColor, (uint*)0x0) == Return.ReturnSuccess && condition)
- {
- hasMatProperty = true;
- }
- }
-
- private bool IsNotBlackColor(System.Numerics.Vector4 diffColor)
- {
- return diffColor != System.Numerics.Vector4.Zero;
- }
-
- private unsafe void BuildLayeredSurface(Silk.NET.Assimp.Material* pMat, bool hasBaseColor, bool hasBaseValue, Color4 baseColor, float baseValue, TextureType textureType, MaterialAsset finalMaterial)
- {
- var nbTextures = assimp.GetMaterialTextureCount(pMat, textureType);
-
- IComputeColor computeColorNode = null;
- int textureCount = 0;
- if (nbTextures == 0)
- {
- if (hasBaseColor)
- {
- computeColorNode = new ComputeColor(baseColor);
- }
- //else if (hasBaseValue)
- //{
- // computeColorNode = gcnew MaterialFloatComputeNode(baseValue);
- //}
- }
- else
- {
- computeColorNode = GenerateOneTextureTypeLayers(pMat, textureType, textureCount, finalMaterial);
- }
-
- if (computeColorNode == null)
- {
- return;
- }
-
- if (textureType == TextureType.TextureTypeDiffuse)
- {
- if (assimp.GetMaterialTextureCount(pMat, TextureType.TextureTypeLightmap) > 0)
- {
- var lightMap = GenerateOneTextureTypeLayers(pMat, TextureType.TextureTypeLightmap, textureCount, finalMaterial);
- if (lightMap != null)
- computeColorNode = new ComputeBinaryColor(computeColorNode, lightMap, BinaryOperator.Add);
- }
-
- finalMaterial.Attributes.Diffuse = new MaterialDiffuseMapFeature(computeColorNode);
-
- // TODO TEMP: Set a default diffuse model
- finalMaterial.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature();
- }
- else if (textureType == TextureType.TextureTypeSpecular)
- {
- var specularFeature = new MaterialSpecularMapFeature
- {
- SpecularMap = computeColorNode
- };
- finalMaterial.Attributes.Specular = specularFeature;
-
- // TODO TEMP: Set a default specular model
- var specularModel = new MaterialSpecularMicrofacetModelFeature
- {
- Fresnel = new MaterialSpecularMicrofacetFresnelSchlick(),
- Visibility = new MaterialSpecularMicrofacetVisibilityImplicit(),
- NormalDistribution = new MaterialSpecularMicrofacetNormalDistributionBlinnPhong()
- };
- finalMaterial.Attributes.SpecularModel = specularModel;
- }
- else if (textureType == TextureType.TextureTypeEmissive)
- {
- // TODO: Add support
- }
- else if (textureType == TextureType.TextureTypeAmbient)
- {
- // TODO: Add support
- }
- else if (textureType == TextureType.TextureTypeReflection)
- {
- // TODO: Add support
- }
- if (textureType == TextureType.TextureTypeOpacity)
- {
- // TODO: Add support
- }
- else if (textureType == TextureType.TextureTypeShininess)
- {
- // TODO: Add support
- }
- if (textureType == TextureType.TextureTypeSpecular)
- {
- // TODO: Add support
- }
- else if (textureType == TextureType.TextureTypeNormals)
- {
- finalMaterial.Attributes.Surface = new MaterialNormalMapFeature(computeColorNode);
- }
- else if (textureType == TextureType.TextureTypeDisplacement)
- {
- // TODO: Add support
- }
- else if (textureType == TextureType.TextureTypeHeight)
- {
- // TODO: Add support
- }
- }
-
- private unsafe IComputeColor GenerateOneTextureTypeLayers(Silk.NET.Assimp.Material* pMat, TextureType textureType, int textureCount, MaterialAsset finalMaterial)
- {
- var stack = Material.Materials.ConvertAssimpStackCppToCs(assimp, pMat, textureType);
-
- var compositionFathers = new Stack();
-
- var sets = new Stack();
- sets.Push(0);
-
- var nbTextures = assimp.GetMaterialTextureCount(pMat, textureType);
-
- IComputeColor curComposition = null, newCompositionFather = null;
-
- var isRootElement = true;
- IComputeColor rootMaterial = null;
-
- while (!stack.IsEmpty)
- {
- var top = stack.Pop();
-
- IComputeColor curCompositionFather = null;
- if (!isRootElement)
- {
- if (compositionFathers.Count == 0)
- Logger.Error("Texture Stack Invalid : Operand without Operation.");
-
- curCompositionFather = compositionFathers.Pop();
- }
-
- var type = top.type;
- var strength = top.blend;
- var alpha = top.alpha;
- int set = sets.Peek();
-
- if (type == Material.StackType.Operation)
- {
- set = sets.Pop();
- var realTop = (Material.StackOperation)top;
- var op = realTop.operation;
- var binNode = new ComputeBinaryColor(null, null, BinaryOperator.Add);
-
- binNode.Operator = op switch
- {
- Material.Operation.Add3ds or Material.Operation.AddMaya => BinaryOperator.Add,
- Material.Operation.Multiply3ds or Material.Operation.MultiplyMaya => BinaryOperator.Multiply,
- _ => BinaryOperator.Add,
- };
- curComposition = binNode;
- }
- else if (type == Material.StackType.Color)
- {
- var realTop = (Material.StackColor)top;
- var ol = realTop.color;
- curComposition = new ComputeColor(new Color4(ol.R, ol.G, ol.B, alpha));
- }
- else if (type == Material.StackType.Texture)
- {
- var realTop = (Material.StackTexture)top;
- var texPath = realTop.texturePath;
- var indexUV = realTop.channel;
- curComposition = GetTextureReferenceNode(vfsOutputFilename, texPath, (uint)indexUV, Vector2.One, ConvertTextureMode(realTop.mappingModeU), ConvertTextureMode(realTop.mappingModeV), finalMaterial);
- }
-
- newCompositionFather = curComposition;
-
- if (strength != 1.0f)
- {
- var strengthAlpha = strength;
- if (type != Material.StackType.Color)
- strengthAlpha *= alpha;
-
- var factorComposition = new ComputeFloat4(new Vector4(strength, strength, strength, strengthAlpha));
- curComposition = new ComputeBinaryColor(curComposition, factorComposition, BinaryOperator.Multiply);
- }
- else if (alpha != 1.0f && type != Material.StackType.Color)
- {
- var factorComposition = new ComputeFloat4(new Vector4(1.0f, 1.0f, 1.0f, alpha));
- curComposition = new ComputeBinaryColor(curComposition, factorComposition, BinaryOperator.Multiply);
- }
-
- if (isRootElement)
- {
- rootMaterial = curComposition;
- isRootElement = false;
- compositionFathers.Push(curCompositionFather);
- }
- else
- {
- if (set == 0)
- {
- ((ComputeBinaryColor)curCompositionFather).LeftChild = curComposition;
- compositionFathers.Push(curCompositionFather);
- sets.Push(1);
- }
- else if (set == 1)
- {
- ((ComputeBinaryColor)curCompositionFather).RightChild = curComposition;
- }
- else
- {
- Logger.Error($"Texture Stack Invalid : Invalid Operand Number {set}.");
- }
- }
-
- if (type == Material.StackType.Operation)
- {
- compositionFathers.Push(newCompositionFather);
- sets.Push(0);
- }
- }
-
- return rootMaterial;
- }
-
- private static TextureAddressMode ConvertTextureMode(Material.MappingMode mappingMode)
- {
- return mappingMode switch
- {
- Material.MappingMode.Clamp => TextureAddressMode.Clamp,
- Material.MappingMode.Decal => TextureAddressMode.Border,
- Material.MappingMode.Mirror => TextureAddressMode.Mirror,
- _ => TextureAddressMode.Wrap,
- };
- }
-
- private ComputeTextureColor GetTextureReferenceNode(string vfsOutputPath, string sourceTextureFile, uint textureUVSetIndex, Vector2 textureUVscaling, TextureAddressMode addressModeU, TextureAddressMode addressModeV, MaterialAsset finalMaterial)
- {
- // TODO: compare with FBX importer - see if there could be some conflict between texture names
- var textureValue = TextureLayerGenerator.GenerateMaterialTextureNode(vfsOutputPath, sourceTextureFile, textureUVSetIndex, textureUVscaling, addressModeU, addressModeV, Logger);
-
- var attachedReference = AttachedReferenceManager.GetAttachedReference(textureValue.Texture);
- var referenceName = attachedReference.Url;
-
- // find a new and correctName
- if (!textureNameCount.ContainsKey(referenceName))
- textureNameCount.Add(referenceName, 1);
- else
- {
- int count = textureNameCount[referenceName];
- textureNameCount[referenceName] = count + 1;
- referenceName = string.Concat(referenceName, "_", count);
- }
-
- return textureValue;
- }
-
- private unsafe List ExtractModels(Scene* scene, Dictionary meshNames, Dictionary materialNames, Dictionary nodeNames)
- {
- GenerateMeshNames(scene, meshNames);
-
- var meshList = new List();
- for (uint i = 0; i < scene->MNumMeshes; ++i)
- {
- var mesh = scene->MMeshes[i];
- var lMaterial = scene->MMaterials[mesh->MMaterialIndex];
-
- var meshParams = new MeshParameters
- {
- MeshName = meshNames[(IntPtr)mesh],
- MaterialName = materialNames[(IntPtr)lMaterial],
- NodeName = SearchMeshNode(scene->MRootNode, i, nodeNames)
- };
-
- meshList.Add(meshParams);
- }
-
- return meshList;
- }
-
- private unsafe string SearchMeshNode(Node* node, uint meshIndex, Dictionary nodeNames)
- {
- for (uint i = 0; i < node->MNumMeshes; ++i)
- {
- if (node->MMeshes[i] == meshIndex)
- return nodeNames[(IntPtr)node];
- }
-
- for (uint i = 0; i < node->MNumChildren; ++i)
- {
- var res = SearchMeshNode(node->MChildren[i], meshIndex, nodeNames);
- if (res != null)
- return res;
- }
-
- return null;
- }
-
- private unsafe List ExtractNodeHierarchy(Scene* scene, Dictionary nodeNames)
- {
- var allNodes = new List();
- GetNodes(scene->MRootNode, 0, nodeNames, allNodes);
- return allNodes;
- }
-
- private unsafe void GetNodes(Node* node, int depth, Dictionary nodeNames, List allNodes)
- {
- var newNodeInfo = new NodeInfo
- {
- Name = nodeNames[(IntPtr)node],
- Depth = depth,
- Preserve = true
- };
-
- allNodes.Add(newNodeInfo);
- for (uint i = 0; i < node->MNumChildren; ++i)
- GetNodes(node->MChildren[i], depth + 1, nodeNames, allNodes);
- }
-
- private unsafe List ExtractAnimations(Scene* scene, Dictionary animationNames)
- {
- if (scene->MNumAnimations == 0)
- return null;
-
- GenerateAnimationNames(scene, animationNames);
-
- var animationList = new List();
- foreach (var animationName in animationNames)
- {
- animationList.Add(animationName.Value);
- }
-
- return animationList;
- }
-
- private unsafe List ExtractTextureDependencies(Scene* scene)
- {
- var textureNames = new List();
-
- // texture search is done by type so we need to loop on them
- var allTextureTypes = new TextureType[]
- {
- TextureType.TextureTypeDiffuse,
- TextureType.TextureTypeSpecular,
- TextureType.TextureTypeAmbient,
- TextureType.TextureTypeEmissive,
- TextureType.TextureTypeHeight,
- TextureType.TextureTypeNormals,
- TextureType.TextureTypeShininess,
- TextureType.TextureTypeOpacity,
- TextureType.TextureTypeDisplacement,
- TextureType.TextureTypeLightmap,
- TextureType.TextureTypeReflection
- };
-
- for (uint i = 0; i < scene->MNumMaterials; i++)
- {
- foreach (var textureType in allTextureTypes)
- {
- var lMaterial = scene->MMaterials[i];
- var nbTextures = assimp.GetMaterialTextureCount(lMaterial, textureType);
-
- for (uint j = 0; j < nbTextures; ++j)
- {
- var path = new AssimpString();
- var mapping = TextureMapping.TextureMappingUV;
- uint uvIndex = 0;
- var blend = 0.0f;
- var textureOp = TextureOp.TextureOpMultiply;
- var mapMode = TextureMapMode.TextureMapModeWrap;
- uint flags = 0;
-
- if (assimp.GetMaterialTexture(lMaterial, textureType, j, ref path, ref mapping, ref uvIndex, ref blend, ref textureOp, ref mapMode, ref flags) == Return.ReturnSuccess)
- {
- var relFileName = path.AsString;
- var fileNameToUse = Path.Combine(vfsInputPath, relFileName);
- textureNames.Add(fileNameToUse);
- break;
- }
- }
- }
- }
-
- return textureNames;
- }
- }
-
- public class MeshInfo
- {
- public MeshDraw Draw;
- public List Bones;
- public string Name;
- public int MaterialIndex;
- public bool HasSkinningPosition = false;
- public bool HasSkinningNormal = false;
- public int TotalClusterCount = 0;
- }
-
- public class MaterialInstantiation
- {
- public List Parameters;
- public MaterialAsset Material;
- public string MaterialName;
- }
-
- public unsafe class MaterialInstances
- {
- public Silk.NET.Assimp.Material* SourceMaterial;
- public List Instances = new();
- public string MaterialsName;
- }
+ {
+ Logger.Warning("The input file contains embeded textures. Embeded textures are not currently supported. This texture will be ignored",
+ new NotImplementedException("Embeded textures extraction"));
+ }
+
+ private unsafe Dictionary ExtractMaterials(Scene* scene, Dictionary materialNames)
+ {
+ GenerateMaterialNames(scene, materialNames);
+
+ var materials = new Dictionary();
+ for (uint i = 0; i < scene->MNumMaterials; i++)
+ {
+ var lMaterial = scene->MMaterials[i];
+ var materialName = materialNames[(IntPtr)lMaterial];
+ materials.Add(materialName, ProcessMeshMaterial(lMaterial));
+ }
+ return materials;
+ }
+
+ private unsafe void GenerateMaterialNames(Scene* scene, Dictionary materialNames)
+ {
+ var baseNames = new List();
+ for (uint i = 0; i < scene->MNumMaterials; i++)
+ {
+ var lMaterial = scene->MMaterials[i];
+
+ var aiMaterial = new AssimpString();
+ var materialName = assimp.GetMaterialString(lMaterial, Silk.NET.Assimp.Assimp.MaterialNameBase, 0, 0, ref aiMaterial) == Return.Success ? aiMaterial.AsString : "Material";
+ baseNames.Add(materialName);
+ }
+
+ GenerateUniqueNames(materialNames, baseNames, i => (IntPtr)scene->MMaterials[i]);
+ }
+
+ private unsafe MaterialAsset ProcessMeshMaterial(Silk.NET.Assimp.Material* pMaterial)
+ {
+ var finalMaterial = new MaterialAsset();
+
+ float specPower = 0;
+ float opacity = 0;
+
+ bool hasDiffColor = false;
+ bool hasSpecColor = false;
+ bool hasAmbientColor = false;
+ bool hasEmissiveColor = false;
+ bool hasReflectiveColor = false;
+ bool hasSpecPower = false;
+ bool hasOpacity = false;
+
+ var diffColor = System.Numerics.Vector4.Zero;
+ var specColor = System.Numerics.Vector4.Zero;
+ var ambientColor = System.Numerics.Vector4.Zero;
+ var emissiveColor = System.Numerics.Vector4.Zero;
+ var reflectiveColor = System.Numerics.Vector4.Zero;
+ var dummyColor = System.Numerics.Vector4.Zero;
+
+ SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorDiffuseBase, ref hasDiffColor, ref diffColor, true);// always keep black color for diffuse
+ SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorSpecularBase, ref hasSpecColor, ref specColor, IsNotBlackColor(specColor));
+ SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorAmbientBase, ref hasAmbientColor, ref ambientColor, IsNotBlackColor(specColor));
+ SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorEmissiveBase, ref hasEmissiveColor, ref emissiveColor, IsNotBlackColor(emissiveColor));
+ SetMaterialColorFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialColorReflectiveBase, ref hasReflectiveColor, ref reflectiveColor, IsNotBlackColor(reflectiveColor));
+ SetMaterialFloatArrayFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialShininessBase, ref hasSpecPower, specPower, specPower > 0);
+ SetMaterialFloatArrayFlag(pMaterial, Silk.NET.Assimp.Assimp.MaterialOpacityBase, ref hasOpacity, opacity, opacity < 1.0);
+
+ BuildLayeredSurface(pMaterial, hasDiffColor, false, diffColor.ToStrideColor(), 0.0f, TextureType.Diffuse, finalMaterial);
+ BuildLayeredSurface(pMaterial, hasSpecColor, false, specColor.ToStrideColor(), 0.0f, TextureType.Specular, finalMaterial);
+ BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Normals, finalMaterial);
+ BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Displacement, finalMaterial);
+ BuildLayeredSurface(pMaterial, hasAmbientColor, false, ambientColor.ToStrideColor(), 0.0f, TextureType.Ambient, finalMaterial);
+ BuildLayeredSurface(pMaterial, false, hasOpacity, dummyColor.ToStrideColor(), opacity, TextureType.Opacity, finalMaterial);
+ BuildLayeredSurface(pMaterial, false, hasSpecPower, dummyColor.ToStrideColor(), specPower, TextureType.Shininess, finalMaterial);
+ BuildLayeredSurface(pMaterial, hasEmissiveColor, false, emissiveColor.ToStrideColor(), 0.0f, TextureType.Emissive, finalMaterial);
+ BuildLayeredSurface(pMaterial, false, false, dummyColor.ToStrideColor(), 0.0f, TextureType.Height, finalMaterial);
+ BuildLayeredSurface(pMaterial, hasReflectiveColor, false, reflectiveColor.ToStrideColor(), 0.0f, TextureType.Reflection, finalMaterial);
+
+ return finalMaterial;
+ }
+
+ private unsafe void SetMaterialColorFlag(Silk.NET.Assimp.Material* pMaterial, string materialColorBase, ref bool hasMatColor, ref System.Numerics.Vector4 matColor, bool condition)
+ {
+ if (assimp.GetMaterialColor(pMaterial, materialColorBase, 0, 0, ref matColor) == Return.Success && condition)
+ {
+ hasMatColor = true;
+ }
+ }
+ private unsafe void SetMaterialFloatArrayFlag(Silk.NET.Assimp.Material* pMaterial, string materialBase, ref bool hasMatProperty, float matColor, bool condition)
+ {
+ if(assimp.GetMaterialFloatArray(pMaterial, materialBase, 0, 0, &matColor, (uint*)0x0) == Return.Success && condition)
+ {
+ hasMatProperty = true;
+ }
+ }
+
+ private bool IsNotBlackColor(System.Numerics.Vector4 diffColor)
+ {
+ return diffColor != System.Numerics.Vector4.Zero;
+ }
+
+ private unsafe void BuildLayeredSurface(Silk.NET.Assimp.Material* pMat, bool hasBaseColor, bool hasBaseValue, Color4 baseColor, float baseValue, TextureType textureType, MaterialAsset finalMaterial)
+ {
+ var nbTextures = assimp.GetMaterialTextureCount(pMat, textureType);
+
+ IComputeColor computeColorNode = null;
+ int textureCount = 0;
+ if (nbTextures == 0)
+ {
+ if (hasBaseColor)
+ {
+ computeColorNode = new ComputeColor(baseColor);
+ }
+ //else if (hasBaseValue)
+ //{
+ // computeColorNode = gcnew MaterialFloatComputeNode(baseValue);
+ //}
+ }
+ else
+ {
+ computeColorNode = GenerateOneTextureTypeLayers(pMat, textureType, textureCount, finalMaterial);
+ }
+
+ if (computeColorNode == null)
+ {
+ return;
+ }
+
+ if (textureType == TextureType.Diffuse)
+ {
+ if (assimp.GetMaterialTextureCount(pMat, TextureType.Lightmap) > 0)
+ {
+ var lightMap = GenerateOneTextureTypeLayers(pMat, TextureType.Lightmap, textureCount, finalMaterial);
+ if (lightMap != null)
+ computeColorNode = new ComputeBinaryColor(computeColorNode, lightMap, BinaryOperator.Add);
+ }
+
+ finalMaterial.Attributes.Diffuse = new MaterialDiffuseMapFeature(computeColorNode);
+
+ // TODO TEMP: Set a default diffuse model
+ finalMaterial.Attributes.DiffuseModel = new MaterialDiffuseLambertModelFeature();
+ }
+ else if (textureType == TextureType.Specular)
+ {
+ var specularFeature = new MaterialSpecularMapFeature
+ {
+ SpecularMap = computeColorNode
+ };
+ finalMaterial.Attributes.Specular = specularFeature;
+
+ // TODO TEMP: Set a default specular model
+ var specularModel = new MaterialSpecularMicrofacetModelFeature
+ {
+ Fresnel = new MaterialSpecularMicrofacetFresnelSchlick(),
+ Visibility = new MaterialSpecularMicrofacetVisibilityImplicit(),
+ NormalDistribution = new MaterialSpecularMicrofacetNormalDistributionBlinnPhong()
+ };
+ finalMaterial.Attributes.SpecularModel = specularModel;
+ }
+ else if (textureType == TextureType.Emissive)
+ {
+ // TODO: Add support
+ }
+ else if (textureType == TextureType.Ambient)
+ {
+ // TODO: Add support
+ }
+ else if (textureType == TextureType.Reflection)
+ {
+ // TODO: Add support
+ }
+ if (textureType == TextureType.Opacity)
+ {
+ // TODO: Add support
+ }
+ else if (textureType == TextureType.Shininess)
+ {
+ // TODO: Add support
+ }
+ if (textureType == TextureType.Specular)
+ {
+ // TODO: Add support
+ }
+ else if (textureType == TextureType.Normals)
+ {
+ finalMaterial.Attributes.Surface = new MaterialNormalMapFeature(computeColorNode);
+ }
+ else if (textureType == TextureType.Displacement)
+ {
+ // TODO: Add support
+ }
+ else if (textureType == TextureType.Height)
+ {
+ // TODO: Add support
+ }
+ }
+
+ private unsafe IComputeColor GenerateOneTextureTypeLayers(Silk.NET.Assimp.Material* pMat, TextureType textureType, int textureCount, MaterialAsset finalMaterial)
+ {
+ var stack = Material.Materials.ConvertAssimpStackCppToCs(assimp, pMat, textureType);
+
+ var compositionFathers = new Stack();
+
+ var sets = new Stack();
+ sets.Push(0);
+
+ var nbTextures = assimp.GetMaterialTextureCount(pMat, textureType);
+
+ IComputeColor curComposition = null, newCompositionFather = null;
+
+ var isRootElement = true;
+ IComputeColor rootMaterial = null;
+
+ while (!stack.IsEmpty)
+ {
+ var top = stack.Pop();
+
+ IComputeColor curCompositionFather = null;
+ if (!isRootElement)
+ {
+ if (compositionFathers.Count == 0)
+ Logger.Error("Texture Stack Invalid : Operand without Operation.");
+
+ curCompositionFather = compositionFathers.Pop();
+ }
+
+ var type = top.Type;
+ var strength = top.Blend;
+ var alpha = top.Alpha;
+ int set = sets.Peek();
+
+ if (type == Material.StackType.Operation)
+ {
+ set = sets.Pop();
+ var realTop = (Material.StackOperation)top;
+ var op = realTop.Operation;
+ var binNode = new ComputeBinaryColor(null, null, BinaryOperator.Add);
+
+ binNode.Operator = op switch
+ {
+ Material.Operation.Add3ds or Material.Operation.AddMaya => BinaryOperator.Add,
+ Material.Operation.Multiply3ds or Material.Operation.MultiplyMaya => BinaryOperator.Multiply,
+ _ => BinaryOperator.Add,
+ };
+ curComposition = binNode;
+ }
+ else if (type == Material.StackType.Color)
+ {
+ var realTop = (Material.StackColor)top;
+ var ol = realTop.Color;
+ curComposition = new ComputeColor(new Color4(ol.R, ol.G, ol.B, alpha));
+ }
+ else if (type == Material.StackType.Texture)
+ {
+ var realTop = (Material.StackTexture)top;
+ var texPath = realTop.TexturePath;
+ var indexUV = realTop.Channel;
+ curComposition = GetTextureReferenceNode(vfsOutputFilename, texPath, (uint)indexUV, Vector2.One, ConvertTextureMode(realTop.MappingModeU), ConvertTextureMode(realTop.MappingModeV), finalMaterial);
+ }
+
+ newCompositionFather = curComposition;
+
+ if (strength != 1.0f)
+ {
+ var strengthAlpha = strength;
+ if (type != Material.StackType.Color)
+ strengthAlpha *= alpha;
+
+ var factorComposition = new ComputeFloat4(new Vector4(strength, strength, strength, strengthAlpha));
+ curComposition = new ComputeBinaryColor(curComposition, factorComposition, BinaryOperator.Multiply);
+ }
+ else if (alpha != 1.0f && type != Material.StackType.Color)
+ {
+ var factorComposition = new ComputeFloat4(new Vector4(1.0f, 1.0f, 1.0f, alpha));
+ curComposition = new ComputeBinaryColor(curComposition, factorComposition, BinaryOperator.Multiply);
+ }
+
+ if (isRootElement)
+ {
+ rootMaterial = curComposition;
+ isRootElement = false;
+ compositionFathers.Push(curCompositionFather);
+ }
+ else
+ {
+ if (set == 0)
+ {
+ ((ComputeBinaryColor)curCompositionFather).LeftChild = curComposition;
+ compositionFathers.Push(curCompositionFather);
+ sets.Push(1);
+ }
+ else if (set == 1)
+ {
+ ((ComputeBinaryColor)curCompositionFather).RightChild = curComposition;
+ }
+ else
+ {
+ Logger.Error($"Texture Stack Invalid : Invalid Operand Number {set}.");
+ }
+ }
+
+ if (type == Material.StackType.Operation)
+ {
+ compositionFathers.Push(newCompositionFather);
+ sets.Push(0);
+ }
+ }
+
+ return rootMaterial;
+ }
+
+ private static TextureAddressMode ConvertTextureMode(Material.MappingMode mappingMode)
+ {
+ return mappingMode switch
+ {
+ Material.MappingMode.Clamp => TextureAddressMode.Clamp,
+ Material.MappingMode.Decal => TextureAddressMode.Border,
+ Material.MappingMode.Mirror => TextureAddressMode.Mirror,
+ _ => TextureAddressMode.Wrap,
+ };
+ }
+
+ private ComputeTextureColor GetTextureReferenceNode(string vfsOutputPath, string sourceTextureFile, uint textureUVSetIndex, Vector2 textureUVscaling, TextureAddressMode addressModeU, TextureAddressMode addressModeV, MaterialAsset finalMaterial)
+ {
+ // TODO: compare with FBX importer - see if there could be some conflict between texture names
+ var textureValue = TextureLayerGenerator.GenerateMaterialTextureNode(vfsOutputPath, sourceTextureFile, textureUVSetIndex, textureUVscaling, addressModeU, addressModeV, Logger);
+
+ var attachedReference = AttachedReferenceManager.GetAttachedReference(textureValue.Texture);
+ var referenceName = attachedReference.Url;
+
+ // find a new and correctName
+ if (!textureNameCount.ContainsKey(referenceName))
+ textureNameCount.Add(referenceName, 1);
+ else
+ {
+ int count = textureNameCount[referenceName];
+ textureNameCount[referenceName] = count + 1;
+ referenceName = string.Concat(referenceName, "_", count);
+ }
+
+ return textureValue;
+ }
+
+ private unsafe List ExtractModels(Scene* scene, Dictionary meshNames, Dictionary materialNames, Dictionary nodeNames)
+ {
+ GenerateMeshNames(scene, meshNames);
+
+ var meshList = new List();
+ for (uint i = 0; i < scene->MNumMeshes; ++i)
+ {
+ var mesh = scene->MMeshes[i];
+ var lMaterial = scene->MMaterials[mesh->MMaterialIndex];
+
+ var meshParams = new MeshParameters
+ {
+ MeshName = meshNames[(IntPtr)mesh],
+ MaterialName = materialNames[(IntPtr)lMaterial],
+ NodeName = SearchMeshNode(scene->MRootNode, i, nodeNames)
+ };
+
+ meshList.Add(meshParams);
+ }
+
+ return meshList;
+ }
+
+ private unsafe string SearchMeshNode(Node* node, uint meshIndex, Dictionary nodeNames)
+ {
+ for (uint i = 0; i < node->MNumMeshes; ++i)
+ {
+ if (node->MMeshes[i] == meshIndex)
+ return nodeNames[(IntPtr)node];
+ }
+
+ for (uint i = 0; i < node->MNumChildren; ++i)
+ {
+ var res = SearchMeshNode(node->MChildren[i], meshIndex, nodeNames);
+ if (res != null)
+ return res;
+ }
+
+ return null;
+ }
+
+ private unsafe List ExtractNodeHierarchy(Scene* scene, Dictionary nodeNames)
+ {
+ var allNodes = new List();
+ GetNodes(scene->MRootNode, 0, nodeNames, allNodes);
+ return allNodes;
+ }
+
+ private unsafe void GetNodes(Node* node, int depth, Dictionary nodeNames, List allNodes)
+ {
+ var newNodeInfo = new NodeInfo
+ {
+ Name = nodeNames[(IntPtr)node],
+ Depth = depth,
+ Preserve = true
+ };
+
+ allNodes.Add(newNodeInfo);
+ for (uint i = 0; i < node->MNumChildren; ++i)
+ GetNodes(node->MChildren[i], depth + 1, nodeNames, allNodes);
+ }
+
+ private unsafe List ExtractAnimations(Scene* scene, Dictionary animationNames)
+ {
+ if (scene->MNumAnimations == 0)
+ return null;
+
+ GenerateAnimationNames(scene, animationNames);
+
+ var animationList = new List();
+ foreach (var animationName in animationNames)
+ {
+ animationList.Add(animationName.Value);
+ }
+
+ return animationList;
+ }
+
+ private unsafe List ExtractTextureDependencies(Scene* scene)
+ {
+ var textureNames = new List();
+
+ // texture search is done by type so we need to loop on them
+ var allTextureTypes = new TextureType[]
+ {
+ TextureType.Diffuse,
+ TextureType.Specular,
+ TextureType.Ambient,
+ TextureType.Emissive,
+ TextureType.Height,
+ TextureType.Normals,
+ TextureType.Shininess,
+ TextureType.Opacity,
+ TextureType.Displacement,
+ TextureType.Lightmap,
+ TextureType.Reflection
+ };
+
+ for (uint i = 0; i < scene->MNumMaterials; i++)
+ {
+ foreach (var textureType in allTextureTypes)
+ {
+ var lMaterial = scene->MMaterials[i];
+ var nbTextures = assimp.GetMaterialTextureCount(lMaterial, textureType);
+
+ for (uint j = 0; j < nbTextures; ++j)
+ {
+ var path = new AssimpString();
+ var mapping = TextureMapping.UV;
+ uint uvIndex = 0;
+ var blend = 0.0f;
+ var textureOp = TextureOp.Multiply;
+ var mapMode = TextureMapMode.Wrap;
+ uint flags = 0;
+
+ if (assimp.GetMaterialTexture(lMaterial, textureType, j, ref path, ref mapping, ref uvIndex, ref blend, ref textureOp, ref mapMode, ref flags) == Return.Success)
+ {
+ var relFileName = path.AsString;
+ var fileNameToUse = Path.Combine(vfsInputPath, relFileName);
+ textureNames.Add(fileNameToUse);
+ break;
+ }
+ }
+ }
+ }
+
+ return textureNames;
+ }
+ }
+
+ public class MeshInfo
+ {
+ public MeshDraw Draw;
+ public List Bones;
+ public string Name;
+ public int MaterialIndex;
+ public bool HasSkinningPosition = false;
+ public bool HasSkinningNormal = false;
+ public int TotalClusterCount = 0;
+ }
+
+ public class MaterialInstantiation
+ {
+ public List Parameters;
+ public MaterialAsset Material;
+ public string MaterialName;
+ }
+
+ public unsafe class MaterialInstances
+ {
+ public Silk.NET.Assimp.Material* SourceMaterial;
+ public List Instances = new();
+ public string MaterialsName;
+ }
}
diff --git a/sources/tools/Stride.Importer.Assimp/Stride.Importer.Assimp.csproj b/sources/tools/Stride.Importer.Assimp/Stride.Importer.Assimp.csproj
index 8ecabe8fba..7349f776fa 100644
--- a/sources/tools/Stride.Importer.Assimp/Stride.Importer.Assimp.csproj
+++ b/sources/tools/Stride.Importer.Assimp/Stride.Importer.Assimp.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/sources/tools/Stride.Importer.Assimp/Utils.cs b/sources/tools/Stride.Importer.Assimp/Utils.cs
index 0dc626ba30..5a0f457d6b 100644
--- a/sources/tools/Stride.Importer.Assimp/Utils.cs
+++ b/sources/tools/Stride.Importer.Assimp/Utils.cs
@@ -1,11 +1,10 @@
+// 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 Silk.NET.Assimp;
using Stride.Animations;
using Stride.Core.Mathematics;
-using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Numerics;
-using System.Text;
-using System.Threading.Tasks;
namespace Stride.Importer.Assimp
{
@@ -30,10 +29,8 @@ public static Core.Mathematics.Vector3 ToStrideVector3(this System.Numerics.Vect
public static Color ToStrideColor(this System.Numerics.Vector4 v)
=> new Color(v.X, v.Y, v.Z, v.W);
- public static Core.Mathematics.Quaternion ToStrideQuaternion(this System.Numerics.Quaternion q)
- // TODO: Not sure why I have to do this Assimp & System.Numerics.Quaternion seems to have the same memory layout (W, X, Y, Z)
- // so just passing X, Y, Z, W without swizzling should work ...
- => new Core.Mathematics.Quaternion(q.Y, q.Z, q.W, q.X);
+ public static Core.Mathematics.Quaternion ToStrideQuaternion(this AssimpQuaternion q)
+ => new Core.Mathematics.Quaternion(q.X, q.Y, q.Z, q.W);
public static unsafe uint GetNumUVChannels(Silk.NET.Assimp.Mesh* mesh)
{
@@ -57,7 +54,7 @@ public static unsafe uint GetNumColorChannels(Silk.NET.Assimp.Mesh* mesh)
return (uint)n;
}
- public static CompressedTimeSpan AiTimeToXkTimeSpan(double time, double aiTickPerSecond)
+ public static CompressedTimeSpan AiTimeToStrideTimeSpan(double time, double aiTickPerSecond)
{
var sdTime = CompressedTimeSpan.TicksPerSecond / aiTickPerSecond * time;
return new CompressedTimeSpan((int)sdTime);
diff --git a/sources/tools/Stride.Importer.Common/MeshMaterials.cs b/sources/tools/Stride.Importer.Common/MeshMaterials.cs
index 02ccbbe4fe..751575cf98 100644
--- a/sources/tools/Stride.Importer.Common/MeshMaterials.cs
+++ b/sources/tools/Stride.Importer.Common/MeshMaterials.cs
@@ -1,12 +1,14 @@
+// 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.Collections.Generic;
using Stride.Assets.Materials;
-
namespace Stride.Importer.Common
{
public class MeshMaterials
{
public Dictionary Materials;
- public List Models;
- public List BoneNodes;
+ public List Models;
+ public List BoneNodes;
}
}
diff --git a/sources/tools/Stride.Importer.Common/MeshParameters.cs b/sources/tools/Stride.Importer.Common/MeshParameters.cs
index 160f321431..575386dc9e 100644
--- a/sources/tools/Stride.Importer.Common/MeshParameters.cs
+++ b/sources/tools/Stride.Importer.Common/MeshParameters.cs
@@ -1,3 +1,6 @@
+// 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.Collections.Generic;
namespace Stride.Importer.Common
@@ -5,8 +8,8 @@ namespace Stride.Importer.Common
public class MeshParameters
{
public string MaterialName;
- public string MeshName;
- public string NodeName;
+ public string MeshName;
+ public string NodeName;
public HashSet BoneNodes;
}
}
diff --git a/sources/tools/Stride.Importer.Common/TextureLayerGenerator.cs b/sources/tools/Stride.Importer.Common/TextureLayerGenerator.cs
index 202279bc65..6e25bc3bc4 100644
--- a/sources/tools/Stride.Importer.Common/TextureLayerGenerator.cs
+++ b/sources/tools/Stride.Importer.Common/TextureLayerGenerator.cs
@@ -1,3 +1,6 @@
+// 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.IO;
using Stride.Core.Assets;
using Stride.Core.Diagnostics;
@@ -26,7 +29,7 @@ public static ShaderClassSource GenerateTextureLayer(string vfsOutputPath, strin
var uvSetName = "TEXCOORD";
if (textureUVSetIndex != 0)
uvSetName += textureUVSetIndex;
- //albedoMaterial->Add(gcnew ShaderClassSource("TextureStream", uvSetName, "TEXTEST" + uvSetIndex));
+
var uvScaling = textureUVscaling;
var textureName = parameterKey.Name;
var needScaling = uvScaling != Vector2.One;