summaryrefslogtreecommitdiffstats
path: root/Scripts/Fold
diff options
context:
space:
mode:
Diffstat (limited to 'Scripts/Fold')
-rw-r--r--Scripts/Fold/Editor/FoldEditorWindow.cs564
-rw-r--r--Scripts/Fold/Editor/FoldPipelineBuilder.cs14
2 files changed, 578 insertions, 0 deletions
diff --git a/Scripts/Fold/Editor/FoldEditorWindow.cs b/Scripts/Fold/Editor/FoldEditorWindow.cs
new file mode 100644
index 0000000..ab02781
--- /dev/null
+++ b/Scripts/Fold/Editor/FoldEditorWindow.cs
@@ -0,0 +1,564 @@
+using System;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+public class FoldEditorWindow : EditorWindow
+{
+ [SerializeField] Material targetMaterial;
+ [SerializeField] Vector2 scrollPos;
+ [SerializeReference] List<DeformOperation> operations = new();
+ [SerializeField] List<int> expandedOps = new();
+ [SerializeField] bool liveUpdate = true;
+
+ [MenuItem("Tools/yum_food/Fold")]
+ static void ShowWindow()
+ {
+ var window = GetWindow<FoldEditorWindow>("Fold");
+ window.minSize = new Vector2(400, 300);
+ }
+
+ void OnEnable()
+ {
+ // Operations list is restored via SerializeReference
+ }
+
+ void OnGUI()
+ {
+ EditorGUILayout.Space(5);
+
+ DrawHeader();
+ EditorGUILayout.Space(10);
+
+ if (targetMaterial == null)
+ {
+ EditorGUILayout.HelpBox("Select a material to build deformation pipelines", MessageType.Info);
+ return;
+ }
+
+ DrawToolbar();
+ EditorGUILayout.Space(5);
+
+ EditorGUI.BeginChangeCheck();
+
+ scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
+ DrawOperationsList();
+ EditorGUILayout.EndScrollView();
+
+ if (EditorGUI.EndChangeCheck() && liveUpdate && targetMaterial != null)
+ ApplyToMaterial();
+
+ EditorGUILayout.Space(5);
+ DrawFooter();
+ }
+
+ void DrawHeader()
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Target Material", EditorStyles.boldLabel, GUILayout.Width(100));
+
+ var newMat = EditorGUILayout.ObjectField(targetMaterial, typeof(Material), false) as Material;
+ if (newMat != targetMaterial)
+ {
+ targetMaterial = newMat;
+ if (targetMaterial != null)
+ LoadFromMaterial();
+ }
+
+ EditorGUILayout.EndHorizontal();
+ }
+
+ void DrawToolbar()
+ {
+ EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
+
+ bool pipelineFull = operations.Count >= 16;
+ using (new EditorGUI.DisabledScope(pipelineFull))
+ {
+ var content = pipelineFull
+ ? new GUIContent("Add Operation", "Pipeline full (16/16)")
+ : new GUIContent("Add Operation");
+ if (GUILayout.Button(content, EditorStyles.toolbarDropDown, GUILayout.Width(120)))
+ ShowAddOperationMenu();
+ }
+
+ GUILayout.FlexibleSpace();
+
+ liveUpdate = GUILayout.Toggle(liveUpdate, "Live", EditorStyles.toolbarButton, GUILayout.Width(40));
+
+ if (GUILayout.Button("Load Presets", EditorStyles.toolbarDropDown, GUILayout.Width(100)))
+ ShowPresetsMenu();
+
+ EditorGUILayout.EndHorizontal();
+ }
+
+ void DrawOperationsList()
+ {
+ if (operations.Count == 0)
+ {
+ EditorGUILayout.HelpBox("No operations. Click 'Add Operation' to begin.", MessageType.Info);
+ return;
+ }
+
+ for (int i = 0; i < operations.Count; i++)
+ {
+ DrawOperation(i);
+ EditorGUILayout.Space(3);
+ }
+ }
+
+ void DrawOperation(int index)
+ {
+ var op = operations[index];
+ bool isExpanded = expandedOps.Contains(index);
+
+ var bgColor = isExpanded ? new Color(0.3f, 0.5f, 0.8f, 0.3f) : new Color(0.2f, 0.2f, 0.2f, 0.3f);
+
+ EditorGUILayout.BeginVertical(GUI.skin.box);
+ GUI.backgroundColor = bgColor;
+
+ // Header with drag handle and delete button
+ EditorGUILayout.BeginHorizontal();
+
+ // Drag handle
+ GUILayout.Label($"#{index}", GUILayout.Width(30));
+
+ // Operation name (clickable to expand/collapse)
+ if (GUILayout.Button(op.GetDisplayName(), EditorStyles.boldLabel))
+ {
+ if (isExpanded) expandedOps.Remove(index);
+ else expandedOps.Add(index);
+ }
+
+ GUILayout.FlexibleSpace();
+
+ // Move up/down
+ GUI.enabled = index > 0;
+ if (GUILayout.Button("▲", GUILayout.Width(25)))
+ {
+ operations.RemoveAt(index);
+ operations.Insert(index - 1, op);
+ if (expandedOps.Contains(index))
+ {
+ expandedOps.Remove(index);
+ expandedOps.Add(index - 1);
+ }
+ }
+
+ GUI.enabled = index < operations.Count - 1;
+ if (GUILayout.Button("▼", GUILayout.Width(25)))
+ {
+ operations.RemoveAt(index);
+ operations.Insert(index + 1, op);
+ if (expandedOps.Contains(index))
+ {
+ expandedOps.Remove(index);
+ expandedOps.Add(index + 1);
+ }
+ }
+
+ GUI.enabled = true;
+
+ // Delete button
+ if (GUILayout.Button("×", GUILayout.Width(25)))
+ {
+ operations.RemoveAt(index);
+ expandedOps.Remove(index);
+ // Adjust indices in expandedOps
+ for (int i = 0; i < expandedOps.Count; i++)
+ {
+ if (expandedOps[i] > index) expandedOps[i]--;
+ }
+ }
+
+ EditorGUILayout.EndHorizontal();
+
+ // Draw parameters when expanded
+ if (isExpanded)
+ {
+ EditorGUILayout.Space(5);
+ EditorGUI.indentLevel++;
+ op.DrawParameters();
+ EditorGUI.indentLevel--;
+ }
+
+ GUI.backgroundColor = Color.white;
+ EditorGUILayout.EndVertical();
+ }
+
+ void DrawFooter()
+ {
+ EditorGUILayout.BeginHorizontal();
+
+ if (GUILayout.Button("Apply to Material", GUILayout.Height(30)))
+ ApplyToMaterial();
+
+ if (GUILayout.Button("Clear All", GUILayout.Height(30), GUILayout.Width(100)))
+ {
+ operations.Clear();
+ expandedOps.Clear();
+ }
+
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.Space(5);
+ EditorGUILayout.LabelField($"Operations: {operations.Count}/16", EditorStyles.miniLabel);
+ }
+
+ 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("Plane to Hemi-Octahedron"), false, () => AddOperation(new PlaneToHemiOctahedronOp()));
+ menu.AddItem(new GUIContent("Hemi-Octahedron to Plane"), false, () => AddOperation(new HemiOctahedronToPlaneOp()));
+ menu.AddSeparator("");
+ 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("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.ShowAsContext();
+ }
+
+ void ShowPresetsMenu()
+ {
+ var menu = new GenericMenu();
+
+ menu.AddItem(new GUIContent("Tube to Plane"), false, () => LoadPreset_TubeToPlane());
+ menu.AddItem(new GUIContent("Plane to Tube"), false, () => LoadPreset_PlaneToTube());
+ menu.AddItem(new GUIContent("Plane to Hemi-Octahedron"), false, () => LoadPreset_PlaneToHemiOct());
+ menu.AddSeparator("");
+ menu.AddItem(new GUIContent("Norm: L1→L2 (Diamond→Sphere)"), false, () => LoadPreset_NormL1L2());
+ menu.AddItem(new GUIContent("Norm: L2→L1 (Sphere→Diamond)"), false, () => LoadPreset_NormL2L1());
+ menu.AddItem(new GUIContent("Norm: L2→L∞ (Sphere→Cube)"), false, () => LoadPreset_NormL2Linf());
+ menu.AddSeparator("");
+ menu.AddItem(new GUIContent("Complex/Tube→Plane + Norm Conv"), false, () => LoadPreset_TubePlaneNorm());
+
+ menu.ShowAsContext();
+ }
+
+ void AddOperation(DeformOperation op)
+ {
+ if (operations.Count >= 16)
+ return;
+ operations.Add(op);
+ expandedOps.Add(operations.Count - 1);
+ }
+
+ void LoadPreset_TubeToPlane()
+ {
+ operations.Clear();
+ expandedOps.Clear();
+ AddOperation(new TubeToPlaneOp());
+ }
+
+ void LoadPreset_PlaneToTube()
+ {
+ operations.Clear();
+ expandedOps.Clear();
+ AddOperation(new PlaneToTubeOp());
+ }
+
+ void LoadPreset_PlaneToHemiOct()
+ {
+ operations.Clear();
+ expandedOps.Clear();
+ AddOperation(new PlaneToHemiOctahedronOp());
+ }
+
+ void LoadPreset_NormL1L2()
+ {
+ operations.Clear();
+ expandedOps.Clear();
+ var op = new NormConversionOp();
+ op.inputK = 1f;
+ op.outputK = 2f;
+ AddOperation(op);
+ }
+
+ void LoadPreset_NormL2L1()
+ {
+ operations.Clear();
+ expandedOps.Clear();
+ var op = new NormConversionOp();
+ op.inputK = 2f;
+ op.outputK = 1f;
+ AddOperation(op);
+ }
+
+ void LoadPreset_NormL2Linf()
+ {
+ operations.Clear();
+ expandedOps.Clear();
+ var op = new NormConversionOp();
+ op.inputK = 2f;
+ op.outputK = float.PositiveInfinity;
+ AddOperation(op);
+ }
+
+ void LoadPreset_TubePlaneNorm()
+ {
+ operations.Clear();
+ expandedOps.Clear();
+ AddOperation(new TubeToPlaneOp());
+ var norm = new NormConversionOp();
+ norm.inputK = 2f;
+ norm.outputK = 1f;
+ AddOperation(norm);
+ }
+
+ void ApplyToMaterial()
+ {
+ var builder = FoldPipelineBuilder.Create().For(targetMaterial);
+
+ foreach (var op in operations)
+ op.ApplyTo(builder);
+
+ builder.Apply();
+ }
+
+ void LoadFromMaterial()
+ {
+ // Could potentially read back from material properties here
+ // For now, just clear the list
+ operations.Clear();
+ expandedOps.Clear();
+ }
+}
+
+// Base class for deformation operations
+[System.Serializable]
+public abstract class DeformOperation
+{
+ public abstract string GetDisplayName();
+ public abstract 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 override string GetDisplayName() => "Tube 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.TubeToPlane(p, r, s, t);
+}
+
+[System.Serializable]
+public class PlaneToTubeOp : DeformOperation
+{
+ public Vector3 p = Vector3.zero;
+ public Vector3 r = Vector3.right;
+ public Vector3 s = Vector3.forward;
+ public float t = 1f;
+
+ 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);
+}
+
+[System.Serializable]
+public class PlaneToHemiOctahedronOp : DeformOperation
+{
+ public Vector3 p = Vector3.zero;
+ public Vector3 r = Vector3.right;
+ public Vector3 s = Vector3.forward;
+ public float t = 1f;
+
+ 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);
+}
+
+[System.Serializable]
+public class HemiOctahedronToPlaneOp : DeformOperation
+{
+ public Vector3 p = Vector3.zero;
+ public Vector3 r = Vector3.right;
+ public Vector3 s = Vector3.forward;
+ public float t = 1f;
+
+ 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);
+}
+
+[System.Serializable]
+public class PointAlignOp : DeformOperation
+{
+ public Vector3 po = Vector3.zero;
+ public Vector3 pp = Vector3.up;
+ public Vector3 r = Vector3.right;
+ public float t = 1f;
+
+ public override string GetDisplayName() => "Point Align";
+
+ 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);
+ }
+
+ public override void ApplyTo(FoldPipelineBuilder builder) =>
+ builder.PointAlign(po, pp, r, t);
+}
+
+[System.Serializable]
+public class AxisAlignOp : DeformOperation
+{
+ public Vector3 po = Vector3.zero;
+ public Vector3 pp = Vector3.up;
+ public Vector3 r = Vector3.right;
+ public float t = 1f;
+
+ public override string GetDisplayName() => "Axis Align";
+
+ 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);
+ }
+
+ public override void ApplyTo(FoldPipelineBuilder builder) =>
+ builder.AxisAlign(po, pp, r, t);
+}
+
+[System.Serializable]
+public class NormConversionOp : DeformOperation
+{
+ public float inputK = 2f;
+ public float outputK = 1f;
+ public float t = 1f;
+
+ 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");
+ }
+
+ public override void DrawParameters()
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Input Norm (k)", GUILayout.Width(120));
+ inputK = EditorGUILayout.FloatField(inputK);
+ if (GUILayout.Button("L∞", GUILayout.Width(30))) inputK = float.PositiveInfinity;
+ EditorGUILayout.EndHorizontal();
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Output Norm (k)", GUILayout.Width(120));
+ outputK = EditorGUILayout.FloatField(outputK);
+ if (GUILayout.Button("L∞", GUILayout.Width(30))) outputK = float.PositiveInfinity;
+ EditorGUILayout.EndHorizontal();
+
+ t = EditorGUILayout.Slider("Interpolation (t)", t, 0f, 1f);
+
+ EditorGUILayout.HelpBox(
+ "Common: L1=diamond, L2=sphere, L∞=cube",
+ MessageType.Info);
+ }
+
+ public override void ApplyTo(FoldPipelineBuilder builder) =>
+ builder.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 override string GetDisplayName() => "Seal";
+
+ public override void DrawParameters()
+ {
+ 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);
+ }
+
+ public override void ApplyTo(FoldPipelineBuilder builder) =>
+ builder.Seal(A, k, st, t);
+}
+
+[System.Serializable]
+public class SineWavesOp : DeformOperation
+{
+ 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();
+}
+
+[System.Serializable]
+public class FBMOp : DeformOperation
+{
+ 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();
+}
diff --git a/Scripts/Fold/Editor/FoldPipelineBuilder.cs b/Scripts/Fold/Editor/FoldPipelineBuilder.cs
index 64032a5..3731cf8 100644
--- a/Scripts/Fold/Editor/FoldPipelineBuilder.cs
+++ b/Scripts/Fold/Editor/FoldPipelineBuilder.cs
@@ -57,6 +57,7 @@ public class FoldPipelineBuilder
public const int SineWaves = 7;
public const int FBM = 8;
public const int PlaneToHemiOctahedron = 9;
+ public const int HemiOctahedronToPlane = 10;
}
FoldPipelineBuilder() { }
@@ -108,6 +109,19 @@ public class FoldPipelineBuilder
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