From 1883b902eebf0587850db8bf972fc68d87b91197 Mon Sep 17 00:00:00 2001 From: yum Date: Thu, 19 Feb 2026 23:09:29 -0800 Subject: Fold: simplify code --- Scripts/Fold/Editor/FoldEditorWindow.cs | 760 ++++++++++------------------- Scripts/Fold/Editor/FoldPipelineBuilder.cs | 295 +++-------- 2 files changed, 320 insertions(+), 735 deletions(-) (limited to 'Scripts/Fold') diff --git a/Scripts/Fold/Editor/FoldEditorWindow.cs b/Scripts/Fold/Editor/FoldEditorWindow.cs index d475c6a..9ad9c29 100755 --- a/Scripts/Fold/Editor/FoldEditorWindow.cs +++ b/Scripts/Fold/Editor/FoldEditorWindow.cs @@ -30,8 +30,7 @@ public class FoldEditorWindow : EditorWindow public static GUIStyle miniButtonMid; public static GUIStyle miniButtonRight; public static GUIStyle footerButton; - public static GUIStyle footerTextButton; - + public static GUIContent iconUp; public static GUIContent iconDown; public static GUIContent iconRemove; @@ -42,7 +41,7 @@ public class FoldEditorWindow : EditorWindow public static GUIContent iconClear; public static GUIContent iconPrev; public static GUIContent iconNext; - + public static void Init() { if (card != null) return; @@ -68,27 +67,21 @@ public class FoldEditorWindow : EditorWindow footerButton.fixedHeight = 24; footerButton.fixedWidth = 32; - footerTextButton = new GUIStyle(EditorStyles.miniButton); - footerTextButton.fixedHeight = 24; - - iconUp = EditorGUIUtility.IconContent("d_scrollup@2x"); - iconDown = EditorGUIUtility.IconContent("d_scrolldown@2x"); - iconRemove = EditorGUIUtility.IconContent("TreeEditor.Trash"); - iconAdd = EditorGUIUtility.IconContent("Toolbar Plus"); - - iconKey = EditorGUIUtility.IconContent("Animation.Record"); - iconRead = EditorGUIUtility.IconContent("d_Import"); + iconUp = EditorGUIUtility.IconContent("d_scrollup@2x"); + iconDown = EditorGUIUtility.IconContent("d_scrolldown@2x"); + iconRemove = EditorGUIUtility.IconContent("TreeEditor.Trash"); + iconAdd = EditorGUIUtility.IconContent("Toolbar Plus"); + iconKey = EditorGUIUtility.IconContent("Animation.Record"); + iconRead = EditorGUIUtility.IconContent("d_Import"); iconDeleteKey = EditorGUIUtility.IconContent("d_Toolbar Minus"); - iconClear = EditorGUIUtility.IconContent("TreeEditor.Trash"); - - iconPrev = EditorGUIUtility.IconContent("Animation.PrevKey"); - iconNext = EditorGUIUtility.IconContent("Animation.NextKey"); - - // Fallbacks if icons are missing - if (iconUp.image == null) iconUp.text = "▲"; - if (iconDown.image == null) iconDown.text = "▼"; - if (iconKey.image == null) iconKey.text = "Rec"; - if (iconRead.image == null) iconRead.text = "Load"; + iconClear = EditorGUIUtility.IconContent("TreeEditor.Trash"); + iconPrev = EditorGUIUtility.IconContent("Animation.PrevKey"); + iconNext = EditorGUIUtility.IconContent("Animation.NextKey"); + + if (iconUp.image == null) iconUp.text = "▲"; + if (iconDown.image == null) iconDown.text = "▼"; + if (iconKey.image == null) iconKey.text = "Rec"; + if (iconRead.image == null) iconRead.text = "Load"; if (iconDeleteKey.image == null) iconDeleteKey.text = "-"; } } @@ -132,16 +125,12 @@ public class FoldEditorWindow : EditorWindow return; } - if (!TryGetAnimationWindowState(out var clip, out float time)) - return; + if (!TryGetAnimationWindowState(out var clip, out float time)) return; if (!Mathf.Approximately(time, lastAnimTime)) { lastAnimTime = time; - var savedExpanded = new List(expandedOps); - LoadFromAnimationClip(clip, time); - expandedOps = savedExpanded; - expandedOps.RemoveAll(i => i >= operations.Count); + LoadFromClipPreservingExpanded(clip, time); Repaint(); } } @@ -156,7 +145,6 @@ public class FoldEditorWindow : EditorWindow wasInAnimationMode = inAnimMode; EditorGUILayout.Space(5); - DrawHeader(); EditorGUILayout.Space(5); @@ -167,13 +155,11 @@ public class FoldEditorWindow : EditorWindow } DrawToolbar(); - - EditorGUI.BeginChangeCheck(); + EditorGUI.BeginChangeCheck(); scrollPos = EditorGUILayout.BeginScrollView(scrollPos); DrawOperationsList(); EditorGUILayout.EndScrollView(); - if (EditorGUI.EndChangeCheck() && targetMaterial != null) ApplyToMaterial(); @@ -184,7 +170,7 @@ public class FoldEditorWindow : EditorWindow void DrawHeader() { EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins); - + EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Target Material", EditorStyles.boldLabel, GUILayout.Width(100)); var newMat = EditorGUILayout.ObjectField(targetMaterial, typeof(Material), false) as Material; @@ -200,7 +186,7 @@ public class FoldEditorWindow : EditorWindow EditorGUILayout.LabelField("Target Object", EditorStyles.boldLabel, GUILayout.Width(100)); targetObject = EditorGUILayout.ObjectField(targetObject, typeof(GameObject), true) as GameObject; EditorGUILayout.EndHorizontal(); - + EditorGUILayout.EndVertical(); } @@ -214,13 +200,11 @@ public class FoldEditorWindow : EditorWindow var content = pipelineFull ? new GUIContent("Add Operation (Full)", "Pipeline full (16/16)") : new GUIContent(" Add Operation", Styles.iconAdd.image); - if (GUILayout.Button(content, EditorStyles.toolbarDropDown, GUILayout.Width(130))) ShowAddOperationMenu(); } GUILayout.FlexibleSpace(); - EditorGUILayout.EndHorizontal(); } @@ -235,9 +219,7 @@ public class FoldEditorWindow : EditorWindow } for (int i = 0; i < operations.Count; i++) - { DrawOperation(i); - } } void DrawOperation(int index) @@ -245,78 +227,50 @@ public class FoldEditorWindow : EditorWindow var op = operations[index]; bool isExpanded = expandedOps.Contains(index); - // Tint background if expanded var defaultColor = GUI.backgroundColor; - if (isExpanded) GUI.backgroundColor = new Color(0.9f, 0.95f, 1f); // Light blue tint + if (isExpanded) GUI.backgroundColor = new Color(0.9f, 0.95f, 1f); EditorGUILayout.BeginVertical(Styles.card); - GUI.backgroundColor = defaultColor; // Reset for content - - // Header Rect + GUI.backgroundColor = defaultColor; + Rect headerRect = EditorGUILayout.GetControlRect(false, 24); - - // Background for header if (Event.current.type == EventType.Repaint) - { Styles.cardHeader.Draw(headerRect, GUIContent.none, false, false, false, false); - } - // Calculate rects for controls inside the header - float btnWidth = 24; - float btnHeight = 18; - float btnSpacing = 0; - float rightPadding = 5; - float rightX = headerRect.xMax - rightPadding; + float btnWidth = 24, btnHeight = 18; float btnY = headerRect.y + (headerRect.height - btnHeight) / 2; + float rightX = headerRect.xMax - 5; - Rect removeRect = new Rect(rightX - btnWidth, btnY, btnWidth, btnHeight); - Rect downRect = new Rect(removeRect.x - btnWidth - btnSpacing, btnY, btnWidth, btnHeight); - Rect upRect = new Rect(downRect.x - btnWidth - btnSpacing, btnY, btnWidth, btnHeight); + Rect removeRect = new Rect(rightX - btnWidth, btnY, btnWidth, btnHeight); + Rect downRect = new Rect(removeRect.x - btnWidth, btnY, btnWidth, btnHeight); + Rect upRect = new Rect(downRect.x - btnWidth, btnY, btnWidth, btnHeight); - // Labels - Rect arrowRect = new Rect(headerRect.x + 5, headerRect.y + 4, 15, 16); + Rect arrowRect = new Rect(headerRect.x + 5, headerRect.y + 4, 15, 16); Rect indexRect = new Rect(headerRect.x + 20, headerRect.y + 4, 30, 16); Rect labelRect = new Rect(headerRect.x + 50, headerRect.y + 4, upRect.x - (headerRect.x + 50), 16); + Rect clickRect = new Rect(headerRect.x, headerRect.y, upRect.x - headerRect.x, headerRect.height); - // Foldout Click Area (covers everything except buttons) - Rect clickRect = new Rect(headerRect.x, headerRect.y, upRect.x - headerRect.x, headerRect.height); if (GUI.Button(clickRect, GUIContent.none, GUIStyle.none)) { if (isExpanded) expandedOps.Remove(index); else expandedOps.Add(index); } - // Draw Labels GUI.Label(arrowRect, isExpanded ? "▼" : "▶", EditorStyles.label); GUI.Label(indexRect, $"#{index}", EditorStyles.miniLabel); GUI.Label(labelRect, op.GetDisplayName(), EditorStyles.boldLabel); - // Draw Buttons - if (GUI.Button(upRect, Styles.iconUp, Styles.miniButtonLeft)) + if (GUI.Button(upRect, Styles.iconUp, Styles.miniButtonLeft) && index > 0) { - if (index > 0) - { - operations.RemoveAt(index); - operations.Insert(index - 1, op); - if (expandedOps.Contains(index)) - { - expandedOps.Remove(index); - expandedOps.Add(index - 1); - } - } + operations.RemoveAt(index); + operations.Insert(index - 1, op); + if (expandedOps.Remove(index)) expandedOps.Add(index - 1); } - if (GUI.Button(downRect, Styles.iconDown, Styles.miniButtonMid)) + if (GUI.Button(downRect, Styles.iconDown, Styles.miniButtonMid) && index < operations.Count - 1) { - if (index < operations.Count - 1) - { - operations.RemoveAt(index); - operations.Insert(index + 1, op); - if (expandedOps.Contains(index)) - { - expandedOps.Remove(index); - expandedOps.Add(index + 1); - } - } + operations.RemoveAt(index); + operations.Insert(index + 1, op); + if (expandedOps.Remove(index)) expandedOps.Add(index + 1); } if (GUI.Button(removeRect, Styles.iconRemove, Styles.miniButtonRight)) { @@ -329,7 +283,6 @@ public class FoldEditorWindow : EditorWindow } } - // Parameters Body if (isExpanded) { EditorGUILayout.BeginVertical(Styles.cardBody); @@ -346,28 +299,22 @@ public class FoldEditorWindow : EditorWindow { EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins); EditorGUILayout.Space(5); - - // Row 1: Keyframe & Animation Tools + EditorGUILayout.BeginHorizontal(); - if (GUILayout.Button(new GUIContent(Styles.iconRead.image, "Read from Playhead"), Styles.miniButtonLeft, GUILayout.Width(35), GUILayout.Height(24))) ReadFromPlayhead(); - if (GUILayout.Button(new GUIContent(Styles.iconKey.image, "Record Keyframe"), Styles.miniButtonMid, GUILayout.Width(35), GUILayout.Height(24))) ApplyToMaterial(recordKeyframes: true); - if (GUILayout.Button(new GUIContent(Styles.iconDeleteKey.image, "Delete Keyframe"), Styles.miniButtonMid, GUILayout.Width(35), GUILayout.Height(24))) DeleteKeyframeAtCurrentTime(); - if (GUILayout.Button(new GUIContent("Snap", "Snap to nearest keyframe"), Styles.miniButtonRight, GUILayout.Width(50), GUILayout.Height(24))) SnapToNearestKeyframe(); - + GUILayout.FlexibleSpace(); if (GUILayout.Button(new GUIContent(Styles.iconClear.image, "Clear All"), Styles.footerButton)) { - if (EditorUtility.DisplayDialog("Clear All Operations", - "Remove all operations from the pipeline?", "Clear", "Cancel")) + if (EditorUtility.DisplayDialog("Clear All Operations", "Remove all operations from the pipeline?", "Clear", "Cancel")) { operations.Clear(); expandedOps.Clear(); @@ -375,66 +322,53 @@ public class FoldEditorWindow : EditorWindow ApplyToMaterial(); } } - EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(4); - // Row 2: Timeline Tools EditorGUILayout.BeginHorizontal(); - if (GUILayout.Button(new GUIContent(Styles.iconPrev.image, "Step Back"), Styles.miniButtonLeft, GUILayout.Width(35), GUILayout.Height(24))) AdvancePlayhead(-frameStep); - - var centerField = new GUIStyle(EditorStyles.numberField); - centerField.alignment = TextAnchor.MiddleCenter; - centerField.fixedHeight = 24; + var centerField = new GUIStyle(EditorStyles.numberField) { alignment = TextAnchor.MiddleCenter, fixedHeight = 24 }; frameStep = EditorGUILayout.IntField(frameStep, centerField, GUILayout.Width(40)); if (frameStep < 1) frameStep = 1; - if (GUILayout.Button(new GUIContent(Styles.iconNext.image, "Step Forward"), Styles.miniButtonRight, GUILayout.Width(35), GUILayout.Height(24))) AdvancePlayhead(frameStep); - GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); - // Row 3: Info EditorGUILayout.Space(5); EditorGUILayout.LabelField($"{operations.Count} / 16 Operations", EditorStyles.centeredGreyMiniLabel); - EditorGUILayout.EndVertical(); } void ShowAddOperationMenu() { var menu = new GenericMenu(); - - menu.AddItem(new GUIContent("Tube to Plane"), false, () => AddOperation(new TubeToPlaneOp())); - menu.AddItem(new GUIContent("Plane to Tube"), false, () => AddOperation(new PlaneToTubeOp())); + menu.AddItem(new GUIContent("Tube to Plane"), false, () => AddOperation(new TubeToPlaneOp())); + menu.AddItem(new GUIContent("Plane to Tube"), false, () => AddOperation(new PlaneToTubeOp())); menu.AddItem(new GUIContent("Plane to Hemi-Octahedron"), false, () => AddOperation(new PlaneToHemiOctahedronOp())); menu.AddItem(new GUIContent("Hemi-Octahedron to Plane"), false, () => AddOperation(new HemiOctahedronToPlaneOp())); - menu.AddItem(new GUIContent("Plane to Octahedron"), false, () => AddOperation(new PlaneToOctahedronOp())); - menu.AddItem(new GUIContent("Octahedron to Plane"), false, () => AddOperation(new OctahedronToPlaneOp())); + menu.AddItem(new GUIContent("Plane to Octahedron"), false, () => AddOperation(new PlaneToOctahedronOp())); + menu.AddItem(new GUIContent("Octahedron to Plane"), false, () => AddOperation(new OctahedronToPlaneOp())); menu.AddSeparator(""); - menu.AddItem(new GUIContent("Point Align"), false, () => AddOperation(new PointAlignOp())); - menu.AddItem(new GUIContent("Axis Align"), false, () => AddOperation(new AxisAlignOp())); + menu.AddItem(new GUIContent("Point Align"), false, () => AddOperation(new PointAlignOp())); + menu.AddItem(new GUIContent("Axis Align"), false, () => AddOperation(new AxisAlignOp())); menu.AddSeparator(""); - menu.AddItem(new GUIContent("Scale"), false, () => AddOperation(new ScaleOp())); - menu.AddItem(new GUIContent("Translate"), false, () => AddOperation(new TranslateOp())); - menu.AddItem(new GUIContent("Rotate"), false, () => AddOperation(new RotateOp())); - menu.AddItem(new GUIContent("Norm Conversion"), false, () => AddOperation(new NormConversionOp())); - menu.AddItem(new GUIContent("Seal"), false, () => AddOperation(new SealOp())); + menu.AddItem(new GUIContent("Scale"), false, () => AddOperation(new ScaleOp())); + menu.AddItem(new GUIContent("Translate"), false, () => AddOperation(new TranslateOp())); + menu.AddItem(new GUIContent("Rotate"), false, () => AddOperation(new RotateOp())); + menu.AddItem(new GUIContent("Norm Conversion"), false, () => AddOperation(new NormConversionOp())); + menu.AddItem(new GUIContent("Seal"), false, () => AddOperation(new SealOp())); menu.AddSeparator(""); - menu.AddItem(new GUIContent("Sine Waves"), false, () => AddOperation(new SineWavesOp())); - menu.AddItem(new GUIContent("FBM"), false, () => AddOperation(new FBMOp())); - + menu.AddItem(new GUIContent("Sine Waves"), false, () => AddOperation(new SineWavesOp())); + menu.AddItem(new GUIContent("FBM"), false, () => AddOperation(new FBMOp())); menu.ShowAsContext(); } void AddOperation(DeformOperation op) { - if (operations.Count >= 16) - return; + if (operations.Count >= 16) return; operations.Add(op); expandedOps.Add(operations.Count - 1); } @@ -442,15 +376,12 @@ public class FoldEditorWindow : EditorWindow void ApplyToMaterial(bool recordKeyframes = false) { var builder = FoldPipelineBuilder.Create().For(targetMaterial); - foreach (var op in operations) op.ApplyTo(builder); - builder.Apply(); if (AnimationMode.InAnimationMode()) ApplyPropertyBlock(builder); - if (recordKeyframes) RecordAnimationKeyframes(builder); } @@ -463,26 +394,13 @@ public class FoldEditorWindow : EditorWindow 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); + if (slot != null) slot.ApplyToPropertyBlock(mpb, i); + else FoldSlot.ClearInPropertyBlock(mpb, i); } - renderer.SetPropertyBlock(mpb); } @@ -498,47 +416,35 @@ public class FoldEditorWindow : EditorWindow void ReadFromPlayhead() { - if (!TryGetAnimationWindowState(out var clip, out float time)) - return; + if (!TryGetAnimationWindowState(out var clip, out float time)) return; + LoadFromClipPreservingExpanded(clip, time); + lastAnimTime = time; + ApplyToMaterial(); + Repaint(); + } + void LoadFromClipPreservingExpanded(AnimationClip clip, float time) + { var savedExpanded = new List(expandedOps); LoadFromAnimationClip(clip, time); expandedOps = savedExpanded; expandedOps.RemoveAll(i => i >= operations.Count); - lastAnimTime = time; - ApplyToMaterial(); - Repaint(); } void RecordAnimationKeyframes(FoldPipelineBuilder builder) { - if (targetObject == null || !AnimationMode.InAnimationMode()) - return; - - var renderer = targetObject.GetComponent(); - if (renderer == null) return; - - if (!TryGetAnimationWindowState(out var clip, out float time)) - return; - - var animator = targetObject.GetComponentInParent(); - string path = animator != null - ? AnimationUtility.CalculateTransformPath(renderer.transform, animator.transform) - : ""; + if (!TryGetRecordingState(out var clip, out float time, out _, out string path)) return; - // Guard against OnEditorUpdate reloading a partially-written clip. isRecording = true; try { Undo.RecordObject(clip, "Create Fold Keyframe"); - 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); @@ -551,9 +457,6 @@ public class FoldEditorWindow : EditorWindow SetVectorKey(clip, path, prefix + "Vector_3", slot?.vec3 ?? Vector4.zero, time); } - // Pin the time so OnEditorUpdate doesn't treat the clip modification - // as a time change and reload (which would overwrite the UI state we - // just recorded from). lastAnimTime = time; } finally @@ -564,29 +467,9 @@ public class FoldEditorWindow : EditorWindow 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(); - if (renderer == null) - { - Debug.LogWarning("Fold: Target object has no Renderer component."); - return; - } - - if (!TryGetAnimationWindowState(out var clip, out float time)) - return; - - var animator = targetObject.GetComponentInParent(); - string path = animator != null - ? AnimationUtility.CalculateTransformPath(renderer.transform, animator.transform) - : ""; + if (!TryGetRecordingState(out var clip, out float time, out _, out string path)) return; Undo.RecordObject(clip, "Delete Fold Keyframe"); - foreach (var binding in AnimationUtility.GetCurveBindings(clip)) { if (binding.type != typeof(Renderer)) continue; @@ -607,15 +490,24 @@ public class FoldEditorWindow : EditorWindow } if (removed) - { - if (curve.length == 0) - AnimationUtility.SetEditorCurve(clip, binding, null); - else - AnimationUtility.SetEditorCurve(clip, binding, curve); - } + AnimationUtility.SetEditorCurve(clip, binding, curve.length == 0 ? null : curve); } } + bool TryGetRecordingState(out AnimationClip clip, out float time, out Renderer renderer, out string path) + { + clip = null; time = 0f; renderer = null; path = ""; + if (targetObject == null || !AnimationMode.InAnimationMode()) return false; + renderer = targetObject.GetComponent(); + if (renderer == null) return false; + if (!TryGetAnimationWindowState(out clip, out time)) return false; + var animator = targetObject.GetComponentInParent(); + path = animator != null + ? AnimationUtility.CalculateTransformPath(renderer.transform, animator.transform) + : ""; + return true; + } + static void SetFloatKey(AnimationClip clip, string path, string prop, float value, float time) { var binding = EditorCurveBinding.FloatCurve(path, typeof(Renderer), prop); @@ -648,22 +540,23 @@ public class FoldEditorWindow : EditorWindow curve.AddKey(new Keyframe(time, value)); } - void AdvancePlayhead(int frames) + void SeekTo(float time) { - if (!TryGetAnimationWindowState(out var clip, out float time)) - return; - float frameDuration = 1f / clip.frameRate; - float newTime = time + frames * frameDuration; - SetAnimationWindowTime(newTime); - lastAnimTime = newTime; // Suppress OnEditorUpdate from reloading animation data. - ApplyToMaterial(); // Re-apply current editor state so the scene stays in sync. + SetAnimationWindowTime(time); + lastAnimTime = time; + ApplyToMaterial(); Repaint(); } + void AdvancePlayhead(int frames) + { + if (!TryGetAnimationWindowState(out var clip, out float time)) return; + SeekTo(time + frames * (1f / clip.frameRate)); + } + void SnapToNearestKeyframe() { - if (!TryGetAnimationWindowState(out var clip, out float time)) - return; + if (!TryGetAnimationWindowState(out var clip, out float time)) return; float bestTime = time; float bestDist = float.MaxValue; @@ -671,38 +564,25 @@ public class FoldEditorWindow : EditorWindow foreach (var binding in AnimationUtility.GetCurveBindings(clip)) { if (!binding.propertyName.StartsWith("material._Vertex_Deformation_")) continue; - var curve = AnimationUtility.GetEditorCurve(clip, binding); if (curve == null) continue; - - for (int i = 0; i < curve.length; i++) + foreach (var key in curve.keys) { - float dist = Mathf.Abs(curve.keys[i].time - time); - if (dist > 0f && dist < bestDist) - { - bestDist = dist; - bestTime = curve.keys[i].time; - } + float dist = Mathf.Abs(key.time - time); + if (dist > 0f && dist < bestDist) { bestDist = dist; bestTime = key.time; } } } if (bestDist < float.MaxValue) - { - SetAnimationWindowTime(bestTime); - lastAnimTime = bestTime; // Suppress OnEditorUpdate from reloading animation data. - ApplyToMaterial(); // Re-apply current editor state so the scene stays in sync. - Repaint(); - } + SeekTo(bestTime); } static void SetAnimationWindowTime(float time) { CacheAnimWindowReflection(); if (s_animWindowType == null || s_timeProp == null) return; - var windows = Resources.FindObjectsOfTypeAll(s_animWindowType); if (windows.Length == 0) return; - s_timeProp.SetValue(windows[0], time); ((EditorWindow)windows[0]).Repaint(); } @@ -711,13 +591,10 @@ public class FoldEditorWindow : EditorWindow { clip = null; time = 0f; - CacheAnimWindowReflection(); if (s_animWindowType == null || s_clipProp == null || s_timeProp == null) return false; - var windows = Resources.FindObjectsOfTypeAll(s_animWindowType); if (windows.Length == 0) return false; - var window = windows[0]; clip = s_clipProp.GetValue(window) as AnimationClip; time = (float)s_timeProp.GetValue(window); @@ -734,12 +611,10 @@ public class FoldEditorWindow : EditorWindow for (int i = 0; i < 16; i++) { var prefix = $"_Vertex_Deformation_Slot_{i}_"; - if (targetMaterial.GetFloat(prefix + "Enabled") < 0.5f) - break; + if (targetMaterial.GetFloat(prefix + "Enabled") < 0.5f) break; int opcode = targetMaterial.GetInteger(prefix + "Opcode"); - if (opcode == FoldPipelineBuilder.Opcodes.None) - break; + if (opcode == 0) break; var slot = new FoldSlot { @@ -748,13 +623,13 @@ public class FoldEditorWindow : EditorWindow float1 = targetMaterial.GetFloat(prefix + "Float_1"), float2 = targetMaterial.GetFloat(prefix + "Float_2"), float3 = targetMaterial.GetFloat(prefix + "Float_3"), - vec0 = targetMaterial.GetVector(prefix + "Vector_0"), - vec1 = targetMaterial.GetVector(prefix + "Vector_1"), - vec2 = targetMaterial.GetVector(prefix + "Vector_2"), - vec3 = targetMaterial.GetVector(prefix + "Vector_3"), + vec0 = targetMaterial.GetVector(prefix + "Vector_0"), + vec1 = targetMaterial.GetVector(prefix + "Vector_1"), + vec2 = targetMaterial.GetVector(prefix + "Vector_2"), + vec3 = targetMaterial.GetVector(prefix + "Vector_3"), }; - var op = CreateOperationFromSlot(slot); + var op = DeformOperation.Create(slot); if (op == null) break; operations.Add(op); } @@ -781,7 +656,7 @@ public class FoldEditorWindow : EditorWindow if (enabled < 0.5f) break; int opcode = SampleDiscreteCurve(clip, path, prefix + "Opcode", time); - if (opcode == FoldPipelineBuilder.Opcodes.None) break; + if (opcode == 0) break; var slot = new FoldSlot { @@ -790,13 +665,13 @@ public class FoldEditorWindow : EditorWindow float1 = SampleFloatCurve(clip, path, prefix + "Float_1", time), float2 = SampleFloatCurve(clip, path, prefix + "Float_2", time), float3 = SampleFloatCurve(clip, path, prefix + "Float_3", time), - vec0 = SampleVectorCurve(clip, path, prefix + "Vector_0", time), - vec1 = SampleVectorCurve(clip, path, prefix + "Vector_1", time), - vec2 = SampleVectorCurve(clip, path, prefix + "Vector_2", time), - vec3 = SampleVectorCurve(clip, path, prefix + "Vector_3", time), + vec0 = SampleVectorCurve(clip, path, prefix + "Vector_0", time), + vec1 = SampleVectorCurve(clip, path, prefix + "Vector_1", time), + vec2 = SampleVectorCurve(clip, path, prefix + "Vector_2", time), + vec3 = SampleVectorCurve(clip, path, prefix + "Vector_3", time), }; - var op = CreateOperationFromSlot(slot); + var op = DeformOperation.Create(slot); if (op == null) break; operations.Add(op); } @@ -815,73 +690,55 @@ public class FoldEditorWindow : EditorWindow var curve = AnimationUtility.GetEditorCurve(clip, binding); if (curve == null || curve.length == 0) return 0; - // Step behavior: use the most recent keyframe at or before time. - var keys = curve.keys; - int value = Mathf.RoundToInt(keys[0].value); - for (int i = 0; i < keys.Length; i++) + int value = Mathf.RoundToInt(curve.keys[0].value); + foreach (var key in curve.keys) { - if (keys[i].time > time) break; - value = Mathf.RoundToInt(keys[i].value); + if (key.time > time) break; + value = Mathf.RoundToInt(key.value); } return value; } - static Vector4 SampleVectorCurve(AnimationClip clip, string path, string prop, float time) - { - return new Vector4( + static Vector4 SampleVectorCurve(AnimationClip clip, string path, string prop, float time) => + new Vector4( SampleFloatCurve(clip, path, prop + ".x", time), SampleFloatCurve(clip, path, prop + ".y", time), SampleFloatCurve(clip, path, prop + ".z", time), SampleFloatCurve(clip, path, prop + ".w", time)); - } - static DeformOperation CreateOperationFromSlot(FoldSlot slot) - { - return slot.opcode switch - { - FoldPipelineBuilder.Opcodes.TubeToPlane => new TubeToPlaneOp(slot), - FoldPipelineBuilder.Opcodes.PlaneToTube => new PlaneToTubeOp(slot), - FoldPipelineBuilder.Opcodes.PlaneToHemiOctahedron => new PlaneToHemiOctahedronOp(slot), - FoldPipelineBuilder.Opcodes.HemiOctahedronToPlane => new HemiOctahedronToPlaneOp(slot), - FoldPipelineBuilder.Opcodes.Scale => new ScaleOp(slot), - FoldPipelineBuilder.Opcodes.Translate => new TranslateOp(slot), - FoldPipelineBuilder.Opcodes.PlaneToOctahedron => new PlaneToOctahedronOp(slot), - FoldPipelineBuilder.Opcodes.OctahedronToPlane => new OctahedronToPlaneOp(slot), - FoldPipelineBuilder.Opcodes.Rotate => new RotateOp(slot), - FoldPipelineBuilder.Opcodes.PointAlign => new PointAlignOp(slot), - FoldPipelineBuilder.Opcodes.AxisAlign => new AxisAlignOp(slot), - FoldPipelineBuilder.Opcodes.NormConversion => new NormConversionOp(slot), - FoldPipelineBuilder.Opcodes.Seal => new SealOp(slot), - FoldPipelineBuilder.Opcodes.SineWaves => new SineWavesOp(), - FoldPipelineBuilder.Opcodes.FBM => new FBMOp(), - _ => null - }; - } } -// Base class for deformation operations +// ─── Operation base class ───────────────────────────────────────────────────── + [System.Serializable] public abstract class DeformOperation { public abstract string GetDisplayName(); - public abstract void DrawParameters(); + public virtual void DrawParameters() { } public abstract void ApplyTo(FoldPipelineBuilder builder); -} - -[System.Serializable] -public class TubeToPlaneOp : DeformOperation -{ - public Vector3 p = Vector3.zero; - public Vector3 r = Vector3.right; - public Vector3 s = Vector3.forward; - public float t = 1f; - - public TubeToPlaneOp() { } - public TubeToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } - public override string GetDisplayName() => "Tube to Plane"; + public static DeformOperation Create(FoldSlot slot) => + slot.opcode switch + { + TubeToPlaneOp.Opcode => new TubeToPlaneOp(slot), + PlaneToTubeOp.Opcode => new PlaneToTubeOp(slot), + PlaneToHemiOctahedronOp.Opcode => new PlaneToHemiOctahedronOp(slot), + HemiOctahedronToPlaneOp.Opcode => new HemiOctahedronToPlaneOp(slot), + ScaleOp.Opcode => new ScaleOp(slot), + TranslateOp.Opcode => new TranslateOp(slot), + PlaneToOctahedronOp.Opcode => new PlaneToOctahedronOp(slot), + OctahedronToPlaneOp.Opcode => new OctahedronToPlaneOp(slot), + RotateOp.Opcode => new RotateOp(slot), + PointAlignOp.Opcode => new PointAlignOp(slot), + AxisAlignOp.Opcode => new AxisAlignOp(slot), + NormConversionOp.Opcode => new NormConversionOp(slot), + SealOp.Opcode => new SealOp(slot), + SineWavesOp.Opcode => new SineWavesOp(), + FBMOp.Opcode => new FBMOp(), + _ => null + }; - public override void DrawParameters() + protected static void DrawPrsParams(ref Vector3 p, ref Vector3 r, ref Vector3 s, ref float t) { p = EditorGUILayout.Vector3Field("Origin (p)", p); r = EditorGUILayout.Vector3Field("R Axis", r); @@ -889,275 +746,191 @@ public class TubeToPlaneOp : DeformOperation t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.TubeToPlane(p, r, s, t); + protected static void DrawAlignParams(ref Vector3 po, ref Vector3 pp, ref Vector3 r, ref float t) + { + po = EditorGUILayout.Vector3Field("Origin Point (po)", po); + pp = EditorGUILayout.Vector3Field("Target Point (pp)", pp); + r = EditorGUILayout.Vector3Field("Rotation Axis (r)", r); + t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); + } } +// ─── Concrete operations ────────────────────────────────────────────────────── + [System.Serializable] -public class PlaneToTubeOp : DeformOperation +public class TubeToPlaneOp : DeformOperation { - public Vector3 p = Vector3.zero; - public Vector3 r = Vector3.right; - public Vector3 s = Vector3.forward; + public const int Opcode = 1; + public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; + public TubeToPlaneOp() { } + public TubeToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } + public override string GetDisplayName() => "Tube to Plane"; + public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); + public override void ApplyTo(FoldPipelineBuilder b) => b.TubeToPlane(p, r, s, t); +} +[System.Serializable] +public class PlaneToTubeOp : DeformOperation +{ + public const int Opcode = 2; + public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; + public float t = 1f; public PlaneToTubeOp() { } public PlaneToTubeOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } - public override string GetDisplayName() => "Plane to Tube"; - - public override void DrawParameters() - { - p = EditorGUILayout.Vector3Field("Origin (p)", p); - r = EditorGUILayout.Vector3Field("R Axis", r); - s = EditorGUILayout.Vector3Field("S Axis", s); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.PlaneToTube(p, r, s, t); + public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); + public override void ApplyTo(FoldPipelineBuilder b) => b.PlaneToTube(p, r, s, t); } [System.Serializable] public class PlaneToHemiOctahedronOp : DeformOperation { - public Vector3 p = Vector3.zero; - public Vector3 r = Vector3.right; - public Vector3 s = Vector3.forward; + public const int Opcode = 9; + public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; - public PlaneToHemiOctahedronOp() { } public PlaneToHemiOctahedronOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } - public override string GetDisplayName() => "Plane to Hemi-Octahedron"; - - public override void DrawParameters() - { - p = EditorGUILayout.Vector3Field("Origin (p)", p); - r = EditorGUILayout.Vector3Field("R Axis", r); - s = EditorGUILayout.Vector3Field("S Axis", s); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.PlaneToHemiOctahedron(p, r, s, t); + public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); + public override void ApplyTo(FoldPipelineBuilder b) => b.PlaneToHemiOctahedron(p, r, s, t); } [System.Serializable] -public class ScaleOp : DeformOperation +public class HemiOctahedronToPlaneOp : DeformOperation { - public Vector3 k = Vector3.one; + public const int Opcode = 10; + public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; - - public ScaleOp() { } - public ScaleOp(FoldSlot slot) { k = slot.vec0; t = slot.float0; } - - public override string GetDisplayName() => "Scale"; - - public override void DrawParameters() - { - k = EditorGUILayout.Vector3Field("Scale", k); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.Scale(k, t); + public HemiOctahedronToPlaneOp() { } + public HemiOctahedronToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } + public override string GetDisplayName() => "Hemi-Octahedron to Plane"; + public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); + public override void ApplyTo(FoldPipelineBuilder b) => b.HemiOctahedronToPlane(p, r, s, t); } [System.Serializable] -public class TranslateOp : DeformOperation +public class PlaneToOctahedronOp : DeformOperation { - public Vector3 offset = Vector3.zero; + public const int Opcode = 13; + public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; - - public TranslateOp() { } - public TranslateOp(FoldSlot slot) { offset = slot.vec0; t = slot.float0; } - - public override string GetDisplayName() => "Translate"; - - public override void DrawParameters() - { - offset = EditorGUILayout.Vector3Field("Offset", offset); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.Translate(offset, t); + public PlaneToOctahedronOp() { } + public PlaneToOctahedronOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } + public override string GetDisplayName() => "Plane to Octahedron"; + public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); + public override void ApplyTo(FoldPipelineBuilder b) => b.PlaneToOctahedron(p, r, s, t); } [System.Serializable] -public class RotateOp : DeformOperation +public class OctahedronToPlaneOp : DeformOperation { - public Vector3 center = Vector3.zero; - public Vector3 axis = Vector3.up; - public float angleDeg = 90f; + public const int Opcode = 14; + public Vector3 p = Vector3.zero, r = Vector3.right, s = Vector3.forward; public float t = 1f; - - public RotateOp() { } - public RotateOp(FoldSlot slot) - { - center = slot.vec0; - axis = slot.vec1; - angleDeg = slot.float0 * Mathf.Rad2Deg; - t = slot.float1; - } - - public override string GetDisplayName() => "Rotate"; - - public override void DrawParameters() - { - center = EditorGUILayout.Vector3Field("Center", center); - axis = EditorGUILayout.Vector3Field("Axis", axis); - angleDeg = EditorGUILayout.Slider("Angle", angleDeg, 0f, 360f); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.Rotate(center, axis, angleDeg * Mathf.Deg2Rad, t); + public OctahedronToPlaneOp() { } + public OctahedronToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } + public override string GetDisplayName() => "Octahedron to Plane"; + public override void DrawParameters() => DrawPrsParams(ref p, ref r, ref s, ref t); + public override void ApplyTo(FoldPipelineBuilder b) => b.OctahedronToPlane(p, r, s, t); } [System.Serializable] -public class HemiOctahedronToPlaneOp : DeformOperation +public class PointAlignOp : DeformOperation { - public Vector3 p = Vector3.zero; - public Vector3 r = Vector3.right; - public Vector3 s = Vector3.forward; + public const int Opcode = 3; + public Vector3 po = Vector3.zero, pp = Vector3.up, r = Vector3.right; public float t = 1f; - - public HemiOctahedronToPlaneOp() { } - public HemiOctahedronToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } - - public override string GetDisplayName() => "Hemi-Octahedron to Plane"; - - public override void DrawParameters() - { - p = EditorGUILayout.Vector3Field("Origin (p)", p); - r = EditorGUILayout.Vector3Field("R Axis", r); - s = EditorGUILayout.Vector3Field("S Axis", s); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.HemiOctahedronToPlane(p, r, s, t); + public PointAlignOp() { } + public PointAlignOp(FoldSlot slot) { po = slot.vec0; pp = slot.vec1; r = slot.vec2; t = slot.float0; } + public override string GetDisplayName() => "Point Align"; + public override void DrawParameters() => DrawAlignParams(ref po, ref pp, ref r, ref t); + public override void ApplyTo(FoldPipelineBuilder b) => b.PointAlign(po, pp, r, t); } [System.Serializable] -public class PlaneToOctahedronOp : DeformOperation +public class AxisAlignOp : DeformOperation { - public Vector3 p = Vector3.zero; - public Vector3 r = Vector3.right; - public Vector3 s = Vector3.forward; + public const int Opcode = 4; + public Vector3 po = Vector3.zero, pp = Vector3.up, r = Vector3.right; public float t = 1f; - - public PlaneToOctahedronOp() { } - public PlaneToOctahedronOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } - - public override string GetDisplayName() => "Plane to Octahedron"; - - public override void DrawParameters() - { - p = EditorGUILayout.Vector3Field("Origin (p)", p); - r = EditorGUILayout.Vector3Field("R Axis", r); - s = EditorGUILayout.Vector3Field("S Axis", s); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.PlaneToOctahedron(p, r, s, t); + public AxisAlignOp() { } + public AxisAlignOp(FoldSlot slot) { po = slot.vec0; pp = slot.vec1; r = slot.vec2; t = slot.float0; } + public override string GetDisplayName() => "Axis Align"; + public override void DrawParameters() => DrawAlignParams(ref po, ref pp, ref r, ref t); + public override void ApplyTo(FoldPipelineBuilder b) => b.AxisAlign(po, pp, r, t); } [System.Serializable] -public class OctahedronToPlaneOp : DeformOperation +public class ScaleOp : DeformOperation { - public Vector3 p = Vector3.zero; - public Vector3 r = Vector3.right; - public Vector3 s = Vector3.forward; + public const int Opcode = 11; + public Vector3 k = Vector3.one; public float t = 1f; - public OctahedronToPlaneOp() { } - public OctahedronToPlaneOp(FoldSlot slot) { p = slot.vec0; r = slot.vec1; s = slot.vec2; t = slot.float0; } - - public override string GetDisplayName() => "Octahedron to Plane"; + public ScaleOp() { } + public ScaleOp(FoldSlot slot) { k = slot.vec0; t = slot.float0; } + public override string GetDisplayName() => "Scale"; public override void DrawParameters() { - p = EditorGUILayout.Vector3Field("Origin (p)", p); - r = EditorGUILayout.Vector3Field("R Axis", r); - s = EditorGUILayout.Vector3Field("S Axis", s); + k = EditorGUILayout.Vector3Field("Scale", k); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.OctahedronToPlane(p, r, s, t); + public override void ApplyTo(FoldPipelineBuilder b) => b.Scale(k, t); } [System.Serializable] -public class PointAlignOp : DeformOperation +public class TranslateOp : DeformOperation { - public Vector3 po = Vector3.zero; - public Vector3 pp = Vector3.up; - public Vector3 r = Vector3.right; + public const int Opcode = 12; + public Vector3 offset = Vector3.zero; public float t = 1f; - public PointAlignOp() { } - public PointAlignOp(FoldSlot slot) { po = slot.vec0; pp = slot.vec1; r = slot.vec2; t = slot.float0; } - - public override string GetDisplayName() => "Point Align"; + public TranslateOp() { } + public TranslateOp(FoldSlot slot) { offset = slot.vec0; t = slot.float0; } + public override string GetDisplayName() => "Translate"; public override void DrawParameters() { - po = EditorGUILayout.Vector3Field("Origin Point (po)", po); - pp = EditorGUILayout.Vector3Field("Target Point (pp)", pp); - r = EditorGUILayout.Vector3Field("Rotation Axis (r)", r); + offset = EditorGUILayout.Vector3Field("Offset", offset); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.PointAlign(po, pp, r, t); + public override void ApplyTo(FoldPipelineBuilder b) => b.Translate(offset, t); } [System.Serializable] -public class AxisAlignOp : DeformOperation +public class RotateOp : DeformOperation { - public Vector3 po = Vector3.zero; - public Vector3 pp = Vector3.up; - public Vector3 r = Vector3.right; - public float t = 1f; - - public AxisAlignOp() { } - public AxisAlignOp(FoldSlot slot) { po = slot.vec0; pp = slot.vec1; r = slot.vec2; t = slot.float0; } + public const int Opcode = 15; + public Vector3 center = Vector3.zero, axis = Vector3.up; + public float angleDeg = 90f, t = 1f; - public override string GetDisplayName() => "Axis Align"; + public RotateOp() { } + public RotateOp(FoldSlot slot) { center = slot.vec0; axis = slot.vec1; angleDeg = slot.float0 * Mathf.Rad2Deg; t = slot.float1; } + public override string GetDisplayName() => "Rotate"; public override void DrawParameters() { - po = EditorGUILayout.Vector3Field("Origin Point (po)", po); - pp = EditorGUILayout.Vector3Field("Target Point (pp)", pp); - r = EditorGUILayout.Vector3Field("Rotation Axis (r)", r); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); + center = EditorGUILayout.Vector3Field("Center", center); + axis = EditorGUILayout.Vector3Field("Axis", axis); + angleDeg = EditorGUILayout.Slider("Angle", angleDeg, 0f, 360f); + t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.AxisAlign(po, pp, r, t); + public override void ApplyTo(FoldPipelineBuilder b) => b.Rotate(center, axis, angleDeg * Mathf.Deg2Rad, t); } [System.Serializable] public class NormConversionOp : DeformOperation { - public float inputK = 2f; - public float outputK = 1f; - public float t = 1f; + public const int Opcode = 5; + public float inputK = 2f, outputK = 1f, t = 1f; public NormConversionOp() { } public NormConversionOp(FoldSlot slot) { inputK = slot.float0; outputK = slot.float1; t = slot.float2; } public override string GetDisplayName() => $"Norm Conversion (L{FormatNorm(inputK)}→L{FormatNorm(outputK)})"; - - string FormatNorm(float k) - { - if (float.IsPositiveInfinity(k)) return "∞"; - return k.ToString("F1"); - } + string FormatNorm(float k) => float.IsPositiveInfinity(k) ? "∞" : k.ToString("F1"); public override void DrawParameters() { @@ -1174,65 +947,46 @@ public class NormConversionOp : DeformOperation EditorGUILayout.EndHorizontal(); t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); - - EditorGUILayout.HelpBox( - "Common: L1=diamond, L2=sphere, L∞=cube", - MessageType.Info); + EditorGUILayout.HelpBox("Common: L1=diamond, L2=sphere, L∞=cube", MessageType.Info); } - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.NormConversion(inputK, outputK, t); + public override void ApplyTo(FoldPipelineBuilder b) => b.NormConversion(inputK, outputK, t); } [System.Serializable] public class SealOp : DeformOperation { - public float A = 0.1f; - public float k = 2f; - public float st = 0.8f; - public float t = 1f; + public const int Opcode = 6; + public float A = 0.1f, k = 2f, st = 0.8f, t = 1f; public SealOp() { } public SealOp(FoldSlot slot) { A = slot.float0; k = slot.float1; st = slot.float2; t = slot.float3; } public override string GetDisplayName() => "Seal"; - public override void DrawParameters() { - A = EditorGUILayout.FloatField("Amplitude (A)", A); - k = EditorGUILayout.FloatField("Smoothness (k)", k); + A = EditorGUILayout.FloatField("Amplitude (A)", A); + k = EditorGUILayout.FloatField("Smoothness (k)", k); st = EditorGUILayout.FloatField("Scale (st)", st); - t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); + t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f); } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.Seal(A, k, st, t); + public override void ApplyTo(FoldPipelineBuilder b) => b.Seal(A, k, st, t); } [System.Serializable] public class SineWavesOp : DeformOperation { + public const int Opcode = 7; public override string GetDisplayName() => "Sine Waves"; - - public override void DrawParameters() - { - EditorGUILayout.HelpBox("Sine waves use shader-side parameters", MessageType.Info); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.SineWaves(); + public override void DrawParameters() => EditorGUILayout.HelpBox("Uses shader-side parameters", MessageType.Info); + public override void ApplyTo(FoldPipelineBuilder b) => b.SineWaves(); } [System.Serializable] public class FBMOp : DeformOperation { + public const int Opcode = 8; public override string GetDisplayName() => "FBM (Fractal Brownian Motion)"; - - public override void DrawParameters() - { - EditorGUILayout.HelpBox("FBM uses shader-side parameters", MessageType.Info); - } - - public override void ApplyTo(FoldPipelineBuilder builder) => - builder.FBM(); + public override void DrawParameters() => EditorGUILayout.HelpBox("Uses shader-side parameters", MessageType.Info); + public override void ApplyTo(FoldPipelineBuilder b) => b.FBM(); } diff --git a/Scripts/Fold/Editor/FoldPipelineBuilder.cs b/Scripts/Fold/Editor/FoldPipelineBuilder.cs index b610aca..ad74b1b 100755 --- a/Scripts/Fold/Editor/FoldPipelineBuilder.cs +++ b/Scripts/Fold/Editor/FoldPipelineBuilder.cs @@ -9,35 +9,47 @@ public class FoldSlot public float float0, float1, float2, float3; public Vector4 vec0, vec1, vec2, vec3; - public void ApplyToMaterial(Material mat, int slotIndex) + static string Prefix(int i) => $"_Vertex_Deformation_Slot_{i}_"; + + void ApplyToTarget(string pfx, Action sf, Action si, Action sv) { - var prefix = $"_Vertex_Deformation_Slot_{slotIndex}_"; - mat.SetFloat(prefix + "Enabled", 1f); - mat.SetInteger(prefix + "Opcode", opcode); - mat.SetFloat(prefix + "Float_0", float0); - mat.SetFloat(prefix + "Float_1", float1); - mat.SetFloat(prefix + "Float_2", float2); - mat.SetFloat(prefix + "Float_3", float3); - mat.SetVector(prefix + "Vector_0", vec0); - mat.SetVector(prefix + "Vector_1", vec1); - mat.SetVector(prefix + "Vector_2", vec2); - mat.SetVector(prefix + "Vector_3", vec3); + sf(pfx + "Enabled", 1f); + si(pfx + "Opcode", opcode); + sf(pfx + "Float_0", float0); + sf(pfx + "Float_1", float1); + sf(pfx + "Float_2", float2); + sf(pfx + "Float_3", float3); + sv(pfx + "Vector_0", vec0); + sv(pfx + "Vector_1", vec1); + sv(pfx + "Vector_2", vec2); + sv(pfx + "Vector_3", vec3); } - public static void ClearSlot(Material mat, int slotIndex) + static void ClearTarget(string pfx, Action sf, Action si, Action sv) { - var prefix = $"_Vertex_Deformation_Slot_{slotIndex}_"; - mat.SetFloat(prefix + "Enabled", 0f); - mat.SetInteger(prefix + "Opcode", 0); - mat.SetFloat(prefix + "Float_0", 0f); - mat.SetFloat(prefix + "Float_1", 0f); - mat.SetFloat(prefix + "Float_2", 0f); - mat.SetFloat(prefix + "Float_3", 0f); - mat.SetVector(prefix + "Vector_0", Vector4.zero); - mat.SetVector(prefix + "Vector_1", Vector4.zero); - mat.SetVector(prefix + "Vector_2", Vector4.zero); - mat.SetVector(prefix + "Vector_3", Vector4.zero); + sf(pfx + "Enabled", 0f); + si(pfx + "Opcode", 0); + sf(pfx + "Float_0", 0f); + sf(pfx + "Float_1", 0f); + sf(pfx + "Float_2", 0f); + sf(pfx + "Float_3", 0f); + sv(pfx + "Vector_0", Vector4.zero); + sv(pfx + "Vector_1", Vector4.zero); + sv(pfx + "Vector_2", Vector4.zero); + sv(pfx + "Vector_3", Vector4.zero); } + + public void ApplyToMaterial(Material mat, int i) => + ApplyToTarget(Prefix(i), mat.SetFloat, mat.SetInteger, (k, v) => mat.SetVector(k, v)); + + public void ApplyToPropertyBlock(MaterialPropertyBlock mpb, int i) => + ApplyToTarget(Prefix(i), mpb.SetFloat, mpb.SetInteger, (k, v) => mpb.SetVector(k, v)); + + public static void ClearSlot(Material mat, int i) => + ClearTarget(Prefix(i), mat.SetFloat, mat.SetInteger, (k, v) => mat.SetVector(k, v)); + + public static void ClearInPropertyBlock(MaterialPropertyBlock mpb, int i) => + ClearTarget(Prefix(i), mpb.SetFloat, mpb.SetInteger, (k, v) => mpb.SetVector(k, v)); } public class FoldPipelineBuilder @@ -45,217 +57,47 @@ public class FoldPipelineBuilder readonly List slots = new(); Material targetMaterial; - public static class Opcodes - { - public const int None = 0; - public const int TubeToPlane = 1; - public const int PlaneToTube = 2; - public const int PointAlign = 3; - public const int AxisAlign = 4; - public const int NormConversion = 5; - public const int Seal = 6; - public const int SineWaves = 7; - public const int FBM = 8; - public const int PlaneToHemiOctahedron = 9; - public const int HemiOctahedronToPlane = 10; - public const int Scale = 11; - public const int Translate = 12; - public const int PlaneToOctahedron = 13; - public const int OctahedronToPlane = 14; - public const int Rotate = 15; - } - FoldPipelineBuilder() { } public static FoldPipelineBuilder Create() => new(); - public FoldPipelineBuilder For(Material material) - { - targetMaterial = material; - return this; - } + public FoldPipelineBuilder For(Material material) { targetMaterial = material; return this; } - public FoldPipelineBuilder TubeToPlane(Vector3 p, Vector3 r, Vector3 s, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.TubeToPlane, - vec0 = p, - vec1 = r, - vec2 = s, - float0 = t - }); - return this; - } + FoldPipelineBuilder AddSlot(FoldSlot slot) { slots.Add(slot); return this; } - public FoldPipelineBuilder PlaneToTube(Vector3 p, Vector3 r, Vector3 s, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.PlaneToTube, - vec0 = p, - vec1 = r, - vec2 = s, - float0 = t - }); - return this; - } + FoldPipelineBuilder Prs(int opcode, Vector3 p, Vector3 r, Vector3 s, float t) => + AddSlot(new FoldSlot { opcode = opcode, vec0 = p, vec1 = r, vec2 = s, float0 = t }); - public FoldPipelineBuilder PlaneToHemiOctahedron(Vector3 p, Vector3 r, Vector3 s, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.PlaneToHemiOctahedron, - vec0 = p, - vec1 = r, - vec2 = s, - float0 = t - }); - return this; - } + FoldPipelineBuilder Vec1t(int opcode, Vector3 v, float t) => + AddSlot(new FoldSlot { opcode = opcode, vec0 = v, float0 = t }); - public FoldPipelineBuilder Scale(Vector3 k, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.Scale, - vec0 = k, - float0 = t - }); - return this; - } + public FoldPipelineBuilder TubeToPlane(Vector3 p, Vector3 r, Vector3 s, float t) => Prs(TubeToPlaneOp.Opcode, p, r, s, t); + public FoldPipelineBuilder PlaneToTube(Vector3 p, Vector3 r, Vector3 s, float t) => Prs(PlaneToTubeOp.Opcode, p, r, s, t); + public FoldPipelineBuilder PlaneToHemiOctahedron(Vector3 p, Vector3 r, Vector3 s, float t) => Prs(PlaneToHemiOctahedronOp.Opcode, p, r, s, t); + public FoldPipelineBuilder HemiOctahedronToPlane(Vector3 p, Vector3 r, Vector3 s, float t) => Prs(HemiOctahedronToPlaneOp.Opcode, p, r, s, t); + public FoldPipelineBuilder PlaneToOctahedron(Vector3 p, Vector3 r, Vector3 s, float t) => Prs(PlaneToOctahedronOp.Opcode, p, r, s, t); + public FoldPipelineBuilder OctahedronToPlane(Vector3 p, Vector3 r, Vector3 s, float t) => Prs(OctahedronToPlaneOp.Opcode, p, r, s, t); + public FoldPipelineBuilder PointAlign(Vector3 po, Vector3 pp, Vector3 r, float t) => Prs(PointAlignOp.Opcode, po, pp, r, t); + public FoldPipelineBuilder AxisAlign(Vector3 po, Vector3 pp, Vector3 r, float t) => Prs(AxisAlignOp.Opcode, po, pp, r, t); + public FoldPipelineBuilder Scale(Vector3 k, float t) => Vec1t(ScaleOp.Opcode, k, t); + public FoldPipelineBuilder Translate(Vector3 offset, float t) => Vec1t(TranslateOp.Opcode, offset, t); - public FoldPipelineBuilder Translate(Vector3 offset, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.Translate, - vec0 = offset, - float0 = t - }); - return this; - } + public FoldPipelineBuilder Rotate(Vector3 center, Vector3 axis, float angle, float t) => + AddSlot(new FoldSlot { opcode = RotateOp.Opcode, vec0 = center, vec1 = axis, float0 = angle, float1 = t }); - public FoldPipelineBuilder PlaneToOctahedron(Vector3 p, Vector3 r, Vector3 s, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.PlaneToOctahedron, - vec0 = p, - vec1 = r, - vec2 = s, - float0 = t - }); - return this; - } + public FoldPipelineBuilder NormConversion(float inputK, float outputK, float t) => + AddSlot(new FoldSlot { opcode = NormConversionOp.Opcode, float0 = inputK, float1 = outputK, float2 = t }); - public FoldPipelineBuilder OctahedronToPlane(Vector3 p, Vector3 r, Vector3 s, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.OctahedronToPlane, - vec0 = p, - vec1 = r, - vec2 = s, - float0 = t - }); - return this; - } + public FoldPipelineBuilder Seal(float A, float k, float st, float t) => + AddSlot(new FoldSlot { opcode = SealOp.Opcode, float0 = A, float1 = k, float2 = st, float3 = t }); - public FoldPipelineBuilder Rotate(Vector3 center, Vector3 axis, float angle, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.Rotate, - vec0 = center, - vec1 = axis, - float0 = angle, - float1 = t - }); - return this; - } - - public FoldPipelineBuilder HemiOctahedronToPlane(Vector3 p, Vector3 r, Vector3 s, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.HemiOctahedronToPlane, - vec0 = p, - vec1 = r, - vec2 = s, - float0 = t - }); - return this; - } - - public FoldPipelineBuilder PointAlign(Vector3 po, Vector3 pp, Vector3 r, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.PointAlign, - vec0 = po, - vec1 = pp, - vec2 = r, - float0 = t - }); - return this; - } - - public FoldPipelineBuilder AxisAlign(Vector3 po, Vector3 pp, Vector3 r, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.AxisAlign, - vec0 = po, - vec1 = pp, - vec2 = r, - float0 = t - }); - return this; - } - - public FoldPipelineBuilder NormConversion(float inputK, float outputK, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.NormConversion, - float0 = inputK, - float1 = outputK, - float2 = t - }); - return this; - } - - public FoldPipelineBuilder Seal(float A, float k, float st, float t) - { - slots.Add(new FoldSlot - { - opcode = Opcodes.Seal, - float0 = A, - float1 = k, - float2 = st, - float3 = t - }); - return this; - } - - public FoldPipelineBuilder SineWaves() - { - slots.Add(new FoldSlot { opcode = Opcodes.SineWaves }); - return this; - } - - public FoldPipelineBuilder FBM() - { - slots.Add(new FoldSlot { opcode = Opcodes.FBM }); - return this; - } + public FoldPipelineBuilder SineWaves() => AddSlot(new FoldSlot { opcode = SineWavesOp.Opcode }); + public FoldPipelineBuilder FBM() => AddSlot(new FoldSlot { opcode = FBMOp.Opcode }); public FoldPipelineBuilder Custom(int opcode, float f0 = 0, float f1 = 0, float f2 = 0, float f3 = 0, - Vector4? v0 = null, Vector4? v1 = null, Vector4? v2 = null, Vector4? v3 = null) - { - slots.Add(new FoldSlot + Vector4? v0 = null, Vector4? v1 = null, Vector4? v2 = null, Vector4? v3 = null) => + AddSlot(new FoldSlot { opcode = opcode, float0 = f0, float1 = f1, float2 = f2, float3 = f3, @@ -264,8 +106,6 @@ public class FoldPipelineBuilder vec2 = v2 ?? Vector4.zero, vec3 = v3 ?? Vector4.zero }); - return this; - } public void Apply() { @@ -276,31 +116,22 @@ public class FoldPipelineBuilder } if (slots.Count > 16) - { Debug.LogWarning($"Too many operations ({slots.Count}). Only the first 16 will be applied."); - } Undo.RecordObject(targetMaterial, "Apply Vertex Deformation"); targetMaterial.SetFloat("_Vertex_Deformation_Enabled", 1f); - // Clear all slots first to ensure clean state for (int i = 0; i < 16; i++) FoldSlot.ClearSlot(targetMaterial, i); - // Apply active slots for (int i = 0; i < slots.Count && i < 16; i++) slots[i].ApplyToMaterial(targetMaterial, i); EditorUtility.SetDirty(targetMaterial); } - public void Clear() - { - slots.Clear(); - } - + public void Clear() => slots.Clear(); public int Count => slots.Count; - public FoldSlot GetSlot(int index) => index < slots.Count ? slots[index] : null; } -- cgit v1.2.3