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; static string Prefix(int i) => $"_Vertex_Deformation_Slot_{i}_"; void ApplyToTarget(string pfx, Action sf, Action si, Action sv) { 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); } static void ClearTarget(string pfx, Action sf, Action si, Action sv) { 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 { readonly List slots = new(); Material targetMaterial; FoldPipelineBuilder() { } public static FoldPipelineBuilder Create() => new(); public FoldPipelineBuilder For(Material material) { targetMaterial = material; return this; } FoldPipelineBuilder AddSlot(FoldSlot slot) { slots.Add(slot); 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 }); FoldPipelineBuilder Vec1t(int opcode, Vector3 v, float t) => AddSlot(new FoldSlot { opcode = opcode, vec0 = v, float0 = t }); 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 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 NormConversion(float inputK, float outputK, float t) => AddSlot(new FoldSlot { opcode = NormConversionOp.Opcode, float0 = inputK, float1 = outputK, float2 = t }); 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 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) => AddSlot(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 }); 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); for (int i = 0; i < 16; i++) FoldSlot.ClearSlot(targetMaterial, i); 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 FoldSlot GetSlot(int index) => index < slots.Count ? slots[index] : null; }