1 /************************************************************************************
2 
3 	AstroMenace
4 	Hardcore 3D space scroll-shooter with spaceship upgrade possibilities.
5 	Copyright (c) 2006-2019 Mikhail Kurinnoi, Viewizard
6 
7 
8 	AstroMenace is free software: you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation, either version 3 of the License, or
11 	(at your option) any later version.
12 
13 	AstroMenace is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 	GNU General Public License for more details.
17 
18 	You should have received a copy of the GNU General Public License
19 	along with AstroMenace. If not, see <https://www.gnu.org/licenses/>.
20 
21 
22 	Website: https://viewizard.com/
23 	Project: https://github.com/viewizard/astromenace
24 	E-mail: viewizard@viewizard.com
25 
26 *************************************************************************************/
27 
28 // NOTE SDL2 could be used for OpenGL context setup. "request" OpenGL context version:
29 //      SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
30 //      SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
31 
32 // NOTE glGetStringi() for GL_EXTENSIONS (since OpenGL 3.0)
33 //      glGetString() usage with GL_EXTENSIONS is deprecated
34 //
35 //      int NumberOfExtensions;
36 //      glGetIntegerv(GL_NUM_EXTENSIONS, &NumberOfExtensions);
37 //      for (int i = 0; i < NumberOfExtensions; i++) {
38 //      	const GLubyte *one_string = glGetStringi(GL_EXTENSIONS, i);
39 //      }
40 
41 // NOTE GL_MAX_TEXTURE_MAX_ANISOTROPY (since OpenGL 4.6)
42 //      could be used to replace GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
43 
44 #include "graphics_internal.h"
45 #include "graphics.h"
46 #include "extensions.h"
47 
48 namespace viewizard {
49 
50 namespace {
51 
52 // hardware device capabilities
53 sDevCaps DevCaps{};
54 
55 // for 2D we need fixed internal resolution, since window's size could be
56 // different and we should guaranty, that 2D looks same for all window's sizes,
57 // it is important to understand, that we don't use this values directly,
58 // but only for 'mapping' internal window coordinates into real window coordinates
59 float InternalWidth{0.0f};
60 float InternalHeight{0.0f};
61 bool InternalResolution{false};
62 
63 // pointer to main window structure
64 SDL_Window *SDLWindow{nullptr};
65 // pointer to OpenGL context
66 SDL_GLContext GLContext{nullptr};
67 
68 // main FBO
69 std::shared_ptr<sFBO> MainFBO{};
70 // resolve FBO (for blit main FBO with multisample)
71 std::shared_ptr<sFBO> ResolveFBO{};
72 
73 } // unnamed namespace
74 
75 
76 /*
77  * Get SDL window handle.
78  */
vw_GetSDLWindow()79 SDL_Window *vw_GetSDLWindow()
80 {
81 	assert(SDLWindow);
82 
83 	return SDLWindow;
84 }
85 
86 /*
87  * Check supported OpenGL extension.
88  */
ExtensionSupported(const char * Extension)89 static bool ExtensionSupported(const char *Extension)
90 {
91 	char *extensions;
92 	extensions = (char *) glGetString(GL_EXTENSIONS); // WARNING fix conversion
93 	if (strstr(extensions, Extension) != nullptr)
94 		return true;
95 	return false;
96 }
97 
98 /*
99  * Create window.
100  */
vw_CreateWindow(const char * Title,int Width,int Height,bool Fullscreen,int DisplayIndex)101 bool vw_CreateWindow(const char *Title, int Width, int Height, bool Fullscreen, int DisplayIndex)
102 {
103 	Uint32 Flags{SDL_WINDOW_OPENGL};
104 
105 	if (Fullscreen)
106 		Flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
107 
108 	SDLWindow = SDL_CreateWindow(Title,
109 				     SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayIndex),
110 				     SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayIndex),
111 				     Width, Height, Flags);
112 	if (!SDLWindow) {
113 		std::cerr << __func__ << "(): " << "SDL_CreateWindow() failed: " << SDL_GetError() << "\n";
114 		std::cerr << __func__ << "(): " << "Can't set video mode " <<  Width << " x " << Height << "\n\n";
115 		return false;
116 	}
117 
118 	if (Fullscreen)
119 		std::cout << "Fullscreen mode: ";
120 	else
121 		std::cout << "Windowed mode: ";
122 	std::cout << Width << " x " << Height << "\n\n";
123 
124 	SDL_DisableScreenSaver();
125 
126 	return true;
127 }
128 
129 /*
130  * Destroy window.
131  */
vw_DestroyWindow()132 void vw_DestroyWindow()
133 {
134 	if (!SDLWindow)
135 		return;
136 
137 	SDL_DestroyWindow(SDLWindow);
138 	SDLWindow = nullptr;
139 }
140 
141 /*
142  * Create OpenGL context.
143  */
vw_CreateOpenGLContext(int VSync)144 bool vw_CreateOpenGLContext(int VSync)
145 {
146 	if (!SDLWindow) {
147 		std::cerr << __func__ << "(): " << "Can't create OpenGL context, create window first.\n";
148 		return false;
149 	}
150 
151 	GLContext = SDL_GL_CreateContext(SDLWindow);
152 
153 	if (!GLContext) {
154 		std::cerr << __func__ << "(): " << "SDL_GL_CreateContext() failed: " << SDL_GetError() << "\n";
155 		std::cerr << __func__ << "(): " << "Can't create OpenGL context.\n";
156 		return false;
157 	}
158 
159 	if (SDL_GL_SetSwapInterval(VSync) == -1)
160 		std::cerr << __func__ << "(): " << "SDL_GL_SetSwapInterval() failed: " << SDL_GetError() << "\n";
161 
162 	DevCaps.OpenGLmajorVersion = 1;
163 	DevCaps.OpenGLminorVersion = 0;
164 	DevCaps.MaxTextureWidth = 0;
165 	DevCaps.MaxTextureHeight = 0;
166 	DevCaps.MaxActiveLights = 0;
167 	DevCaps.MaxAnisotropyLevel = 0;
168 	DevCaps.FramebufferObjectDepthSize = 0;
169 
170 	DevCaps.OpenGL_1_3_supported = Initialize_OpenGL_1_3();
171 	DevCaps.OpenGL_1_5_supported = Initialize_OpenGL_1_5();
172 	DevCaps.OpenGL_2_0_supported = Initialize_OpenGL_2_0();
173 	DevCaps.OpenGL_2_1_supported = Initialize_OpenGL_2_1();
174 	DevCaps.OpenGL_3_0_supported = Initialize_OpenGL_3_0();
175 	DevCaps.OpenGL_4_2_supported = Initialize_OpenGL_4_2();
176 	Initialize_GL_NV_framebuffer_multisample_coverage(); // we don't have it in DevCaps, this is 1 function check only
177 
178 	DevCaps.EXT_texture_compression_s3tc = ExtensionSupported("GL_EXT_texture_compression_s3tc");
179 	DevCaps.ARB_texture_compression_bptc = ExtensionSupported("GL_ARB_texture_compression_bptc");
180 	DevCaps.ARB_texture_non_power_of_two = ExtensionSupported("GL_ARB_texture_non_power_of_two");
181 	DevCaps.SGIS_generate_mipmap = ExtensionSupported("GL_SGIS_generate_mipmap");
182 
183 	if (ExtensionSupported("GL_EXT_texture_filter_anisotropic")) {
184 		glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &DevCaps.MaxAnisotropyLevel);
185 		std::cout << "Max anisotropy: " << DevCaps.MaxAnisotropyLevel << "\n";
186 	}
187 
188 	std::cout << "Vendor     : " << glGetString(GL_VENDOR) << "\n";
189 	std::cout << "Renderer   : " << glGetString(GL_RENDERER) << "\n";
190 	std::cout << "Version    : " << glGetString(GL_VERSION) << "\n";
191 	glGetIntegerv(GL_MAJOR_VERSION, &DevCaps.OpenGLmajorVersion);
192 	glGetIntegerv(GL_MINOR_VERSION, &DevCaps.OpenGLminorVersion);
193 	glGetError(); // reset errors
194 	std::cout << "OpenGL Version    : " << DevCaps.OpenGLmajorVersion << "." << DevCaps.OpenGLminorVersion << "\n\n";
195 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &DevCaps.MaxTextureHeight);
196 	std::cout << "Max texture height: " << DevCaps.MaxTextureHeight << "\n";
197 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &DevCaps.MaxTextureWidth);
198 	std::cout << "Max texture width: " << DevCaps.MaxTextureWidth << "\n";
199 	glGetIntegerv(GL_MAX_LIGHTS, &DevCaps.MaxActiveLights);
200 	std::cout << "Max lights: " << DevCaps.MaxActiveLights << "\n";
201 
202 	// since we support FBO, should check supported samples
203 	if (DevCaps.OpenGL_3_0_supported) {
204 		int MaxSamples{0};
205 		glGetIntegerv(GL_MAX_SAMPLES_EXT, &MaxSamples);
206 		std::cout << "Max Samples: " << MaxSamples << "\n";
207 
208 		// MSAA
209 		for (int i = 2; i <= MaxSamples; i *= 2) {
210 			DevCaps.MultisampleCoverageModes.emplace_back(i, i);
211 		}
212 
213 		// CSAA
214 		if (ExtensionSupported("GL_NV_framebuffer_multisample_coverage")) {
215 			int NumModes{0};
216 			glGetIntegerv(GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV, &NumModes);
217 			std::cout << "Coverage modes: " << NumModes << "\n";
218 			std::vector<int> modes(NumModes * 2);
219 			glGetIntegerv(GL_MULTISAMPLE_COVERAGE_MODES_NV, modes.data());
220 			// fill MultisampleCoverageModes with MSAA/CSAA modes
221 			DevCaps.MultisampleCoverageModes.clear();
222 			for (int i = 0; i < (NumModes * 2); i += 2) {
223 				DevCaps.MultisampleCoverageModes.emplace_back(modes[i + 1], modes[i]);
224 			}
225 		}
226 	}
227 
228 #ifndef NDEBUG
229 	// print all supported OpenGL extensions (one per line)
230 	if (glGetString(GL_EXTENSIONS) != nullptr) {
231 		std::string extensions{(char *)glGetString(GL_EXTENSIONS)}; // WARNING fix conversion
232 		if (!extensions.empty()) {
233 			std::replace(extensions.begin(), extensions.end(), ' ', '\n'); // replace all ' ' to '\n'
234 			std::cout << "Supported OpenGL extensions:\n" << extensions << "\n";
235 		}
236 	}
237 #endif // NDEBUG
238 
239 	std::cout << "\n";
240 
241 	return true;
242 }
243 
244 /*
245  * Delete OpenGL context.
246  */
vw_DeleteOpenGLContext()247 void vw_DeleteOpenGLContext()
248 {
249 	if (!GLContext)
250 		return;
251 
252 	SDL_GL_DeleteContext(GLContext);
253 	GLContext = nullptr;
254 }
255 
256 /*
257  * Initialize (or reinitialize) and setup OpenGL related stuff.
258  */
vw_InitOpenGLStuff(int Width,int Height,int * MSAA,int * CSAA)259 void vw_InitOpenGLStuff(int Width, int Height, int *MSAA, int *CSAA)
260 {
261 	glPixelStorei(GL_PACK_ALIGNMENT, 1);
262 	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
263 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
264 	glEnable(GL_CULL_FACE);
265 	glPolygonMode(GL_FRONT, GL_FILL);
266 	glEnable(GL_TEXTURE_2D);
267 	glEnable(GL_DEPTH_TEST);
268 	glShadeModel(GL_SMOOTH);
269 	glClearDepth(1.0);
270 	glClearStencil(0);
271 	glDepthFunc(GL_LEQUAL);
272 	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
273 	glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
274 	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
275 	glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
276 
277 	if (DevCaps.OpenGL_3_0_supported) {
278 		MainFBO = vw_BuildFBO(Width, Height, true, true, *MSAA, CSAA);
279 		ResolveFBO = vw_BuildFBO(Width, Height, true, false);
280 		if (!MainFBO || !ResolveFBO) {
281 			*MSAA = 0;
282 			MainFBO.reset();
283 			ResolveFBO.reset();
284 			DevCaps.FramebufferObjectDepthSize = 0;
285 		}
286 	} else {
287 		*MSAA = 0;
288 		MainFBO.reset();
289 		ResolveFBO.reset();
290 		DevCaps.FramebufferObjectDepthSize = 0;
291 	}
292 }
293 
294 /*
295  * Release OpenGL related stuff.
296  */
vw_ReleaseOpenGLStuff()297 void vw_ReleaseOpenGLStuff()
298 {
299 	vw_ReleaseAllShaders();
300 
301 	MainFBO.reset();
302 	ResolveFBO.reset();
303 }
304 
305 /*
306  * Get device capability.
307  */
vw_DevCaps()308 const sDevCaps &vw_DevCaps()
309 {
310 	return DevCaps;
311 }
312 
313 /*
314  * Internal access to DevCaps, with write access.
315  */
ChangeDevCaps()316 sDevCaps &ChangeDevCaps()
317 {
318 	return DevCaps;
319 }
320 
321 /*
322  * Resize scene.
323  */
vw_ResizeScene(float FieldOfViewAngle,float AspectRatio,float zNearClip,float zFarClip)324 void vw_ResizeScene(float FieldOfViewAngle, float AspectRatio, float zNearClip, float zFarClip)
325 {
326 	glMatrixMode(GL_PROJECTION);
327 	glLoadIdentity();
328 
329 	gluPerspective(FieldOfViewAngle, AspectRatio, zNearClip, zFarClip);
330 
331 	glMatrixMode(GL_MODELVIEW);
332 	glLoadIdentity();
333 }
334 
335 /*
336  * Clear buffers.
337  */
vw_Clear(int mask)338 void vw_Clear(int mask)
339 {
340 	GLbitfield glmask{0};
341 
342 	if (mask & 0x1000)
343 		glmask = glmask | GL_COLOR_BUFFER_BIT;
344 	if (mask & 0x0100)
345 		glmask = glmask | GL_DEPTH_BUFFER_BIT;
346 	if (mask & 0x0010)
347 		glmask = glmask | GL_ACCUM_BUFFER_BIT;
348 	if (mask & 0x0001)
349 		glmask = glmask | GL_STENCIL_BUFFER_BIT;
350 
351 	glClear(glmask);
352 }
353 
354 /*
355  * Begin rendering.
356  */
vw_BeginRendering(int mask)357 void vw_BeginRendering(int mask)
358 {
359 	vw_BindFBO(MainFBO);
360 
361 	vw_Clear(mask);
362 
363 	glMatrixMode(GL_MODELVIEW);
364 	glLoadIdentity();
365 }
366 
367 /*
368  * End rendering.
369  */
vw_EndRendering()370 void vw_EndRendering()
371 {
372 	if (MainFBO) {
373 		std::shared_ptr<sFBO> tmpEmptyFBO{};
374 
375 		if (MainFBO->ColorTexture)
376 			vw_DrawColorFBO(MainFBO, tmpEmptyFBO);
377 		else {
378 			// if we use multisamples, should blit to color buffer first
379 			vw_BlitFBO(MainFBO, ResolveFBO);
380 			vw_DrawColorFBO(ResolveFBO, tmpEmptyFBO);
381 		}
382 	}
383 
384 	assert(SDLWindow);
385 
386 	SDL_GL_SwapWindow(SDLWindow);
387 }
388 
389 /*
390  * Set virtual internal resolution size and status.
391  */
vw_SetInternalResolution(float Width,float Height,bool Status)392 void vw_SetInternalResolution(float Width, float Height, bool Status)
393 {
394 	InternalResolution = Status;
395 
396 	if (Status) {
397 		InternalWidth = Width;
398 		InternalHeight = Height;
399 	}
400 }
401 
402 /*
403  * Get virtual internal resolution.
404  */
vw_GetInternalResolution(float * Width,float * Height)405 bool vw_GetInternalResolution(float *Width, float *Height)
406 {
407 	if (Width)
408 		*Width = InternalWidth;
409 	if (Height)
410 		*Height = InternalHeight;
411 
412 	return InternalResolution;
413 }
414 
415 /*
416  * Set depth range.
417  */
vw_DepthRange(GLdouble zNear,GLdouble zFar)418 void vw_DepthRange(GLdouble zNear, GLdouble zFar)
419 {
420 	glDepthRange(zNear, zFar);
421 }
422 
423 /*
424  * Set viewport data.
425  */
vw_SetViewport(GLint x,GLint y,GLsizei width,GLsizei height,eOrigin Origin)426 void vw_SetViewport(GLint x, GLint y, GLsizei width, GLsizei height, eOrigin Origin)
427 {
428 	assert(SDLWindow);
429 
430 	if (Origin == eOrigin::upper_left) {
431 		int SDLWindowWidth, SDLWindowHeight;
432 		SDL_GetWindowSize(SDLWindow, &SDLWindowWidth, &SDLWindowHeight);
433 		y = SDLWindowHeight - y - height;
434 	}
435 
436 	glViewport(x, y, width, height);
437 }
438 
439 /*
440  * Get viewport data.
441  */
vw_GetViewport(float * x,float * y,float * width,float * height)442 void vw_GetViewport(float *x, float *y, float *width, float *height)
443 {
444 	GLfloat buff[4];
445 	glGetFloatv(GL_VIEWPORT, buff);
446 
447 	if (x)
448 		*x = buff[0];
449 	if (y)
450 		*y = buff[1];
451 	if (width)
452 		*width = buff[2];
453 	if (height)
454 		*height = buff[3];
455 }
456 
457 /*
458  * Get viewport data.
459  */
vw_GetViewport(int * x,int * y,int * width,int * height)460 void vw_GetViewport(int *x, int *y, int *width, int *height)
461 {
462 	GLint buff[4];
463 	glGetIntegerv(GL_VIEWPORT, buff);
464 
465 	if (x)
466 		*x = buff[0];
467 	if (y)
468 		*y = buff[1];
469 	if (width)
470 		*width = buff[2];
471 	if (height)
472 		*height = buff[3];
473 }
474 
475 /*
476  * Set what facets can be culled.
477  */
vw_CullFace(eCullFace mode)478 void vw_CullFace(eCullFace mode)
479 {
480 	if (mode == eCullFace::NONE) {
481 		glDisable(GL_CULL_FACE);
482 		return;
483 	}
484 
485 	glEnable(GL_CULL_FACE);
486 	glCullFace(static_cast<GLenum>(mode));
487 }
488 
489 /*
490  * Set the scale and units used to calculate depth values.
491  */
vw_PolygonOffset(bool status,GLfloat factor,GLfloat units)492 void vw_PolygonOffset(bool status, GLfloat factor, GLfloat units)
493 {
494 	if (status)
495 		glEnable(GL_POLYGON_OFFSET_FILL);
496 	else
497 		glDisable(GL_POLYGON_OFFSET_FILL);
498 
499 	glPolygonOffset(factor, units);
500 }
501 
502 /*
503  * Specify clear values for the color buffers.
504  */
vw_SetClearColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha)505 void vw_SetClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
506 {
507 	glClearColor(red, green, blue, alpha);
508 }
509 
510 /*
511  * Set color.
512  */
vw_SetColor(GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)513 void vw_SetColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
514 {
515 	glColor4f(red, green, blue, alpha);
516 }
517 
518 /*
519  * Specifies whether the individual color components in the frame buffer can or cannot be written.
520  */
vw_SetColorMask(GLboolean red,GLboolean green,GLboolean blue,GLboolean alpha)521 void vw_SetColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
522 {
523 	glColorMask(red, green, blue, alpha);
524 }
525 
526 /*
527  * Set depth buffer.
528  */
vw_DepthTest(bool mode,eCompareFunc func)529 void vw_DepthTest(bool mode, eCompareFunc func)
530 {
531 	if (mode) {
532 		glEnable(GL_DEPTH_TEST);
533 		glDepthFunc(static_cast<GLenum>(func));
534 	} else
535 		glDisable(GL_DEPTH_TEST);
536 }
537 
538 } // viewizard namespace
539