1 /++ 2 A module for rendering objects. 3 4 Macros: 5 LREF = <a href="#$1">$1</a> 6 HREF = <a href="$1">$2</a> 7 8 Authors: $(HREF https://github.com/TodNaz,TodNaz) 9 Copyright: Copyright (c) 2020 - 2021, TodNaz. 10 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 11 +/ 12 module tida.render; 13 14 /++ 15 Camera control object in render, 16 +/ 17 class Camera 18 { 19 import tida.vector, 20 tida.shape, 21 tida.instance; 22 23 struct CameraObject 24 { 25 Vector!float* position; 26 Vector!float size; 27 } 28 29 private: 30 Shape!float _port; 31 Shape!float _shape; 32 CameraObject object; 33 Vector!float _trackDistance = vec!float(4.0f, 4.0f); 34 Vector!float _sizeRoom = vecNaN!float; 35 36 37 public @safe nothrow pure: 38 /// The allowed room size for camera scrolling. 39 @property Vector!float sizeRoom() 40 { 41 return _sizeRoom; 42 } 43 44 /// The allowed room size for camera scrolling. 45 @property Vector!float sizeRoom(Vector!float value) 46 { 47 return _sizeRoom = value; 48 } 49 50 /// A method to change the allowed size of a scrolling room for a camera. 51 void resizeRoom(Vector!float value) 52 { 53 _sizeRoom = value; 54 } 55 56 /++ 57 A method for binding a specific object to a camera to track it. 58 59 Params: 60 position = The reference to the variable for which the tracking 61 will be performed. We need a variable that will be 62 alive during the camera's tracking cycle. 63 size = The size of the object. (Each object is represented as a rectangle.) 64 +/ 65 void bindObject( ref Vector!float position, 66 Vector!float size = vecNaN!float) @trusted 67 { 68 object.position = &position; 69 object.size = size.isVectorNaN ? vec!float(1, 1) : size; 70 } 71 72 /++ 73 A method for binding a specific object to a camera to track it. 74 75 Params: 76 position = The reference to the variable for which the tracking 77 will be performed. We need a variable that will be 78 alive during the camera's tracking cycle. 79 size = The size of the object. (Each object is represented as a rectangle.) 80 +/ 81 void bindObject( Vector!float* position, 82 Vector!float size = vecNaN!float) 83 { 84 object.position = position; 85 object.size = size.isVectorNaN ? vec!float(1, 1) : size; 86 } 87 88 /++ 89 A method for binding a specific object to a camera to track it. 90 91 Params: 92 instance = An object in the scene that will be monitored by the camera. 93 The size is calculated from the object's touch mask. 94 +/ 95 void bindObject(Instance instance) 96 { 97 object.position = &instance.position; 98 object.size = instance.mask.calculateSize(); 99 } 100 101 /++ 102 A method that reproduces the process of tracking an object. 103 +/ 104 void followObject() 105 { 106 Vector!float velocity = vecZero!float; 107 108 if (object.position.x < port.begin.x + _trackDistance.x) 109 { 110 velocity.x = (port.begin.x + _trackDistance.x) - object.position.x; 111 } else 112 if (object.position.x + object.size.x> port.begin.x + port.end.x - _trackDistance.x) 113 { 114 velocity.x = (port.begin.x + port.end.x - _trackDistance.x) - (object.position.x + object.size.x); 115 } 116 117 if (object.position.y < port.begin.y + _trackDistance.y) 118 { 119 velocity.y = (port.begin.y + _trackDistance.y) - object.position.y; 120 } else 121 if (object.position.y + object.size.y > port.begin.y + port.end.y - _trackDistance.y) 122 { 123 velocity.y = (port.begin.y + port.end.y - _trackDistance.y) - (object.position.y + object.size.y); 124 } 125 126 immutable preBegin = port.begin - velocity; 127 128 if (!_sizeRoom.isVectorNaN) 129 { 130 if (preBegin.x > 0 && 131 preBegin.x + port.end.x < _sizeRoom.x) 132 { 133 port = Shapef.Rectangle(vec!float(preBegin.x, port.begin.y), port.end); 134 } 135 136 if (preBegin.y > 0 && 137 preBegin.y + port.end.y < _sizeRoom.y) 138 { 139 port = Shapef.Rectangle(vec!float(port.begin.x, preBegin.y), port.end); 140 } 141 } else 142 port = Shapef.Rectangle(preBegin, port.end); 143 } 144 145 /// Distance between camera boundaries and subject for the scene to move the camera's view. 146 @property Vector!float trackDistance() 147 { 148 return _trackDistance; 149 } 150 151 /// Distance between camera boundaries and subject for the scene to move the camera's view. 152 @property Vector!float trackDistance(Vector!float value) 153 { 154 return _trackDistance = value; 155 } 156 157 /++ 158 The port is the immediate visible part in the "room". The entire area in 159 the world that must be covered in the field of view. 160 +/ 161 @property Shape!float port() 162 { 163 return _port; 164 } 165 166 /// ditto 167 @property Shape!float port(Shape!float value) 168 { 169 return _port = value; 170 } 171 172 /++ 173 The size of the visible part in the plane of the window. 174 +/ 175 @property Shape!float shape() 176 { 177 return _shape; 178 } 179 180 /// ditto 181 @property Shape!float shape(Shape!float value) 182 { 183 return _shape = value; 184 } 185 186 /++ 187 Moves the visible field. 188 189 Params: 190 value = Factor movement. 191 +/ 192 void moveView(Vecf value) 193 { 194 _port = Shape!float.Rectangle(_port.begin + value, _port.end); 195 } 196 } 197 198 /// Renderer type 199 enum RenderType 200 { 201 unknown, 202 software, 203 opengl, 204 directx, // Not implement 205 vulkan // Not implement 206 } 207 208 /// A property that explains whether blending should be applied or not. 209 enum BlendMode 210 { 211 withoutBlend, /// Without blending 212 withBlend /// With blending 213 } 214 215 interface ITarget 216 { 217 void bind(IRenderer render) @safe; 218 219 void unbind(IRenderer render) @safe; 220 221 void drawning(IRenderer render) @safe; 222 } 223 224 /++ 225 An interface for rendering objects to a display or other storehouse of pixels. 226 +/ 227 interface IRenderer 228 { 229 import tida.color, 230 tida.vector, 231 tida.shader, 232 tida.drawable, 233 tida.matrix; 234 235 @safe: 236 // null - default 237 void bindTarget(ITarget target) @safe; 238 239 @property ITarget currentTarget() @safe; 240 241 /// Updates the rendering surface if, for example, the window is resized. 242 void reshape(); 243 244 ///Camera for rendering. 245 @property void camera(Camera camera); 246 247 /// Camera for rendering. 248 @property Camera camera(); 249 250 /++ 251 Drawing a point. 252 253 Params: 254 vec = Point position. 255 color = Point color. 256 +/ 257 void point(Vecf vec, Color!ubyte color) @safe; 258 259 /++ 260 Line drawing. 261 262 Params: 263 points = Tops of lines. 264 color = Line color. 265 +/ 266 void line(Vecf[2] points, Color!ubyte color) @safe; 267 268 /++ 269 Drawing a rectangle. 270 271 Params: 272 position = Rectangle position. 273 width = Rectangle width. 274 height = Rectangle height. 275 color = Rectangle color. 276 isFill = Whether to fill the rectangle with color. 277 +/ 278 void rectangle( Vecf position, 279 uint width, 280 uint height, 281 Color!ubyte color, 282 bool isFill) @safe; 283 284 /++ 285 Drawning a circle. 286 287 Params: 288 position = Circle position. 289 radius = Circle radius. 290 color = Circle color. 291 isFill = Whether to fill the circle with color. 292 +/ 293 void circle(Vecf position, 294 float radius, 295 Color!ubyte color, 296 bool isFill) @safe; 297 298 /++ 299 Drawing a triangle by its three vertices. 300 301 Params: 302 points = Triangle vertices 303 color = Triangle color. 304 isFill = Whether it is necessary to fill the triangle with color. 305 +/ 306 void triangle(Vecf[3] points, Color!ubyte color, bool isFill) @safe; 307 308 /++ 309 Draws a rectangle with rounded edges. 310 (Rendering is available only through hardware acceleration). 311 312 Params: 313 position = Position roundrectangle. 314 width = Width roundrectangle. 315 height = Height roundrectangle. 316 radius = Radius rounded edges. 317 color = Color roundrect. 318 isFill = Roundrect is filled color? 319 +/ 320 void roundrect( Vecf position, 321 uint width, 322 uint height, 323 float radius, 324 Color!ubyte color, 325 bool isFill) @safe; 326 327 /++ 328 Drawing a polygon from an array of vertices. 329 330 Params: 331 position = Polygon position. 332 points = Polygon vertices/ 333 color = Polygon color. 334 isFill = Whether it is necessary to fill the polygon with color. 335 +/ 336 void polygon( Vecf position, 337 Vecf[] points, 338 Color!ubyte color, 339 bool isFill) @safe; 340 341 /// Cleans the surface by filling it with color. 342 void clear() @safe; 343 344 /// Outputs the buffer to the window. 345 void drawning() @safe; 346 347 /// Gives the type of render. 348 RenderType type() @safe; 349 350 /// Set the coloring method. Those. with or without alpha blending. 351 void blendMode(BlendMode mode) @safe; 352 353 /// Set factor blend 354 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) @safe; 355 356 /// The color to fill when clearing. 357 void background(Color!ubyte background) @safe @property; 358 359 /// ditto 360 Color!ubyte background() @safe @property; 361 362 /++ 363 Memorize the shader for future reference. 364 365 Params: 366 name = The name of the shader by which it will be possible to pick up 367 the shader in the future. 368 program = Shader program. 369 +/ 370 void setShader(string name, Shader!Program program) @safe; 371 372 /++ 373 Pulls a shader from memory, getting it by name. Returns a null pointer 374 if no shader is found. 375 376 Params: 377 name = Shader name. 378 +/ 379 Shader!Program getShader(string name) @safe; 380 381 /// The current shader for the next object rendering. 382 void currentShader(Shader!Program program) @safe @property; 383 384 /// The current shader for the next object rendering. 385 Shader!Program currentShader() @safe @property; 386 387 /// Reset the shader to main. 388 void resetShader() @safe; 389 390 /// Current model matrix. 391 float[4][4] currentModelMatrix() @safe @property; 392 393 /// ditto 394 void currentModelMatrix(float[4][4] matrix) @safe @property; 395 396 /// Reset current model matrix. 397 final void resetModelMatrix() @safe 398 { 399 this.currentModelMatrix = identity(); 400 } 401 402 /++ 403 Renders an object. 404 405 See_Also: `tida.graph.drawable`. 406 +/ 407 void draw(IDrawable drawable, Vecf position) @safe; 408 409 /// ditto 410 void drawEx( IDrawableEx drawable, 411 Vecf position, 412 float angle, 413 Vecf center, 414 Vecf size, 415 ubyte alpha, 416 Color!ubyte color = rgb(255, 255, 255)) @safe; 417 } 418 419 /++ 420 Render objects using hardware acceleration through an open graphics library. 421 +/ 422 class GLRender : IRenderer 423 { 424 import tida.window; 425 import tida.gl; 426 import tida.shader; 427 import tida.vertgen; 428 import tida.color; 429 import tida.vector; 430 import tida.matrix; 431 import tida.shape; 432 import tida.drawable; 433 434 enum deprecatedVertex = 435 " 436 #version 130 437 in vec3 position; 438 439 uniform mat4 projection; 440 uniform mat4 model; 441 442 void main() 443 { 444 gl_Position = projection * model * vec4(position, 1.0f); 445 } 446 "; 447 448 enum deprecatedFragment = 449 " 450 #version 130 451 uniform vec4 color = vec4(1.0f, 1.0f, 1.0f, 1.0f); 452 453 void main() 454 { 455 gl_FragColor = color; 456 } 457 "; 458 459 enum modernVertex = 460 " 461 #version 330 core 462 layout (location = 0) in vec3 position; 463 464 uniform mat4 projection; 465 uniform mat4 model; 466 467 void main() 468 { 469 gl_Position = projection * model * vec4(position, 1.0f); 470 } 471 "; 472 473 enum modernFragment = 474 " 475 #version 330 core 476 uniform vec4 color = vec4(1.0f, 1.0f, 1.0f, 1.0f); 477 478 out vec4 fragColor; 479 480 void main() 481 { 482 fragColor = color; 483 } 484 "; 485 486 private: 487 IWindow window; 488 Color!ubyte _background; 489 Camera _camera; 490 mat4 _projection; 491 492 Shader!Program[string] shaders; 493 Shader!Program current; 494 495 mat4 _model; 496 497 bool _isModern = false; 498 ITarget _target; 499 500 public @trusted: 501 this(IWindow window) 502 { 503 this.window = window; 504 505 _camera = new Camera(); 506 _camera.shape = Shapef.Rectangle(vecf(0, 0), vecf(window.width, window.height)); 507 _camera.port = _camera.shape; 508 509 Shader!Program defaultShader = new Shader!Program(); 510 511 string vsource, fsource; 512 513 if (glslVersion == "1.10" || glslVersion == "1.20" || glslVersion == "1.30") 514 { 515 vsource = deprecatedVertex; 516 fsource = deprecatedFragment; 517 _isModern = false; 518 } else 519 { 520 vsource = modernVertex; 521 fsource = modernFragment; 522 _isModern = true; 523 } 524 525 Shader!Vertex defaultVertex = new Shader!Vertex(); 526 defaultVertex.bindSource(vsource); 527 528 Shader!Fragment defaultFragment = new Shader!Fragment(); 529 defaultFragment.bindSource(fsource); 530 531 defaultShader.attach(defaultVertex); 532 defaultShader.attach(defaultFragment); 533 defaultShader.link(); 534 535 setShader("Default", defaultShader); 536 537 _model = identity(); 538 blendMode(BlendMode.withBlend); 539 blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha); 540 541 this.reshape(); 542 } 543 544 @property Shader!Program[string] getShaders() 545 { 546 return shaders; 547 } 548 549 @property mat4 projection() 550 { 551 return _projection; 552 } 553 554 int glBlendFactor(BlendFactor factor) 555 { 556 if (factor == BlendFactor.Zero) 557 return GL_ZERO; 558 else 559 if (factor == BlendFactor.One) 560 return GL_ONE; 561 else 562 if (factor == BlendFactor.SrcColor) 563 return GL_SRC_COLOR; 564 else 565 if (factor == BlendFactor.DstColor) 566 return GL_DST_COLOR; 567 else 568 if (factor == BlendFactor.OneMinusSrcColor) 569 return GL_ONE_MINUS_SRC_COLOR; 570 else 571 if (factor == BlendFactor.OneMinusDstColor) 572 return GL_ONE_MINUS_DST_COLOR; 573 else 574 if (factor == BlendFactor.SrcAlpha) 575 return GL_SRC_ALPHA; 576 else 577 if (factor == BlendFactor.DstAlpha) 578 return GL_DST_ALPHA; 579 else 580 if (factor == BlendFactor.OneMinusSrcAlpha) 581 return GL_ONE_MINUS_SRC_ALPHA; 582 else 583 if (factor == BlendFactor.OneMinusDstAlpha) 584 return GL_ONE_MINUS_DST_ALPHA; 585 586 return 0; 587 } 588 589 void setDefaultUniform(Color!ubyte color) 590 { 591 if (currentShader.getUniformLocation("projection") != -1) 592 currentShader.setUniform("projection", _projection); 593 594 if (currentShader.getUniformLocation("color") != -1) 595 currentShader.setUniform("color", color); 596 597 if (currentShader.getUniformLocation("model") != -1) 598 currentShader.setUniform("model", _model); 599 } 600 601 @property bool isModern() 602 { 603 return _isModern; 604 } 605 606 override: 607 void draw(IDrawable drawable, Vecf position) @safe 608 { 609 drawable.draw(this, position - camera.port.begin); 610 } 611 612 /// ditto 613 void drawEx(IDrawableEx drawable, 614 Vecf position, 615 float angle, 616 Vecf center, 617 Vecf size, 618 ubyte alpha, 619 Color!ubyte color = rgb(255, 255, 255)) @safe 620 { 621 drawable.drawEx(this, position - camera.port.begin, angle, center, size, alpha, color); 622 } 623 624 void reshape() 625 { 626 import std.conv : to; 627 628 int yborder = 0; 629 630 version (Windows) 631 { 632 if (_target is null) 633 yborder = (cast(Window) window).windowBorderSize; 634 } 635 636 glViewport( 637 -_camera.shape.begin.x.to!int, 638 -_camera.shape.begin.y.to!int - yborder, 639 _camera.shape.end.x.to!int, 640 _camera.shape.end.y.to!int 641 ); 642 643 this._projection = ortho(0.0, _camera.port.end.x, _camera.port.end.y, 0.0, -1.0, 1.0); 644 } 645 646 @property void camera(Camera camera) 647 { 648 _camera = camera; 649 } 650 651 @property Camera camera() 652 { 653 return _camera; 654 } 655 656 void bindTarget(ITarget target) @safe 657 { 658 if (_target !is null) 659 _target.unbind(this); 660 661 if (target !is null) 662 target.bind(this); 663 664 _target = target; 665 } 666 667 @property ITarget currentTarget() @safe 668 { 669 return _target; 670 } 671 672 void point(Vecf vec, Color!ubyte color) 673 { 674 if (currentShader is null) 675 currentShader = getShader("Default"); 676 677 vec -= camera.port.begin; 678 679 scope vertexInfo = new VertexInfo!(float)(); 680 scope buffer = new BufferInfo!float(); 681 682 buffer.append (vec); 683 684 buffer.bind(); 685 vertexInfo.bind(); 686 buffer.move(); 687 688 vertexInfo.vertexAttribPointer( 689 currentShader.getAttribLocation("position"), 690 2, 691 2, 692 0 693 ); 694 buffer.unbind(); 695 696 debug (GLDebug) checkGLError(); 697 698 currentShader.using(); 699 currentShader.enableVertex("position"); 700 701 setDefaultUniform(color); 702 if (currentShader.getUniformLocation("size") != -1) 703 currentShader.setUniform("size", tida.vector.vec!float(1, 1)); 704 705 vertexInfo.draw (ShapeType.point); 706 707 currentShader.disableVertex("position"); 708 vertexInfo.unbind(); 709 710 resetShader(); 711 resetModelMatrix(); 712 } 713 714 void line(Vecf[2] points, Color!ubyte color) 715 { 716 if (currentShader is null) 717 currentShader = getShader("Default"); 718 719 scope vertexInfo = new VertexInfo!(float)(); 720 scope buffer = new BufferInfo!float(); 721 722 buffer.append (points[0] - camera.port.begin); 723 buffer.append (points[1] - camera.port.begin); 724 725 buffer.bind(); 726 vertexInfo.bind(); 727 buffer.move(); 728 729 vertexInfo.vertexAttribPointer( 730 currentShader.getAttribLocation("position"), 731 2, 732 2, 733 0 734 ); 735 buffer.unbind(); 736 737 debug (GLDebug) checkGLError(); 738 739 currentShader.using(); 740 currentShader.enableVertex("position"); 741 742 setDefaultUniform(color); 743 if (currentShader.getUniformLocation("size") != -1) 744 currentShader.setUniform("size", abs(points[1] - points[0])); 745 746 vertexInfo.draw (ShapeType.line); 747 748 currentShader.disableVertex("position"); 749 vertexInfo.unbind(); 750 751 resetShader(); 752 resetModelMatrix(); 753 } 754 755 void rectangle(Vecf position, uint width, uint height, Color!ubyte color, bool isFill) 756 { 757 if (currentShader is null) 758 currentShader = getShader("Default"); 759 760 position -= camera.port.begin; 761 762 scope vertexInfo = new VertexInfo!(float)(); 763 scope buffer = new BufferInfo!float(); 764 scope elements = new ElementInfo!uint(); 765 vertexInfo.elements = elements; 766 767 buffer.append (position); 768 buffer.append (position + vec!float (width, 0)); 769 buffer.append (position + vec!float (width, height)); 770 buffer.append (position + vec!float (0, height)); 771 772 if (isFill) 773 { 774 elements.data = [0, 1, 3, 1, 3, 2]; 775 } else 776 { 777 elements.data = [0, 1, 1, 2, 2, 3, 3, 0]; 778 } 779 780 buffer.bind(); 781 vertexInfo.bind(); 782 elements.bind(); 783 784 elements.attach(); 785 buffer.move(); 786 787 vertexInfo.vertexAttribPointer( 788 currentShader.getAttribLocation("position"), 789 2, 790 2, 791 0 792 ); 793 buffer.unbind(); 794 795 debug (GLDebug) checkGLError(); 796 797 currentShader.using(); 798 currentShader.enableVertex("position"); 799 800 setDefaultUniform(color); 801 if (currentShader.getUniformLocation("size") != -1) 802 currentShader.setUniform("size", vec!float(width, height)); 803 804 if (isFill) 805 { 806 vertexInfo.draw (ShapeType.rectangle); 807 } else 808 { 809 vertexInfo.draw (ShapeType.line); 810 } 811 812 currentShader.disableVertex("position"); 813 elements.unbind(); 814 vertexInfo.unbind(); 815 816 resetShader(); 817 resetModelMatrix(); 818 } 819 820 void circle(Vecf position, float radius, Color!ubyte color, bool isFill) 821 { 822 if (currentShader is null) 823 currentShader = getShader("Default"); 824 825 position -= camera.port.begin; 826 827 scope vertexInfo = new VertexInfo!(float)(); 828 scope buffer = new BufferInfo!float(); 829 vertexInfo.buffer = buffer; 830 831 auto shape = isFill ? 832 Shape!float.Circle (position, radius) : 833 Shape!float.CircleLine (position, radius); 834 835 buffer.vertexData = generateBuffer!float(shape); 836 837 buffer.bind(); 838 vertexInfo.bind(); 839 buffer.attach(); 840 841 vertexInfo.vertexAttribPointer( 842 currentShader.getAttribLocation("position"), 843 2, 844 2, 845 0 846 ); 847 buffer.unbind(); 848 849 debug (GLDebug) checkGLError(); 850 851 currentShader.using(); 852 currentShader.enableVertex("position"); 853 854 setDefaultUniform(color); 855 if (currentShader.getUniformLocation("size") != -1) 856 currentShader.setUniform("size", vec!float(radius * 2, radius * 2)); 857 858 if (isFill) 859 { 860 vertexInfo.draw (ShapeType.circle); 861 } else 862 { 863 vertexInfo.draw (ShapeType.line, cast(uint) shape.shapes.length); 864 } 865 866 currentShader.disableVertex("position"); 867 vertexInfo.unbind(); 868 869 resetShader(); 870 resetModelMatrix(); 871 } 872 873 void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill) 874 { 875 if (currentShader is null) 876 currentShader = getShader("Default"); 877 878 position -= camera.port.begin; 879 880 scope vertexInfo = new VertexInfo!(float)(); 881 scope buffer = new BufferInfo!float(); 882 vertexInfo.buffer = buffer; 883 884 auto shape = isFill ? 885 Shape!float.RoundRectangle (position, position + vec!float(width, height), radius) : 886 Shape!float.RoundRectangleLine (position, position + vec!float(width, height), radius); 887 888 buffer.vertexData = generateBuffer!float(shape); 889 890 buffer.bind(); 891 vertexInfo.bind(); 892 buffer.attach(); 893 894 vertexInfo.vertexAttribPointer( 895 currentShader.getAttribLocation("position"), 896 2, 897 2, 898 0 899 ); 900 buffer.unbind(); 901 902 debug (GLDebug) checkGLError(); 903 904 currentShader.using(); 905 currentShader.enableVertex("position"); 906 907 setDefaultUniform(color); 908 if (currentShader.getUniformLocation("size") != -1) 909 currentShader.setUniform("size", vec!float(width, height)); 910 911 if (isFill) 912 { 913 vertexInfo.draw (ShapeType.roundrect); 914 } else 915 { 916 vertexInfo.draw (ShapeType.line, cast(uint) shape.shapes.length); 917 } 918 919 currentShader.disableVertex("position"); 920 vertexInfo.unbind(); 921 922 resetShader(); 923 resetModelMatrix(); 924 } 925 926 void triangle(Vecf[3] points, Color!ubyte color, bool isFill) 927 { 928 if (currentShader is null) 929 currentShader = getShader("Default"); 930 931 scope vertexInfo = new VertexInfo!(float)(); 932 scope buffer = new BufferInfo!float(); 933 934 auto shape = Shape!float(); 935 if (isFill) 936 shape = Shape!float.Triangle (points); 937 else 938 shape = Shape!float.TriangleLine (points); 939 940 buffer.vertexData = generateBuffer!float(shape); 941 942 buffer.bind(); 943 vertexInfo.bind(); 944 buffer.move(); 945 946 vertexInfo.vertexAttribPointer( 947 currentShader.getAttribLocation("position"), 948 2, 949 2, 950 0 951 ); 952 buffer.unbind(); 953 954 debug (GLDebug) checkGLError(); 955 956 currentShader.using(); 957 currentShader.enableVertex("position"); 958 959 setDefaultUniform(color); 960 if (currentShader.getUniformLocation("size") != -1) 961 currentShader.setUniform("size", abs(points[2] - points[0])); 962 963 if (isFill) 964 vertexInfo.draw (ShapeType.triangle); 965 else 966 vertexInfo.draw (ShapeType.line, cast(uint) shape.shapes.length); 967 968 currentShader.disableVertex("position"); 969 vertexInfo.unbind(); 970 971 resetShader(); 972 resetModelMatrix(); 973 } 974 975 void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill) 976 { 977 import std.algorithm : each; 978 979 if (currentShader is null) 980 currentShader = getShader("Default"); 981 982 position -= camera.port.begin; 983 984 scope vertexInfo = new VertexInfo!(float)(); 985 scope buffer = new BufferInfo!float(); 986 vertexInfo.buffer = buffer; 987 988 auto shape = Shape!float(); 989 990 if (isFill) 991 Shape!float.Polygon (points, position); 992 else 993 Shape!float.PolygonLine (points, position); 994 995 buffer.vertexData = generateBuffer!float(shape); 996 997 buffer.bind(); 998 vertexInfo.bind(); 999 buffer.attach(); 1000 1001 vertexInfo.vertexAttribPointer( 1002 currentShader.getAttribLocation("position"), 1003 2, 1004 2, 1005 0 1006 ); 1007 buffer.unbind(); 1008 1009 debug (GLDebug) checkGLError(); 1010 1011 currentShader.using(); 1012 currentShader.enableVertex("position"); 1013 1014 setDefaultUniform(color); 1015 if (currentShader.getUniformLocation("size") != -1) 1016 currentShader.setUniform("size", vec!float(1, 1)); 1017 1018 if (isFill) 1019 vertexInfo.draw (ShapeType.polygon); 1020 else 1021 vertexInfo.draw (ShapeType.line, cast(uint) shape.shapes.length); 1022 1023 currentShader.disableVertex("position"); 1024 vertexInfo.unbind(); 1025 1026 resetShader(); 1027 resetModelMatrix(); 1028 } 1029 1030 @property RenderType type() 1031 { 1032 return RenderType.opengl; 1033 } 1034 1035 @property void background(Color!ubyte color) 1036 { 1037 _background = color; 1038 glClearColor(color.rf, color.gf, color.bf, color.af); 1039 } 1040 1041 @property Color!ubyte background() 1042 { 1043 return _background; 1044 } 1045 1046 void clear() 1047 { 1048 glClear(GL_COLOR_BUFFER_BIT); 1049 } 1050 1051 void drawning() 1052 { 1053 if (_target is null) 1054 window.swapBuffers(); 1055 else 1056 _target.drawning(this); 1057 } 1058 1059 void blendMode(BlendMode mode) 1060 { 1061 if (mode == BlendMode.withBlend) 1062 { 1063 glEnable(GL_BLEND); 1064 } else 1065 if (mode == BlendMode.withoutBlend) 1066 { 1067 glDisable(GL_BLEND); 1068 } 1069 } 1070 1071 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1072 { 1073 glBlendFunc(glBlendFactor(sfactor), glBlendFactor(dfactor)); 1074 } 1075 1076 void currentShader(Shader!Program program) 1077 { 1078 current = program; 1079 } 1080 1081 Shader!Program currentShader() 1082 { 1083 return current; 1084 } 1085 1086 void currentModelMatrix(float[4][4] matrix) 1087 { 1088 _model = matrix; 1089 } 1090 1091 float[4][4] currentModelMatrix() 1092 { 1093 return _model; 1094 } 1095 1096 void setShader(string name, Shader!Program program) 1097 { 1098 shaders[name] = program; 1099 } 1100 1101 Shader!Program getShader(string name) 1102 { 1103 if (name in shaders) 1104 return shaders[name]; 1105 else 1106 return null; 1107 } 1108 1109 void resetShader() 1110 { 1111 current = null; 1112 } 1113 } 1114 1115 debug (GLDebug) class GLDebugRender : IRenderer 1116 { 1117 import tida.vector; 1118 import tida.color; 1119 import tida.drawable; 1120 import tida.shader; 1121 1122 public struct Operation 1123 { 1124 string name; 1125 1126 IDrawable object; 1127 IDrawableEx objectEx; 1128 1129 Vecf size; 1130 Vecf center; 1131 float angle; 1132 ubyte alpha; 1133 1134 Color!ubyte color; 1135 bool isFill = true; 1136 1137 Shader!Program shader; 1138 string shaderName; 1139 float[4][4] matrix; 1140 1141 Vecf[] vertexs; 1142 float radius; 1143 1144 BlendFactor sfactor; 1145 BlendFactor dfactor; 1146 } 1147 1148 private: 1149 GLRender render; 1150 size_t frame; 1151 Color!ubyte _background; 1152 1153 public: 1154 Operation[][] operations; 1155 1156 @safe: 1157 this(GLRender render) 1158 { 1159 this.render = render; 1160 1161 operations ~= [[]]; 1162 frame = 1; 1163 } 1164 1165 string findShadername(Shader!Program shader) 1166 { 1167 auto shaders = render.getShaders(); 1168 1169 foreach (key, value; shaders) 1170 { 1171 if (value is shader) 1172 return key; 1173 } 1174 1175 return "unknown"; 1176 } 1177 1178 string textReport() @safe 1179 { 1180 import std.conv : to; 1181 1182 string data; 1183 size_t iframe = 0; 1184 1185 foreach (frame; operations) 1186 { 1187 iframe++; 1188 data ~= "Frame " ~ iframe.to!string ~ ":\n"; 1189 foreach (operand; frame) 1190 { 1191 data ~= operand.name ~ ":\n"; 1192 data ~= "L color: " ~ operand.color.to!string ~ "\n"; 1193 data ~= "L isFill: " ~ operand.isFill.to!string ~ "\n"; 1194 data ~= "L shader: " ~ operand.shaderName ~ "\n"; 1195 data ~= "L matrix: " ~ operand.matrix.to!string ~ "\n"; 1196 data ~= "L vertexs: " ~ operand.vertexs.to!string ~ "\n\n"; 1197 } 1198 } 1199 1200 return data; 1201 } 1202 1203 void drawFrame(int idframe) @safe 1204 { 1205 auto frame = operations[idframe]; 1206 1207 foreach (i; 0 .. cast(int) frame.length) 1208 drawOperation(idframe, i); 1209 } 1210 1211 void drawOperation(int idframe, int idoper) @safe 1212 { 1213 auto operation = operations[idframe][idoper]; 1214 1215 if (operation.object !is null) 1216 { 1217 render.draw(operation.object, operation.vertexs[0]); 1218 } else 1219 if (operation.objectEx !is null) 1220 { 1221 render.drawEx(operation.objectEx, operation.vertexs[0], 1222 operation.angle, 1223 operation.center, 1224 operation.size, 1225 operation.alpha, 1226 operation.color); 1227 } else 1228 { 1229 switch (operation.name) 1230 { 1231 case "tida.render.GLRender.point": 1232 render.point(operation.vertexs[0], operation.color); 1233 break; 1234 1235 case "tida.render.GLRender.line": 1236 render.line([operation.vertexs[0], operation.vertexs[1]], operation.color); 1237 break; 1238 1239 case "tida.render.GLRender.rectangle": { 1240 uint width = cast(uint) (operation.vertexs[1].x - operation.vertexs[0].x); 1241 uint height = cast(uint) (operation.vertexs[1].y - operation.vertexs[0].y); 1242 1243 render.rectangle(operation.vertexs[0], width, height, operation.color, operation.isFill); 1244 } 1245 break; 1246 1247 case "tida.render.GLRender.triangle": 1248 render.triangle([operation.vertexs[0], operation.vertexs[1], operation.vertexs[2]], 1249 operation.color, operation.isFill); 1250 break; 1251 1252 case "tida.render.GLRender.circle": 1253 render.circle(operation.vertexs[0], operation.radius, operation.color, operation.isFill); 1254 break; 1255 1256 case "tida.render.GLRender.roundrect": { 1257 uint width = cast(uint) (operation.vertexs[1].x - operation.vertexs[0].x); 1258 uint height = cast(uint) (operation.vertexs[1].y - operation.vertexs[0].y); 1259 1260 render.roundrect(operation.vertexs[0], width, height, operation.radius, operation.color, operation.isFill); 1261 } 1262 break; 1263 1264 case "tida.render.GLRender.polygon": 1265 render.polygon(operation.vertexs[0], operation.vertexs[1 .. $], operation.color, operation.isFill); 1266 break; 1267 1268 default: 1269 return; 1270 } 1271 } 1272 } 1273 1274 override: 1275 void bindTarget(ITarget target) @trusted 1276 { 1277 Operation operation; 1278 operation.name = "bindTarget"; 1279 1280 operations[frame - 1] ~= operation; 1281 1282 render.bindTarget(target); 1283 } 1284 1285 @property ITarget currentTarget() @trusted 1286 { 1287 Operation operation; 1288 operation.name = "currentTarget"; 1289 1290 operations[frame - 1] ~= operation; 1291 1292 return render.currentTarget; 1293 } 1294 1295 void draw(IDrawable drawable, Vecf position) @trusted 1296 { 1297 Operation operation; 1298 operation.name = (cast(Object) drawable).toString; 1299 operation.object = drawable; 1300 operation.vertexs ~= position; 1301 operation.shader = render.currentShader !is null ? 1302 render.currentShader : 1303 render.getShader("Default"); 1304 operation.shaderName = render.currentShader !is null ? 1305 findShadername(render.currentShader) : 1306 "Default"; 1307 1308 operations[frame - 1] ~= operation; 1309 1310 render.draw(drawable, position); 1311 } 1312 1313 /// ditto 1314 void drawEx( IDrawableEx drawable, 1315 Vecf position, 1316 float angle, 1317 Vecf center, 1318 Vecf size, 1319 ubyte alpha, 1320 Color!ubyte color = rgb(255, 255, 255)) @trusted 1321 { 1322 Operation operation; 1323 operation.name = (cast(Object) drawable).toString; 1324 operation.objectEx = drawable; 1325 operation.vertexs ~= [position, position + (size.isVecfNaN ? vecfZero : size)]; 1326 operation.shader = render.currentShader !is null ? 1327 render.currentShader : 1328 render.getShader("Default"); 1329 1330 operation.angle = angle; 1331 operation.center = center; 1332 operation.size = size; 1333 operation.alpha = alpha; 1334 1335 operation.shaderName = render.currentShader !is null ? 1336 findShadername(render.currentShader) : 1337 "Default"; 1338 1339 operation.color = color; 1340 1341 operations[frame - 1] ~= operation; 1342 1343 render.drawEx(drawable, position, angle, center, size, alpha, color); 1344 } 1345 1346 void clear() 1347 { 1348 frame++; 1349 operations.length += 1; 1350 1351 Operation operation; 1352 operation.name = "tida.render.GLRender.clear"; 1353 operation.color = render.background; 1354 operations[frame - 1] ~= operation; 1355 1356 render.clear(); 1357 } 1358 1359 void drawning() 1360 { 1361 Operation operation; 1362 operation.name = "tida.render.GLRender.drawning"; 1363 operations[frame - 1] ~= operation; 1364 1365 render.drawning(); 1366 } 1367 1368 void point(Vecf position, Color!ubyte color) 1369 { 1370 Operation operation; 1371 operation.name = "tida.render.GLRender.point"; 1372 operation.color = color; 1373 operation.shader = render.currentShader !is null ? 1374 render.currentShader : 1375 render.getShader("Default"); 1376 1377 operation.shaderName = render.currentShader !is null ? 1378 findShadername(render.currentShader) : 1379 "Default"; 1380 1381 operation.vertexs ~= position; 1382 operations[frame - 1] ~= operation; 1383 1384 render.point(position, color); 1385 } 1386 1387 void line(Vecf[2] points, Color!ubyte color) 1388 { 1389 Operation operation; 1390 operation.name = "tida.render.GLRender.line"; 1391 operation.color = color; 1392 operation.shader = render.currentShader !is null ? 1393 render.currentShader : 1394 render.getShader("Default"); 1395 1396 operation.shaderName = render.currentShader !is null ? 1397 findShadername(render.currentShader) : 1398 "Default"; 1399 1400 operation.vertexs ~= [points[0], points[1]]; 1401 operations[frame - 1] ~= operation; 1402 1403 render.line(points, color); 1404 } 1405 1406 void rectangle(Vecf position, uint width, uint height, Color!ubyte color, bool isFill) 1407 { 1408 Operation operation; 1409 operation.name = "tida.render.GLRender.rectangle"; 1410 operation.color = color; 1411 operation.shader = render.currentShader !is null ? 1412 render.currentShader : 1413 render.getShader("Default"); 1414 1415 operation.shaderName = render.currentShader !is null ? 1416 findShadername(render.currentShader) : 1417 "Default"; 1418 1419 operation.vertexs ~= [position, position + vecf(width, height)]; 1420 operation.isFill = isFill; 1421 1422 operations[frame - 1] ~= operation; 1423 1424 render.rectangle(position, width, height, color, isFill); 1425 } 1426 1427 void triangle(Vecf[3] points, Color!ubyte color, bool isFill) 1428 { 1429 Operation operation; 1430 operation.name = "tida.render.GLRender.triangle"; 1431 operation.color = color; 1432 operation.shader = render.currentShader !is null ? 1433 render.currentShader : 1434 render.getShader("Default"); 1435 1436 operation.shaderName = render.currentShader !is null ? 1437 findShadername(render.currentShader) : 1438 "Default"; 1439 1440 operation.vertexs ~= [points[0], points[1], points[2]]; 1441 operation.isFill = isFill; 1442 1443 operations[frame - 1] ~= operation; 1444 1445 render.triangle(points, color, isFill); 1446 } 1447 1448 void circle(Vecf position, float radius, Color!ubyte color, bool isFill) 1449 { 1450 Operation operation; 1451 operation.name = "tida.render.GLRender.circle"; 1452 operation.color = color; 1453 operation.shader = render.currentShader !is null ? 1454 render.currentShader : 1455 render.getShader("Default"); 1456 1457 operation.shaderName = render.currentShader !is null ? 1458 findShadername(render.currentShader) : 1459 "Default"; 1460 1461 operation.vertexs ~= [position]; 1462 operation.isFill = isFill; 1463 operation.radius = radius; 1464 1465 operations[frame - 1] ~= operation; 1466 1467 render.circle(position, radius, color, isFill); 1468 } 1469 1470 void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill) 1471 { 1472 Operation operation; 1473 operation.name = "tida.render.GLRender.polygon"; 1474 operation.color = color; 1475 operation.shader = render.currentShader !is null ? 1476 render.currentShader : 1477 render.getShader("Default"); 1478 1479 operation.shaderName = render.currentShader !is null ? 1480 findShadername(render.currentShader) : 1481 "Default"; 1482 1483 operation.vertexs ~= [position] ~ points; 1484 operation.isFill = isFill; 1485 1486 operations[frame - 1] ~= operation; 1487 1488 render.polygon(position, points, color, isFill); 1489 } 1490 1491 void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill) 1492 { 1493 Operation operation; 1494 operation.name = "tida.render.GLRender.line"; 1495 operation.color = color; 1496 operation.shader = render.currentShader !is null ? 1497 render.currentShader : 1498 render.getShader("Default"); 1499 1500 operation.shaderName = render.currentShader !is null ? 1501 findShadername(render.currentShader) : 1502 "Default"; 1503 1504 operation.vertexs ~= [position, position + vecf(width, height)]; 1505 operation.isFill = isFill; 1506 operation.radius = radius; 1507 1508 operations[frame - 1] ~= operation; 1509 1510 render.roundrect(position, width, height, radius, color, isFill); 1511 } 1512 1513 Shader!Program getShader(string name) 1514 { 1515 Operation operation; 1516 operation.name = "tida.render.GLRender.getShader"; 1517 operation.shader = render.getShader(name); 1518 operation.shaderName = name; 1519 1520 operations[frame - 1] ~= operation; 1521 1522 return render.getShader(name); 1523 } 1524 1525 void setShader(string name, Shader!Program shader) 1526 { 1527 Operation operation; 1528 operation.name = "tida.render.GLRender.setShader"; 1529 operation.shader = shader; 1530 operation.shaderName = name; 1531 1532 operations[frame - 1] ~= operation; 1533 1534 render.setShader(name, shader); 1535 } 1536 1537 @property RenderType type() 1538 { 1539 return RenderType.opengl; 1540 } 1541 1542 @property void currentModelMatrix(float[4][4] matrix) 1543 { 1544 Operation operation; 1545 operation.name = "tida.render.GLRender.currentModelMatrix[set]"; 1546 operation.matrix = matrix; 1547 1548 operations[frame - 1] ~= operation; 1549 1550 render.currentModelMatrix = matrix; 1551 } 1552 1553 @property float[4][4] currentModelMatrix() 1554 { 1555 Operation operation; 1556 operation.name = "tida.render.GLRender.currentModeMatrix[get]"; 1557 1558 operations[frame - 1] ~= operation; 1559 1560 return render.currentModelMatrix; 1561 } 1562 1563 @property Camera camera() 1564 { 1565 Operation operation; 1566 operation.name = "tida.render.GLRender.camera[get]"; 1567 1568 operations[frame - 1] ~= operation; 1569 1570 return render.camera; 1571 } 1572 1573 @property void camera(Camera value) 1574 { 1575 Operation operation; 1576 operation.name = "tida.render.GLRender.camera[set]"; 1577 1578 operations[frame - 1] ~= operation; 1579 1580 render.camera = value; 1581 } 1582 1583 @property void blendMode(BlendMode mode) 1584 { 1585 Operation operation; 1586 operation.name = "tida.render.GLRender.blendMode[set]"; 1587 1588 operations[frame - 1] ~= operation; 1589 1590 render.blendMode(mode); 1591 } 1592 1593 @property void reshape() 1594 { 1595 Operation operation; 1596 operation.name = "tida.render.GLRender.reshape"; 1597 operation.matrix = render.projection; 1598 operation.vertexs = [camera.port.begin, camera.port.end]; 1599 1600 operations[frame - 1] ~= operation; 1601 1602 render.reshape(); 1603 } 1604 1605 @property Color!ubyte background() 1606 { 1607 Operation operation; 1608 operation.name = "tida.render.GLRender.background[get]"; 1609 operation.color = render.background; 1610 1611 operations[frame - 1] ~= operation; 1612 1613 return render.background; 1614 } 1615 1616 @property void background(Color!ubyte color) 1617 { 1618 Operation operation; 1619 operation.name = "tida.render.GLRender.background[set]"; 1620 operation.color = color; 1621 1622 operations[frame - 1] ~= operation; 1623 1624 render.background = color; 1625 } 1626 1627 @property void currentShader(Shader!Program shader) 1628 { 1629 Operation operation; 1630 operation.name = "tida.render.GLRender.currentShader[set]"; 1631 operation.shader = shader; 1632 operation.shaderName = findShadername(shader); 1633 1634 operations[frame - 1] ~= operation; 1635 1636 render.currentShader = shader; 1637 } 1638 1639 @property Shader!Program currentShader() 1640 { 1641 Operation operation; 1642 operation.name = "tida.render.GLRender.currentShader[get]"; 1643 operation.shader = render.currentShader; 1644 operation.shaderName = findShadername(render.currentShader); 1645 1646 operations[frame - 1] ~= operation; 1647 1648 return render.currentShader; 1649 } 1650 1651 void resetShader() 1652 { 1653 Operation operation; 1654 operation.name = "tida.render.GLRender.resetShader"; 1655 operation.shader = render.currentShader; 1656 operation.shaderName = findShadername(render.currentShader); 1657 1658 operations[frame - 1] ~= operation; 1659 1660 render.resetShader(); 1661 } 1662 1663 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1664 { 1665 Operation operation; 1666 operation.name = "tida.render.GLRender.blendOperation"; 1667 operation.sfactor = sfactor; 1668 operation.dfactor = dfactor; 1669 1670 operations[frame - 1] ~= operation; 1671 1672 render.blendOperation(sfactor, dfactor); 1673 } 1674 } 1675 1676 /++ 1677 Implementation of the interface for interacting with the rendering canvas. 1678 +/ 1679 interface ICanvas 1680 { 1681 import tida.color; 1682 import tida.vector; 1683 1684 @safe: 1685 /++ 1686 Allocate memory for the canvas at the specified size. 1687 1688 Params: 1689 width = Canvas width. 1690 height = Canvas height. 1691 +/ 1692 void allocatePlace(uint width, uint height); 1693 1694 /++ 1695 Cleared the canvas with one color. 1696 1697 Params: 1698 color = Cleared color. 1699 +/ 1700 void clearPlane(Color!ubyte color); 1701 1702 /++ 1703 Draws a buffer to a storage object. 1704 +/ 1705 void drawTo(); 1706 1707 /// Blending mode (blend or not). 1708 @property void blendMode(BlendMode mode); 1709 1710 /// ditto 1711 @property BlendMode blendMode(); 1712 1713 /++ 1714 Sets the color mixing factor (which formula to mix colors with). 1715 +/ 1716 void blendOperation(BlendFactor sfactor, BlendFactor dfactor); 1717 1718 /++ 1719 Mixing factor (sfactor, dfactor). 1720 +/ 1721 BlendFactor[2] blendOperation(); 1722 1723 /++ 1724 Draw a point on the canvas. 1725 Draw only a point, the rest of the shapes are rendered. 1726 1727 Params: 1728 position = Point position. 1729 color = Point color. 1730 +/ 1731 void pointTo(Vecf position, Color!ubyte color); 1732 /++ 1733 Set port of visibility. 1734 1735 Params: 1736 w = Port width. 1737 h = Port height. 1738 +/ 1739 void viewport(uint w, uint h); 1740 1741 /++ 1742 Move the visibility port to the specified coordinates. 1743 1744 Params: 1745 x = Port x-axis position. 1746 y = Port y-axis position. 1747 +/ 1748 void move(int x,int y); 1749 1750 /++ 1751 Canvas data. 1752 +/ 1753 @property ref ubyte[] data(); 1754 1755 /// Canvas size. 1756 @property uint[2] size(); 1757 1758 /++ 1759 The real size of the world, where from the world it will be drawn to 1760 the size of the canvas. 1761 +/ 1762 @property uint[2] portSize(); 1763 1764 /++ 1765 Camera position (offset of all drawing points). 1766 +/ 1767 @property int[2] cameraPosition(); 1768 } 1769 1770 template PointToImpl(int pixelformat, int bpc) 1771 { 1772 import tida.vector; 1773 import tida.color; 1774 1775 static assert(isValidFormat!pixelformat); 1776 1777 static if(bpc == 0) 1778 enum bytesperpixel = bytesPerColor!pixelformat; 1779 else 1780 enum bytesperpixel = bpc; 1781 1782 override void pointTo(Vecf position, Color!ubyte color) 1783 { 1784 import tida.each : Coord; 1785 import std.conv : to; 1786 1787 position = position - vecf(cameraPosition); 1788 1789 immutable scaleWidth = (cast(float) portSize[0]) / (cast(float) size[0]); 1790 immutable scaleHeight = (cast(float) portSize[1]) / (cast(float) size[1]); 1791 int w = size[0] / portSize[0] + 1; 1792 int h = size[0] / portSize[1] + 1; 1793 1794 position = position / vecf(scaleWidth, scaleHeight); 1795 1796 Color!ubyte bcolor; 1797 1798 foreach (ix, iy; Coord( position.x.to!int + w, position.y.to!int + h, 1799 position.x.to!int, position.y.to!int)) 1800 { 1801 if (ix >= size[0] || iy >= size[1] || ix < 0 || iy < 0) continue; 1802 immutable pos = ((iy * size[0]) + ix) * bytesperpixel; 1803 1804 if (blendMode == BlendMode.withBlend) { 1805 Color!ubyte blendcolor; 1806 1807 static if (pixelformat == PixelFormat.BGRA) 1808 { 1809 blendcolor = rgba( data[pos+3], 1810 data[pos+2], 1811 data[pos+1], 1812 data[pos]); 1813 }else 1814 static if (pixelformat == PixelFormat.BGR) 1815 { 1816 blendcolor = rgba( data[pos+2], 1817 data[pos+1], 1818 data[pos], 1819 255); 1820 }else 1821 static if (pixelformat == PixelFormat.RGBA) 1822 { 1823 blendcolor = rgba( data[pos], 1824 data[pos+1], 1825 data[pos+2], 1826 data[pos+3]); 1827 }else 1828 static if (pixelformat == PixelFormat.RGB) 1829 { 1830 blendcolor = rgba( data[pos], 1831 data[pos+1], 1832 data[pos+2], 1833 255); 1834 } 1835 1836 BlendFactor[2] factors = blendOperation(); 1837 bcolor = BlendFunc!ubyte(factors[0], factors[1])(color, blendcolor); 1838 }else 1839 bcolor = color; 1840 1841 if (pos < data.length) 1842 { 1843 static if (pixelformat == PixelFormat.BGRA) 1844 { 1845 data[pos] = bcolor.b; 1846 data[pos+1] = bcolor.g; 1847 data[pos+2] = bcolor.r; 1848 data[pos+3] = bcolor.a; 1849 }else 1850 static if (pixelformat == PixelFormat.BGR) 1851 { 1852 data[pos] = bcolor.b; 1853 data[pos+1] = bcolor.g; 1854 data[pos+2] = bcolor.r; 1855 }else 1856 static if (pixelformat == PixelFormat.RGBA) 1857 { 1858 data[pos] = bcolor.r; 1859 data[pos+1] = bcolor.g; 1860 data[pos+2] = bcolor.b; 1861 data[pos+3] = bcolor.a; 1862 }else 1863 static if (pixelformat == PixelFormat.RGB) 1864 { 1865 data[pos] = bcolor.r; 1866 data[pos+1] = bcolor.g; 1867 data[pos+2] = bcolor.b; 1868 } 1869 } 1870 } 1871 } 1872 } 1873 1874 import tida.color : PixelFormat; 1875 1876 version(Posix) 1877 class Canvas : ICanvas 1878 { 1879 import x11.X, x11.Xlib, x11.Xutil; 1880 import tida.window; 1881 import tida.runtime; 1882 import tida.color; 1883 import std.exception : enforce; 1884 1885 private: 1886 GC context; 1887 XImage* ximage; 1888 tida.window.Window window; 1889 1890 ubyte[] buffer; 1891 uint width; 1892 uint height; 1893 1894 uint pwidth; 1895 uint pheight; 1896 1897 int xput = 0; 1898 int yput = 0; 1899 1900 bool isAlloc = true; 1901 1902 BlendFactor[2] bfactor; 1903 BlendMode bmode; 1904 1905 @trusted: 1906 public: 1907 this(tida.window.Window window, bool isAlloc = true) 1908 { 1909 this.isAlloc = isAlloc; 1910 1911 this.window = window; 1912 if (isAlloc) 1913 { 1914 context = XCreateGC(runtime.display, this.window.handle, 0, null); 1915 enforce!Exception(context, "Software context is not a create!"); 1916 } 1917 } 1918 1919 override: 1920 void allocatePlace(uint width, uint height) 1921 { 1922 this.width = width; 1923 this.height = height; 1924 1925 buffer = new ubyte[](width * height * bytesPerColor!(PixelFormat.BGRA)); 1926 if (isAlloc) 1927 { 1928 if(ximage !is null) { 1929 XFree(ximage); 1930 ximage = null; 1931 } 1932 1933 Visual* visual = window.getVisual(); 1934 int depth = window.getDepth(); 1935 1936 ximage = XCreateImage(runtime.display, visual, depth, 1937 ZPixmap, 0, cast(char*) buffer, width, height, 32, 0); 1938 1939 enforce!Exception(ximage, "[X11] XImage is not create!"); 1940 } 1941 } 1942 1943 void viewport(uint w, uint h) 1944 { 1945 pwidth = w; 1946 pheight = h; 1947 } 1948 1949 void move(int x, int y) 1950 { 1951 xput = x; 1952 yput = y; 1953 } 1954 1955 void clearPlane(Color!ubyte color) 1956 { 1957 for (size_t i = 0; 1958 i < width * height * bytesPerColor!(PixelFormat.BGRA); 1959 i += bytesPerColor!(PixelFormat.BGRA)) 1960 { 1961 buffer[i] = color.b; 1962 buffer[i+1] = color.g; 1963 buffer[i+2] = color.r; 1964 buffer[i+3] = color.a; 1965 } 1966 } 1967 1968 void drawTo() 1969 { 1970 if (isAlloc) 1971 { 1972 XPutImage( runtime.display, window.handle, context, ximage, 1973 xput, yput, xput, yput, width, height); 1974 1975 XSync(runtime.display, false); 1976 } 1977 } 1978 1979 BlendFactor[2] blendOperation() 1980 { 1981 return bfactor; 1982 } 1983 1984 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 1985 { 1986 bfactor = [sfactor, dfactor]; 1987 } 1988 1989 BlendMode blendMode() 1990 { 1991 return bmode; 1992 } 1993 1994 void blendMode(BlendMode mode) 1995 { 1996 bmode = mode; 1997 } 1998 1999 @property ref ubyte[] data() 2000 { 2001 return buffer; 2002 } 2003 2004 @property uint[2] size() 2005 { 2006 return [width, height]; 2007 } 2008 2009 @property uint[2] portSize() 2010 { 2011 return [pwidth, pheight]; 2012 } 2013 2014 @property int[2] cameraPosition() 2015 { 2016 return [xput, yput]; 2017 } 2018 2019 mixin PointToImpl!(PixelFormat.BGRA, 4); 2020 } 2021 2022 version(Windows) 2023 class Canvas : ICanvas 2024 { 2025 import core.sys.windows.windows; 2026 import tida.color; 2027 import std.exception : enforce; 2028 2029 private: 2030 PAINTSTRUCT paintstr; 2031 HDC hdc; 2032 HDC pdc; 2033 HBITMAP bitmap; 2034 2035 tida.window.Window window; 2036 2037 ubyte[] buffer; 2038 uint _width; 2039 uint _height; 2040 uint _pwidth; 2041 uint _pheight; 2042 int xput; 2043 int yput; 2044 2045 Color!ubyte _background; 2046 BlendMode bmode; 2047 BlendFactor sfactor; 2048 BlendFactor dfactor; 2049 2050 bool _isAlloc = true; 2051 2052 public @trusted: 2053 this(tida.window.Window window, bool isAlloc = true) 2054 { 2055 this.window = window; 2056 _isAlloc = isAlloc; 2057 } 2058 2059 void recreateBitmap() 2060 { 2061 if (bitmap !is null) 2062 DeleteObject(bitmap); 2063 2064 if (hdc is null) 2065 hdc = GetDC((cast(Window) window).handle); 2066 2067 bitmap = CreateBitmap(_width, _height, 1, 32, cast(LPBYTE) buffer); 2068 enforce(bitmap, "[WINAPI] bitmap is not a create!"); 2069 2070 if (pdc is null) 2071 pdc = CreateCompatibleDC(hdc); 2072 2073 SelectObject(pdc, bitmap); 2074 } 2075 2076 override: 2077 void allocatePlace(uint width, uint height) 2078 { 2079 _width = width; 2080 _height = height; 2081 2082 buffer = new ubyte[](_width * _height * 4); 2083 } 2084 2085 void viewport(uint width, uint height) 2086 { 2087 _pwidth = width; 2088 _pheight = height; 2089 } 2090 2091 void move(int x, int y) 2092 { 2093 xput = x; 2094 yput = y; 2095 } 2096 2097 void clearPlane(Color!ubyte color) 2098 { 2099 for (size_t i = 0; i < _width * _height * 4; i += 4) 2100 { 2101 buffer[i] = color.b; 2102 buffer[i + 1] = color.g; 2103 buffer[i + 2] = color.r; 2104 buffer[i + 3] = color.Max; 2105 } 2106 } 2107 2108 void drawTo() 2109 { 2110 if (_isAlloc) 2111 { 2112 recreateBitmap(); 2113 BitBlt(hdc, 0, 0, _width, _height, pdc, 0, 0, SRCCOPY); 2114 } 2115 } 2116 2117 BlendMode blendMode() 2118 { 2119 return bmode; 2120 } 2121 2122 void blendMode(BlendMode mode) 2123 { 2124 bmode = mode; 2125 } 2126 2127 BlendFactor[2] blendOperation() 2128 { 2129 return [sfactor, dfactor]; 2130 } 2131 2132 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 2133 { 2134 this.sfactor = sfactor; 2135 this.dfactor = dfactor; 2136 } 2137 2138 @property ref ubyte[] data() 2139 { 2140 return buffer; 2141 } 2142 2143 @property uint[2] size() 2144 { 2145 return [_width, _height]; 2146 } 2147 2148 @property uint[2] portSize() 2149 { 2150 return [_pwidth, _pheight]; 2151 } 2152 2153 @property int[2] cameraPosition() 2154 { 2155 return [xput, yput]; 2156 } 2157 2158 mixin PointToImpl!(PixelFormat.BGRA, 4); 2159 } 2160 2161 class Software : IRenderer 2162 { 2163 import tida.window; 2164 import tida.color; 2165 import tida.vector; 2166 import tida.shape; 2167 import tida.drawable; 2168 2169 private: 2170 ICanvas canvas; 2171 Camera _camera; 2172 Color!ubyte _background; 2173 2174 public @safe: 2175 this(IWindow window, bool isAlloc = true) 2176 { 2177 _camera = new Camera(); 2178 canvas = new Canvas(cast(Window) window); 2179 2180 _camera.port = Shape!float.Rectangle(vecf(0, 0), vecf(window.width, window.height)); 2181 _camera.shape = _camera.port; 2182 2183 canvas.blendMode(BlendMode.withBlend); 2184 canvas.blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha); 2185 2186 reshape(); 2187 } 2188 2189 this(ICanvas canvas, bool isAlloc = true) 2190 { 2191 _camera = new Camera(); 2192 this.canvas = canvas; 2193 2194 canvas.blendMode(BlendMode.withBlend); 2195 canvas.blendOperation(BlendFactor.SrcAlpha, BlendFactor.OneMinusSrcAlpha); 2196 } 2197 2198 override: 2199 @property RenderType type() 2200 { 2201 return RenderType.software; 2202 } 2203 2204 void bindTarget(ITarget target) @safe 2205 { 2206 assert(null, "Target not a support with software renderer!"); 2207 } 2208 2209 @property ITarget currentTarget() @safe 2210 { 2211 return null; 2212 } 2213 2214 void reshape() 2215 { 2216 import std.conv : to; 2217 2218 canvas.allocatePlace(_camera.shape.end.x.to!int,_camera.shape.end.y.to!int); 2219 canvas.viewport(_camera.port.end.x.to!int,_camera.port.end.y.to!int); 2220 canvas.move(_camera.port.begin.x.to!int,_camera.port.begin.y.to!int); 2221 } 2222 2223 @property Camera camera() 2224 { 2225 return _camera; 2226 } 2227 2228 @property void camera(Camera cam) 2229 { 2230 _camera = cam; 2231 } 2232 2233 @property Color!ubyte background() 2234 { 2235 return _background; 2236 } 2237 2238 @property void background(Color!ubyte color) 2239 { 2240 _background = color; 2241 } 2242 2243 void point(Vecf position, Color!ubyte color) 2244 { 2245 canvas.pointTo(position, color); 2246 } 2247 2248 void line(Vecf[2] points, Color!ubyte color) 2249 { 2250 import tida.each : Line; 2251 2252 foreach (x, y; Line(points[0], points[1])) 2253 canvas.pointTo(vecf(x, y), color); 2254 } 2255 2256 void rectangle( Vecf position, 2257 uint width, 2258 uint height, 2259 Color!ubyte color, 2260 bool isFill) 2261 { 2262 import tida.each : Coord; 2263 import std.conv : to; 2264 2265 if (isFill) 2266 { 2267 foreach (ix, iy; Coord(width.to!int, height.to!int)) 2268 { 2269 point(vecf(position.x.to!int + ix, position.y.to!int + iy), color); 2270 } 2271 }else 2272 { 2273 foreach (ix, iy; Coord(width.to!int, height.to!int)) 2274 { 2275 point(vecf(position.x.to!int + ix,position.y.to!int), color); 2276 point(vecf(position.x.to!int + ix,position.y.to!int + height.to!int), color); 2277 2278 point(vecf(position.x.to!int,position.y.to!int + iy), color); 2279 point(vecf(position.x.to!int + width.to!int,position.y.to!int + iy), color); 2280 } 2281 } 2282 } 2283 2284 void roundrect(Vecf position, uint width, uint height, float radius, Color!ubyte color, bool isFill) @safe 2285 { 2286 import std.math : cos, sin; 2287 2288 immutable size = vecf(width, height); 2289 immutable iter = 0.25; 2290 2291 position += camera.port.begin; 2292 2293 if (isFill) 2294 { 2295 rectangle(position + vecf(radius, 0), cast(int) (width - radius * 2), height, color, true); 2296 rectangle(position + vecf(0, radius), width, cast(int) (height - radius * 2), color, true); 2297 2298 void rounded(Vecf pos, float a, float b, float iter) @safe 2299 { 2300 import tida.angle; 2301 2302 for (float i = a; i <= b;) 2303 { 2304 Vecf temp; 2305 temp = pos + vecf(cos(i.from!(Degrees, Radians)), sin(i.from!(Degrees, Radians))) * radius; 2306 2307 line([pos, temp], color); 2308 i += iter; 2309 } 2310 } 2311 2312 rounded(position + vecf(radius, radius), 180, 270, iter); 2313 rounded(position + vecf(size.x - radius, radius), 270, 360, iter); 2314 rounded(position + vecf(radius, size.y - radius), 90, 180, iter); 2315 rounded(position + vecf(size.x - radius, size.y - radius), 0, 90, iter); 2316 }else 2317 { 2318 void rounded(Vecf pos, float a, float b,float iter) @safe 2319 { 2320 import tida.angle; 2321 2322 for (float i = a; i <= b;) 2323 { 2324 Vecf temp; 2325 temp = pos + vecf(cos(i.from!(Degrees, Radians)), sin(i.from!(Degrees, Radians))) * radius; 2326 point(temp, color); 2327 i += iter; 2328 } 2329 } 2330 2331 rounded(position + vecf(radius, radius), 180, 270, iter); 2332 rounded(position + vecf(size.x - radius, radius), 270, 360, iter); 2333 rounded(position + vecf(radius, size.y - radius), 90, 180, iter); 2334 rounded(position + vecf(size.x - radius, size.y - radius), 0, 90, iter); 2335 2336 line([position + vecf(radius, 0), position + vecf(width - radius, 0)], color); 2337 line([position + vecf(width, radius), position + vecf(width, height - radius)], color); 2338 line([position + vecf(radius, height), position + vecf(width - radius, height)], color); 2339 line([position + vecf(0, radius), position + vecf(0, height - radius)], color); 2340 } 2341 } 2342 2343 void circle(Vecf position, float radius, Color!ubyte color, bool isFill) 2344 { 2345 import tida.image, tida.each; 2346 2347 int rad = cast(int) radius; 2348 Image buffer = new Image(rad * 2, rad * 2); 2349 buffer.fill(rgba(255,255,255,0)); 2350 2351 int x = 0; 2352 int y = rad; 2353 2354 int X1 = rad; 2355 int Y1 = rad; 2356 2357 int delta = 1 - 2 * cast(int) radius; 2358 int error = 0; 2359 2360 void bufferLine(Vecf[2] points, Color!ubyte color) @safe 2361 { 2362 foreach (ix, iy; Line(points[0], points[1])) 2363 { 2364 buffer.setPixel(ix, iy, color); 2365 } 2366 } 2367 2368 while (y >= 0) 2369 { 2370 if (isFill) 2371 { 2372 bufferLine([Vecf(X1 + x, Y1 + y),Vecf(X1 + x, Y1 - y)],color); 2373 bufferLine([Vecf(X1 - x, Y1 + y),Vecf(X1 - x, Y1 - y)],color); 2374 }else 2375 { 2376 buffer.setPixel(X1 + x, Y1 + y,color); 2377 buffer.setPixel(X1 + x, Y1 - y,color); 2378 buffer.setPixel(X1 - x, Y1 + y,color); 2379 buffer.setPixel(X1 - x, Y1 - y,color); 2380 } 2381 2382 error = 2 * (delta + y) - 1; 2383 if ((delta < 0) && (error <= 0)) 2384 { 2385 delta += 2 * ++x + 1; 2386 continue; 2387 } 2388 2389 if ((delta > 0) && (error > 0)) 2390 { 2391 delta -= 2 * --y + 1; 2392 continue; 2393 } 2394 delta += 2 * (++x - --y); 2395 } 2396 2397 foreach (ix, iy; Coord(buffer.width, buffer.height)) 2398 { 2399 Color!ubyte pixel; 2400 2401 if ((pixel = buffer.getPixel(ix,iy)).a != 0) 2402 { 2403 point(position + vecf(ix, iy), pixel); 2404 } 2405 } 2406 } 2407 2408 void triangle(Vecf[3] position,Color!ubyte color,bool isFill) @trusted 2409 { 2410 import tida.each; 2411 2412 if (isFill) 2413 { 2414 foreach (x, y; Line(position[0], position[1])) { 2415 auto p = vecf(x,y); 2416 2417 line([p, position[2]], color); 2418 } 2419 } else 2420 { 2421 line([position[0], position[1]], color); 2422 line([position[1], position[2]], color); 2423 line([position[2], position[0]], color); 2424 } 2425 } 2426 2427 void polygon(Vecf position, Vecf[] points, Color!ubyte color, bool isFill) 2428 { 2429 import std.algorithm : each; 2430 points = points.dup; 2431 points.each!((ref e) => e = e + position); 2432 2433 if (!isFill) 2434 { 2435 int next = 0; 2436 for (int i = 0; i < points.length; i++) 2437 { 2438 next = (i + 1 == points.length) ? 0 : i + 1; 2439 line([points[i],points[next]], color); 2440 } 2441 }else 2442 { 2443 import std.algorithm : minElement, maxElement; 2444 import tida.collision : placeLineLineImpl; 2445 2446 float maxX = points.maxElement!"a.x".x; 2447 float minY = points.minElement!"a.y".y; 2448 float maxY = points.maxElement!"a.y".y; 2449 float minX = points.minElement!"a.x".x; 2450 2451 alias LineIter = Vecf[2]; 2452 2453 LineIter[] drowning; 2454 2455 for (float i = minY; i <= maxY; i += 1.0f) 2456 { 2457 Vecf firstPoint = vecfNaN; 2458 float lastX = minX > position.x ? position.x : minX; 2459 2460 for (float j = lastX; j <= maxX; j += 1.0f) 2461 { 2462 size_t next = 0; 2463 for (size_t currPointI = 0; currPointI < points.length; currPointI++) 2464 { 2465 next = (currPointI + 1 == points.length) ? 0 : currPointI + 1; 2466 2467 auto iter = placeLineLineImpl( [vecf(lastX, i), vecf(j, i)], 2468 [points[currPointI], points[next]]); 2469 if (!iter.isVecfNaN) { 2470 if (firstPoint.isVecfNaN) 2471 { 2472 firstPoint = vecf(j, i); 2473 } else 2474 { 2475 drowning ~= [firstPoint, vecf(j, i)]; 2476 firstPoint = vecfNaN; 2477 } 2478 2479 lastX = j; 2480 } 2481 } 2482 } 2483 } 2484 2485 foreach (e; drowning) 2486 { 2487 line(e, color); 2488 } 2489 } 2490 } 2491 2492 void clear() 2493 { 2494 import std.conv : to; 2495 2496 canvas.move(_camera.port.begin.x.to!int,_camera.port.begin.y.to!int); 2497 canvas.clearPlane(_background); 2498 } 2499 2500 void drawning() 2501 { 2502 canvas.drawTo(); 2503 } 2504 2505 @property void blendMode(BlendMode mode) 2506 { 2507 canvas.blendMode(mode); 2508 } 2509 2510 void blendOperation(BlendFactor sfactor, BlendFactor dfactor) 2511 { 2512 canvas.blendOperation(sfactor, dfactor); 2513 } 2514 2515 void draw(IDrawable drawable, Vecf position) @safe 2516 { 2517 position -= camera.port.begin; 2518 drawable.draw(this, position); 2519 } 2520 2521 /// ditto 2522 void drawEx( IDrawableEx drawable, 2523 Vecf position, 2524 float angle, 2525 Vecf center, 2526 Vecf size, 2527 ubyte alpha, 2528 Color!ubyte color = rgb(255, 255, 255)) @safe 2529 { 2530 position -= camera.port.begin; 2531 drawable.drawEx(this, position, angle, center, size, alpha, color); 2532 } 2533 2534 import tida.shader; 2535 2536 override Shader!Program getShader(string name) @safe 2537 { 2538 assert(null, "There are no shaders in this version of the render."); 2539 } 2540 2541 override void setShader(string name,Shader!Program program) @safe 2542 { 2543 assert(null, "There are no shaders in this version of the render."); 2544 } 2545 2546 override void currentShader(Shader!Program program) @safe @property 2547 { 2548 assert(null, "There are no shaders in this version of the render."); 2549 } 2550 2551 override Shader!Program currentShader() @safe @property 2552 { 2553 assert(null, "There are no shaders in this version of the render."); 2554 } 2555 2556 override void resetShader() @safe 2557 { 2558 assert(null, "There are no shaders in this version of the render."); 2559 } 2560 2561 override void currentModelMatrix(float[4][4] matrix) @safe @property 2562 { 2563 assert(null, "There are no matrix in this version of the render."); 2564 } 2565 2566 override float[4][4] currentModelMatrix() @safe @property 2567 { 2568 assert(null, "There are not matrix in this version of the render."); 2569 } 2570 } 2571 2572 import tida.window; 2573 2574 /++ 2575 Creates a render based on hardware acceleration capabilities. 2576 It should be used if the program does not use intentional hardware 2577 acceleration objects. 2578 2579 Params: 2580 window = Window object. 2581 +/ 2582 IRenderer createRenderer(IWindow window) @trusted 2583 { 2584 import bindbc.opengl; 2585 2586 if (isOpenGLLoaded()) 2587 { 2588 immutable ver = loadedOpenGLVersion(); 2589 if (ver != GLSupport.gl11 && ver != GLSupport.gl12 && 2590 ver != GLSupport.gl13 && ver != GLSupport.gl14 && 2591 ver != GLSupport.gl15) 2592 { 2593 return new GLRender(window); 2594 } 2595 } 2596 2597 return new Software(window, true); 2598 } 2599 2600 import tida.image; 2601 import tida.vector; 2602 2603 /++ 2604 Reads frame image data. Time consuming operation. 2605 2606 Params: 2607 render = render instance. 2608 position = Begin position read. 2609 width = frame width. 2610 height = frame height. 2611 +/ 2612 Image renderRead(IRenderer render, Vecf position, int width, int height) @trusted 2613 { 2614 import tida.gl; 2615 import std.conv : to; 2616 2617 Image image = new Image(width, height); 2618 glReadPixels( position.x.to!int, position.y.to!int, 2619 width, height, GL_RGBA, GL_UNSIGNED_BYTE, cast(void*) image.pixels); 2620 2621 return image; 2622 }