1 // Written in the D programming language.
2 /++
3  + Authors: KanzakiKino
4  + Copyright: KanzakiKino 2018
5  + License: LGPL-3.0
6 ++/
7 module w4d.util.clipping;
8 import g4d;
9 import std.algorithm;
10 
11 /// A utility object that clips the draw area.
12 class ClipRect
13 {
14     protected struct Rect
15     {
16         vec2 pos; // center of the rect.
17         vec2 size;
18     }
19 
20     protected Rect[]       _rcStack;
21     protected RectElement  _elm;
22     protected Fill3DShader _shader;
23 
24     ///
25     this ( Fill3DShader s )
26     {
27         _rcStack = [];
28         _elm     = new RectElement;
29         _shader  = s;
30     }
31 
32     /// Checks if clipping is enabled.
33     const @property isClipping ()
34     {
35         return !!_rcStack.length;
36     }
37     protected const @property currentRect ()
38     {
39         if ( isClipping ) {
40             return _rcStack[$-1];
41         }
42         return Rect( vec2(0,0), vec2(int.max,int.max) );
43     }
44 
45     /// Adds clipping rect.
46     void pushRect ( vec2 pos, vec2 size )
47     {
48         auto left   = pos.x;
49         auto top    = pos.y;
50         auto right  = pos.x + size.x;
51         auto bottom = pos.y + size.y;
52 
53         auto cur    = currentRect;
54         auto curpos = cur.pos - cur.size/2;
55 
56         left   = max( left  , curpos.x );
57         top    = max( top   , curpos.y );
58         right  = min( right , curpos.x+cur.size.x );
59         bottom = min( bottom, curpos.y+cur.size.y );
60 
61         auto rpos  = vec2(left,top);
62         auto rsize = vec2(right,bottom) - pos;
63 
64         rpos     += rsize/2; // pos is center of rect
65         _rcStack ~= Rect( rpos, rsize );
66         apply();
67     }
68 
69     /// Removes the clipping rect added last.
70     void popRect ()
71     {
72         if ( isClipping ) {
73             _rcStack = _rcStack[0..$-1];
74         }
75         apply();
76     }
77 
78     /// Redraws the stencil buffer.
79     void apply ()
80     {
81         if ( !isClipping ) {
82             StencilBuffer.disable();
83             return;
84         }
85 
86         StencilBuffer.enable();
87         StencilBuffer.clear( 0 );
88 
89         DepthBuffer.mask( false );
90         ColorBuffer.mask( false, false, false, false );
91 
92         StencilBuffer.startModify( 1 );
93 
94         const rc = currentRect;
95         _elm.resize( rc.size );
96 
97         _shader.use();
98         _shader.matrix.late = vec3( rc.pos, 0 );
99         _elm.draw( _shader );
100 
101         StencilBuffer.startTest( 1 );
102         DepthBuffer.mask( true );
103         ColorBuffer.mask( true, true, true, true );
104     }
105 }