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 }