Model Baker Updates

...
Added fps to book data.
Cleanup.
Preparing LOD generation.
This commit is contained in:
max
2021-01-14 00:45:25 +01:00
parent 322b50b2bd
commit d3b4d34409
20 changed files with 1009 additions and 838 deletions

View File

@ -3,187 +3,190 @@ using UnityEngine;
namespace TAO.VertexAnimation
{
public static class AnimationBaker
{
[System.Serializable]
public struct BakedData
{
public Mesh mesh;
public List<Texture2D> positionMaps;
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];
}
}
}
// 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;
[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;
public int fps;
// Create animation info and calculate values.
public AnimationInfo(Mesh mesh, int frames, int textureWidth)
{
this.frames = frames;
this.textureWidth = textureWidth;
// Create animation info and calculate values.
public AnimationInfo(Mesh mesh, int frames, int textureWidth, int fps)
{
this.frames = frames;
this.textureWidth = textureWidth;
this.fps = fps;
rawFrameHeight = Mathf.CeilToInt((float)mesh.vertices.Length / this.textureWidth);
frameHeight = Mathf.NextPowerOfTwo(rawFrameHeight);
frameSpacing = (frameHeight - rawFrameHeight) + 1;
rawFrameHeight = Mathf.CeilToInt((float)mesh.vertices.Length / this.textureWidth);
frameHeight = Mathf.NextPowerOfTwo(rawFrameHeight);
frameSpacing = (frameHeight - rawFrameHeight) + 1;
textureHeight = Mathf.NextPowerOfTwo(frameHeight * this.frames);
textureHeight = Mathf.NextPowerOfTwo(frameHeight * this.frames);
maxFrames = textureHeight / frameHeight;
}
}
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>()
};
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);
// 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;
}
}
if (maxFrames < frames)
{
maxFrames = frames;
}
}
// Get the target mesh to calculate the animation info.
Mesh mesh = model.GetComponent<SkinnedMeshRenderer>().sharedMesh;
// 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);
// Get the info for the biggest animation.
AnimationInfo animationInfo = new AnimationInfo(mesh, maxFrames, textureWidth, fps);
foreach (AnimationClip ac in animationClips)
{
// Set the frames for this animation.
animationInfo.frames = Mathf.FloorToInt(fps * ac.length);
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);
}
BakedData bd = Bake(model, ac, animationInfo);
bakedData.mesh = bd.mesh;
bakedData.positionMaps.AddRange(bd.positionMaps);
}
return bakedData;
}
return bakedData;
}
public static BakedData Bake(this GameObject model, AnimationClip animationClip, AnimationInfo animationInfo)
{
Mesh mesh = new Mesh
{
name = string.Format("{0}", model.name)
};
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();
// 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);
mesh.uv3 = mesh.BakePositionUVs(animationInfo);
BakedData bakedData = new BakedData()
{
mesh = mesh,
positionMaps = new List<Texture2D>() { BakePositionMap(model, animationClip, animationInfo) }
};
BakedData bakedData = new BakedData()
{
mesh = mesh,
positionMaps = new List<Texture2D>() { BakePositionMap(model, animationClip, animationInfo) }
};
return bakedData;
}
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);
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>();
// 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);
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();
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];
List<Vector3> verts = new List<Vector3>();
sampledMesh.GetVertices(verts);
List<Vector3> normals = new List<Vector3>();
sampledMesh.GetNormals(normals);
positionMap.SetPixel(x, y,
new Color(vert.x, vert.y, vert.z,
VectorUtils.Float3ToFloat(normal))
);
int x = 0;
for (int v = 0; v < verts.Count; v++)
{
positionMap.SetPixel(x, y,
new Color(verts[v].x, verts[v].y, verts[v].z,
VectorUtils.Float3ToFloat(normals[v]))
);
x++;
if (x >= animationInfo.textureWidth)
{
x = 0;
y++;
}
}
y += animationInfo.frameSpacing;
}
x++;
if (x >= animationInfo.textureWidth)
{
x = 0;
y++;
}
}
y += animationInfo.frameSpacing;
}
GameObject.DestroyImmediate(inst);
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);
positionMap.name = string.Format("VA_N-{0}_F-{1}_MF-{2}_FPS-{3}", animationClip.name, animationInfo.frames, animationInfo.maxFrames, animationInfo.fps);
positionMap.filterMode = FilterMode.Point;
positionMap.Apply(false, true);
return positionMap;
}
return positionMap;
}
public static Vector2[] BakePositionUVs(this Mesh mesh, AnimationInfo animationInfo)
{
Vector2[] uv3 = new Vector2[mesh.vertices.Length];
public static Vector2[] BakePositionUVs(this Mesh mesh, AnimationInfo animationInfo)
{
Vector2[] uv3 = new Vector2[mesh.vertexCount];
float xOffset = 1.0f / animationInfo.textureWidth;
float yOffset = 1.0f / animationInfo.textureHeight;
float xOffset = 1.0f / animationInfo.textureWidth;
float yOffset = 1.0f / animationInfo.textureHeight;
float x = xOffset / 2.0f;
float y = yOffset / 2.0f;
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);
for (int v = 0; v < uv3.Length; v++)
{
uv3[v] = new Vector2(x, y);
x += xOffset;
if (x >= 1.0f)
{
x = xOffset / 2.0f;
y += yOffset;
}
}
x += xOffset;
if (x >= 1.0f)
{
x = xOffset / 2.0f;
y += yOffset;
}
}
mesh.uv3 = uv3;
mesh.uv3 = uv3;
return uv3;
}
}
return uv3;
}
}
}

