1 /**
2     Inochi2D Mesh Deformer Node
3 
4     Copyright: 
5         Copyright © 2020-2026, Inochi2D Project
6     
7     License:
8         $(LINK2 https://github.com/Inochi2D/inochi2d/blob/main/LICENSE, BSD 2-clause License)
9     
10     Authors:
11         Luna Nielsen
12         seagetch
13 */
14 module inochi2d.nodes.deformer.meshdeformer;
15 import inochi2d.nodes.deformer;
16 import inochi2d.nodes;
17 import inochi2d.common;
18 import inochi2d.core;
19 import nulib.string;
20 import numem;
21 
22 import inochi2d.core.math.simd;
23 import inteli;
24 
25 alias MeshDeformerLUT = DeformerLUT!((DeformedMesh src, IDeformable target) @nogc {
26     ptrdiff_t[2][] mappings = nu_malloca!(ptrdiff_t[2])(target.deformPoints.length);
27     foreach (j; 0 .. mappings.length) {
28         vec2 mp = target.deformPoints[j];
29 
30         mappings[j] = [-1, -1];
31         foreach (k; 0 .. src.elementCount / 3) {
32             uint[3] idx = [
33                 src.indices[(k * 3) + 0],
34                 src.indices[(k * 3) + 1],
35                 src.indices[(k * 3) + 2],
36             ];
37             Triangle tri = Triangle(
38                 src.points[idx[0]],
39                 src.points[idx[1]],
40                 src.points[idx[2]],
41             );
42 
43             // Do some cheaper checks first.
44             float minX = min(min(tri.p1.x, tri.p2.x), tri.p3.x);
45             float maxX = max(max(tri.p1.x, tri.p2.x), tri.p3.x);
46             float minY = min(min(tri.p1.y, tri.p2.y), tri.p3.y);
47             float maxY = max(max(tri.p1.y, tri.p2.y), tri.p3.y);
48             if (!(minX < mp.x && maxX > mp.x) &&
49                 !(minY < mp.y && maxY > mp.y))
50                 continue;
51 
52             // Mapping found, add it!
53             mappings[j] = [k * 3, j];
54             break;
55         }
56     }
57 
58     return mappings;
59 });
60 
61 /**
62     A deformer which deforms child nodes stored within it,
63 */
64 @TypeId("MeshDeformer", IN_MAKE_TAG!(1, 2))  // Modern name
65 @TypeId("MeshGroup", IN_MAKE_TAG!(1, 2))  // Legacy name
66 class MeshDeformer : Deformer {
67 private:
68     Mesh mesh_;
69     DeformedMesh base_;
70     DeformedMesh deformed_;
71     vec2[] deformDeltas_;
72 
73     // Accelleration structures
74     MeshDeformerLUT[] luts_;
75     vec2[] deformBuffer_;
76 
77 protected:
78 
79     /**
80         Serializes this node to a DataNode.
81 
82         Params:
83             object =    The DataNode to serialize to.
84     */
85     override
86     void onSerialize(ref DataNode object) {
87         super.onSerialize(object);
88 
89         // NOTE:    MeshData is set up to free its contents on
90         //          scope exit.
91         MeshData data = MeshData(mesh);
92         object["mesh"] = data.serialize();
93     }
94 
95     /**
96         Deserializes this node from a DataNode.
97 
98         Params:
99             object =    The DataNode to deserialize from.
100             state =     The state of the deserializer.
101     */
102     override
103     void onDeserialize(ref DataNode object, ref ModelState state) {
104         super.onDeserialize(object, state);
105 
106         this.deformed_ = nogc_new!DeformedMesh();
107         this.base_ = nogc_new!DeformedMesh();
108         auto meshData = object.tryGet!MeshData(state, "mesh");
109         this.mesh = Mesh.fromMeshData(meshData);
110 
111         if (state.doUpgrade08 && !object.tryGet(state, "dynamic_deformation", false)) {
112             state.warning(nstring(this.name[], " uses static deformation, this was removed in 0.9..."));
113         }
114 
115         if (state.doUpgrade08 && object.tryGet(state, "translate_children", false)) {
116             state.warning(nstring(this.name[], " translates its children via deformation, this was removed in 0.9..."));
117         }
118     }
119 
120     /**
121         Called during the early update phase of a new frame.
122         
123         Params:
124             drawList =  The drawlist for the active scene.
125     */
126     override
127     void onPreUpdate(DrawList drawList) {
128         super.onPreUpdate(drawList);
129         this.resetDeform();
130     }
131 
132     /**
133         Called during the update phase of a new frame.
134         
135         Params:
136             delta =     Time since the last frame.
137             drawList =  The drawlist for the active scene.
138     */
139     override
140     void onUpdate(float delta, DrawList drawList) {
141         base_.pushMatrix(this.deformMatrix);
142         deformed_.pushMatrix(this.deformMatrix);
143         super.onUpdate(delta, drawList);
144     }
145 
146     /**
147         Called during the late update phase of a new frame.
148         
149         Params:
150             drawList =  The drawlist for the active scene.
151     */
152     override
153     void onPostUpdate(DrawList drawList) {
154 
155         // No deltas?
156         if (deformDeltas_.length == 0) {
157             super.onPostUpdate(drawList);
158             return;
159         }
160 
161         // Calculate the deltas from the world matrix.
162         simd_meshcopy(deformDeltas_, base_.points);
163         simd_sub(deformDeltas_, deformed_.points);
164         foreach (i, mesh; toDeform) {
165             size_t w_length = nu_min(deformBuffer_.length, mesh.deformPoints.length);
166             deformBuffer_[0 .. w_length] = vec2(0, 0);
167 
168             // Setup temporary buffer.
169             foreach (entry; luts_[i].entries) {
170 
171                 // Skip vertices out of bounds.
172                 if (entry[0] < 0 || entry[1] < 0 || entry[1] >= w_length)
173                     continue;
174 
175                 size_t p0 = mesh_.indices[entry[0] + 0];
176                 size_t p1 = mesh_.indices[entry[0] + 1];
177                 size_t p2 = mesh_.indices[entry[0] + 2];
178 
179                 // Build triangle from start index.
180                 Triangle tri = Triangle(
181                         deformed_.points[p0],
182                         deformed_.points[p1],
183                         deformed_.points[p2],
184                 );
185 
186                 vec3 bc = tri.barycentric(mesh.deformPoints[entry[1]]);
187                 deformBuffer_[entry[1]] = -(
188                         (deformDeltas_[p0] * bc.x) +
189                         (deformDeltas_[p1] * bc.y) +
190                         (deformDeltas_[p2] * bc.z)
191                 );
192             }
193 
194             mesh.deform(deformBuffer_[0 .. w_length]);
195         }
196 
197         super.onPostUpdate(drawList);
198     }
199 
200     /**
201         Called when the deformer's internal data should be
202         rebuilt.
203     */
204     override
205     void onRebuild() {
206         super.onRebuild();
207 
208         // Delete old LUTs
209         if (luts_)
210             nu_freea(luts_);
211 
212         // Find children and rebuild.
213         this.luts_ = nu_malloca!MeshDeformerLUT(toDeform.length);
214         foreach (i, target; toDeform) {
215             luts_[i].rebuild(deformed_, target);
216 
217             // Resize temporary deformation buffer.
218             if (target.deformPoints.length > deformBuffer_.length)
219                 deformBuffer_ = deformBuffer_.nu_resize(target.deformPoints.length);
220         }
221     }
222 
223 public:
224 
225     /**
226         The mesh
227     */
228     @property Mesh mesh() @nogc => mesh_;
229     final @property void mesh(Mesh value) @nogc {
230         if (value is mesh_)
231             return;
232 
233         if (mesh_)
234             mesh_.release();
235 
236         this.mesh_ = value.retained();
237         this.deformDeltas_ = deformDeltas_.nu_resize(mesh_.vertexCount);
238 
239         this.base_.parent = value;
240         this.deformed_.parent = value;
241 
242         this.base_.reset();
243         this.base_.pushMatrix(this.deformBaseMatrix);
244     }
245 
246     /**
247         The control points of the deformer.
248     */
249     override @property vec2[] controlPoints() @nogc => deformed_.points;
250     override @property void controlPoints(vec2[] value) @nogc {
251         import nulib.math : min;
252 
253         size_t m = min(value.length, deformed_.points.length);
254         deformed_.points[0 .. m] = value[0 .. m];
255     }
256 
257     /**
258         The base position of the deformable's points, in world space.
259     */
260     override @property const(vec2)[] basePoints() @nogc => base_.points;
261 
262     /**
263         The points which may be deformed by a deformer, in world space.
264     */
265     override @property vec2[] deformPoints() @nogc => deformed_.points;
266 
267     // Destructor
268     ~this() {
269         nu_freea(deformDeltas_);
270         nogc_delete(deformed_);
271         nogc_delete(base_);
272         mesh_.release();
273     }
274 
275     /**
276         Constructs a new MeshGroup node
277     */
278     this(Node parent = null) {
279         super(parent);
280     }
281 
282     /**
283         Deforms the IDeformable.
284 
285         Params:
286             deformed =  The deformation delta.
287             absolute =  Whether the deformation is absolute,
288                         replacing the original deformation.
289     */
290     override
291     void deform(vec2[] deformed, bool absolute = false) {
292         deformed_.deform(deformed);
293     }
294 
295     /**
296         Resets the deformation for the IDeformable.
297     */
298     override
299     void resetDeform() {
300         deformed_.reset();
301         base_.reset();
302     }
303 }
304 
305 mixin Register!(MeshDeformer, in_node_registry);