1 module dash.graphics.adapters.gl;
2 import dash.core, dash.components, dash.graphics;
3 import dash.utility.output, dash.utility.math;
4 
5 import derelict.opengl3.gl3;
6 
7 import std.algorithm, std.array;
8 
9 abstract class OpenGL : Adapter
10 {
11 private:
12     uint _deferredFrameBuffer;
13     uint _diffuseRenderTexture; //Alpha channel stores Specular map average
14     uint _normalRenderTexture; //Alpha channel stores nothing important
15     uint _depthRenderTexture;
16 
17 public:
18     /// FBO for deferred render textures
19     mixin( Property!_deferredFrameBuffer );
20     /// Texture storing the Diffuse colors and Specular Intensity
21     mixin( Property!_diffuseRenderTexture );
22     /// Texture storing the Sphermapped Normal XY and the Object ID in Z
23     mixin( Property!_normalRenderTexture );
24     /// Texture storing the depth
25     mixin( Property!_depthRenderTexture );
26 
27     /**
28      * Initializes the FBO and Textures for deferred rendering
29      */
30     override void initializeDeferredRendering()
31     {
32         //http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/
33 
34         //Create the frame buffer, which will contain the textures to render to
35         deferredFrameBuffer = 0;
36         glGenFramebuffers( 1, &_deferredFrameBuffer );
37         glBindFramebuffer( GL_FRAMEBUFFER, _deferredFrameBuffer );
38 
39         //Generate our 3 textures
40         glGenTextures( 1, &_diffuseRenderTexture );
41         glGenTextures( 1, &_normalRenderTexture );
42         glGenTextures( 1, &_depthRenderTexture );
43 
44         // Initialize render textures
45         resizeDefferedRenderBuffer();
46 
47         //And finally set all of these to our frameBuffer
48         glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _diffuseRenderTexture, 0 );
49         glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _normalRenderTexture, 0 );
50         glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthRenderTexture, 0 );
51 
52         GLenum[ 2 ] DrawBuffers = [ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 ];
53         glDrawBuffers( 2, DrawBuffers.ptr );
54 
55         auto status = glCheckFramebufferStatus( GL_FRAMEBUFFER );
56         if( status != GL_FRAMEBUFFER_COMPLETE )
57         {
58             string mapFramebufferError( int code )
59             {
60                 switch( code )
61                 {
62                     case(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT): return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
63                     case(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT): return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
64                     case(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER): return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
65                     case(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER): return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
66                     case(GL_FRAMEBUFFER_UNSUPPORTED): return "GL_FRAMEBUFFER_UNSUPPORTED";
67                     case(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE): return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
68                     case(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS): return "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS";
69                     default: return "UNKNOWN";
70                 }
71             }
72 
73             fatalf( "Deffered rendering Frame Buffer was not initialized correctly. Error: %s", mapFramebufferError(status) );
74             assert(false);
75         }
76     }
77 
78     /**
79      * Resizes the deffered rendering buffer.
80      */
81     final void resizeDefferedRenderBuffer()
82     {
83         //For each texture, we bind it to our active texture, and set the format and filtering
84         glBindTexture( GL_TEXTURE_2D, _diffuseRenderTexture );
85         glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null );
86         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
87         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
88         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
89         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
90 
91         glBindTexture( GL_TEXTURE_2D, _normalRenderTexture );
92         glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, null );
93         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
94         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
95         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
96         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
97 
98         glBindTexture( GL_TEXTURE_2D, _depthRenderTexture );
99         glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null );
100         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
101         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
102         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
103         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
104     }
105 
106     /**
107      * Read from the depth buffer at the given point.
108      */
109     override float getDepthAtScreenPoint( vec2ui point )
110     {
111         float depth;
112         glBindFramebuffer( GL_FRAMEBUFFER, deferredFrameBuffer );
113         glReadBuffer( GL_DEPTH_ATTACHMENT );
114         glReadPixels( point.x, point.y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
115         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
116         return depth;
117     }
118 
119     /**
120      * Read from the depth buffer at the given point.
121      */
122     override uint getObjectIDAtScreenPoint( vec2ui point )
123     {
124         float fId;
125         glBindFramebuffer( GL_FRAMEBUFFER, deferredFrameBuffer );
126         glReadBuffer( GL_COLOR_ATTACHMENT1 );
127         glReadPixels( point.x, point.y, 1, 1, GL_ALPHA, GL_FLOAT, &fId );
128         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
129         return cast(uint)fId;
130     }
131 
132     /**
133      * Currently the entire rendering pass for the active Scene. TODO: Refactor the name
134      */
135     override void endDraw()
136     {
137         if( !DGame.instance.activeScene )
138         {
139             warning( "No active scene." );
140             return;
141         }
142 
143         auto scene = DGame.instance.activeScene;
144 
145         if( !scene.camera )
146         {
147             warning( "No camera on active scene." );
148             return;
149         }
150 
151         auto lights = scene.objects
152             .filter!(obj => obj.stateFlags.drawLight && obj.light)
153             .map!(obj => obj.light);
154 
155         auto getLightsByType( Type )()
156         {
157             return lights
158                 .filter!(obj => typeid(obj) == typeid(Type))
159                 .map!(obj => cast(Type)obj);
160         }
161 
162         auto ambientLights = getLightsByType!AmbientLight;
163         auto directionalLights = getLightsByType!DirectionalLight;
164         auto pointLights = getLightsByType!PointLight;
165         auto spotLights = getLightsByType!SpotLight;
166 
167         mat4f projection = scene.camera.perspectiveMatrix;
168         mat4f invProj = scene.camera.inversePerspectiveMatrix;
169 
170         scene.root.transform.updateMatrix();
171 
172         /**
173         * Pass for all objects with Meshes
174         */
175         void geometryPass()
176         {
177             foreach( obj; scene.objects )
178             {
179                 if( obj.mesh && obj.stateFlags.drawMesh )
180                 {
181                     mat4f worldView = scene.camera.viewMatrix * obj.transform.matrix;
182                     mat4f worldViewProj = projection * worldView;
183 
184                     if( !( obj.mesh.boundingBox in Frustum( worldViewProj ) ) )
185                     {
186                         // If we can't see an object, don't draw it.
187                         continue;
188                     }
189 
190                     // set the shader
191                     Shader shader = obj.mesh.animated
192                         ? Shaders.animatedGeometry
193                         : Shaders.geometry;
194 
195                     glUseProgram( shader.programID );
196                     glBindVertexArray( obj.mesh.glVertexArray );
197 
198                     shader.bindUniformMatrix4fv( shader.WorldView, worldView );
199                     shader.bindUniformMatrix4fv( shader.WorldViewProjection, worldViewProj );
200                     shader.bindUniform1ui( shader.ObjectId, obj.id );
201 
202                     if( obj.mesh.animated )
203                         shader.bindUniformMatrix4fvArray( shader.Bones, obj.animation.currBoneTransforms );
204 
205                     shader.bindMaterial( obj.material );
206 
207                     glDrawElements( GL_TRIANGLES, obj.mesh.numVertices, GL_UNSIGNED_INT, null );
208 
209                     glBindVertexArray(0);
210                 }
211             }
212         }
213 
214         /**
215         * Calculate shadow maps for lights in the scene.
216         */
217         void shadowPass()
218         {
219             foreach( light; directionalLights )
220             {
221                 if( light.castShadows )
222                 {
223                     glBindFramebuffer( GL_FRAMEBUFFER, light.shadowMapFrameBuffer );
224                     glClear( GL_DEPTH_BUFFER_BIT );
225                     glViewport( 0, 0, light.shadowMapSize, light.shadowMapSize );
226 
227                     // determine the world space volume for all objects
228                     box3f frustum;
229                     foreach( object; scene.objects )
230                     {
231                         if( object.mesh && object.stateFlags.drawMesh )
232                         {
233                             frustum.expandInPlace( (object.transform.matrix * vec4f(object.mesh.boundingBox.min, 1.0f)).xyz );
234                             frustum.expandInPlace( (object.transform.matrix * vec4f(object.mesh.boundingBox.max, 1.0f)).xyz );
235                         }
236                     }
237 
238                     light.calculateProjView( frustum );
239 
240                     foreach( object; scene.objects )
241                     {
242                         if( object.mesh && object.stateFlags.drawMesh )
243                         {
244                             // set the shader
245                             Shader shader = object.mesh.animated
246                                 ? Shaders.animatedShadowMap
247                                 : Shaders.shadowMap;
248 
249                             glUseProgram( shader.programID );
250                             glBindVertexArray( object.mesh.glVertexArray );
251 
252                             shader.bindUniformMatrix4fv( shader.WorldViewProjection,
253                                                          light.projView * object.transform.matrix);
254 
255                             if( object.mesh.animated )
256                                 shader.bindUniformMatrix4fvArray( shader.Bones, object.animation.currBoneTransforms );
257 
258                             glDrawElements( GL_TRIANGLES, object.mesh.numVertices, GL_UNSIGNED_INT, null );
259 
260                             glBindVertexArray(0);
261                         }
262                     }
263                     glBindFramebuffer( GL_FRAMEBUFFER, 0 );
264                 }
265             }
266 
267             foreach( light; pointLights ){}
268 
269             foreach( light; spotLights ){}
270         }
271 
272         /**
273         * Pass for all objects with lights
274         */
275         void lightPass()
276         {
277             /**
278             * Binds the g-buffer textures for sampling.
279             */
280             void bindGeometryOutputs( Shader shader )
281             {
282                 // diffuse
283                 glUniform1i( shader.DiffuseTexture, 0 );
284                 glActiveTexture( GL_TEXTURE0 );
285                 glBindTexture( GL_TEXTURE_2D, _diffuseRenderTexture );
286 
287                 // normal
288                 glUniform1i( shader.NormalTexture, 1 );
289                 glActiveTexture( GL_TEXTURE1 );
290                 glBindTexture( GL_TEXTURE_2D, _normalRenderTexture );
291 
292                 // depth
293                 glUniform1i( shader.DepthTexture, 2 );
294                 glActiveTexture( GL_TEXTURE2 );
295                 glBindTexture( GL_TEXTURE_2D, _depthRenderTexture );
296             }
297 
298             // Ambient Light
299             if( !ambientLights.empty )
300             {
301                 auto shader = Shaders.ambientLight;
302                 glUseProgram( shader.programID );
303 
304                 bindGeometryOutputs( shader );
305 
306                 shader.bindAmbientLight( ambientLights.front );
307 
308                 // bind the window mesh for ambient lights
309                 glBindVertexArray( Assets.unitSquare.glVertexArray );
310                 glDrawElements( GL_TRIANGLES, Assets.unitSquare.numVertices, GL_UNSIGNED_INT, null );
311 
312                 ambientLights.popFront;
313 
314                 if( !ambientLights.empty )
315                 {
316                     warning( "Only one ambient light per scene is utilized." );
317                 }
318             }
319 
320             // Directional Lights
321             if( !directionalLights.empty )
322             {
323                 auto shader = Shaders.directionalLight;
324                 glUseProgram( shader.programID );
325 
326                 bindGeometryOutputs( shader );
327 
328                 // bind inverseProj for rebuilding world positions from pixel locations
329                 shader.bindUniformMatrix4fv( shader.InverseProjection, invProj );
330                 shader.bindUniform2f( shader.ProjectionConstants, scene.camera.projectionConstants );
331 
332                 // bind the window mesh for directional lights
333                 glBindVertexArray( Assets.unitSquare.glVertexArray );
334 
335                 // bind and draw directional lights
336                 foreach( light; directionalLights )
337                 {
338                     glUniform1i( shader.ShadowMap, 3 );
339                     glActiveTexture( GL_TEXTURE3 );
340                     glBindTexture( GL_TEXTURE_2D, light.shadowMapTexture );
341 
342                     shader.bindUniformMatrix4fv( shader.LightProjectionView, light.projView );
343                     shader.bindUniformMatrix4fv( shader.CameraView, scene.camera.viewMatrix);
344                     shader.bindDirectionalLight( light, scene.camera.viewMatrix );
345 
346                     glDrawElements( GL_TRIANGLES, Assets.unitSquare.numVertices, GL_UNSIGNED_INT, null );
347                 }
348             }
349 
350             // Point Lights
351             if( !pointLights.empty )
352             {
353                 auto shader = Shaders.pointLight;
354                 glUseProgram( shader.programID );
355 
356                 bindGeometryOutputs( shader );
357 
358                 // bind WorldView for creating the View rays for reconstruction position
359                 shader.bindUniform2f( shader.ProjectionConstants, scene.camera.projectionConstants );
360 
361                 // bind the mesh for point lights
362                 glBindVertexArray( Assets.unitSquare.glVertexArray );
363 
364                 // bind and draw point lights
365                 foreach( light; pointLights )
366                 {
367                     //  logInfo(light.owner.name);
368                     shader.bindUniformMatrix4fv( shader.WorldView,
369                                                  scene.camera.viewMatrix * light.getTransform() );
370                     shader.bindUniformMatrix4fv( shader.WorldViewProjection,
371                                                  projection * scene.camera.viewMatrix * light.getTransform() );
372                     shader.bindPointLight( light, scene.camera.viewMatrix );
373                     glDrawElements( GL_TRIANGLES, Assets.unitSquare.numVertices, GL_UNSIGNED_INT, null );
374                 }
375             }
376 
377             // Spot Lights
378             if( !spotLights.empty )
379             {
380                 // TODO
381             }
382         }
383 
384         /**
385         * Draw the UI
386         */
387         void uiPass()
388         {
389             Shader shader = Shaders.userInterface;
390             glUseProgram( shader.programID );
391             glBindVertexArray( Assets.unitSquare.glVertexArray );
392 
393 			auto ui = scene.ui;
394             shader.bindUniformMatrix4fv( shader.WorldProj,
395                                          scene.camera.orthogonalMatrix * ui.scaleMat );
396             shader.bindUI( ui );
397             glDrawElements( GL_TRIANGLES, Assets.unitSquare.numVertices, GL_UNSIGNED_INT, null );
398 
399             glBindVertexArray(0);
400         }
401 
402         glBindFramebuffer( GL_FRAMEBUFFER, _deferredFrameBuffer );
403         // must be called before glClear to clear the depth buffer, otherwise depth buffer won't be cleared
404         glDepthMask( GL_TRUE );
405         glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
406         glEnable( GL_DEPTH_TEST );
407         glDisable( GL_BLEND );
408 
409         geometryPass();
410 
411         //glCullFace( GL_FRONT );
412 
413         shadowPass();
414 
415         //glCullFace( GL_BACK );
416         glViewport( 0, 0, Graphics.width, Graphics.height );
417 
418         // settings for light pass
419         glDepthMask( GL_FALSE );
420         glDisable( GL_DEPTH_TEST );
421         glEnable( GL_BLEND );
422         glBlendFunc( GL_ONE, GL_ONE );
423 
424         //This line switches back to the default framebuffer
425         glBindFramebuffer( GL_FRAMEBUFFER, 0 );
426         glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
427 
428         lightPass();
429 
430         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
431 
432         //ui is currently broken on Linux
433         //TODO: Repair the UI on Linux systems
434         version(linux){}
435         else
436             uiPass();
437 
438         // put it on the screen
439         swapBuffers();
440 
441         // clean up
442         glBindVertexArray(0);
443         glUseProgram(0);
444     }
445 }