View File

@ -0,0 +1,18 @@
using UnityEngine;
namespace TAO.VertexAnimation
{
public static class AnimationMaterial
{
public static Material Create(string name, Shader shader)
{
Material material = new Material(shader)
{
name = name,
enableInstancing = true
};
return material;
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6752e365d065458469473b601e74e699
guid: 2c06ab1e65507bd46925ac9091097b58
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -1,242 +1,247 @@
using UnityEngine;
// 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
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 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>();
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);
}
}
// 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
});
}
}
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);
}
}
// 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
});
}
}
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;
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);
}
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;
// 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);
}
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;
}
// 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>();
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];
// 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;
}
//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;
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];
// 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;
bWeight.boneIndex0 += boneOffset;
bWeight.boneIndex1 += boneOffset;
bWeight.boneIndex2 += boneOffset;
bWeight.boneIndex3 += boneOffset;
boneWeights.Add(bWeight);
}
boneWeights.Add(bWeight);
}
boneOffset += smr.bones.Length;
boneOffset += smr.bones.Length;
Transform[] meshBones = smr.bones;
for (int i = 0; i < meshBones.Length; ++i)
{
bones.Add(meshBones[i]);
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);
}
//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);
CombineInstance ci = new CombineInstance
{
mesh = smr.sharedMesh,
transform = smr.transform.localToWorldMatrix
};
combineInstances.Add(ci);
GameObject.DestroyImmediate(smr);
}
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;
}
// 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
};
// 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);
}
boneWeights.Add(bWeight);
}
boneOffset += 1;
boneOffset += 1;
bones.Add(filter.transform);
bones.Add(filter.transform);
// TODO: figure out what this should be.
bindPoses.Add(filter.transform.worldToLocalMatrix);
// 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);
CombineInstance ci = new CombineInstance
{
mesh = filter.sharedMesh,
transform = filter.transform.localToWorldMatrix
};
combineInstances.Add(ci);
GameObject.DestroyImmediate(filter);
GameObject.DestroyImmediate(renderer);
}
GameObject.DestroyImmediate(filter);
GameObject.DestroyImmediate(renderer);
}
// Actually combine and recalculate mesh.
Mesh skinnedMesh = new Mesh();
skinnedMesh.CombineMeshes(combineInstances.ToArray(), true, true);
skinnedMesh.RecalculateBounds();
// 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();
// 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;
}
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));
}
}
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);
}
}
// Add target mesh.
SkinnedMeshRenderer target = gameObject.AddComponent<SkinnedMeshRenderer>();
target.Combine(skinnedMeshes, meshes);
}
}
}

View File

@ -1,45 +0,0 @@
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;
[Range(1, 60)]
public int fps = 24;
public int textureWidth = 512;
#if UNITY_EDITOR
public bool saveBakedDataToAsset = true;
public bool generateAnimationBook = true;
public bool generatePrefab = true;
public Shader materialShader = null;
public GameObject prefab = null;
public Material material = null;
public VA_AnimationBook book = null;
#endif
[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);
}
}
}

