mirror of
				https://github.com/maxartz15/VertexAnimation.git
				synced 2025-11-04 10:25:44 +01:00 
			
		
		
		
	ModelBaker Test
MeshCombiner, combine SkinnedMeshRenderers and MeshRenderers into one. AnimationBaker, bake SkinnedMeshRenderer with of animations into vertex animations. Test shader, flipped Y compared to previvious version, it now starts at 0,0 and goes into the positive direciton.
This commit is contained in:
		
							
								
								
									
										8
									
								
								Runtime/Scripts/ModelBaker.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Runtime/Scripts/ModelBaker.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
fileFormatVersion: 2
 | 
			
		||||
guid: 657facf5ba53f664b95d329ffb58abb1
 | 
			
		||||
folderAsset: yes
 | 
			
		||||
DefaultImporter:
 | 
			
		||||
  externalObjects: {}
 | 
			
		||||
  userData: 
 | 
			
		||||
  assetBundleName: 
 | 
			
		||||
  assetBundleVariant: 
 | 
			
		||||
							
								
								
									
										189
									
								
								Runtime/Scripts/ModelBaker/AnimationBaker.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								Runtime/Scripts/ModelBaker/AnimationBaker.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,189 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
 | 
			
		||||
namespace TAO.VertexAnimation
 | 
			
		||||
{
 | 
			
		||||
    public static class AnimationBaker
 | 
			
		||||
    {
 | 
			
		||||
        [System.Serializable]
 | 
			
		||||
        public struct BakedData
 | 
			
		||||
        {
 | 
			
		||||
            public Mesh mesh;
 | 
			
		||||
            public List<Texture2D> positionMaps;
 | 
			
		||||
 | 
			
		||||
            // Returns main position map.
 | 
			
		||||
            public Texture2D GetPositionMap
 | 
			
		||||
            {
 | 
			
		||||
                get
 | 
			
		||||
                {
 | 
			
		||||
                    return positionMaps[0];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [System.Serializable]
 | 
			
		||||
        public struct AnimationInfo
 | 
			
		||||
        {
 | 
			
		||||
            public int rawFrameHeight;
 | 
			
		||||
            public int frameHeight;
 | 
			
		||||
            public int frameSpacing;
 | 
			
		||||
            public int frames;
 | 
			
		||||
            public int maxFrames;
 | 
			
		||||
            public int textureWidth;
 | 
			
		||||
            public int textureHeight;
 | 
			
		||||
 | 
			
		||||
            // Create animation info and calculate values.
 | 
			
		||||
            public AnimationInfo(Mesh mesh, int frames, int textureWidth)
 | 
			
		||||
            {
 | 
			
		||||
                this.frames = frames;
 | 
			
		||||
                this.textureWidth = textureWidth;
 | 
			
		||||
 | 
			
		||||
                rawFrameHeight = Mathf.CeilToInt((float)mesh.vertices.Length / this.textureWidth);
 | 
			
		||||
                frameHeight = Mathf.NextPowerOfTwo(rawFrameHeight);
 | 
			
		||||
                frameSpacing = (frameHeight - rawFrameHeight) + 1;
 | 
			
		||||
 | 
			
		||||
                textureHeight = Mathf.NextPowerOfTwo(frameHeight * this.frames);
 | 
			
		||||
 | 
			
		||||
                maxFrames = textureHeight / frameHeight;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static BakedData Bake(this GameObject model, AnimationClip[] animationClips, int fps, int textureWidth)
 | 
			
		||||
        {
 | 
			
		||||
            BakedData bakedData = new BakedData()
 | 
			
		||||
            {
 | 
			
		||||
                mesh = null,
 | 
			
		||||
                positionMaps = new List<Texture2D>()
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Calculate what our max frames/time is going to be.
 | 
			
		||||
            int maxFrames = 0;
 | 
			
		||||
            foreach (AnimationClip ac in animationClips)
 | 
			
		||||
            {
 | 
			
		||||
                int frames = Mathf.FloorToInt(fps * ac.length);
 | 
			
		||||
 | 
			
		||||
                if (maxFrames < frames)
 | 
			
		||||
                {
 | 
			
		||||
                    maxFrames = frames;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Get the target mesh to calculate the animation info.
 | 
			
		||||
            Mesh mesh = model.GetComponent<SkinnedMeshRenderer>().sharedMesh;
 | 
			
		||||
 | 
			
		||||
            // Get the info for the biggest animation.
 | 
			
		||||
            AnimationInfo animationInfo = new AnimationInfo(mesh, maxFrames, textureWidth);
 | 
			
		||||
 | 
			
		||||
            foreach (AnimationClip ac in animationClips)
 | 
			
		||||
            {
 | 
			
		||||
                // Set the frames for this animation.
 | 
			
		||||
                animationInfo.frames = Mathf.FloorToInt(fps * ac.length);
 | 
			
		||||
 | 
			
		||||
                BakedData bd = Bake(model, ac, animationInfo);
 | 
			
		||||
                bakedData.mesh = bd.mesh;
 | 
			
		||||
                bakedData.positionMaps.AddRange(bd.positionMaps);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return bakedData;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static BakedData Bake(this GameObject model, AnimationClip animationClip, AnimationInfo animationInfo)
 | 
			
		||||
        {
 | 
			
		||||
            Mesh mesh = new Mesh
 | 
			
		||||
            {
 | 
			
		||||
                name = string.Format("{0}", model.name)
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Bake mesh for a copy and to apply the new UV's to.
 | 
			
		||||
            SkinnedMeshRenderer skinnedMeshRenderer = model.GetComponent<SkinnedMeshRenderer>();
 | 
			
		||||
            skinnedMeshRenderer.BakeMesh(mesh);
 | 
			
		||||
            mesh.RecalculateBounds();
 | 
			
		||||
 | 
			
		||||
            mesh.uv3 = mesh.BakePositionUVs(animationInfo);
 | 
			
		||||
 | 
			
		||||
            BakedData bakedData = new BakedData()
 | 
			
		||||
            {
 | 
			
		||||
                mesh = mesh,
 | 
			
		||||
                positionMaps = new List<Texture2D>() { BakePositionMap(model, animationClip, animationInfo) }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return bakedData;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Texture2D BakePositionMap(this GameObject model, AnimationClip animationClip, AnimationInfo animationInfo)
 | 
			
		||||
        {
 | 
			
		||||
            // Create positionMap Texture without MipMaps which is Linear and HDR to store values in a bigger range.
 | 
			
		||||
            Texture2D positionMap = new Texture2D(animationInfo.textureWidth, animationInfo.textureHeight, TextureFormat.RGBAHalf, false, true);
 | 
			
		||||
 | 
			
		||||
            // Create instance to sample from.
 | 
			
		||||
            GameObject inst = GameObject.Instantiate(model);
 | 
			
		||||
            SkinnedMeshRenderer skinnedMeshRenderer = inst.GetComponent<SkinnedMeshRenderer>();
 | 
			
		||||
 | 
			
		||||
            int y = 0;
 | 
			
		||||
            for (int f = 0; f < animationInfo.frames; f++)
 | 
			
		||||
            {
 | 
			
		||||
                animationClip.SampleAnimation(inst, (animationClip.length / animationInfo.frames) * f);
 | 
			
		||||
 | 
			
		||||
                Mesh sampledMesh = new Mesh();
 | 
			
		||||
                skinnedMeshRenderer.BakeMesh(sampledMesh);
 | 
			
		||||
                sampledMesh.RecalculateBounds();
 | 
			
		||||
 | 
			
		||||
                int x = 0;
 | 
			
		||||
                for (int v = 0; v < sampledMesh.vertices.Length; v++)
 | 
			
		||||
                {
 | 
			
		||||
                    Vector3 vert = sampledMesh.vertices[v];
 | 
			
		||||
                    Vector3 normal = sampledMesh.normals[v];
 | 
			
		||||
 | 
			
		||||
                    positionMap.SetPixel(x, y,
 | 
			
		||||
                            new Color(vert.x, vert.y, vert.z,
 | 
			
		||||
                            VectorUtils.Float3ToFloat(normal))
 | 
			
		||||
                        );
 | 
			
		||||
 | 
			
		||||
                    x++;
 | 
			
		||||
                    if (x >= animationInfo.textureWidth)
 | 
			
		||||
                    {
 | 
			
		||||
                        x = 0;
 | 
			
		||||
                        y++;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                y += animationInfo.frameSpacing;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            GameObject.DestroyImmediate(inst);
 | 
			
		||||
 | 
			
		||||
            positionMap.name = string.Format("VA_N-{0}_F-{1}_MF-{2}", animationClip.name, animationInfo.frames, animationInfo.maxFrames);
 | 
			
		||||
            positionMap.filterMode = FilterMode.Point;
 | 
			
		||||
            // TODO: Make no longer readable.
 | 
			
		||||
            positionMap.Apply(false, false);
 | 
			
		||||
 | 
			
		||||
            return positionMap;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Vector2[] BakePositionUVs(this Mesh mesh, AnimationInfo animationInfo)
 | 
			
		||||
        {
 | 
			
		||||
            Vector2[] uv3 = new Vector2[mesh.vertices.Length];
 | 
			
		||||
 | 
			
		||||
            float xOffset = 1.0f / animationInfo.textureWidth;
 | 
			
		||||
            float yOffset = 1.0f / animationInfo.textureHeight;
 | 
			
		||||
 | 
			
		||||
            float x = xOffset / 2.0f;
 | 
			
		||||
            float y = yOffset / 2.0f;
 | 
			
		||||
 | 
			
		||||
            for (int v = 0; v < mesh.vertices.Length; v++)
 | 
			
		||||
            {
 | 
			
		||||
                uv3[v] = new Vector2(x, y);
 | 
			
		||||
 | 
			
		||||
                x += xOffset;
 | 
			
		||||
                if (x >= 1.0f)
 | 
			
		||||
                {
 | 
			
		||||
                    x = xOffset / 2.0f;
 | 
			
		||||
                    y += yOffset;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            mesh.uv3 = uv3;
 | 
			
		||||
 | 
			
		||||
            return uv3;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								Runtime/Scripts/ModelBaker/AnimationBaker.cs.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Runtime/Scripts/ModelBaker/AnimationBaker.cs.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
fileFormatVersion: 2
 | 
			
		||||
guid: f73c308e347c32142b0f61e8b2550914
 | 
			
		||||
MonoImporter:
 | 
			
		||||
  externalObjects: {}
 | 
			
		||||
  serializedVersion: 2
 | 
			
		||||
  defaultReferences: []
 | 
			
		||||
  executionOrder: 0
 | 
			
		||||
  icon: {instanceID: 0}
 | 
			
		||||
  userData: 
 | 
			
		||||
  assetBundleName: 
 | 
			
		||||
  assetBundleVariant: 
 | 
			
		||||
							
								
								
									
										254
									
								
								Runtime/Scripts/ModelBaker/MeshCombiner.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								Runtime/Scripts/ModelBaker/MeshCombiner.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,254 @@
 | 
			
		||||
// References:
 | 
			
		||||
// https://forum.unity.com/threads/help-combining-and-manipulating-skinned-mesh-renderers-imported-from-blender.505078/
 | 
			
		||||
// http://wiki.unity3d.com/index.php/CombineSkinnedMeshes
 | 
			
		||||
// http://wiki.unity3d.com/index.php/SkinnedMeshCombiner
 | 
			
		||||
 | 
			
		||||
// TODO:
 | 
			
		||||
// ---Bake ALL the MeshRenderers/SkinnedMeshRenderers and merge them together.---
 | 
			
		||||
// ---Bake multiple animations.---
 | 
			
		||||
// ---Get the longest animation to calculate the texture height, so all the textures have the same height for the 3D texture.---
 | 
			
		||||
// Add options and previews for texture size, animation phasing/fps.
 | 
			
		||||
// Either merge with the animation books or generate them from this and maybe store them as child (and then don't destroy them on re-bake to keep the reference but replace it).
 | 
			
		||||
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace TAO.VertexAnimation
 | 
			
		||||
{
 | 
			
		||||
    public static class MeshCombiner
 | 
			
		||||
    {
 | 
			
		||||
        private struct MaterialMeshGroup
 | 
			
		||||
        {
 | 
			
		||||
            public List<SkinnedMeshRenderer> skinnedMeshes;
 | 
			
		||||
            public List<(MeshFilter mf, MeshRenderer mr)> meshes;
 | 
			
		||||
            public Material material;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static SkinnedMeshRenderer Combine(this SkinnedMeshRenderer target, List<SkinnedMeshRenderer> skinnedMeshes, List<(MeshFilter mf, MeshRenderer mr)> meshes)
 | 
			
		||||
        {
 | 
			
		||||
            List<MaterialMeshGroup> groups = new List<MaterialMeshGroup>();
 | 
			
		||||
 | 
			
		||||
            // Group skinnedMeshes.
 | 
			
		||||
            foreach (var sm in skinnedMeshes)
 | 
			
		||||
            {
 | 
			
		||||
                bool hasGroup = false;
 | 
			
		||||
                foreach (var g in groups)
 | 
			
		||||
                {
 | 
			
		||||
                    if (sm.sharedMaterial == g.material)
 | 
			
		||||
                    {
 | 
			
		||||
                        hasGroup = true;
 | 
			
		||||
                        g.skinnedMeshes.Add(sm);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!hasGroup)
 | 
			
		||||
                {
 | 
			
		||||
                    groups.Add(new MaterialMeshGroup()
 | 
			
		||||
                    { 
 | 
			
		||||
                        skinnedMeshes = new List<SkinnedMeshRenderer>()
 | 
			
		||||
                        {
 | 
			
		||||
                            sm
 | 
			
		||||
                        },
 | 
			
		||||
                        meshes = new List<(MeshFilter mf, MeshRenderer mr)>(),
 | 
			
		||||
                        material = sm.sharedMaterial
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Group Meshes.
 | 
			
		||||
            foreach (var m in meshes)
 | 
			
		||||
            {
 | 
			
		||||
                bool hasGroup = false;
 | 
			
		||||
                foreach (var g in groups)
 | 
			
		||||
                {
 | 
			
		||||
                    if (m.mr.sharedMaterial == g.material)
 | 
			
		||||
                    {
 | 
			
		||||
                        hasGroup = true;
 | 
			
		||||
                        g.meshes.Add(m);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!hasGroup)
 | 
			
		||||
                {
 | 
			
		||||
                    groups.Add(new MaterialMeshGroup()
 | 
			
		||||
                    {
 | 
			
		||||
                        skinnedMeshes = new List<SkinnedMeshRenderer>(),
 | 
			
		||||
                        meshes = new List<(MeshFilter mf, MeshRenderer mr)>()
 | 
			
		||||
                        {
 | 
			
		||||
                            m
 | 
			
		||||
                        },
 | 
			
		||||
                        material = m.mr.sharedMaterial
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            List<GameObject> tmp = new List<GameObject>();
 | 
			
		||||
            for (int i = 0; i < groups.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                tmp.Add(new GameObject("tmpChild", typeof(SkinnedMeshRenderer)));
 | 
			
		||||
                tmp[i].transform.parent = target.transform;
 | 
			
		||||
 | 
			
		||||
                MaterialMeshGroup mmg = groups[i];
 | 
			
		||||
                tmp[i].GetComponent<SkinnedMeshRenderer>().Combine(mmg.skinnedMeshes, mmg.meshes, mmg.material);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // TODO: Merge materialMergedObjects.
 | 
			
		||||
            // TEMP: Remove when materialMergedObjects.
 | 
			
		||||
            SkinnedMeshRenderer newSkinnedMeshRenderer = tmp[0].GetComponent<SkinnedMeshRenderer>();
 | 
			
		||||
            target.sharedMesh = newSkinnedMeshRenderer.sharedMesh;
 | 
			
		||||
            target.sharedMaterial = newSkinnedMeshRenderer.sharedMaterial;
 | 
			
		||||
            target.bones = newSkinnedMeshRenderer.bones;
 | 
			
		||||
 | 
			
		||||
            foreach (var go in tmp)
 | 
			
		||||
            {
 | 
			
		||||
                GameObject.DestroyImmediate(go);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Set a name to make it more clear.
 | 
			
		||||
            target.sharedMesh.name = target.transform.name.Replace("(Clone)", "");
 | 
			
		||||
            return target;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static SkinnedMeshRenderer Combine(this SkinnedMeshRenderer target, List<SkinnedMeshRenderer> skinnedMeshes, List<(MeshFilter mf, MeshRenderer mr)> meshes, Material mainMaterial)
 | 
			
		||||
        {
 | 
			
		||||
            List<Transform> bones = new List<Transform>();
 | 
			
		||||
            List<BoneWeight> boneWeights = new List<BoneWeight>();
 | 
			
		||||
            List<Matrix4x4> bindPoses = new List<Matrix4x4>();
 | 
			
		||||
            List<CombineInstance> combineInstances = new List<CombineInstance>();
 | 
			
		||||
 | 
			
		||||
            // Combine SkinnedMeshes.
 | 
			
		||||
            int boneOffset = 0;
 | 
			
		||||
            for (int s = 0; s < skinnedMeshes.Count; s++)
 | 
			
		||||
            {
 | 
			
		||||
                SkinnedMeshRenderer smr = skinnedMeshes[s];
 | 
			
		||||
 | 
			
		||||
                //if the skinned mesh renderer has a material other than the default
 | 
			
		||||
                //we assume it's a one-off face material and deal with it later
 | 
			
		||||
                if (smr.sharedMaterial != mainMaterial)
 | 
			
		||||
                {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                BoneWeight[] meshBoneweight = smr.sharedMesh.boneWeights;
 | 
			
		||||
 | 
			
		||||
                // May want to modify this if the renderer shares bones as unnecessary bones will get added.
 | 
			
		||||
                // We don't care since it is going to be converted into vertex animations later anyways.
 | 
			
		||||
                for (int i = 0; i < meshBoneweight.Length; ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    BoneWeight bWeight = meshBoneweight[i];
 | 
			
		||||
 | 
			
		||||
                    bWeight.boneIndex0 += boneOffset;
 | 
			
		||||
                    bWeight.boneIndex1 += boneOffset;
 | 
			
		||||
                    bWeight.boneIndex2 += boneOffset;
 | 
			
		||||
                    bWeight.boneIndex3 += boneOffset;
 | 
			
		||||
 | 
			
		||||
                    boneWeights.Add(bWeight);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                boneOffset += smr.bones.Length;
 | 
			
		||||
 | 
			
		||||
                Transform[] meshBones = smr.bones;
 | 
			
		||||
                for (int i = 0; i < meshBones.Length; ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    bones.Add(meshBones[i]);
 | 
			
		||||
 | 
			
		||||
                    //we take the old bind pose that mapped from our mesh to world to bone,
 | 
			
		||||
                    //and take out our localToWorldMatrix, so now it's JUST the bone matrix
 | 
			
		||||
                    //since our skinned mesh renderer is going to be on the root of our object that works
 | 
			
		||||
                    bindPoses.Add(smr.sharedMesh.bindposes[i] * smr.transform.worldToLocalMatrix);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                CombineInstance ci = new CombineInstance
 | 
			
		||||
                {
 | 
			
		||||
                    mesh = smr.sharedMesh,
 | 
			
		||||
                    transform = smr.transform.localToWorldMatrix
 | 
			
		||||
                };
 | 
			
		||||
                combineInstances.Add(ci);
 | 
			
		||||
 | 
			
		||||
                GameObject.DestroyImmediate(smr);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Combine Meshes.
 | 
			
		||||
            for (int s = 0; meshes != null && s < meshes.Count; s++)
 | 
			
		||||
            {
 | 
			
		||||
                MeshFilter filter = meshes[s].mf;
 | 
			
		||||
                MeshRenderer renderer = meshes[s].mr;
 | 
			
		||||
            
 | 
			
		||||
                //if the skinned mesh renderer has a material other than the default
 | 
			
		||||
                //we assume it's a one-off face material and deal with it later
 | 
			
		||||
                if (renderer.sharedMaterial != mainMaterial)
 | 
			
		||||
                {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // May want to modify this if the renderer shares bones as unnecessary bones will get added.
 | 
			
		||||
                // We don't care since it is going to be converted into vertex animations later anyways.
 | 
			
		||||
                int vertCount = filter.sharedMesh.vertexCount;
 | 
			
		||||
                for (int i = 0; i < vertCount; ++i)
 | 
			
		||||
                {
 | 
			
		||||
                    BoneWeight bWeight = new BoneWeight
 | 
			
		||||
                    {
 | 
			
		||||
                        boneIndex0 = boneOffset,
 | 
			
		||||
                        boneIndex1 = boneOffset,
 | 
			
		||||
                        boneIndex2 = boneOffset,
 | 
			
		||||
                        boneIndex3 = boneOffset,
 | 
			
		||||
                        weight0 = 1
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    boneWeights.Add(bWeight);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                boneOffset += 1;
 | 
			
		||||
 | 
			
		||||
                bones.Add(filter.transform);
 | 
			
		||||
 | 
			
		||||
                // TODO: figure out what this should be.
 | 
			
		||||
                bindPoses.Add(filter.transform.worldToLocalMatrix);
 | 
			
		||||
 | 
			
		||||
                CombineInstance ci = new CombineInstance
 | 
			
		||||
                {
 | 
			
		||||
                    mesh = filter.sharedMesh,
 | 
			
		||||
                    transform = filter.transform.localToWorldMatrix
 | 
			
		||||
                };
 | 
			
		||||
                combineInstances.Add(ci);
 | 
			
		||||
 | 
			
		||||
                GameObject.DestroyImmediate(filter);
 | 
			
		||||
                GameObject.DestroyImmediate(renderer);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Actually combine and recalculate mesh.
 | 
			
		||||
            Mesh skinnedMesh = new Mesh();
 | 
			
		||||
            skinnedMesh.CombineMeshes(combineInstances.ToArray(), true, true);
 | 
			
		||||
            skinnedMesh.RecalculateBounds();
 | 
			
		||||
 | 
			
		||||
            // Copy settings to target.
 | 
			
		||||
            target.sharedMesh = skinnedMesh;
 | 
			
		||||
            target.sharedMaterial = mainMaterial;
 | 
			
		||||
            target.bones = bones.ToArray();
 | 
			
		||||
            target.sharedMesh.boneWeights = boneWeights.ToArray();
 | 
			
		||||
            target.sharedMesh.bindposes = bindPoses.ToArray();
 | 
			
		||||
 | 
			
		||||
            return target;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static void ConbineAndConvertGameObject(this GameObject gameObject)
 | 
			
		||||
        {
 | 
			
		||||
            // Get Skinned Meshes.
 | 
			
		||||
            List<SkinnedMeshRenderer> skinnedMeshes = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true).ToList();
 | 
			
		||||
            // Get Meshes.
 | 
			
		||||
            List<(MeshFilter, MeshRenderer)> meshes = new List<(MeshFilter, MeshRenderer)>();
 | 
			
		||||
            foreach (var mf in gameObject.GetComponentsInChildren<MeshFilter>(true))
 | 
			
		||||
            {
 | 
			
		||||
                if (mf.TryGetComponent(out MeshRenderer mr))
 | 
			
		||||
                {
 | 
			
		||||
                    meshes.Add((mf, mr));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Add target mesh.
 | 
			
		||||
            SkinnedMeshRenderer target = gameObject.AddComponent<SkinnedMeshRenderer>();
 | 
			
		||||
            target.Combine(skinnedMeshes, meshes);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								Runtime/Scripts/ModelBaker/MeshCombiner.cs.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Runtime/Scripts/ModelBaker/MeshCombiner.cs.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
fileFormatVersion: 2
 | 
			
		||||
guid: f43224daff50a5042a182c6fb12440a8
 | 
			
		||||
MonoImporter:
 | 
			
		||||
  externalObjects: {}
 | 
			
		||||
  serializedVersion: 2
 | 
			
		||||
  defaultReferences: []
 | 
			
		||||
  executionOrder: 0
 | 
			
		||||
  icon: {instanceID: 0}
 | 
			
		||||
  userData: 
 | 
			
		||||
  assetBundleName: 
 | 
			
		||||
  assetBundleVariant: 
 | 
			
		||||
							
								
								
									
										37
									
								
								Runtime/Scripts/ModelBaker/VA_ModelBaker.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								Runtime/Scripts/ModelBaker/VA_ModelBaker.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
 | 
			
		||||
namespace TAO.VertexAnimation
 | 
			
		||||
{
 | 
			
		||||
	[CreateAssetMenu(fileName = "new ModelBaker", menuName = "VA_ModelBaker/ModelBaker", order = 400)]
 | 
			
		||||
	public class VA_ModelBaker : ScriptableObject
 | 
			
		||||
	{
 | 
			
		||||
		public GameObject model;
 | 
			
		||||
		public AnimationClip[] animationClips;
 | 
			
		||||
		public int fps = 24;
 | 
			
		||||
		public int textureWidth = 512;
 | 
			
		||||
		public bool saveBakedDataToAsset = true;
 | 
			
		||||
		public bool generateAnimationBook = false;
 | 
			
		||||
 | 
			
		||||
		[SerializeField]
 | 
			
		||||
		private AnimationBaker.BakedData bakedData;
 | 
			
		||||
		public AnimationBaker.BakedData BakedData
 | 
			
		||||
        {
 | 
			
		||||
			get
 | 
			
		||||
			{
 | 
			
		||||
				return bakedData;
 | 
			
		||||
			}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Bake()
 | 
			
		||||
		{
 | 
			
		||||
			var target = Instantiate(model);
 | 
			
		||||
 | 
			
		||||
			target.ConbineAndConvertGameObject();
 | 
			
		||||
			bakedData = target.Bake(animationClips, fps, textureWidth);
 | 
			
		||||
 | 
			
		||||
			DestroyImmediate(target);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								Runtime/Scripts/ModelBaker/VA_ModelBaker.cs.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Runtime/Scripts/ModelBaker/VA_ModelBaker.cs.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
fileFormatVersion: 2
 | 
			
		||||
guid: 6752e365d065458469473b601e74e699
 | 
			
		||||
MonoImporter:
 | 
			
		||||
  externalObjects: {}
 | 
			
		||||
  serializedVersion: 2
 | 
			
		||||
  defaultReferences: []
 | 
			
		||||
  executionOrder: 0
 | 
			
		||||
  icon: {instanceID: 0}
 | 
			
		||||
  userData: 
 | 
			
		||||
  assetBundleName: 
 | 
			
		||||
  assetBundleVariant: 
 | 
			
		||||
							
								
								
									
										34
									
								
								Runtime/Scripts/ModelBaker/VectorUtils.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Runtime/Scripts/ModelBaker/VectorUtils.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
 | 
			
		||||
namespace TAO.VertexAnimation
 | 
			
		||||
{
 | 
			
		||||
	public static class VectorUtils
 | 
			
		||||
	{
 | 
			
		||||
		public static Vector2 Float3ToFloat2(this Vector3 f3)
 | 
			
		||||
		{
 | 
			
		||||
			Vector3 rotation = Vector3.Normalize(new Vector3(f3.x, 0, f3.z));
 | 
			
		||||
	
 | 
			
		||||
			Vector2 f2 = new Vector2();
 | 
			
		||||
			f2.x = Mathf.Acos(Vector3.Dot(rotation, new Vector3(1, 0, 0))) * Mathf.Sign(f3.z);
 | 
			
		||||
			f2.x = ((f2.x / Mathf.PI) + 1) * 0.5f;
 | 
			
		||||
	
 | 
			
		||||
			f2.y = Mathf.Acos(f3.y) / Mathf.PI;
 | 
			
		||||
	
 | 
			
		||||
			f2 *= 15;
 | 
			
		||||
			f2.x = Mathf.Round(f2.x);
 | 
			
		||||
			f2.y = Mathf.Round(f2.y);
 | 
			
		||||
	
 | 
			
		||||
			return f2;
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
		public static float Float2ToFloat(this Vector2 f2)
 | 
			
		||||
		{
 | 
			
		||||
			return (f2.x + (16 * f2.y)) / 255;
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
		public static float Float3ToFloat(this Vector3 f3)
 | 
			
		||||
		{
 | 
			
		||||
			return Float2ToFloat(Float3ToFloat2(f3));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								Runtime/Scripts/ModelBaker/VectorUtils.cs.meta
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Runtime/Scripts/ModelBaker/VectorUtils.cs.meta
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
fileFormatVersion: 2
 | 
			
		||||
guid: 0a4c82d2f184d3f4d8bb111e89009c3f
 | 
			
		||||
MonoImporter:
 | 
			
		||||
  externalObjects: {}
 | 
			
		||||
  serializedVersion: 2
 | 
			
		||||
  defaultReferences: []
 | 
			
		||||
  executionOrder: 0
 | 
			
		||||
  icon: {instanceID: 0}
 | 
			
		||||
  userData: 
 | 
			
		||||
  assetBundleName: 
 | 
			
		||||
  assetBundleVariant: 
 | 
			
		||||
		Reference in New Issue
	
	Block a user