ThrowBall/Assets/Plugins/RayFire/Scripts/Classes/RFRecord.cs

331 lines
13 KiB
C#

#if UNITY_EDITOR
using System.IO;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
namespace RayFire
{
public static class RFRecorder
{
// Create animation clip
public static void CreateAnimationClip(List<RFCache> cacheList, List<float> timeList, float threshold, int rate, string assetFolder, string clipName, bool optimizeKeys)
{
// Stop
if (timeList.Count == 0)
return;
// Create main clip
AnimationClip clip = new AnimationClip();
clip.legacy = false;
clip.frameRate = rate;
clip.name = clipName + "_animation";
// Create curves for each object
foreach (RFCache cache in cacheList)
{
if (cache.trm == null)
continue;
// Position
SetCurvePosition (ref clip, cache.pos, timeList, 0, cache.name, "localPosition.x", threshold, rate, optimizeKeys);
SetCurvePosition (ref clip, cache.pos, timeList, 1, cache.name, "localPosition.y", threshold, rate, optimizeKeys);
SetCurvePosition (ref clip, cache.pos, timeList, 2, cache.name, "localPosition.z", threshold, rate, optimizeKeys);
// Rotation
SetCurveRotation (ref clip, cache.rot, timeList, 0, cache.name, "localRotation.x", threshold, rate, optimizeKeys);
SetCurveRotation (ref clip, cache.rot, timeList, 1, cache.name, "localRotation.y", threshold, rate, optimizeKeys);
SetCurveRotation (ref clip, cache.rot, timeList, 2, cache.name, "localRotation.z", threshold, rate, optimizeKeys);
SetCurveRotation (ref clip, cache.rot, timeList, 3, cache.name, "localRotation.w", threshold, rate, optimizeKeys);
// Active
SetCurveActive (ref clip, cache.act, timeList, cache.name, "m_IsActive", threshold, rate, optimizeKeys);
}
// Set Folder
if (Directory.Exists (assetFolder) == false)
Directory.CreateDirectory(assetFolder);
// Save clip asset
string clipPath = assetFolder + clipName + "_animation.anim";
AssetDatabase.CreateAsset(clip, clipPath);
// Save controller
string controllerPath = assetFolder + clipName + "_controller.controller";
AnimatorController cont = AnimatorController.CreateAnimatorControllerAtPath (controllerPath);
AnimatorStateMachine stateMachine = cont.layers[0].stateMachine;
// Set clip and states
SetStates (stateMachine, clip);
// Asset ops
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
// Set clip and states
static int SetStates (AnimatorStateMachine stateMachine, AnimationClip clip)
{
// Empty entry state
AnimatorState emptyState = stateMachine.AddState ("EmptyState");
emptyState.speed = 0;
emptyState.writeDefaultValues = false;
// Animation state
AnimatorState recordState = stateMachine.AddState (clip.name);
recordState.motion = clip;
recordState.tag = clip.name + "Tag";
// Exit transition
AnimatorStateTransition exitTransition = recordState.AddExitTransition();
exitTransition.duration = 0;
exitTransition.hasExitTime = true;
return recordState.nameHash;
}
/// //////////////////////////////////////////////////
/// Demolition exports
/// //////////////////////////////////////////////////
// Save demolished rigid fragments
public static void ExportAssets(RayfireRigid rigid, RayfireRecorder recorder)
{
// Get fragments meshfilters
List<MeshFilter> meshFilters = new List<MeshFilter>();
for (int i = 0; i < rigid.fragments.Count; i++)
meshFilters.Add (rigid.fragments[i].mFlt);
// Instantiate mesh in order to export it into asset
foreach (var mf in meshFilters)
{
Mesh tempMesh = Object.Instantiate(mf.sharedMesh);
tempMesh.name = mf.name;
mf.sharedMesh = tempMesh;
}
// Export meshes into asset
ExportMeshes (meshFilters, rigid.rtC.name, recorder.clipName);
// Export prefabs
ExportPrefabs (rigid, recorder);
}
// Export meshes into asset
static void ExportMeshes(List<MeshFilter> meshFilters, string rootName, string clipName)
{
// Create meshes folder
string folderMeshes = "Assets/RayFireRecords/" + clipName + "_meshes/";
if (Directory.Exists(folderMeshes) == false)
Directory.CreateDirectory (folderMeshes);
// Get save path
string savePath = folderMeshes + rootName + ".asset";
// Create asset
AssetDatabase.CreateAsset(new Mesh(), savePath);
AssetDatabase.AddObjectToAsset (new Mesh(), savePath);
// Save each fragment mesh
for (int i = 0; i < meshFilters.Count; i++)
AssetDatabase.AddObjectToAsset (meshFilters[i].sharedMesh, savePath);
// Save
AssetDatabase.SaveAssets();
}
// Export prefabs
static void ExportPrefabs(RayfireRigid rigid, RayfireRecorder recorder)
{
// Create prefab folder
string folderPrefab = "Assets/RayFireRecords/" + recorder.clipName + "_prefabs/";
if (Directory.Exists(folderPrefab) == false)
Directory.CreateDirectory (folderPrefab);
// Create prefab
string filePath = folderPrefab + rigid.rtC.name + ".prefab";
GameObject prefab = PrefabUtility.SaveAsPrefabAsset (rigid.rtC.gameObject, filePath);
// Collect prefabs
recorder.pfList.Add (prefab);
// https://discussions.unity.com/t/editor-scripting-how-to-save-a-script-generated-mesh-as-an-asset-fbx/12050/2
// https://forum.unity.com/threads/saving-modified-mesh.404212/
}
// Destroy prefab components
public static void DestroyPrefabComponents(List<GameObject> pfList)
{
if (pfList.Count == 0)
return;
foreach (var pf in pfList)
{
Rigidbody[] rbs = pf.GetComponentsInChildren<Rigidbody>();
for (int i = rbs.Length - 1; i >= 0; i--)
Object.DestroyImmediate(rbs[i], true);
RayfireRigid[] rgs = pf.GetComponentsInChildren<RayfireRigid>();
for (int i = rgs.Length - 1; i >= 0; i--)
Object.DestroyImmediate(rgs[i], true);
Collider[] cls = pf.GetComponentsInChildren<Collider>();
for (int i = cls.Length - 1; i >= 0; i--)
Object.DestroyImmediate(cls[i], true);
EditorUtility.SetDirty(pf);
PrefabUtility.SavePrefabAsset(pf);
}
}
/// //////////////////////////////////////////////////
/// Create curves
/// //////////////////////////////////////////////////
// Set curve to clop
static void SetCurvePosition (ref AnimationClip clip, List<Vector3> posList, List<float> timeList, int ind, string nameVar, string track, float threshold, int rate, bool optimizeKeys)
{
// Create keys
Keyframe[] keys = new Keyframe[timeList.Count];
for (int i = 0; i < timeList.Count; i++)
keys[i] = new Keyframe(timeList[i], posList[i][ind], 0f, 0f, 0f, 0f);
// Optimize
if (optimizeKeys == true)
keys = OptimizeKeys(keys, threshold, rate);
// All keys was reduced
if (keys.Length < 2)
return;
// Set keys to curve
AnimationCurve curve = new AnimationCurve(keys);
// Set key type
for (int i = 0; i < curve.keys.Length; i++)
{
AnimationUtility.SetKeyLeftTangentMode (curve, i, AnimationUtility.TangentMode.Linear);
AnimationUtility.SetKeyRightTangentMode (curve, i, AnimationUtility.TangentMode.Linear);
}
// Set curve to track
clip.SetCurve(nameVar, typeof(Transform), track, curve);
}
// Set curve to clop
static void SetCurveRotation (ref AnimationClip clip, List<Quaternion> rotList, List<float> timeList, int ind, string nameVar, string track, float threshold, int rate, bool optimizeKeys)
{
// Create keys
Keyframe[] keys = new Keyframe[timeList.Count];
for (int i = 0; i < timeList.Count; i++)
keys[i] = new Keyframe (timeList[i], rotList[i][ind], 0f, 0f, 0f, 0f);
// Optimize
if (optimizeKeys == true)
keys = OptimizeKeys(keys, threshold, rate);
// All keys was reduced
if (keys.Length < 2)
return;
// Set keys to curve
AnimationCurve curve = new AnimationCurve(keys);
// Set key type
for (int i = 0; i < curve.keys.Length; i++)
{
AnimationUtility.SetKeyLeftTangentMode (curve, i, AnimationUtility.TangentMode.Linear);
AnimationUtility.SetKeyRightTangentMode (curve, i, AnimationUtility.TangentMode.Linear);
}
// Set curve to track
clip.SetCurve(nameVar, typeof(Transform), track, curve);
}
// Set curve to clop
static void SetCurveActive(ref AnimationClip clip, List<bool> actList, List<float> timeList, string nameVar, string track, float threshold, int rate, bool optimizeKeys)
{
// Skip if always active
if (actList.Contains (false) == false)
return;
// Create keys
Keyframe[] keys = new Keyframe[timeList.Count];
for (int i = 0; i < timeList.Count; i++)
keys[i] = new Keyframe (timeList[i], (actList[i] == true ? 1f : 0f));
// Optimize
if (optimizeKeys == true)
keys = OptimizeKeys(keys, threshold, rate);
// All keys was reduced
if (keys.Length < 2)
return;
// Set keys to curve
AnimationCurve curve = new AnimationCurve(keys);
// Set key type
for (int i = 0; i < curve.keys.Length; i++)
{
AnimationUtility.SetKeyLeftTangentMode (curve, i, AnimationUtility.TangentMode.Linear);
AnimationUtility.SetKeyRightTangentMode (curve, i, AnimationUtility.TangentMode.Linear);
}
// Set curve to track
clip.SetCurve(nameVar, typeof(GameObject), track, curve);
}
// Optimize keys
static Keyframe[] OptimizeKeys(Keyframe[] keys, float threshold, int rate)
{
if (keys.Length <= 1)
return keys;
// Remove same keys
List<int> removeInd = new List<int>();
List<Keyframe> list = keys.ToList();
// Collect indexes of all same keys between it's neibs
for (int i = list.Count - 2; i > 1; i--)
if (list[i].value - list[i - 1].value == 0 && list[i].value - list[i + 1].value == 0)
removeInd.Add (i);
// Remove same keys
for (int i = 0; i < removeInd.Count; i++)
list.RemoveAt (removeInd[i]);
if (list.Count == 1)
{
list.Clear();
return list.ToArray();
}
// Remove by threshold
if (threshold > 0 && list.Count > 6)
{
removeInd.Clear();
float val = threshold / rate;
for (int i = list.Count - 3; i > 3; i--)
if (Mathf.Abs (list[i].value - list[i - 1].value) > val && Mathf.Abs (list[i].value - list[i + 1].value) > val)
removeInd.Add (i);
// Remove same keys
for (int i = 0; i < removeInd.Count; i++)
list.RemoveAt (removeInd[i]);
if (list.Count == 1)
{
list.Clear();
return list.ToArray();
}
}
return list.ToArray();
}
}
}
#endif