1 module graphics.adapters.adapter;
2 import core.gameobject, core.properties;
3 import components;
4 import graphics.shaders;
5 import utility.config, utility.output;
6 
7 import gl3n.linalg;
8 import derelict.opengl3.gl3;
9 
10 version( Windows )
11 {
12 	import win32.windef;
13 	
14 	alias HGLRC GLRenderContext;
15 	alias HDC GLDeviceContext;
16 }
17 else version( OSX )
18 {
19 	import derelict.opengl3.gl3, derelict.opengl3.cgl;
20 	
21 	alias CGLContextObj GLRenderContext;
22 	alias uint GLDeviceContext;
23 }
24 else
25 {
26 	import derelict.opengl3.glx, derelict.opengl3.glxext;
27 	
28 	//alias OpenGLRenderContext GLRenderContext;
29 	alias GLXContext GLRenderContext;
30 	alias uint GLDeviceContext;
31 }
32 
33 abstract class Adapter
34 {
35 public:
36 	// Graphics contexts
37 	mixin Property!( "GLDeviceContext", "deviceContext", "protected" );
38 	mixin Property!( "GLRenderContext", "renderContext", "protected" );
39 
40 	mixin Property!( "uint", "width", "protected" );
41 	mixin Property!( "uint", "screenWidth", "protected" );
42 	mixin Property!( "uint", "height", "protected" );
43 	mixin Property!( "uint", "screenHeight", "protected" );
44 	mixin Property!( "bool", "fullscreen", "protected" );
45 	mixin Property!( "bool", "backfaceCulling", "protected" );
46 	mixin Property!( "bool", "vsync", "protected" );
47 	mixin Property!( "float", "fov", "protected" );
48 	mixin Property!( "float", "near", "protected" );
49 	mixin Property!( "float", "far", "protected" );
50 	mixin Property!( "uint", "deferredFrameBuffer", "protected" );
51 	mixin Property!( "uint", "diffuseRenderTexture", "protected" ); //Alpha channel stores Specular color
52 	mixin Property!( "uint", "normalRenderTexture", "protected" ); //Alpha channel stores Specular power
53 	mixin Property!( "uint", "depthRenderTexture", "protected" );
54 	
55 	enum : string 
56 	{
57 		GeometryShader = "geometry",
58 		LightingShader = "lighting",
59 		WindowMesh = "WindowMesh"
60 	}
61 
62 	abstract void initialize();
63 	abstract void shutdown();
64 	abstract void resize();
65 	abstract void reload();
66 	abstract void swapBuffers();
67 
68 	abstract void openWindow();
69 	abstract void closeWindow();
70 	
71 	abstract void messageLoop();
72 
73 	final void initializeDeferredRendering()
74 	{
75 		//http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/
76 
77 		//Create the frame buffer, which will contain the textures to render to
78 		deferredFrameBuffer = 0;
79 		glGenFramebuffers( 1, &_deferredFrameBuffer );
80 		glBindFramebuffer( GL_FRAMEBUFFER, deferredFrameBuffer );
81 
82 		//Generate our 3 textures
83 		glGenTextures( 1, &_diffuseRenderTexture );
84 		glGenTextures( 1, &_normalRenderTexture );
85 		glGenTextures( 1, &_depthRenderTexture );
86 
87 		//For each texture, we bind it to our active texture, and set the format and filtering
88 		glBindTexture( GL_TEXTURE_2D, diffuseRenderTexture );
89 		glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null );
90 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
91 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
92 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
93 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
94 
95 		glBindTexture( GL_TEXTURE_2D, normalRenderTexture );
96 		glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, null );
97 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
98 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
99 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
100 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
101 
102 		glBindTexture( GL_TEXTURE_2D, depthRenderTexture );
103 		glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, null );
104 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
105 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
106 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
107 		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
108 
109 		//And finally set all of these to our frameBuffer
110 		glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, diffuseRenderTexture, 0 );
111 		glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, normalRenderTexture, 0 );
112 		glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthRenderTexture, 0 );
113 
114 		GLenum[ 2 ] DrawBuffers = [ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 ];
115 		glDrawBuffers( 2, DrawBuffers.ptr );
116 		glViewport(0, 0, width, height);
117 		updateProjection();
118 
119 		if( glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE )
120 		{
121 			log( OutputType.Error, "Deffered rendering Frame Buffer was not initialized correctly.");
122 			assert(false);
123 		}
124 	}
125 	
126 	/**
127 	 * sets up the rendering pipeline for the geometry pass
128 	 */
129 	final void beginDraw()
130 	{
131 		glBindFramebuffer( GL_FRAMEBUFFER, deferredFrameBuffer );
132 	
133 		// must be called before glClear to clear the depth buffer, otherwise
134 		// depth buffer won't be cleared
135 		glDepthMask( GL_TRUE );
136 
137 		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
138 		
139 		glEnable( GL_DEPTH_TEST );
140 		glDisable( GL_BLEND );
141 
142 		glUseProgram( Shaders[GeometryShader].programID );
143 	}
144 	
145 	/**
146 	 * draws an object for the geometry pass
147 	 * beginDraw must be called before any calls of this function
148 	 * Params:
149 	 *	object = the object to be drawn
150 	 */
151 	final void drawObject( GameObject object )
152 	{
153 		// set the shader
154 		auto shader = Shaders[GeometryShader];
155 		glBindVertexArray( object.mesh.glVertexArray );
156 
157 		shader.bindUniformMatrix4fv( ShaderUniform.World , object.transform.matrix );
158 		shader.bindUniformMatrix4fv( ShaderUniform.WorldViewProjection , projection * 
159 										( ( activeCamera !is null ) ? activeCamera.viewMatrix : mat4.identity ) *
160 										object.transform.matrix );
161 
162 		shader.bindMaterial( object.material );
163 
164 		glDrawElements( GL_TRIANGLES, object.mesh.numVertices, GL_UNSIGNED_INT, null );
165 
166 		glBindVertexArray(0);
167 	}
168 	
169 	/**
170 	 * called after all desired objects are drawn
171 	 * handles lighting and post processing
172 	 */
173 	final void endDraw()
174 	{
175 		// settings for light pass
176 		glDepthMask( GL_FALSE );
177 		glDisable( GL_DEPTH_TEST );
178 		glEnable( GL_BLEND );
179 		// glBlendEquation( GL_FUNC_ADD );
180 		// glBlendFunc(GL_ONE, GL_ONE );
181 		
182 		//This line switches back to the default framebuffer
183 		glBindFramebuffer( GL_FRAMEBUFFER, 0 );
184 		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
185 
186 		auto shader = Shaders[LightingShader];
187 		glUseProgram( shader.programID );
188 		
189 		// bind geometry pass textures
190 		GLint textureLocation = shader.getUniformLocation( ShaderUniform.DiffuseTexture );
191 		glUniform1i( textureLocation, 0 );
192 		glActiveTexture( GL_TEXTURE0 );
193 		glBindTexture( GL_TEXTURE_2D, diffuseRenderTexture );
194 
195 		textureLocation = shader.getUniformLocation( ShaderUniform.NormalTexture );
196 		glUniform1i( textureLocation, 1 );
197 		glActiveTexture( GL_TEXTURE1 );
198 		glBindTexture( GL_TEXTURE_2D, normalRenderTexture );
199 
200 		textureLocation = shader.getUniformLocation( ShaderUniform.DepthTexture );
201 		glUniform1i( textureLocation, 2 );
202 		glActiveTexture( GL_TEXTURE2 );
203 		glBindTexture( GL_TEXTURE_2D, depthRenderTexture );
204 		
205 		// bind the directional and ambient lights
206 		if( directionalLight is null )
207 		{
208 			directionalLight = new DirectionalLight( vec3(), vec3() );
209 		}
210 		if( ambientLight is null )
211 		{
212 			ambientLight = new AmbientLight( vec3() );
213 		}
214 		shader.bindDirectionalLight( directionalLight );
215 		shader.bindAmbientLight( ambientLight );
216 		
217 		// bind the window mesh for directional lights
218 		glBindVertexArray( Assets.get!Mesh( WindowMesh ).glVertexArray );
219 		glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_INT, null );
220 
221 		glBindVertexArray(0);
222 		glUseProgram(0);
223 
224 		swapBuffers();
225 
226 		lights = [];
227 		ambientLight = null;
228 		directionalLight = null;
229 	}
230 
231 	final void addLight( Light light )
232 	{
233 		if( typeid( light ) == typeid( AmbientLight ) )
234 		{
235 			if( ambientLight is null )
236 			{
237 				ambientLight = cast(AmbientLight)light;
238 			}
239 			else
240 				log( OutputType.Info, "Attemtping to add multiple ambient lights to the scene.  Ignoring additional ambient lights." );
241 		}
242 		else if( typeid( light ) == typeid( DirectionalLight ) )
243 		{
244 			if( directionalLight is null )
245 			{
246 				directionalLight = cast(DirectionalLight)light;
247 			}
248 			else
249 				log( OutputType.Info, "Attemtping to add multiple directional lights to the scene.  Ignoring additional directional lights." );
250 		}
251 		else
252 		{
253 			lights ~= light;
254 		}
255 	}
256 
257 	final void setCamera( Camera camera )
258 	{
259 		activeCamera = camera;
260 	}
261 
262 protected:
263 	final void loadProperties()
264 	{
265 		fullscreen = Config.get!bool( "Display.Fullscreen" );
266 		if( fullscreen )
267 		{
268 			width = screenWidth;
269 			height = screenHeight;
270 		}
271 		else
272 		{
273 			width = Config.get!uint( "Display.Width" );
274 			height = Config.get!uint( "Display.Height" );
275 		}
276 
277 		backfaceCulling = Config.get!bool( "Graphics.BackfaceCulling" );
278 		vsync = Config.get!bool( "Graphics.VSync" );
279 		fov = Config.get!float( "Display.FieldOfView" );
280 		near = Config.get!float( "Display.NearPlane" );
281 		far = Config.get!float( "Display.FarPlane" );
282 	}
283 
284 	final void updateProjection()
285 	{
286 		projection = mat4.perspective( cast(float)width, cast(float)height, fov, near, far );
287 	}
288 
289 private:
290 	Camera activeCamera;
291 	mat4 projection;
292 	//To be cleared after a draw call:
293 	AmbientLight ambientLight;
294 	DirectionalLight directionalLight;
295 	Light[] lights;
296 }