Line data Source code
1 0 : /*
2 :
3 : OOOpenGL.m
4 :
5 : Oolite
6 : Copyright (C) 2004-2013 Giles C Williams and contributors
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License
10 : as published by the Free Software Foundation; either version 2
11 : of the License, or (at your option) any later version.
12 :
13 : This program 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 this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 : MA 02110-1301, USA.
22 :
23 : */
24 :
25 : #import "OOOpenGL.h"
26 : #import "OOLogging.h"
27 : #import "OOMaths.h"
28 : #import "OOMacroOpenGL.h"
29 : #import "OOFunctionAttributes.h"
30 : #import "OOOpenGLExtensionManager.h"
31 :
32 : #ifndef NDEBUG
33 0 : static NSString * const kOOLogOpenGLStateDump = @"rendering.opengl.stateDump";
34 : #endif
35 :
36 0 : static GLfloat sDisplayScaleFactor = 1.0f;
37 :
38 :
39 0 : BOOL OOCheckOpenGLErrors(NSString *format, ...)
40 : {
41 : GLenum errCode;
42 : const GLubyte *errString = NULL;
43 : BOOL errorOccurred = NO;
44 : va_list args;
45 : static BOOL noReenter;
46 :
47 : if (noReenter) return NO;
48 : noReenter = YES;
49 :
50 : OO_ENTER_OPENGL();
51 :
52 : // Short-circut here, because glGetError() is quite expensive.
53 : if (OOLogWillDisplayMessagesInClass(kOOLogOpenGLError))
54 : {
55 : for (;;)
56 : {
57 : errCode = glGetError();
58 :
59 : if (errCode == GL_NO_ERROR) break;
60 :
61 : errorOccurred = YES;
62 : errString = gluErrorString(errCode);
63 : if (format == nil) format = @"<unknown>";
64 :
65 : va_start(args, format);
66 : format = [[[NSString alloc] initWithFormat:format arguments:args] autorelease];
67 : va_end(args);
68 : OOLog(kOOLogOpenGLError, @"OpenGL error: \"%s\" (%#x), context: %@", errString, errCode, format);
69 : }
70 : }
71 :
72 : #if OO_CHECK_GL_HEAVY
73 : if (errorOccurred)
74 : {
75 : OOLogIndent();
76 : LogOpenGLState();
77 : OOLogOutdent();
78 : while (glGetError() != 0) {} // Suppress any errors caused by LogOpenGLState().
79 : }
80 : #endif
81 :
82 : noReenter = NO;
83 : return errorOccurred;
84 : }
85 :
86 :
87 0 : void OOGLWireframeModeOn(void)
88 : {
89 : OO_ENTER_OPENGL();
90 :
91 : /* Because of where this is called relative to the
92 : * OpenGLStateManager methods, pushAttrib/popAttrib causes the
93 : * OpenGL state to move away from where it should be. So, instead,
94 : * just switch polygon mode as needed. Linewidth is set already by
95 : * everything that needs it and not handled by the state
96 : * manager. - CIM*/
97 : // OOGL(glPushAttrib(GL_POLYGON_BIT | GL_LINE_BIT | GL_TEXTURE_BIT));
98 : OOGL(GLScaledLineWidth(1.0f));
99 : OOGL(glPolygonMode(GL_FRONT_AND_BACK, GL_LINE));
100 : }
101 :
102 :
103 0 : void OOGLWireframeModeOff(void)
104 : {
105 : OO_ENTER_OPENGL();
106 : OOGL(glPolygonMode(GL_FRONT_AND_BACK, GL_FILL));
107 : // OOGL(glPopAttrib());
108 : }
109 :
110 :
111 0 : void GLDrawBallBillboard(GLfloat radius, GLfloat step, GLfloat z_distance)
112 : {
113 : if (EXPECT_NOT((radius <= 0)||(step < 1))) return;
114 : if (EXPECT_NOT(radius >= z_distance)) return; // inside the sphere
115 :
116 : GLfloat i, delta;
117 : GLfloat s, c;
118 : GLfloat r;
119 :
120 : OO_ENTER_OPENGL();
121 :
122 : r = radius * z_distance / sqrt(z_distance * z_distance - radius * radius);
123 : delta = step * M_PI / 180.0f; // Convert step from degrees to radians
124 :
125 : glVertex3i(0, 0, 0);
126 : for (i = 0; i < (M_PI * 2.0); i += delta)
127 : {
128 : s = r * sin(i);
129 : c = r * cos(i);
130 : glVertex3f(s, c, 0.0);
131 : }
132 : glVertex3f(0.0, r, 0.0); //repeat the zero value to close
133 : }
134 :
135 :
136 0 : static void GLDrawOvalPoints(GLfloat x, GLfloat y, GLfloat z, NSSize siz, GLfloat step)
137 : {
138 : GLfloat ww = 0.5 * siz.width;
139 : GLfloat hh = 0.5 * siz.height;
140 : GLfloat theta;
141 : GLfloat delta;
142 :
143 : OO_ENTER_OPENGL();
144 :
145 : delta = step * M_PI / 180.0f;
146 :
147 : for (theta = 0.0f; theta < (2.0f * M_PI); theta += delta)
148 : {
149 : glVertex3f(x + ww * sin(theta), y + hh * cos(theta), z);
150 : }
151 : glVertex3f(x, y + hh, z);
152 : }
153 :
154 :
155 0 : void GLDrawOval(GLfloat x, GLfloat y, GLfloat z, NSSize siz, GLfloat step)
156 : {
157 : OO_ENTER_OPENGL();
158 :
159 : OOGLBEGIN(GL_LINE_STRIP);
160 : GLDrawOvalPoints(x, y, z, siz, step);
161 : OOGLEND();
162 : }
163 :
164 :
165 0 : void GLDrawFilledOval(GLfloat x, GLfloat y, GLfloat z, NSSize siz, GLfloat step)
166 : {
167 : OO_ENTER_OPENGL();
168 :
169 : OOGLBEGIN(GL_TRIANGLE_FAN);
170 : GLDrawOvalPoints(x, y, z, siz, step);
171 : OOGLEND();
172 : }
173 :
174 0 : void GLDrawPoints(OOGLVector *points, int n)
175 : {
176 : int i;
177 : OO_ENTER_OPENGL();
178 : OOGLBEGIN(GL_LINE_STRIP);
179 : for (i = 0; i < n; i++)
180 : {
181 : glVertex3f(points->x, points->y, points->z);
182 : points++;
183 : }
184 : OOGLEND();
185 : return;
186 : }
187 :
188 0 : void GLDrawFilledPoints(OOGLVector *points, int n)
189 : {
190 : int i;
191 : OO_ENTER_OPENGL();
192 : OOGLBEGIN(GL_TRIANGLE_FAN);
193 : for (i = 0; i < n; i++)
194 : {
195 : glVertex3f(points->x, points->y, points->z);
196 : points++;
197 : }
198 : OOGLEND();
199 : return;
200 : }
201 :
202 :
203 0 : void GLDrawQuadStrip(OOGLVector *points, int n)
204 : {
205 : int i;
206 : OO_ENTER_OPENGL();
207 : OOGLBEGIN(GL_QUAD_STRIP);
208 : for (i = 0; i < n; i++)
209 : {
210 : glVertex3f(points->x, points->y, points->z );
211 : points++;
212 : }
213 : OOGLEND();
214 : return;
215 : }
216 :
217 :
218 0 : void GLScaledLineWidth(GLfloat width)
219 : {
220 : OO_ENTER_OPENGL();
221 : glLineWidth(width * sDisplayScaleFactor);
222 : }
223 :
224 :
225 0 : void GLScaledPointSize(GLfloat size)
226 : {
227 : OO_ENTER_OPENGL();
228 : glPointSize(size * sDisplayScaleFactor);
229 : }
230 :
231 :
232 0 : GLfloat GLGetDisplayScaleFactor(void)
233 : {
234 : return sDisplayScaleFactor;
235 : }
236 :
237 :
238 0 : void GLSetDisplayScaleFactor(GLfloat factor)
239 : {
240 : NSCParameterAssert(factor >= 0.0f && isfinite(factor));
241 : sDisplayScaleFactor = factor;
242 : }
243 :
244 :
245 : // MARK: LogOpenGLState() and helpers
246 :
247 : #ifndef NDEBUG
248 :
249 : static void GLDumpLightState(unsigned lightIdx);
250 : static void GLDumpMaterialState(void);
251 : static void GLDumpCullingState(void);
252 : static void GLDumpFogState(void);
253 : static void GLDumpStateFlags(void);
254 :
255 :
256 0 : void LogOpenGLState(void)
257 : {
258 : unsigned i;
259 :
260 : if (!OOLogWillDisplayMessagesInClass(kOOLogOpenGLStateDump)) return;
261 :
262 : OO_ENTER_OPENGL();
263 :
264 : OOLog(kOOLogOpenGLStateDump, @"%@", @"OpenGL state dump:");
265 : OOLogIndent();
266 :
267 : GLDumpMaterialState();
268 : GLDumpCullingState();
269 : if (glIsEnabled(GL_LIGHTING))
270 : {
271 : OOLog(kOOLogOpenGLStateDump, @"%@", @"Lighting: ENABLED");
272 : for (i = 0; i != 8; ++i)
273 : {
274 : GLDumpLightState(i);
275 : }
276 : }
277 : else
278 : {
279 : OOLog(kOOLogOpenGLStateDump, @"%@", @"Lighting: disabled");
280 : }
281 :
282 : GLDumpFogState();
283 : GLDumpStateFlags();
284 :
285 : OOCheckOpenGLErrors(@"After state dump");
286 :
287 : OOLogOutdent();
288 : }
289 :
290 :
291 0 : NSString *OOGLColorToString(GLfloat color[4])
292 : {
293 0 : #define COLOR_EQUAL(color, r, g, b, a) (color[0] == (r) && color[1] == (g) && color[2] == (b) && color[3] == (a))
294 0 : #define COLOR_CASE(r, g, b, a, str) do { if (COLOR_EQUAL(color, (r), (g), (b), (a))) return (str); } while (0)
295 :
296 : COLOR_CASE(1, 1, 1, 1, @"white");
297 : COLOR_CASE(0, 0, 0, 1, @"black");
298 : COLOR_CASE(0, 0, 0, 0, @"clear");
299 : COLOR_CASE(1, 0, 0, 1, @"red");
300 : COLOR_CASE(0, 1, 0, 1, @"green");
301 : COLOR_CASE(0, 0, 1, 1, @"blue");
302 : COLOR_CASE(0, 1, 1, 1, @"cyan");
303 : COLOR_CASE(1, 0, 1, 1, @"magenta");
304 : COLOR_CASE(1, 1, 0, 1, @"yellow");
305 :
306 : return [NSString stringWithFormat:@"(%.2ff, %.2ff, %.2ff, %.2ff)", color[0], color[1], color[2], color[3]];
307 : }
308 :
309 :
310 0 : static void GLDumpLightState(unsigned lightIdx)
311 : {
312 : BOOL enabled;
313 : GLenum lightID = GL_LIGHT0 + lightIdx;
314 : GLfloat color[4];
315 :
316 : OO_ENTER_OPENGL();
317 :
318 : OOGL(enabled = glIsEnabled(lightID));
319 : OOLog(kOOLogOpenGLStateDump, @"Light %u: %@", lightIdx, OOGLFlagToString(enabled));
320 :
321 : if (enabled)
322 : {
323 : OOLogIndent();
324 :
325 : OOGL(glGetLightfv(GL_LIGHT1, GL_AMBIENT, color));
326 : OOLog(kOOLogOpenGLStateDump, @"Ambient: %@", OOGLColorToString(color));
327 : OOGL(glGetLightfv(GL_LIGHT1, GL_DIFFUSE, color));
328 : OOLog(kOOLogOpenGLStateDump, @"Diffuse: %@", OOGLColorToString(color));
329 : OOGL(glGetLightfv(GL_LIGHT1, GL_SPECULAR, color));
330 : OOLog(kOOLogOpenGLStateDump, @"Specular: %@", OOGLColorToString(color));
331 :
332 : OOLogOutdent();
333 : }
334 : }
335 :
336 :
337 0 : static void GLDumpMaterialState(void)
338 : {
339 : GLfloat color[4];
340 : GLfloat shininess;
341 : GLint shadeModel,
342 : blendSrc,
343 : blendDst,
344 : texMode;
345 : BOOL blending;
346 :
347 : OO_ENTER_OPENGL();
348 :
349 : OOLog(kOOLogOpenGLStateDump, @"%@", @"Material state:");
350 : OOLogIndent();
351 :
352 : OOGL(glGetMaterialfv(GL_FRONT, GL_AMBIENT, color));
353 : OOLog(kOOLogOpenGLStateDump, @"Ambient: %@", OOGLColorToString(color));
354 :
355 : OOGL(glGetMaterialfv(GL_FRONT, GL_DIFFUSE, color));
356 : OOLog(kOOLogOpenGLStateDump, @"Diffuse: %@", OOGLColorToString(color));
357 :
358 : OOGL(glGetMaterialfv(GL_FRONT, GL_EMISSION, color));
359 : OOLog(kOOLogOpenGLStateDump, @"Emission: %@", OOGLColorToString(color));
360 :
361 : OOGL(glGetMaterialfv(GL_FRONT, GL_SPECULAR, color));
362 : OOLog(kOOLogOpenGLStateDump, @"Specular: %@", OOGLColorToString(color));
363 :
364 : OOGL(glGetMaterialfv(GL_FRONT, GL_SHININESS, &shininess));
365 : OOLog(kOOLogOpenGLStateDump, @"Shininess: %g", shininess);
366 :
367 : OOGL(OOLog(kOOLogOpenGLStateDump, @"Colour material: %@", OOGLFlagToString(glIsEnabled(GL_COLOR_MATERIAL))));
368 :
369 : OOGL(glGetFloatv(GL_CURRENT_COLOR, color));
370 : OOLog(kOOLogOpenGLStateDump, @"Current color: %@", OOGLColorToString(color));
371 :
372 : OOGL(glGetIntegerv(GL_SHADE_MODEL, &shadeModel));
373 : OOLog(kOOLogOpenGLStateDump, @"Shade model: %@", OOGLEnumToString(shadeModel));
374 :
375 : OOGL(blending = glIsEnabled(GL_BLEND));
376 : OOLog(kOOLogOpenGLStateDump, @"Blending: %@", OOGLFlagToString(blending));
377 : if (blending)
378 : {
379 : OOGL(glGetIntegerv(GL_BLEND_SRC, &blendSrc));
380 : OOGL(glGetIntegerv(GL_BLEND_DST, &blendDst));
381 : OOLog(kOOLogOpenGLStateDump, @"Blend function: %@, %@", OOGLEnumToString(blendSrc), OOGLEnumToString(blendDst));
382 : }
383 :
384 : OOGL(glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &texMode));
385 : OOLog(kOOLogOpenGLStateDump, @"Texture env mode: %@", OOGLEnumToString(texMode));
386 :
387 : #if OO_MULTITEXTURE
388 : if ([[OOOpenGLExtensionManager sharedManager] textureUnitCount] > 1)
389 : {
390 : GLint textureUnit;
391 : OOGL(glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &textureUnit));
392 : OOLog(kOOLogOpenGLStateDump, @"Active texture unit: %@", OOGLEnumToString(textureUnit));
393 :
394 : OOGL(glGetIntegerv(GL_CLIENT_ACTIVE_TEXTURE_ARB, &textureUnit));
395 : OOLog(kOOLogOpenGLStateDump, @"Active client texture unit: %@", OOGLEnumToString(textureUnit));
396 : }
397 : #endif
398 :
399 : OOLogOutdent();
400 : }
401 :
402 :
403 0 : static void GLDumpCullingState(void)
404 : {
405 : OO_ENTER_OPENGL();
406 :
407 : bool enabled;
408 : OOGL(enabled = glIsEnabled(GL_CULL_FACE));
409 : OOLog(kOOLogOpenGLStateDump, @"Face culling: %@", OOGLFlagToString(enabled));
410 : if (enabled)
411 : {
412 : GLint value;
413 :
414 : OOLogIndent();
415 :
416 : OOGL(glGetIntegerv(GL_CULL_FACE_MODE, &value));
417 : OOLog(kOOLogOpenGLStateDump, @"Cull face mode: %@", OOGLEnumToString(value));
418 :
419 : OOGL(glGetIntegerv(GL_FRONT_FACE, &value));
420 : OOLog(kOOLogOpenGLStateDump, @"Front face direction: %@", OOGLEnumToString(value));
421 :
422 : OOLogOutdent();
423 : }
424 : }
425 :
426 :
427 0 : static void GLDumpFogState(void)
428 : {
429 : BOOL enabled;
430 : GLint value;
431 : GLfloat start,
432 : end,
433 : density,
434 : index;
435 : GLfloat color[4];
436 :
437 : OO_ENTER_OPENGL();
438 :
439 : OOGL(enabled = glIsEnabled(GL_FOG));
440 : OOLog(kOOLogOpenGLStateDump, @"Fog: %@", OOGLFlagToString(enabled));
441 : if (enabled)
442 : {
443 : OOLogIndent();
444 :
445 : OOGL(glGetIntegerv(GL_FOG_MODE, &value));
446 : OOLog(kOOLogOpenGLStateDump, @"Fog mode: %@", OOGLEnumToString(value));
447 :
448 : OOGL(glGetFloatv(GL_FOG_COLOR, color));
449 : OOLog(kOOLogOpenGLStateDump, @"Fog colour: %@", OOGLColorToString(color));
450 :
451 : OOGL(glGetFloatv(GL_FOG_START, &start));
452 : OOGL(glGetFloatv(GL_FOG_END, &end));
453 : OOLog(kOOLogOpenGLStateDump, @"Fog start, end: %g, %g", start, end);
454 :
455 : OOGL(glGetFloatv(GL_FOG_DENSITY, &density));
456 : OOLog(kOOLogOpenGLStateDump, @"Fog density: %g", density);
457 :
458 : OOGL(glGetFloatv(GL_FOG_DENSITY, &index));
459 : OOLog(kOOLogOpenGLStateDump, @"Fog index: %g", index);
460 :
461 : OOLogOutdent();
462 : }
463 : }
464 :
465 :
466 0 : static void GLDumpStateFlags(void)
467 : {
468 : OO_ENTER_OPENGL();
469 :
470 0 : #define DUMP_STATE_FLAG(x) OOLog(kOOLogOpenGLStateDump, @ #x ": %@", OOGLFlagToString(glIsEnabled(x)))
471 0 : #define DUMP_GET_FLAG(x) do { GLboolean flag; glGetBooleanv(x, &flag); OOLog(kOOLogOpenGLStateDump, @ #x ": %@", OOGLFlagToString(flag)); } while (0)
472 :
473 : OOLog(kOOLogOpenGLStateDump, @"%@", @"Selected state flags:");
474 : OOLogIndent();
475 :
476 : DUMP_STATE_FLAG(GL_VERTEX_ARRAY);
477 : DUMP_STATE_FLAG(GL_NORMAL_ARRAY);
478 : DUMP_STATE_FLAG(GL_TEXTURE_COORD_ARRAY);
479 : DUMP_STATE_FLAG(GL_COLOR_ARRAY);
480 : DUMP_STATE_FLAG(GL_TEXTURE_2D);
481 : DUMP_STATE_FLAG(GL_DEPTH_TEST);
482 : DUMP_GET_FLAG(GL_DEPTH_WRITEMASK);
483 :
484 : OOLogOutdent();
485 :
486 : #undef DUMP_STATE_FLAG
487 : }
488 :
489 :
490 0 : #define CASE(x) case x: return @#x
491 :
492 0 : NSString *OOGLEnumToString(GLenum value)
493 : {
494 : switch (value)
495 : {
496 : // ShadingModel
497 : CASE(GL_FLAT);
498 : CASE(GL_SMOOTH);
499 :
500 : // BlendingFactorSrc/BlendingFactorDest
501 : CASE(GL_ZERO);
502 : CASE(GL_ONE);
503 : CASE(GL_DST_COLOR);
504 : CASE(GL_SRC_COLOR);
505 : CASE(GL_ONE_MINUS_DST_COLOR);
506 : CASE(GL_ONE_MINUS_SRC_COLOR);
507 : CASE(GL_SRC_ALPHA);
508 : CASE(GL_DST_ALPHA);
509 : CASE(GL_ONE_MINUS_SRC_ALPHA);
510 : CASE(GL_ONE_MINUS_DST_ALPHA);
511 : CASE(GL_SRC_ALPHA_SATURATE);
512 :
513 : // TextureEnvMode
514 : CASE(GL_MODULATE);
515 : CASE(GL_DECAL);
516 : CASE(GL_BLEND);
517 : CASE(GL_REPLACE);
518 :
519 : // FrontFaceDirection
520 : CASE(GL_CW);
521 : CASE(GL_CCW);
522 :
523 : // CullFaceMode
524 : CASE(GL_FRONT);
525 : CASE(GL_BACK);
526 : CASE(GL_FRONT_AND_BACK);
527 :
528 : // FogMode
529 : CASE(GL_LINEAR);
530 : CASE(GL_EXP);
531 : CASE(GL_EXP2);
532 :
533 : #if OO_MULTITEXTURE
534 : // Texture units
535 : #ifdef GL_TEXTURE0
536 : #define TEXCASE CASE
537 : #else
538 : #define TEXCASE(x) CASE(x##_ARB)
539 : #endif
540 : TEXCASE(GL_TEXTURE0);
541 : TEXCASE(GL_TEXTURE1);
542 : TEXCASE(GL_TEXTURE2);
543 : TEXCASE(GL_TEXTURE3);
544 : TEXCASE(GL_TEXTURE4);
545 : TEXCASE(GL_TEXTURE5);
546 : TEXCASE(GL_TEXTURE6);
547 : TEXCASE(GL_TEXTURE7);
548 : TEXCASE(GL_TEXTURE8);
549 : TEXCASE(GL_TEXTURE9);
550 : TEXCASE(GL_TEXTURE10);
551 : TEXCASE(GL_TEXTURE11);
552 : TEXCASE(GL_TEXTURE12);
553 : TEXCASE(GL_TEXTURE13);
554 : TEXCASE(GL_TEXTURE14);
555 : TEXCASE(GL_TEXTURE15);
556 : #endif
557 :
558 : default: return [NSString stringWithFormat:@"unknown: %u", value];
559 : }
560 : }
561 :
562 :
563 0 : NSString *OOGLFlagToString(bool value)
564 : {
565 : return value ? @"ENABLED" : @"disabled";
566 : }
567 :
568 : #else
569 :
570 : void LogOpenGLState(void)
571 : {
572 :
573 : }
574 :
575 : #endif
|