yum/3ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum/3ner

yumAdd seal interpolatorb3a533b

master
8.6 KiB212 linesraw
1using System;
2using System.Collections.Generic;
3using System.Reflection;
4using UnityEngine;
5
6namespace Yum.Fold
7{
8    [AttributeUsage(AttributeTargets.Field)]
9    public abstract class JumpbackAttribute : Attribute
10    {
11        public abstract object Apply(object authored);
12    }
13    public sealed class CopyAttribute : JumpbackAttribute
14    {
15        public override object Apply(object authored) => authored;
16    }
17    public sealed class ZeroAttribute : JumpbackAttribute
18    {
19        public override object Apply(object authored) => authored is Vector4 v
20            ? (object)new Vector4(0, 0, 0, v.w) : 0f;
21    }
22    public sealed class OneAttribute : JumpbackAttribute
23    {
24        public override object Apply(object authored) => authored is Vector4 v
25            ? (object)new Vector4(1, 1, 1, v.w) : 1f;
26    }
27
28    // Slot maps a named field to its scalar or vector slot in the shader ABI.
29    [AttributeUsage(AttributeTargets.Field)]
30    sealed class SlotAttribute : Attribute
31    {
32        public readonly int index;
33        public SlotAttribute(int index) { this.index = index; }
34    }
35    [AttributeUsage(AttributeTargets.Class)]
36    sealed class OpcodeAttribute : Attribute
37    {
38        public readonly int value;
39        public OpcodeAttribute(int value) { this.value = value; }
40    }
41
42    public static class FoldOperators
43    {
44        [Opcode(0)] public sealed class None { }
45
46        [Opcode(1)] public sealed class TubeToPlane
47        {
48            [Copy, Slot(0)] public Vector3 Origin = Vector3.zero;
49            [Copy, Slot(1)] public Vector3 RAxis = Vector3.right;
50            [Copy, Slot(2)] public Vector3 SAxis = Vector3.forward;
51            [Zero, Slot(0)] public float T = 1;
52        }
53
54        [Opcode(2)] public sealed class PlaneToTube
55        {
56            [Copy, Slot(0)] public Vector3 Origin = Vector3.zero;
57            [Copy, Slot(1)] public Vector3 RAxis = Vector3.right;
58            [Copy, Slot(2)] public Vector3 SAxis = Vector3.forward;
59            [Zero, Slot(0)] public float T = 1;
60        }
61
62        [Opcode(3)] public sealed class PointAlign
63        {
64            [Copy, Slot(0)] public Vector3 Origin = Vector3.zero;
65            [Copy, Slot(1)] public Vector3 Target = Vector3.up;
66            [Copy, Slot(2)] public Vector3 RAxis = Vector3.right;
67            [Zero, Slot(0)] public float T = 1;
68        }
69
70        [Opcode(4)] public sealed class AxisAlign
71        {
72            [Copy, Slot(0)] public Vector3 Origin = Vector3.zero;
73            [Copy, Slot(1)] public Vector3 Target = Vector3.up;
74            [Copy, Slot(2)] public Vector3 RAxis = Vector3.right;
75            [Zero, Slot(0)] public float T = 1;
76        }
77
78        [Opcode(5)] public sealed class NormConversion
79        {
80            [Copy, Slot(0)] public float InputNorm = 2;
81            [Copy, Slot(1)] public float OutputNorm = 1;
82            [Zero, Slot(2)] public float T = 1;
83        }
84
85        [Opcode(6)] public sealed class Seal
86        {
87            [Copy, Slot(0)] public float Amplitude = .1f;
88            [Copy, Slot(1)] public float Frequency = 2;
89            [Copy, Slot(2)] public float TimeMultiplier = .8f;
90            [Zero, Slot(3)] public float T = 1;
91        }
92
93        [Opcode(7)] public sealed class SineWaves
94        {
95            [Zero, Slot(0)] public Vector3 Amplitude = new Vector3(0, .1f, 0);
96            [Copy, Slot(1)] public Vector3 Direction = Vector3.right;
97            [Copy, Slot(2)] public Vector3 WaveNumber = Vector3.one;
98            [Copy, Slot(3)] public Vector3 AngularVelocity = Vector3.one;
99        }
100
101        [Opcode(8)] public sealed class FBM
102        {
103            [Zero, Slot(0)] public Vector3 Amplitude = Vector3.one * .1f;
104            [Copy, Slot(1)] public Vector3 Velocity = Vector3.zero;
105            [Copy, Slot(2)] public Vector3 Period = Vector3.one;
106            [Copy, Slot(0)] public float Gain = .5f;
107            [Copy, Slot(1)] public float Lacunarity = 2;
108            [Copy, Slot(2)] public float Octaves = 4;
109            [Copy, Slot(3)] public float Scale = 1;
110        }
111
112        [Opcode(9)] public sealed class PlaneToHemiOctahedron
113        {
114            [Copy, Slot(0)] public Vector3 Origin = Vector3.zero;
115            [Copy, Slot(1)] public Vector3 RAxis = Vector3.right;
116            [Copy, Slot(2)] public Vector3 SAxis = Vector3.forward;
117            [Zero, Slot(0)] public float T = 1;
118        }
119
120        [Opcode(10)] public sealed class HemiOctahedronToPlane
121        {
122            [Copy, Slot(0)] public Vector3 Origin = Vector3.zero;
123            [Copy, Slot(1)] public Vector3 RAxis = Vector3.right;
124            [Copy, Slot(2)] public Vector3 SAxis = Vector3.forward;
125            [Zero, Slot(0)] public float T = 1;
126        }
127
128        [Opcode(11)] public sealed class Scale
129        {
130            [One, Slot(0)] public Vector3 Factors = Vector3.one;
131            [Zero, Slot(0)] public float T = 1;
132        }
133
134        [Opcode(12)] public sealed class Translate
135        {
136            [Zero, Slot(0)] public Vector3 Offset = Vector3.zero;
137            [Zero, Slot(0)] public float T = 1;
138        }
139
140        [Opcode(13)] public sealed class PlaneToOctahedron
141        {
142            [Copy, Slot(0)] public Vector3 Origin = Vector3.zero;
143            [Copy, Slot(1)] public Vector3 RAxis = Vector3.right;
144            [Copy, Slot(2)] public Vector3 SAxis = Vector3.forward;
145            [Zero, Slot(0)] public float T = 1;
146        }
147
148        [Opcode(14)] public sealed class OctahedronToPlane
149        {
150            [Copy, Slot(0)] public Vector3 Origin = Vector3.zero;
151            [Copy, Slot(1)] public Vector3 RAxis = Vector3.right;
152            [Copy, Slot(2)] public Vector3 SAxis = Vector3.forward;
153            [Zero, Slot(0)] public float T = 1;
154        }
155
156        [Opcode(15)] public sealed class Rotate
157        {
158            [Copy, Slot(0)] public Vector3 Center = Vector3.zero;
159            [Copy, Slot(1)] public Vector3 Axis = Vector3.up;
160            [Copy, Slot(0)] public float Angle = Mathf.PI / 2; // Radians.
161            [Zero, Slot(1)] public float T = 1;
162        }
163
164        sealed class Definition
165        {
166            public FoldOperator defaults;
167            public readonly Dictionary<FieldInfo, JumpbackAttribute> policies = new Dictionary<FieldInfo, JumpbackAttribute>();
168        }
169
170        // Cache mappings once. Serialized slots and animation property names stay stable.
171        static readonly Dictionary<int, Definition> Definitions = BuildDefinitions();
172        static Dictionary<int, Definition> BuildDefinitions()
173        {
174            var definitions = new Dictionary<int, Definition>();
175            foreach (var type in typeof(FoldOperators).GetNestedTypes(BindingFlags.Public))
176            {
177                var opcode = type.GetCustomAttribute<OpcodeAttribute>();
178                if (opcode == null) continue;
179                var source = Activator.CreateInstance(type);
180                var definition = new Definition { defaults = new FoldOperator { enabled = true, opcode = opcode.value } };
181                foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
182                {
183                    var slot = field.GetCustomAttribute<SlotAttribute>();
184                    var policy = field.GetCustomAttribute<JumpbackAttribute>();
185                    bool vector = field.FieldType == typeof(Vector3);
186                    if (slot == null || policy == null || slot.index < 0 || slot.index > 3 ||
187                        (!vector && field.FieldType != typeof(float)))
188                        throw new InvalidOperationException($"Invalid Fold field definition: {type.Name}.{field.Name}");
189                    var packed = typeof(FoldOperator).GetField((vector ? "vector" : "float") + slot.index);
190                    object value = field.GetValue(source);
191                    packed.SetValue(definition.defaults, vector ? (object)(Vector4)(Vector3)value : value);
192                    definition.policies.Add(packed, policy);
193                }
194                definitions.Add(opcode.value, definition);
195            }
196            return definitions;
197        }
198
199        public static FoldOperator Defaults(int opcode) => Definitions.TryGetValue(opcode, out var definition)
200            ? definition.defaults.Copy() : new FoldOperator { enabled = true, opcode = opcode };
201
202        public static FoldOperator Jumpback(FoldOperator authored)
203        {
204            // Opcode, enabled, unused fields and vector w components retain their authored values.
205            var result = authored.Copy();
206            if (Definitions.TryGetValue(authored.opcode, out var definition))
207                foreach (var entry in definition.policies)
208                    entry.Key.SetValue(result, entry.Value.Apply(entry.Key.GetValue(authored)));
209            return result;
210        }
211    }
212}