ThrowBall/Assets/Plugins/RayFire/Scripts/Components/RayfireRecorder.cs

442 lines
14 KiB
C#
Raw Normal View History

2024-12-01 20:53:59 +08:00
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace RayFire
{
/// <summary>
/// Rayfire Recorder animation cache class.
/// </summary>
[Serializable]
public class RFCache
{
// Vars
public string name;
public List<bool> act;
public List<Vector3> pos;
public List<Quaternion> rot;
public Transform trm;
// Constructor
public RFCache (Transform recRootTm, Transform tm, int offset = 0)
{
trm = tm;
name = tm.name;
act = new List<bool>();
pos = new List<Vector3>();
rot = new List<Quaternion>();
// Add activation offset to lists
SetOffset (this, offset);
// Not child of main recorder root. Name should include all parents names up to main recorder root
if (tm.parent != recRootTm)
{
Transform lastParent = tm.parent;
while (recRootTm != lastParent)
{
name = name.Insert (0, "/");
name = name.Insert (0, lastParent.name);
lastParent = lastParent.parent;
}
}
}
// Set activation offset
static void SetOffset(RFCache cache, int offset)
{
if (offset <= 0)
return;
for (int i = 0; i < offset; i++)
{
cache.act.Add (false);
cache.pos.Add (cache.trm.localPosition);
cache.rot.Add (cache.trm.localRotation);
}
}
}
[SelectionBase]
[DisallowMultipleComponent]
[AddComponentMenu ("RayFire/Rayfire Recorder")]
[HelpURL ("https://rayfirestudios.com/unity-online-help/components/unity-recorder-component/")]
public class RayfireRecorder : MonoBehaviour
{
public enum AnimatorType
{
Disabled = 0,
Record = 2,
Play = 8
}
public enum RigidActionType
{
Disable = 0,
SetKinematik = 2
}
// Main
public AnimatorType mode = AnimatorType.Record;
// Record
public bool recordOnStart = true;
public string clipName;
public float duration = 5f;
public int rate = 15;
public bool reduceKeys = true;
public float threshold;
public bool demolition;
// Playback
public bool playOnStart;
public AnimationClip animationClip;
public RuntimeAnimatorController controller;
public RigidActionType rigidAction;
// Public Non Serialized
[NonSerialized] public bool recorder;
[NonSerialized] public float recordedTime;
// Private Non Serialized
[NonSerialized] public List<GameObject> pfList;
[NonSerialized] string assetFolder;
[NonSerialized] float stepTime;
[NonSerialized] Animator animator;
[NonSerialized] List<Transform> tmList;
[NonSerialized] List<RFCache> cacheList;
[NonSerialized] List<float> timeList;
[NonSerialized] List<RayfireRigid> rigids;
// Static
string recordFolder = "RayFireRecords/";
/// //////////////////////////////////////////////////
/// Common
/// //////////////////////////////////////////////////
// Awake
void Awake()
{
// Set vars
SetVariables();
}
// Start
void Start()
{
// Collect rigid
SetRigidPlay();
// Start ops
if (mode == AnimatorType.Record && recordOnStart == true)
StartRecord();
else if (mode == AnimatorType.Play && playOnStart == true)
StartPlay();
}
// Set vars
void SetVariables()
{
if (mode != AnimatorType.Disabled)
{
animator = GetComponent<Animator>();
// Get list of cached transforms
tmList = gameObject.GetComponentsInChildren<Transform> (false).ToList();
tmList.Remove (transform);
// No children
if (tmList.Count == 0)
{
Debug.Log ("RayFire Record: " + gameObject.name + " Mode set to " + mode.ToString() + " but object has no children. Mode set to None.", gameObject);
mode = AnimatorType.Disabled;
return;
}
// Record set
SetModeRecord();
// Play set
SetModePlay();
}
}
// Play set
void SetModePlay()
{
if (mode == AnimatorType.Play)
{
// Check for null controller
if (controller == null)
{
Debug.Log ("RayFire Record: " + gameObject.name + " Mode set to " + mode.ToString() + " but controller is not defined. Mode set to None.", gameObject);
mode = AnimatorType.Disabled;
return;
}
// Check for null controller
if (animationClip == null)
{
Debug.Log ("RayFire Record: " + gameObject.name + " Mode set to " + mode.ToString() + " but animation clip is not defined. Mode set to None.", gameObject);
mode = AnimatorType.Disabled;
return;
}
// Check for clip in controller
bool hasClip = false;
foreach (var anim in controller.animationClips)
if (anim == animationClip)
hasClip = true;
if (hasClip == false)
{
Debug.Log ("RayFire Record: " + gameObject.name + " Mode set to " + mode.ToString() + " but animation clip is not defined in controller. Mode set to None.", gameObject);
mode = AnimatorType.Disabled;
return;
}
// Create animator
if (animator == null)
animator = gameObject.AddComponent<Animator>();
animator.updateMode = AnimatorUpdateMode.AnimatePhysics;
// Set defined controller
animator.runtimeAnimatorController = controller;
}
}
/// //////////////////////////////////////////////////
/// Record
/// //////////////////////////////////////////////////
// Record set
void SetModeRecord()
{
if (mode == AnimatorType.Record)
{
// Null active controller
if (animator != null)
animator.runtimeAnimatorController = null;
// Prepare cache list
if (tmList.Count > 0)
{
cacheList = new List<RFCache>();
for (int i = 0; i < tmList.Count; i++)
cacheList.Add (new RFCache (transform, tmList[i]));
}
// Time list
timeList = new List<float>();
// Clip folder
assetFolder = "Assets/" + recordFolder;
// Rigid Runtime Demolition
if (demolition == true)
SetRigidRecord();
}
}
// Start record
public void StartRecord()
{
// Stop
if (cacheList.Count == 0)
return;
// Set demolition parent to parent of demolished object in order to record animation clip
RayfireMan.inst.advancedDemolitionProperties.parent = FragmentParentType.GlobalParent;
RayfireMan.inst.advancedDemolitionProperties.globalParent = transform;
// Start recording cor
StartCoroutine (RecordCor());
}
// Stop record
public void StopRecord()
{
recorder = false;
}
// Reset
void Reset()
{
clipName = gameObject.name;
}
// Record tm every frame
IEnumerator RecordCor()
{
// Set time step
stepTime = 1.0f / rate;
// Set the playback framerate. IMPORTANT: use for smooth keys recording
Time.captureDeltaTime = stepTime;
recorder = true;
while (recorder == true)
{
// Save data
timeList.Add (recordedTime);
CacheFrame();
// Set time
recordedTime += stepTime;
// Temp
if (duration > 0 && recordedTime > duration)
StopRecord();
// Wait
yield return new WaitForSeconds (stepTime);
}
#if UNITY_EDITOR
// Create clip
RFRecorder.CreateAnimationClip (cacheList, timeList, threshold, rate, assetFolder, clipName, reduceKeys);
// Destroy prefab components
RFRecorder.DestroyPrefabComponents(pfList);
#endif
}
// Cache frame data
void CacheFrame()
{
for (int i = 0; i < tmList.Count; i++)
{
if (tmList[i] == null)
continue;
cacheList[i].act.Add (tmList[i].gameObject.activeSelf);
cacheList[i].pos.Add (tmList[i].localPosition);
cacheList[i].rot.Add (tmList[i].localRotation);
}
}
/// //////////////////////////////////////////////////
/// Play
/// //////////////////////////////////////////////////
// Start play
public void StartPlay()
{
if (mode == AnimatorType.Play)
animator.Play (animationClip.name);
}
/// //////////////////////////////////////////////////
/// Rigid
/// //////////////////////////////////////////////////
// Set rigid props
void SetRigidRecord()
{
// Destroy lists
pfList = new List<GameObject>();
// Get all Rigids
rigids = gameObject.GetComponentsInChildren<RayfireRigid>().ToList();
// Setup
PrepareRigidRecord (rigids);
}
// Prepare Rigid for record
void PrepareRigidRecord(List<RayfireRigid> rigidList)
{
for (int i = 0; i < rigidList.Count; i++)
{
if (rigidList[i].dmlTp == DemolitionType.Runtime ||
rigidList[i].dmlTp == DemolitionType.AwakePrecache ||
rigidList[i].dmlTp == DemolitionType.AwakePrefragment)
{
// Used by Recorder record
rigidList[i].physics.rec = true;
// One level of demolition TEMP
// rigids[i].limitations.depth = 1;
// Do not destroy after demolition
rigidList[i].reset.action = RFReset.PostDemolitionType.DeactivateToReset;
rigidList[i].limitations.desc = new List<RayfireRigid>();
// Subscribe to demolition
#if UNITY_EDITOR
rigidList[i].demolitionEvent.LocalEvent += RigidDemolition;
#endif
}
}
}
// Set rigid props
void SetRigidPlay()
{
if (mode == AnimatorType.Play)
{
rigids = gameObject.GetComponentsInChildren<RayfireRigid>().ToList();
foreach (RayfireRigid rigid in rigids)
{
if (rigid.physics.exclude == false)
{
// Used by Recorder playback
rigid.physics.rec = true;
// Disable Rigid
if (rigidAction == RigidActionType.Disable)
{
rigid.enabled = false;
}
// Check for kinematic state
else if (rigidAction == RigidActionType.SetKinematik)
{
rigid.simTp = SimType.Kinematic;
RFPhysic.SetSimulationType (rigid.physics.rb, rigid.simTp, rigid.objTp, rigid.physics.gr, rigid.physics.si, rigid.physics.st);
}
}
}
}
}
/// //////////////////////////////////////////////////
/// Demolition event
/// //////////////////////////////////////////////////
// Runtime demolition ops
void RigidDemolition(RayfireRigid rigid)
{
// Mesh to fragments demolition
if (rigid.objTp == ObjectType.Mesh)
{
// Collect root and fragments to continue record animation
tmList.Add (rigid.rtC);
cacheList.Add (new RFCache (transform, rigid.rtC, timeList.Count));
for (int i = 0; i < rigid.fragments.Count; i++)
{
tmList.Add (rigid.fragments[i].transform);
cacheList.Add (new RFCache (transform, rigid.fragments[i].transform, timeList.Count));
}
// Prepare new Rigids for secondary demolition record
PrepareRigidRecord (rigid.fragments);
// Export fragments meshes into asset
#if UNITY_EDITOR
RFRecorder.ExportAssets (rigid, this);
#endif
}
// TODO other demolition types support
}
}
}