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