View File

@ -3,173 +3,176 @@ using UnityEngine;
namespace TAO.VertexAnimation
{
[CreateAssetMenu(fileName = "new AnimationBook", menuName = "VA_Animation/AnimationBook", order = 400)]
public class VA_AnimationBook : ScriptableObject
{
public PlayData playData = null;
[CreateAssetMenu(fileName = "new AnimationBook", menuName = "VA_Animation/AnimationBook", order = 400)]
public class VA_AnimationBook : ScriptableObject
{
public PlayData playData = null;
#if UNITY_EDITOR
public EditorData editorData = new EditorData();
public EditorData editorData = new EditorData();
#endif
private void OnValidate()
{
// TODO: Check for naming conflicts and textures.
// TODO: Debug message box instead of debug logs.
}
private void OnValidate()
{
// TODO: Check for naming conflicts and textures.
// TODO: Debug message box instead of debug logs.
}
public void SetMaterials()
{
if (playData.materials != null)
{
foreach (Material mat in playData.materials)
{
if (mat != null)
{
if (mat.HasProperty("_MaxFrames"))
{
mat.SetFloat("_MaxFrames", playData.maxFrames);
}
public void SetMaterials()
{
if (playData.materials != null)
{
foreach (Material mat in playData.materials)
{
if (mat != null)
{
if (mat.HasProperty("_MaxFrames"))
{
mat.SetFloat("_MaxFrames", playData.maxFrames);
}
for (int i = 0; i < playData.texture2DArray.Count; i++)
{
if (mat.HasProperty(playData.textureGroups[i].shaderParamName))
{
mat.SetTexture(playData.textureGroups[i].shaderParamName, playData.texture2DArray[i]);
}
}
}
}
}
}
for (int i = 0; i < playData.texture2DArray.Count; i++)
{
if (mat.HasProperty(playData.textureGroups[i].shaderParamName))
{
mat.SetTexture(playData.textureGroups[i].shaderParamName, playData.texture2DArray[i]);
}
}
}
}
}
}
#region PlayData
[System.Serializable]
public class PlayData
{
public List<PlayTextureGroup> textureGroups = new List<PlayTextureGroup>();
public List<PlayAnimationPage> animationPages = new List<PlayAnimationPage>();
#region PlayData
[System.Serializable]
public class PlayData
{
public List<PlayTextureGroup> textureGroups = new List<PlayTextureGroup>();
public List<PlayAnimationPage> animationPages = new List<PlayAnimationPage>();
public int maxFrames;
public Material[] materials;
public List<Texture2DArray> texture2DArray = new List<Texture2DArray>();
public int fps;
public int maxFrames;
public Material[] materials;
public List<Texture2DArray> texture2DArray = new List<Texture2DArray>();
// NOTE: for some reason FixedString32 data gets lost when entering play mode.
// That is why this is here... and also the animationPages...
public List<VA_AnimationData> GetAnimations
{
get
{
List<VA_AnimationData> animations = new List<VA_AnimationData>();
foreach (var ap in animationPages)
{
animations.Add(new VA_AnimationData
{
name = ap.name,
frames = ap.frames,
maxFrames = maxFrames,
frameTime = 1.0f / maxFrames,
duration = 1.0f / maxFrames * ap.frames,
animationMapIndex = GetFirstAnimationMapIndex(in ap.textures, in textureGroups),
colorMapIndex = GetFirstColorMapIndex(in ap.textures, in textureGroups)
});
}
return animations;
}
}
// NOTE: for some reason FixedString32 data gets lost when entering play mode.
// That is why this is here... and also the animationPages...
public List<VA_AnimationData> GetAnimations
{
get
{
List<VA_AnimationData> animations = new List<VA_AnimationData>();
foreach (var ap in animationPages)
{
animations.Add(new VA_AnimationData
{
name = ap.name,
frames = ap.frames,
maxFrames = maxFrames,
frameTime = 1.0f / maxFrames * fps,
// TODO: Frames -1 ?????
duration = 1.0f / maxFrames * (ap.frames - 1),
animationMapIndex = GetFirstAnimationMapIndex(in ap.textures, in textureGroups),
colorMapIndex = GetFirstColorMapIndex(in ap.textures, in textureGroups)
});
}
return animations;
}
}
public static int GetFirstAnimationMapIndex(in List<PlayTextureEntry> textures, in List<PlayTextureGroup> textureGroups)
{
for (int i = 0; i < textureGroups.Count; i++)
{
if (textureGroups[i].textureType == TextureType.AnimationMap)
{
return textures[i].textureArrayIndex;
}
}
public static int GetFirstAnimationMapIndex(in List<PlayTextureEntry> textures, in List<PlayTextureGroup> textureGroups)
{
for (int i = 0; i < textureGroups.Count; i++)
{
if (textureGroups[i].textureType == TextureType.AnimationMap)
{
return textures[i].textureArrayIndex;
}
}
return -1;
}
return -1;
}
public static int GetFirstColorMapIndex(in List<PlayTextureEntry> textures, in List<PlayTextureGroup> textureGroups)
{
for (int i = 0; i < textureGroups.Count; i++)
{
if (textureGroups[i].textureType == TextureType.ColorMap)
{
return textures[i].textureArrayIndex;
}
}
public static int GetFirstColorMapIndex(in List<PlayTextureEntry> textures, in List<PlayTextureGroup> textureGroups)
{
for (int i = 0; i < textureGroups.Count; i++)
{
if (textureGroups[i].textureType == TextureType.ColorMap)
{
return textures[i].textureArrayIndex;
}
}
return -1;
}
}
return -1;
}
}
[System.Serializable]
public struct PlayAnimationPage
{
public string name;
public int frames;
public List<PlayTextureEntry> textures;
}
[System.Serializable]
public struct PlayAnimationPage
{
public string name;
public int frames;
public List<PlayTextureEntry> textures;
}
[System.Serializable]
public struct PlayTextureGroup
{
public string shaderParamName;
public TextureType textureType;
}
[System.Serializable]
public struct PlayTextureGroup
{
public string shaderParamName;
public TextureType textureType;
}
[System.Serializable]
public struct PlayTextureEntry
{
public int textureArrayIndex;
}
#endregion
[System.Serializable]
public struct PlayTextureEntry
{
public int textureArrayIndex;
}
#endregion
#region EditorData
#region EditorData
#if UNITY_EDITOR
[System.Serializable]
public class EditorData
{
public List<EditorTextureGroup> textureGroups = new List<EditorTextureGroup>() { new EditorTextureGroup { shaderParamName = "_PositionMap", textureType = TextureType.AnimationMap, wrapMode = TextureWrapMode.Repeat, filterMode = FilterMode.Point, isLinear = false } };
public List<EditorAnimationPage> animationPages = new List<EditorAnimationPage>();
[System.Serializable]
public class EditorData
{
public List<EditorTextureGroup> textureGroups = new List<EditorTextureGroup>() { new EditorTextureGroup { shaderParamName = "_PositionMap", textureType = TextureType.AnimationMap, wrapMode = TextureWrapMode.Repeat, filterMode = FilterMode.Point, isLinear = false } };
public List<EditorAnimationPage> animationPages = new List<EditorAnimationPage>();
public int maxFrames;
public Material[] materials;
public List<Texture2DArray> texture2DArray = null;
}
public int fps;
public int maxFrames;
public Material[] materials;
public List<Texture2DArray> texture2DArray = null;
}
[System.Serializable]
public struct EditorAnimationPage
{
public string name;
public int frames;
public List<EditorTextureEntry> textures;
}
[System.Serializable]
public struct EditorAnimationPage
{
public string name;
public int frames;
public List<EditorTextureEntry> textures;
}
[System.Serializable]
public struct EditorTextureGroup
{
public string shaderParamName;
public TextureType textureType;
public TextureWrapMode wrapMode;
public FilterMode filterMode;
public bool isLinear;
}
[System.Serializable]
public struct EditorTextureGroup
{
public string shaderParamName;
public TextureType textureType;
public TextureWrapMode wrapMode;
public FilterMode filterMode;
public bool isLinear;
}
[System.Serializable]
public class EditorTextureEntry
{
public Texture2D texture2D = null;
public int textureArrayIndex = -1;
}
[System.Serializable]
public class EditorTextureEntry
{
public Texture2D texture2D = null;
public int textureArrayIndex = -1;
}
#endif
#endregion
#endregion
public enum TextureType
{
AnimationMap,
ColorMap
}
}
public enum TextureType
{
AnimationMap,
ColorMap
}
}
}

