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         Node.preProcess();
58     }
59 
60     override
61     void postProcess() {
62         Node.postProcess();
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         if (data.indices.length > 0) {
116             if (!precalculated) {
117                 precalculate();
118             }
119             transformedVertices.length = vertices.length;
120             foreach(i, vertex; vertices) {
121                 transformedVertices[i] = vertex+this.deformation[i];
122             }
123             foreach (index; 0..triangles.length) {
124                 auto p1 = transformedVertices[data.indices[index * 3]];
125                 auto p2 = transformedVertices[data.indices[index * 3 + 1]];
126                 auto p3 = transformedVertices[data.indices[index * 3 + 2]];
127                 triangles[index].transformMatrix = mat3([p2.x - p1.x, p3.x - p1.x, p1.x,
128                                                         p2.y - p1.y, p3.y - p1.y, p1.y,
129                                                         0, 0, 1]) * triangles[index].offsetMatrices;
130             }
131             forwardMatrix = transform.matrix;
132             inverseMatrix = globalTransform.matrix.inverse;
133         }
134 
135        super.update(); 
136     }
137 
138     override
139     void draw() {
140         super.draw();
141     }
142 
143 
144     void precalculate() {
145         if (data.indices.length == 0) {
146             triangles.length = 0;
147             bitMask.length   = 0;
148             return;
149         }
150 
151         vec4 getBounds(T)(ref T vertices) {
152             vec4 bounds = vec4(float.max, float.max, -float.max, -float.max);
153             foreach (v; vertices) {
154                 bounds = vec4(min(bounds.x, v.x), min(bounds.y, v.y), max(bounds.z, v.x), max(bounds.w, v.y));
155             }
156             bounds.x = floor(bounds.x);
157             bounds.y = floor(bounds.y);
158             bounds.z = ceil(bounds.z);
159             bounds.w = ceil(bounds.w);
160             return bounds;
161         }
162 
163         // Calculating conversion matrix for triangles
164         bounds = getBounds(data.vertices);
165         triangles.length = 0;
166         foreach (i; 0..data.indices.length / 3) {
167             Triangle t;
168             vec2[3] tvertices = [
169                 data.vertices[data.indices[3*i]],
170                 data.vertices[data.indices[3*i+1]],
171                 data.vertices[data.indices[3*i+2]]
172             ];
173             
174             vec2* p1 = &tvertices[0];
175             vec2* p2 = &tvertices[1];
176             vec2* p3 = &tvertices[2];
177 
178             vec2 axis0 = *p2 - *p1;
179             float axis0len = axis0.length;
180             axis0 /= axis0len;
181             vec2 axis1 = *p3 - *p1;
182             float axis1len = axis1.length;
183             axis1 /= axis1len;
184 
185             vec3 raxis1 = mat3([axis0.x, axis0.y, 0, -axis0.y, axis0.x, 0, 0, 0, 1]) * vec3(axis1, 1);
186             float cosA = raxis1.x;
187             float sinA = raxis1.y;
188             t.offsetMatrices = 
189                 mat3([axis0len > 0? 1/axis0len: 0, 0, 0,
190                         0, axis1len > 0? 1/axis1len: 0, 0,
191                         0, 0, 1]) * 
192                 mat3([1, -cosA/sinA, 0, 
193                         0, 1/sinA, 0, 
194                         0, 0, 1]) * 
195                 mat3([axis0.x, axis0.y, 0, 
196                         -axis0.y, axis0.x, 0, 
197                         0, 0, 1]) * 
198                 mat3([1, 0, -(p1).x, 
199                         0, 1, -(p1).y, 
200                         0, 0, 1]);
201             triangles ~= t;
202         }
203 
204         // Construct bitMap
205         int width  = cast(int)(ceil(bounds.z) - floor(bounds.x) + 1);
206         int height = cast(int)(ceil(bounds.w) - floor(bounds.y) + 1);
207         bitMask.length = width * height;
208         bitMask[] = 0;
209         foreach (size_t i, t; triangles) {
210             vec2[3] tvertices = [
211                 data.vertices[data.indices[3*i]],
212                 data.vertices[data.indices[3*i+1]],
213                 data.vertices[data.indices[3*i+2]]
214             ];
215 
216             vec4 tbounds = getBounds(tvertices);
217             int bwidth  = cast(int)(ceil(tbounds.z) - floor(tbounds.x) + 1);
218             int bheight = cast(int)(ceil(tbounds.w) - floor(tbounds.y) + 1);
219             int top  = cast(int)floor(tbounds.y);
220             int left = cast(int)floor(tbounds.x);
221             foreach (y; 0..bheight) {
222                 foreach (x; 0..bwidth) {
223                     vec2 pt = vec2(left + x, top + y);
224                     if (isPointInTriangle(pt, tvertices)) {
225                         ushort id = cast(ushort)(i + 1);
226                         pt-= bounds.xy;
227                         bitMask[cast(int)(pt.y * width + pt.x)] = id;
228                     }
229                 }
230             }
231         }
232 
233         precalculated = true;
234         foreach (child; children) {
235             setupChild(child);
236         }
237     }
238 
239     override
240     void renderMask(bool dodge = false) {
241 
242     }
243 
244     override
245     void rebuffer(ref MeshData data) {
246         super.rebuffer(data);
247         if (dynamic) {
248             precalculated = false;
249         }
250     }
251 
252     override
253     void serializeSelfImpl(ref InochiSerializer serializer, bool recursive = true) {
254         super.serializeSelfImpl(serializer, recursive);
255 
256         serializer.putKey("dynamic_deformation");
257         serializer.serializeValue(dynamic);
258 
259         serializer.putKey("translate_children");
260         serializer.serializeValue(translateChildren);
261     }
262 
263     override
264     SerdeException deserializeFromFghj(Fghj data) {
265         super.deserializeFromFghj(data);
266 
267         if (!data["dynamic_deformation"].isEmpty) 
268             data["dynamic_deformation"].deserializeValue(dynamic);
269 
270         translateChildren = false;
271         if (!data["translate_children"].isEmpty)
272             data["translate_children"].deserializeValue(translateChildren);
273 
274         return null;
275     }
276 
277     override
278     void setupChild(Node child) {
279  
280         void setGroup(Node node) {
281             auto drawable = cast(Drawable)node;
282             auto group    = cast(MeshGroup)node;
283             bool isDrawable = drawable !is null && group is null;
284             if (translateChildren || isDrawable) {
285                 if (isDrawable && dynamic) {
286                     node.preProcessFilter  = null;
287                     node.postProcessFilter = &filterChildren;
288                 } else {
289                     node.preProcessFilter  = &filterChildren;
290                     node.postProcessFilter = null;
291                 }
292             } else {
293                 node.preProcessFilter  = null;
294                 node.postProcessFilter = null;
295             }
296             // traverse children if node is Drawable and is not MeshGroup instance.
297             if (isDrawable) {
298                 foreach (child; node.children) {
299                     setGroup(child);
300                 }
301             }
302         }
303 
304         if (data.indices.length > 0) {
305             setGroup(child);
306         } 
307 
308     }
309 
310     void applyDeformToChildren(Parameter[] params) {
311         if (dynamic || data.indices.length == 0)
312             return;
313 
314         if (!precalculated) {
315             precalculate();
316         }
317         forwardMatrix = transform.matrix;
318         inverseMatrix = globalTransform.matrix.inverse;
319 
320         foreach (param; params) {
321             void transferChildren(Node node, int x, int y) {
322                 auto drawable = cast(Drawable)node;
323                 auto group = cast(MeshGroup)node;
324                 bool isDrawable = drawable !is null && group is null;
325                 if (isDrawable) {
326                     auto vertices = drawable.vertices;
327                     mat4 matrix = drawable.transform.matrix;
328 
329                     auto nodeBinding = cast(DeformationParameterBinding)param.getOrAddBinding(node, "deform");
330                     auto nodeDeform = nodeBinding.values[x][y].vertexOffsets.dup;
331                     Tuple!(vec2[], mat4*) filterResult = filterChildren(vertices, nodeDeform, &matrix);
332                     if (filterResult[0] !is null) {
333                         nodeBinding.values[x][y].vertexOffsets = filterResult[0];
334                     }
335                 } else if (translateChildren) {
336                     auto vertices = [node.localTransform.translation.xy];
337                     mat4 matrix = node.parent? node.parent.transform.matrix: mat4.identity;
338 
339                     auto nodeBindingX = cast(ValueParameterBinding)param.getOrAddBinding(node, "transform.t.x");
340                     auto nodeBindingY = cast(ValueParameterBinding)param.getOrAddBinding(node, "transform.t.y");
341                     auto nodeDeform = [node.offsetTransform.translation.xy];
342                     Tuple!(vec2[], mat4*) filterResult = filterChildren(vertices, nodeDeform, &matrix);
343                     if (filterResult[0] !is null) {
344                         nodeBindingX.values[x][y] += filterResult[0][0].x;
345                         nodeBindingY.values[x][y] += filterResult[0][0].y;
346                     }
347 
348                 }
349                 if (isDrawable) {
350                     foreach (child; node.children) {
351                         transferChildren(child, x, y);
352                     }
353                 }
354             }
355 
356 
357             if  (auto binding = param.getBinding(this, "deform")) {
358                 auto deformBinding = cast(DeformationParameterBinding)binding;
359                 assert(deformBinding !is null);
360                 Node target = binding.getTarget().node;
361 
362                 for (int x = 0; x < param.axisPoints[0].length; x ++) {
363                     for (int y = 0; y < param.axisPoints[1].length; y ++) {
364 
365                         vec2[] deformation;
366                         if (deformBinding.isSet_[x][y])
367                             deformation = deformBinding.values[x][y].vertexOffsets;
368                         else {
369                             bool rightMost  = x == param.axisPoints[0].length - 1;
370                             bool bottomMost = y == param.axisPoints[1].length - 1;
371                             deformation = deformBinding.interpolate(vec2u(rightMost? x - 1: x, bottomMost? y - 1: y), vec2(rightMost? 1: 0, bottomMost? 1:0)).vertexOffsets;
372                         }
373                         transformedVertices.length = vertices.length;
374                         foreach(i, vertex; vertices) {
375                             transformedVertices[i] = vertex + deformation[i];
376                         }
377                         foreach (index; 0..triangles.length) {
378                             auto p1 = transformedVertices[data.indices[index * 3]];
379                             auto p2 = transformedVertices[data.indices[index * 3 + 1]];
380                             auto p3 = transformedVertices[data.indices[index * 3 + 2]];
381                             triangles[index].transformMatrix = mat3([p2.x - p1.x, p3.x - p1.x, p1.x,
382                                                                     p2.y - p1.y, p3.y - p1.y, p1.y,
383                                                                     0, 0, 1]) * triangles[index].offsetMatrices;
384                         }
385 
386                         foreach (child; children) {
387                             transferChildren(child, x, y);
388                         }
389 
390                     }
391                 }
392                 translateChildren = false;
393                 param.removeBinding(binding);
394             }
395 
396         }
397         data.indices.length = 0;
398         data.vertices.length = 0;
399         data.uvs.length = 0;
400         rebuffer(data);
401         precalculated = false;
402     }
403 
404     void switchMode(bool dynamic) {
405         if (this.dynamic != dynamic) {
406             this.dynamic = dynamic;
407             precalculated = false;
408         }
409     }
410 
411     bool getTranslateChildren() { return translateChildren; }
412 
413     void setTranslateChildren(bool value) {
414         translateChildren = value;
415         foreach (child; children)
416             setupChild(child);
417     }
418 
419     void clearCache() {
420         precalculated = false;
421         bitMask.length = 0;
422         triangles.length = 0;
423     }
424 }