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