summaryrefslogtreecommitdiffstats
path: root/Scripts/Fold
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-02-19 23:09:29 -0800
committeryum <yum.food.vr@gmail.com>2026-02-20 00:42:58 -0800
commit1883b902eebf0587850db8bf972fc68d87b91197 (patch)
tree7b2af84f9ca055a171c02b8ce8ad1208782fcabb /Scripts/Fold
parentb536da97225c962885cd87c37c269b2b7c524005 (diff)
Fold: simplify code
Diffstat (limited to 'Scripts/Fold')
-rwxr-xr-xScripts/Fold/Editor/FoldEditorWindow.cs760
-rwxr-xr-xScripts/Fold/Editor/FoldPipelineBuilder.cs295
2 files changed, 320 insertions, 735 deletions
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<int>(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<int>(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<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)
- : "";
+ 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<Renderer>();
- 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<Animator>();
- 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<Renderer>();
+ if (renderer == null) return false;
+ if (!TryGetAnimationWindowState(out clip, out time)) return false;
+ var animator = targetObject.GetComponentInParent<Animator>();
+ 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<string, float> sf, Action<string, int> si, Action<string, Vector4> 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<string, float> sf, Action<string, int> si, Action<string, Vector4> 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<FoldSlot> 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;
}