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

@ -0,0 +1,47 @@
using UnityEngine;
using UnityEditor;
namespace TAO.VertexAnimation.Editor
{
public static class AssetDatabaseUtils
{
public static bool HasChildAsset(Object parent, Object child)
{
var assets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(parent));
foreach (var a in assets)
{
if (a == child)
{
return true;
}
}
return false;
}
public static void RemoveChildAssets(Object parent, Object[] filter = null)
{
var assets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(parent));
foreach (var a in assets)
{
bool filterSkip = false;
foreach (var f in filter)
{
if (a == f)
{
filterSkip = true;
break;
}
}
if (!filterSkip && a != parent)
{
AssetDatabase.RemoveObjectFromAsset(a);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d394aa43ff9889d45878e965ac0f1a30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -68,6 +68,7 @@ namespace TAO.VertexAnimation.Editor
using (new EditorGUILayout.VerticalScope()) using (new EditorGUILayout.VerticalScope())
{ {
EditorGUILayout.LabelField("General", EditorStyles.centeredGreyMiniLabel); EditorGUILayout.LabelField("General", EditorStyles.centeredGreyMiniLabel);
EditorGUILayout.PropertyField(editorData.FindPropertyRelative("fps"));
EditorGUILayout.PropertyField(editorData.FindPropertyRelative("maxFrames")); EditorGUILayout.PropertyField(editorData.FindPropertyRelative("maxFrames"));
} }
} }

View File

@ -0,0 +1,52 @@
using UnityEngine;
using UnityEditor;
namespace TAO.VertexAnimation.Editor
{
public static class AnimationPrefab
{
public static GameObject Create(string path, string name, Mesh[] meshes, Material material, float[] lodTransitions)
{
// Create parent.
GameObject parent = new GameObject(name, typeof(LODGroup), typeof(VA_AnimatorComponentAuthoring), typeof(Unity.Entities.ConvertToEntity));
// Create all LODs.
LOD[] lods = new LOD[meshes.Length];
for (int i = 0; i < meshes.Length; i++)
{
GameObject lod = new GameObject(string.Format("{0}_LOD{1}", name, i), typeof(MeshFilter), typeof(MeshRenderer));
var mf = lod.GetComponent<MeshFilter>();
mf.sharedMesh = meshes[i];
var mr = lod.GetComponent<MeshRenderer>();
mr.sharedMaterial = material;
lod.transform.SetParent(parent.transform);
lods[i] = new LOD(lodTransitions[i], new Renderer[1] { mr });
}
var lodGroup = parent.GetComponent<LODGroup>();
lodGroup.SetLODs(lods);
lodGroup.RecalculateBounds();
// Create prefab.
GameObject prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(parent, path, InteractionMode.AutomatedAction);
GameObject.DestroyImmediate(parent);
return prefab;
}
public static GameObject Create(string path, string name, Mesh[] meshes, Material material, AnimationCurve lodTransitions)
{
float[] lt = new float[meshes.Length];
for (int i = 0; i < lt.Length; i++)
{
lt[i] = lodTransitions.Evaluate((1.0f / lt.Length) * (i + 1));
}
return Create(path, name, meshes, material, lt);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b2f9ea47456fd24387722ab88de04c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -5,212 +5,87 @@ using System.Linq;
namespace TAO.VertexAnimation.Editor namespace TAO.VertexAnimation.Editor
{ {
[CustomEditor(typeof(VA_ModelBaker))] [CustomEditor(typeof(VA_ModelBaker))]
public class VA_ModelBakerEditor : UnityEditor.Editor public class VA_ModelBakerEditor : UnityEditor.Editor
{ {
private VA_ModelBaker modelBaker = null; private VA_ModelBaker modelBaker = null;
void OnEnable() void OnEnable()
{ {
modelBaker = target as VA_ModelBaker; modelBaker = target as VA_ModelBaker;
} }
public override void OnInspectorGUI() public override void OnInspectorGUI()
{ {
serializedObject.Update(); serializedObject.Update();
InputGUI(); InputGUI();
EditorGUILayoutUtils.HorizontalLine(color: Color.gray); EditorGUILayoutUtils.HorizontalLine(color: Color.gray);
BakeGUI(); BakeGUI();
serializedObject.ApplyModifiedProperties(); serializedObject.ApplyModifiedProperties();
EditorGUILayoutUtils.HorizontalLine(color: Color.gray); EditorGUILayoutUtils.HorizontalLine(color: Color.gray);
OutputGUI(); OutputGUI();
} }
private void InputGUI() private void InputGUI()
{ {
EditorGUILayout.PropertyField(serializedObject.FindProperty("model")); EditorGUILayout.PropertyField(serializedObject.FindProperty("model"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("animationClips")); EditorGUILayout.PropertyField(serializedObject.FindProperty("animationClips"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("fps")); EditorGUILayout.PropertyField(serializedObject.FindProperty("fps"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("textureWidth")); EditorGUILayout.PropertyField(serializedObject.FindProperty("textureWidth"));
} }
private void BakeGUI() private void BakeGUI()
{ {
EditorGUILayout.PropertyField(serializedObject.FindProperty("saveBakedDataToAsset")); EditorGUILayout.PropertyField(serializedObject.FindProperty("saveBakedDataToAsset"));
int il = EditorGUI.indentLevel; int il = EditorGUI.indentLevel;
if (modelBaker.saveBakedDataToAsset) if (modelBaker.saveBakedDataToAsset)
{ {
EditorGUI.indentLevel++; EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(serializedObject.FindProperty("generateAnimationBook")); EditorGUILayout.PropertyField(serializedObject.FindProperty("generateAnimationBook"));
using (new EditorGUILayout.HorizontalScope()) using (new EditorGUILayout.HorizontalScope())
{ {
EditorGUILayout.PropertyField(serializedObject.FindProperty("generatePrefab")); EditorGUILayout.PropertyField(serializedObject.FindProperty("generateLODS"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("materialShader"), new GUIContent("")); EditorGUILayout.PropertyField(serializedObject.FindProperty("lodCurve"), new GUIContent(""));
} }
}
EditorGUI.indentLevel = il;
if (GUILayout.Button("Bake", GUILayout.Height(32))) using (new EditorGUILayout.HorizontalScope())
{ {
ClearBakedData(); EditorGUILayout.PropertyField(serializedObject.FindProperty("generatePrefab"));
EditorGUILayout.PropertyField(serializedObject.FindProperty("materialShader"), new GUIContent(""));
}
}
EditorGUI.indentLevel = il;
modelBaker.Bake(); if (GUILayout.Button("Bake", GUILayout.Height(32)))
{
modelBaker.Bake();
if (modelBaker.saveBakedDataToAsset) if (modelBaker.saveBakedDataToAsset)
{ {
SaveBakedData(); modelBaker.SaveAssets();
} }
} }
if (modelBaker.BakedData.mesh != null) if (GUILayout.Button("Delete", EditorStyles.miniButtonRight))
{ {
using (new EditorGUILayout.HorizontalScope()) if (EditorUtility.DisplayDialog("Delete Assets", "Deleting assets will loose references within the project.", "Ok", "Cancel"))
{ {
if (GUILayout.Button("Save", EditorStyles.miniButtonLeft)) modelBaker.DeleteSavedAssets();
{ }
SaveBakedData(); }
} }
if (GUILayout.Button("Clear", EditorStyles.miniButtonRight)) private void OutputGUI()
{ {
ClearBakedData(); using (new EditorGUI.DisabledGroupScope(true))
} {
} EditorGUILayout.PropertyField(serializedObject.FindProperty("bakedData"));
}
if (modelBaker.prefab && GUILayout.Button("Remove Prefab")) }
{ }
DeletePrefab();
}
}
}
private void OutputGUI()
{
using (new EditorGUI.DisabledGroupScope(true))
{
EditorGUILayout.PropertyField(serializedObject.FindProperty("bakedData"));
}
}
private void SaveBakedData()
{
ClearBakedData();
AssetDatabase.AddObjectToAsset(modelBaker.BakedData.mesh, modelBaker);
foreach (var pm in modelBaker.BakedData.positionMaps)
{
AssetDatabase.AddObjectToAsset(pm, modelBaker);
}
AssetDatabase.SaveAssets();
if (modelBaker.generatePrefab)
{
GeneratePrefab();
}
if(modelBaker.generateAnimationBook)
{
GenerateBook();
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
private void ClearBakedData()
{
var assets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(modelBaker));
foreach (var a in assets)
{
if (a != modelBaker)
{
AssetDatabase.RemoveObjectFromAsset(a);
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
private void DeletePrefab()
{
string path = AssetDatabase.GetAssetPath(modelBaker.prefab);
AssetDatabase.DeleteAsset(path);
AssetDatabase.Refresh();
}
private void GeneratePrefab()
{
string path = AssetDatabase.GetAssetPath(modelBaker);
int start = path.LastIndexOf('/');
path = path.Remove(start, path.Length - start);
path += "/" + modelBaker.name + ".prefab";
// Generate Material
modelBaker.material = new Material(modelBaker.materialShader);
modelBaker.material.name = modelBaker.name;
AssetDatabase.AddObjectToAsset(modelBaker.material, modelBaker);
// Generate Object.
if (!modelBaker.prefab)
{
GameObject go = new GameObject(modelBaker.model.name, typeof(MeshFilter), typeof(MeshRenderer));
modelBaker.prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(go, path, InteractionMode.AutomatedAction);
DestroyImmediate(go);
}
GameObject inst = PrefabUtility.InstantiatePrefab(modelBaker.prefab) as GameObject;
inst.GetComponent<MeshFilter>().sharedMesh = modelBaker.BakedData.mesh;
inst.GetComponent<MeshRenderer>().sharedMaterial = modelBaker.material;
// Save.
PrefabUtility.ApplyPrefabInstance(inst, InteractionMode.UserAction);
AssetDatabase.SaveAssets();
DestroyImmediate(inst);
}
private void GenerateBook()
{
if (!modelBaker.book)
{
modelBaker.book = CreateInstance<VA_AnimationBook>();
}
modelBaker.book.name = modelBaker.model.name;
modelBaker.book.editorData = new VA_AnimationBook.EditorData();
modelBaker.book.editorData.materials = new Material[1] { modelBaker.material };
foreach (Texture2D tex in modelBaker.BakedData.positionMaps)
{
modelBaker.book.editorData.animationPages.Add(new VA_AnimationBook.EditorAnimationPage
{
name = "",
frames = 0,
textures = new List<VA_AnimationBook.EditorTextureEntry>()
{
new VA_AnimationBook.EditorTextureEntry
{
texture2D = tex
}
}
});
}
VA_AssetBuilder.AutoFill(ref modelBaker.book);
AssetDatabase.AddObjectToAsset(modelBaker.book, modelBaker);
AssetDatabase.SaveAssets();
}
}
} }

View File

@ -0,0 +1,169 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace TAO.VertexAnimation.Editor
{
[CreateAssetMenu(fileName = "new ModelBaker", menuName = "VA_ModelBaker/ModelBaker", order = 400)]
public class VA_ModelBaker : ScriptableObject
{
#if UNITY_EDITOR
// Input.
public GameObject model;
public AnimationClip[] animationClips;
[Range(1, 60)]
public int fps = 24;
public int textureWidth = 512;
public bool generateLODS = true;
public AnimationCurve lodCurve = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1, 0.01f));
public bool saveBakedDataToAsset = true;
public bool generateAnimationBook = true;
public bool generatePrefab = true;
public Shader materialShader = null;
// Output.
public GameObject prefab = null;
public Material material = null;
public Mesh[] meshes = null;
public VA_AnimationBook book = null;
[SerializeField]
private AnimationBaker.BakedData bakedData;
public void Bake()
{
var target = Instantiate(model);
target.name = model.name;
target.ConbineAndConvertGameObject();
bakedData = target.Bake(animationClips, fps, textureWidth);
if (generateLODS)
{
// TODO: LODs.
meshes = new Mesh[1] { bakedData.mesh };
}
else
{
meshes = new Mesh[1] { bakedData.mesh };
}
DestroyImmediate(target);
}
public void SaveAssets()
{
AssetDatabaseUtils.RemoveChildAssets(this, new Object[2] { book, material });
// TODO: LODs
AssetDatabase.AddObjectToAsset(bakedData.mesh, this);
foreach (var pm in bakedData.positionMaps)
{
AssetDatabase.AddObjectToAsset(pm, this);
}
AssetDatabase.SaveAssets();
if (generatePrefab)
{
GeneratePrefab();
}
if (generateAnimationBook)
{
GenerateBook();
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
public void DeleteSavedAssets()
{
// Remove assets.
var assets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(this));
foreach (var a in assets)
{
if (a != this)
{
AssetDatabase.RemoveObjectFromAsset(a);
}
}
// Delete prefab.
string path = AssetDatabase.GetAssetPath(prefab);
AssetDatabase.DeleteAsset(path);
// Clear variables.
prefab = null;
material = null;
meshes = null;
book = null;
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
public void GeneratePrefab()
{
string path = AssetDatabase.GetAssetPath(this);
int start = path.LastIndexOf('/');
path = path.Remove(start, path.Length - start);
path += "/" + name + ".prefab";
// Generate Material
if (!AssetDatabaseUtils.HasChildAsset(this, material))
{
material = AnimationMaterial.Create(name, materialShader);
AssetDatabase.AddObjectToAsset(material, this);
}
else
{
material.shader = materialShader;
}
// Generate Prefab
prefab = AnimationPrefab.Create(path, name, meshes, material, lodCurve);
}
public void GenerateBook()
{
if (!book)
{
book = CreateInstance<VA_AnimationBook>();
}
book.name = string.Format("{0}Book", name);
book.editorData = new VA_AnimationBook.EditorData
{
materials = new Material[1] { material }
};
foreach (Texture2D tex in bakedData.positionMaps)
{
book.editorData.animationPages.Add(new VA_AnimationBook.EditorAnimationPage
{
name = "",
frames = 0,
textures = new List<VA_AnimationBook.EditorTextureEntry>()
{
new VA_AnimationBook.EditorTextureEntry
{
texture2D = tex
}
}
});
}
VA_AssetBuilder.AutoFill(ref book);
if (!AssetDatabaseUtils.HasChildAsset(this, book))
{
AssetDatabase.AddObjectToAsset(book, this);
}
}
#endif
}
}

View File

@ -300,6 +300,13 @@ namespace TAO.VertexAnimation.Editor
book.editorData.maxFrames = maxFrames; book.editorData.maxFrames = maxFrames;
} }
} }
else if (p.StartsWith("FPS-"))
{
if (int.TryParse(p.Remove(0, 4), out int fps))
{
book.editorData.fps = fps;
}
}
} }
} }
book.editorData.animationPages[i] = ap; book.editorData.animationPages[i] = ap;
@ -339,6 +346,7 @@ namespace TAO.VertexAnimation.Editor
{ {
book.playData = new VA_AnimationBook.PlayData book.playData = new VA_AnimationBook.PlayData
{ {
fps = book.editorData.fps,
maxFrames = book.editorData.maxFrames, maxFrames = book.editorData.maxFrames,
materials = book.editorData.materials materials = book.editorData.materials
}; };

View File

@ -3,7 +3,9 @@
"rootNamespace": "TAO.VertexAnimation.Editor", "rootNamespace": "TAO.VertexAnimation.Editor",
"references": [ "references": [
"TAO.VertexAnimation", "TAO.VertexAnimation",
"Unity.Collections" "Unity.Collections",
"Unity.Entities",
"Unity.Entities.Hybrid"
], ],
"includePlatforms": [ "includePlatforms": [
"Editor" "Editor"

View File

@ -3,187 +3,190 @@ using UnityEngine;
namespace TAO.VertexAnimation namespace TAO.VertexAnimation
{ {
public static class AnimationBaker public static class AnimationBaker
{ {
[System.Serializable] [System.Serializable]
public struct BakedData public struct BakedData
{ {
public Mesh mesh; public Mesh mesh;
public List<Texture2D> positionMaps; public List<Texture2D> positionMaps;
// Returns main position map. // Returns main position map.
public Texture2D GetPositionMap public Texture2D GetPositionMap
{ {
get get
{ {
return positionMaps[0]; return positionMaps[0];
} }
} }
} }
[System.Serializable] [System.Serializable]
public struct AnimationInfo public struct AnimationInfo
{ {
public int rawFrameHeight; public int rawFrameHeight;
public int frameHeight; public int frameHeight;
public int frameSpacing; public int frameSpacing;
public int frames; public int frames;
public int maxFrames; public int maxFrames;
public int textureWidth; public int textureWidth;
public int textureHeight; public int textureHeight;
public int fps;
// Create animation info and calculate values. // Create animation info and calculate values.
public AnimationInfo(Mesh mesh, int frames, int textureWidth) public AnimationInfo(Mesh mesh, int frames, int textureWidth, int fps)
{ {
this.frames = frames; this.frames = frames;
this.textureWidth = textureWidth; this.textureWidth = textureWidth;
this.fps = fps;
rawFrameHeight = Mathf.CeilToInt((float)mesh.vertices.Length / this.textureWidth); rawFrameHeight = Mathf.CeilToInt((float)mesh.vertices.Length / this.textureWidth);
frameHeight = Mathf.NextPowerOfTwo(rawFrameHeight); frameHeight = Mathf.NextPowerOfTwo(rawFrameHeight);
frameSpacing = (frameHeight - rawFrameHeight) + 1; 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) public static BakedData Bake(this GameObject model, AnimationClip[] animationClips, int fps, int textureWidth)
{ {
BakedData bakedData = new BakedData() BakedData bakedData = new BakedData()
{ {
mesh = null, mesh = null,
positionMaps = new List<Texture2D>() positionMaps = new List<Texture2D>()
}; };
// Calculate what our max frames/time is going to be. // Calculate what our max frames/time is going to be.
int maxFrames = 0; int maxFrames = 0;
foreach (AnimationClip ac in animationClips) foreach (AnimationClip ac in animationClips)
{ {
int frames = Mathf.FloorToInt(fps * ac.length); int frames = Mathf.FloorToInt(fps * ac.length);
if (maxFrames < frames) if (maxFrames < frames)
{ {
maxFrames = frames; maxFrames = frames;
} }
} }
// Get the target mesh to calculate the animation info. // Get the target mesh to calculate the animation info.
Mesh mesh = model.GetComponent<SkinnedMeshRenderer>().sharedMesh; Mesh mesh = model.GetComponent<SkinnedMeshRenderer>().sharedMesh;
// Get the info for the biggest animation. // Get the info for the biggest animation.
AnimationInfo animationInfo = new AnimationInfo(mesh, maxFrames, textureWidth); AnimationInfo animationInfo = new AnimationInfo(mesh, maxFrames, textureWidth, fps);
foreach (AnimationClip ac in animationClips) foreach (AnimationClip ac in animationClips)
{ {
// Set the frames for this animation. // Set the frames for this animation.
animationInfo.frames = Mathf.FloorToInt(fps * ac.length); animationInfo.frames = Mathf.FloorToInt(fps * ac.length);
BakedData bd = Bake(model, ac, animationInfo); BakedData bd = Bake(model, ac, animationInfo);
bakedData.mesh = bd.mesh; bakedData.mesh = bd.mesh;
bakedData.positionMaps.AddRange(bd.positionMaps); bakedData.positionMaps.AddRange(bd.positionMaps);
} }
return bakedData; return bakedData;
} }
public static BakedData Bake(this GameObject model, AnimationClip animationClip, AnimationInfo animationInfo) public static BakedData Bake(this GameObject model, AnimationClip animationClip, AnimationInfo animationInfo)
{ {
Mesh mesh = new Mesh Mesh mesh = new Mesh
{ {
name = string.Format("{0}", model.name) name = string.Format("{0}", model.name)
}; };
// Bake mesh for a copy and to apply the new UV's to. // Bake mesh for a copy and to apply the new UV's to.
SkinnedMeshRenderer skinnedMeshRenderer = model.GetComponent<SkinnedMeshRenderer>(); SkinnedMeshRenderer skinnedMeshRenderer = model.GetComponent<SkinnedMeshRenderer>();
skinnedMeshRenderer.BakeMesh(mesh); skinnedMeshRenderer.BakeMesh(mesh);
mesh.RecalculateBounds(); mesh.RecalculateBounds();
mesh.uv3 = mesh.BakePositionUVs(animationInfo); mesh.uv3 = mesh.BakePositionUVs(animationInfo);
BakedData bakedData = new BakedData() BakedData bakedData = new BakedData()
{ {
mesh = mesh, mesh = mesh,
positionMaps = new List<Texture2D>() { BakePositionMap(model, animationClip, animationInfo) } positionMaps = new List<Texture2D>() { BakePositionMap(model, animationClip, animationInfo) }
}; };
return bakedData; return bakedData;
} }
public static Texture2D BakePositionMap(this GameObject model, AnimationClip animationClip, AnimationInfo animationInfo) 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. // 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); Texture2D positionMap = new Texture2D(animationInfo.textureWidth, animationInfo.textureHeight, TextureFormat.RGBAHalf, false, true);
// Create instance to sample from. // Create instance to sample from.
GameObject inst = GameObject.Instantiate(model); GameObject inst = GameObject.Instantiate(model);
SkinnedMeshRenderer skinnedMeshRenderer = inst.GetComponent<SkinnedMeshRenderer>(); SkinnedMeshRenderer skinnedMeshRenderer = inst.GetComponent<SkinnedMeshRenderer>();
int y = 0; int y = 0;
for (int f = 0; f < animationInfo.frames; f++) for (int f = 0; f < animationInfo.frames; f++)
{ {
animationClip.SampleAnimation(inst, (animationClip.length / animationInfo.frames) * f); animationClip.SampleAnimation(inst, (animationClip.length / animationInfo.frames) * f);
Mesh sampledMesh = new Mesh(); Mesh sampledMesh = new Mesh();
skinnedMeshRenderer.BakeMesh(sampledMesh); skinnedMeshRenderer.BakeMesh(sampledMesh);
sampledMesh.RecalculateBounds(); sampledMesh.RecalculateBounds();
int x = 0; List<Vector3> verts = new List<Vector3>();
for (int v = 0; v < sampledMesh.vertices.Length; v++) sampledMesh.GetVertices(verts);
{ List<Vector3> normals = new List<Vector3>();
Vector3 vert = sampledMesh.vertices[v]; sampledMesh.GetNormals(normals);
Vector3 normal = sampledMesh.normals[v];
positionMap.SetPixel(x, y, int x = 0;
new Color(vert.x, vert.y, vert.z, for (int v = 0; v < verts.Count; v++)
VectorUtils.Float3ToFloat(normal)) {
); positionMap.SetPixel(x, y,
new Color(verts[v].x, verts[v].y, verts[v].z,
VectorUtils.Float3ToFloat(normals[v]))
);
x++; x++;
if (x >= animationInfo.textureWidth) if (x >= animationInfo.textureWidth)
{ {
x = 0; x = 0;
y++; y++;
} }
} }
y += animationInfo.frameSpacing; 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.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.filterMode = FilterMode.Point;
// TODO: Make no longer readable. positionMap.Apply(false, true);
positionMap.Apply(false, false);
return positionMap; return positionMap;
} }
public static Vector2[] BakePositionUVs(this Mesh mesh, AnimationInfo animationInfo) public static Vector2[] BakePositionUVs(this Mesh mesh, AnimationInfo animationInfo)
{ {
Vector2[] uv3 = new Vector2[mesh.vertices.Length]; Vector2[] uv3 = new Vector2[mesh.vertexCount];
float xOffset = 1.0f / animationInfo.textureWidth; float xOffset = 1.0f / animationInfo.textureWidth;
float yOffset = 1.0f / animationInfo.textureHeight; float yOffset = 1.0f / animationInfo.textureHeight;
float x = xOffset / 2.0f; float x = xOffset / 2.0f;
float y = yOffset / 2.0f; float y = yOffset / 2.0f;
for (int v = 0; v < mesh.vertices.Length; v++) for (int v = 0; v < uv3.Length; v++)
{ {
uv3[v] = new Vector2(x, y); uv3[v] = new Vector2(x, y);
x += xOffset; x += xOffset;
if (x >= 1.0f) if (x >= 1.0f)
{ {
x = xOffset / 2.0f; x = xOffset / 2.0f;
y += yOffset; 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

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c06ab1e65507bd46925ac9091097b58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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.Collections.Generic;
using System.Linq; using System.Linq;
namespace TAO.VertexAnimation namespace TAO.VertexAnimation
{ {
public static class MeshCombiner public static class MeshCombiner
{ {
private struct MaterialMeshGroup private struct MaterialMeshGroup
{ {
public List<SkinnedMeshRenderer> skinnedMeshes; public List<SkinnedMeshRenderer> skinnedMeshes;
public List<(MeshFilter mf, MeshRenderer mr)> meshes; public List<(MeshFilter mf, MeshRenderer mr)> meshes;
public Material material; public Material material;
} }
public static SkinnedMeshRenderer Combine(this SkinnedMeshRenderer target, List<SkinnedMeshRenderer> skinnedMeshes, List<(MeshFilter mf, MeshRenderer mr)> meshes) public static SkinnedMeshRenderer Combine(this SkinnedMeshRenderer target, List<SkinnedMeshRenderer> skinnedMeshes, List<(MeshFilter mf, MeshRenderer mr)> meshes)
{ {
List<MaterialMeshGroup> groups = new List<MaterialMeshGroup>(); List<MaterialMeshGroup> groups = new List<MaterialMeshGroup>();
// Group skinnedMeshes. // Group skinnedMeshes.
foreach (var sm in skinnedMeshes) foreach (var sm in skinnedMeshes)
{ {
bool hasGroup = false; bool hasGroup = false;
foreach (var g in groups) foreach (var g in groups)
{ {
if (sm.sharedMaterial == g.material) if (sm.sharedMaterial == g.material)
{ {
hasGroup = true; hasGroup = true;
g.skinnedMeshes.Add(sm); g.skinnedMeshes.Add(sm);
} }
} }
if (!hasGroup) if (!hasGroup)
{ {
groups.Add(new MaterialMeshGroup() groups.Add(new MaterialMeshGroup()
{ {
skinnedMeshes = new List<SkinnedMeshRenderer>() skinnedMeshes = new List<SkinnedMeshRenderer>()
{ {
sm sm
}, },
meshes = new List<(MeshFilter mf, MeshRenderer mr)>(), meshes = new List<(MeshFilter mf, MeshRenderer mr)>(),
material = sm.sharedMaterial material = sm.sharedMaterial
}); });
} }
} }
// Group Meshes. // Group Meshes.
foreach (var m in meshes) foreach (var m in meshes)
{ {
bool hasGroup = false; bool hasGroup = false;
foreach (var g in groups) foreach (var g in groups)
{ {
if (m.mr.sharedMaterial == g.material) if (m.mr.sharedMaterial == g.material)
{ {
hasGroup = true; hasGroup = true;
g.meshes.Add(m); g.meshes.Add(m);
} }
} }
if (!hasGroup) if (!hasGroup)
{ {
groups.Add(new MaterialMeshGroup() groups.Add(new MaterialMeshGroup()
{ {
skinnedMeshes = new List<SkinnedMeshRenderer>(), skinnedMeshes = new List<SkinnedMeshRenderer>(),
meshes = new List<(MeshFilter mf, MeshRenderer mr)>() meshes = new List<(MeshFilter mf, MeshRenderer mr)>()
{ {
m m
}, },
material = m.mr.sharedMaterial material = m.mr.sharedMaterial
}); });
} }
} }
List<GameObject> tmp = new List<GameObject>(); List<GameObject> tmp = new List<GameObject>();
for (int i = 0; i < groups.Count; i++) for (int i = 0; i < groups.Count; i++)
{ {
tmp.Add(new GameObject("tmpChild", typeof(SkinnedMeshRenderer))); tmp.Add(new GameObject("tmpChild", typeof(SkinnedMeshRenderer)));
tmp[i].transform.parent = target.transform; tmp[i].transform.parent = target.transform;
MaterialMeshGroup mmg = groups[i]; MaterialMeshGroup mmg = groups[i];
tmp[i].GetComponent<SkinnedMeshRenderer>().Combine(mmg.skinnedMeshes, mmg.meshes, mmg.material); tmp[i].GetComponent<SkinnedMeshRenderer>().Combine(mmg.skinnedMeshes, mmg.meshes, mmg.material);
} }
// TODO: Merge materialMergedObjects. // TODO: Merge materialMergedObjects.
// TEMP: Remove when materialMergedObjects. // TEMP: Remove when materialMergedObjects.
SkinnedMeshRenderer newSkinnedMeshRenderer = tmp[0].GetComponent<SkinnedMeshRenderer>(); SkinnedMeshRenderer newSkinnedMeshRenderer = tmp[0].GetComponent<SkinnedMeshRenderer>();
target.sharedMesh = newSkinnedMeshRenderer.sharedMesh; target.sharedMesh = newSkinnedMeshRenderer.sharedMesh;
target.sharedMaterial = newSkinnedMeshRenderer.sharedMaterial; target.sharedMaterial = newSkinnedMeshRenderer.sharedMaterial;
target.bones = newSkinnedMeshRenderer.bones; target.bones = newSkinnedMeshRenderer.bones;
foreach (var go in tmp) foreach (var go in tmp)
{ {
GameObject.DestroyImmediate(go); GameObject.DestroyImmediate(go);
} }
// Set a name to make it more clear. // Set a name to make it more clear.
target.sharedMesh.name = target.transform.name.Replace("(Clone)", ""); target.sharedMesh.name = target.transform.name.Replace("(Clone)", "");
return target; return target;
} }
public static SkinnedMeshRenderer Combine(this SkinnedMeshRenderer target, List<SkinnedMeshRenderer> skinnedMeshes, List<(MeshFilter mf, MeshRenderer mr)> meshes, Material mainMaterial) 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<Transform> bones = new List<Transform>();
List<BoneWeight> boneWeights = new List<BoneWeight>(); List<BoneWeight> boneWeights = new List<BoneWeight>();
List<Matrix4x4> bindPoses = new List<Matrix4x4>(); List<Matrix4x4> bindPoses = new List<Matrix4x4>();
List<CombineInstance> combineInstances = new List<CombineInstance>(); List<CombineInstance> combineInstances = new List<CombineInstance>();
// Combine SkinnedMeshes. // Combine SkinnedMeshes.
int boneOffset = 0; int boneOffset = 0;
for (int s = 0; s < skinnedMeshes.Count; s++) for (int s = 0; s < skinnedMeshes.Count; s++)
{ {
SkinnedMeshRenderer smr = skinnedMeshes[s]; SkinnedMeshRenderer smr = skinnedMeshes[s];
//if the skinned mesh renderer has a material other than the default //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 //we assume it's a one-off face material and deal with it later
if (smr.sharedMaterial != mainMaterial) if (smr.sharedMaterial != mainMaterial)
{ {
continue; 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. // 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. // We don't care since it is going to be converted into vertex animations later anyways.
for (int i = 0; i < meshBoneweight.Length; ++i) for (int i = 0; i < meshBoneweight.Length; ++i)
{ {
BoneWeight bWeight = meshBoneweight[i]; BoneWeight bWeight = meshBoneweight[i];
bWeight.boneIndex0 += boneOffset; bWeight.boneIndex0 += boneOffset;
bWeight.boneIndex1 += boneOffset; bWeight.boneIndex1 += boneOffset;
bWeight.boneIndex2 += boneOffset; bWeight.boneIndex2 += boneOffset;
bWeight.boneIndex3 += boneOffset; bWeight.boneIndex3 += boneOffset;
boneWeights.Add(bWeight); boneWeights.Add(bWeight);
} }
boneOffset += smr.bones.Length; boneOffset += smr.bones.Length;
Transform[] meshBones = smr.bones; Transform[] meshBones = smr.bones;
for (int i = 0; i < meshBones.Length; ++i) for (int i = 0; i < meshBones.Length; ++i)
{ {
bones.Add(meshBones[i]); bones.Add(meshBones[i]);
//we take the old bind pose that mapped from our mesh to world to bone, //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 //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 //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); bindPoses.Add(smr.sharedMesh.bindposes[i] * smr.transform.worldToLocalMatrix);
} }
CombineInstance ci = new CombineInstance CombineInstance ci = new CombineInstance
{ {
mesh = smr.sharedMesh, mesh = smr.sharedMesh,
transform = smr.transform.localToWorldMatrix transform = smr.transform.localToWorldMatrix
}; };
combineInstances.Add(ci); combineInstances.Add(ci);
GameObject.DestroyImmediate(smr); GameObject.DestroyImmediate(smr);
} }
// Combine Meshes. // Combine Meshes.
for (int s = 0; meshes != null && s < meshes.Count; s++) for (int s = 0; meshes != null && s < meshes.Count; s++)
{ {
MeshFilter filter = meshes[s].mf; MeshFilter filter = meshes[s].mf;
MeshRenderer renderer = meshes[s].mr; MeshRenderer renderer = meshes[s].mr;
//if the skinned mesh renderer has a material other than the default //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 //we assume it's a one-off face material and deal with it later
if (renderer.sharedMaterial != mainMaterial) if (renderer.sharedMaterial != mainMaterial)
{ {
continue; continue;
} }
// May want to modify this if the renderer shares bones as unnecessary bones will get added. // 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. // We don't care since it is going to be converted into vertex animations later anyways.
int vertCount = filter.sharedMesh.vertexCount; int vertCount = filter.sharedMesh.vertexCount;
for (int i = 0; i < vertCount; ++i) for (int i = 0; i < vertCount; ++i)
{ {
BoneWeight bWeight = new BoneWeight BoneWeight bWeight = new BoneWeight
{ {
boneIndex0 = boneOffset, boneIndex0 = boneOffset,
boneIndex1 = boneOffset, boneIndex1 = boneOffset,
boneIndex2 = boneOffset, boneIndex2 = boneOffset,
boneIndex3 = boneOffset, boneIndex3 = boneOffset,
weight0 = 1 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. // TODO: figure out what this should be.
bindPoses.Add(filter.transform.worldToLocalMatrix); bindPoses.Add(filter.transform.worldToLocalMatrix);
CombineInstance ci = new CombineInstance CombineInstance ci = new CombineInstance
{ {
mesh = filter.sharedMesh, mesh = filter.sharedMesh,
transform = filter.transform.localToWorldMatrix transform = filter.transform.localToWorldMatrix
}; };
combineInstances.Add(ci); combineInstances.Add(ci);
GameObject.DestroyImmediate(filter); GameObject.DestroyImmediate(filter);
GameObject.DestroyImmediate(renderer); GameObject.DestroyImmediate(renderer);
} }
// Actually combine and recalculate mesh. // Actually combine and recalculate mesh.
Mesh skinnedMesh = new Mesh(); Mesh skinnedMesh = new Mesh();
skinnedMesh.CombineMeshes(combineInstances.ToArray(), true, true); skinnedMesh.CombineMeshes(combineInstances.ToArray(), true, true);
skinnedMesh.RecalculateBounds(); skinnedMesh.RecalculateBounds();
// Copy settings to target. // Copy settings to target.
target.sharedMesh = skinnedMesh; target.sharedMesh = skinnedMesh;
target.sharedMaterial = mainMaterial; target.sharedMaterial = mainMaterial;
target.bones = bones.ToArray(); target.bones = bones.ToArray();
target.sharedMesh.boneWeights = boneWeights.ToArray(); target.sharedMesh.boneWeights = boneWeights.ToArray();
target.sharedMesh.bindposes = bindPoses.ToArray(); target.sharedMesh.bindposes = bindPoses.ToArray();
return target; return target;
} }
public static void ConbineAndConvertGameObject(this GameObject gameObject) public static void ConbineAndConvertGameObject(this GameObject gameObject)
{ {
// Get Skinned Meshes. // Get Skinned Meshes.
List<SkinnedMeshRenderer> skinnedMeshes = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true).ToList(); List<SkinnedMeshRenderer> skinnedMeshes = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>(true).ToList();
// Get Meshes. // Get Meshes.
List<(MeshFilter, MeshRenderer)> meshes = new List<(MeshFilter, MeshRenderer)>(); List<(MeshFilter, MeshRenderer)> meshes = new List<(MeshFilter, MeshRenderer)>();
foreach (var mf in gameObject.GetComponentsInChildren<MeshFilter>(true)) foreach (var mf in gameObject.GetComponentsInChildren<MeshFilter>(true))
{ {
if (mf.TryGetComponent(out MeshRenderer mr)) if (mf.TryGetComponent(out MeshRenderer mr))
{ {
meshes.Add((mf, mr)); meshes.Add((mf, mr));
} }
} }
// Add target mesh. // Add target mesh.
SkinnedMeshRenderer target = gameObject.AddComponent<SkinnedMeshRenderer>(); SkinnedMeshRenderer target = gameObject.AddComponent<SkinnedMeshRenderer>();
target.Combine(skinnedMeshes, meshes); 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 namespace TAO.VertexAnimation
{ {
[CreateAssetMenu(fileName = "new AnimationBook", menuName = "VA_Animation/AnimationBook", order = 400)] [CreateAssetMenu(fileName = "new AnimationBook", menuName = "VA_Animation/AnimationBook", order = 400)]
public class VA_AnimationBook : ScriptableObject public class VA_AnimationBook : ScriptableObject
{ {
public PlayData playData = null; public PlayData playData = null;
#if UNITY_EDITOR #if UNITY_EDITOR
public EditorData editorData = new EditorData(); public EditorData editorData = new EditorData();
#endif #endif
private void OnValidate() private void OnValidate()
{ {
// TODO: Check for naming conflicts and textures. // TODO: Check for naming conflicts and textures.
// TODO: Debug message box instead of debug logs. // TODO: Debug message box instead of debug logs.
} }
public void SetMaterials() public void SetMaterials()
{ {
if (playData.materials != null) if (playData.materials != null)
{ {
foreach (Material mat in playData.materials) foreach (Material mat in playData.materials)
{ {
if (mat != null) if (mat != null)
{ {
if (mat.HasProperty("_MaxFrames")) if (mat.HasProperty("_MaxFrames"))
{ {
mat.SetFloat("_MaxFrames", playData.maxFrames); mat.SetFloat("_MaxFrames", playData.maxFrames);
} }
for (int i = 0; i < playData.texture2DArray.Count; i++) for (int i = 0; i < playData.texture2DArray.Count; i++)
{ {
if (mat.HasProperty(playData.textureGroups[i].shaderParamName)) if (mat.HasProperty(playData.textureGroups[i].shaderParamName))
{ {
mat.SetTexture(playData.textureGroups[i].shaderParamName, playData.texture2DArray[i]); mat.SetTexture(playData.textureGroups[i].shaderParamName, playData.texture2DArray[i]);
} }
} }
} }
} }
} }
} }
#region PlayData #region PlayData
[System.Serializable] [System.Serializable]
public class PlayData public class PlayData
{ {
public List<PlayTextureGroup> textureGroups = new List<PlayTextureGroup>(); public List<PlayTextureGroup> textureGroups = new List<PlayTextureGroup>();
public List<PlayAnimationPage> animationPages = new List<PlayAnimationPage>(); public List<PlayAnimationPage> animationPages = new List<PlayAnimationPage>();
public int maxFrames; public int fps;
public Material[] materials; public int maxFrames;
public List<Texture2DArray> texture2DArray = new List<Texture2DArray>(); public Material[] materials;
public List<Texture2DArray> texture2DArray = new List<Texture2DArray>();
// NOTE: for some reason FixedString32 data gets lost when entering play mode. // NOTE: for some reason FixedString32 data gets lost when entering play mode.
// That is why this is here... and also the animationPages... // That is why this is here... and also the animationPages...
public List<VA_AnimationData> GetAnimations public List<VA_AnimationData> GetAnimations
{ {
get get
{ {
List<VA_AnimationData> animations = new List<VA_AnimationData>(); List<VA_AnimationData> animations = new List<VA_AnimationData>();
foreach (var ap in animationPages) foreach (var ap in animationPages)
{ {
animations.Add(new VA_AnimationData animations.Add(new VA_AnimationData
{ {
name = ap.name, name = ap.name,
frames = ap.frames, frames = ap.frames,
maxFrames = maxFrames, maxFrames = maxFrames,
frameTime = 1.0f / maxFrames, frameTime = 1.0f / maxFrames * fps,
duration = 1.0f / maxFrames * ap.frames, // TODO: Frames -1 ?????
animationMapIndex = GetFirstAnimationMapIndex(in ap.textures, in textureGroups), duration = 1.0f / maxFrames * (ap.frames - 1),
colorMapIndex = GetFirstColorMapIndex(in ap.textures, in textureGroups) animationMapIndex = GetFirstAnimationMapIndex(in ap.textures, in textureGroups),
}); colorMapIndex = GetFirstColorMapIndex(in ap.textures, in textureGroups)
} });
return animations; }
} return animations;
} }
}
public static int GetFirstAnimationMapIndex(in List<PlayTextureEntry> textures, in List<PlayTextureGroup> textureGroups) public static int GetFirstAnimationMapIndex(in List<PlayTextureEntry> textures, in List<PlayTextureGroup> textureGroups)
{ {
for (int i = 0; i < textureGroups.Count; i++) for (int i = 0; i < textureGroups.Count; i++)
{ {
if (textureGroups[i].textureType == TextureType.AnimationMap) if (textureGroups[i].textureType == TextureType.AnimationMap)
{ {
return textures[i].textureArrayIndex; return textures[i].textureArrayIndex;
} }
} }
return -1; return -1;
} }
public static int GetFirstColorMapIndex(in List<PlayTextureEntry> textures, in List<PlayTextureGroup> textureGroups) public static int GetFirstColorMapIndex(in List<PlayTextureEntry> textures, in List<PlayTextureGroup> textureGroups)
{ {
for (int i = 0; i < textureGroups.Count; i++) for (int i = 0; i < textureGroups.Count; i++)
{ {
if (textureGroups[i].textureType == TextureType.ColorMap) if (textureGroups[i].textureType == TextureType.ColorMap)
{ {
return textures[i].textureArrayIndex; return textures[i].textureArrayIndex;
} }
} }
return -1; return -1;
} }
} }
[System.Serializable] [System.Serializable]
public struct PlayAnimationPage public struct PlayAnimationPage
{ {
public string name; public string name;
public int frames; public int frames;
public List<PlayTextureEntry> textures; public List<PlayTextureEntry> textures;
} }
[System.Serializable] [System.Serializable]
public struct PlayTextureGroup public struct PlayTextureGroup
{ {
public string shaderParamName; public string shaderParamName;
public TextureType textureType; public TextureType textureType;
} }
[System.Serializable] [System.Serializable]
public struct PlayTextureEntry public struct PlayTextureEntry
{ {
public int textureArrayIndex; public int textureArrayIndex;
} }
#endregion #endregion
#region EditorData #region EditorData
#if UNITY_EDITOR #if UNITY_EDITOR
[System.Serializable] [System.Serializable]
public class EditorData 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<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 List<EditorAnimationPage> animationPages = new List<EditorAnimationPage>();
public int maxFrames; public int fps;
public Material[] materials; public int maxFrames;
public List<Texture2DArray> texture2DArray = null; public Material[] materials;
} public List<Texture2DArray> texture2DArray = null;
}
[System.Serializable] [System.Serializable]
public struct EditorAnimationPage public struct EditorAnimationPage
{ {
public string name; public string name;
public int frames; public int frames;
public List<EditorTextureEntry> textures; public List<EditorTextureEntry> textures;
} }
[System.Serializable] [System.Serializable]
public struct EditorTextureGroup public struct EditorTextureGroup
{ {
public string shaderParamName; public string shaderParamName;
public TextureType textureType; public TextureType textureType;
public TextureWrapMode wrapMode; public TextureWrapMode wrapMode;
public FilterMode filterMode; public FilterMode filterMode;
public bool isLinear; public bool isLinear;
} }
[System.Serializable] [System.Serializable]
public class EditorTextureEntry public class EditorTextureEntry
{ {
public Texture2D texture2D = null; public Texture2D texture2D = null;
public int textureArrayIndex = -1; public int textureArrayIndex = -1;
} }
#endif #endif
#endregion #endregion
public enum TextureType public enum TextureType
{ {
AnimationMap, AnimationMap,
ColorMap ColorMap
} }
} }
} }

View File

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

View File

@ -12,7 +12,7 @@ namespace TAO.VertexAnimation
public int frames; public int frames;
// The maximum of frames the texture holds. // The maximum of frames the texture holds.
public int maxFrames; public int maxFrames;
// 1.0f / maxFrames. // 1.0f / fps.
public float frameTime; public float frameTime;
// FrameTime * frames. // FrameTime * frames.
public float duration; public float duration;
@ -27,27 +27,27 @@ namespace TAO.VertexAnimation
public BlobArray<VA_AnimationData> animations; public BlobArray<VA_AnimationData> animations;
} }
public static class VA_AnimationLibraryUtils public static class VA_AnimationLibraryUtils
{ {
public const string AnimationLibraryAssetStoreName = "VA_AnimationLibrary"; public const string AnimationLibraryAssetStoreName = "VA_AnimationLibrary";
public static int GetAnimation(ref VA_AnimationLibraryData animationsRef, FixedString32 animationName) 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)
{ {
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) public static int GetColorMapIndex(ref VA_AnimationLibraryData animationsRef, int animation)
{ {

View File

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

View File

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