1 /* 2 Inochi2D Part 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.part; 10 import inochi2d.integration; 11 import inochi2d.fmt; 12 import inochi2d.core.nodes.drawable; 13 import inochi2d.core; 14 import inochi2d.math; 15 import bindbc.opengl; 16 import std.exception; 17 import std.algorithm.mutation : copy; 18 public import inochi2d.core.nodes.common; 19 import std.math : isNaN; 20 21 public import inochi2d.core.meshdata; 22 23 24 package(inochi2d) { 25 private { 26 Texture boundAlbedo; 27 28 Shader partShader; 29 Shader partMaskShader; 30 31 /* GLSL Uniforms (Normal) */ 32 GLint mvp; 33 GLint offset; 34 GLint gopacity; 35 GLint gMultColor; 36 GLint gScreenColor; 37 GLint gEmissionStrength; 38 39 /* GLSL Uniforms (Masks) */ 40 GLint mmvp; 41 GLint mthreshold; 42 43 GLuint sVertexBuffer; 44 GLuint sUVBuffer; 45 GLuint sElementBuffer; 46 } 47 48 void inInitPart() { 49 inRegisterNodeType!Part; 50 51 version(InDoesRender) { 52 partShader = new Shader(import("basic/basic.vert"), import("basic/basic.frag")); 53 partMaskShader = new Shader(import("basic/basic.vert"), import("basic/basic-mask.frag")); 54 55 incDrawableBindVAO(); 56 57 partShader.use(); 58 partShader.setUniform(partShader.getUniformLocation("albedo"), 0); 59 partShader.setUniform(partShader.getUniformLocation("emissive"), 1); 60 partShader.setUniform(partShader.getUniformLocation("bumpmap"), 2); 61 mvp = partShader.getUniformLocation("mvp"); 62 offset = partShader.getUniformLocation("offset"); 63 gopacity = partShader.getUniformLocation("opacity"); 64 gMultColor = partShader.getUniformLocation("multColor"); 65 gScreenColor = partShader.getUniformLocation("screenColor"); 66 gEmissionStrength = partShader.getUniformLocation("emissionStrength"); 67 68 partMaskShader.use(); 69 partMaskShader.setUniform(partMaskShader.getUniformLocation("albedo"), 0); 70 partMaskShader.setUniform(partMaskShader.getUniformLocation("emissive"), 1); 71 partMaskShader.setUniform(partMaskShader.getUniformLocation("bumpmap"), 2); 72 mmvp = partMaskShader.getUniformLocation("mvp"); 73 mthreshold = partMaskShader.getUniformLocation("threshold"); 74 75 glGenBuffers(1, &sVertexBuffer); 76 glGenBuffers(1, &sUVBuffer); 77 glGenBuffers(1, &sElementBuffer); 78 } 79 } 80 } 81 82 83 /** 84 Creates a simple part that is sized after the texture given 85 part is created based on file path given. 86 Supported file types are: png, tga and jpeg 87 88 This is unoptimal for normal use and should only be used 89 for real-time use when you want to add/remove parts on the fly 90 */ 91 Part inCreateSimplePart(string file, Node parent = null) { 92 return inCreateSimplePart(ShallowTexture(file), parent, file); 93 } 94 95 /** 96 Creates a simple part that is sized after the texture given 97 98 This is unoptimal for normal use and should only be used 99 for real-time use when you want to add/remove parts on the fly 100 */ 101 Part inCreateSimplePart(ShallowTexture texture, Node parent = null, string name = "New Part") { 102 return inCreateSimplePart(new Texture(texture), parent, name); 103 } 104 105 /** 106 Creates a simple part that is sized after the texture given 107 108 This is unoptimal for normal use and should only be used 109 for real-time use when you want to add/remove parts on the fly 110 */ 111 Part inCreateSimplePart(Texture tex, Node parent = null, string name = "New Part") { 112 MeshData data = MeshData([ 113 vec2(-(tex.width/2), -(tex.height/2)), 114 vec2(-(tex.width/2), tex.height/2), 115 vec2(tex.width/2, -(tex.height/2)), 116 vec2(tex.width/2, tex.height/2), 117 ], 118 [ 119 vec2(0, 0), 120 vec2(0, 1), 121 vec2(1, 0), 122 vec2(1, 1), 123 ], 124 [ 125 0, 1, 2, 126 2, 1, 3 127 ]); 128 Part p = new Part(data, [tex], parent); 129 p.name = name; 130 return p; 131 } 132 133 enum NO_TEXTURE = uint.max; 134 135 enum TextureUsage : size_t { 136 Albedo, 137 Emissive, 138 Bumpmap, 139 COUNT 140 } 141 142 /** 143 Dynamic Mesh Part 144 */ 145 @TypeId("Part") 146 class Part : Drawable { 147 private: 148 GLuint uvbo; 149 150 void updateUVs() { 151 version(InDoesRender) { 152 glBindBuffer(GL_ARRAY_BUFFER, uvbo); 153 glBufferData(GL_ARRAY_BUFFER, data.uvs.length*vec2.sizeof, data.uvs.ptr, GL_STATIC_DRAW); 154 } 155 } 156 157 /* 158 RENDERING 159 */ 160 void drawSelf(bool isMask = false)() { 161 162 // In some cases this may happen 163 if (textures.length == 0) return; 164 165 // Bind the vertex array 166 incDrawableBindVAO(); 167 mat4 matrix = transform.matrix(); 168 if (overrideTransformMatrix !is null) 169 matrix = overrideTransformMatrix.matrix; 170 if (oneTimeTransform !is null) 171 matrix = (*oneTimeTransform) * matrix; 172 static if (isMask) { 173 partMaskShader.use(); 174 partMaskShader.setUniform(offset, data.origin); 175 partMaskShader.setUniform(mmvp, inGetCamera().matrix * puppet.transform.matrix * matrix); 176 partMaskShader.setUniform(mthreshold, clamp(offsetMaskThreshold + maskAlphaThreshold, 0, 1)); 177 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 178 } else { 179 partShader.use(); 180 partShader.setUniform(offset, data.origin); 181 partShader.setUniform(mvp, inGetCamera().matrix * puppet.transform.matrix * matrix); 182 partShader.setUniform(gopacity, clamp(offsetOpacity * opacity, 0, 1)); 183 partShader.setUniform(gEmissionStrength, emissionStrength*offsetEmissionStrength); 184 185 partShader.setUniform(partShader.getUniformLocation("albedo"), 0); 186 partShader.setUniform(partShader.getUniformLocation("emissive"), 1); 187 partShader.setUniform(partShader.getUniformLocation("bumpmap"), 2); 188 189 vec3 clampedColor = tint; 190 if (!offsetTint.x.isNaN) clampedColor.x = clamp(tint.x*offsetTint.x, 0, 1); 191 if (!offsetTint.y.isNaN) clampedColor.y = clamp(tint.y*offsetTint.y, 0, 1); 192 if (!offsetTint.z.isNaN) clampedColor.z = clamp(tint.z*offsetTint.z, 0, 1); 193 partShader.setUniform(gMultColor, clampedColor); 194 195 clampedColor = screenTint; 196 if (!offsetScreenTint.x.isNaN) clampedColor.x = clamp(screenTint.x+offsetScreenTint.x, 0, 1); 197 if (!offsetScreenTint.y.isNaN) clampedColor.y = clamp(screenTint.y+offsetScreenTint.y, 0, 1); 198 if (!offsetScreenTint.z.isNaN) clampedColor.z = clamp(screenTint.z+offsetScreenTint.z, 0, 1); 199 partShader.setUniform(gScreenColor, clampedColor); 200 inSetBlendMode(blendingMode); 201 202 // TODO: EXT MODE 203 } 204 205 // Make sure we check whether we're already bound 206 // Otherwise we're wasting GPU resources 207 if (boundAlbedo != textures[0]) { 208 209 // Bind the textures 210 foreach(i, ref texture; textures) { 211 if (texture) texture.bind(cast(uint)i); 212 else { 213 214 // Disable texture when none is there. 215 glActiveTexture(GL_TEXTURE0+cast(uint)i); 216 glBindTexture(GL_TEXTURE_2D, 0); 217 } 218 } 219 } 220 221 222 // Enable points array 223 glEnableVertexAttribArray(0); 224 glBindBuffer(GL_ARRAY_BUFFER, vbo); 225 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null); 226 227 // Enable UVs array 228 glEnableVertexAttribArray(1); // uvs 229 glBindBuffer(GL_ARRAY_BUFFER, uvbo); 230 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, null); 231 232 // Enable deform array 233 glEnableVertexAttribArray(2); // deforms 234 glBindBuffer(GL_ARRAY_BUFFER, dbo); 235 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, null); 236 237 // Bind index buffer 238 this.bindIndex(); 239 240 // Disable the vertex attribs after use 241 glDisableVertexAttribArray(0); 242 glDisableVertexAttribArray(1); 243 glDisableVertexAttribArray(2); 244 } 245 246 protected: 247 248 override 249 string typeId() { return "Part"; } 250 251 /** 252 Allows serializing self data (with pretty serializer) 253 */ 254 override 255 void serializeSelfImpl(ref InochiSerializer serializer, bool recursive = true) { 256 super.serializeSelfImpl(serializer, recursive); 257 version (InDoesRender) { 258 if (inIsINPMode()) { 259 serializer.putKey("textures"); 260 auto state = serializer.arrayBegin(); 261 foreach(ref texture; textures) { 262 if (texture) { 263 ptrdiff_t index = puppet.getTextureSlotIndexFor(texture); 264 if (index >= 0) { 265 serializer.elemBegin; 266 serializer.putValue(cast(size_t)index); 267 } else { 268 serializer.elemBegin; 269 serializer.putValue(cast(size_t)NO_TEXTURE); 270 } 271 } else { 272 serializer.elemBegin; 273 serializer.putValue(cast(size_t)NO_TEXTURE); 274 } 275 } 276 serializer.arrayEnd(state); 277 } 278 } 279 280 serializer.putKey("blend_mode"); 281 serializer.serializeValue(blendingMode); 282 283 serializer.putKey("tint"); 284 tint.serialize(serializer); 285 286 serializer.putKey("screenTint"); 287 screenTint.serialize(serializer); 288 289 serializer.putKey("emissionStrength"); 290 serializer.serializeValue(emissionStrength); 291 292 if (masks.length > 0) { 293 serializer.putKey("masks"); 294 auto state = serializer.arrayBegin(); 295 foreach(m; masks) { 296 serializer.elemBegin; 297 serializer.serializeValue(m); 298 } 299 serializer.arrayEnd(state); 300 } 301 302 serializer.putKey("mask_threshold"); 303 serializer.putValue(maskAlphaThreshold); 304 305 serializer.putKey("opacity"); 306 serializer.putValue(opacity); 307 } 308 309 override 310 SerdeException deserializeFromFghj(Fghj data) { 311 super.deserializeFromFghj(data); 312 313 314 version(InRenderless) { 315 if (inIsINPMode()) { 316 foreach(texElement; data["textures"].byElement) { 317 uint textureId; 318 texElement.deserializeValue(textureId); 319 if (textureId == NO_TEXTURE) continue; 320 321 textureIds ~= textureId; 322 } 323 } else { 324 assert(0, "Raw Inochi2D JSON not supported in renderless mode"); 325 } 326 327 // Do nothing in this instance 328 } else { 329 if (inIsINPMode()) { 330 331 size_t i; 332 foreach(texElement; data["textures"].byElement) { 333 uint textureId; 334 texElement.deserializeValue(textureId); 335 336 // uint max = no texture set 337 if (textureId == NO_TEXTURE) continue; 338 339 textureIds ~= textureId; 340 this.textures[i++] = inGetTextureFromId(textureId); 341 } 342 } else { 343 enforce(0, "Loading from texture path is deprecated."); 344 } 345 } 346 347 data["opacity"].deserializeValue(this.opacity); 348 data["mask_threshold"].deserializeValue(this.maskAlphaThreshold); 349 350 // Older models may not have tint 351 if (!data["tint"].isEmpty) deserialize(tint, data["tint"]); 352 353 // Older models may not have screen tint 354 if (!data["screenTint"].isEmpty) deserialize(screenTint, data["screenTint"]); 355 356 // Older models may not have emission 357 if (!data["emissionStrength"].isEmpty) deserialize(tint, data["emissionStrength"]); 358 359 // Older models may not have blend mode 360 if (!data["blend_mode"].isEmpty) data["blend_mode"].deserializeValue(this.blendingMode); 361 362 if (!data["masked_by"].isEmpty) { 363 MaskingMode mode; 364 data["mask_mode"].deserializeValue(mode); 365 366 // Go every masked part 367 foreach(imask; data["masked_by"].byElement) { 368 uint uuid; 369 if (auto exc = imask.deserializeValue(uuid)) return exc; 370 this.masks ~= MaskBinding(uuid, mode, null); 371 } 372 } 373 374 if (!data["masks"].isEmpty) { 375 data["masks"].deserializeValue(this.masks); 376 } 377 378 // Update indices and vertices 379 this.updateUVs(); 380 return null; 381 } 382 383 override 384 void serializePartial(ref InochiSerializer serializer, bool recursive=true) { 385 super.serializePartial(serializer, recursive); 386 serializer.putKey("textureUUIDs"); 387 auto state = serializer.arrayBegin(); 388 foreach(ref texture; textures) { 389 uint uuid; 390 if (texture !is null) { 391 uuid = texture.getRuntimeUUID(); 392 } else { 393 uuid = InInvalidUUID; 394 } 395 serializer.elemBegin; 396 serializer.putValue(cast(size_t)uuid); 397 } 398 serializer.arrayEnd(state); 399 } 400 401 // 402 // PARAMETER OFFSETS 403 // 404 float offsetMaskThreshold = 0; 405 float offsetOpacity = 1; 406 float offsetEmissionStrength = 1; 407 vec3 offsetTint = vec3(0); 408 vec3 offsetScreenTint = vec3(0); 409 410 // TODO: Cache this 411 size_t maskCount() { 412 size_t c; 413 foreach(m; masks) if (m.mode == MaskingMode.Mask) c++; 414 return c; 415 } 416 417 size_t dodgeCount() { 418 size_t c; 419 foreach(m; masks) if (m.mode == MaskingMode.DodgeMask) c++; 420 return c; 421 } 422 423 public: 424 /** 425 List of textures this part can use 426 427 TODO: use more than texture 0 428 */ 429 Texture[TextureUsage.COUNT] textures; 430 431 /** 432 List of texture IDs 433 */ 434 int[] textureIds; 435 436 /** 437 List of masks to apply 438 */ 439 MaskBinding[] masks; 440 441 /** 442 Blending mode 443 */ 444 BlendMode blendingMode = BlendMode.Normal; 445 446 /** 447 Alpha Threshold for the masking system, the higher the more opaque pixels will be discarded in the masking process 448 */ 449 float maskAlphaThreshold = 0.5; 450 451 /** 452 Opacity of the mesh 453 */ 454 float opacity = 1; 455 456 /** 457 Strength of emission 458 */ 459 float emissionStrength = 1; 460 461 /** 462 Multiplicative tint color 463 */ 464 vec3 tint = vec3(1, 1, 1); 465 466 /** 467 Screen tint color 468 */ 469 vec3 screenTint = vec3(0, 0, 0); 470 471 /** 472 Gets the active texture 473 */ 474 Texture activeTexture() { 475 return textures[0]; 476 } 477 478 /** 479 Constructs a new part 480 */ 481 this(MeshData data, Texture[] textures, Node parent = null) { 482 this(data, textures, inCreateUUID(), parent); 483 } 484 485 /** 486 Constructs a new part 487 */ 488 this(Node parent = null) { 489 super(parent); 490 491 version(InDoesRender) glGenBuffers(1, &uvbo); 492 } 493 494 /** 495 Constructs a new part 496 */ 497 this(MeshData data, Texture[] textures, uint uuid, Node parent = null) { 498 super(data, uuid, parent); 499 foreach(i; 0..TextureUsage.COUNT) { 500 if (i >= textures.length) break; 501 this.textures[i] = textures[i]; 502 } 503 504 version(InDoesRender) { 505 glGenBuffers(1, &uvbo); 506 507 mvp = partShader.getUniformLocation("mvp"); 508 gopacity = partShader.getUniformLocation("opacity"); 509 510 mmvp = partMaskShader.getUniformLocation("mvp"); 511 mthreshold = partMaskShader.getUniformLocation("threshold"); 512 } 513 514 this.updateUVs(); 515 } 516 517 override 518 void renderMask(bool dodge = false) { 519 520 // Enable writing to stencil buffer and disable writing to color buffer 521 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 522 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); 523 glStencilFunc(GL_ALWAYS, dodge ? 0 : 1, 0xFF); 524 glStencilMask(0xFF); 525 526 // Draw ourselves to the stencil buffer 527 drawSelf!true(); 528 529 // Disable writing to stencil buffer and enable writing to color buffer 530 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 531 } 532 533 override 534 bool hasParam(string key) { 535 if (super.hasParam(key)) return true; 536 537 switch(key) { 538 case "alphaThreshold": 539 case "opacity": 540 case "tint.r": 541 case "tint.g": 542 case "tint.b": 543 case "screenTint.r": 544 case "screenTint.g": 545 case "screenTint.b": 546 case "emissionStrength": 547 return true; 548 default: 549 return false; 550 } 551 } 552 553 override 554 float getDefaultValue(string key) { 555 // Skip our list of our parent already handled it 556 float def = super.getDefaultValue(key); 557 if (!isNaN(def)) return def; 558 559 switch(key) { 560 case "alphaThreshold": 561 return 0; 562 case "opacity": 563 case "tint.r": 564 case "tint.g": 565 case "tint.b": 566 return 1; 567 case "screenTint.r": 568 case "screenTint.g": 569 case "screenTint.b": 570 return 0; 571 case "emissionStrength": 572 return 1; 573 default: return float(); 574 } 575 } 576 577 override 578 bool setValue(string key, float value) { 579 580 // Skip our list of our parent already handled it 581 if (super.setValue(key, value)) return true; 582 583 switch(key) { 584 case "alphaThreshold": 585 offsetMaskThreshold *= value; 586 return true; 587 case "opacity": 588 offsetOpacity *= value; 589 return true; 590 case "tint.r": 591 offsetTint.x *= value; 592 return true; 593 case "tint.g": 594 offsetTint.y *= value; 595 return true; 596 case "tint.b": 597 offsetTint.z *= value; 598 return true; 599 case "screenTint.r": 600 offsetScreenTint.x *= value; 601 return true; 602 case "screenTint.g": 603 offsetScreenTint.y *= value; 604 return true; 605 case "screenTint.b": 606 offsetScreenTint.z *= value; 607 return true; 608 case "emissionStrength": 609 offsetEmissionStrength += value; 610 return true; 611 default: return false; 612 } 613 } 614 615 override 616 float getValue(string key) { 617 switch(key) { 618 case "alphaThreshold": return offsetMaskThreshold; 619 case "opacity": return offsetOpacity; 620 case "tint.r": return offsetTint.x; 621 case "tint.g": return offsetTint.y; 622 case "tint.b": return offsetTint.z; 623 case "screenTint.r": return offsetScreenTint.x; 624 case "screenTint.g": return offsetScreenTint.y; 625 case "screenTint.b": return offsetScreenTint.z; 626 case "emissionStrength": return offsetEmissionStrength; 627 default: return super.getValue(key); 628 } 629 } 630 631 bool isMaskedBy(Drawable drawable) { 632 foreach(mask; masks) { 633 if (mask.maskSrc.uuid == drawable.uuid) return true; 634 } 635 return false; 636 } 637 638 ptrdiff_t getMaskIdx(Drawable drawable) { 639 if (drawable is null) return -1; 640 foreach(i, ref mask; masks) { 641 if (mask.maskSrc.uuid == drawable.uuid) return i; 642 } 643 return -1; 644 } 645 646 ptrdiff_t getMaskIdx(uint uuid) { 647 foreach(i, ref mask; masks) { 648 if (mask.maskSrc.uuid == uuid) return i; 649 } 650 return -1; 651 } 652 653 override 654 void beginUpdate() { 655 offsetMaskThreshold = 0; 656 offsetOpacity = 1; 657 offsetTint = vec3(1, 1, 1); 658 offsetScreenTint = vec3(0, 0, 0); 659 offsetEmissionStrength = 1; 660 super.beginUpdate(); 661 } 662 663 override 664 void rebuffer(ref MeshData data) { 665 super.rebuffer(data); 666 this.updateUVs(); 667 } 668 669 override 670 void draw() { 671 if (!enabled) return; 672 this.drawOne(); 673 674 foreach(child; children) { 675 child.draw(); 676 } 677 } 678 679 override 680 void drawOne() { 681 version (InDoesRender) { 682 if (!enabled) return; 683 if (!data.isReady) return; // Yeah, don't even try 684 685 size_t cMasks = maskCount; 686 687 if (masks.length > 0) { 688 import std.stdio : writeln; 689 inBeginMask(cMasks > 0); 690 691 foreach(ref mask; masks) { 692 mask.maskSrc.renderMask(mask.mode == MaskingMode.DodgeMask); 693 } 694 695 inBeginMaskContent(); 696 697 // We are the content 698 this.drawSelf(); 699 700 inEndMask(); 701 return; 702 } 703 704 // No masks, draw normally 705 this.drawSelf(); 706 } 707 super.drawOne(); 708 } 709 710 override 711 void drawOneDirect(bool forMasking) { 712 if (forMasking) this.drawSelf!true(); 713 else this.drawSelf!false(); 714 } 715 716 override 717 void finalize() { 718 super.finalize(); 719 720 MaskBinding[] validMasks; 721 foreach(i; 0..masks.length) { 722 if (Drawable nMask = puppet.find!Drawable(masks[i].maskSrcUUID)) { 723 masks[i].maskSrc = nMask; 724 validMasks ~= masks[i]; 725 } 726 } 727 728 // Remove invalid masks 729 masks = validMasks; 730 } 731 732 733 override 734 void setOneTimeTransform(mat4* transform) { 735 super.setOneTimeTransform(transform); 736 foreach (m; masks) { 737 m.maskSrc.oneTimeTransform = transform; 738 } 739 } 740 741 } 742 743 /** 744 Draws a texture at the transform of the specified part 745 */ 746 void inDrawTextureAtPart(Texture texture, Part part) { 747 const float texWidthP = texture.width()/2; 748 const float texHeightP = texture.height()/2; 749 750 // Bind the vertex array 751 incDrawableBindVAO(); 752 753 partShader.use(); 754 partShader.setUniform(mvp, 755 inGetCamera().matrix * 756 mat4.translation(vec3(part.transform.matrix() * vec4(1, 1, 1, 1))) 757 ); 758 partShader.setUniform(gopacity, part.opacity); 759 partShader.setUniform(gMultColor, part.tint); 760 partShader.setUniform(gScreenColor, part.screenTint); 761 762 // Bind the texture 763 texture.bind(); 764 765 // Enable points array 766 glEnableVertexAttribArray(0); 767 glBindBuffer(GL_ARRAY_BUFFER, sVertexBuffer); 768 glBufferData(GL_ARRAY_BUFFER, 4*vec2.sizeof, [ 769 -texWidthP, -texHeightP, 770 texWidthP, -texHeightP, 771 -texWidthP, texHeightP, 772 texWidthP, texHeightP, 773 ].ptr, GL_STATIC_DRAW); 774 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null); 775 776 // Enable UVs array 777 glEnableVertexAttribArray(1); // uvs 778 glBindBuffer(GL_ARRAY_BUFFER, sUVBuffer); 779 glBufferData(GL_ARRAY_BUFFER, 4*vec2.sizeof, [ 780 0, 0, 781 1, 0, 782 0, 1, 783 1, 1, 784 ].ptr, GL_STATIC_DRAW); 785 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, null); 786 787 // Bind element array and draw our mesh 788 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sElementBuffer); 789 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*ushort.sizeof, (cast(ushort[])[ 790 0u, 1u, 2u, 791 2u, 1u, 3u 792 ]).ptr, GL_STATIC_DRAW); 793 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, null); 794 795 // Disable the vertex attribs after use 796 glDisableVertexAttribArray(0); 797 glDisableVertexAttribArray(1); 798 } 799 800 /** 801 Draws a texture at the transform of the specified part 802 */ 803 void inDrawTextureAtPosition(Texture texture, vec2 position, float opacity = 1, vec3 color = vec3(1, 1, 1), vec3 screenColor = vec3(0, 0, 0)) { 804 const float texWidthP = texture.width()/2; 805 const float texHeightP = texture.height()/2; 806 807 // Bind the vertex array 808 incDrawableBindVAO(); 809 810 partShader.use(); 811 partShader.setUniform(mvp, 812 inGetCamera().matrix * 813 mat4.scaling(1, 1, 1) * 814 mat4.translation(vec3(position, 0)) 815 ); 816 partShader.setUniform(gopacity, opacity); 817 partShader.setUniform(gMultColor, color); 818 partShader.setUniform(gScreenColor, screenColor); 819 820 // Bind the texture 821 texture.bind(); 822 823 // Enable points array 824 glEnableVertexAttribArray(0); 825 glBindBuffer(GL_ARRAY_BUFFER, sVertexBuffer); 826 glBufferData(GL_ARRAY_BUFFER, 4*vec2.sizeof, [ 827 -texWidthP, -texHeightP, 828 texWidthP, -texHeightP, 829 -texWidthP, texHeightP, 830 texWidthP, texHeightP, 831 ].ptr, GL_STATIC_DRAW); 832 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null); 833 834 // Enable UVs array 835 glEnableVertexAttribArray(1); // uvs 836 glBindBuffer(GL_ARRAY_BUFFER, sUVBuffer); 837 glBufferData(GL_ARRAY_BUFFER, 4*vec2.sizeof, (cast(float[])[ 838 0, 0, 839 1, 0, 840 0, 1, 841 1, 1, 842 ]).ptr, GL_STATIC_DRAW); 843 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, null); 844 845 // Bind element array and draw our mesh 846 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sElementBuffer); 847 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*ushort.sizeof, (cast(ushort[])[ 848 0u, 1u, 2u, 849 2u, 1u, 3u 850 ]).ptr, GL_STATIC_DRAW); 851 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, null); 852 853 // Disable the vertex attribs after use 854 glDisableVertexAttribArray(0); 855 glDisableVertexAttribArray(1); 856 } 857 858 /** 859 Draws a texture at the transform of the specified part 860 */ 861 void inDrawTextureAtRect(Texture texture, rect area, rect uvs = rect(0, 0, 1, 1), float opacity = 1, vec3 color = vec3(1, 1, 1), vec3 screenColor = vec3(0, 0, 0), Shader s = null, Camera cam = null) { 862 863 // Bind the vertex array 864 incDrawableBindVAO(); 865 866 if (!s) s = partShader; 867 if (!cam) cam = inGetCamera(); 868 s.use(); 869 s.setUniform(s.getUniformLocation("mvp"), 870 cam.matrix * 871 mat4.scaling(1, 1, 1) 872 ); 873 s.setUniform(s.getUniformLocation("opacity"), opacity); 874 s.setUniform(s.getUniformLocation("multColor"), color); 875 s.setUniform(s.getUniformLocation("screenColor"), screenColor); 876 877 // Bind the texture 878 texture.bind(); 879 880 // Enable points array 881 glEnableVertexAttribArray(0); 882 glBindBuffer(GL_ARRAY_BUFFER, sVertexBuffer); 883 glBufferData(GL_ARRAY_BUFFER, 4*vec2.sizeof, [ 884 area.left, area.top, 885 area.right, area.top, 886 area.left, area.bottom, 887 area.right, area.bottom, 888 ].ptr, GL_STATIC_DRAW); 889 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null); 890 891 // Enable UVs array 892 glEnableVertexAttribArray(1); // uvs 893 glBindBuffer(GL_ARRAY_BUFFER, sUVBuffer); 894 glBufferData(GL_ARRAY_BUFFER, 4*vec2.sizeof, (cast(float[])[ 895 uvs.x, uvs.y, 896 uvs.width, uvs.y, 897 uvs.x, uvs.height, 898 uvs.width, uvs.height, 899 ]).ptr, GL_STATIC_DRAW); 900 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, null); 901 902 // Bind element array and draw our mesh 903 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sElementBuffer); 904 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*ushort.sizeof, (cast(ushort[])[ 905 0u, 1u, 2u, 906 2u, 1u, 3u 907 ]).ptr, GL_STATIC_DRAW); 908 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, null); 909 910 // Disable the vertex attribs after use 911 glDisableVertexAttribArray(0); 912 glDisableVertexAttribArray(1); 913 }