mirror of
https://github.com/maxartz15/VertexAnimation.git
synced 2024-12-05 01:30:44 +01:00
Model Baker Updates
... Added fps to book data. Cleanup. Preparing LOD generation.
This commit is contained in:
parent
322b50b2bd
commit
d3b4d34409
47
Editor/Scripts/Editor/AssetDatabaseUtils.cs
Normal file
47
Editor/Scripts/Editor/AssetDatabaseUtils.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Editor/Scripts/Editor/AssetDatabaseUtils.cs.meta
Normal file
11
Editor/Scripts/Editor/AssetDatabaseUtils.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d394aa43ff9889d45878e965ac0f1a30
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
52
Editor/Scripts/ModelBaker/AnimationPrefab.cs
Normal file
52
Editor/Scripts/ModelBaker/AnimationPrefab.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Editor/Scripts/ModelBaker/AnimationPrefab.cs.meta
Normal file
11
Editor/Scripts/ModelBaker/AnimationPrefab.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0b2f9ea47456fd24387722ab88de04c3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
169
Editor/Scripts/ModelBaker/VA_ModelBaker.cs
Normal file
169
Editor/Scripts/ModelBaker/VA_ModelBaker.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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"
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
18
Runtime/Scripts/ModelBaker/AnimationMaterial.cs
Normal file
18
Runtime/Scripts/ModelBaker/AnimationMaterial.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Runtime/Scripts/ModelBaker/AnimationMaterial.cs.meta
Normal file
11
Runtime/Scripts/ModelBaker/AnimationMaterial.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2c06ab1e65507bd46925ac9091097b58
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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];
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user