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 }