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);