2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
24 /*---------------------------------------------------------------------------*/
26 #define IMG_DEFAULT "ball/default.png"
27 #define IMG_ARBBALL "ball/arbball.png"
29 static int has_solid = 0;
30 static int has_inner = 0;
31 static int has_outer = 0;
33 static struct s_file solid;
34 static struct s_file inner;
35 static struct s_file outer;
41 #define F_DEPTHTEST 16
43 static int solid_flags;
44 static int inner_flags;
45 static int outer_flags;
47 static float solid_alpha;
48 static float inner_alpha;
49 static float outer_alpha;
51 static GLuint oldball_list;
52 static GLuint oldball_text;
53 static GLuint arbball_list;
54 static GLuint arbball_text;
56 /*---------------------------------------------------------------------------*/
58 /* These are the faces of an octahedron in positive longitude/latitude. */
60 static float oldball_octahedron[8][3][2] = {
61 {{ 0.0f, 90.0f }, { 0.0f, 0.0f }, { 90.0f, 0.0f }},
62 {{ 90.0f, 90.0f }, { 90.0f, 0.0f }, { 180.0f, 0.0f }},
63 {{ 180.0f, 90.0f }, { 180.0f, 0.0f }, { 270.0f, 0.0f }},
64 {{ 270.0f, 90.0f }, { 270.0f, 0.0f }, { 360.0f, 0.0f }},
65 {{ 0.0f, -90.0f }, { 90.0f, 0.0f }, { 0.0f, 0.0f }},
66 {{ 90.0f, -90.0f }, { 180.0f, 0.0f }, { 90.0f, 0.0f }},
67 {{ 180.0f, -90.0f }, { 270.0f, 0.0f }, { 180.0f, 0.0f }},
68 {{ 270.0f, -90.0f }, { 360.0f, 0.0f }, { 270.0f, 0.0f }},
71 static void oldball_midpoint(float *P, const float *A, const float *B)
75 /* The haversine midpoint method. */
77 D[0] = fcosf(B[1]) * fcosf(B[0] - A[0]);
78 D[1] = fcosf(B[1]) * fsinf(B[0] - A[0]);
80 P[0] = A[0] + fatan2f(D[1], fcosf(A[1]) + D[0]);
82 P[1] = fatan2f(fsinf(A[1]) +
84 fsqrtf((fcosf(A[1]) + D[0]) *
85 (fcosf(A[1]) + D[0]) + D[1] * D[1]));
88 static void oldball_vertex(const float *p)
90 /* Draw a vertex with normal and texture coordinate at the given lon/lat. */
92 const float x = fsinf(p[0]) * fcosf(p[1]);
93 const float y = fsinf(p[1]);
94 const float z = fcosf(p[0]) * fcosf(p[1]);
96 glTexCoord2f((+p[0] ) / V_RAD(360.0f),
97 (-p[1] + V_RAD(90.0f)) / V_RAD(180.0f));
103 static void oldball_subdiv(const float *a,
105 const float *c, int D)
109 /* Recursively subdivide the given triangle. */
115 oldball_midpoint(d, a, b);
116 oldball_midpoint(e, b, c);
117 oldball_midpoint(f, c, a);
119 oldball_subdiv(a, d, f, D - 1);
120 oldball_subdiv(d, b, e, D - 1);
121 oldball_subdiv(f, e, c, D - 1);
122 oldball_subdiv(d, e, f, D - 1);
126 /* Draw the given triangle. */
134 void oldball_init(int b)
138 strncpy(name, IMG_DEFAULT, MAXSTR - 12);
140 if ((oldball_text = make_image_from_file(name)))
142 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
143 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
146 oldball_list = glGenLists(1);
148 glNewList(oldball_list, GL_COMPILE);
151 int i, d = b ? 4 : 3;
153 glBegin(GL_TRIANGLES);
155 for (i = 0; i < 8; ++i)
161 a[0] = V_RAD(oldball_octahedron[i][0][0]);
162 a[1] = V_RAD(oldball_octahedron[i][0][1]);
164 b[0] = V_RAD(oldball_octahedron[i][1][0]);
165 b[1] = V_RAD(oldball_octahedron[i][1][1]);
167 c[0] = V_RAD(oldball_octahedron[i][2][0]);
168 c[1] = V_RAD(oldball_octahedron[i][2][1]);
170 oldball_subdiv(a, b, c, d);
175 int i, slices = b ? 32 : 16;
176 int j, stacks = b ? 16 : 8;
178 for (i = 0; i < stacks; i++)
180 float k0 = (float) i / stacks;
181 float k1 = (float) (i + 1) / stacks;
183 float s0 = fsinf(V_PI * (k0 - 0.5));
184 float c0 = fcosf(V_PI * (k0 - 0.5));
185 float s1 = fsinf(V_PI * (k1 - 0.5));
186 float c1 = fcosf(V_PI * (k1 - 0.5));
188 glBegin(GL_QUAD_STRIP);
190 for (j = 0; j <= slices; j++)
192 float k = (float) j / slices;
193 float s = fsinf(V_PI * k * 2.0);
194 float c = fcosf(V_PI * k * 2.0);
197 glNormal3f(s * c0, c * c0, s0);
198 glVertex3f(s * c0, c * c0, s0);
201 glNormal3f(s * c1, c * c1, s1);
202 glVertex3f(s * c1, c * c1, s1);
211 strncpy(name, IMG_ARBBALL, MAXSTR - 12);
213 if ((arbball_text = make_image_from_file(name)))
215 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
216 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
219 arbball_list = glGenLists(1);
221 glNewList(arbball_list, GL_COMPILE);
224 int i, d = b ? 4 : 3;
226 glBegin(GL_TRIANGLES);
228 for (i = 0; i < 8; ++i)
234 a[0] = V_RAD(oldball_octahedron[i][0][0]);
235 a[1] = V_RAD(oldball_octahedron[i][0][1]);
237 b[0] = V_RAD(oldball_octahedron[i][1][0]);
238 b[1] = V_RAD(oldball_octahedron[i][1][1]);
240 c[0] = V_RAD(oldball_octahedron[i][2][0]);
241 c[1] = V_RAD(oldball_octahedron[i][2][1]);
243 oldball_subdiv(a, b, c, d);
248 int i, slices = b ? 32 : 16;
249 int j, stacks = b ? 16 : 8;
251 for (i = 0; i < stacks; i++)
253 float k0 = (float) i / stacks;
254 float k1 = (float) (i + 1) / stacks;
256 float s0 = fsinf(V_PI * (k0 - 0.5));
257 float c0 = fcosf(V_PI * (k0 - 0.5));
258 float s1 = fsinf(V_PI * (k1 - 0.5));
259 float c1 = fcosf(V_PI * (k1 - 0.5));
261 glBegin(GL_QUAD_STRIP);
263 for (j = 0; j <= slices; j++)
265 float k = (float) j / slices;
266 float s = fsinf(V_PI * k * 2.0);
267 float c = fcosf(V_PI * k * 2.0);
270 glNormal3f(s * c0, c * c0, s0);
271 glVertex3f(s * c0, c * c0, s0);
274 glNormal3f(s * c1, c * c1, s1);
275 glVertex3f(s * c1, c * c1, s1);
285 void oldball_free(void)
287 if (glIsList(oldball_list))
288 glDeleteLists(oldball_list, 1);
290 if (glIsTexture(oldball_text))
291 glDeleteTextures(1, &oldball_text);
293 if (glIsList(arbball_list))
294 glDeleteLists(arbball_list, 1);
296 if (glIsTexture(arbball_text))
297 glDeleteTextures(1, &arbball_text);
306 void oldball_draw(int arb)
308 static const float a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
309 static const float s[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
310 static const float e[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
311 static const float h[1] = { 20.0f };
313 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, a);
314 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, s);
315 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, e);
316 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
318 glEnable(GL_COLOR_MATERIAL);
320 glBindTexture(GL_TEXTURE_2D, (arb) ? (arbball_text) : (oldball_text));
322 /* Render the ball back to front in case it is translucent. */
324 glDepthMask(GL_FALSE);
326 glCullFace(GL_FRONT);
327 glCallList((arb) ? (arbball_list) : (oldball_list));
329 glCallList(oldball_list);
331 glDepthMask(GL_TRUE);
333 /* Render the ball into the depth buffer. */
335 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
337 glCallList((arb) ? (arbball_list) : (oldball_list));
339 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
341 /* Ensure the ball is visible even when obscured by geometry. */
343 glDisable(GL_DEPTH_TEST);
345 glColor4f(1.0f, 1.0f, 1.0f, 0.1f);
346 glCallList((arb) ? (arbball_list) : (oldball_list));
348 glEnable(GL_DEPTH_TEST);
350 glDisable(GL_COLOR_MATERIAL);
353 /*---------------------------------------------------------------------------*/
355 #define SET(B, v, b) ((v) ? ((B) | (b)) : ((B) & ~(b)))
357 static int ball_opts(const struct s_file *fp, float *alpha)
359 int flags = F_DEPTHTEST;
362 for (di = 0; di < fp->dc; ++di)
364 char *k = fp->av + fp->dv[di].ai;
365 char *v = fp->av + fp->dv[di].aj;
367 if (strcmp(k, "pendulum") == 0)
368 flags = SET(flags, atoi(v), F_PENDULUM);
369 if (strcmp(k, "drawback") == 0)
370 flags = SET(flags, atoi(v), F_DRAWBACK);
371 if (strcmp(k, "drawclip") == 0)
372 flags = SET(flags, atoi(v), F_DRAWCLIP);
373 if (strcmp(k, "depthmask") == 0)
374 flags = SET(flags, atoi(v), F_DEPTHMASK);
375 if (strcmp(k, "depthtest") == 0)
376 flags = SET(flags, atoi(v), F_DEPTHTEST);
377 if (strcmp(k, "alphatest") == 0)
378 sscanf(v, "%f", alpha);
386 int T = config_get_d(CONFIG_TEXTURES);
388 char ball_file[PATHMAX];
389 char solid_file[PATHMAX];
390 char inner_file[PATHMAX];
391 char outer_file[PATHMAX];
393 config_get_s(CONFIG_BALL, ball_file, PATHMAX / 2 - 12);
395 strncpy(solid_file, "ball/", PATHMAX);
396 strncpy(inner_file, "ball/", PATHMAX);
397 strncpy(outer_file, "ball/", PATHMAX);
399 strcat(solid_file, ball_file);
400 strcat(inner_file, ball_file);
401 strcat(outer_file, ball_file);
403 strcat(solid_file, "/");
404 strcat(inner_file, "/");
405 strcat(outer_file, "/");
407 strcat(solid_file, ball_file);
408 strcat(inner_file, ball_file);
409 strcat(outer_file, ball_file);
411 strcat(solid_file, "-solid.sol");
412 strcat(inner_file, "-inner.sol");
413 strcat(outer_file, "-outer.sol");
423 if ((has_solid = sol_load_gl(&solid, config_data(solid_file), T, 0)))
424 solid_flags = ball_opts(&solid, &solid_alpha);
426 if ((has_inner = sol_load_gl(&inner, config_data(inner_file), T, 0)))
427 inner_flags = ball_opts(&inner, &inner_alpha);
429 if ((has_outer = sol_load_gl(&outer, config_data(outer_file), T, 0)))
430 outer_flags = ball_opts(&outer, &outer_alpha);
435 if (has_outer) sol_free_gl(&outer);
436 if (has_inner) sol_free_gl(&inner);
437 if (has_solid) sol_free_gl(&solid);
439 has_solid = has_inner = has_outer = 0;
442 /*---------------------------------------------------------------------------*/
444 static void ball_draw_solid(const float *ball_M,
445 const float *ball_bill_M, float t)
449 const int mask = (solid_flags & F_DEPTHMASK);
450 const int test = (solid_flags & F_DEPTHTEST);
452 if (solid_alpha < 1.0f)
454 glEnable(GL_ALPHA_TEST);
455 glAlphaFunc(GL_GEQUAL, solid_alpha);
460 /* Apply the ball rotation. */
462 glMultMatrixf(ball_M);
464 /* Draw the solid billboard geometry. */
468 if (test == 0) glDisable(GL_DEPTH_TEST);
469 if (mask == 0) glDepthMask(GL_FALSE);
470 glDisable(GL_LIGHTING);
472 sol_bill(&solid, ball_bill_M, t);
474 glEnable(GL_LIGHTING);
475 if (mask == 0) glDepthMask(GL_TRUE);
476 if (test == 0) glEnable(GL_DEPTH_TEST);
479 /* Draw the solid opaque and transparent geometry. */
481 sol_draw(&solid, mask, test);
485 if (solid_alpha < 1.0f)
486 glDisable(GL_ALPHA_TEST);
490 static void ball_draw_inner(const float *pend_M,
492 const float *pend_bill_M, float t)
496 const int pend = (inner_flags & F_PENDULUM);
497 const int mask = (inner_flags & F_DEPTHMASK);
498 const int test = (inner_flags & F_DEPTHTEST);
500 if (inner_alpha < 1.0f)
502 glEnable(GL_ALPHA_TEST);
503 glAlphaFunc(GL_GEQUAL, inner_alpha);
506 /* Apply the pendulum rotation. */
511 glMultMatrixf(pend_M);
514 /* Draw the inner opaque and transparent geometry. */
516 sol_draw(&inner, mask, test);
518 /* Draw the inner billboard geometry. */
522 if (test == 0) glDisable(GL_DEPTH_TEST);
523 if (mask == 0) glDepthMask(GL_FALSE);
524 glDisable(GL_LIGHTING);
527 sol_bill(&inner, pend_bill_M, t);
529 sol_bill(&inner, bill_M, t);
532 glEnable(GL_LIGHTING);
533 if (mask == 0) glDepthMask(GL_TRUE);
534 if (test == 0) glEnable(GL_DEPTH_TEST);
540 if (inner_alpha < 1.0f)
541 glDisable(GL_ALPHA_TEST);
545 static void ball_draw_outer(const float *pend_M,
547 const float *pend_bill_M, float t)
551 const int pend = (outer_flags & F_PENDULUM);
552 const int mask = (outer_flags & F_DEPTHMASK);
553 const int test = (outer_flags & F_DEPTHTEST);
555 if (outer_alpha < 1.0f)
557 glEnable(GL_ALPHA_TEST);
558 glAlphaFunc(GL_GEQUAL, outer_alpha);
561 /* Apply the pendulum rotation. */
566 glMultMatrixf(pend_M);
569 /* Draw the outer opaque and transparent geometry. */
571 sol_draw(&outer, mask, test);
573 /* Draw the outer billboard geometry. */
577 if (test == 0) glDisable(GL_DEPTH_TEST);
578 if (mask == 0) glDepthMask(GL_FALSE);
579 glDisable(GL_LIGHTING);
582 sol_bill(&outer, pend_bill_M, t);
584 sol_bill(&outer, bill_M, t);
586 glEnable(GL_LIGHTING);
587 if (mask == 0) glDepthMask(GL_TRUE);
588 if (test == 0) glEnable(GL_DEPTH_TEST);
594 if (outer_alpha < 1.0f)
595 glDisable(GL_ALPHA_TEST);
599 /*---------------------------------------------------------------------------*/
601 static void ball_pass_inner(const float *ball_M,
604 const float *ball_bill_M,
605 const float *pend_bill_M, float t)
607 /* Sort the inner ball using clip planes. */
609 if (inner_flags & F_DRAWCLIP)
611 glEnable(GL_CLIP_PLANE1);
612 ball_draw_inner( pend_M, bill_M, pend_bill_M, t);
613 glDisable(GL_CLIP_PLANE1);
615 glEnable(GL_CLIP_PLANE2);
616 ball_draw_inner( pend_M, bill_M, pend_bill_M, t);
617 glDisable(GL_CLIP_PLANE2);
620 /* Sort the inner ball using face culling. */
622 else if (inner_flags & F_DRAWBACK)
624 glCullFace(GL_FRONT);
625 ball_draw_inner( pend_M, bill_M, pend_bill_M, t);
627 ball_draw_inner( pend_M, bill_M, pend_bill_M, t);
630 /* Draw the inner ball normally. */
634 ball_draw_inner( pend_M, bill_M, pend_bill_M, t);
638 static void ball_pass_solid(const float *ball_M,
641 const float *ball_bill_M,
642 const float *pend_bill_M, float t)
644 /* Sort the solid ball with the inner ball using clip planes. */
646 if (solid_flags & F_DRAWCLIP)
648 glEnable(GL_CLIP_PLANE1);
649 ball_draw_solid(ball_M, ball_bill_M, t);
650 glDisable(GL_CLIP_PLANE1);
652 ball_pass_inner(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
654 glEnable(GL_CLIP_PLANE2);
655 ball_draw_solid(ball_M, ball_bill_M, t);
656 glDisable(GL_CLIP_PLANE2);
659 /* Sort the solid ball with the inner ball using face culling. */
661 else if (solid_flags & F_DRAWBACK)
663 glCullFace(GL_FRONT);
664 ball_draw_solid(ball_M, ball_bill_M, t);
667 ball_pass_inner(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
668 ball_draw_solid(ball_M, ball_bill_M, t);
671 /* Draw the solid ball after the inner ball. */
675 ball_pass_inner(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
676 ball_draw_solid(ball_M, ball_bill_M, t);
680 static void ball_pass_outer(const float *ball_M,
683 const float *ball_bill_M,
684 const float *pend_bill_M, float t)
686 /* Sort the outer ball with the solid ball using clip planes. */
688 if (outer_flags & F_DRAWCLIP)
690 glEnable(GL_CLIP_PLANE1);
691 ball_draw_outer( pend_M, bill_M, pend_bill_M, t);
692 glDisable(GL_CLIP_PLANE1);
694 ball_pass_solid(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
696 glEnable(GL_CLIP_PLANE2);
697 ball_draw_outer( pend_M, bill_M, pend_bill_M, t);
698 glDisable(GL_CLIP_PLANE2);
701 /* Sort the outer ball with the solid ball using face culling. */
703 else if (outer_flags & F_DRAWBACK)
705 glCullFace(GL_FRONT);
706 ball_draw_outer( pend_M, bill_M, pend_bill_M, t);
709 ball_pass_solid(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
710 ball_draw_outer( pend_M, bill_M, pend_bill_M, t);
713 /* Draw the outer ball after the solid ball. */
717 ball_pass_solid(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
718 ball_draw_outer( pend_M, bill_M, pend_bill_M, t);
722 /*---------------------------------------------------------------------------*/
724 void ball_draw(const float *ball_M,
726 const float *bill_M, float t)
728 /* Compute transforms for ball and pendulum billboards. */
730 float ball_T[16], ball_bill_M[16];
731 float pend_T[16], pend_bill_M[16];
733 m_xps(ball_T, ball_M);
734 m_xps(pend_T, pend_M);
735 m_xps(pend_T, pend_M);
737 m_mult(ball_bill_M, ball_T, bill_M);
738 m_mult(pend_bill_M, pend_T, bill_M);
740 /* Go to GREAT pains to ensure all layers are drawn back-to-front. */
742 ball_pass_outer(ball_M, pend_M, bill_M, ball_bill_M, pend_bill_M, t);
745 /*---------------------------------------------------------------------------*/