ソースを参照

Merge pull request #1136 from sebavan/UnityAnimationExporter

Unity Skeleton Animation
David Catuhe 9 年 前
コミット
df0247f54e

+ 83 - 1
Exporters/Unity 5/Unity3D2Babylon/SceneBuilder.Animations.cs

@@ -57,10 +57,92 @@ namespace Unity3D2Babylon
             return animatable.animations.Any(animation => animation.property.Contains("rotationQuaternion"));
             return animatable.animations.Any(animation => animation.property.Contains("rotationQuaternion"));
         }
         }
 
 
+        private static void ExportSkeletonAnimationClips(Animator animator, bool autoPlay, BabylonSkeleton skeleton, Transform[] bones, BabylonMesh babylonMesh)
+        {
+            AnimationClip clip = null;
+            AnimatorController ac = animator.runtimeAnimatorController as AnimatorController;
+            if (ac == null)
+            {
+                return;
+            }
+            var layer = ac.layers[0];
+            if (layer == null)
+            {
+                return;
+            }
+            AnimatorStateMachine sm = layer.stateMachine;
+            if (sm.states.Length > 0)
+            {
+                // Only the first state is supported so far.
+                var state = sm.states[0].state;
+                clip = state.motion as AnimationClip;
+            }
+
+            if (clip == null)
+            {
+                return;
+            }
+
+            ExportSkeletonAnimationClipData(animator, autoPlay, skeleton, bones, babylonMesh, clip);
+        }
+
+        private static void ExportSkeletonAnimationClipData(Animator animator, bool autoPlay, BabylonSkeleton skeleton, Transform[] bones, BabylonMesh babylonMesh, AnimationClip clip)
+        {
+            var frameTime = 1.0f / clip.frameRate;
+            int animationFrameCount = (int)(clip.length * clip.frameRate);
+
+            if (autoPlay)
+            {
+                babylonMesh.autoAnimate = true;
+                babylonMesh.autoAnimateFrom = 0;
+                babylonMesh.autoAnimateTo = animationFrameCount;
+                babylonMesh.autoAnimateLoop = true;
+            }
+
+            foreach (var bone in skeleton.bones)
+            {
+                var keys = new List<BabylonAnimationKey>();
+                var transform = bones.Single(b => b.name == bone.name);
+
+                AnimationMode.BeginSampling();
+                for (var i = 0; i < animationFrameCount; i++)
+                {
+                    clip.SampleAnimation(animator.gameObject, i * frameTime);
+
+                    var local = (transform.parent.localToWorldMatrix.inverse * transform.localToWorldMatrix);
+                    float[] matrix = new[] {
+                        local[0, 0], local[1, 0], local[2, 0], local[3, 0],
+                        local[0, 1], local[1, 1], local[2, 1], local[3, 1],
+                        local[0, 2], local[1, 2], local[2, 2], local[3, 2],
+                        local[0, 3], local[1, 3], local[2, 3], local[3, 3]
+                    };
+
+                    var key = new BabylonAnimationKey
+                    {
+                        frame = i,
+                        values = matrix,
+                    };
+                    keys.Add(key);
+                }
+                AnimationMode.EndSampling();
+
+                var babylonAnimation = new BabylonAnimation
+                {
+                    name = bone.name + "Animation",
+                    property = "_matrix",
+                    dataType = (int)BabylonAnimation.DataType.Matrix,
+                    loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle,
+                    framePerSecond = (int)clip.frameRate,
+                    keys = keys.ToArray()
+                };
+
+                bone.animation = babylonAnimation;
+            }
+        }
+
         private static void ExportAnimationClip(AnimationClip clip, bool autoPlay, BabylonIAnimatable animatable)
         private static void ExportAnimationClip(AnimationClip clip, bool autoPlay, BabylonIAnimatable animatable)
         {
         {
             var curveBindings = AnimationUtility.GetCurveBindings(clip);
             var curveBindings = AnimationUtility.GetCurveBindings(clip);
-
             var animations = new List<BabylonAnimation>();
             var animations = new List<BabylonAnimation>();
 
 
             var maxFrame = 0;
             var maxFrame = 0;

+ 1 - 2
Exporters/Unity 5/Unity3D2Babylon/SceneBuilder.Materials.cs

@@ -394,7 +394,6 @@ namespace Unity3D2Babylon
                                 {
                                 {
                                     for (var j = 0; j < albedoTexture.height; j++)
                                     for (var j = 0; j < albedoTexture.height; j++)
                                     {
                                     {
-                                        var pixel = albedoPixels[j * albedoTexture.width + i];
                                         var metallicPixel = metallicPixels[j * albedoTexture.width + i];
                                         var metallicPixel = metallicPixels[j * albedoTexture.width + i];
                                         albedoPixels[j * albedoTexture.width + i].r *= metallicPixel.r;
                                         albedoPixels[j * albedoTexture.width + i].r *= metallicPixel.r;
                                         albedoPixels[j * albedoTexture.width + i].g *= metallicPixel.r;
                                         albedoPixels[j * albedoTexture.width + i].g *= metallicPixel.r;
@@ -489,7 +488,7 @@ namespace Unity3D2Babylon
             if (material.HasProperty("_Mode"))
             if (material.HasProperty("_Mode"))
             {
             {
                 var mode = material.GetFloat("_Mode");
                 var mode = material.GetFloat("_Mode");
-                if (mode == 2.0f)
+                if (mode >= 2.0f)
                 {
                 {
                     // Transparent Albedo
                     // Transparent Albedo
                     if (babylonPbrMaterial.albedoTexture != null && babylonPbrMaterial.albedoTexture.hasAlpha)
                     if (babylonPbrMaterial.albedoTexture != null && babylonPbrMaterial.albedoTexture.hasAlpha)

+ 85 - 1
Exporters/Unity 5/Unity3D2Babylon/SceneBuilder.Meshes.cs

@@ -1,4 +1,6 @@
 using System;
 using System;
+using System.Collections.Generic;
+using System.Linq;
 using BabylonExport.Entities;
 using BabylonExport.Entities;
 using UnityEngine;
 using UnityEngine;
 
 
@@ -34,7 +36,7 @@ namespace Unity3D2Babylon
             }
             }
         }
         }
 
 
-        private void ConvertUnityMeshToBabylon(Mesh mesh, Transform transform, GameObject gameObject, float progress)
+        private BabylonMesh ConvertUnityMeshToBabylon(Mesh mesh, Transform transform, GameObject gameObject, float progress)
         {
         {
             BabylonMesh babylonMesh = new BabylonMesh();
             BabylonMesh babylonMesh = new BabylonMesh();
             var renderer = gameObject.GetComponent<Renderer>();
             var renderer = gameObject.GetComponent<Renderer>();
@@ -144,6 +146,29 @@ namespace Unity3D2Babylon
                     babylonMesh.indices[i + 1] = mesh.triangles[i + 1];
                     babylonMesh.indices[i + 1] = mesh.triangles[i + 1];
                     babylonMesh.indices[i + 2] = mesh.triangles[i];
                     babylonMesh.indices[i + 2] = mesh.triangles[i];
                 }
                 }
+                
+                if (mesh.boneWeights.Length == mesh.vertexCount)
+                {
+                    babylonMesh.matricesIndices = new int[mesh.vertexCount];
+                    babylonMesh.matricesWeights = new float[mesh.vertexCount * 4];
+
+                    for (int i = 0; i < mesh.vertexCount; i++)
+                    {
+                        // Weight Packing.
+                        babylonMesh.matricesIndices[i] = (mesh.boneWeights[i].boneIndex3 << 24) | (mesh.boneWeights[i].boneIndex2 << 16) | (mesh.boneWeights[i].boneIndex1 << 8) | mesh.boneWeights[i].boneIndex0;
+                        
+                        babylonMesh.matricesWeights[i * 4 + 0] = mesh.boneWeights[i].weight0;
+                        babylonMesh.matricesWeights[i * 4 + 1] = mesh.boneWeights[i].weight1;
+                        babylonMesh.matricesWeights[i * 4 + 2] = mesh.boneWeights[i].weight2;
+                        babylonMesh.matricesWeights[i * 4 + 3] = mesh.boneWeights[i].weight3;
+
+                        var totalWeight = mesh.boneWeights[i].weight0 + mesh.boneWeights[i].weight1 + mesh.boneWeights[i].weight2 + mesh.boneWeights[i].weight3;
+                        if (Mathf.Abs(totalWeight - 1.0f) > 0.01f)
+                        {
+                            throw new Exception("Total bone weights is not normalized for: " + mesh);
+                        }
+                    }
+                }
 
 
                 if (renderer != null && renderer.sharedMaterial != null)
                 if (renderer != null && renderer.sharedMaterial != null)
                 {
                 {
@@ -225,6 +250,65 @@ namespace Unity3D2Babylon
                     }
                     }
                 }
                 }
             }
             }
+
+            return babylonMesh;
+        }
+
+        private BabylonSkeleton ConvertUnitySkeletonToBabylon(Transform[] bones, Matrix4x4[] bindPoses, Transform transform, GameObject gameObject, float progress)
+        {
+            ExporterWindow.ReportProgress(progress, "Exporting Skeleton: " + gameObject.name);
+            BabylonSkeleton babylonSkeleton = new BabylonSkeleton();
+            babylonSkeleton.name = gameObject.name;
+            babylonSkeleton.id = Math.Abs(GetID(transform.gameObject).GetHashCode());
+            babylonSkeleton.needInitialSkinMatrix = false;
+            
+            // Prefilled to keep order and track parents.
+            var transformToBoneMap = new Dictionary<Transform, BabylonBone>();
+            for (var i = 0; i < bones.Length; i++)
+            {
+                var unityBone = bones[i];
+                ExporterWindow.ReportProgress(progress, "Exporting bone: " + unityBone.name + " at index " + i);
+                
+                var babylonBone = new BabylonBone();
+                babylonBone.name = unityBone.name;
+                babylonBone.index = i;
+
+                transformToBoneMap.Add(unityBone, babylonBone);
+            }
+            
+            // Attaches Matrix and parent.
+            for (var i = 0; i < bones.Length; i++)
+            {
+                var unityBone = bones[i];
+                var babylonBone = transformToBoneMap[unityBone];
+                Matrix4x4 localTransform;
+                
+                // Unity BindPose is already inverse so take the inverse again :-)
+                if (transformToBoneMap.ContainsKey(unityBone.parent))
+                {
+                    var babylonParentBone = transformToBoneMap[unityBone.parent];
+                    babylonBone.parentBoneIndex = babylonParentBone.index;
+                    localTransform = bindPoses[babylonBone.parentBoneIndex] * bindPoses[i].inverse;
+                }
+                else
+                {
+                    babylonBone.parentBoneIndex = -1;
+                    localTransform = bindPoses[i].inverse;
+                }
+                
+                transformToBoneMap[unityBone].matrix = new[] {
+                    localTransform[0, 0], localTransform[1, 0], localTransform[2, 0], localTransform[3, 0],
+                    localTransform[0, 1], localTransform[1, 1], localTransform[2, 1], localTransform[3, 1],
+                    localTransform[0, 2], localTransform[1, 2], localTransform[2, 2], localTransform[3, 2],
+                    localTransform[0, 3], localTransform[1, 3], localTransform[2, 3], localTransform[3, 3]
+                };
+            }
+
+            // Reorder and attach the skeleton.
+            babylonSkeleton.bones = transformToBoneMap.Values.OrderBy(b => b.index).ToArray();
+            babylonScene.SkeletonsList.Add(babylonSkeleton);
+
+            return babylonSkeleton;
         }
         }
     }
     }
 }
 }

