浏览代码

Export morph targets for BABYLON

noalak 8 年之前
父节点
当前提交
270f1a3325

+ 2 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj

@@ -56,6 +56,8 @@
   <ItemGroup>
     <Compile Include="BabylonActions.cs" />
     <Compile Include="BabylonAnimation.cs" />
+    <Compile Include="BabylonMorphTarget.cs" />
+    <Compile Include="BabylonMorphTargetManager.cs" />
     <Compile Include="BabylonPBRMetallicRoughnessMaterial.cs" />
     <Compile Include="BabylonAnimationKey.cs" />
     <Compile Include="BabylonBone.cs" />

+ 3 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonMesh.cs

@@ -110,6 +110,9 @@ namespace BabylonExport.Entities
         [DataMember]
         public string tags { get; set; }
 
+        [DataMember(EmitDefaultValue = false)]
+        public int? morphTargetManagerId { get; set; }
+
         public bool isDummy = false;
 
         public BabylonMesh()

+ 23 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonMorphTarget.cs

@@ -0,0 +1,23 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonMorphTarget
+    {
+        [DataMember(EmitDefaultValue = false)]
+        public string name { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float influence { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float[] positions { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public float[] normals { get; set; }
+
+        [DataMember(EmitDefaultValue = false)]
+        public BabylonAnimation[] animations { get; set; }
+    }
+}

+ 26 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonMorphTargetManager.cs

@@ -0,0 +1,26 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonMorphTargetManager
+    {
+        private static int NB_BABYLON_MORPH_TARGET_MANAGER;
+
+        [DataMember(IsRequired = true)]
+        public int id { get; set; }
+
+        [DataMember(IsRequired = true)]
+        public BabylonMorphTarget[] targets { get; set; }
+
+        public static void Reset()
+        {
+            NB_BABYLON_MORPH_TARGET_MANAGER = 0;
+        }
+
+        public BabylonMorphTargetManager()
+        {
+            id = NB_BABYLON_MORPH_TARGET_MANAGER++;
+        }
+    }
+}

+ 9 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonScene.cs

@@ -89,6 +89,9 @@ namespace BabylonExport.Entities
         [DataMember]
         public bool workerCollisions { get; set; }
 
+        [DataMember]
+        public BabylonMorphTargetManager[] morphTargetManagers { get; set; }
+
         public BabylonVector3 MaxVector { get; set; }
         public BabylonVector3 MinVector { get; set; }
 
@@ -102,6 +105,7 @@ namespace BabylonExport.Entities
         public List<BabylonMultiMaterial> MultiMaterialsList { get; private set; }
         public List<BabylonShadowGenerator> ShadowGeneratorsList { get; private set; }
         public List<BabylonSkeleton> SkeletonsList { get; private set; }
+        public List<BabylonMorphTargetManager> MorphTargetManagersList { get; private set; }
 
         readonly List<string> exportedTextures = new List<string>();
 
@@ -117,6 +121,7 @@ namespace BabylonExport.Entities
             ShadowGeneratorsList = new List<BabylonShadowGenerator>();
             SkeletonsList = new List<BabylonSkeleton>();
             SoundsList = new List<BabylonSound>();
+            MorphTargetManagersList = new List<BabylonMorphTargetManager>();
 
             // Default values
             autoClear = true;
@@ -137,6 +142,10 @@ namespace BabylonExport.Entities
             multiMaterials = MultiMaterialsList.ToArray();
             shadowGenerators = ShadowGeneratorsList.ToArray();
             skeletons = SkeletonsList.ToArray();
+            if (MorphTargetManagersList.Count > 0)
+            {
+                morphTargetManagers = MorphTargetManagersList.ToArray();
+            }
 
             if (CamerasList.Count == 0 && generateDefaultCamera)
             {

+ 102 - 30
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Animation.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using Autodesk.Max;
 using BabylonExport.Entities;
+using System.Runtime.InteropServices;
 
 namespace Max2Babylon
 {
@@ -9,6 +10,101 @@ namespace Max2Babylon
     {
         const int Ticks = 160;
 
+        private static bool ExportBabylonKeys(List<BabylonAnimationKey> keys, string property, List<BabylonAnimation> animations, BabylonAnimation.DataType dataType, BabylonAnimation.LoopBehavior loopBehavior)
+        {
+            if (keys.Count == 0)
+            {
+                return false;
+            }
+
+            var end = Loader.Core.AnimRange.End;
+            if (keys[keys.Count - 1].frame != end / Ticks)
+            {
+                keys.Add(new BabylonAnimationKey()
+                {
+                    frame = end / Ticks,
+                    values = keys[keys.Count - 1].values
+                });
+            }
+
+            var babylonAnimation = new BabylonAnimation
+            {
+                dataType = (int)dataType,
+                name = property + " animation",
+                keys = keys.ToArray(),
+                framePerSecond = Loader.Global.FrameRate,
+                loopBehavior = (int)loopBehavior,
+                property = property
+            };
+
+            animations.Add(babylonAnimation);
+
+            return true;
+        }
+
+        // -----------------------
+        // -- From GameControl ---
+        // -----------------------
+
+        private bool ExportFloatGameController(IIGameControl control, string property, List<BabylonAnimation> animations)
+        {
+            return ExportGameController(control, property, animations, IGameControlType.Float, BabylonAnimation.DataType.Float, gameKey => new float[] { gameKey.SampleKey.Fval / 100.0f });
+        }
+
+        private bool ExportGameController(IIGameControl control, string property, List<BabylonAnimation> animations, IGameControlType type, BabylonAnimation.DataType dataType, Func<IIGameKey, float[]> extractValueFunc)
+        {
+            var keys = ExportBabylonKeysFromGameController(control, type, extractValueFunc);
+
+            if (keys == null)
+            {
+                return false;
+            }
+
+            var loopBehavior = BabylonAnimation.LoopBehavior.Cycle;
+            return ExportBabylonKeys(keys, property, animations, dataType, loopBehavior);
+        }
+
+        private List<BabylonAnimationKey> ExportBabylonKeysFromGameController(IIGameControl control, IGameControlType type, Func<IIGameKey, float[]> extractValueFunc)
+        {
+            if (control == null)
+            {
+                return null;
+            }
+
+            ITab<IIGameKey> gameKeyTab = GlobalInterface.Instance.Tab.Create<IIGameKey>();
+            control.GetQuickSampledKeys(gameKeyTab, type);
+
+            if (gameKeyTab == null)
+            {
+                return null;
+            }
+
+            var keys = new List<BabylonAnimationKey>();
+            for (int indexKey = 0; indexKey < gameKeyTab.Count; indexKey++)
+            {
+#if MAX2017
+                var indexer = indexKey;
+#else
+                    var indexer = new IntPtr(indexKey);
+                    Marshal.FreeHGlobal(indexer);
+#endif
+                var gameKey = gameKeyTab[indexer];
+
+                var key = new BabylonAnimationKey()
+                {
+                    frame = gameKey.T / Ticks,
+                    values = extractValueFunc(gameKey)
+                };
+                keys.Add(key);
+            }
+
+            return keys;
+        }
+
+        // -----------------------
+        // ---- From Control -----
+        // -----------------------
+
         private static BabylonAnimationKey GenerateFloatFunc(int index, IIKeyControl keyControl)
         {
             var key = Loader.Global.ILinFloatKey.Create();
@@ -119,9 +215,7 @@ namespace Max2Babylon
                 return false;
             }
 
-            var keys = new List<BabylonAnimationKey>();
             BabylonAnimation.LoopBehavior loopBehavior;
-
             switch (control.GetORT(2))
             {
                 case 2:
@@ -132,41 +226,19 @@ namespace Max2Babylon
                     break;
             }
 
+            var keys = new List<BabylonAnimationKey>();
             for (var index = 0; index < keyControl.NumKeys; index++)
             {
                 keys.Add(generateFunc(index, keyControl));
             }
 
-            if (keys.Count == 0)
-            {
-                return false;
-            }
-
-            var end = Loader.Core.AnimRange.End;
-            if (keys[keys.Count - 1].frame != end / Ticks)
-            {
-                keys.Add(new BabylonAnimationKey()
-                {
-                    frame = end / Ticks,
-                    values = keys[keys.Count - 1].values
-                });
-            }
-
-            var babylonAnimation = new BabylonAnimation
-            {
-                dataType = (int)dataType,
-                name = property + " animation",
-                keys = keys.ToArray(),
-                framePerSecond = Loader.Global.FrameRate,
-                loopBehavior = (int)loopBehavior,
-                property = property
-            };
-
-            animations.Add(babylonAnimation);
-
-            return true;
+            return ExportBabylonKeys(keys, property, animations, dataType, loopBehavior);
         }
 
+        // -----------------------
+        // ---- From ext func ----
+        // -----------------------
+
         private static void ExportColor3Animation(string property, List<BabylonAnimation> animations,
             Func<int, float[]> extractValueFunc)
         {

+ 93 - 0
Exporters/3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs

@@ -356,6 +356,80 @@ namespace Max2Babylon
 
                 // Buffers - Indices
                 babylonMesh.indices = indices.ToArray();
+
+                // ------------------------
+                // ---- Morph targets -----
+                // ------------------------
+
+                // Retreive modifiers with morpher flag
+                List<IIGameModifier> modifiers = new List<IIGameModifier>();
+                for (int i = 0; i < meshNode.IGameObject.NumModifiers; i++)
+                {
+                    var modifier = meshNode.IGameObject.GetIGameModifier(i);
+                    if (modifier.ModifierType == Autodesk.Max.IGameModifier.ModType.Morpher)
+                    {
+                        modifiers.Add(modifier);
+                    }
+                }
+
+                // Cast modifiers to morphers
+                List<IIGameMorpher> morphers = modifiers.ConvertAll(new Converter<IIGameModifier, IIGameMorpher>(modifier => modifier.AsGameMorpher()));
+
+                var hasMorphTarget = false;
+                morphers.ForEach(morpher =>
+                {
+                    if (morpher.NumberOfMorphTargets > 0)
+                    {
+                        hasMorphTarget = true;
+                    }
+                });
+
+                if (hasMorphTarget)
+                {
+                    RaiseMessage("Export morph targets", 2);
+
+                    // Morph Target Manager
+                    var babylonMorphTargetManager = new BabylonMorphTargetManager();
+                    babylonScene.MorphTargetManagersList.Add(babylonMorphTargetManager);
+                    babylonMesh.morphTargetManagerId = babylonMorphTargetManager.id;
+
+                    // Morph Targets
+                    var babylonMorphTargets = new List<BabylonMorphTarget>();
+                    // All morphers are considered identical
+                    // Their targets are concatenated
+                    morphers.ForEach(morpher =>
+                    {
+                        for (int i = 0; i < morpher.NumberOfMorphTargets; i++)
+                        {
+                            // Morph target
+                            var maxMorphTarget = morpher.GetMorphTarget(i);
+                            var babylonMorphTarget = new BabylonMorphTarget
+                            {
+                                name = maxMorphTarget.Name
+                            };
+                            babylonMorphTargets.Add(babylonMorphTarget);
+
+                            // TODO - Influence
+                            babylonMorphTarget.influence = 0f;
+
+                            // Target geometry
+                            var targetVertices = ExtractVertices(maxMorphTarget, optimizeVertices);
+                            babylonMorphTarget.positions = targetVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray();
+                            babylonMorphTarget.normals = targetVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray();
+
+                            // Animations
+                            var animations = new List<BabylonAnimation>();
+                            var morphWeight = morpher.GetMorphWeight(i);
+                            ExportFloatGameController(morphWeight, "influence", animations);
+                            if (animations.Count > 0)
+                            {
+                                babylonMorphTarget.animations = animations.ToArray();
+                            }
+                        }
+                    });
+
+                    babylonMorphTargetManager.targets = babylonMorphTargets.ToArray();
+                }
             }
 
             babylonScene.MeshesList.Add(babylonMesh);
@@ -363,6 +437,25 @@ namespace Max2Babylon
             return babylonMesh;
         }
 
+        private List<GlobalVertex> ExtractVertices(IIGameNode maxMorphTarget, bool optimizeVertices)
+        {
+            var gameMesh = maxMorphTarget.IGameObject.AsGameMesh();
+            bool initialized = gameMesh.InitializeData; // needed, the property is in fact a method initializing the exporter that has wrongly been auto 
+                                                        // translated into a property because it has no parameters
+
+            var mtl = maxMorphTarget.NodeMaterial;
+            var multiMatsCount = 1;
+
+            if (mtl != null)
+            {
+                multiMatsCount = Math.Max(mtl.SubMaterialCount, 1);
+            }
+
+            var vertices = new List<GlobalVertex>();
+            ExtractGeometry(vertices, new List<int>(), new List<BabylonSubMesh>(), null, null, gameMesh, false, false, false, false, optimizeVertices, multiMatsCount, maxMorphTarget);
+            return vertices;
+        }
+
         private void ExtractGeometry(List<GlobalVertex> vertices, List<int> indices, List<BabylonSubMesh> subMeshes, List<int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode)
         {
             List<GlobalVertex>[] verticesAlreadyExported = null;

+ 13 - 1
Exporters/3ds Max/Max2Babylon/Tools/Tools.cs

@@ -105,6 +105,18 @@ namespace Max2Babylon
             }
         }
 
+        public static IIGameMorpher AsGameMorpher(this IIGameModifier obj)
+        {
+            var type = GetWrappersAssembly().GetType("Autodesk.Max.Wrappers.IGameMorpher");
+            var constructor = type.GetConstructors()[0];
+            // var pointerType = GetWrappersAssembly().GetType("IGameCamera");
+            unsafe
+            {
+                var voidPtr = obj.GetNativeHandle().ToPointer();
+                return (IIGameMorpher)constructor.Invoke(new object[] { obj.GetNativeHandle(), false });
+            }
+        }
+
         public const float Epsilon = 0.001f;
 
         public static IPoint3 XAxis { get { return Loader.Global.Point3.Create(1, 0, 0); } }
@@ -118,7 +130,7 @@ namespace Max2Babylon
         }
 
         public static IMatrix3 Identity { get { return Loader.Global.Matrix3.Create(XAxis, YAxis, ZAxis, Origin); } }
-        
+
         public static Vector3 ToEulerAngles(this IQuat q)
         {
             // Store the Euler angles in radians