1 /* 2 Inochi2D Drawable base class 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.drawable; 10 public import inochi2d.core.nodes.defstack; 11 import inochi2d.integration; 12 import inochi2d.fmt.serialize; 13 import inochi2d.math; 14 import bindbc.opengl; 15 import std.exception; 16 import inochi2d.core.dbg; 17 import inochi2d.core; 18 19 private GLuint drawableVAO; 20 21 package(inochi2d) { 22 void inInitDrawable() { 23 version(InDoesRender) glGenVertexArrays(1, &drawableVAO); 24 } 25 26 27 /** 28 Binds the internal vertex array for rendering 29 */ 30 void incDrawableBindVAO() { 31 32 // Bind our vertex array 33 glBindVertexArray(drawableVAO); 34 } 35 36 bool doGenerateBounds = false; 37 } 38 39 /** 40 Sets whether Inochi2D should keep track of the bounds 41 */ 42 void inSetUpdateBounds(bool state) { 43 doGenerateBounds = state; 44 } 45 46 /** 47 Nodes that are meant to render something in to the Inochi2D scene 48 Other nodes don't have to render anything and serve mostly other 49 purposes. 50 51 The main types of Drawables are Parts and Masks 52 */ 53 54 @TypeId("Drawable") 55 abstract class Drawable : Node { 56 private: 57 void updateIndices() { 58 version (InDoesRender) { 59 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 60 glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.indices.length*ushort.sizeof, data.indices.ptr, GL_STATIC_DRAW); 61 } 62 } 63 64 void updateVertices() { 65 version (InDoesRender) { 66 67 // Important check since the user can change this every frame 68 glBindBuffer(GL_ARRAY_BUFFER, vbo); 69 glBufferData(GL_ARRAY_BUFFER, data.vertices.length*vec2.sizeof, data.vertices.ptr, GL_DYNAMIC_DRAW); 70 } 71 72 // Zero-fill the deformation delta 73 this.deformation.length = vertices.length; 74 foreach(i; 0..deformation.length) { 75 this.deformation[i] = vec2(0, 0); 76 } 77 this.updateDeform(); 78 } 79 80 void updateDeform() { 81 // Important check since the user can change this every frame 82 enforce( 83 deformation.length == vertices.length, 84 "Data length mismatch, if you want to change the mesh you need to change its data with Part.rebuffer." 85 ); 86 87 version (InDoesRender) { 88 89 glBindBuffer(GL_ARRAY_BUFFER, dbo); 90 glBufferData(GL_ARRAY_BUFFER, this.deformation.length*vec2.sizeof, this.deformation.ptr, GL_DYNAMIC_DRAW); 91 } 92 93 this.updateBounds(); 94 } 95 96 protected: 97 /** 98 OpenGL Index Buffer Object 99 */ 100 GLuint ibo; 101 102 /** 103 OpenGL Vertex Buffer Object 104 */ 105 GLuint vbo; 106 107 /** 108 OpenGL Vertex Buffer Object for deformation 109 */ 110 GLuint dbo; 111 112 /** 113 The mesh data of this part 114 115 NOTE: DO NOT MODIFY! 116 The data in here is only to be used for reference. 117 */ 118 MeshData data; 119 120 /** 121 Binds Index Buffer for rendering 122 */ 123 final void bindIndex() { 124 version (InDoesRender) { 125 // Bind element array and draw our mesh 126 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 127 glDrawElements(GL_TRIANGLES, cast(int)data.indices.length, GL_UNSIGNED_SHORT, null); 128 } 129 } 130 131 abstract void renderMask(bool dodge = false); 132 133 /** 134 Allows serializing self data (with pretty serializer) 135 */ 136 override 137 void serializeSelf(ref InochiSerializer serializer) { 138 super.serializeSelf(serializer); 139 serializer.putKey("mesh"); 140 serializer.serializeValue(data); 141 } 142 143 /** 144 Allows serializing self data (with compact serializer) 145 */ 146 override 147 void serializeSelf(ref InochiSerializerCompact serializer) { 148 super.serializeSelf(serializer); 149 serializer.putKey("mesh"); 150 serializer.serializeValue(data); 151 } 152 153 override 154 SerdeException deserializeFromFghj(Fghj data) { 155 import std.stdio : writeln; 156 super.deserializeFromFghj(data); 157 if (auto exc = data["mesh"].deserializeValue(this.data)) return exc; 158 159 this.vertices = this.data.vertices.dup; 160 161 // Update indices and vertices 162 this.updateIndices(); 163 this.updateVertices(); 164 return null; 165 } 166 167 public: 168 169 /** 170 Constructs a new drawable surface 171 */ 172 this(Node parent = null) { 173 super(parent); 174 175 version(InDoesRender) { 176 177 // Generate the buffers 178 glGenBuffers(1, &vbo); 179 glGenBuffers(1, &ibo); 180 glGenBuffers(1, &dbo); 181 } 182 183 // Create deformation stack 184 this.deformStack = DeformationStack(this); 185 } 186 187 /** 188 Constructs a new drawable surface 189 */ 190 this(MeshData data, Node parent = null) { 191 this(data, inCreateUUID(), parent); 192 } 193 194 /** 195 Constructs a new drawable surface 196 */ 197 this(MeshData data, uint uuid, Node parent = null) { 198 super(uuid, parent); 199 this.data = data; 200 this.deformStack = DeformationStack(this); 201 202 // Set the deformable points to their initial position 203 this.vertices = data.vertices.dup; 204 205 version(InDoesRender) { 206 207 // Generate the buffers 208 glGenBuffers(1, &vbo); 209 glGenBuffers(1, &ibo); 210 glGenBuffers(1, &dbo); 211 } 212 213 // Update indices and vertices 214 this.updateIndices(); 215 this.updateVertices(); 216 } 217 218 ref vec2[] vertices() { 219 return data.vertices; 220 } 221 222 /** 223 Deformation offset to apply 224 */ 225 vec2[] deformation; 226 227 /** 228 The bounds of this drawable 229 */ 230 vec4 bounds; 231 232 /** 233 Deformation stack 234 */ 235 DeformationStack deformStack; 236 237 /** 238 Refreshes the drawable, updating its vertices 239 */ 240 final void refresh() { 241 this.updateVertices(); 242 } 243 244 /** 245 Refreshes the drawable, updating its deformation deltas 246 */ 247 final void refreshDeform() { 248 this.updateDeform(); 249 } 250 251 override 252 void beginUpdate() { 253 deformStack.preUpdate(); 254 super.beginUpdate(); 255 } 256 257 /** 258 Updates the drawable 259 */ 260 override 261 void update() { 262 super.update(); 263 deformStack.update(); 264 this.updateDeform(); 265 } 266 267 /** 268 Draws the drawable 269 */ 270 override 271 void drawOne() { 272 super.drawOne(); 273 } 274 275 /** 276 Draws the drawable without any processing 277 */ 278 void drawOneDirect(bool forMasking) { } 279 280 override 281 string typeId() { return "Drawable"; } 282 283 /** 284 Updates the drawable's bounds 285 */ 286 void updateBounds() { 287 if (!doGenerateBounds) return; 288 289 // Calculate bounds 290 Transform wtransform = transform; 291 bounds = vec4(wtransform.translation.xyxy); 292 foreach(i, vertex; vertices) { 293 vec2 vertOriented = vec2(transform.matrix * vec4(vertex+deformation[i], 0, 1)); 294 if (vertOriented.x < bounds.x) bounds.x = vertOriented.x; 295 if (vertOriented.y < bounds.y) bounds.y = vertOriented.y; 296 if (vertOriented.x > bounds.z) bounds.z = vertOriented.x; 297 if (vertOriented.y > bounds.w) bounds.w = vertOriented.y; 298 } 299 } 300 301 /** 302 Draws bounds 303 */ 304 override 305 void drawBounds() { 306 if (!doGenerateBounds) return; 307 if (vertices.length == 0) return; 308 309 float width = bounds.z-bounds.x; 310 float height = bounds.w-bounds.y; 311 inDbgSetBuffer([ 312 vec3(bounds.x, bounds.y, 0), 313 vec3(bounds.x + width, bounds.y, 0), 314 315 vec3(bounds.x + width, bounds.y, 0), 316 vec3(bounds.x + width, bounds.y+height, 0), 317 318 vec3(bounds.x + width, bounds.y+height, 0), 319 vec3(bounds.x, bounds.y+height, 0), 320 321 vec3(bounds.x, bounds.y+height, 0), 322 vec3(bounds.x, bounds.y, 0), 323 ]); 324 inDbgLineWidth(3); 325 inDbgDrawLines(vec4(.5, .5, .5, 1)); 326 inDbgLineWidth(1); 327 } 328 329 version (InDoesRender) { 330 /** 331 Draws line of mesh 332 */ 333 void drawMeshLines() { 334 if (vertices.length == 0) return; 335 336 auto trans = transform.matrix(); 337 ushort[] indices = data.indices; 338 339 vec3[] points = new vec3[indices.length*2]; 340 foreach(i; 0..indices.length/3) { 341 size_t ix = i*3; 342 size_t iy = ix*2; 343 auto indice = indices[ix]; 344 345 points[iy+0] = vec3(vertices[indice]-data.origin+deformation[indice], 0); 346 points[iy+1] = vec3(vertices[indices[ix+1]]-data.origin+deformation[indices[ix+1]], 0); 347 348 points[iy+2] = vec3(vertices[indices[ix+1]]-data.origin+deformation[indices[ix+1]], 0); 349 points[iy+3] = vec3(vertices[indices[ix+2]]-data.origin+deformation[indices[ix+2]], 0); 350 351 points[iy+4] = vec3(vertices[indices[ix+2]]-data.origin+deformation[indices[ix+2]], 0); 352 points[iy+5] = vec3(vertices[indice]-data.origin+deformation[indice], 0); 353 } 354 355 inDbgSetBuffer(points); 356 inDbgDrawLines(vec4(.5, .5, .5, 1), trans); 357 } 358 359 /** 360 Draws the points of the mesh 361 */ 362 void drawMeshPoints() { 363 if (vertices.length == 0) return; 364 365 auto trans = transform.matrix(); 366 vec3[] points = new vec3[vertices.length]; 367 foreach(i, point; vertices) { 368 points[i] = vec3(point-data.origin+deformation[i], 0); 369 } 370 371 inDbgSetBuffer(points); 372 inDbgPointsSize(8); 373 inDbgDrawPoints(vec4(0, 0, 0, 1), trans); 374 inDbgPointsSize(4); 375 inDbgDrawPoints(vec4(1, 1, 1, 1), trans); 376 } 377 } 378 379 /** 380 Returns the mesh data for this Part. 381 */ 382 final ref MeshData getMesh() { 383 return this.data; 384 } 385 386 /** 387 Changes this mesh's data 388 */ 389 void rebuffer(ref MeshData data) { 390 this.data = data; 391 this.updateIndices(); 392 this.updateVertices(); 393 } 394 395 /** 396 Resets the vertices of this drawable 397 */ 398 final void reset() { 399 vertices[] = data.vertices; 400 } 401 } 402 403 version (InDoesRender) { 404 /** 405 Begins a mask 406 407 This causes the next draw calls until inBeginMaskContent/inBeginDodgeContent or inEndMask 408 to be written to the current mask. 409 410 This also clears whatever old mask there was. 411 */ 412 void inBeginMask(bool hasMasks) { 413 414 // Enable and clear the stencil buffer so we can write our mask to it 415 glEnable(GL_STENCIL_TEST); 416 glClearStencil(hasMasks ? 0 : 1); 417 glClear(GL_STENCIL_BUFFER_BIT); 418 } 419 420 /** 421 End masking 422 423 Once masking is ended content will no longer be masked by the defined mask. 424 */ 425 void inEndMask() { 426 427 // We're done stencil testing, disable it again so that we don't accidentally mask more stuff out 428 glStencilMask(0xFF); 429 glStencilFunc(GL_ALWAYS, 1, 0xFF); 430 glDisable(GL_STENCIL_TEST); 431 } 432 433 /** 434 Starts masking content 435 436 NOTE: This have to be run within a inBeginMask and inEndMask block! 437 */ 438 void inBeginMaskContent() { 439 440 glStencilFunc(GL_EQUAL, 1, 0xFF); 441 glStencilMask(0x00); 442 } 443 }