View File

@ -30,7 +30,7 @@ namespace TAO.VertexAnimation
// Set all the data.
BlobBuilderArray<VA_AnimationData> animationDataArray = blobBuilder.Allocate(ref animationDataBlobAsset.animations, animationLib.animationLibrary.animations.Count);
for (int i = 0; i < animationDataArray.Length; i++)
for (int i = 0; i < animationDataArray.Length; i++)
{
// Copy data.
animationDataArray[i] = animationLib.animationLibrary.animations[i];

View File

@ -12,7 +12,7 @@ namespace TAO.VertexAnimation
public int frames;
// The maximum of frames the texture holds.
public int maxFrames;
// 1.0f / maxFrames.
// 1.0f / fps.
public float frameTime;
// FrameTime * frames.
public float duration;
@ -27,27 +27,27 @@ namespace TAO.VertexAnimation
public BlobArray<VA_AnimationData> animations;
}
public static class VA_AnimationLibraryUtils
public static class VA_AnimationLibraryUtils
{
public const string AnimationLibraryAssetStoreName = "VA_AnimationLibrary";
public static int GetAnimation(ref VA_AnimationLibraryData animationsRef, FixedString32 animationName)
{
for (int i = 0; i < animationsRef.animations.Length; i++)
{
if (animationsRef.animations[i].name == animationName)
{
return i;
}
}
return -1;
}
public static int GetAnimationMapIndex(ref VA_AnimationLibraryData animationsRef, int animation)
public static int GetAnimation(ref VA_AnimationLibraryData animationsRef, FixedString32 animationName)
{
return animationsRef.animations[animation].animationMapIndex;
}
for (int i = 0; i < animationsRef.animations.Length; i++)
{
if (animationsRef.animations[i].name == animationName)
{
return i;
}
}
return -1;
}
public static int GetAnimationMapIndex(ref VA_AnimationLibraryData animationsRef, int animation)
{
return animationsRef.animations[animation].animationMapIndex;
}
public static int GetColorMapIndex(ref VA_AnimationLibraryData animationsRef, int animation)
{

View File

@ -51,9 +51,9 @@ namespace TAO.VertexAnimation
Entity ent = GetPrimaryEntity(children[i]);
VA_AnimationDataComponent animationData = new VA_AnimationDataComponent();
DstEntityManager.AddComponentData(ent, animationData);
}
});
DstEntityManager.AddComponentData(ent, animationData);
}
});
}
}
}

