1 /*
2     Inochi2D MeshGroup Node
3 
4     Copyright © 2020, Inochi2D Project
5     Distributed under the 2-Clause BSD License, see LICENSE file.
6     
7     Authors: Luna Nielsen
8 */
9 module inochi2d.core.nodes.meshgroup;
10 import inochi2d.core.nodes.drawable;
11 import inochi2d.integration;
12 import inochi2d.fmt.serialize;
13 import inochi2d.math;
14 import inochi2d.math.triangle;
15 import std.exception;
16 import inochi2d.core.dbg;
17 import inochi2d.core;
18 import std.typecons: tuple, Tuple;
19 import std.stdio;
20 
21 package(inochi2d) {
22     void inInitMeshGroup() {
23         inRegisterNodeType!MeshGroup;
24     }
25 }
26 
27 
28 private {
29 struct Triangle{
30     mat3 offsetMatrices;
31     mat3 transformMatrix;
32 }
33 }
34 
35 /**
36     Contains various deformation shapes that can be applied to
37     children of this node
38 */
39 @TypeId("MeshGroup")
40 class MeshGroup : Drawable {
41 protected:
42     ushort[] bitMask;
43     vec4 bounds;
44     Triangle[] triangles;
45     vec2[] transformedVertices = [];
46     mat4 forwardMatrix;
47     mat4 inverseMatrix;
48     bool translateChildren = true;
49 
50     override
51     string typeId() { return "MeshGroup"; }
52 
53     bool precalculated = false;
54 
55     override
56     void preProcess() {
57         super.preProcess();
58     }
59 
60     override
61     void postProcess() {
62         super.preProcess();
63     }
64 
65 public:
66     bool dynamic = false;
67 
68     /**
69         Constructs a new MeshGroup node
70     */
71     this(Node parent = null) {
72         super(parent);
73     }
74 
75     Tuple!(vec2[], mat4*) filterChildren(vec2[] origVertices, vec2[] origDeformation, mat4* origTransform) {
76         if (!precalculated)
77             return Tuple!(vec2[], mat4*)(null, null);
78 
79         mat4 centerMatrix = inverseMatrix * (*origTransform);
80 
81         // Transform children vertices in MeshGroup coordinates.
82         auto r = rect(bounds.x, bounds.y, (ceil(bounds.z) - floor(bounds.x) + 1), (ceil(bounds.w) - floor(bounds.y) + 1));
83         foreach(i, vertex; origVertices) {
84             vec2 cVertex;
85             if (dynamic)
86                 cVertex = vec2(centerMatrix * vec4(vertex+origDeformation[i], 0, 1));
87             else
88                 cVertex = vec2(centerMatrix * vec4(vertex, 0, 1));
89             int index = -1;
90             if (bounds.x <= cVertex.x && cVertex.x < bounds.z && bounds.y <= cVertex.y && cVertex.y < bounds.w) {
91                 ushort bit = bitMask[cast(int)(cVertex.y - bounds.y) * cast(int)r.width + cast(int)(cVertex.x - bounds.x)];
92                 index = bit - 1;
93             }
94             vec2 newPos = (index < 0)? cVertex: (triangles[index].transformMatrix * vec3(cVertex, 1)).xy;
95             if (!dynamic) {
96                 mat4 inv = centerMatrix.inverse;
97                 inv[0][3] = 0;
98                 inv[1][3] = 0;
99                 inv[2][3] = 0;
100                 origDeformation[i] += (inv * vec4(newPos - cVertex, 0, 1)).xy;
101             } else
102                 origDeformation[i] = newPos - origVertices[i];
103         }
104 
105         if (!dynamic)
106             return tuple(origDeformation, cast(mat4*)null);
107         return tuple(origDeformation, &forwardMatrix);
108     }
109 
110     /**
111         A list of the shape offsets to apply per part
112     */
113     override
114     void update() {
115         preProcess();
116         deformStack.update();
117         
118         if (data.indices.length > 0) {
119             if (!precalculated) {
120                 precalculate();
121             }
122             transformedVertices.length = vertices.length;
123             foreach(i, vertex; vertices) {
124                 transformedVertices[i] = vertex+this.deformation[i];
125             }
126             foreach (index; 0..triangles.length) {
127                 auto p1 = transformedVertices[data.indices[index * 3]];
128                 auto p2 = transformedVertices[data.indices[index * 3 + 1]];
129                 auto p3 = transformedVertices[data.indices[index * 3 + 2]];
130                 triangles[index].transformMatrix = mat3([p2.x - p1.x, p3.x - p1.x, p1.x,
131                                                         p2.y - p1.y, p3.y - p1.y, p1.y,
132                                                         0, 0, 1]) * triangles[index].offsetMatrices;
133             }
134             forwardMatrix = transform.matrix;
135             inverseMatrix = globalTransform.matrix.inverse;
136         }
137 
138         Node.update();
139         this.updateDeform();
140    }
141 
142     override
143     void draw() {
144         super.draw();
145     }
146 
147 
148     void precalculate() {
149         if (data.indices.length == 0) {
150             triangles.length = 0;
151             bitMask.length   = 0;
152             return;
153         }
154 
155         vec4 getBounds(T)(ref T vertices) {
156             vec4 bounds = vec4(float.max, float.max, -float.max, -float.max);
157             foreach (v; vertices) {
158                 bounds = vec4(min(bounds.x, v.x), min(bounds.y, v.y), max(bounds.z, v.x), max(bounds.w, v.y));
159             }
160             bounds.x = floor(bounds.x);
161             bounds.y = floor(bounds.y);
162             bounds.z = ceil(bounds.z);
163             bounds.w = ceil(bounds.w);
164             return bounds;
165         }
166 
167         // Calculating conversion matrix for triangles
168         bounds = getBounds(data.vertices);
169         triangles.length = 0;
170         foreach (i; 0..data.indices.length / 3) {
171             Triangle t;
172             vec2[3] tvertices = [
173                 data.vertices[data.indices[3*i]],
174                 data.vertices[data.indices[3*i+1]],
175                 data.vertices[data.indices[3*i+2]]
176             ];
177             
178             vec2* p1 = &tvertices[0];
179             vec2* p2 = &tvertices[1];
180             vec2* p3 = &tvertices[2];
181 
182             vec2 axis0 = *p2 - *p1;
183             float axis0len = axis0.length;
184             axis0 /= axis0len;
185             vec2 axis1 = *p3 - *p1;
186             float axis1len = axis1.length;
187             axis1 /= axis1len;
188 
189             vec3 raxis1 = mat3([axis0.x, axis0.y, 0, -axis0.y, axis0.x, 0, 0, 0, 1]) * vec3(axis1, 1);
190             float cosA = raxis1.x;
191             float sinA = raxis1.y;
192             t.offsetMatrices = 
193                 mat3([axis0len > 0? 1/axis0len: 0, 0, 0,
194                         0, axis1len > 0? 1/axis1len: 0, 0,
195                         0, 0, 1]) * 
196                 mat3([1, -cosA/sinA, 0, 
197                         0, 1/sinA, 0, 
198                         0, 0, 1]) * 
199                 mat3([axis0.x, axis0.y, 0, 
200                         -axis0.y, axis0.x, 0, 
201                         0, 0, 1]) * 
202                 mat3([1, 0, -(p1).x, 
203                         0, 1, -(p1).y, 
204                         0, 0, 1]);
205             triangles ~= t;
206         }
207 
208         // Construct bitMap
209         int width  = cast(int)(ceil(bounds.z) - floor(bounds.x) + 1);
210         int height = cast(int)(ceil(bounds.w) - floor(bounds.y) + 1);
211         bitMask.length = width * height;
212         bitMask[] = 0;
213         foreach (size_t i, t; triangles) {
214             vec2[3] tvertices = [
215                 data.vertices[data.indices[3*i]],
216                 data.vertices[data.indices[3*i+1]],
217                 data.vertices[data.indices[3*i+2]]
218             ];
219 
220             vec4 tbounds = getBounds(tvertices);
221             int bwidth  = cast(int)(ceil(tbounds.z) - floor(tbounds.x) + 1);
222             int bheight = cast(int)(ceil(tbounds.w) - floor(tbounds.y) + 1);
223             int top  = cast(int)floor(tbounds.y);
224             int left = cast(int)floor(tbounds.x);
225             foreach (y; 0..bheight) {
226                 foreach (x; 0..bwidth) {
227                     vec2 pt = vec2(left + x, top + y);
228                     if (isPointInTriangle(pt, tvertices)) {
229                         ushort id = cast(ushort)(i + 1);
230                         pt-= bounds.xy;
231                         bitMask[cast(int)(pt.y * width + pt.x)] = id;
232                     }
233                 }
234             }
235         }
236 
237         precalculated = true;
238         foreach (child; children) {
239             setupChild(child);
240         }
241     }
242 
243     override
244     void renderMask(bool dodge = false) {
245 
246     }
247 
248     override
249     void rebuffer(ref MeshData data) {
250         super.rebuffer(data);
251         if (dynamic) {
252             precalculated = false;
253         }
254     }
255 
256     override
257     void serializeSelfImpl(ref InochiSerializer serializer, bool recursive = true) {
258         super.serializeSelfImpl(serializer, recursive);
259 
260         serializer.putKey("dynamic_deformation");
261         serializer.serializeValue(dynamic);
262 
263         serializer.putKey("translate_children");
264         serializer.serializeValue(translateChildren);
265     }
266 
267     override
268     SerdeException deserializeFromFghj(Fghj data) {
269         super.deserializeFromFghj(data);
270 
271         if (!data["dynamic_deformation"].isEmpty) 
272             data["dynamic_deformation"].deserializeValue(dynamic);
273 
274         translateChildren = false;
275         if (!data["translate_children"].isEmpty)
276             data["translate_children"].deserializeValue(translateChildren);
277 
278         return null;
279     }
280 
281     override
282     void setupChild(Node child) {
283  
284         void setGroup(Node node) {
285             auto drawable = cast(Drawable)node;
286             auto group    = cast(MeshGroup)node;
287             auto composite = cast(Composite)node;
288             bool isDrawable = drawable !is null;
289             bool isComposite = composite !is null && composite.propagateMeshGroup;
290             bool mustPropagate = (isDrawable && group is null) || isComposite;
291             if (translateChildren || isDrawable) {
292                 if (isDrawable && dynamic) {
293                     node.preProcessFilter  = null;
294                     node.postProcessFilter = &filterChildren;
295                 } else {
296                     node.preProcessFilter  = &filterChildren;
297                     node.postProcessFilter = null;
298                 }
299             } else {
300                 node.preProcessFilter  = null;
301                 node.postProcessFilter = null;
302             }
303             // traverse children if node is Drawable and is not MeshGroup instance.
304             if (mustPropagate) {
305                 foreach (child; node.children) {
306                     setGroup(child);
307                 }
308             }
309         }
310 
311         if (data.indices.length > 0) {
312             setGroup(child);
313         } 
314 
315     }
316 
317     void applyDeformToChildren(Parameter[] params) {
318         if (dynamic || data.indices.length == 0)
319             return;
320 
321         if (!precalculated) {
322             precalculate();
323         }
324         forwardMatrix = transform.matrix;
325         inverseMatrix = globalTransform.matrix.inverse;
326 
327         foreach (param; params) {
328             void transferChildren(Node node, int x, int y) {
329                 auto drawable = cast(Drawable)node;
330                 auto group = cast(MeshGroup)node;
331                 auto composite = cast(Composite)node;
332                 bool isDrawable = drawable !is null;
333                 bool isComposite = composite !is null && composite.propagateMeshGroup;
334                 bool mustPropagate = (isDrawable && group is null) || isComposite;
335                 if (isDrawable) {
336                     auto vertices = drawable.vertices;
337                     mat4 matrix = drawable.transform.matrix;
338 
339                     auto nodeBinding = cast(DeformationParameterBinding)param.getOrAddBinding(node, "deform");
340                     auto nodeDeform = nodeBinding.values[x][y].vertexOffsets.dup;
341                     Tuple!(vec2[], mat4*) filterResult = filterChildren(vertices, nodeDeform, &matrix);
342                     if (filterResult[0] !is null) {
343                         nodeBinding.values[x][y].vertexOffsets = filterResult[0];
344                         nodeBinding.getIsSet()[x][y] = true;
345                     }
346                 } else if (translateChildren && !isComposite) {
347                     auto vertices = [node.localTransform.translation.xy];
348                     mat4 matrix = node.parent? node.parent.transform.matrix: mat4.identity;
349 
350                     auto nodeBindingX = cast(ValueParameterBinding)param.getOrAddBinding(node, "transform.t.x");
351                     auto nodeBindingY = cast(ValueParameterBinding)param.getOrAddBinding(node, "transform.t.y");
352                     auto nodeDeform = [node.offsetTransform.translation.xy];
353                     Tuple!(vec2[], mat4*) filterResult = filterChildren(vertices, nodeDeform, &matrix);
354                     if (filterResult[0] !is null) {
355                         nodeBindingX.values[x][y] += filterResult[0][0].x;
356                         nodeBindingY.values[x][y] += filterResult[0][0].y;
357                         nodeBindingX.getIsSet()[x][y] = true;
358                         nodeBindingY.getIsSet()[x][y] = true;
359                     }
360 
361                 }
362                 if (mustPropagate) {
363                     foreach (child; node.children) {
364                         transferChildren(child, x, y);
365                     }
366                 }
367             }
368 
369 
370             if  (auto binding = param.getBinding(this, "deform")) {
371                 auto deformBinding = cast(DeformationParameterBinding)binding;
372                 assert(deformBinding !is null);
373                 Node target = binding.getTarget().node;
374 
375                 for (int x = 0; x < param.axisPoints[0].length; x ++) {
376                     for (int y = 0; y < param.axisPoints[1].length; y ++) {
377 
378                         vec2[] deformation;
379                         if (deformBinding.isSet_[x][y])
380                             deformation = deformBinding.values[x][y].vertexOffsets;
381                         else {
382                             bool rightMost  = x == param.axisPoints[0].length - 1;
383                             bool bottomMost = y == param.axisPoints[1].length - 1;
384                             deformation = deformBinding.interpolate(vec2u(rightMost? x - 1: x, bottomMost? y - 1: y), vec2(rightMost? 1: 0, bottomMost? 1:0)).vertexOffsets;
385                         }
386                         transformedVertices.length = vertices.length;
387                         foreach(i, vertex; vertices) {
388                             transformedVertices[i] = vertex + deformation[i];
389                         }
390                         foreach (index; 0..triangles.length) {
391                             auto p1 = transformedVertices[data.indices[index * 3]];
392                             auto p2 = transformedVertices[data.indices[index * 3 + 1]];
393                             auto p3 = transformedVertices[data.indices[index * 3 + 2]];
394                             triangles[index].transformMatrix = mat3([p2.x - p1.x, p3.x - p1.x, p1.x,
395                                                                     p2.y - p1.y, p3.y - p1.y, p1.y,
396                                                                     0, 0, 1]) * triangles[index].offsetMatrices;
397                         }
398 
399                         foreach (child; children) {
400                             transferChildren(child, x, y);
401                         }
402 
403                     }
404                 }
405                 param.removeBinding(binding);
406             }
407 
408         }
409         data.indices.length = 0;
410         data.vertices.length = 0;
411         data.uvs.length = 0;
412         rebuffer(data);
413         translateChildren = false;
414         precalculated = false;
415     }
416 
417     void switchMode(bool dynamic) {
418         if (this.dynamic != dynamic) {
419             this.dynamic = dynamic;
420             precalculated = false;
421         }
422     }
423 
424     bool getTranslateChildren() { return translateChildren; }
425 
426     void setTranslateChildren(bool value) {
427         translateChildren = value;
428         foreach (child; children)
429             setupChild(child);
430     }
431 
432     void clearCache() {
433         precalculated = false;
434         bitMask.length = 0;
435         triangles.length = 0;
436     }
437 }