From df71068c0d11b60ebd60e0c2f76d0b323a43d6e6 Mon Sep 17 00:00:00 2001 From: hybridherbst Date: Mon, 16 Oct 2023 10:55:04 +0200 Subject: [PATCH] add callback to GLTFRecorder with calculated translation bounds commit d6f58fc75bfb24acba24924098af6bab6509269b Author: pfcDorn <> Date: Mon Oct 16 09:33:32 2023 +0200 removed animation bound marker in gltfrecorder, added postexport callback commit 1e80426e1c60bbae74405c0ceed49a8a7bc364cc Author: pfcDorn <> Date: Wed Sep 27 11:46:33 2023 +0200 add callback for animation data editing, remove trim function commit f846fe384cad72ff2e7d87d8851e08653c813c10 Author: pfcDorn <> Date: Wed Sep 20 13:40:32 2023 +0200 changed to support multible time saving a recording commit 2a27665b777ba10c92303a5fb1933ebcf4989202 Author: pfcDorn <> Date: Wed Sep 20 12:04:19 2023 +0200 changed recording animation cutting commit 91b8b50e648aec7e1b894235eff61ee148c4f81a Author: pfcDorn <> Date: Tue Sep 19 15:32:30 2023 +0200 Added recording cut functionality --- .../SceneExporter/ExporterAnimation.cs | 2 +- Runtime/Scripts/Timeline/GLTFRecorder.cs | 138 ++++++++++-------- 2 files changed, 75 insertions(+), 65 deletions(-) diff --git a/Runtime/Scripts/SceneExporter/ExporterAnimation.cs b/Runtime/Scripts/SceneExporter/ExporterAnimation.cs index 1098da95e..67a4d5797 100644 --- a/Runtime/Scripts/SceneExporter/ExporterAnimation.cs +++ b/Runtime/Scripts/SceneExporter/ExporterAnimation.cs @@ -1499,7 +1499,7 @@ private bool ArrayRangeEquals(object[] array, int sectionLength, int lastExporte public void RemoveUnneededKeyframes(ref float[] times, ref object[] values) { - if (times.Length == 1) + if (times.Length <= 1) return; removeAnimationUnneededKeyframesMarker.Begin(); diff --git a/Runtime/Scripts/Timeline/GLTFRecorder.cs b/Runtime/Scripts/Timeline/GLTFRecorder.cs index ee845153b..13f098b9b 100644 --- a/Runtime/Scripts/Timeline/GLTFRecorder.cs +++ b/Runtime/Scripts/Timeline/GLTFRecorder.cs @@ -14,7 +14,7 @@ namespace UnityGLTF.Timeline { public class GLTFRecorder { - public GLTFRecorder(Transform root, bool recordBlendShapes = true, bool recordRootInWorldSpace = false, bool recordAnimationPointer = false, bool addBoundsMarkerNodes = false) + public GLTFRecorder(Transform root, bool recordBlendShapes = true, bool recordRootInWorldSpace = false, bool recordAnimationPointer = false) { if (!root) throw new ArgumentNullException(nameof(root), "Please provide a root transform to record."); @@ -23,7 +23,6 @@ public GLTFRecorder(Transform root, bool recordBlendShapes = true, bool recordRo this.recordBlendShapes = recordBlendShapes; this.recordRootInWorldSpace = recordRootInWorldSpace; this.recordAnimationPointer = recordAnimationPointer; - this.addBoundsMarkerNodes = addBoundsMarkerNodes; } /// @@ -36,20 +35,71 @@ public GLTFRecorder(Transform root, bool recordBlendShapes = true, bool recordRo private Dictionary data = new Dictionary(64); private double startTime; private double lastRecordedTime; + private bool hasRecording; private bool isRecording; - + private readonly bool recordBlendShapes; private readonly bool recordRootInWorldSpace; private readonly bool recordAnimationPointer; - private readonly bool addBoundsMarkerNodes; + public bool HasRecording => hasRecording; public bool IsRecording => isRecording; + public double LastRecordedTime => lastRecordedTime; + public string AnimationName = "Recording"; + public delegate void OnBeforeAddAnimationDataDelegate(AnimationDataArgs animationData); + public delegate void OnPostExportDelegate(PostExportArgs animationData); + + /// + /// Callback to modify the animation data before it is added to the animation. + /// Is called once for each track after the recording has ended. + /// + public OnBeforeAddAnimationDataDelegate OnBeforeAddAnimationData; + + /// + /// Callback to modify or add additional data to the gltf root after the recording has ended and animation + /// data is added to the animation. + /// + public OnPostExportDelegate OnPostExport; + + + public class PostExportArgs + { + public Bounds AnimationTranslationBounds { get; private set; } + public GLTFSceneExporter Exporter { get; private set; } + public GLTFRoot GltfRoot { get; private set; } + + internal PostExportArgs(Bounds animationTranslationBounds, GLTFSceneExporter exporter, GLTFRoot gltfRoot) + { + this.AnimationTranslationBounds = animationTranslationBounds; + this.Exporter = exporter; + this.GltfRoot = gltfRoot; + } + } + + public class AnimationDataArgs + { + public Object AnimatedObject => plan.GetTarget(tr.tr); + public string PropertyName => plan.propertyName; + + private AnimationData tr; + private AnimationData.ExportPlan plan; + + public readonly Dictionary Samples; + + internal AnimationDataArgs( AnimationData.ExportPlan plan, AnimationData tr, Dictionary samples) + { + this.plan = plan; + this.tr = tr; + this.Samples = samples; + } + } + internal class AnimationData { - private Transform tr; + internal Transform tr; private SkinnedMeshRenderer smr; private bool recordBlendShapes; private bool inWorldSpace = false; @@ -180,6 +230,11 @@ internal class Track private Dictionary samples; private object lastSample; + internal AnimationDataArgs GetAnimationDataArgs() + { + return new AnimationDataArgs(plan, tr, samples); + } + public Track(AnimationData tr, ExportPlan plan, double time) { this.tr = tr; @@ -199,6 +254,7 @@ public void SampleIfChanged(double time) lastSample = value; } } + } public void Update(double time) @@ -224,6 +280,7 @@ public void StartRecording(double time) } isRecording = true; + hasRecording = true; } public void UpdateRecording(double time) @@ -258,19 +315,17 @@ public void UpdateRecording(double time) lastRecordedTime = time; } - + internal void EndRecording(out Dictionary param) { param = null; - if (!isRecording) return; - - isRecording = false; + if (!hasRecording) return; param = data; } public void EndRecording(string filename, string sceneName = "scene", GLTFSettings settings = null) { - if (!isRecording) return; + if (!hasRecording) return; var dir = Path.GetDirectoryName(filename); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir); @@ -282,9 +337,8 @@ public void EndRecording(string filename, string sceneName = "scene", GLTFSettin public void EndRecording(Stream stream, string sceneName = "scene", GLTFSettings settings = null) { - if (!isRecording) return; + if (!hasRecording) return; isRecording = false; - Debug.Log("Gltf Recording saved. Tracks: " + data.Count + ", Total Keyframes: " + data.Sum(x => x.Value.tracks.Sum(y => y.values.Count()))); if (!settings) @@ -319,59 +373,12 @@ private void PostExport(GLTFSceneExporter exporter, GLTFRoot gltfRoot) GLTFAnimation anim = new GLTFAnimation(); anim.Name = AnimationName; - CollectAndProcessAnimation(exporter, anim, addBoundsMarkerNodes, out Bounds translationBounds); - - if (addBoundsMarkerNodes) - { - Debug.Log("Animation bounds: " + translationBounds.center + " => " + translationBounds.size); - - // create Cube primitive - var cube = GameObject.CreatePrimitive(PrimitiveType.Quad); - cube.transform.localScale = Vector3.one * 0.0001f; - cube.hideFlags = HideFlags.HideAndDontSave; - - var collider = cube.GetComponent(); - SafeDestroy(collider); - - var boundsRoot = new GameObject("AnimationBounds"); - boundsRoot.hideFlags = HideFlags.HideAndDontSave; - boundsRoot.transform.position = translationBounds.center; - - var extremePointsOnBounds = new Vector3[6]; - extremePointsOnBounds[0] = translationBounds.center + new Vector3(+translationBounds.extents.x, 0, 0); - extremePointsOnBounds[1] = translationBounds.center + new Vector3(-translationBounds.extents.x, 0, 0); - extremePointsOnBounds[2] = translationBounds.center + new Vector3(0, +translationBounds.extents.y, 0); - extremePointsOnBounds[3] = translationBounds.center + new Vector3(0, -translationBounds.extents.y, 0); - extremePointsOnBounds[4] = translationBounds.center + new Vector3(0, 0, +translationBounds.extents.z); - extremePointsOnBounds[5] = translationBounds.center + new Vector3(0, 0, -translationBounds.extents.z); - - int index = 0; - foreach (var point in extremePointsOnBounds) - { - var cubeInstance = Object.Instantiate(cube); - cubeInstance.name = $"AnimationBounds {index.ToString()}"; - cubeInstance.transform.position = point; - cubeInstance.transform.parent = boundsRoot.transform; - index++; - } - - // export and add explicitly to the scene list, otherwise these nodes at the root level will be ignored - var nodeId = exporter.ExportNode(boundsRoot); - gltfRoot.Scenes[0].Nodes.Add(nodeId); - - // move skinned meshes to the center of the bounds – - // they will be moved by their bones anyways, but this prevents wrong bounds calculations from them. - foreach (var skinnedMeshRenderer in root.GetComponentsInChildren()) - skinnedMeshRenderer.transform.position = translationBounds.center; - - SafeDestroy(boundsRoot); - SafeDestroy(cube); - } - - // check if the root node is outside these bounds, move it to the center if necessary + CollectAndProcessAnimation(exporter, anim, true, out Bounds translationBounds); if (anim.Channels.Count > 0 && anim.Samplers.Count > 0) gltfRoot.Animations.Add(anim); + + OnPostExport?.Invoke( new PostExportArgs(translationBounds, exporter, gltfRoot)); } private static void SafeDestroy(Object obj) @@ -397,8 +404,11 @@ private void CollectAndProcessAnimation(GLTFSceneExporter gltfSceneExporter, GLT foreach (var tr in kvp.Value.tracks) { if (tr.times.Length == 0) continue; - var times = tr.times; - var values = tr.values; + + OnBeforeAddAnimationData?.Invoke(tr.GetAnimationDataArgs()); + + float[] times = tr.times; + object[] values = tr.values; if (calculateTranslationBounds && tr.propertyName == "translation") {