1 module prova.graphics.rendertarget;
2 
3 import prova.graphics,
4        prova.math,
5        std.typecons;
6 
7 ///
8 class RenderTarget
9 {
10   static ShaderProgram flatShaderProgram;
11   private static uint currentFrameBuffer = -1;
12 
13   ///
14   SpriteBatch spriteBatch;
15   ///
16   protected Matrix projection;
17 
18   private uint renderBufferId;
19   private uint _frameBufferId;
20   private Texture _texture;
21   private int _width;
22   private int _height;
23   private bool begun;
24 
25   ///
26   this(int width, int height)
27   {
28     if(!flatShaderProgram)
29       flatShaderProgram = new FlatShaderProgram();
30 
31     spriteBatch = new SpriteBatch();
32     _texture = new Texture(width, height);
33     _width = width;
34     _height = height;
35 
36     createFrameBuffer();
37     createRenderBuffer();
38   }
39 
40   private void createFrameBuffer()
41   {
42     glGenFramebuffers(1, &_frameBufferId);
43     bindFrameBuffer();
44 
45     // attach texture to the frame buffer
46     glFramebufferTexture2D(
47       GL_FRAMEBUFFER,
48       GL_COLOR_ATTACHMENT0,
49       GL_TEXTURE_2D,
50       _texture.id,
51       0
52     );
53 
54     glDepthFunc(GL_LEQUAL);
55     glEnable(GL_BLEND);
56     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
57     glViewport(0, 0, width, height);
58   }
59 
60   private void createRenderBuffer()
61   {
62     glGenRenderbuffers(1, &renderBufferId);
63     glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
64     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
65 
66     bindFrameBuffer();
67 
68     glFramebufferRenderbuffer(
69       GL_FRAMEBUFFER,
70       GL_DEPTH_ATTACHMENT,
71       GL_RENDERBUFFER,
72       renderBufferId
73     );
74   }
75 
76   ///
77   void resize(int width, int height)
78   {
79     _width = width;
80     _height = height;
81 
82     texture.recreate(null, width, height);
83 
84     glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
85     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
86 
87     // update settings
88     glViewport(0, 0, width, height);
89   }
90 
91   ///
92   @property int width()
93   {
94     return _width;
95   }
96 
97   ///
98   @property int height()
99   {
100     return _height;
101   }
102 
103   ///
104   @property Texture texture()
105   {
106     return _texture;
107   }
108 
109   ///
110   @property uint frameBufferId()
111   {
112     return _frameBufferId;
113   }
114 
115   package(prova) void bindFrameBuffer()
116   {
117     bindFrameBuffer(_frameBufferId);
118   }
119 
120   /// Updates projection and prepares batches
121   void begin(Matrix projection)
122   {
123     if(begun)
124       throw new Exception("RenderTarget already started");
125 
126     spriteBatch.begin(this, projection);
127 
128     this.projection = projection;
129     begun = true;
130   }
131 
132   /// Finishes the batch
133   void end()
134   {
135     if(!begun)
136       throw new Exception("RenderTarget not ready, call begin(Matrix projection)");
137 
138     spriteBatch.end();
139     begun = false;
140   }
141 
142   /// Draws mesh using a flat shader
143   void drawMesh(Mesh mesh, Matrix transform, DrawMode mode, Color color)
144   {
145     if(!begun)
146       throw new Exception("RenderTarget not ready, call begin(Matrix projection)");
147 
148     flatShaderProgram.setMatrix("transform", projection * transform);
149     flatShaderProgram.setVector4("color", color);
150     flatShaderProgram.drawMesh(mesh, this, mode);
151   }
152 
153   ///
154   void drawSprite(Sprite sprite, Matrix transform)
155   {
156     spriteBatch.batchSprite(sprite, transform);
157   }
158 
159   ///
160   void drawSprite(AnimatedSprite sprite, Matrix transform)
161   {
162     spriteBatch.batchSprite(sprite, transform);
163   }
164 
165   ///
166   void clear()
167   {
168     bindFrameBuffer();
169 
170     glClearDepth(1);
171     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
172   }
173 
174   package(prova) static void bindFrameBuffer(uint id)
175   {
176     if(id == currentFrameBuffer)
177       return;
178 
179     glBindFramebuffer(GL_FRAMEBUFFER, id);
180     currentFrameBuffer = id;
181   }
182 
183   ~this()
184   {
185     glDeleteFramebuffers(1, &_frameBufferId);
186     glDeleteRenderbuffers(1, &renderBufferId);
187   }
188 }