1 /*
2  * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "graphics/opengl/OpenGLRenderer.h"
21 
22 #include "core/Application.h"
23 #include "graphics/opengl/GLNoVertexBuffer.h"
24 #include "graphics/opengl/GLTexture2D.h"
25 #include "graphics/opengl/GLTextureStage.h"
26 #include "graphics/opengl/GLVertexBuffer.h"
27 #include "io/log/Logger.h"
28 #include "platform/CrashHandler.h"
29 #include "window/RenderWindow.h"
30 
31 static const char vertexShaderSource[] = "void main() {\n"
32 	"	// Convert pre-transformed D3D vertices to OpenGL vertices.\n"
33 	"	float w = 1.0 / gl_Vertex.w;\n"
34 	"	vec4 vertex = vec4(gl_Vertex.xyz * w, w);\n"
35 	"	// We only need the projection matrix as modelview will always be idenity.\n"
36 	"	gl_Position = gl_ProjectionMatrix * vertex;\n"
37 	"	gl_FrontColor = gl_BackColor = gl_Color;\n"
38 	"	gl_TexCoord[0] = gl_MultiTexCoord0;\n"
39 	"	gl_FogFragCoord = vertex.z;\n"
40 	"}\n";
41 
OpenGLRenderer()42 OpenGLRenderer::OpenGLRenderer() : useVertexArrays(false), useVBOs(false), maxTextureStage(0), shader(0), maximumAnisotropy(1.f), initialized(false) { }
43 
~OpenGLRenderer()44 OpenGLRenderer::~OpenGLRenderer() {
45 
46 	shutdown();
47 
48 	// TODO textures must be destructed before OpenGLRenderer or not at all
49 	//for(TextureList::iterator it = textures.begin(); it != textures.end(); ++it) {
50 	//	LogWarning << "Texture still loaded: " << it->getFileName();
51 	//}
52 
53 };
54 
55 enum GLTransformMode {
56 	GL_UnsetTransform,
57 	GL_NoTransform,
58 	GL_ModelViewProjectionTransform
59 };
60 
61 static GLTransformMode currentTransform;
62 
checkShader(GLuint object,const char * op,GLuint check)63 static bool checkShader(GLuint object, const char * op, GLuint check) {
64 
65 	GLint status;
66 	glGetObjectParameterivARB(object, check, &status);
67 	if(!status) {
68 		int logLength;
69 		glGetObjectParameterivARB(object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &logLength);
70 		char * log = new char[logLength];
71 		glGetInfoLogARB(object, logLength, NULL, log);
72 		LogWarning << "Failed to " << op << " vertex shader: " << log;
73 		delete[] log;
74 		return false;
75 	}
76 
77 	return true;
78 }
79 
loadVertexShader(const char * source)80 static GLuint loadVertexShader(const char * source) {
81 
82 	GLuint shader = glCreateProgramObjectARB();
83 	if(!shader) {
84 		LogWarning << "Failed to create program object";
85 		return 0;
86 	}
87 
88 	GLuint obj = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
89 	if(!obj) {
90 		LogWarning << "Failed to create shader object";
91 		glDeleteObjectARB(shader);
92 		return 0;
93 	}
94 
95 	glShaderSourceARB(obj, 1, &source, NULL);
96 	glCompileShaderARB(obj);
97 	if(!checkShader(obj, "compile", GL_OBJECT_COMPILE_STATUS_ARB)) {
98 		glDeleteObjectARB(obj);
99 		glDeleteObjectARB(shader);
100 		return 0;
101 	}
102 
103 	glAttachObjectARB(shader, obj);
104 	glDeleteObjectARB(obj);
105 
106 	glLinkProgramARB(shader);
107 	if(!checkShader(shader, "link", GL_OBJECT_LINK_STATUS_ARB)) {
108 		glDeleteObjectARB(shader);
109 		return 0;
110 	}
111 
112 	return shader;
113 }
114 
Initialize()115 void OpenGLRenderer::Initialize() {
116 
117 	if(glewInit() != GLEW_OK) {
118 		LogError << "GLEW init failed";
119 	}
120 
121 	CrashHandler::setVariable("GLEW version", glewGetString(GLEW_VERSION));
122 
123 	LogInfo << "Using OpenGL " << glGetString(GL_VERSION);
124 	CrashHandler::setVariable("OpenGL version", glGetString(GL_VERSION));
125 
126 	LogInfo << " ├─ Vendor: " << glGetString(GL_VENDOR);
127 	CrashHandler::setVariable("OpenGL vendor", glGetString(GL_VENDOR));
128 
129 	LogInfo << " └─ Device: " << glGetString(GL_RENDERER);
130 	CrashHandler::setVariable("OpenGL device", glGetString(GL_RENDERER));
131 
132 	reinit();
133 }
134 
reinit()135 void OpenGLRenderer::reinit() {
136 
137 	arx_assert(!initialized);
138 
139 	if(!GLEW_ARB_vertex_array_bgra) {
140 		LogWarning << "Missing OpenGL extension ARB_vertex_array_bgra, not using vertex arrays!";
141 	}
142 	useVertexArrays = GLEW_ARB_vertex_array_bgra == GL_TRUE;
143 
144 	if(!GLEW_ARB_draw_elements_base_vertex) {
145 		LogWarning << "Missing OpenGL extension ARB_draw_elements_base_vertex!";
146 	}
147 
148 	useVBOs = useVertexArrays;
149 	if(useVBOs && !GLEW_ARB_map_buffer_range) {
150 		LogWarning << "Missing OpenGL extension ARB_map_buffer_range, VBO performance will suffer.";
151 	}
152 
153 	glEnable(GL_POLYGON_OFFSET_FILL);
154 
155 	glDepthFunc(GL_LEQUAL);
156 
157 	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
158 	glPixelStorei(GL_PACK_ALIGNMENT, 1);
159 
160 	// number of conventional fixed-function texture units
161 	GLint texunits = 0;
162 	glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texunits);
163 	m_TextureStages.resize(texunits, NULL);
164 	for(size_t i = 0; i < m_TextureStages.size(); ++i) {
165 		m_TextureStages[i] = new GLTextureStage(this, i);
166 	}
167 
168 	SetRenderState(ColorKey, true);
169 
170 	// Clear screen
171 	Clear(ColorBuffer | DepthBuffer);
172 
173 	currentTransform = GL_UnsetTransform;
174 	glArrayClientState = GL_NoArray;
175 
176 	CHECK_GL;
177 
178 	if(useVertexArrays && useVBOs) {
179 		if(!GLEW_ARB_shader_objects) {
180 			LogWarning << "Missing OpenGL extension ARB_shader_objects.";
181 		} else if(!GLEW_ARB_vertex_program) {
182 			LogWarning << "Missing OpenGL extension ARB_vertex_program.";
183 		} else {
184 			shader = loadVertexShader(vertexShaderSource);
185 			CHECK_GL;
186 		}
187 		if(!shader) {
188 			LogWarning << "Missing vertex shader, cannot use vertex arrays for pre-transformed vertices.";
189 		}
190 	}
191 
192 	if(GLEW_EXT_texture_filter_anisotropic) {
193 		glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maximumAnisotropy);
194 		CHECK_GL;
195 	}
196 
197 	initialized = true;
198 }
199 
shutdown()200 void OpenGLRenderer::shutdown() {
201 
202 	arx_assert(initialized);
203 
204 	if(shader) {
205 		glDeleteObjectARB(shader);
206 		CHECK_GL;
207 	}
208 
209 	for(size_t i = 0; i < m_TextureStages.size(); ++i) {
210 		delete m_TextureStages[i];
211 	}
212 	m_TextureStages.clear();
213 
214 	maximumAnisotropy = 1.f;
215 
216 	initialized = false;
217 }
218 
BeginScene()219 void OpenGLRenderer::BeginScene() {
220 }
221 
EndScene()222 void OpenGLRenderer::EndScene() {
223 
224 	glFlush();
225 }
226 
227 static EERIEMATRIX projection;
228 static EERIEMATRIX view;
229 
enableTransform()230 void OpenGLRenderer::enableTransform() {
231 
232 	if(currentTransform == GL_ModelViewProjectionTransform) {
233 		return;
234 	}
235 
236 	if(shader) {
237 		glUseProgram(0);
238 	}
239 
240 	glMatrixMode(GL_MODELVIEW);
241 	glLoadMatrixf(&view._11);
242 
243 	glMatrixMode(GL_PROJECTION);
244 	glLoadMatrixf(&projection._11);
245 
246 	currentTransform = GL_ModelViewProjectionTransform;
247 
248 	CHECK_GL;
249 }
250 
disableTransform()251 void OpenGLRenderer::disableTransform() {
252 
253 	if(currentTransform == GL_NoTransform) {
254 		return;
255 	}
256 
257 	// D3D doesn't apply any transform for D3DTLVERTEX
258 	// but we still need to change from D3D to OpenGL coordinates
259 
260 	if(shader) {
261 		glUseProgram(shader);
262 	} else {
263 		glMatrixMode(GL_MODELVIEW);
264 		glLoadIdentity();
265 	}
266 
267 	glMatrixMode(GL_PROJECTION);
268 	glLoadIdentity();
269 
270 	// Change coordinate system from [0, width] x [0, height] to [-1, 1] x [-1, 1] and flip the y axis
271 	glTranslatef(-1.f, 1.f, 0.f);
272 	glScalef(2.f/viewport.width(), -2.f/viewport.height(), 1.f);
273 
274 	// Change the viewport and pixel origins
275 	glTranslatef(.5f - viewport.left, .5f - viewport.top, 0.f);
276 
277 	currentTransform = GL_NoTransform;
278 
279 	CHECK_GL;
280 }
281 
SetViewMatrix(const EERIEMATRIX & matView)282 void OpenGLRenderer::SetViewMatrix(const EERIEMATRIX & matView) {
283 
284 	if(!memcmp(&view, &matView, sizeof(EERIEMATRIX))) {
285 		return;
286 	}
287 
288 	if(currentTransform == GL_ModelViewProjectionTransform) {
289 		currentTransform = GL_UnsetTransform;
290 	}
291 
292 	view = matView;
293 }
294 
GetViewMatrix(EERIEMATRIX & matView) const295 void OpenGLRenderer::GetViewMatrix(EERIEMATRIX & matView) const {
296 	matView = view;
297 }
298 
SetProjectionMatrix(const EERIEMATRIX & matProj)299 void OpenGLRenderer::SetProjectionMatrix(const EERIEMATRIX & matProj) {
300 
301 	if(!memcmp(&projection, &matProj, sizeof(EERIEMATRIX))) {
302 		return;
303 	}
304 
305 	if(currentTransform == GL_ModelViewProjectionTransform) {
306 		currentTransform = GL_UnsetTransform;
307 	}
308 
309 	projection = matProj;
310 }
311 
GetProjectionMatrix(EERIEMATRIX & matProj) const312 void OpenGLRenderer::GetProjectionMatrix(EERIEMATRIX & matProj) const {
313 	matProj = projection;
314 }
315 
ReleaseAllTextures()316 void OpenGLRenderer::ReleaseAllTextures() {
317 	for(TextureList::iterator it = textures.begin(); it != textures.end(); ++it) {
318 		it->Destroy();
319 	}
320 }
321 
RestoreAllTextures()322 void OpenGLRenderer::RestoreAllTextures() {
323 	for(TextureList::iterator it = textures.begin(); it != textures.end(); ++it) {
324 		it->Restore();
325 	}
326 }
327 
CreateTexture2D()328 Texture2D * OpenGLRenderer::CreateTexture2D() {
329 	GLTexture2D * texture = new GLTexture2D(this);
330 	textures.push_back(*texture);
331 	return texture;
332 }
333 
setGLState(GLenum state,bool enable)334 static inline void setGLState(GLenum state, bool enable) {
335 	if(enable) {
336 		glEnable(state);
337 	} else {
338 		glDisable(state);
339 	}
340 }
341 
SetRenderState(RenderState renderState,bool enable)342 void OpenGLRenderer::SetRenderState(RenderState renderState, bool enable) {
343 
344 	switch(renderState) {
345 
346 		case AlphaBlending: {
347 			setGLState(GL_BLEND, enable);
348 			break;
349 		}
350 
351 		case AlphaTest: {
352 			setGLState(GL_ALPHA_TEST, enable);
353 			break;
354 		}
355 
356 		case ColorKey: {
357 			SetRenderState(AlphaTest, enable);
358 			if(enable)
359 				SetAlphaFunc(CmpNotEqual, 0.0f);
360 			break;
361 		}
362 
363 		case DepthTest: {
364 			setGLState(GL_DEPTH_TEST, enable);
365 			break;
366 		}
367 
368 		case DepthWrite: {
369 			glDepthMask(enable ? GL_TRUE : GL_FALSE);
370 			break;
371 		}
372 
373 		case Fog: {
374 			setGLState(GL_FOG, enable);
375 			break;
376 		}
377 
378 		case Lighting: {
379 			setGLState(GL_LIGHTING, enable);
380 			break;
381 		}
382 
383 		case ZBias: {
384 			setGLState(GL_POLYGON_OFFSET_FILL, enable);
385 			break;
386 		}
387 
388 		default:
389 			LogWarning << "Unsupported render state: " << renderState;
390 	}
391 
392 	CHECK_GL;
393 }
394 
395 static const GLenum arxToGlPixelCompareFunc[] = {
396 	GL_NEVER, // CmpNever,
397 	GL_LESS, // CmpLess,
398 	GL_EQUAL, // CmpEqual,
399 	GL_LEQUAL, // CmpLessEqual,
400 	GL_GREATER, // CmpGreater,
401 	GL_NOTEQUAL, // CmpNotEqual,
402 	GL_GEQUAL, // CmpGreaterEqual,
403 	GL_ALWAYS // CmpAlways
404 };
405 
SetAlphaFunc(PixelCompareFunc func,float ref)406 void OpenGLRenderer::SetAlphaFunc(PixelCompareFunc func, float ref) {
407 	glAlphaFunc(arxToGlPixelCompareFunc[func], ref);
408 	CHECK_GL;
409 }
410 
411 static const GLenum arxToGlBlendFactor[] = {
412 	GL_ZERO, // BlendZero,
413 	GL_ONE, // BlendOne,
414 	GL_SRC_COLOR, // BlendSrcColor,
415 	GL_SRC_ALPHA, // BlendSrcAlpha,
416 	GL_ONE_MINUS_SRC_COLOR, // BlendInvSrcColor,
417 	GL_ONE_MINUS_SRC_ALPHA, // BlendInvSrcAlpha,
418 	GL_SRC_ALPHA_SATURATE, // BlendSrcAlphaSaturate,
419 	GL_DST_COLOR, // BlendDstColor,
420 	GL_DST_ALPHA, // BlendDstAlpha,
421 	GL_ONE_MINUS_DST_COLOR, // BlendInvDstColor,
422 	GL_ONE_MINUS_DST_ALPHA // BlendInvDstAlpha
423 };
424 
SetBlendFunc(PixelBlendingFactor srcFactor,PixelBlendingFactor dstFactor)425 void OpenGLRenderer::SetBlendFunc(PixelBlendingFactor srcFactor, PixelBlendingFactor dstFactor) {
426 	glBlendFunc(arxToGlBlendFactor[srcFactor], arxToGlBlendFactor[dstFactor]);
427 	CHECK_GL;
428 }
429 
SetViewport(const Rect & _viewport)430 void OpenGLRenderer::SetViewport(const Rect & _viewport) {
431 
432 	viewport = _viewport;
433 
434 	// TODO maybe it's better to always have the viewport cover the whole window and use glScissor instead?
435 
436 	int height = mainApp->GetWindow()->getSize().y;
437 
438 	glViewport(viewport.left, height - viewport.bottom, viewport.width(), viewport.height());
439 
440 	if(currentTransform == GL_NoTransform) {
441 		currentTransform = GL_UnsetTransform;
442 	}
443 
444 	CHECK_GL;
445 }
446 
GetViewport()447 Rect OpenGLRenderer::GetViewport() {
448 	return viewport;
449 }
450 
Begin2DProjection(float left,float right,float bottom,float top,float zNear,float zFar)451 void OpenGLRenderer::Begin2DProjection(float left, float right, float bottom, float top, float zNear, float zFar) {
452 	ARX_UNUSED(left), ARX_UNUSED(right), ARX_UNUSED(bottom), ARX_UNUSED(top), ARX_UNUSED(zNear), ARX_UNUSED(zFar);
453 	// Do nothing!
454 }
455 
End2DProjection()456 void OpenGLRenderer::End2DProjection() {
457 	// Do nothing!
458 }
459 
Clear(BufferFlags bufferFlags,Color clearColor,float clearDepth,size_t nrects,Rect * rect)460 void OpenGLRenderer::Clear(BufferFlags bufferFlags, Color clearColor, float clearDepth, size_t nrects, Rect * rect) {
461 
462 	GLbitfield buffers = 0;
463 
464 	if(bufferFlags & ColorBuffer) {
465 		Color4f col = clearColor.to<float>();
466 		glClearColor(col.r, col.g, col.b, col.a);
467 		buffers |= GL_COLOR_BUFFER_BIT;
468 	}
469 
470 	if(bufferFlags & DepthBuffer) {
471 		glClearDepth(clearDepth);
472 		buffers |= GL_DEPTH_BUFFER_BIT;
473 	}
474 
475 	if(bufferFlags & StencilBuffer) {
476 		buffers |= GL_STENCIL_BUFFER_BIT;
477 	}
478 
479 	if(nrects) {
480 
481 		glEnable(GL_SCISSOR_TEST);
482 
483 		int height = mainApp->GetWindow()->getSize().y;
484 
485 		for(size_t i = 0; i < nrects; i++) {
486 			glScissor(rect[i].left, height - rect[i].bottom, rect[i].width(), rect[i].height());
487 			glClear(buffers);
488 		}
489 
490 		glDisable(GL_SCISSOR_TEST);
491 
492 	} else {
493 
494 		glClear(buffers);
495 
496 	}
497 
498 	CHECK_GL;
499 }
500 
SetFogColor(Color color)501 void OpenGLRenderer::SetFogColor(Color color) {
502 	Color4f colorf = color.to<float>();
503 	GLfloat fogColor[4]= {colorf.r, colorf.g, colorf.b, colorf.a};
504 	glFogfv(GL_FOG_COLOR, fogColor);
505 	CHECK_GL;
506 }
507 
508 static const GLint arxToGlFogMode[] = {
509 	-1, // FogNone, TODO(unused) why is there a FogNone if there is also a separate Fog render state?
510 	GL_EXP, // FogExp,
511 	GL_EXP2, // FogExp2,
512 	GL_LINEAR, // FogLinear
513 };
514 
515 
SetFogParams(FogMode fogMode,float fogStart,float fogEnd,float fogDensity)516 void OpenGLRenderer::SetFogParams(FogMode fogMode, float fogStart, float fogEnd, float fogDensity) {
517 
518 	glFogi(GL_FOG_MODE, arxToGlFogMode[fogMode]);
519 
520 	glFogf(GL_FOG_START, fogStart);
521 	glFogf(GL_FOG_END, fogEnd);
522 	glFogf(GL_FOG_DENSITY, fogDensity);
523 
524 	CHECK_GL;
525 }
526 
SetAntialiasing(bool enable)527 void OpenGLRenderer::SetAntialiasing(bool enable) {
528 
529 	// This is mostly useless as multisampling must be enabled/disabled at GL context creation.
530 	setGLState(GL_MULTISAMPLE, enable);
531 
532 	CHECK_GL;
533 }
534 
535 static const GLenum arxToGlCullMode[] = {
536 	(GLenum)-1, // CullNone,
537 	GL_BACK, // CullCW,
538 	GL_FRONT, // CullCCW,
539 };
540 
SetCulling(CullingMode mode)541 void OpenGLRenderer::SetCulling(CullingMode mode) {
542 	if(mode == CullNone) {
543 		glDisable(GL_CULL_FACE);
544 	} else {
545 		glEnable(GL_CULL_FACE);
546 		glCullFace(arxToGlCullMode[mode]);
547 	}
548 	CHECK_GL;
549 }
550 
SetDepthBias(int depthBias)551 void OpenGLRenderer::SetDepthBias(int depthBias) {
552 
553 	float bias = -(float)depthBias;
554 
555 	glPolygonOffset(bias, bias);
556 
557 	CHECK_GL;
558 }
559 
560 static const GLenum arxToGlFillMode[] = {
561 	GL_POINT, // FillPoint,
562 	GL_LINE,  // FillWireframe,
563 	GL_FILL,  // FillSolid
564 };
565 
SetFillMode(FillMode mode)566 void OpenGLRenderer::SetFillMode(FillMode mode) {
567 	glPolygonMode(GL_FRONT_AND_BACK, arxToGlFillMode[mode]);
568 	CHECK_GL;
569 }
570 
DrawTexturedRect(float x,float y,float w,float h,float uStart,float vStart,float uEnd,float vEnd,Color color)571 void OpenGLRenderer::DrawTexturedRect(float x, float y, float w, float h, float uStart, float vStart, float uEnd, float vEnd, Color color) {
572 
573 	applyTextureStages();
574 	disableTransform();
575 
576 	x -= .5f;
577 	y -= .5f;
578 
579 	glColor3ub(color.r, color.g, color.b);
580 
581 	glBegin(GL_QUADS);
582 
583 		glMultiTexCoord2f(GL_TEXTURE0, uStart, vStart);
584 		glVertex3f(x, y, 0);
585 
586 		glMultiTexCoord2f(GL_TEXTURE0, uEnd, vStart);
587 		glVertex3f(x + w, y, 0);
588 
589 		glMultiTexCoord2f(GL_TEXTURE0, uEnd, vEnd);
590 		glVertex3f(x + w, y + h, 0);
591 
592 		glMultiTexCoord2f(GL_TEXTURE0, uStart, vEnd);
593 		glVertex3f(x, y + h, 0);
594 
595 	glEnd();
596 
597 	CHECK_GL;
598 }
599 
createVertexBufferTL(size_t capacity,BufferUsage usage)600 VertexBuffer<TexturedVertex> * OpenGLRenderer::createVertexBufferTL(size_t capacity, BufferUsage usage) {
601 	if(useVBOs && shader) {
602 		return new GLVertexBuffer<TexturedVertex>(this, capacity, usage);
603 	} else {
604 		return new GLNoVertexBuffer<TexturedVertex>(this, capacity);
605 	}
606 }
607 
createVertexBuffer(size_t capacity,BufferUsage usage)608 VertexBuffer<SMY_VERTEX> * OpenGLRenderer::createVertexBuffer(size_t capacity, BufferUsage usage) {
609 	if(useVBOs) {
610 		return new GLVertexBuffer<SMY_VERTEX>(this, capacity, usage);
611 	} else {
612 		return new GLNoVertexBuffer<SMY_VERTEX>(this, capacity);
613 	}
614 }
615 
createVertexBuffer3(size_t capacity,BufferUsage usage)616 VertexBuffer<SMY_VERTEX3> * OpenGLRenderer::createVertexBuffer3(size_t capacity, BufferUsage usage) {
617 	if(useVBOs) {
618 		return new GLVertexBuffer<SMY_VERTEX3>(this, capacity, usage);
619 	} else {
620 		return new GLNoVertexBuffer<SMY_VERTEX3>(this, capacity);
621 	}
622 }
623 
624 const GLenum arxToGlPrimitiveType[] = {
625 	GL_TRIANGLES, // TriangleList,
626 	GL_TRIANGLE_STRIP, // TriangleStrip,
627 	GL_TRIANGLE_FAN, // TriangleFan,
628 	GL_LINES, // LineList,
629 	GL_LINE_STRIP // LineStrip
630 };
631 
drawIndexed(Primitive primitive,const TexturedVertex * vertices,size_t nvertices,unsigned short * indices,size_t nindices)632 void OpenGLRenderer::drawIndexed(Primitive primitive, const TexturedVertex * vertices, size_t nvertices, unsigned short * indices, size_t nindices) {
633 
634 	beforeDraw<TexturedVertex>();
635 
636 	if(useVertexArrays && shader) {
637 
638 		glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
639 
640 		setVertexArray(vertices, vertices);
641 
642 		glDrawRangeElements(arxToGlPrimitiveType[primitive], 0, nvertices - 1, nindices, GL_UNSIGNED_SHORT, indices);
643 
644 	} else {
645 
646 		glBegin(arxToGlPrimitiveType[primitive]);
647 
648 		for(size_t i = 0; i < nindices; i++) {
649 			renderVertex(vertices[indices[i]]);
650 		}
651 
652 		glEnd();
653 
654 	}
655 
656 	CHECK_GL;
657 }
658 
getSnapshot(Image & image)659 bool OpenGLRenderer::getSnapshot(Image & image) {
660 
661 	Vec2i size = mainApp->GetWindow()->getSize();
662 
663 	image.Create(size.x, size.y, Image::Format_R8G8B8);
664 
665 	glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, image.GetData());
666 
667 	image.FlipY();
668 
669 	CHECK_GL;
670 
671 	return true;
672 }
673 
getSnapshot(Image & image,size_t width,size_t height)674 bool OpenGLRenderer::getSnapshot(Image & image, size_t width, size_t height) {
675 
676 	// TODO handle scaling on the GPU so we don't need to download the whole image
677 
678 	// duplication to ensure use of Image::Format_R8G8B8
679 	Image fullsize;
680 	Vec2i size = mainApp->GetWindow()->getSize();
681 	fullsize.Create(size.x, size.y, Image::Format_R8G8B8);
682 	glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, fullsize.GetData());
683 
684 	image.ResizeFrom(fullsize, width, height, true);
685 
686 	return true;
687 }
688 
applyTextureStages()689 void OpenGLRenderer::applyTextureStages() {
690 	for(size_t i = 0; i <= maxTextureStage; i++) {
691 		GetTextureStage(i)->apply();
692 	}
693 }
694 
isFogInEyeCoordinates()695 bool OpenGLRenderer::isFogInEyeCoordinates() {
696 	return true;
697 }
698 
getGLErrorString(GLenum error)699 const char * getGLErrorString(GLenum error) {
700 	switch(error) {
701 		case GL_NO_ERROR: return "GL_NO_ERROR";
702 		case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
703 		case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
704 		case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
705 		case GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW";
706 		case GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW";
707 		case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
708 		default: return "(unknown error)";
709 	}
710 }
711