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