diff options
Diffstat (limited to 'Scripts/Fold/Editor/FoldEditorWindow.cs')
| -rwxr-xr-x | Scripts/Fold/Editor/FoldEditorWindow.cs | 266 |
1 files changed, 259 insertions, 7 deletions
diff --git a/Scripts/Fold/Editor/FoldEditorWindow.cs b/Scripts/Fold/Editor/FoldEditorWindow.cs index e8faf1f..31d47fd 100755 --- a/Scripts/Fold/Editor/FoldEditorWindow.cs +++ b/Scripts/Fold/Editor/FoldEditorWindow.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using UnityEditor; using UnityEngine; @@ -10,6 +11,11 @@ public class FoldEditorWindow : EditorWindow [SerializeReference] List<DeformOperation> operations = new(); [SerializeField] List<int> expandedOps = new(); [SerializeField] bool liveUpdate = true; + [SerializeField] GameObject targetObject; + int frameStep; + bool wasInAnimationMode; + + const string FrameStepPrefKey = "Fold_FrameStep"; [MenuItem("Tools/yum_food/Fold")] static void ShowWindow() @@ -18,8 +24,21 @@ public class FoldEditorWindow : EditorWindow window.minSize = new Vector2(400, 300); } + void OnEnable() => frameStep = EditorPrefs.GetInt(FrameStepPrefKey, 1); + + void OnDisable() + { + EditorPrefs.SetInt(FrameStepPrefKey, frameStep); + ClearPropertyBlock(); + } + void OnGUI() { + bool inAnimMode = AnimationMode.InAnimationMode(); + if (wasInAnimationMode && !inAnimMode) + ClearPropertyBlock(); + wasInAnimationMode = inAnimMode; + EditorGUILayout.Space(5); DrawHeader(); @@ -61,6 +80,11 @@ public class FoldEditorWindow : EditorWindow } EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Target Object", EditorStyles.boldLabel, GUILayout.Width(100)); + targetObject = EditorGUILayout.ObjectField(targetObject, typeof(GameObject), true) as GameObject; + EditorGUILayout.EndHorizontal(); } void DrawToolbar() @@ -182,19 +206,36 @@ public class FoldEditorWindow : EditorWindow { EditorGUILayout.BeginHorizontal(); - if (GUILayout.Button("Apply to Material", GUILayout.Height(30))) - ApplyToMaterial(); + if (GUILayout.Button("Create Keyframe", GUILayout.Height(30))) + ApplyToMaterial(recordKeyframes: true); - if (GUILayout.Button("Clear All", GUILayout.Height(30), GUILayout.Width(100))) + if (GUILayout.Button("Delete Keyframe", GUILayout.Height(30), GUILayout.Width(120))) + DeleteKeyframeAtCurrentTime(); + + if (GUILayout.Button("Clear All", GUILayout.Height(30), GUILayout.Width(80))) { - operations.Clear(); - expandedOps.Clear(); + if (EditorUtility.DisplayDialog("Clear All Operations", + "Remove all operations from the pipeline?", "Clear", "Cancel")) + { + operations.Clear(); + expandedOps.Clear(); + } } EditorGUILayout.EndHorizontal(); - EditorGUILayout.Space(5); + EditorGUILayout.Space(3); + + EditorGUILayout.BeginHorizontal(); + if (GUILayout.Button("<", GUILayout.Width(30), GUILayout.Height(20))) + AdvancePlayhead(-frameStep); + frameStep = EditorGUILayout.IntField(frameStep, GUILayout.Width(40)); + if (frameStep < 1) frameStep = 1; + if (GUILayout.Button(">", GUILayout.Width(30), GUILayout.Height(20))) + AdvancePlayhead(frameStep); + GUILayout.FlexibleSpace(); EditorGUILayout.LabelField($"Operations: {operations.Count}/16", EditorStyles.miniLabel); + EditorGUILayout.EndHorizontal(); } void ShowAddOperationMenu() @@ -231,7 +272,7 @@ public class FoldEditorWindow : EditorWindow expandedOps.Add(operations.Count - 1); } - void ApplyToMaterial() + void ApplyToMaterial(bool recordKeyframes = false) { var builder = FoldPipelineBuilder.Create().For(targetMaterial); @@ -239,8 +280,219 @@ public class FoldEditorWindow : EditorWindow op.ApplyTo(builder); builder.Apply(); + + if (AnimationMode.InAnimationMode()) + ApplyPropertyBlock(builder); + + if (recordKeyframes) + RecordAnimationKeyframes(builder); + } + + void ApplyPropertyBlock(FoldPipelineBuilder builder) + { + if (targetObject == null) return; + var renderer = targetObject.GetComponent<Renderer>(); + if (renderer == null) return; + + var mpb = new MaterialPropertyBlock(); + renderer.GetPropertyBlock(mpb); + + mpb.SetFloat("_Vertex_Deformation_Enabled", 1f); + + for (int i = 0; i < 16; i++) + { + var slot = builder.GetSlot(i); + var prefix = $"_Vertex_Deformation_Slot_{i}_"; + + mpb.SetFloat(prefix + "Enabled", slot != null ? 1f : 0f); + mpb.SetInteger(prefix + "Opcode", slot?.opcode ?? 0); + mpb.SetFloat(prefix + "Float_0", slot?.float0 ?? 0f); + mpb.SetFloat(prefix + "Float_1", slot?.float1 ?? 0f); + mpb.SetFloat(prefix + "Float_2", slot?.float2 ?? 0f); + mpb.SetFloat(prefix + "Float_3", slot?.float3 ?? 0f); + mpb.SetVector(prefix + "Vector_0", slot?.vec0 ?? Vector4.zero); + mpb.SetVector(prefix + "Vector_1", slot?.vec1 ?? Vector4.zero); + mpb.SetVector(prefix + "Vector_2", slot?.vec2 ?? Vector4.zero); + mpb.SetVector(prefix + "Vector_3", slot?.vec3 ?? Vector4.zero); + } + + renderer.SetPropertyBlock(mpb); + } + + void ClearPropertyBlock() + { + if (targetObject == null) return; + var renderer = targetObject.GetComponent<Renderer>(); + if (renderer != null) + renderer.SetPropertyBlock(null); + } + + #region Animation Recording + + void RecordAnimationKeyframes(FoldPipelineBuilder builder) + { + if (targetObject == null || !AnimationMode.InAnimationMode()) + return; + + var renderer = targetObject.GetComponent<Renderer>(); + if (renderer == null) return; + + if (!TryGetAnimationWindowState(out var clip, out float time)) + return; + + var animator = targetObject.GetComponentInParent<Animator>(); + string path = animator != null + ? AnimationUtility.CalculateTransformPath(renderer.transform, animator.transform) + : ""; + + SetFloatKey(clip, path, "material._Vertex_Deformation_Enabled", 1f, time); + + for (int i = 0; i < 16; i++) + { + var slot = builder.GetSlot(i); + var prefix = $"material._Vertex_Deformation_Slot_{i}_"; + + SetFloatKey(clip, path, prefix + "Enabled", slot != null ? 1f : 0f, time); + SetDiscreteKey(clip, path, prefix + "Opcode", slot?.opcode ?? 0, time); + SetFloatKey(clip, path, prefix + "Float_0", slot?.float0 ?? 0f, time); + SetFloatKey(clip, path, prefix + "Float_1", slot?.float1 ?? 0f, time); + SetFloatKey(clip, path, prefix + "Float_2", slot?.float2 ?? 0f, time); + SetFloatKey(clip, path, prefix + "Float_3", slot?.float3 ?? 0f, time); + SetVectorKey(clip, path, prefix + "Vector_0", slot?.vec0 ?? Vector4.zero, time); + SetVectorKey(clip, path, prefix + "Vector_1", slot?.vec1 ?? Vector4.zero, time); + SetVectorKey(clip, path, prefix + "Vector_2", slot?.vec2 ?? Vector4.zero, time); + SetVectorKey(clip, path, prefix + "Vector_3", slot?.vec3 ?? Vector4.zero, time); + } + } + + void DeleteKeyframeAtCurrentTime() + { + if (targetObject == null || !AnimationMode.InAnimationMode()) + { + Debug.LogWarning("Fold: No target object set or Animation window is not recording."); + return; + } + + var renderer = targetObject.GetComponent<Renderer>(); + if (renderer == null) return; + + if (!TryGetAnimationWindowState(out var clip, out float time)) + return; + + var animator = targetObject.GetComponentInParent<Animator>(); + string path = animator != null + ? AnimationUtility.CalculateTransformPath(renderer.transform, animator.transform) + : ""; + + Undo.RecordObject(clip, "Delete Fold Keyframe"); + + foreach (var binding in AnimationUtility.GetCurveBindings(clip)) + { + if (binding.type != typeof(Renderer)) continue; + if (binding.path != path) continue; + if (!binding.propertyName.StartsWith("material._Vertex_Deformation_")) continue; + + var curve = AnimationUtility.GetEditorCurve(clip, binding); + if (curve == null) continue; + + bool removed = false; + for (int i = curve.length - 1; i >= 0; i--) + { + if (Mathf.Approximately(curve.keys[i].time, time)) + { + curve.RemoveKey(i); + removed = true; + } + } + + if (removed) + { + if (curve.length == 0) + AnimationUtility.SetEditorCurve(clip, binding, null); + else + AnimationUtility.SetEditorCurve(clip, binding, curve); + } + } + } + + static void SetFloatKey(AnimationClip clip, string path, string prop, float value, float time) + { + var binding = EditorCurveBinding.FloatCurve(path, typeof(Renderer), prop); + var curve = AnimationUtility.GetEditorCurve(clip, binding) ?? new AnimationCurve(); + AddOrReplaceKey(curve, time, value); + AnimationUtility.SetEditorCurve(clip, binding, curve); + } + + static void SetDiscreteKey(AnimationClip clip, string path, string prop, int value, float time) + { + var binding = EditorCurveBinding.DiscreteCurve(path, typeof(Renderer), prop); + var curve = AnimationUtility.GetEditorCurve(clip, binding) ?? new AnimationCurve(); + AddOrReplaceKey(curve, time, value); + AnimationUtility.SetEditorCurve(clip, binding, curve); + } + + static void SetVectorKey(AnimationClip clip, string path, string prop, Vector4 v, float time) + { + SetFloatKey(clip, path, prop + ".x", v.x, time); + SetFloatKey(clip, path, prop + ".y", v.y, time); + SetFloatKey(clip, path, prop + ".z", v.z, time); + SetFloatKey(clip, path, prop + ".w", v.w, time); + } + + static void AddOrReplaceKey(AnimationCurve curve, float time, float value) + { + for (int i = curve.length - 1; i >= 0; i--) + if (Mathf.Approximately(curve.keys[i].time, time)) + curve.RemoveKey(i); + curve.AddKey(new Keyframe(time, value)); } + void AdvancePlayhead(int frames) + { + if (!TryGetAnimationWindowState(out var clip, out float time)) + return; + float frameDuration = 1f / clip.frameRate; + SetAnimationWindowTime(time + frames * frameDuration); + } + + static void SetAnimationWindowTime(float time) + { + var windowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.AnimationWindow"); + if (windowType == null) return; + + var windows = Resources.FindObjectsOfTypeAll(windowType); + if (windows.Length == 0) return; + + var timeProp = windowType.GetProperty("time", + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + timeProp?.SetValue(windows[0], time); + } + + static bool TryGetAnimationWindowState(out AnimationClip clip, out float time) + { + clip = null; + time = 0f; + + var windowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.AnimationWindow"); + if (windowType == null) return false; + + var windows = Resources.FindObjectsOfTypeAll(windowType); + if (windows.Length == 0) return false; + + var window = windows[0]; + const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + + var clipProp = windowType.GetProperty("animationClip", flags); + var timeProp = windowType.GetProperty("time", flags); + if (clipProp == null || timeProp == null) return false; + + clip = clipProp.GetValue(window) as AnimationClip; + time = (float)timeProp.GetValue(window); + return clip != null; + } + + #endregion + void LoadFromMaterial() { operations.Clear(); |
