1 module armos.graphics.renderer; 2 3 import std.math; 4 import derelict.opengl3.gl; 5 import armos.app; 6 import armos.graphics; 7 import armos.math; 8 import armos.types; 9 10 /++ 11 ポリゴンのレンダリング方法を表します 12 +/ 13 enum PolyRenderMode { 14 Points,/// 頂点のみ描画します. 15 WireFrame,/// 線のみ描画します. 16 Fill,/// 面を塗りつぶして描画します. 17 } 18 19 /++ 20 ポリゴンのプリミティブを指定します. 21 +/ 22 enum PrimitiveMode{ 23 Triangles, 24 TriangleStrip, 25 TriangleFan, 26 Lines, 27 LineStrip, 28 LineLoop, 29 Points, 30 } 31 32 /++ 33 openGLで扱うMatrixの種類を表します. 34 +/ 35 enum MatrixMode{ 36 ModelView, 37 Projection, 38 Texture 39 } 40 41 /++ 42 +/ 43 enum BlendMode{ 44 Disable, 45 Alpha, 46 Add, 47 Screen, 48 Subtract 49 } 50 51 /++ 52 +/ 53 GLuint getGLPrimitiveMode(PrimitiveMode mode){ 54 GLuint return_mode; 55 switch (mode) { 56 case PrimitiveMode.Triangles: 57 return_mode = GL_TRIANGLES; 58 break; 59 case PrimitiveMode.TriangleStrip: 60 return_mode = GL_TRIANGLE_STRIP; 61 break; 62 case PrimitiveMode.TriangleFan: 63 return_mode = GL_TRIANGLE_FAN; 64 break; 65 case PrimitiveMode.Lines: 66 return_mode = GL_LINES; 67 break; 68 case PrimitiveMode.LineStrip: 69 return_mode = GL_LINE_STRIP; 70 break; 71 case PrimitiveMode.LineLoop: 72 return_mode = GL_LINE_LOOP; 73 break; 74 case PrimitiveMode.Points: 75 return_mode = GL_POINTS; 76 break; 77 default : assert(0); 78 79 } 80 return return_mode; 81 } 82 83 /++ 84 +/ 85 GLuint getGLPolyRenderMode(PolyRenderMode mode){ 86 GLuint return_mode; 87 switch (mode) { 88 case PolyRenderMode.Points: 89 return_mode = GL_POINTS; 90 break; 91 case PolyRenderMode.WireFrame: 92 return_mode = GL_LINE; 93 break; 94 case PolyRenderMode.Fill: 95 return_mode = GL_FILL; 96 break; 97 default : assert(0); 98 } 99 return return_mode; 100 } 101 102 /++ 103 +/ 104 GLuint getGLMatrixMode(MatrixMode mode){ 105 GLuint return_mode; 106 switch (mode) { 107 case MatrixMode.ModelView: 108 return_mode = GL_MODELVIEW; 109 break; 110 case MatrixMode.Projection: 111 return_mode = GL_PROJECTION; 112 break; 113 case MatrixMode.Texture: 114 return_mode = GL_TEXTURE; 115 break; 116 default : assert(0); 117 } 118 return return_mode; 119 } 120 121 /++ 122 +/ 123 armos.graphics.Renderer currentRenderer(){ 124 return armos.app.mainLoop.renderer; 125 } 126 127 /++ 128 +/ 129 armos.graphics.Style currentStyle(){ 130 return armos.graphics.currentRenderer.currentStyle; 131 } 132 133 /++ 134 +/ 135 void style(in armos.graphics.Style s){ 136 currentRenderer.style(s); 137 } 138 139 /++ 140 +/ 141 void pushStyle(){ 142 currentRenderer.pushStyle; 143 } 144 145 /++ 146 +/ 147 void popStyle(){ 148 currentRenderer.popStyle; 149 } 150 151 /++ 152 +/ 153 void background(const armos.types.Color color){ 154 currentRenderer.background = color; 155 } 156 157 /++ 158 +/ 159 void background(const float gray){ 160 currentRenderer.background = armos.types.Color(gray, gray, gray, 1); 161 } 162 163 /++ 164 +/ 165 void background(in float r, in float g, in float b, in float a = 1){ 166 currentRenderer.background = armos.types.Color(r, g, b, a); 167 } 168 169 /// 170 void fillBackground(){ 171 currentRenderer.fillBackground = currentRenderer.background; 172 } 173 174 /// 175 void fillBackground(const armos.types.Color color){ 176 currentRenderer.fillBackground = color; 177 } 178 179 /// 180 void fillBackground(const float gray){ 181 currentRenderer.fillBackground = armos.types.Color(gray, gray, gray, 1); 182 } 183 /// 184 void fillBackground(in float r, in float g, in float b, in float a = 1){ 185 currentRenderer.fillBackground = armos.types.Color(r, g, b, a); 186 } 187 188 /++ 189 +/ 190 void isBackgrounding(in bool b){ 191 currentRenderer.isBackgrounding = b; 192 } 193 194 /// 195 void clear(in armos.types.Color c){ 196 currentRenderer.fillBackground(c); 197 } 198 199 /++ 200 +/ 201 void color(in float r, in float g, in float b, in float a = 1){ 202 currentRenderer.color = armos.types.Color(r, g, b, a); 203 } 204 205 /++ 206 +/ 207 void color(const armos.types.Color c){ 208 currentRenderer.color = c; 209 } 210 211 /++ 212 +/ 213 void color(const float gray){ 214 currentRenderer.color = armos.types.Color(gray, gray, gray, 1); 215 } 216 217 /++ 218 +/ 219 void drawLine(T)(in T x1, in T y1, in T z1, in T x2, in T y2, in T z2){ 220 import std.conv; 221 currentRenderer.drawLine(x1.to!float, y1.to!float, z1.to!float, x2.to!float, y2.to!float, z2.to!float); 222 } 223 224 /++ 225 +/ 226 void drawLine(T)(in T x1, in T y1, in T x2, in T y2){ 227 drawLine(x1, y1, 0, x2, y2, 0); 228 } 229 230 /++ 231 +/ 232 void drawLine(V)(in V vec1, in V vec2)if(armos.math.isVector!(V) && V.dimention == 3){ 233 drawLine(vec1[0], vec1[1], vec1[2], vec2[0], vec2[1], vec2[2]); 234 } 235 236 /++ 237 +/ 238 void drawLine(V)(in V vec1, in V vec2)if(armos.math.isVector!(V) && V.dimention == 2){ 239 drawLine(vec1[0], vec1[1], 0, vec2[0], vec2[1], 0); 240 } 241 242 /++ 243 +/ 244 void drawRectangle(T)(in T x, in T y, in T w, in T h){ 245 import std.conv; 246 currentRenderer.drawRectangle(x.to!float, y.to!float, w.to!float, h.to!float); 247 } 248 249 /++ 250 +/ 251 void drawRectangle(Vector)(in Vector position, in Vector size){ 252 drawRectangle(position[0], position[1], size[0], size[1]); 253 } 254 255 /++ 256 +/ 257 void drawGridPlane( 258 in float stepSize, in int numberOfSteps, 259 in bool labels=false 260 ){ 261 for (int i = -numberOfSteps; i <= numberOfSteps; i++) { 262 drawLine( -stepSize*numberOfSteps, 0.0, i*stepSize, stepSize*numberOfSteps, 0.0, i*stepSize ); 263 drawLine( i*stepSize, 0.0, -stepSize*numberOfSteps, i*stepSize , 0.0, stepSize*numberOfSteps ); 264 } 265 } 266 267 /++ 268 +/ 269 void drawAxis( 270 in float size 271 ){ 272 pushStyle;{ 273 lineWidth = 2.0; 274 color(1, 0, 0); 275 drawLine(-size*0.5, 0.0, 0.0, size, 0.0, 0.0); 276 color(0, 1, 0); 277 drawLine(0.0, -size*0.5, 0.0, 0.0, size, 0.0); 278 color(0, 0, 1); 279 drawLine(0.0, 0.0, -size*0.5, 0.0, 0.0, size); 280 }popStyle; 281 } 282 283 /++ 284 +/ 285 void drawGrid( 286 in float stepSize, in int numberOfSteps, 287 in bool labels=false, in bool x=true, in bool y=true, in bool z=true 288 ){ 289 pushStyle;{ 290 pushMatrix;{ 291 rotate(90.0, 0, 0, 1); 292 if(x){ 293 color(1, 0, 0); 294 drawGridPlane(stepSize, numberOfSteps); 295 } 296 rotate(-90.0, 0, 0, 1); 297 if(y){ 298 color(0, 1, 0); 299 drawGridPlane(stepSize, numberOfSteps); 300 } 301 rotate(90.0, 1, 0, 0); 302 if(z){ 303 color(0, 0, 1); 304 drawGridPlane(stepSize, numberOfSteps); 305 } 306 }popMatrix; 307 }popStyle; 308 } 309 310 /++ 311 +/ 312 void popMatrix(){ 313 // currentRenderer.popMatrix(); 314 popModelMatrix; 315 } 316 317 /++ 318 +/ 319 void pushMatrix(){ 320 // currentRenderer.pushMatrix(); 321 pushModelMatrix; 322 } 323 324 /// 325 M translationMatrix(T, M = armos.math.Matrix!(T, 4, 4))(in T x, in T y, in T z) 326 if(armos.math.isMatrix!(M) && __traits(isArithmetic, T) && M.rowSize == 4){ 327 import std.conv; 328 auto t = M.identity; 329 t[0][M.colSize-1] = x.to!(M.elementType); 330 t[1][M.colSize-1] = y.to!(M.elementType); 331 t[2][M.colSize-1] = z.to!(M.elementType); 332 return t; 333 } 334 335 unittest{ 336 alias M = armos.math.Matrix4f; 337 M m = translationMatrix(2f, 3f, 4f); 338 immutable ans = M( 339 [1, 0, 0, 2], 340 [0, 1, 0, 3], 341 [0, 0, 1, 4], 342 [0, 0, 0, 1], 343 ); 344 assert(m == ans); 345 } 346 347 /// 348 M translate(M, T)(in M matrix, in T x, in T y, in T z) 349 if(armos.math.isMatrix!(M) && __traits(isArithmetic, T) && M.rowSize == 4){ 350 import std.conv; 351 alias E = M.elementType; 352 return matrix * translationMatrix(x.to!E, y.to!E, z.to!E); 353 } 354 355 unittest{ 356 alias M = armos.math.Matrix4d; 357 immutable ans = M( 358 [1, 0, 0, 2], 359 [0, 1, 0, 3], 360 [0, 0, 1, 4], 361 [0, 0, 0, 1], 362 ); 363 assert(M.identity.translate(2.0, 3.0, 4.0) == ans); 364 } 365 366 /++ 367 +/ 368 void translate(T)(in T x, in T y, in T z)if(__traits(isArithmetic, T)){ 369 import std.conv; 370 multModelMatrix(translationMatrix(x.to!float, y.to!float, z.to!float)); 371 } 372 373 /++ 374 +/ 375 void translate(armos.math.Vector3f vec){ 376 multModelMatrix(translationMatrix(vec[0], vec[1], vec[2])); 377 } 378 379 /++ 380 +/ 381 void translate(armos.math.Vector3d vec){ 382 multModelMatrix(translationMatrix!(float)(vec[0], vec[1], vec[2])); 383 } 384 385 /// 386 M scalingMatrix(T, M = armos.math.Matrix!(T, 4, 4))(in T x, in T y, in T z) 387 if(armos.math.isMatrix!(M) && __traits(isArithmetic, T) && M.rowSize == 4){ 388 import std.conv; 389 auto t = M.identity; 390 t[0][0] = x.to!(M.elementType); 391 t[1][1] = y.to!(M.elementType); 392 t[2][2] = z.to!(M.elementType); 393 return t; 394 } 395 396 unittest{ 397 alias M = armos.math.Matrix4d; 398 M m = scalingMatrix(2.0, 3.0, 4.0); 399 immutable ans = M( 400 [2, 0, 0, 0], 401 [0, 3, 0, 0], 402 [0, 0, 4, 0], 403 [0, 0, 0, 1], 404 ); 405 assert(m == ans); 406 } 407 408 /// 409 M scale(M, T)(in M matrix, in T x, in T y, in T z) 410 if( 411 armos.math.isMatrix!(M) && 412 __traits(isArithmetic, T) && 413 M.rowSize == 4 414 ){ 415 import std.conv; 416 alias E = M.elementType; 417 return matrix * scalingMatrix(x.to!E, y.to!E, z.to!E); 418 } 419 420 unittest{ 421 alias M = armos.math.Matrix4d; 422 immutable ans = M( 423 [2, 0, 0, 0], 424 [0, 3, 0, 0], 425 [0, 0, 4, 0], 426 [0, 0, 0, 1], 427 ); 428 assert(M.identity.scale(2, 3, 4) == ans); 429 } 430 431 /++ 432 +/ 433 void scale(float x, float y, float z){ 434 multModelMatrix(scalingMatrix(x, y, z)); 435 } 436 437 /++ 438 +/ 439 void scale(float s){ 440 multModelMatrix(scalingMatrix(s, s, s)); 441 } 442 443 /++ 444 +/ 445 void scale(armos.math.Vector3f vec){ 446 multModelMatrix(scalingMatrix(vec[0], vec[1], vec[2])); 447 } 448 449 /// 450 M4 rotationMatrix(T, M4 = armos.math.Matrix!(T, 4, 4))(in T rad, in T x, in T y, in T z) 451 if(armos.math.isMatrix!(M4) && __traits(isArithmetic, T) && M4.rowSize == 4){ 452 import std.conv; 453 import std.math; 454 alias E = M4.elementType; 455 alias M3 = armos.math.Matrix!(E, 3, 3); 456 immutable E d = rad.to!E; 457 immutable n = armos.math.Vector!(E, 3)(x, y, z).normalized; 458 immutable r = M3( 459 [E(0), -n[2], n[1] ], 460 [n[2], E(0), -n[0]], 461 [-n[1], n[0], E(0) ], 462 ); 463 immutable m = M3.identity + sin(d)*r + (1-cos(d))*r*r; 464 immutable t = M4.identity.setMatrix(m); 465 return t; 466 } 467 468 unittest{ 469 alias M = armos.math.Matrix4d; 470 immutable m = rotationMatrix(PI, 0.0, 1.0, 0.0); 471 immutable ans = M( 472 [-1, 0, 0, 0], 473 [0, 1, 0, 0], 474 [0, 0, -1, 0], 475 [0, 0, 0, 1], 476 ); 477 import std.math; 478 foreach (int i, ref r; m.elements) { 479 foreach (int j, ref c; r.elements) { 480 assert(approxEqual(c, ans[i][j])); 481 } 482 } 483 } 484 485 /// 486 M rotate(M, T)(in M matrix, in T rad, in T x, in T y, in T z) 487 if(armos.math.isMatrix!(M) && __traits(isArithmetic, T) && M.rowSize == 4){ 488 import std.conv; 489 alias E = M.elementType; 490 return matrix * rotationMatrix(rad.to!E, x.to!E, y.to!E, z.to!E); 491 } 492 493 unittest{ 494 alias M = armos.math.Matrix4d; 495 immutable ans = M( 496 [-1, 0, 0, 0], 497 [0, 1, 0, 0], 498 [0, 0, -1, 0], 499 [0, 0, 0, 1], 500 ); 501 502 import std.math; 503 immutable m = M.identity.rotate(PI, 0.0, 1.0, 0.0); 504 foreach (int i, ref r; m.elements) { 505 foreach (int j, ref c; r.elements) { 506 assert(approxEqual(c, ans[i][j])); 507 } 508 } 509 } 510 511 /++ 512 +/ 513 void rotate(T)(in T rad, in T vec_x, in T vec_y, in T vec_z)if(__traits(isArithmetic, T)){ 514 multModelMatrix(rotationMatrix!float(rad, vec_x, vec_y, vec_z)); 515 } 516 517 /++ 518 +/ 519 void rotate(T, V)(in T rad, V vec)if(__traits(isArithmetic, T) && armos.math.isVector!(V)){ 520 521 multModelMatrix(rotationMatrix(rad, vec[0], vec[1], vec[2])); 522 } 523 524 // /++ 525 // +/ 526 // void loadIdentity(){ 527 // currentRenderer.loadIdentity(); 528 // } 529 // 530 // /++ 531 // +/ 532 // void loadMatrix(Q)(Q matrix){ 533 // currentRenderer.loadMatrix(matrix); 534 // } 535 // 536 // /++ 537 // +/ 538 // void multMatrix(Q)(Q matrix){ 539 // currentRenderer.multMatrix(matrix); 540 // } 541 542 /++ 543 +/ 544 void lineWidth(float width){ 545 currentRenderer.lineWidth(width); 546 } 547 548 /++ 549 +/ 550 void isUsingDepthTest(in bool b){ 551 currentRenderer.isUsingDepthTest(b); 552 } 553 /++ 554 +/ 555 void enableDepthTest(){ 556 currentRenderer.isUsingDepthTest(true); 557 } 558 559 /++ 560 +/ 561 void disableDepthTest(){ 562 currentRenderer.isUsingDepthTest(false); 563 } 564 565 /++ 566 +/ 567 void isUsingFbo(in bool b){ 568 currentRenderer.isUsingFbo(b); 569 } 570 571 /++ 572 +/ 573 void enableUsingFbo(){ 574 currentRenderer.isUsingFbo(true); 575 } 576 577 /++ 578 +/ 579 void disableUsingFbo(){ 580 currentRenderer.isUsingFbo(false); 581 } 582 583 /++ 584 +/ 585 void blendMode(armos.graphics.BlendMode mode){ 586 currentRenderer.blendMode = mode; 587 } 588 589 /// 590 mixin armos.graphics.matrixstack.MatrixStackFunction!("Model"); 591 592 /// 593 mixin armos.graphics.matrixstack.MatrixStackFunction!("View"); 594 595 /// 596 mixin armos.graphics.matrixstack.MatrixStackFunction!("Projection"); 597 598 /// 599 mixin armos.graphics.matrixstack.MatrixStackFunction!("Texture"); 600 601 armos.math.Matrix4f modelViewProjectionMatrix(){ 602 return projectionMatrix * viewMatrix * modelMatrix; 603 } 604 605 /++ 606 +/ 607 private struct ScopedMatrixStack{ 608 public{ 609 this(armos.graphics.MatrixStack matrixStack, in armos.math.Matrix4f matrix){ 610 _matrixStack = matrixStack; 611 _matrixStack.push(); 612 _matrixStack.mult(matrix); 613 } 614 615 ~this(){ 616 _matrixStack.pop; 617 } 618 }//public 619 620 private{ 621 armos.graphics.MatrixStack _matrixStack; 622 }//private 623 }//struct ScopedMatrixStack 624 625 ScopedMatrixStack scopedModelMatrix(in armos.math.Matrix4f matrix = armos.math.Matrix4f.identity){ 626 return ScopedMatrixStack(currentRenderer._modelMatrixStack, matrix); 627 } 628 629 ScopedMatrixStack scopedViewMatrix(in armos.math.Matrix4f matrix = armos.math.Matrix4f.identity){ 630 return ScopedMatrixStack(currentRenderer._viewMatrixStack, matrix); 631 } 632 633 ScopedMatrixStack scopedProjectionMatrix(in armos.math.Matrix4f matrix = armos.math.Matrix4f.identity){ 634 return ScopedMatrixStack(currentRenderer._projectionMatrixStack, matrix); 635 } 636 637 ScopedMatrixStack scopedTextureMatrix(in armos.math.Matrix4f matrix = armos.math.Matrix4f.identity){ 638 return ScopedMatrixStack(currentRenderer._textureMatrixStack, matrix); 639 } 640 641 // /// 642 // void currentMaterial(armos.graphics.Material material){ 643 // currentRenderer.currentMaterial = material; 644 // } 645 646 /// 647 void pushMaterialStack(armos.graphics.Material material){ 648 currentRenderer.pushMaterialStack(material); 649 } 650 651 /// 652 void popMaterialStack(){ 653 currentRenderer.popMaterialStack; 654 } 655 656 /// 657 armos.graphics.Material currentMaterial(){ 658 return currentRenderer.currentMaterial; 659 } 660 661 /// 662 void samples(in int s){ 663 currentRenderer.samples = s; 664 } 665 666 /// 667 int samples(){ 668 return currentRenderer.samples; 669 670 } 671 672 /// 673 armos.graphics.Fbo currentFbo(){ 674 return currentRenderer._fbo; 675 } 676 677 /++ 678 +/ 679 class Renderer { 680 mixin armos.graphics.matrixstack.MatrixStackManipulator!("Model"); 681 mixin armos.graphics.matrixstack.MatrixStackManipulator!("View"); 682 mixin armos.graphics.matrixstack.MatrixStackManipulator!("Projection"); 683 mixin armos.graphics.matrixstack.MatrixStackManipulator!("Texture"); 684 685 public{ 686 687 /++ 688 +/ 689 this(){ 690 _fbo = (new armos.graphics.Fbo).minFilter(TextureMinFilter.Linear); 691 692 _bufferMesh = new armos.graphics.BufferMesh; 693 _materialStack ~= new armos.graphics.DefaultMaterial; 694 auto bitmap = (new armos.graphics.Bitmap!(char)) 695 .allocate(2, 2, armos.graphics.ColorFormat.RGBA) 696 .setAllPixels(0, 255) 697 .setAllPixels(1, 255) 698 .setAllPixels(2, 255) 699 .setAllPixels(3, 255); 700 currentMaterial.texture("tex0", (new armos.graphics.Texture).allocate(bitmap)); 701 _bufferEntity = new armos.graphics.BufferEntity(_bufferMesh, currentMaterial); 702 703 _modelMatrixStack.push; 704 _viewMatrixStack.push; 705 _projectionMatrixStack.push; 706 _textureMatrixStack.push; 707 708 } 709 710 /++ 711 +/ 712 void isBackgrounding(bool b){ 713 _isBackgrounding = b; 714 }; 715 716 /++ 717 +/ 718 void background(in armos.types.Color color){ 719 _currentStyle.backgroundColor = cast(armos.types.Color)color; 720 glClearColor(color.r/Color.limit, 721 color.g/Color.limit, 722 color.b/Color.limit, 723 color.a/Color.limit); 724 } 725 726 /// 727 armos.types.Color background(){ 728 return _currentStyle.backgroundColor; 729 } 730 731 /++ 732 +/ 733 void fillBackground(in armos.types.Color color){ 734 background = color; 735 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 736 } 737 738 /++ 739 +/ 740 void color(in armos.types.Color c){ 741 import std.conv:to; 742 _currentStyle.color = cast(armos.types.Color)c; 743 _materialStack[0].uniform("diffuse", armos.math.Vector4f(c.r.to!float,c.g.to!float,c.b.to!float,c.a.to!float)/Color.limit); 744 glColor4f(c.r/Color.limit,c.g/Color.limit,c.b/Color.limit,c.a/Color.limit); 745 } 746 747 /++ 748 +/ 749 void color(in int colorCode){ 750 auto c= armos.types.Color(colorCode); 751 color(c); 752 } 753 754 /++ 755 +/ 756 void style(armos.graphics.Style s){ 757 color(s.color); 758 background = s.backgroundColor; 759 blendMode = s.blendMode; 760 lineWidth = s.lineWidth; 761 isSmoothingLine = s.isSmoothing; 762 isUsingDepthTest(s.isDepthTest); 763 _currentStyle.polyRenderMode = s.polyRenderMode; 764 } 765 766 /++ 767 +/ 768 ref armos.graphics.Style currentStyle(){ 769 return _currentStyle; 770 }; 771 772 /++ 773 +/ 774 void pushStyle(){ 775 _styleStack ~= _currentStyle; 776 } 777 778 /++ 779 +/ 780 void popStyle(){ 781 import std.range; 782 if(_styleStack.length == 0){ 783 assert(0, "stack is empty"); 784 }else{ 785 _currentStyle = _styleStack[$-1]; 786 _styleStack.popBack; 787 style(_currentStyle); 788 } 789 } 790 791 /++ 792 +/ 793 void translate(T)(in T x, in T y, in T z)if(__traits(isArithmetic, T)){ 794 import std.conv; 795 _modelMatrixStack.mult(translationMatrix!(float)(x, y, z)); 796 } 797 798 /++ 799 +/ 800 void translate(V)(in V vec)if(armos.math.isVector!(V)){ 801 translate(vec[0], vec[1], vec[2]); 802 }; 803 804 /++ 805 +/ 806 void scale(T)(in T x, in T y, in T z)if(__traits(isArithmetic, T)){ 807 import std.conv; 808 _modelMatrixStack.mult(scalingMatrix!(float)(x, y, z)); 809 } 810 811 /++ 812 +/ 813 814 void scale(V)(in V vec)if(armos.math.isVector!(V)){ 815 scale(vec[0], vec[1], vec[2]); 816 } 817 818 /++ 819 +/ 820 void rotate(T)(in T degrees, in T vecX, in T vecY, in T vecZ)if(__traits(isArithmetic, T)){ 821 import std.conv; 822 _modelMatrixStack.mult(rotationMatrix!(float)(degrees, vecX, vecY, vecZ)); 823 } 824 825 /// 826 void rotate(T, V)(in T degrees, in V vec)if(__traits(isArithmetic, T) && armos.math.isVector!(V)){ 827 rotate(degrees, vec[0], vec[1], vec[2]); 828 } 829 830 /++ 831 +/ 832 void lineWidth(T)(in T width)if(__traits(isArithmetic, T)){ 833 import std.conv; 834 _currentStyle.lineWidth = width.to!float; 835 glLineWidth(width.to!float); 836 } 837 838 /++ 839 +/ 840 void isSmoothingLine(in bool smooth){ 841 _currentStyle.isSmoothing = smooth; 842 } 843 844 /++ 845 +/ 846 void setup(){ 847 glEnable(GL_LINE_SMOOTH); 848 glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); 849 import std.conv; 850 viewport(Vector2f.zero, armos.app.currentWindow.frameBufferSize.to!Vector2f); 851 852 if(_isUseFbo){ 853 _fbo.begin; 854 } 855 856 fillBackground(currentStyle.backgroundColor); 857 858 if(_isUseFbo){ 859 _fbo.end; 860 } 861 }; 862 863 /++ 864 +/ 865 void resize(){ 866 if(_isUseFbo){ 867 import std.conv; 868 _fbo.resize((armos.app.currentWindow.frameBufferSize.to!Vector2f/armos.app.currentWindow.pixelScreenCoordScale).to!Vector2i); 869 _fbo.begin; 870 } 871 872 fillBackground(currentStyle.backgroundColor ); 873 874 if(_isUseFbo){ 875 _fbo.end; 876 } 877 } 878 879 /++ 880 +/ 881 void startRender(){ 882 _projectionMatrixStack.push; 883 import std.conv; 884 auto windowSize = (armos.app.currentWindow.frameBufferSize.to!Vector2f/armos.app.currentWindow.pixelScreenCoordScale).to!Vector2i; 885 _projectionMatrixStack.load(screenPerspectiveMatrix(windowSize)); 886 _projectionMatrixStack.mult(scalingMatrix!float(1f, -1f, 1f)*translationMatrix!float(0, -windowSize.y, 0)); 887 if(_isUseFbo){ 888 _fbo.begin; 889 } 890 if( _isBackgrounding ){ 891 fillBackground(currentStyle.backgroundColor); 892 } 893 }; 894 895 /++ 896 +/ 897 void finishRender(){ 898 armos.types.Color tmp = currentStyle.color; 899 bool isEnableDepthTest; 900 if(_isUseFbo){ 901 _fbo.end; 902 color(0xFFFFFF); 903 904 glGetBooleanv(GL_DEPTH_TEST, cast(ubyte*)&isEnableDepthTest); 905 disableDepthTest; 906 } 907 908 import std.conv; 909 viewport(Vector2f.zero, armos.app.currentWindow.frameBufferSize.to!Vector2f); 910 911 if(_isUseFbo) _fbo.draw; 912 _projectionMatrixStack.pop; 913 color(tmp); 914 if(isEnableDepthTest){ 915 enableDepthTest; 916 } 917 }; 918 919 /++ 920 +/ 921 void drawLine(in float x1, in float y1, in float z1, in float x2, in float y2, in float z2){ 922 armos.math.Vector4f[2] vertices = [ 923 armos.math.Vector4f(x1, y1, z1, 1f), 924 armos.math.Vector4f(x2, y2, z2, 1f) 925 ]; 926 immutable freq = armos.graphics.BufferUsageFrequency.Dynamic; 927 immutable nature = armos.graphics.BufferUsageNature.Draw; 928 auto shader = currentMaterial.shader; 929 930 import armos.utils.scoped; 931 const scopedVao = scoped(_bufferMesh.vao); 932 const scopedMaterial = scoped(currentMaterial); 933 934 _bufferMesh.attrs["vertex"].array(vertices, freq, nature); 935 936 _bufferMesh.attrs["vertex"].begin; 937 shader.attr("vertex"); 938 _bufferMesh.attrs["vertex"].end; 939 940 shader.uniform("modelViewMatrix", viewMatrix * modelMatrix); 941 shader.uniform("projectionMatrix", projectionMatrix); 942 shader.uniform("modelViewProjectionMatrix", modelViewProjectionMatrix); 943 944 const scopedShader = scoped(shader); 945 shader.enableAttrib("vertex"); 946 glDrawArrays(GL_LINES, 0, vertices.length); 947 shader.disableAttrib("vertex"); 948 }; 949 950 /++ 951 +/ 952 void drawRectangle(in float x, in float y, in float w, in float h){ 953 armos.math.Vector4f[4] vertices; 954 vertices[0] = armos.math.Vector4f(x, y, 0f, 1f); 955 vertices[1] = armos.math.Vector4f(x, y+h, 0f, 1f); 956 vertices[2] = armos.math.Vector4f(x+w, y+h, 0f, 1f); 957 vertices[3] = armos.math.Vector4f(x+w, y, 0f, 1f); 958 959 armos.math.Vector4f[] texCoords = [ 960 armos.math.Vector4f(0f, 0f, 0.0, 1.0f), 961 armos.math.Vector4f(0, 1f, 0.0, 1.0f), 962 armos.math.Vector4f(1f, 1f, 0.0, 1.0f), 963 armos.math.Vector4f(1f, 0, 0.0, 1.0f), 964 ]; 965 966 int[] indices = [0, 1, 2, 2, 3, 0]; 967 968 draw( 969 vertices, 970 null, 971 null, 972 texCoords, 973 indices, 974 armos.graphics.PrimitiveMode.TriangleStrip, 975 _currentStyle.polyRenderMode, 976 true, 977 false, 978 false 979 ); 980 } 981 982 /++ 983 +/ 984 void draw( 985 armos.graphics.Mesh mesh, 986 in armos.graphics.PolyRenderMode renderMode, 987 in bool useColors, 988 in bool useTextures, 989 in bool useNormals 990 ){ 991 draw( 992 mesh.vertices, 993 mesh.normals, 994 mesh.colors, 995 mesh.texCoords, 996 mesh.indices, 997 mesh.primitiveMode, 998 renderMode, 999 useColors, 1000 useTextures, 1001 useNormals 1002 ); 1003 } 1004 1005 /++ 1006 +/ 1007 void draw( 1008 armos.math.Vector4f[] vertices, 1009 armos.math.Vector3f[] normals, 1010 armos.types.FloatColor[] colors, 1011 armos.math.Vector4f[] texCoords, 1012 int[] indices, 1013 in armos.graphics.PrimitiveMode primitiveMode, 1014 in armos.graphics.PolyRenderMode renderMode, 1015 in bool useColors, 1016 in bool useTextures, 1017 in bool useNormals 1018 ){ 1019 immutable freq = armos.graphics.BufferUsageFrequency.Dynamic; 1020 immutable nature = armos.graphics.BufferUsageNature.Draw; 1021 1022 import armos.utils.scoped; 1023 const scopedVao = scoped(_bufferMesh.vao); 1024 1025 //set attr 1026 _bufferMesh.attrs["vertex"].array(vertices, freq, nature); 1027 if(useNormals) _bufferMesh.attrs["normal"].array(normals, freq, nature); 1028 import std.algorithm; 1029 import std.array; 1030 if(useColors) _bufferMesh.attrs["color"].array(colors.map!(c => armos.math.Vector4f(c.r, c.g, c.b, c.a)).array, freq, nature); 1031 _bufferMesh.attrs["texCoord0"].array(texCoords, freq, nature); 1032 _bufferMesh.attrs["index"].array(indices, 0, freq, nature); 1033 1034 _bufferEntity.updateShaderAttribs; 1035 1036 glPolygonMode(GL_FRONT_AND_BACK, armos.graphics.getGLPolyRenderMode(renderMode)); 1037 _bufferEntity.primitiveMode(primitiveMode).draw(); 1038 glPolygonMode(GL_FRONT_AND_BACK, getGLPolyRenderMode(_currentStyle.polyRenderMode)); 1039 } 1040 1041 /++ 1042 +/ 1043 void isUsingDepthTest(in bool b){ 1044 if(b){ 1045 glEnable(GL_DEPTH_TEST); 1046 }else{ 1047 glDisable(GL_DEPTH_TEST); 1048 } 1049 _currentStyle.isDepthTest = b; 1050 } 1051 1052 /++ 1053 +/ 1054 void isUsingFbo(in bool b){ 1055 _isUseFbo = b; 1056 } 1057 1058 /++ 1059 +/ 1060 void blendMode(armos.graphics.BlendMode mode){ 1061 switch (mode) { 1062 case BlendMode.Alpha: 1063 glEnable(GL_BLEND); 1064 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1065 break; 1066 case BlendMode.Add: 1067 glEnable(GL_BLEND); 1068 glBlendFunc(GL_ONE, GL_ONE); 1069 break; 1070 case BlendMode.Disable: 1071 glDisable(GL_BLEND); 1072 break; 1073 default: 1074 assert(0); 1075 } 1076 _currentStyle.blendMode = mode; 1077 } 1078 1079 void pushMaterialStack(armos.graphics.Material material){ 1080 _materialStack ~= material; 1081 _bufferEntity.material = currentMaterial; 1082 } 1083 1084 void popMaterialStack(){ 1085 import std.range; 1086 if (_materialStack.length == 0) { 1087 assert(0, "stack is empty"); 1088 }else{ 1089 _materialStack.popBack; 1090 } 1091 _bufferEntity.material = currentMaterial; 1092 1093 } 1094 1095 armos.graphics.Material currentMaterial(){ 1096 return _materialStack[$-1]; 1097 } 1098 1099 void samples(in int s){ 1100 _fbo.samples = s; 1101 } 1102 1103 int samples()const{ 1104 return _fbo.samples; 1105 1106 } 1107 }//public 1108 1109 private{ 1110 armos.graphics.Fbo _fbo; 1111 bool _isUseFbo = true; 1112 1113 armos.graphics.Style _currentStyle = armos.graphics.Style(); 1114 armos.graphics.Style[] _styleStack; 1115 1116 armos.graphics.Material[] _materialStack; 1117 1118 armos.graphics.BufferMesh _bufferMesh; 1119 armos.graphics.BufferEntity _bufferEntity; 1120 1121 bool _isBackgrounding = true; 1122 }//private 1123 } 1124 1125 /++ 1126 +/ 1127 void viewport(in float x, in float y, in float width, in float height, in bool vflip=true){ 1128 Vector2f position = armos.math.Vector2f(x, y); 1129 import std.conv; 1130 Vector2f size = armos.math.Vector2f(width, height); 1131 viewport(position, size); 1132 } 1133 1134 /++ 1135 +/ 1136 void viewport(V)(in V position, in V size, in bool vflip=true)if(isVector!V){ 1137 V pos = position; 1138 if(vflip) pos[1] = size[1] - (pos[1] + size[1]); 1139 glViewport(cast(int)pos[0], cast(int)pos[1], cast(int)size[0], cast(int)size[1]); 1140 } 1141 1142 /// 1143 armos.math.Matrix4f screenPerspectiveMatrix(in float width, in float height, in float fov = 60, in float nearDist = 0, in float farDist = 0){ 1144 float viewW, viewH; 1145 viewW = width; 1146 viewH = height; 1147 1148 immutable float eyeX = viewW / 2.0; 1149 immutable float eyeY = viewH / 2.0; 1150 immutable float halfFov = PI * fov / 360.0; 1151 immutable float theTan = tan(halfFov); 1152 immutable float dist = eyeY / theTan; 1153 immutable float aspect = viewW / viewH; 1154 // 1155 immutable float near = (nearDist==0)?dist / 10.0f:nearDist; 1156 immutable float far = (farDist==0)?dist * 10.0f:farDist; 1157 1158 armos.math.Matrix4f persp = perspectiveMatrix(fov, aspect, near, far); 1159 1160 armos.math.Matrix4f lookAt = lookAtViewMatrix( 1161 armos.math.Vector3f(eyeX, eyeY, dist), 1162 armos.math.Vector3f(eyeX, eyeY, 0), 1163 armos.math.Vector3f(0, 1, 0) 1164 ); 1165 1166 return persp*lookAt; 1167 } 1168 1169 /// 1170 armos.math.Matrix4f screenPerspectiveMatrix(V)(in V size, in float fov = 60, in float nearDist = 0, in float farDist = 0)if(isVector!V && V.dimention == 2){ 1171 import std.conv; 1172 return screenPerspectiveMatrix(size.x.to!float, size.y.to!float, fov, nearDist, farDist); 1173 } 1174 1175 /// 1176 armos.math.Matrix4f perspectiveMatrix(in float fov, in float aspect, in float nearDist, in float farDist){ 1177 double tan_fovy = tan(fov*0.5*PI/180.0); 1178 double right = tan_fovy * aspect* nearDist; 1179 double left = -right; 1180 double top = tan_fovy * nearDist; 1181 double bottom = -top; 1182 1183 return frustumMatrix(left,right,bottom,top,nearDist,farDist); 1184 } 1185 1186 /// 1187 armos.math.Matrix4f frustumMatrix(in double left, in double right, in double bottom, in double top, in double zNear, in double zFar){ 1188 double A = (right+left)/(right-left); 1189 double B = (top+bottom)/(top-bottom); 1190 double C = -(zFar+zNear)/(zFar-zNear); 1191 double D = -2.0*zFar*zNear/(zFar-zNear); 1192 1193 return armos.math.Matrix4f( 1194 [2.0*zNear/(right-left), 0.0, A, 0.0 ], 1195 [0.0, 2.0*zNear/(top-bottom), B, 0.0 ], 1196 [0.0, 0.0, C, D ], 1197 [0.0, 0.0, -1.0, 0.0 ] 1198 ); 1199 } 1200 1201 /// 1202 armos.math.Matrix4f lookAtViewMatrix(in armos.math.Vector3f eye, in armos.math.Vector3f center, in armos.math.Vector3f up){ 1203 armos.math.Vector3f zaxis; 1204 if((eye-center).norm>0){ 1205 zaxis = (eye-center).normalized; 1206 }else{ 1207 zaxis = armos.math.Vector3f(); 1208 } 1209 1210 armos.math.Vector3f xaxis; 1211 if(up.vectorProduct(zaxis).norm>0){ 1212 xaxis = up.vectorProduct(zaxis).normalized; 1213 }else{ 1214 xaxis = armos.math.Vector3f(); 1215 } 1216 1217 armos.math.Vector3f yaxis = zaxis.vectorProduct(xaxis); 1218 1219 return armos.math.Matrix4f( 1220 [xaxis[0], xaxis[1], xaxis[2], -xaxis.dotProduct(eye)], 1221 [yaxis[0], yaxis[1], yaxis[2], -yaxis.dotProduct(eye)], 1222 [zaxis[0], zaxis[1], zaxis[2], -zaxis.dotProduct(eye)], 1223 [0, 0, 0, 1] 1224 ); 1225 }; 1226