using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; public class FoldSlot { public int opcode; public float float0, float1, float2, float3; public Vector4 vec0, vec1, vec2, vec3; public void ApplyToMaterial(Material mat, int slotIndex) { 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); } public static void ClearSlot(Material mat, int slotIndex) { 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); } } 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; } FoldPipelineBuilder() { } public static FoldPipelineBuilder Create() => new(); 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; } 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; } 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; } 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 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 { opcode = opcode, float0 = f0, float1 = f1, float2 = f2, float3 = f3, vec0 = v0 ?? Vector4.zero, vec1 = v1 ?? Vector4.zero, vec2 = v2 ?? Vector4.zero, vec3 = v3 ?? Vector4.zero }); return this; } public void Apply() { if (targetMaterial == null) { Debug.LogError("No target material set. Use .For(material) before .Apply()"); return; } 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 int Count => slots.Count; } public static class FoldPresets { public static void TubeToPlaneFull(Material mat) => FoldPipelineBuilder.Create() .For(mat) .TubeToPlane(Vector3.zero, Vector3.right, Vector3.forward, 1f) .Apply(); public static void PlaneToTubeFull(Material mat) => FoldPipelineBuilder.Create() .For(mat) .PlaneToTube(Vector3.zero, Vector3.right, Vector3.forward, 1f) .Apply(); public static void PlaneToHemiOctahedronFull(Material mat) => FoldPipelineBuilder.Create() .For(mat) .PlaneToHemiOctahedron(Vector3.zero, Vector3.right, Vector3.forward, 1f) .Apply(); public static void TubeToPlaneThenNormConv(Material mat) => FoldPipelineBuilder.Create() .For(mat) .TubeToPlane(Vector3.zero, Vector3.right, Vector3.forward, 1f) .NormConversion(2f, 1f, 1f) .Apply(); public static void NormConvL1ToL2(Material mat) => FoldPipelineBuilder.Create() .For(mat) .NormConversion(1f, 2f, 1f) .Apply(); public static void NormConvL2ToL1(Material mat) => FoldPipelineBuilder.Create() .For(mat) .NormConversion(2f, 1f, 1f) .Apply(); public static void NormConvL2ToLinf(Material mat) => FoldPipelineBuilder.Create() .For(mat) .NormConversion(2f, float.PositiveInfinity, 1f) .Apply(); }