+ 30 - 2
Exporters/Unity 5/Unity3D2Babylon/SceneBuilder.cs

@@ -128,7 +128,11 @@ namespace Unity3D2Babylon
                 var skinnedMesh = gameObject.GetComponent<SkinnedMeshRenderer>();
                 var skinnedMesh = gameObject.GetComponent<SkinnedMeshRenderer>();
                 if (skinnedMesh != null)
                 if (skinnedMesh != null)
                 {
                 {
-                    ConvertUnityMeshToBabylon(skinnedMesh.sharedMesh, skinnedMesh.transform, gameObject, progress);
+                    var babylonMesh = ConvertUnityMeshToBabylon(skinnedMesh.sharedMesh, skinnedMesh.transform, gameObject, progress);
+                    var skeleton = ConvertUnitySkeletonToBabylon(skinnedMesh.bones, skinnedMesh.sharedMesh.bindposes, skinnedMesh.transform, gameObject, progress);
+                    babylonMesh.skeletonId = skeleton.id;
+
+                    ExportSkeletonAnimation(skinnedMesh, babylonMesh, skeleton);
                     continue;
                     continue;
                 }
                 }
 
 
@@ -169,6 +173,30 @@ namespace Unity3D2Babylon
             {
             {
                 babylonScene.gravity = exportationOptions.Gravity.ToFloat();
                 babylonScene.gravity = exportationOptions.Gravity.ToFloat();
             }
             }
-        }     
+        }
+
+        private static void ExportSkeletonAnimation(SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, BabylonSkeleton skeleton)
+        {
+            var animator = skinnedMesh.rootBone.gameObject.GetComponent<Animator>();
+            if (animator != null)
+            {
+                ExportSkeletonAnimationClips(animator, true, skeleton, skinnedMesh.bones, babylonMesh);
+            }
+            else
+            {
+                var parent = skinnedMesh.rootBone.parent;
+                while (parent != null)
+                {
+                    animator = parent.gameObject.GetComponent<Animator>();
+                    if (animator != null)
+                    {
+                        ExportSkeletonAnimationClips(animator, true, skeleton, skinnedMesh.bones, babylonMesh);
+                        break;
+                    }
+
+                    parent = parent.parent;
+                }
+            }
+        }
     }
     }
 }
 }