View File

@ -2,104 +2,104 @@
namespace TAO.VertexAnimation
{
public static class VA_Texture2DArrayUtils
{
public static Texture2DArray CreateTextureArray(Texture2D[] a_textures, bool a_useMipChain, bool a_isLinear,
TextureWrapMode a_wrapMode = TextureWrapMode.Repeat, FilterMode a_filterMode = FilterMode.Bilinear, int a_anisoLevel = 1, string a_name = "")
{
if(!IsValidForTextureArray(a_textures) || !IsValidCopyTexturePlatform())
{
return null;
}
public static class VA_Texture2DArrayUtils
{
public static Texture2DArray CreateTextureArray(Texture2D[] a_textures, bool a_useMipChain, bool a_isLinear,
TextureWrapMode a_wrapMode = TextureWrapMode.Repeat, FilterMode a_filterMode = FilterMode.Bilinear, int a_anisoLevel = 1, string a_name = "")
{
if(!IsValidForTextureArray(a_textures) || !IsValidCopyTexturePlatform())
{
return null;
}
Texture2DArray textureArray = new Texture2DArray(a_textures[0].width, a_textures[0].height, a_textures.Length, a_textures[0].format, a_useMipChain, a_isLinear);
Texture2DArray textureArray = new Texture2DArray(a_textures[0].width, a_textures[0].height, a_textures.Length, a_textures[0].format, a_useMipChain, a_isLinear);
if (IsValidCopyTexturePlatform())
{
for (int i = 0; i < a_textures.Length; i++)
{
Graphics.CopyTexture(a_textures[i], 0, 0, textureArray, i, 0);
}
}
if (IsValidCopyTexturePlatform())
{
for (int i = 0; i < a_textures.Length; i++)
{
Graphics.CopyTexture(a_textures[i], 0, 0, textureArray, i, 0);
}
}
textureArray.wrapMode = a_wrapMode;
textureArray.filterMode = a_filterMode;
textureArray.anisoLevel = a_anisoLevel;
textureArray.name = a_name;
textureArray.wrapMode = a_wrapMode;
textureArray.filterMode = a_filterMode;
textureArray.anisoLevel = a_anisoLevel;
textureArray.name = a_name;
textureArray.Apply(false, false);
textureArray.Apply(false, false);
return textureArray;
}
return textureArray;
}
public static bool IsValidForTextureArray(Texture2D[] a_textures)
{
if (a_textures == null || a_textures.Length <= 0)
{
Debug.LogError("No textures assigned!");
return false;
}
public static bool IsValidForTextureArray(Texture2D[] a_textures)
{
if (a_textures == null || a_textures.Length <= 0)
{
Debug.LogError("No textures assigned!");
return false;
}
for (int i = 0; i < a_textures.Length; i++)
{
if (a_textures[i] == null)
{
Debug.LogError("Texture " + i.ToString() + " not assigned!");
return false;
}
for (int i = 0; i < a_textures.Length; i++)
{
if (a_textures[i] == null)
{
Debug.LogError("Texture " + i.ToString() + " not assigned!");
return false;
}
if (a_textures[0].width != a_textures[i].width || a_textures[0].height != a_textures[i].height)
{
Debug.LogError("Texture " + a_textures[i].name + " has a different size!");
return false;
}
if (a_textures[0].width != a_textures[i].width || a_textures[0].height != a_textures[i].height)
{
Debug.LogError("Texture " + a_textures[i].name + " has a different size!");
return false;
}
if (a_textures[0].format != a_textures[i].format || a_textures[0].graphicsFormat != a_textures[i].graphicsFormat)
{
Debug.LogError("Texture " + a_textures[i].name + " has a different format/graphics format!");
return false;
}
if (a_textures[0].format != a_textures[i].format || a_textures[0].graphicsFormat != a_textures[i].graphicsFormat)
{
Debug.LogError("Texture " + a_textures[i].name + " has a different format/graphics format!");
return false;
}
if (!a_textures[0].isReadable)
{
if (!a_textures[0].isReadable)
{
#if UNITY_EDITOR
//Debug.LogWarning("Texture " + a_textures[i].name + " is not readable!");
return true;
//Debug.LogWarning("Texture " + a_textures[i].name + " is not readable!");
return true;
#else
Debug.LogError("Texture " + a_textures[i].name + " is not readable!");
return false;
Debug.LogError("Texture " + a_textures[i].name + " is not readable!");
return false;
#endif
}
}
}
}
return true;
}
return true;
}
public static bool IsValidCopyTexturePlatform()
{
switch (SystemInfo.copyTextureSupport)
{
case UnityEngine.Rendering.CopyTextureSupport.None:
Debug.LogError("No CopyTextureSupport on this platform!");
return false;
case UnityEngine.Rendering.CopyTextureSupport.Basic:
return true;
case UnityEngine.Rendering.CopyTextureSupport.Copy3D:
return true;
case UnityEngine.Rendering.CopyTextureSupport.DifferentTypes:
return true;
case UnityEngine.Rendering.CopyTextureSupport.TextureToRT:
return true;
case UnityEngine.Rendering.CopyTextureSupport.RTToTexture:
return true;
default:
public static bool IsValidCopyTexturePlatform()
{
switch (SystemInfo.copyTextureSupport)
{
case UnityEngine.Rendering.CopyTextureSupport.None:
Debug.LogError("No CopyTextureSupport on this platform!");
return false;
case UnityEngine.Rendering.CopyTextureSupport.Basic:
return true;
case UnityEngine.Rendering.CopyTextureSupport.Copy3D:
return true;
case UnityEngine.Rendering.CopyTextureSupport.DifferentTypes:
return true;
case UnityEngine.Rendering.CopyTextureSupport.TextureToRT:
return true;
case UnityEngine.Rendering.CopyTextureSupport.RTToTexture:
return true;
default:
#if UNITY_EDITOR
return true;
return true;
#else
Debug.LogError("No CopyTextureSupport on this platform!");
return false;
Debug.LogError("No CopyTextureSupport on this platform!");
return false;
#endif
}
}
}
}
}
}
}