]> git.hungrycats.org Git - xscreensaver/blob - hacks/glx/romanboy.c
From https://www.jwz.org/xscreensaver/xscreensaver-6.09.tar.gz
[xscreensaver] / hacks / glx / romanboy.c
1 /* romanboy --- Shows a 3d immersion of the real projective plane
2    that rotates in 3d or on which you can walk and that can deform
3    smoothly between the Roman surface and the Boy surface. */
4
5 #if 0
6 static const char sccsid[] = "@(#)romanboy.c  1.1 14/10/03 xlockmore";
7 #endif
8
9 /* Copyright (c) 2014-2021 Carsten Steger <carsten@mirsanmir.org>. */
10
11 /*
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * REVISION HISTORY:
25  * C. Steger - 14/10/03: Initial version
26  * C. Steger - 20/01/06: Added the changing colors mode
27  * C. Steger - 20/12/19: Added per-fragment shading
28  * C. Steger - 20/12/30: Make the shader code work under macOS and iOS
29  */
30
31 /*
32  * This program shows a 3d immersion of the real projective plane that
33  * smoothly deforms between the Roman surface and the Boy surface.
34  * You can walk on the projective plane or turn in 3d.  The smooth
35  * deformation (homotopy) between these two famous immersions of the
36  * real projective plane was constructed by François Apéry.
37  *
38  * The real projective plane is a non-orientable surface.  To make
39  * this apparent, the two-sided color mode can be used.
40  * Alternatively, orientation markers (curling arrows) can be drawn as
41  * a texture map on the surface of the projective plane.  While
42  * walking on the projective plane, you will notice that the
43  * orientation of the curling arrows changes (which it must because
44  * the projective plane is non-orientable).
45  *
46  * The real projective plane is a model for the projective geometry in
47  * 2d space.  One point can be singled out as the origin.  A line can
48  * be singled out as the line at infinity, i.e., a line that lies at
49  * an infinite distance to the origin.  The line at infinity, like all
50  * lines in the projective plane, is topologically a circle.  Points
51  * on the line at infinity are also used to model directions in
52  * projective geometry.  The origin can be visualized in different
53  * manners.  When using distance colors (and using static colors), the
54  * origin is the point that is displayed as fully saturated red, which
55  * is easier to see as the center of the reddish area on the
56  * projective plane.  Alternatively, when using distance bands, the
57  * origin is the center of the only band that projects to a disk.
58  * When using direction bands, the origin is the point where all
59  * direction bands collapse to a point.  Finally, when orientation
60  * markers are being displayed, the origin the the point where all
61  * orientation markers are compressed to a point.  The line at
62  * infinity can also be visualized in different ways.  When using
63  * distance colors (and using static colors), the line at infinity is
64  * the line that is displayed as fully saturated magenta.  When
65  * two-sided (and static) colors are used, the line at infinity lies
66  * at the points where the red and green "sides" of the projective
67  * plane meet (of course, the real projective plane only has one side,
68  * so this is a design choice of the visualization).  Alternatively,
69  * when orientation markers are being displayed, the line at infinity
70  * is the place where the orientation markers change their
71  * orientation.
72  *
73  * Note that when the projective plane is displayed with bands, the
74  * orientation markers are placed in the middle of the bands.  For
75  * distance bands, the bands are chosen in such a way that the band at
76  * the origin is only half as wide as the remaining bands, which
77  * results in a disk being displayed at the origin that has the same
78  * diameter as the remaining bands.  This choice, however, also
79  * implies that the band at infinity is half as wide as the other
80  * bands.  Since the projective plane is attached to itself (in a
81  * complicated fashion) at the line at infinity, effectively the band
82  * at infinity is again as wide as the remaining bands.  However,
83  * since the orientation markers are displayed in the middle of the
84  * bands, this means that only one half of the orientation markers
85  * will be displayed twice at the line at infinity if distance bands
86  * are used.  If direction bands are used or if the projective plane
87  * is displayed as a solid surface, the orientation markers are
88  * displayed fully at the respective sides of the line at infinity.
89  *
90  * The immersed projective plane can be projected to the screen either
91  * perspectively or orthographically.  When using the walking modes,
92  * perspective projection to the screen will be used.
93  *
94  * There are three display modes for the projective plane: mesh
95  * (wireframe), solid, or transparent.  Furthermore, the appearance of
96  * the projective plane can be as a solid object or as a set of
97  * see-through bands.  The bands can be distance bands, i.e., bands
98  * that lie at increasing distances from the origin, or direction
99  * bands, i.e., bands that lie at increasing angles with respect to
100  * the origin.
101  *
102  * When the projective plane is displayed with direction bands, you
103  * will be able to see that each direction band (modulo the "pinching"
104  * at the origin) is a Moebius strip, which also shows that the
105  * projective plane is non-orientable.
106  *
107  * Finally, the colors with with the projective plane is drawn can be
108  * set to one-sided, two-sided, distance, or direction.  In one-sided
109  * mode, the projective plane is drawn with the same color on both
110  * "sides."  In two-sided mode (using static colors), the projective
111  * plane is drawn with red on one "side" and green on the "other
112  * side."  As described above, the projective plane only has one side,
113  * so the color jumps from red to green along the line at infinity.
114  * This mode enables you to see that the projective plane is
115  * non-orientable.  If changing colors are used in two-sided mode,
116  * changing complementary colors are used on the respective "sides."
117  * In distance mode, the projective plane is displayed with fully
118  * saturated colors that depend on the distance of the points on the
119  * projective plane to the origin.  If static colors are used, the
120  * origin is displayed in red, while the line at infinity is displayed
121  * in magenta.  If the projective plane is displayed as distance
122  * bands, each band will be displayed with a different color.  In
123  * direction mode, the projective plane is displayed with fully
124  * saturated colors that depend on the angle of the points on the
125  * projective plane with respect to the origin.  Angles in opposite
126  * directions to the origin (e.g., 15 and 205 degrees) are displayed
127  * in the same color since they are projectively equivalent.  If the
128  * projective plane is displayed as direction bands, each band will be
129  * displayed with a different color.
130  *
131  * The rotation speed for each of the three coordinate axes around
132  * which the projective plane rotates can be chosen.
133  *
134  * Furthermore, in the walking mode the walking direction in the 2d
135  * base square of the projective plane and the walking speed can be
136  * chosen.  The walking direction is measured as an angle in degrees
137  * in the 2d square that forms the coordinate system of the surface of
138  * the projective plane.  A value of 0 or 180 means that the walk is
139  * along a circle at a randomly chosen distance from the origin
140  * (parallel to a distance band).  A value of 90 or 270 means that the
141  * walk is directly from the origin to the line at infinity and back
142  * (analogous to a direction band).  Any other value results in a
143  * curved path from the origin to the line at infinity and back.
144  *
145  * By default, the immersion of the real projective plane smoothly
146  * deforms between the Roman and Boy surfaces.  It is possible to
147  * choose the speed of the deformation.  Furthermore, it is possible
148  * to switch the deformation off.  It is also possible to determine
149  * the initial deformation of the immersion.  This is mostly useful if
150  * the deformation is switched off, in which case it will determine
151  * the appearance of the surface.
152  *
153  * As a final option, it is possible to display generalized versions
154  * of the immersion discussed above by specifying the order of the
155  * surface.  The default surface order of 3 results in the immersion
156  * of the real projective described above.  The surface order can be
157  * chosen between 2 and 9.  Odd surface orders result in generalized
158  * immersions of the real projective plane, while even numbers result
159  * in a immersion of a topological sphere (which is orientable).  The
160  * most interesting even case is a surface order of 2, which results
161  * in an immersion of the halfway model of Morin's sphere eversion (if
162  * the deformation is switched off).
163  *
164  * This program is inspired by François Apéry's book "Models of the
165  * Real Projective Plane", Vieweg, 1987.
166  */
167
168 #include "curlicue.h"
169
170 #ifndef M_PI
171 #define M_PI 3.14159265358979323846
172 #endif
173
174 #define DISP_WIREFRAME             0
175 #define DISP_SURFACE               1
176 #define DISP_TRANSPARENT           2
177 #define NUM_DISPLAY_MODES          3
178
179 #define APPEARANCE_SOLID           0
180 #define APPEARANCE_DISTANCE_BANDS  1
181 #define APPEARANCE_DIRECTION_BANDS 2
182 #define NUM_APPEARANCES            3
183
184 #define COLORS_ONESIDED            0
185 #define COLORS_TWOSIDED            1
186 #define COLORS_DISTANCE            2
187 #define COLORS_DIRECTION           3
188 #define NUM_COLORS                 4
189
190 #define VIEW_WALK                  0
191 #define VIEW_TURN                  1
192 #define NUM_VIEW_MODES             2
193
194 #define DISP_PERSPECTIVE           0
195 #define DISP_ORTHOGRAPHIC          1
196 #define NUM_DISP_MODES             2
197
198 #define DEF_DISPLAY_MODE           "random"
199 #define DEF_APPEARANCE             "random"
200 #define DEF_COLORS                 "random"
201 #define DEF_VIEW_MODE              "random"
202 #define DEF_MARKS                  "False"
203 #define DEF_CHANGE_COLORS          "False"
204 #define DEF_DEFORM                 "True"
205 #define DEF_PROJECTION             "random"
206 #define DEF_SPEEDX                 "1.1"
207 #define DEF_SPEEDY                 "1.3"
208 #define DEF_SPEEDZ                 "1.5"
209 #define DEF_WALK_DIRECTION         "83.0"
210 #define DEF_WALK_SPEED             "20.0"
211 #define DEF_DEFORM_SPEED           "10.0"
212 #define DEF_INIT_DEFORM            "1000.0"
213 #define DEF_SURFACE_ORDER          "3"
214
215
216 #ifdef STANDALONE
217 # define DEFAULTS           "*delay:      25000 \n" \
218                             "*showFPS:    False \n" \
219                             "*prefersGLSL: True \n" \
220
221 # define release_romanboy 0
222 # include "xlockmore.h"         /* from the xscreensaver distribution */
223 #else  /* !STANDALONE */
224 # include "xlock.h"             /* from the xlockmore distribution */
225 #endif /* !STANDALONE */
226
227 #ifdef USE_GL
228
229 #ifndef HAVE_JWXYZ
230 # include <X11/keysym.h>
231 #endif
232
233 #include "glsl-utils.h"
234 #include "gltrackball.h"
235
236 #include <float.h>
237
238
239 #ifdef USE_MODULES
240 ModStruct romanboy_description =
241 {"romanboy", "init_romanboy", "draw_romanboy",
242  NULL, "draw_romanboy", "change_romanboy",
243  "free_romanboy", &romanboy_opts, 25000, 1, 1, 1, 1.0, 4, "",
244  "Rotate a 3d immersion of the real projective plane in 3d or walk on it",
245  0, NULL};
246
247 #endif
248
249
250 static char *mode;
251 static char *appear;
252 static char *color_mode;
253 static char *view_mode;
254 static Bool marks;
255 static Bool deform;
256 static Bool change_colors;
257 static char *proj;
258 static float speed_x;
259 static float speed_y;
260 static float speed_z;
261 static float walk_direction;
262 static float walk_speed;
263 static float deform_speed;
264 static float init_deform;
265 static int surface_order;
266
267
268 static XrmOptionDescRec opts[] =
269 {
270   {"-mode",                ".displayMode",   XrmoptionSepArg, 0 },
271   {"-wireframe",           ".displayMode",   XrmoptionNoArg,  "wireframe" },
272   {"-surface",             ".displayMode",   XrmoptionNoArg,  "surface" },
273   {"-transparent",         ".displayMode",   XrmoptionNoArg,  "transparent" },
274   {"-appearance",          ".appearance",    XrmoptionSepArg, 0 },
275   {"-solid",               ".appearance",    XrmoptionNoArg,  "solid" },
276   {"-distance-bands",      ".appearance",    XrmoptionNoArg,  "distance-bands" },
277   {"-direction-bands",     ".appearance",    XrmoptionNoArg,  "direction-bands" },
278   {"-colors",              ".colors",        XrmoptionSepArg, 0 },
279   {"-onesided-colors",     ".colors",        XrmoptionNoArg,  "one-sided" },
280   {"-twosided-colors",     ".colors",        XrmoptionNoArg,  "two-sided" },
281   {"-distance-colors",     ".colors",        XrmoptionNoArg,  "distance" },
282   {"-direction-colors",    ".colors",        XrmoptionNoArg,  "direction" },
283   {"-change-colors",       ".changeColors",  XrmoptionNoArg,  "on"},
284   {"+change-colors",       ".changeColors",  XrmoptionNoArg,  "off"},
285   {"-view-mode",           ".viewMode",      XrmoptionSepArg, 0 },
286   {"-walk",                ".viewMode",      XrmoptionNoArg,  "walk" },
287   {"-turn",                ".viewMode",      XrmoptionNoArg,  "turn" },
288   {"-deform",              ".deform",        XrmoptionNoArg,  "on"},
289   {"+deform",              ".deform",        XrmoptionNoArg,  "off"},
290   {"-orientation-marks",   ".marks",         XrmoptionNoArg,  "on"},
291   {"+orientation-marks",   ".marks",         XrmoptionNoArg,  "off"},
292   {"-projection",          ".projection",    XrmoptionSepArg, 0 },
293   {"-perspective",         ".projection",    XrmoptionNoArg,  "perspective" },
294   {"-orthographic",        ".projection",    XrmoptionNoArg,  "orthographic" },
295   {"-speed-x",             ".speedx",        XrmoptionSepArg, 0 },
296   {"-speed-y",             ".speedy",        XrmoptionSepArg, 0 },
297   {"-speed-z",             ".speedz",        XrmoptionSepArg, 0 },
298   {"-walk-direction",      ".walkDirection", XrmoptionSepArg, 0 },
299   {"-walk-speed",          ".walkSpeed",     XrmoptionSepArg, 0 },
300   {"-deformation-speed",   ".deformSpeed",   XrmoptionSepArg, 0 },
301   {"-initial-deformation", ".initDeform",    XrmoptionSepArg, 0 },
302   {"-roman",               ".initDeform",    XrmoptionNoArg,  "0.0" },
303   {"-boy",                 ".initDeform",    XrmoptionNoArg,  "1000.0" },
304   {"-surface-order",       ".surfaceOrder",  XrmoptionSepArg, 0 },
305 };
306
307 static argtype vars[] =
308 {
309   { &mode,           "displayMode",   "DisplayMode",   DEF_DISPLAY_MODE,   t_String },
310   { &appear,         "appearance",    "Appearance",    DEF_APPEARANCE,     t_String },
311   { &color_mode,     "colors",        "Colors",        DEF_COLORS,         t_String },
312   { &change_colors,  "changeColors",  "ChangeColors",  DEF_CHANGE_COLORS,  t_Bool },
313   { &view_mode,      "viewMode",      "ViewMode",      DEF_VIEW_MODE,      t_String },
314   { &deform,         "deform",        "Deform",        DEF_DEFORM,         t_Bool },
315   { &marks,          "marks",         "Marks",         DEF_MARKS,          t_Bool },
316   { &proj,           "projection",    "Projection",    DEF_PROJECTION,     t_String },
317   { &speed_x,        "speedx",        "Speedx",        DEF_SPEEDX,         t_Float},
318   { &speed_y,        "speedy",        "Speedy",        DEF_SPEEDY,         t_Float},
319   { &speed_z,        "speedz",        "Speedz",        DEF_SPEEDZ,         t_Float},
320   { &walk_direction, "walkDirection", "WalkDirection", DEF_WALK_DIRECTION, t_Float},
321   { &walk_speed,     "walkSpeed",     "WalkSpeed",     DEF_WALK_SPEED,     t_Float},
322   { &deform_speed,   "deformSpeed",   "DeformSpeed",   DEF_DEFORM_SPEED,   t_Float},
323   { &init_deform,    "initDeform",    "InitDeform",    DEF_INIT_DEFORM,    t_Float },
324   { &surface_order,  "surfaceOrder",  "SurfaceOrder",  DEF_SURFACE_ORDER,  t_Int }
325 };
326
327 ENTRYPOINT ModeSpecOpt romanboy_opts =
328 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, NULL};
329
330
331 /* Offset by which we walk above the projective plane */
332 #define DELTAY  0.01
333
334 /* Color change speeds */
335 #define DRHO    0.7
336 #define DSIGMA  1.1
337 #define DTAU    1.7
338
339 /* Number of subdivisions of the projective plane */
340 #define NUMU 64
341 #define NUMV 128
342
343 /* Number of subdivisions per band */
344 #define NUMB 8
345
346
347 typedef struct {
348   GLint WindH, WindW;
349   GLXContext *glx_context;
350   /* Options */
351   int display_mode;
352   int appearance;
353   int colors;
354   Bool change_colors;
355   int view;
356   int projection;
357   Bool marks;
358   /* 3D rotation angles */
359   float alpha, beta, delta;
360   /* Color rotation angles */
361   float rho, sigma, tau;
362   /* Movement parameters */
363   float umove, vmove, dumove, dvmove;
364   int side, dir;
365   /* Deformation parameters */
366   float dd;
367   int defdir;
368   /* The type of the generalized Roman-Boy surface */
369   int g;
370   /* The viewing offset in 3d */
371   float offset3d[3];
372   /* The 3d coordinates of the projective plane and their normals */
373   float *pp;
374   float *pn;
375   /* The precomputed colors of the projective plane */
376   float *col;
377   /* The precomputed texture coordinates of the projective plane */
378   float *tex;
379   /* The "curlicue" texture */
380   GLuint tex_name;
381   /* Aspect ratio of the current window */
382   float aspect;
383   /* Trackball states */
384   trackball_state *trackball;
385   Bool button_pressed;
386   /* A random factor to modify the rotation speeds */
387   float speed_scale;
388 #ifdef HAVE_GLSL
389   GLfloat *uv;
390   GLuint *indices;
391   Bool use_shaders, buffers_initialized;
392   GLuint shader_program;
393   GLint vertex_uv_index, vertex_t_index, color_index;
394   GLint mat_mv_index, mat_p_index, g_index, d_index;
395   GLint bool_textures_index, draw_lines_index;
396   GLint glbl_ambient_index, lt_ambient_index;
397   GLint lt_diffuse_index, lt_specular_index;
398   GLint lt_direction_index, lt_halfvect_index;
399   GLint front_ambient_index, back_ambient_index;
400   GLint front_diffuse_index, back_diffuse_index;
401   GLint specular_index, shininess_index;
402   GLint texture_sampler_index;
403   GLuint vertex_uv_buffer,  vertex_t_buffer;
404   GLuint color_buffer, indices_buffer;
405   GLint ni, ne, nt;
406 #endif /* HAVE_GLSL */
407 } romanboystruct;
408
409 static romanboystruct *romanboy = (romanboystruct *) NULL;
410
411
412 #ifdef HAVE_GLSL
413
414 /* The GLSL versions that correspond to different versions of OpenGL. */
415 static const GLchar *shader_version_2_1 =
416   "#version 120\n";
417 static const GLchar *shader_version_3_0 =
418   "#version 130\n";
419 static const GLchar *shader_version_3_0_es =
420   "#version 300 es\n"
421   "precision highp float;\n"
422   "precision highp int;\n";
423
424 /* The vertex shader code is composed of code fragments that depend on
425    the OpenGL version and code fragments that are version-independent.
426    They are concatenated by glsl_CompileAndLinkShaders in the function
427    init_glsl(). */
428 static const GLchar *vertex_shader_attribs_2_1 =
429   "attribute vec2 VertexUV;\n"
430   "attribute vec4 VertexT;\n"
431   "attribute vec4 VertexColor;\n"
432   "\n"
433   "varying vec3 Normal;\n"
434   "varying vec4 Color;\n"
435   "varying vec4 TexCoord;\n"
436   "\n";
437 static const GLchar *vertex_shader_attribs_3_0 =
438   "in vec2 VertexUV;\n"
439   "in vec4 VertexT;\n"
440   "in vec4 VertexColor;\n"
441   "\n"
442   "out vec3 Normal;\n"
443   "out vec4 Color;\n"
444   "out vec4 TexCoord;\n"
445   "\n";
446 static const GLchar *vertex_shader_main =
447   "uniform mat4 MatModelView;\n"
448   "uniform mat4 MatProj;\n"
449   "uniform int G;\n"
450   "uniform float D;\n"
451   "uniform bool BoolTextures;\n"
452   "\n"
453   "void main (void)\n"
454   "{\n"
455   "  const float EPSILON = 1.19e-6f;\n"
456   "  const float M_PI = 3.14159265359f;\n"
457   "  const float M_SQRT2 = 1.41421356237f;\n"
458   "  float g = float(G);\n"
459   "  float u = VertexUV.x;\n"
460   "  float v = VertexUV.y;\n"
461   "  float sqrt2og = M_SQRT2/g;\n"
462   "  float h1m1og = 0.5f*(1.0f-1.0f/g);\n"
463   "  float gm1 = g-1.0f;\n"
464   "  float cu = cos(u);\n"
465   "  float su = sin(u);\n"
466   "  float cgu = cos(g*u);\n"
467   "  float sgu = sin(g*u);\n"
468   "  float cgm1u = cos(gm1*u);\n"
469   "  float sgm1u = sin(gm1*u);\n"
470   "  float cv = cos(v);\n"
471   "  float c2v = cos(2.0f*v);\n"
472   "  float s2v = sin(2.0f*v);\n"
473   "  float cv2 = cv*cv;\n"
474   "  float nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;\n"
475   "  float nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;\n"
476   "  float nomux = -sqrt2og*cv2*gm1*sgm1u-h1m1og*s2v*su;\n"
477   "  float nomuy = sqrt2og*cv2*gm1*cgm1u-h1m1og*s2v*cu;\n"
478   "  float nomvx = -sqrt2og*s2v*cgm1u+2.0f*h1m1og*c2v*cu;\n"
479   "  float nomvy = -sqrt2og*s2v*sgm1u-2.0f*h1m1og*c2v*su;\n"
480   "  float den = 1.0f/(1.0f-0.5f*M_SQRT2*D*s2v*sgu);\n"
481   "  float den2 = den*den;\n"
482   "  float denu = 0.5f*M_SQRT2*D*g*cgu*s2v;\n"
483   "  float denv = M_SQRT2*D*sgu*c2v;\n"
484   "  vec3 x = vec3(nomx*den,\n"
485   "                nomy*den,\n"
486   "                cv2*den);\n"
487   "  if (0.5f*M_PI-abs(v) < EPSILON)\n"
488   "  {\n"
489   "    if (0.5f*M_PI-v < EPSILON)\n"
490   "      v = 0.5f*M_PI-EPSILON;\n"
491   "    else\n"
492   "      v = -0.5f*M_PI+EPSILON;\n"
493   "    cv = cos(v);\n"
494   "    c2v = cos(2.0f*v);\n"
495   "    s2v = sin(2.0f*v);\n"
496   "    cv2 = cv*cv;\n"
497   "    nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;\n"
498   "    nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;\n"
499   "    nomux = -sqrt2og*cv2*gm1*sgm1u-h1m1og*s2v*su;\n"
500   "    nomuy = sqrt2og*cv2*gm1*cgm1u-h1m1og*s2v*cu;\n"
501   "    nomvx = -sqrt2og*s2v*cgm1u+2.0f*h1m1og*c2v*cu;\n"
502   "    nomvy = -sqrt2og*s2v*sgm1u-2.0f*h1m1og*c2v*su;\n"
503   "    den = 1.0f/(1.0f-0.5f*M_SQRT2*D*s2v*sgu);\n"
504   "    den2 = den*den;\n"
505   "    denu = 0.5f*M_SQRT2*D*g*cgu*s2v;\n"
506   "    denv = M_SQRT2*D*sgu*c2v;\n"
507   "  }\n"
508   "  vec3 xu = vec3(nomux*den+nomx*denu*den2,\n"
509   "                 nomuy*den+nomy*denu*den2,\n"
510   "                 cv2*denu*den2);\n"
511   "  vec3 xv = vec3(nomvx*den+nomx*denv*den2,\n"
512   "                 nomvy*den+nomy*denv*den2,\n"
513   "                 -s2v*den+cv2*denv*den2);\n"
514   "  vec4 Position = MatModelView*vec4(x,1.0f);\n"
515   "  vec4 pu = MatModelView*vec4(xu,0.0f);\n"
516   "  vec4 pv = MatModelView*vec4(xv,0.0f);\n"
517   "  Normal = normalize(cross(pu.xyz,pv.xyz));\n"
518   "  gl_Position = MatProj*Position;\n"
519   "  Color = VertexColor;\n"
520   "  if (BoolTextures)\n"
521   "    TexCoord = VertexT;\n"
522   "}\n";
523
524 /* The fragment shader code is composed of code fragments that depend on
525    the OpenGL version and code fragments that are version-independent.
526    They are concatenated by glsl_CompileAndLinkShaders in the function
527    init_glsl(). */
528 static const GLchar *fragment_shader_attribs_2_1 =
529   "varying vec3 Normal;\n"
530   "varying vec4 Color;\n"
531   "varying vec4 TexCoord;\n"
532   "\n";
533 static const GLchar *fragment_shader_attribs_3_0 =
534   "in vec3 Normal;\n"
535   "in vec4 Color;\n"
536   "in vec4 TexCoord;\n"
537   "\n"
538   "out vec4 FragColor;\n"
539   "\n";
540 static const GLchar *fragment_shader_main =
541   "uniform bool DrawLines;\n"
542   "uniform vec4 LtGlblAmbient;\n"
543   "uniform vec4 LtAmbient, LtDiffuse, LtSpecular;\n"
544   "uniform vec3 LtDirection, LtHalfVector;\n"
545   "uniform vec4 MatFrontAmbient, MatBackAmbient;\n"
546   "uniform vec4 MatFrontDiffuse, MatBackDiffuse;\n"
547   "uniform vec4 MatSpecular;\n"
548   "uniform float MatShininess;\n"
549   "uniform bool BoolTextures;\n"
550   "uniform sampler2D TextureSampler;"
551   "\n"
552   "void main (void)\n"
553   "{\n"
554   "  vec4 color;\n"
555   "  if (DrawLines)\n"
556   "  {\n"
557   "    color = Color;\n"
558   "  }\n"
559   "  else\n"
560   "  {\n"
561   "    vec3 normalDirection;\n"
562   "    vec4 ambientColor, diffuseColor, sceneColor;\n"
563   "    vec4 ambientLighting, diffuseReflection, specularReflection;\n"
564   "    float ndotl, ndoth, pf;\n"
565   "    \n"
566   "    if (gl_FrontFacing)\n"
567   "    {\n"
568   "      normalDirection = normalize(Normal);\n"
569   "      sceneColor = Color*MatFrontAmbient*LtGlblAmbient;\n"
570   "      ambientColor = Color*MatFrontAmbient;\n"
571   "      diffuseColor = Color*MatFrontDiffuse;\n"
572   "    }\n"
573   "    else\n"
574   "    {\n"
575   "      normalDirection = -normalize(Normal);\n"
576   "      sceneColor = Color*MatBackAmbient*LtGlblAmbient;\n"
577   "      ambientColor = Color*MatBackAmbient;\n"
578   "      diffuseColor = Color*MatBackDiffuse;\n"
579   "    }\n"
580   "    \n"
581   "    ndotl = max(0.0,dot(normalDirection,LtDirection));\n"
582   "    ndoth = max(0.0,dot(normalDirection,LtHalfVector));\n"
583   "    if (ndotl == 0.0)\n"
584   "      pf = 0.0;\n"
585   "    else\n"
586   "      pf = pow(ndoth,MatShininess);\n"
587   "    ambientLighting = ambientColor*LtAmbient;\n"
588   "    diffuseReflection = LtDiffuse*diffuseColor*ndotl;\n"
589   "    specularReflection = LtSpecular*MatSpecular*pf;\n"
590   "    color = sceneColor+ambientLighting+diffuseReflection;\n";
591 static const GLchar *fragment_shader_out_2_1 =
592   "    if (BoolTextures)\n"
593   "      color *= texture2D(TextureSampler,TexCoord.st);"
594   "    color += specularReflection;\n"
595   "  }\n"
596   "  gl_FragColor = clamp(color,0.0,1.0);\n"
597   "}\n";
598 static const GLchar *fragment_shader_out_3_0 =
599   "    if (BoolTextures)\n"
600   "      color *= texture(TextureSampler,TexCoord.st);"
601   "    color += specularReflection;\n"
602   "  }\n"
603   "  FragColor = clamp(color,0.0,1.0);\n"
604   "}\n";
605
606 #endif /* HAVE_GLSL */
607
608
609 /* Add a rotation around the x-axis to the matrix m. */
610 static void rotatex(float m[3][3], float phi)
611 {
612   float c, s, u, v;
613   int i;
614
615   phi *= M_PI/180.0;
616   c = cos(phi);
617   s = sin(phi);
618   for (i=0; i<3; i++)
619   {
620     u = m[i][1];
621     v = m[i][2];
622     m[i][1] = c*u+s*v;
623     m[i][2] = -s*u+c*v;
624   }
625 }
626
627
628 /* Add a rotation around the y-axis to the matrix m. */
629 static void rotatey(float m[3][3], float phi)
630 {
631   float c, s, u, v;
632   int i;
633
634   phi *= M_PI/180.0;
635   c = cos(phi);
636   s = sin(phi);
637   for (i=0; i<3; i++)
638   {
639     u = m[i][0];
640     v = m[i][2];
641     m[i][0] = c*u-s*v;
642     m[i][2] = s*u+c*v;
643   }
644 }
645
646
647 /* Add a rotation around the z-axis to the matrix m. */
648 static void rotatez(float m[3][3], float phi)
649 {
650   float c, s, u, v;
651   int i;
652
653   phi *= M_PI/180.0;
654   c = cos(phi);
655   s = sin(phi);
656   for (i=0; i<3; i++)
657   {
658     u = m[i][0];
659     v = m[i][1];
660     m[i][0] = c*u+s*v;
661     m[i][1] = -s*u+c*v;
662   }
663 }
664
665
666 /* Compute the rotation matrix m from the rotation angles. */
667 static void rotateall(float al, float be, float de, float m[3][3])
668 {
669   int i, j;
670
671   for (i=0; i<3; i++)
672     for (j=0; j<3; j++)
673       m[i][j] = (i==j);
674   rotatex(m,al);
675   rotatey(m,be);
676   rotatez(m,de);
677 }
678
679
680 /* Multiply two rotation matrices: o=m*n. */
681 static void mult_rotmat(float m[3][3], float n[3][3], float o[3][3])
682 {
683   int i, j, k;
684
685   for (i=0; i<3; i++)
686   {
687     for (j=0; j<3; j++)
688     {
689       o[i][j] = 0.0;
690       for (k=0; k<3; k++)
691         o[i][j] += m[i][k]*n[k][j];
692     }
693   }
694 }
695
696
697 /* Compute a 3D rotation matrix from a unit quaternion. */
698 static void quat_to_rotmat(float p[4], float m[3][3])
699 {
700   double al, be, de;
701   double r00, r01, r02, r12, r22;
702
703   r00 = 1.0-2.0*(p[1]*p[1]+p[2]*p[2]);
704   r01 = 2.0*(p[0]*p[1]+p[2]*p[3]);
705   r02 = 2.0*(p[2]*p[0]-p[1]*p[3]);
706   r12 = 2.0*(p[1]*p[2]+p[0]*p[3]);
707   r22 = 1.0-2.0*(p[1]*p[1]+p[0]*p[0]);
708
709   al = atan2(-r12,r22)*180.0/M_PI;
710   be = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
711   de = atan2(-r01,r00)*180.0/M_PI;
712
713   rotateall(al,be,de,m);
714 }
715
716
717 /* Compute a fully saturated and bright color based on an angle. */
718 static void color(romanboystruct *pp, double angle, float mat[3][3],
719                   float col[4])
720 {
721   int s;
722   double t, ca, sa;
723   float m;
724
725   if (!pp->change_colors)
726   {
727     if (pp->colors == COLORS_ONESIDED || pp->colors == COLORS_TWOSIDED)
728       return;
729
730     if (angle >= 0.0)
731       angle = fmod(angle,2.0*M_PI);
732     else
733       angle = fmod(angle,-2.0*M_PI);
734     s = floor(angle/(M_PI/3));
735     t = angle/(M_PI/3)-s;
736     if (s >= 6)
737       s = 0;
738     switch (s)
739     {
740       case 0:
741         col[0] = 1.0;
742         col[1] = t;
743         col[2] = 0.0;
744         break;
745       case 1:
746         col[0] = 1.0-t;
747         col[1] = 1.0;
748         col[2] = 0.0;
749         break;
750       case 2:
751         col[0] = 0.0;
752         col[1] = 1.0;
753         col[2] = t;
754         break;
755       case 3:
756         col[0] = 0.0;
757         col[1] = 1.0-t;
758         col[2] = 1.0;
759         break;
760       case 4:
761         col[0] = t;
762         col[1] = 0.0;
763         col[2] = 1.0;
764         break;
765       case 5:
766         col[0] = 1.0;
767         col[1] = 0.0;
768         col[2] = 1.0-t;
769         break;
770     }
771   }
772   else /* pp->change_colors */
773   {
774     if (pp->colors == COLORS_ONESIDED || pp->colors == COLORS_TWOSIDED)
775     {
776       col[0] = mat[0][2];
777       col[1] = mat[1][2];
778       col[2] = mat[2][2];
779     }
780     else
781     {
782       ca = cos(angle);
783       sa = sin(angle);
784       col[0] = ca*mat[0][0]+sa*mat[0][1];
785       col[1] = ca*mat[1][0]+sa*mat[1][1];
786       col[2] = ca*mat[2][0]+sa*mat[2][1];
787     }
788     m = 0.5f/fmaxf(fmaxf(fabsf(col[0]),fabsf(col[1])),fabsf(col[2]));
789     col[0] = m*col[0]+0.5f;
790     col[1] = m*col[1]+0.5f;
791     col[2] = m*col[2]+0.5f;
792   }
793   if (pp->display_mode == DISP_TRANSPARENT)
794     col[3] = 0.7;
795   else
796     col[3] = 1.0;
797 }
798
799
800 /* Set up the projective plane colors and texture. */
801 static void setup_roman_boy_color_texture(ModeInfo *mi, double umin,
802                                           double umax, double vmin,
803                                           double vmax, int numu, int numv)
804 {
805   int i, j, k, g;
806   double u, v, ur, vr;
807   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
808
809   g = pp->g;
810   ur = umax-umin;
811   vr = vmax-vmin;
812   for (i=0; i<=numv; i++)
813   {
814     for (j=0; j<=numu; j++)
815     {
816       k = i*(numu+1)+j;
817       if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
818         u = -ur*j/numu+umin;
819       else
820         u = ur*j/numu+umin;
821       v = vr*i/numv+vmin;
822       if (!pp->change_colors)
823       {
824         if (pp->colors == COLORS_DIRECTION)
825           color(pp,2.0*M_PI-fmod(2.0*u,2.0*M_PI),NULL,&pp->col[4*k]);
826         else /* pp->colors == COLORS_DISTANCE */
827           color(pp,v*(5.0/6.0),NULL,&pp->col[4*k]);
828       }
829       pp->tex[2*k+0] = -16*g*u/(2.0*M_PI);
830       if (pp->appearance == APPEARANCE_DISTANCE_BANDS)
831         pp->tex[2*k+1] = 32*v/(2.0*M_PI)-0.5;
832       else
833         pp->tex[2*k+1] = 32*v/(2.0*M_PI);
834     }
835   }
836 }
837
838
839 /* Compute the current walk frame, i.e., the coordinate system of the
840    point and direction at which the viewer is currently walking on the
841    projective plane. */
842 static void compute_walk_frame(romanboystruct *pp, int g, float d,
843                                float radius, float oz, float mat[3][3])
844 {
845   float p[3], pu[3], pv[3], pm[3], n[3], b[3];
846   int l, m;
847   float u, v;
848   float xx[3], xxu[3], xxv[3];
849   float r, t;
850   float cu, su, cgu, sgu, cgm1u, sgm1u, cv, c2v, s2v, cv2;
851   float sqrt2og, h1m1og, gm1, nomx, nomy, nomux, nomuy, nomvx, nomvy;
852   float den, den2, denu, denv;
853
854   u = pp->umove;
855   v = pp->vmove;
856   if (g & 1)
857     v = 0.5f*(float)M_PI-0.25f*v;
858   else
859     v = 0.5f*(float)M_PI-0.5f*v;
860   sqrt2og = (float)M_SQRT2/g;
861   h1m1og = 0.5f*(1.0f-1.0f/g);
862   gm1 = g-1.0f;
863   cu = cosf(u);
864   su = sinf(u);
865   cgu = cosf(g*u);
866   sgu = sinf(g*u);
867   cgm1u = cosf(gm1*u);
868   sgm1u = sinf(gm1*u);
869   cv = cosf(v);
870   c2v = cosf(2.0f*v);
871   s2v = sinf(2.0f*v);
872   cv2 = cv*cv;
873   nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;
874   nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;
875   nomux = -sqrt2og*cv2*gm1*sgm1u-h1m1og*s2v*su;
876   nomuy = sqrt2og*cv2*gm1*cgm1u-h1m1og*s2v*cu;
877   nomvx = -sqrt2og*s2v*cgm1u+2.0f*h1m1og*c2v*cu;
878   nomvy = -sqrt2og*s2v*sgm1u-2.0f*h1m1og*c2v*su;
879   den = 1.0f/(1.0f-0.5f*(float)M_SQRT2*d*s2v*sgu);
880   den2 = den*den;
881   denu = 0.5f*(float)M_SQRT2*d*g*cgu*s2v;
882   denv = (float)M_SQRT2*d*sgu*c2v;
883   xx[0] = nomx*den;
884   xx[1] = nomy*den;
885   xx[2] = cv2*den-oz;
886   /* Avoid degenerate tangential plane basis vectors. */
887   if (0.5f*(float)M_PI-fabsf(v) < 10.0f*(float)FLT_EPSILON)
888   {
889     if (0.5f*(float)M_PI-v < 10.0f*(float)FLT_EPSILON)
890       v = 0.5f*(float)M_PI-10.0f*(float)FLT_EPSILON;
891     else
892       v = -0.5f*(float)M_PI+10.0f*(float)FLT_EPSILON;
893     cv = cosf(v);
894     c2v = cosf(2.0f*v);
895     s2v = sinf(2.0f*v);
896     cv2 = cv*cv;
897     nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;
898     nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;
899     nomux = -sqrt2og*cv2*gm1*sgm1u-h1m1og*s2v*su;
900     nomuy = sqrt2og*cv2*gm1*cgm1u-h1m1og*s2v*cu;
901     nomvx = -sqrt2og*s2v*cgm1u+2.0f*h1m1og*c2v*cu;
902     nomvy = -sqrt2og*s2v*sgm1u-2.0f*h1m1og*c2v*su;
903     den = 1.0f/(1.0f-0.5f*(float)M_SQRT2*d*s2v*sgu);
904     den2 = den*den;
905     denu = 0.5f*(float)M_SQRT2*d*g*cgu*s2v;
906     denv = (float)M_SQRT2*d*sgu*c2v;
907   }
908   xxu[0] = nomux*den+nomx*denu*den2;
909   xxu[1] = nomuy*den+nomy*denu*den2;
910   xxu[2] = cv2*denu*den2;
911   xxv[0] = nomvx*den+nomx*denv*den2;
912   xxv[1] = nomvy*den+nomy*denv*den2;
913   xxv[2] = -s2v*den+cv2*denv*den2;
914   for (l=0; l<3; l++)
915   {
916     p[l] = xx[l]*radius;
917     pu[l] = xxu[l]*radius;
918     pv[l] = xxv[l]*radius;
919   }
920   n[0] = pu[1]*pv[2]-pu[2]*pv[1];
921   n[1] = pu[2]*pv[0]-pu[0]*pv[2];
922   n[2] = pu[0]*pv[1]-pu[1]*pv[0];
923   t = 1.0f/(pp->side*4.0f*sqrtf(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]));
924   n[0] *= t;
925   n[1] *= t;
926   n[2] *= t;
927   pm[0] = pu[0]*pp->dumove-pv[0]*0.25f*pp->dvmove;
928   pm[1] = pu[1]*pp->dumove-pv[1]*0.25f*pp->dvmove;
929   pm[2] = pu[2]*pp->dumove-pv[2]*0.25f*pp->dvmove;
930   t = 1.0f/(4.0f*sqrtf(pm[0]*pm[0]+pm[1]*pm[1]+pm[2]*pm[2]));
931   pm[0] *= t;
932   pm[1] *= t;
933   pm[2] *= t;
934   b[0] = n[1]*pm[2]-n[2]*pm[1];
935   b[1] = n[2]*pm[0]-n[0]*pm[2];
936   b[2] = n[0]*pm[1]-n[1]*pm[0];
937   t = 1.0f/(4.0f*sqrtf(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]));
938   b[0] *= t;
939   b[1] *= t;
940   b[2] *= t;
941
942   /* Compute alpha, beta, gamma from the three basis vectors.
943          |  -b[0]  -b[1]  -b[2] |
944      m = |   n[0]   n[1]   n[2] |
945          | -pm[0] -pm[1] -pm[2] |
946   */
947   pp->alpha = atan2f(-n[2],-pm[2])*180.0f/(float)M_PI;
948   pp->beta = atan2f(-b[2],sqrtf(b[0]*b[0]+b[1]*b[1]))*180.0f/(float)M_PI;
949   pp->delta = atan2f(b[1],-b[0])*180.0f/(float)M_PI;
950
951   /* Compute the rotation that rotates the projective plane in 3D. */
952   rotateall(pp->alpha,pp->beta,pp->delta,mat);
953
954   u = pp->umove;
955   v = pp->vmove;
956   if (g & 1)
957     v = 0.5f*(float)M_PI-0.25f*v;
958   else
959     v = 0.5f*(float)M_PI-0.5f*v;
960   sqrt2og = (float)M_SQRT2/g;
961   h1m1og = 0.5f*(1.0f-1.0f/g);
962   gm1 = g-1.0f;
963   cu = cosf(u);
964   su = sinf(u);
965   sgu = sinf(g*u);
966   cgm1u = cosf(gm1*u);
967   sgm1u = sinf(gm1*u);
968   cv = cosf(v);
969   s2v = sinf(2.0f*v);
970   cv2 = cv*cv;
971   nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;
972   nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;
973   den = 1.0f/(1.0f-0.5f*(float)M_SQRT2*d*s2v*sgu);
974   xx[0] = nomx*den;
975   xx[1] = nomy*den;
976   xx[2] = cv2*den-oz;
977   for (l=0; l<3; l++)
978   {
979     r = 0.0;
980     for (m=0; m<3; m++)
981       r += mat[l][m]*xx[m];
982     p[l] = r*radius;
983   }
984
985   pp->offset3d[0] = -p[0];
986   pp->offset3d[1] = -p[1]-DELTAY;
987   pp->offset3d[2] = -p[2];
988 }
989
990
991 /* Draw a 3d immersion of the projective plane using OpenGL's fixed
992    functionality. */
993 static int roman_boy_ff(ModeInfo *mi, double umin, double umax,
994                         double vmin, double vmax, int numu, int numv)
995 {
996   static const GLfloat light_ambient[]          = { 0.0, 0.0, 0.0, 1.0 };
997   static const GLfloat light_diffuse[]          = { 1.0, 1.0, 1.0, 1.0 };
998   static const GLfloat light_specular[]         = { 1.0, 1.0, 1.0, 1.0 };
999   static const GLfloat light_position[]         = { 1.0, 1.0, 1.0, 0.0 };
1000   static const GLfloat mat_specular[]           = { 1.0, 1.0, 1.0, 1.0 };
1001   static const GLfloat mat_diff_red[]           = { 1.0, 0.0, 0.0, 1.0 };
1002   static const GLfloat mat_diff_green[]         = { 0.0, 1.0, 0.0, 1.0 };
1003   static const GLfloat mat_diff_oneside[]       = { 0.9, 0.4, 0.3, 1.0 };
1004   static const GLfloat mat_diff_trans_red[]     = { 1.0, 0.0, 0.0, 0.7 };
1005   static const GLfloat mat_diff_trans_green[]   = { 0.0, 1.0, 0.0, 0.7 };
1006   static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
1007   float mat_diff_dyn[4], mat_diff_dyn_compl[4];
1008   float p[3], pu[3], pv[3], n[3], mat[3][3], matc[3][3];
1009   int i, j, k, l, m, o, g;
1010   float u, v, ur, vr, oz;
1011   float xx[3], xxu[3], xxv[3];
1012   float r, s, t;
1013   float d, dd, radius;
1014   float cu, su, cgu, sgu, cgm1u, sgm1u, cv, c2v, s2v, cv2;
1015   float sqrt2og, h1m1og, gm1, nomx, nomy, nomux, nomuy, nomvx, nomvy;
1016   float den, den2, denu, denv;
1017   float qu[4], r1[3][3], r2[3][3];
1018   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
1019   int polys;
1020
1021   glMatrixMode(GL_PROJECTION);
1022   glLoadIdentity();
1023   if (pp->projection == DISP_PERSPECTIVE || pp->view == VIEW_WALK)
1024   {
1025     if (pp->view == VIEW_WALK)
1026       gluPerspective(60.0,pp->aspect,0.01,10.0);
1027     else
1028       gluPerspective(60.0,pp->aspect,0.1,10.0);
1029   }
1030   else
1031   {
1032     if (pp->aspect >= 1.0)
1033       glOrtho(-pp->aspect,pp->aspect,-1.0,1.0,0.1,10.0);
1034     else
1035       glOrtho(-1.0,1.0,-1.0/pp->aspect,1.0/pp->aspect,0.1,10.0);
1036   }
1037   glMatrixMode(GL_MODELVIEW);
1038   glLoadIdentity();
1039
1040   if (pp->display_mode == DISP_SURFACE)
1041   {
1042     glEnable(GL_DEPTH_TEST);
1043     glDepthFunc(GL_LESS);
1044     glDepthMask(GL_TRUE);
1045     glShadeModel(GL_SMOOTH);
1046     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
1047     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
1048     glEnable(GL_LIGHTING);
1049     glEnable(GL_LIGHT0);
1050     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
1051     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
1052     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
1053     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
1054     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
1055     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
1056     glDisable(GL_BLEND);
1057   }
1058   else if (pp->display_mode == DISP_TRANSPARENT)
1059   {
1060     glDisable(GL_DEPTH_TEST);
1061     glDepthMask(GL_FALSE);
1062     glShadeModel(GL_SMOOTH);
1063     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
1064     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
1065     glEnable(GL_LIGHTING);
1066     glEnable(GL_LIGHT0);
1067     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
1068     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
1069     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
1070     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
1071     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
1072     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
1073     glEnable(GL_BLEND);
1074     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
1075   }
1076   else  /* pp->display_mode == DISP_WIREFRAME */
1077   {
1078     glEnable(GL_DEPTH_TEST);
1079     glDepthFunc(GL_LESS);
1080     glDepthMask(GL_TRUE);
1081     glShadeModel(GL_FLAT);
1082     glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
1083     glDisable(GL_LIGHTING);
1084     glDisable(GL_LIGHT0);
1085     glDisable(GL_BLEND);
1086   }
1087
1088   if (pp->marks)
1089   {
1090     glEnable(GL_TEXTURE_2D);
1091 #ifndef HAVE_JWZGLES
1092     glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR);
1093 #endif
1094   }
1095   else
1096   {
1097     glDisable(GL_TEXTURE_2D);
1098 #ifndef HAVE_JWZGLES
1099     glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SINGLE_COLOR);
1100 #endif
1101   }
1102
1103   g = pp->g;
1104   dd = pp->dd;
1105   d = ((6.0f*dd-15.0f)*dd+10.0f)*dd*dd*dd;
1106   r = 1.0f+d*d*(1.0f/2.0f+d*d*(1.0f/6.0f+d*d*(1.0f/3.0f)));
1107   radius = 1.0f/r;
1108   oz = 0.5f*r;
1109
1110   if (pp->change_colors)
1111     rotateall(pp->rho,pp->sigma,pp->tau,matc);
1112
1113   if (pp->view == VIEW_WALK)
1114   {
1115     /* Compute the walk frame. */
1116     compute_walk_frame(pp,g,d,radius,oz,mat);
1117   }
1118   else
1119   {
1120     /* Compute the rotation that rotates the projective plane in 3D,
1121        including the trackball rotations. */
1122     rotateall(pp->alpha,pp->beta,pp->delta,r1);
1123
1124     gltrackball_get_quaternion(pp->trackball,qu);
1125     quat_to_rotmat(qu,r2);
1126
1127     mult_rotmat(r2,r1,mat);
1128   }
1129
1130   if (!pp->change_colors)
1131   {
1132     if (pp->colors == COLORS_ONESIDED)
1133     {
1134       glColor3fv(mat_diff_oneside);
1135       if (pp->display_mode == DISP_TRANSPARENT)
1136       {
1137         glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
1138                      mat_diff_trans_oneside);
1139       }
1140       else
1141       {
1142         glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
1143                      mat_diff_oneside);
1144       }
1145     }
1146     else if (pp->colors == COLORS_TWOSIDED)
1147     {
1148       glColor3fv(mat_diff_red);
1149       if (pp->display_mode == DISP_TRANSPARENT)
1150       {
1151         glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
1152         glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
1153       }
1154       else
1155       {
1156         glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
1157         glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
1158       }
1159     }
1160   }
1161   else /* pp->change_colors */
1162   {
1163     color(pp,0.0,matc,mat_diff_dyn);
1164     if (pp->colors == COLORS_ONESIDED)
1165     {
1166       glColor3fv(mat_diff_dyn);
1167       glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
1168     }
1169     else if (pp->colors == COLORS_TWOSIDED)
1170     {
1171       mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
1172       mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
1173       mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
1174       mat_diff_dyn_compl[3] = mat_diff_dyn[3];
1175       glColor3fv(mat_diff_dyn);
1176       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn);
1177       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_dyn_compl);
1178     }
1179   }
1180   glBindTexture(GL_TEXTURE_2D,pp->tex_name);
1181
1182   ur = umax-umin;
1183   vr = vmax-vmin;
1184
1185   /* Set up the projective plane coordinates and normals. */
1186   if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
1187   {
1188     for (i=0; i<=numv; i++)
1189     {
1190       if (pp->appearance == APPEARANCE_DISTANCE_BANDS &&
1191           ((i & (NUMB-1)) >= NUMB/4+1) && ((i & (NUMB-1)) < 3*NUMB/4))
1192         continue;
1193       for (j=0; j<=numu; j++)
1194       {
1195         o = i*(numu+1)+j;
1196         u = ur*j/numu+umin;
1197         v = vr*i/numv+vmin;
1198         if (pp->change_colors)
1199         {
1200           /* Compute the colors dynamically. */
1201           if (pp->colors == COLORS_DIRECTION)
1202             color(pp,2.0*M_PI-fmod(2.0*u,2.0*M_PI),matc,&pp->col[4*o]);
1203           else if (pp->colors == COLORS_DISTANCE)
1204             color(pp,v*(5.0/6.0),matc,&pp->col[4*o]);
1205         }
1206         if (g & 1)
1207           v = 0.5f*(float)M_PI-0.25f*v;
1208         else
1209           v = 0.5f*(float)M_PI-0.5f*v;
1210         sqrt2og = (float)M_SQRT2/g;
1211         h1m1og = 0.5f*(1.0f-1.0f/g);
1212         gm1 = g-1.0f;
1213         cu = cosf(u);
1214         su = sinf(u);
1215         cgu = cosf(g*u);
1216         sgu = sinf(g*u);
1217         cgm1u = cosf(gm1*u);
1218         sgm1u = sinf(gm1*u);
1219         cv = cosf(v);
1220         c2v = cosf(2.0f*v);
1221         s2v = sinf(2.0f*v);
1222         cv2 = cv*cv;
1223         nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;
1224         nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;
1225         nomux = -sqrt2og*cv2*gm1*sgm1u-h1m1og*s2v*su;
1226         nomuy = sqrt2og*cv2*gm1*cgm1u-h1m1og*s2v*cu;
1227         nomvx = -sqrt2og*s2v*cgm1u+2.0f*h1m1og*c2v*cu;
1228         nomvy = -sqrt2og*s2v*sgm1u-2.0f*h1m1og*c2v*su;
1229         den = 1.0f/(1.0f-0.5f*(float)M_SQRT2*d*s2v*sgu);
1230         den2 = den*den;
1231         denu = 0.5f*(float)M_SQRT2*d*g*cgu*s2v;
1232         denv = (float)M_SQRT2*d*sgu*c2v;
1233         xx[0] = nomx*den;
1234         xx[1] = nomy*den;
1235         xx[2] = cv2*den-oz;
1236         /* Avoid degenerate tangential plane basis vectors. */
1237         if (0.5f*(float)M_PI-fabsf(v) < 10.0f*(float)FLT_EPSILON)
1238         {
1239           if (0.5f*(float)M_PI-v < 10.0f*(float)FLT_EPSILON)
1240             v = 0.5f*(float)M_PI-10.0f*(float)FLT_EPSILON;
1241           else
1242             v = -0.5f*(float)M_PI+10.0f*(float)FLT_EPSILON;
1243           cv = cosf(v);
1244           c2v = cosf(2.0f*v);
1245           s2v = sinf(2.0f*v);
1246           cv2 = cv*cv;
1247           nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;
1248           nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;
1249           nomux = -sqrt2og*cv2*gm1*sgm1u-h1m1og*s2v*su;
1250           nomuy = sqrt2og*cv2*gm1*cgm1u-h1m1og*s2v*cu;
1251           nomvx = -sqrt2og*s2v*cgm1u+2.0f*h1m1og*c2v*cu;
1252           nomvy = -sqrt2og*s2v*sgm1u-2.0f*h1m1og*c2v*su;
1253           den = 1.0f/(1.0f-0.5f*(float)M_SQRT2*d*s2v*sgu);
1254           den2 = den*den;
1255           denu = 0.5f*(float)M_SQRT2*d*g*cgu*s2v;
1256           denv = (float)M_SQRT2*d*sgu*c2v;
1257         }
1258         xxu[0] = nomux*den+nomx*denu*den2;
1259         xxu[1] = nomuy*den+nomy*denu*den2;
1260         xxu[2] = cv2*denu*den2;
1261         xxv[0] = nomvx*den+nomx*denv*den2;
1262         xxv[1] = nomvy*den+nomy*denv*den2;
1263         xxv[2] = -s2v*den+cv2*denv*den2;
1264         for (l=0; l<3; l++)
1265         {
1266           r = 0.0f;
1267           s = 0.0f;
1268           t = 0.0f;
1269           for (m=0; m<3; m++)
1270           {
1271             r += mat[l][m]*xx[m];
1272             s += mat[l][m]*xxu[m];
1273             t += mat[l][m]*xxv[m];
1274           }
1275           p[l] = r*radius+pp->offset3d[l];
1276           pu[l] = s*radius;
1277           pv[l] = t*radius;
1278         }
1279         n[0] = pu[1]*pv[2]-pu[2]*pv[1];
1280         n[1] = pu[2]*pv[0]-pu[0]*pv[2];
1281         n[2] = pu[0]*pv[1]-pu[1]*pv[0];
1282         t = 1.0f/sqrtf(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
1283         n[0] *= t;
1284         n[1] *= t;
1285         n[2] *= t;
1286         pp->pp[3*o+0] = p[0];
1287         pp->pp[3*o+1] = p[1];
1288         pp->pp[3*o+2] = p[2];
1289         pp->pn[3*o+0] = n[0];
1290         pp->pn[3*o+1] = n[1];
1291         pp->pn[3*o+2] = n[2];
1292       }
1293     }
1294   }
1295   else /* pp->appearance == APPEARANCE_DIRECTION_BANDS */
1296   {
1297     for (j=0; j<=numu; j++)
1298     {
1299       if ((j & (NUMB-1)) >= NUMB/2+1)
1300         continue;
1301       for (i=0; i<=numv; i++)
1302       {
1303         o = i*(numu+1)+j;
1304         u = -ur*j/numu+umin;
1305         v = vr*i/numv+vmin;
1306         if (pp->change_colors)
1307         {
1308           /* Compute the colors dynamically. */
1309           if (pp->colors == COLORS_DIRECTION)
1310             color(pp,2.0*M_PI-fmod(2.0*u,2.0*M_PI),matc,&pp->col[4*o]);
1311           else if (pp->colors == COLORS_DISTANCE)
1312             color(pp,v*(5.0/6.0),matc,&pp->col[4*o]);
1313         }
1314         if (g & 1)
1315           v = 0.5f*(float)M_PI-0.25f*v;
1316         else
1317           v = 0.5f*(float)M_PI-0.5f*v;
1318         sqrt2og = (float)M_SQRT2/g;
1319         h1m1og = 0.5f*(1.0f-1.0f/g);
1320         gm1 = g-1.0f;
1321         cu = cosf(u);
1322         su = sinf(u);
1323         cgu = cosf(g*u);
1324         sgu = sinf(g*u);
1325         cgm1u = cosf(gm1*u);
1326         sgm1u = sinf(gm1*u);
1327         cv = cosf(v);
1328         c2v = cosf(2.0f*v);
1329         s2v = sinf(2.0f*v);
1330         cv2 = cv*cv;
1331         nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;
1332         nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;
1333         nomux = -sqrt2og*cv2*gm1*sgm1u-h1m1og*s2v*su;
1334         nomuy = sqrt2og*cv2*gm1*cgm1u-h1m1og*s2v*cu;
1335         nomvx = -sqrt2og*s2v*cgm1u+2.0f*h1m1og*c2v*cu;
1336         nomvy = -sqrt2og*s2v*sgm1u-2.0f*h1m1og*c2v*su;
1337         den = 1.0f/(1.0f-0.5f*(float)M_SQRT2*d*s2v*sgu);
1338         den2 = den*den;
1339         denu = 0.5f*(float)M_SQRT2*d*g*cgu*s2v;
1340         denv = (float)M_SQRT2*d*sgu*c2v;
1341         xx[0] = nomx*den;
1342         xx[1] = nomy*den;
1343         xx[2] = cv2*den-oz;
1344         /* Avoid degenerate tangential plane basis vectors. */
1345         if (0.5f*(float)M_PI-fabsf(v) < 10.0f*(float)FLT_EPSILON)
1346         {
1347           if (0.5f*(float)M_PI-v < 10.0f*(float)FLT_EPSILON)
1348             v = 0.5f*(float)M_PI-10.0f*(float)FLT_EPSILON;
1349           else
1350             v = -0.5f*(float)M_PI+10.0f*(float)FLT_EPSILON;
1351           cv = cosf(v);
1352           c2v = cosf(2.0f*v);
1353           s2v = sinf(2.0f*v);
1354           cv2 = cv*cv;
1355           nomx = sqrt2og*cv2*cgm1u+h1m1og*s2v*cu;
1356           nomy = sqrt2og*cv2*sgm1u-h1m1og*s2v*su;
1357           nomux = -sqrt2og*cv2*gm1*sgm1u-h1m1og*s2v*su;
1358           nomuy = sqrt2og*cv2*gm1*cgm1u-h1m1og*s2v*cu;
1359           nomvx = -sqrt2og*s2v*cgm1u+2.0f*h1m1og*c2v*cu;
1360           nomvy = -sqrt2og*s2v*sgm1u-2.0f*h1m1og*c2v*su;
1361           den = 1.0f/(1.0f-0.5f*(float)M_SQRT2*d*s2v*sgu);
1362           den2 = den*den;
1363           denu = 0.5f*(float)M_SQRT2*d*g*cgu*s2v;
1364           denv = (float)M_SQRT2*d*sgu*c2v;
1365         }
1366         xxu[0] = nomux*den+nomx*denu*den2;
1367         xxu[1] = nomuy*den+nomy*denu*den2;
1368         xxu[2] = cv2*denu*den2;
1369         xxv[0] = nomvx*den+nomx*denv*den2;
1370         xxv[1] = nomvy*den+nomy*denv*den2;
1371         xxv[2] = -s2v*den+cv2*denv*den2;
1372         for (l=0; l<3; l++)
1373         {
1374           r = 0.0f;
1375           s = 0.0f;
1376           t = 0.0f;
1377           for (m=0; m<3; m++)
1378           {
1379             r += mat[l][m]*xx[m];
1380             s += mat[l][m]*xxu[m];
1381             t += mat[l][m]*xxv[m];
1382           }
1383           p[l] = r*radius+pp->offset3d[l];
1384           pu[l] = s*radius;
1385           pv[l] = t*radius;
1386         }
1387         n[0] = pu[1]*pv[2]-pu[2]*pv[1];
1388         n[1] = pu[2]*pv[0]-pu[0]*pv[2];
1389         n[2] = pu[0]*pv[1]-pu[1]*pv[0];
1390         t = 1.0f/sqrtf(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
1391         n[0] *= t;
1392         n[1] *= t;
1393         n[2] *= t;
1394         pp->pp[3*o+0] = p[0];
1395         pp->pp[3*o+1] = p[1];
1396         pp->pp[3*o+2] = p[2];
1397         pp->pn[3*o+0] = n[0];
1398         pp->pn[3*o+1] = n[1];
1399         pp->pn[3*o+2] = n[2];
1400       }
1401     }
1402   }
1403
1404   if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
1405   {
1406     for (i=0; i<numv; i++)
1407     {
1408       if (pp->appearance == APPEARANCE_DISTANCE_BANDS &&
1409           ((i & (NUMB-1)) >= NUMB/4) && ((i & (NUMB-1)) < 3*NUMB/4))
1410         continue;
1411       if (pp->display_mode == DISP_WIREFRAME)
1412         glBegin(GL_QUAD_STRIP);
1413       else
1414         glBegin(GL_TRIANGLE_STRIP);
1415       for (j=0; j<=numu; j++)
1416       {
1417         for (k=0; k<=1; k++)
1418         {
1419           l = i+k;
1420           m = j;
1421           o = l*(numu+1)+m;
1422           glTexCoord2fv(&pp->tex[2*o]);
1423           if (pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
1424           {
1425             glColor3fv(&pp->col[4*o]);
1426             glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
1427                          &pp->col[4*o]);
1428           }
1429           glNormal3fv(&pp->pn[3*o]);
1430           glVertex3fv(&pp->pp[3*o]);
1431         }
1432       }
1433       glEnd();
1434     }
1435     polys = 2*numv*(numu+1);
1436     if (pp->appearance == APPEARANCE_DISTANCE_BANDS)
1437       polys /= 2;
1438   }
1439   else /* pp->appearance == APPEARANCE_DIRECTION_BANDS */
1440   {
1441     for (j=0; j<numu; j++)
1442     {
1443       if ((j & (NUMB-1)) >= NUMB/2)
1444         continue;
1445       if (pp->display_mode == DISP_WIREFRAME)
1446         glBegin(GL_QUAD_STRIP);
1447       else
1448         glBegin(GL_TRIANGLE_STRIP);
1449       for (i=0; i<=numv; i++)
1450       {
1451         for (k=0; k<=1; k++)
1452         {
1453           l = i;
1454           m = j+k;
1455           o = l*(numu+1)+m;
1456           glTexCoord2fv(&pp->tex[2*o]);
1457           if (pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
1458           {
1459             glColor3fv(&pp->col[4*o]);
1460             glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,
1461                          &pp->col[4*o]);
1462           }
1463           glNormal3fv(&pp->pn[3*o]);
1464           glVertex3fv(&pp->pp[3*o]);
1465         }
1466       }
1467       glEnd();
1468     }
1469     polys = numu*(numv+1);
1470   }
1471
1472   return polys;
1473 }
1474
1475
1476 #ifdef HAVE_GLSL
1477
1478 /* Draw a 3d immersion of the projective plane using OpenGL's programmable
1479    functionality. */
1480 static int roman_boy_pf(ModeInfo *mi, double umin, double umax,
1481                         double vmin, double vmax, int numu, int numv)
1482 {
1483   static const GLfloat light_model_ambient[]    = { 0.2, 0.2, 0.2, 1.0 };
1484   static const GLfloat light_ambient[]          = { 0.0, 0.0, 0.0, 1.0 };
1485   static const GLfloat light_diffuse[]          = { 1.0, 1.0, 1.0, 1.0 };
1486   static const GLfloat light_specular[]         = { 1.0, 1.0, 1.0, 1.0 };
1487   static const GLfloat light_position[]         = { 1.0, 1.0, 1.0, 0.0 };
1488   static const GLfloat mat_specular[]           = { 1.0, 1.0, 1.0, 1.0 };
1489   static const GLfloat mat_diff_red[]           = { 1.0, 0.0, 0.0, 1.0 };
1490   static const GLfloat mat_diff_green[]         = { 0.0, 1.0, 0.0, 1.0 };
1491   static const GLfloat mat_diff_oneside[]       = { 0.9, 0.4, 0.3, 1.0 };
1492   static const GLfloat mat_diff_trans_red[]     = { 1.0, 0.0, 0.0, 0.7 };
1493   static const GLfloat mat_diff_trans_green[]   = { 0.0, 1.0, 0.0, 0.7 };
1494   static const GLfloat mat_diff_trans_oneside[] = { 0.9, 0.4, 0.3, 0.7 };
1495   static const GLfloat mat_diff_white[]         = { 1.0, 1.0, 1.0, 1.0 };
1496   GLfloat light_direction[3], half_vector[3], len;
1497   GLfloat p_mat[16], mv_mat[16], rot_mat[16];
1498   float mat_diff_dyn[4], mat_diff_dyn_compl[4];
1499   float mat[3][3], matc[3][3];
1500   int i, j, k, l, m, o, g;
1501   float u, v, ur, vr, oz;
1502   float r;
1503   float d, dd, radius;
1504   float qu[4], r1[3][3], r2[3][3];
1505   GLsizeiptr index_offset;
1506   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
1507   int polys;
1508
1509   if (!pp->use_shaders)
1510     return 0;
1511
1512   g = pp->g;
1513   dd = pp->dd;
1514   d = ((6.0f*dd-15.0f)*dd+10.0f)*dd*dd*dd;
1515   r = 1.0f+d*d*(1.0f/2.0f+d*d*(1.0f/6.0f+d*d*(1.0f/3.0f)));
1516   radius = 1.0f/r;
1517   oz = 0.5f*r;
1518
1519   if (!pp->buffers_initialized)
1520   {
1521     /* The u and v values need to be computed once (or each time the value
1522        of appearance changes, once we support that). */
1523     ur = umax-umin;
1524     vr = vmax-vmin;
1525     for (i=0; i<=numv; i++)
1526     {
1527       for (j=0; j<=numu; j++)
1528       {
1529         o = i*(numu+1)+j;
1530         if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
1531           u = ur*j/numu+umin;
1532         else
1533           u = -ur*j/numu+umin;
1534         v = vr*i/numv+vmin;
1535         if (g & 1)
1536           v = 0.5f*(float)M_PI-0.25f*v;
1537         else
1538           v = 0.5f*(float)M_PI-0.5f*v;
1539         pp->uv[2*o+0] = u;
1540         pp->uv[2*o+1] = v;
1541       }
1542     }
1543     glBindBuffer(GL_ARRAY_BUFFER,pp->vertex_uv_buffer);
1544     glBufferData(GL_ARRAY_BUFFER,2*(numu+1)*(numv+1)*sizeof(GLfloat),
1545                  pp->uv,GL_STATIC_DRAW);
1546     glBindBuffer(GL_ARRAY_BUFFER,0);
1547
1548     glBindBuffer(GL_ARRAY_BUFFER,pp->vertex_t_buffer);
1549     glBufferData(GL_ARRAY_BUFFER,2*(numu+1)*(numv+1)*sizeof(GLfloat),
1550                  pp->tex,GL_STATIC_DRAW);
1551     glBindBuffer(GL_ARRAY_BUFFER,0);
1552
1553     if (!pp->change_colors &&
1554         pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
1555     {
1556       glBindBuffer(GL_ARRAY_BUFFER,pp->color_buffer);
1557       glBufferData(GL_ARRAY_BUFFER,4*(numu+1)*(numv+1)*sizeof(GLfloat),
1558                    pp->col,GL_STATIC_DRAW);
1559       glBindBuffer(GL_ARRAY_BUFFER,0);
1560     }
1561
1562     /* The indices only need to be computed once (or each time the value of
1563        appearance changes, once we support that). */
1564     pp->ni = 0;
1565     pp->ne = 0;
1566     pp->nt = 0;
1567     if (pp->display_mode != DISP_WIREFRAME)
1568     {
1569       if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
1570       {
1571         for (i=0; i<numv; i++)
1572         {
1573           if (pp->appearance == APPEARANCE_DISTANCE_BANDS &&
1574               ((i & (NUMB-1)) >= NUMB/4) && ((i & (NUMB-1)) < 3*NUMB/4))
1575             continue;
1576           for (j=0; j<=numu; j++)
1577           {
1578             for (k=0; k<=1; k++)
1579             {
1580               l = i+k;
1581               m = j;
1582               o = l*(numu+1)+m;
1583               pp->indices[pp->ni++] = o;
1584             }
1585           }
1586           pp->ne++;
1587         }
1588         pp->nt = 2*(numu+1);
1589       }
1590       else /* pp->appearance == APPEARANCE_DIRECTION_BANDS */
1591       {
1592         for (j=0; j<numu; j++)
1593         {
1594           if ((j & (NUMB-1)) >= NUMB/2)
1595             continue;
1596           for (i=0; i<=numv; i++)
1597           {
1598             for (k=0; k<=1; k++)
1599             {
1600               l = i;
1601               m = j+k;
1602               o = l*(numu+1)+m;
1603               pp->indices[pp->ni++] = o;
1604             }
1605           }
1606           pp->ne++;
1607         }
1608         pp->nt = 2*(numv+1);
1609       }
1610     }
1611     else /* pp->display_mode == DISP_WIREFRAME */
1612     {
1613       if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
1614       {
1615         for (i=0; i<=numv; i++)
1616         {
1617           if (pp->appearance == APPEARANCE_DISTANCE_BANDS &&
1618               ((i & (NUMB-1)) > NUMB/4) && ((i & (NUMB-1)) < 3*NUMB/4))
1619             continue;
1620           if (pp->appearance == APPEARANCE_DISTANCE_BANDS &&
1621               ((i & (NUMB-1)) == NUMB/4))
1622           {
1623             for (j=0; j<numu; j++)
1624             {
1625               pp->indices[pp->ni++] = i*(numu+1)+j;
1626               pp->indices[pp->ni++] = i*(numu+1)+j+1;
1627             }
1628             continue;
1629           }
1630           for (j=0; j<numu; j++)
1631           {
1632             pp->indices[pp->ni++] = i*(numu+1)+j;
1633             pp->indices[pp->ni++] = i*(numu+1)+j+1;
1634             if (i < numv)
1635             {
1636               pp->indices[pp->ni++] = i*(numu+1)+j;
1637               pp->indices[pp->ni++] = (i+1)*(numu+1)+j;
1638             }
1639           }
1640         }
1641       }
1642       else /* pp->appearance == APPEARANCE_DIRECTION_BANDS */
1643       {
1644         for (j=0; j<numu; j++)
1645         {
1646           if ((j & (NUMB-1)) > NUMB/2)
1647             continue;
1648           if ((j & (NUMB-1)) == NUMB/2)
1649           {
1650             for (i=0; i<numv; i++)
1651             {
1652               pp->indices[pp->ni++] = i*(numu+1)+j;
1653               pp->indices[pp->ni++] = (i+1)*(numu+1)+j;
1654             }
1655             continue;
1656           }
1657           for (i=0; i<=numv; i++)
1658           {
1659             pp->indices[pp->ni++] = i*(numu+1)+j;
1660             pp->indices[pp->ni++] = i*(numu+1)+j+1;
1661             if (i < numv)
1662             {
1663               pp->indices[pp->ni++] = i*(numu+1)+j;
1664               pp->indices[pp->ni++] = (i+1)*(numu+1)+j;
1665             }
1666           }
1667         }
1668       }
1669       pp->ne = 1;
1670     }
1671     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,pp->indices_buffer);
1672     glBufferData(GL_ELEMENT_ARRAY_BUFFER,pp->ni*sizeof(GLuint),
1673                  pp->indices,GL_STATIC_DRAW);
1674     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
1675
1676     pp->buffers_initialized = True;
1677   }
1678
1679   if (pp->change_colors)
1680     rotateall(pp->rho,pp->sigma,pp->tau,matc);
1681
1682   if (pp->view == VIEW_WALK)
1683   {
1684     /* Compute the walk frame. */
1685     compute_walk_frame(pp,g,d,radius,oz,mat);
1686   }
1687   else
1688   {
1689     /* Compute the rotation that rotates the projective plane in 3D,
1690        including the trackball rotations. */
1691     rotateall(pp->alpha,pp->beta,pp->delta,r1);
1692
1693     gltrackball_get_quaternion(pp->trackball,qu);
1694     quat_to_rotmat(qu,r2);
1695
1696     mult_rotmat(r2,r1,mat);
1697   }
1698
1699   if (pp->change_colors &&
1700       (pp->colors == COLORS_DIRECTION || pp->colors == COLORS_DISTANCE))
1701   {
1702     ur = umax-umin;
1703     vr = vmax-vmin;
1704     for (i=0; i<=numv; i++)
1705     {
1706       for (j=0; j<=numu; j++)
1707       {
1708         o = i*(numu+1)+j;
1709         if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
1710           u = ur*j/numu+umin;
1711         else
1712           u = -ur*j/numu+umin;
1713         v = vr*i/numv+vmin;
1714         if (pp->colors == COLORS_DIRECTION)
1715           color(pp,2.0*M_PI-fmod(2.0*u,2.0*M_PI),matc,&pp->col[4*o]);
1716         else if (pp->colors == COLORS_DISTANCE)
1717           color(pp,v*(5.0/6.0),matc,&pp->col[4*o]);
1718       }
1719     }
1720   }
1721
1722   glUseProgram(pp->shader_program);
1723
1724   glUniform1i(pp->g_index,g);
1725   glUniform1f(pp->d_index,d);
1726
1727   glsl_Identity(p_mat);
1728   if (pp->projection == DISP_PERSPECTIVE || pp->view == VIEW_WALK)
1729   {
1730     if (pp->view == VIEW_WALK)
1731       glsl_Perspective(p_mat,60.0f,pp->aspect,0.01f,10.0f);
1732     else
1733       glsl_Perspective(p_mat,60.0f,pp->aspect,0.1f,10.0f);
1734   }
1735   else
1736   {
1737     if (pp->aspect >= 1.0)
1738       glsl_Orthographic(p_mat,-pp->aspect,pp->aspect,-1.0f,1.0f,
1739                         0.1f,10.0f);
1740     else
1741       glsl_Orthographic(p_mat,-1.0f,1.0f,-1.0f/pp->aspect,1.0f/pp->aspect,
1742                         0.1f,10.0f);
1743   }
1744   glUniformMatrix4fv(pp->mat_p_index,1,GL_FALSE,p_mat);
1745   glsl_Identity(rot_mat);
1746   for (i=0; i<3; i++)
1747     for (j=0; j<3; j++)
1748       rot_mat[GLSL__LINCOOR(i,j,4)] = mat[i][j];
1749   glsl_Identity(mv_mat);
1750   glsl_Translate(mv_mat,pp->offset3d[0],pp->offset3d[1],pp->offset3d[2]);
1751   glsl_Scale(mv_mat,radius,radius,radius);
1752   glsl_MultMatrix(mv_mat,rot_mat);
1753   glsl_Translate(mv_mat,0.0f,0.0f,-oz);
1754   glUniformMatrix4fv(pp->mat_mv_index,1,GL_FALSE,mv_mat);
1755
1756   len = sqrtf(light_position[0]*light_position[0]+
1757               light_position[1]*light_position[1]+
1758               light_position[2]*light_position[2]);
1759   light_direction[0] = light_position[0]/len;
1760   light_direction[1] = light_position[1]/len;
1761   light_direction[2] = light_position[2]/len;
1762   half_vector[0] = light_direction[0];
1763   half_vector[1] = light_direction[1];
1764   half_vector[2] = light_direction[2]+1.0f;
1765   len = sqrtf(half_vector[0]*half_vector[0]+
1766               half_vector[1]*half_vector[1]+
1767               half_vector[2]*half_vector[2]);
1768   half_vector[0] /= len;
1769   half_vector[1] /= len;
1770   half_vector[2] /= len;
1771
1772   glUniform4fv(pp->front_ambient_index,1,mat_diff_white);
1773   glUniform4fv(pp->front_diffuse_index,1,mat_diff_white);
1774   glUniform4fv(pp->back_ambient_index,1,mat_diff_white);
1775   glUniform4fv(pp->back_diffuse_index,1,mat_diff_white);
1776   glVertexAttrib4f(pp->color_index,1.0f,1.0f,1.0f,1.0f);
1777
1778   if (pp->display_mode == DISP_SURFACE)
1779   {
1780     glEnable(GL_DEPTH_TEST);
1781     glDepthFunc(GL_LESS);
1782     glDepthMask(GL_TRUE);
1783     glDisable(GL_BLEND);
1784     glUniform4fv(pp->glbl_ambient_index,1,light_model_ambient);
1785     glUniform4fv(pp->lt_ambient_index,1,light_ambient);
1786     glUniform4fv(pp->lt_diffuse_index,1,light_diffuse);
1787     glUniform4fv(pp->lt_specular_index,1,light_specular);
1788     glUniform3fv(pp->lt_direction_index,1,light_direction);
1789     glUniform3fv(pp->lt_halfvect_index,1,half_vector);
1790     glUniform4fv(pp->specular_index,1,mat_specular);
1791     glUniform1f(pp->shininess_index,50.0f);
1792     glUniform1i(pp->draw_lines_index,GL_FALSE);
1793   }
1794   else if (pp->display_mode == DISP_TRANSPARENT)
1795   {
1796     glDisable(GL_DEPTH_TEST);
1797     glDepthMask(GL_FALSE);
1798     glEnable(GL_BLEND);
1799     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
1800     glUniform4fv(pp->glbl_ambient_index,1,light_model_ambient);
1801     glUniform4fv(pp->lt_ambient_index,1,light_ambient);
1802     glUniform4fv(pp->lt_diffuse_index,1,light_diffuse);
1803     glUniform4fv(pp->lt_specular_index,1,light_specular);
1804     glUniform3fv(pp->lt_direction_index,1,light_direction);
1805     glUniform3fv(pp->lt_halfvect_index,1,half_vector);
1806     glUniform4fv(pp->specular_index,1,mat_specular);
1807     glUniform1f(pp->shininess_index,50.0f);
1808     glUniform1i(pp->draw_lines_index,GL_FALSE);
1809   }
1810   else  /* pp->display_mode == DISP_WIREFRAME */
1811   {
1812     glEnable(GL_DEPTH_TEST);
1813     glDepthFunc(GL_LESS);
1814     glDepthMask(GL_TRUE);
1815     glDisable(GL_BLEND);
1816     glUniform1i(pp->draw_lines_index,GL_TRUE);
1817   }
1818
1819   if (pp->marks)
1820     glEnable(GL_TEXTURE_2D);
1821   else
1822     glDisable(GL_TEXTURE_2D);
1823
1824   if (!pp->change_colors)
1825   {
1826     if (pp->colors == COLORS_ONESIDED)
1827     {
1828       if (pp->display_mode == DISP_TRANSPARENT)
1829       {
1830         glUniform4fv(pp->front_ambient_index,1,mat_diff_trans_oneside);
1831         glUniform4fv(pp->front_diffuse_index,1,mat_diff_trans_oneside);
1832         glUniform4fv(pp->back_ambient_index,1,mat_diff_trans_oneside);
1833         glUniform4fv(pp->back_diffuse_index,1,mat_diff_trans_oneside);
1834       }
1835       else if (pp->display_mode == DISP_SURFACE)
1836       {
1837         glUniform4fv(pp->front_ambient_index,1,mat_diff_oneside);
1838         glUniform4fv(pp->front_diffuse_index,1,mat_diff_oneside);
1839         glUniform4fv(pp->back_ambient_index,1,mat_diff_oneside);
1840         glUniform4fv(pp->back_diffuse_index,1,mat_diff_oneside);
1841       }
1842       else /* pp->display_mode == DISP_WIREFRAME */
1843       {
1844         glVertexAttrib4fv(pp->color_index,mat_diff_oneside);
1845       }
1846     }
1847     else if (pp->colors == COLORS_TWOSIDED)
1848     {
1849       if (pp->display_mode == DISP_TRANSPARENT)
1850       {
1851         glUniform4fv(pp->front_ambient_index,1,mat_diff_trans_red);
1852         glUniform4fv(pp->front_diffuse_index,1,mat_diff_trans_red);
1853         glUniform4fv(pp->back_ambient_index,1,mat_diff_trans_green);
1854         glUniform4fv(pp->back_diffuse_index,1,mat_diff_trans_green);
1855       }
1856       else if (pp->display_mode == DISP_SURFACE)
1857       {
1858         glUniform4fv(pp->front_ambient_index,1,mat_diff_red);
1859         glUniform4fv(pp->front_diffuse_index,1,mat_diff_red);
1860         glUniform4fv(pp->back_ambient_index,1,mat_diff_green);
1861         glUniform4fv(pp->back_diffuse_index,1,mat_diff_green);
1862       }
1863       else /* pp->display_mode == DISP_WIREFRAME */
1864       {
1865         glVertexAttrib4fv(pp->color_index,mat_diff_red);
1866       }
1867     }
1868   }
1869   else /* pp->change_colors */
1870   {
1871     color(pp,0.0,matc,mat_diff_dyn);
1872     if (pp->colors == COLORS_ONESIDED)
1873     {
1874       if (pp->display_mode == DISP_TRANSPARENT ||
1875           pp->display_mode == DISP_SURFACE)
1876       {
1877         glUniform4fv(pp->front_ambient_index,1,mat_diff_dyn);
1878         glUniform4fv(pp->front_diffuse_index,1,mat_diff_dyn);
1879         glUniform4fv(pp->back_ambient_index,1,mat_diff_dyn);
1880         glUniform4fv(pp->back_diffuse_index,1,mat_diff_dyn);
1881       }
1882       else /* pp->display_mode == DISP_WIREFRAME */
1883       {
1884         glVertexAttrib4fv(pp->color_index,mat_diff_dyn);
1885       }
1886     }
1887     else if (pp->colors == COLORS_TWOSIDED)
1888     {
1889       if (pp->display_mode == DISP_TRANSPARENT ||
1890           pp->display_mode == DISP_SURFACE)
1891       {
1892         mat_diff_dyn_compl[0] = 1.0f-mat_diff_dyn[0];
1893         mat_diff_dyn_compl[1] = 1.0f-mat_diff_dyn[1];
1894         mat_diff_dyn_compl[2] = 1.0f-mat_diff_dyn[2];
1895         mat_diff_dyn_compl[3] = mat_diff_dyn[3];
1896         glUniform4fv(pp->front_ambient_index,1,mat_diff_dyn);
1897         glUniform4fv(pp->front_diffuse_index,1,mat_diff_dyn);
1898         glUniform4fv(pp->back_ambient_index,1,mat_diff_dyn_compl);
1899         glUniform4fv(pp->back_diffuse_index,1,mat_diff_dyn_compl);
1900       }
1901       else /* pp->display_mode == DISP_WIREFRAME */
1902       {
1903         glVertexAttrib4fv(pp->color_index,mat_diff_dyn);
1904       }
1905     }
1906   }
1907
1908   glActiveTexture(GL_TEXTURE0);
1909   glBindTexture(GL_TEXTURE_2D,pp->tex_name);
1910   glUniform1i(pp->texture_sampler_index,0);
1911   glUniform1i(pp->bool_textures_index,marks);
1912
1913   glEnableVertexAttribArray(pp->vertex_uv_index);
1914   glBindBuffer(GL_ARRAY_BUFFER,pp->vertex_uv_buffer);
1915   glVertexAttribPointer(pp->vertex_uv_index,2,GL_FLOAT,GL_FALSE,0,0);
1916
1917   glEnableVertexAttribArray(pp->vertex_t_index);
1918   glBindBuffer(GL_ARRAY_BUFFER,pp->vertex_t_buffer);
1919   glVertexAttribPointer(pp->vertex_t_index,2,GL_FLOAT,GL_FALSE,0,0);
1920
1921   if (pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
1922   {
1923     glEnableVertexAttribArray(pp->color_index);
1924     glBindBuffer(GL_ARRAY_BUFFER,pp->color_buffer);
1925     if (pp->change_colors)
1926       glBufferData(GL_ARRAY_BUFFER,4*(numu+1)*(numv+1)*sizeof(GLfloat),
1927                    pp->col,GL_STREAM_DRAW);
1928     glVertexAttribPointer(pp->color_index,4,GL_FLOAT,GL_FALSE,0,0);
1929   }
1930
1931   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,pp->indices_buffer);
1932
1933   if (pp->display_mode != DISP_WIREFRAME)
1934   {
1935     for (i=0; i<pp->ne; i++)
1936     {
1937       index_offset = pp->nt*i*sizeof(GLuint);
1938       glDrawElements(GL_TRIANGLE_STRIP,pp->nt,GL_UNSIGNED_INT,
1939                      (const GLvoid *)index_offset);
1940     }
1941   }
1942   else /* pp->display_mode == DISP_WIREFRAME */
1943   {
1944     glLineWidth(1.0f);
1945     index_offset = 0;
1946     glDrawElements(GL_LINES,pp->ni,GL_UNSIGNED_INT,
1947                    (const void *)index_offset);
1948   }
1949
1950   glDisableVertexAttribArray(pp->vertex_uv_index);
1951   glDisableVertexAttribArray(pp->vertex_t_index);
1952   if (pp->colors != COLORS_ONESIDED && pp->colors != COLORS_TWOSIDED)
1953     glDisableVertexAttribArray(pp->color_index);
1954   glBindBuffer(GL_ARRAY_BUFFER,0);
1955   glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
1956
1957   glUseProgram(0);
1958
1959   if (pp->appearance != APPEARANCE_DIRECTION_BANDS)
1960   {
1961     polys = 2*numv*(numu+1);
1962     if (pp->appearance == APPEARANCE_DISTANCE_BANDS)
1963       polys /= 2;
1964   }
1965   else /* pp->appearance == APPEARANCE_DIRECTION_BANDS */
1966   {
1967     polys = numu*(numv+1);
1968   }
1969
1970   return polys;
1971 }
1972
1973 #endif /* HAVE_GLSL */
1974
1975
1976 /* Generate a texture image that shows the orientation reversal. */
1977 static void gen_texture(ModeInfo *mi)
1978 {
1979   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
1980
1981   glPixelStorei(GL_UNPACK_ALIGNMENT,1);
1982   glGenTextures(1,&pp->tex_name);
1983   glBindTexture(GL_TEXTURE_2D,pp->tex_name);
1984   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
1985   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
1986   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
1987   glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
1988   glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
1989   glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,TEX_DIMENSION,TEX_DIMENSION,0,
1990                GL_LUMINANCE,GL_UNSIGNED_BYTE,texture);
1991 }
1992
1993
1994 #ifdef HAVE_GLSL
1995
1996 static void init_glsl(ModeInfo *mi)
1997 {
1998   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
1999   GLint gl_major, gl_minor, glsl_major, glsl_minor;
2000   GLboolean gl_gles3;
2001   const GLchar *vertex_shader_source[3];
2002   const GLchar *fragment_shader_source[4];
2003
2004   pp->uv = calloc(2*pp->g*(NUMU+1)*(NUMV+1),sizeof(float));
2005   pp->indices = calloc(4*pp->g*(NUMU+1)*(NUMV+1),sizeof(float));
2006
2007   /* Determine whether to use shaders to render the projective plane. */
2008   pp->use_shaders = False;
2009   pp->buffers_initialized = False;
2010   pp->shader_program = 0;
2011   pp->ni = 0;
2012   pp->ne = 0;
2013   pp->nt = 0;
2014
2015   if (!glsl_GetGlAndGlslVersions(&gl_major,&gl_minor,&glsl_major,&glsl_minor,
2016                                  &gl_gles3))
2017     return;
2018   if (!gl_gles3)
2019   {
2020     if (gl_major < 3 ||
2021         (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 30)))
2022     {
2023       if ((gl_major < 2 || (gl_major == 2 && gl_minor < 1)) ||
2024           (glsl_major < 1 || (glsl_major == 1 && glsl_minor < 20)))
2025         return;
2026       /* We have at least OpenGL 2.1 and at least GLSL 1.20. */
2027       vertex_shader_source[0] = shader_version_2_1;
2028       vertex_shader_source[1] = vertex_shader_attribs_2_1;
2029       vertex_shader_source[2] = vertex_shader_main;
2030       fragment_shader_source[0] = shader_version_2_1;
2031       fragment_shader_source[1] = fragment_shader_attribs_2_1;
2032       fragment_shader_source[2] = fragment_shader_main;
2033       fragment_shader_source[3] = fragment_shader_out_2_1;
2034     }
2035     else
2036     {
2037       /* We have at least OpenGL 3.0 and at least GLSL 1.30. */
2038       vertex_shader_source[0] = shader_version_3_0;
2039       vertex_shader_source[1] = vertex_shader_attribs_3_0;
2040       vertex_shader_source[2] = vertex_shader_main;
2041       fragment_shader_source[0] = shader_version_3_0;
2042       fragment_shader_source[1] = fragment_shader_attribs_3_0;
2043       fragment_shader_source[2] = fragment_shader_main;
2044       fragment_shader_source[3] = fragment_shader_out_3_0;
2045     }
2046   }
2047   else /* gl_gles3 */
2048   {
2049     if (gl_major < 3 || glsl_major < 3)
2050       return;
2051     /* We have at least OpenGL ES 3.0 and at least GLSL ES 3.0. */
2052     vertex_shader_source[0] = shader_version_3_0_es;
2053     vertex_shader_source[1] = vertex_shader_attribs_3_0;
2054     vertex_shader_source[2] = vertex_shader_main;
2055     fragment_shader_source[0] = shader_version_3_0_es;
2056     fragment_shader_source[1] = fragment_shader_attribs_3_0;
2057     fragment_shader_source[2] = fragment_shader_main;
2058     fragment_shader_source[3] = fragment_shader_out_3_0;
2059   }
2060   if (!glsl_CompileAndLinkShaders(3,vertex_shader_source,
2061                                   4,fragment_shader_source,
2062                                   &pp->shader_program))
2063     return;
2064   pp->vertex_uv_index = glGetAttribLocation(pp->shader_program,"VertexUV");
2065   pp->vertex_t_index = glGetAttribLocation(pp->shader_program,"VertexT");
2066   pp->color_index = glGetAttribLocation(pp->shader_program,"VertexColor");
2067   if (pp->vertex_uv_index == -1 || pp->vertex_t_index == -1 ||
2068       pp->color_index == -1)
2069   {
2070     glDeleteProgram(pp->shader_program);
2071     return;
2072   }
2073   pp->mat_mv_index = glGetUniformLocation(pp->shader_program,
2074                                           "MatModelView");
2075   pp->mat_p_index = glGetUniformLocation(pp->shader_program,
2076                                          "MatProj");
2077   pp->g_index = glGetUniformLocation(pp->shader_program,
2078                                      "G");
2079   pp->d_index = glGetUniformLocation(pp->shader_program,
2080                                      "D");
2081   pp->bool_textures_index = glGetUniformLocation(pp->shader_program,
2082                                                  "BoolTextures");
2083   pp->draw_lines_index = glGetUniformLocation(pp->shader_program,
2084                                               "DrawLines");
2085   pp->glbl_ambient_index = glGetUniformLocation(pp->shader_program,
2086                                                 "LtGlblAmbient");
2087   pp->lt_ambient_index = glGetUniformLocation(pp->shader_program,
2088                                               "LtAmbient");
2089   pp->lt_diffuse_index = glGetUniformLocation(pp->shader_program,
2090                                               "LtDiffuse");
2091   pp->lt_specular_index = glGetUniformLocation(pp->shader_program,
2092                                                "LtSpecular");
2093   pp->lt_direction_index = glGetUniformLocation(pp->shader_program,
2094                                                 "LtDirection");
2095   pp->lt_halfvect_index = glGetUniformLocation(pp->shader_program,
2096                                                "LtHalfVector");
2097   pp->front_ambient_index = glGetUniformLocation(pp->shader_program,
2098                                                  "MatFrontAmbient");
2099   pp->back_ambient_index = glGetUniformLocation(pp->shader_program,
2100                                                 "MatBackAmbient");
2101   pp->front_diffuse_index = glGetUniformLocation(pp->shader_program,
2102                                                  "MatFrontDiffuse");
2103   pp->back_diffuse_index = glGetUniformLocation(pp->shader_program,
2104                                                 "MatBackDiffuse");
2105   pp->specular_index = glGetUniformLocation(pp->shader_program,
2106                                             "MatSpecular");
2107   pp->shininess_index = glGetUniformLocation(pp->shader_program,
2108                                              "MatShininess");
2109   pp->texture_sampler_index = glGetUniformLocation(pp->shader_program,
2110                                                    "TextureSampler");
2111   if (pp->mat_mv_index == -1 || pp->mat_p_index == -1 ||
2112       pp->g_index == -1 || pp->d_index == -1 ||
2113       pp->bool_textures_index == -1 || pp->draw_lines_index == -1 ||
2114       pp->glbl_ambient_index == -1 || pp->lt_ambient_index == -1 ||
2115       pp->lt_diffuse_index == -1 || pp->lt_specular_index == -1 ||
2116       pp->lt_direction_index == -1 || pp->lt_halfvect_index == -1 ||
2117       pp->front_ambient_index == -1 || pp->back_ambient_index == -1 ||
2118       pp->front_diffuse_index == -1 || pp->back_diffuse_index == -1 ||
2119       pp->specular_index == -1 || pp->shininess_index == -1 ||
2120       pp->texture_sampler_index == -1)
2121   {
2122     glDeleteProgram(pp->shader_program);
2123     return;
2124   }
2125
2126   glGenBuffers(1,&pp->vertex_uv_buffer);
2127   glGenBuffers(1,&pp->vertex_t_buffer);
2128   glGenBuffers(1,&pp->color_buffer);
2129   glGenBuffers(1,&pp->indices_buffer);
2130
2131   pp->use_shaders = True;
2132 }
2133
2134 #endif /* HAVE_GLSL */
2135
2136
2137 static void init(ModeInfo *mi)
2138 {
2139   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
2140
2141   if (deform_speed == 0.0)
2142     deform_speed = 10.0;
2143
2144   if (init_deform < 0.0)
2145     init_deform = 0.0;
2146   if (init_deform > 1000.0)
2147     init_deform = 1000.0;
2148
2149   if (walk_speed == 0.0)
2150     walk_speed = 20.0;
2151
2152   if (pp->view == VIEW_TURN)
2153   {
2154     pp->alpha = frand(360.0);
2155     pp->beta = frand(360.0);
2156     pp->delta = frand(360.0);
2157   }
2158   else
2159   {
2160     pp->alpha = 0.0;
2161     pp->beta = 0.0;
2162     pp->delta = 0.0;
2163   }
2164   pp->umove = frand(2.0*M_PI);
2165   pp->vmove = frand(2.0*M_PI);
2166   pp->dumove = 0.0;
2167   pp->dvmove = 0.0;
2168   pp->side = 1;
2169   if (sin(walk_direction*M_PI/180.0) >= 0.0)
2170     pp->dir = 1;
2171   else
2172     pp->dir = -1;
2173
2174   pp->dd = init_deform*0.001;
2175   pp->defdir = -1;
2176
2177   pp->rho = frand(360.0);
2178   pp->sigma = frand(360.0);
2179   pp->tau = frand(360.0);
2180
2181   pp->offset3d[0] = 0.0;
2182   pp->offset3d[1] = 0.0;
2183   pp->offset3d[2] = -1.8;
2184
2185   pp->pp = calloc(3*pp->g*(NUMU+1)*(NUMV+1),sizeof(float));
2186   pp->pn = calloc(3*pp->g*(NUMU+1)*(NUMV+1),sizeof(float));
2187   pp->col = calloc(4*pp->g*(NUMU+1)*(NUMV+1),sizeof(float));
2188   pp->tex = calloc(2*pp->g*(NUMU+1)*(NUMV+1),sizeof(float));
2189
2190   gen_texture(mi);
2191   setup_roman_boy_color_texture(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,pp->g*NUMU,NUMV);
2192
2193 #ifdef HAVE_GLSL
2194   init_glsl(mi);
2195 #endif /* HAVE_GLSL */
2196
2197 #ifdef HAVE_ANDROID
2198   /* glPolygonMode(...,GL_LINE) is not supported for an OpenGL ES 1.1
2199      context. */
2200   if (!pp->use_shaders && pp->display_mode == DISP_WIREFRAME)
2201     pp->display_mode = DISP_SURFACE;
2202 #endif /* HAVE_GLSL */
2203 }
2204
2205
2206 /* Redisplay the projective plane. */
2207 static void display_romanboy(ModeInfo *mi)
2208 {
2209   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
2210
2211   if (!pp->button_pressed)
2212   {
2213     if (deform)
2214     {
2215       pp->dd += pp->defdir*deform_speed*0.001;
2216       if (pp->dd < 0.0)
2217       {
2218         pp->dd = -pp->dd;
2219         pp->defdir = -pp->defdir;
2220       }
2221       if (pp->dd > 1.0)
2222       {
2223         pp->dd = 2.0-pp->dd;
2224         pp->defdir = -pp->defdir;
2225       }
2226     }
2227     if (pp->view == VIEW_TURN)
2228     {
2229       pp->alpha += speed_x * pp->speed_scale;
2230       if (pp->alpha >= 360.0)
2231         pp->alpha -= 360.0;
2232       pp->beta += speed_y * pp->speed_scale;
2233       if (pp->beta >= 360.0)
2234         pp->beta -= 360.0;
2235       pp->delta += speed_z * pp->speed_scale;
2236       if (pp->delta >= 360.0)
2237         pp->delta -= 360.0;
2238     }
2239     if (pp->view == VIEW_WALK)
2240     {
2241       pp->dvmove = (pp->dir*sin(walk_direction*M_PI/180.0)*
2242                     walk_speed*M_PI/4096.0);
2243       pp->vmove += pp->dvmove;
2244       if (pp->vmove > 2.0*M_PI)
2245       {
2246         pp->vmove = 4.0*M_PI-pp->vmove;
2247         pp->umove = pp->umove-M_PI;
2248         if (pp->umove < 0.0)
2249           pp->umove += 2.0*M_PI;
2250         pp->side = -pp->side;
2251         pp->dir = -pp->dir;
2252         pp->dvmove = -pp->dvmove;
2253       }
2254       if (pp->vmove < 0.0)
2255       {
2256         pp->vmove = -pp->vmove;
2257         pp->umove = pp->umove-M_PI;
2258         if (pp->umove < 0.0)
2259           pp->umove += 2.0*M_PI;
2260         pp->dir = -pp->dir;
2261         pp->dvmove = -pp->dvmove;
2262       }
2263       pp->dumove = cos(walk_direction*M_PI/180.0)*walk_speed*M_PI/4096.0;
2264       pp->umove += pp->dumove;
2265       if (pp->umove >= 2.0*M_PI)
2266         pp->umove -= 2.0*M_PI;
2267       if (pp->umove < 0.0)
2268         pp->umove += 2.0*M_PI;
2269     }
2270     if (pp->change_colors)
2271     {
2272       pp->rho += DRHO;
2273       if (pp->rho >= 360.0)
2274         pp->rho -= 360.0;
2275       pp->sigma += DSIGMA;
2276       if (pp->sigma >= 360.0)
2277         pp->sigma -= 360.0;
2278       pp->tau += DTAU;
2279       if (pp->tau >= 360.0)
2280         pp->tau -= 360.0;
2281     }
2282   }
2283
2284   gltrackball_rotate(pp->trackball);
2285 #ifdef HAVE_GLSL
2286   if (pp->use_shaders)
2287     mi->polygon_count = roman_boy_pf(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,
2288                                      pp->g*NUMU,NUMV);
2289   else
2290 #endif /* HAVE_GLSL */
2291     mi->polygon_count = roman_boy_ff(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,
2292                                      pp->g*NUMU,NUMV);
2293 }
2294
2295
2296 ENTRYPOINT void reshape_romanboy(ModeInfo *mi, int width, int height)
2297 {
2298   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
2299
2300   pp->WindW = (GLint)width;
2301   pp->WindH = (GLint)height;
2302   glViewport(0,0,width,height);
2303   pp->aspect = (GLfloat)width/(GLfloat)height;
2304 }
2305
2306
2307 ENTRYPOINT Bool romanboy_handle_event(ModeInfo *mi, XEvent *event)
2308 {
2309   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
2310
2311   if (event->xany.type == ButtonPress && event->xbutton.button == Button1)
2312   {
2313     pp->button_pressed = True;
2314     gltrackball_start(pp->trackball, event->xbutton.x, event->xbutton.y,
2315                       MI_WIDTH(mi), MI_HEIGHT(mi));
2316     return True;
2317   }
2318   else if (event->xany.type == ButtonRelease &&
2319            event->xbutton.button == Button1)
2320   {
2321     pp->button_pressed = False;
2322     gltrackball_stop(pp->trackball);
2323     return True;
2324   }
2325   else if (event->xany.type == MotionNotify && pp->button_pressed)
2326   {
2327     gltrackball_track(pp->trackball, event->xmotion.x, event->xmotion.y,
2328                       MI_WIDTH(mi), MI_HEIGHT(mi));
2329     return True;
2330   }
2331
2332   return False;
2333 }
2334
2335
2336 /*
2337  *-----------------------------------------------------------------------------
2338  *-----------------------------------------------------------------------------
2339  *    Xlock hooks.
2340  *-----------------------------------------------------------------------------
2341  *-----------------------------------------------------------------------------
2342  */
2343
2344 /*
2345  *-----------------------------------------------------------------------------
2346  *    Initialize romanboy.  Called each time the window changes.
2347  *-----------------------------------------------------------------------------
2348  */
2349
2350 ENTRYPOINT void init_romanboy(ModeInfo *mi)
2351 {
2352   romanboystruct *pp;
2353
2354   MI_INIT (mi, romanboy);
2355   pp = &romanboy[MI_SCREEN(mi)];
2356
2357   if (surface_order < 2)
2358     pp->g = 2;
2359   else if (surface_order > 9)
2360     pp->g = 9;
2361   else
2362     pp->g = surface_order;
2363
2364   pp->trackball = gltrackball_init(False);
2365   pp->button_pressed = False;
2366
2367   /* Set the display mode. */
2368   if (!strcasecmp(mode,"random"))
2369   {
2370     pp->display_mode = random() % NUM_DISPLAY_MODES;
2371   }
2372   else if (!strcasecmp(mode,"wireframe"))
2373   {
2374     pp->display_mode = DISP_WIREFRAME;
2375   }
2376   else if (!strcasecmp(mode,"surface"))
2377   {
2378     pp->display_mode = DISP_SURFACE;
2379   }
2380   else if (!strcasecmp(mode,"transparent"))
2381   {
2382     pp->display_mode = DISP_TRANSPARENT;
2383   }
2384   else
2385   {
2386     pp->display_mode = random() % NUM_DISPLAY_MODES;
2387   }
2388
2389   pp->marks = marks;
2390
2391   /* Orientation marks don't make sense in wireframe mode. */
2392   if (pp->display_mode == DISP_WIREFRAME)
2393     pp->marks = False;
2394
2395   /* Set the appearance. */
2396   if (!strcasecmp(appear,"random"))
2397   {
2398     pp->appearance = random() % NUM_APPEARANCES;
2399   }
2400   else if (!strcasecmp(appear,"solid"))
2401   {
2402     pp->appearance = APPEARANCE_SOLID;
2403   }
2404   else if (!strcasecmp(appear,"distance-bands"))
2405   {
2406     pp->appearance = APPEARANCE_DISTANCE_BANDS;
2407   }
2408   else if (!strcasecmp(appear,"direction-bands"))
2409   {
2410     pp->appearance = APPEARANCE_DIRECTION_BANDS;
2411   }
2412   else
2413   {
2414     pp->appearance = random() % NUM_APPEARANCES;
2415   }
2416
2417   /* Set the color mode. */
2418   if (!strcasecmp(color_mode,"random"))
2419   {
2420     pp->colors = random() % NUM_COLORS;
2421   }
2422   else if (!strcasecmp(color_mode,"one-sided"))
2423   {
2424     pp->colors = COLORS_ONESIDED;
2425   }
2426   else if (!strcasecmp(color_mode,"two-sided"))
2427   {
2428     pp->colors = COLORS_TWOSIDED;
2429   }
2430   else if (!strcasecmp(color_mode,"distance"))
2431   {
2432     pp->colors = COLORS_DISTANCE;
2433   }
2434   else if (!strcasecmp(color_mode,"direction"))
2435   {
2436     pp->colors = COLORS_DIRECTION;
2437   }
2438   else
2439   {
2440     pp->colors = random() % NUM_COLORS;
2441   }
2442
2443   pp->change_colors = change_colors;
2444
2445   /* Set the view mode. */
2446   if (!strcasecmp(view_mode,"random"))
2447   {
2448     pp->view = random() % NUM_VIEW_MODES;
2449   }
2450   else if (!strcasecmp(view_mode,"walk"))
2451   {
2452     pp->view = VIEW_WALK;
2453   }
2454   else if (!strcasecmp(view_mode,"turn"))
2455   {
2456     pp->view = VIEW_TURN;
2457   }
2458   else
2459   {
2460     pp->view = random() % NUM_VIEW_MODES;
2461   }
2462
2463   /* Set the 3d projection mode. */
2464   if (!strcasecmp(proj,"random"))
2465   {
2466     /* Orthographic projection only makes sense in turn mode. */
2467     if (pp->view == VIEW_TURN)
2468       pp->projection = random() % NUM_DISP_MODES;
2469     else
2470       pp->projection = DISP_PERSPECTIVE;
2471   }
2472   else if (!strcasecmp(proj,"perspective"))
2473   {
2474     pp->projection = DISP_PERSPECTIVE;
2475   }
2476   else if (!strcasecmp(proj,"orthographic"))
2477   {
2478     pp->projection = DISP_ORTHOGRAPHIC;
2479   }
2480   else
2481   {
2482     /* Orthographic projection only makes sense in turn mode. */
2483     if (pp->view == VIEW_TURN)
2484       pp->projection = random() % NUM_DISP_MODES;
2485     else
2486       pp->projection = DISP_PERSPECTIVE;
2487   }
2488
2489   /* make multiple screens rotate at slightly different rates. */
2490   pp->speed_scale = 0.9 + frand(0.3);
2491
2492   if ((pp->glx_context = init_GL(mi)) != NULL)
2493   {
2494     reshape_romanboy(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
2495     init(mi);
2496   }
2497   else
2498   {
2499     MI_CLEARWINDOW(mi);
2500   }
2501 }
2502
2503 /*
2504  *-----------------------------------------------------------------------------
2505  *    Called by the mainline code periodically to update the display.
2506  *-----------------------------------------------------------------------------
2507  */
2508 ENTRYPOINT void draw_romanboy(ModeInfo *mi)
2509 {
2510   Display *display = MI_DISPLAY(mi);
2511   Window window = MI_WINDOW(mi);
2512   romanboystruct *pp;
2513
2514   if (romanboy == NULL)
2515     return;
2516   pp = &romanboy[MI_SCREEN(mi)];
2517
2518   MI_IS_DRAWN(mi) = True;
2519   if (!pp->glx_context)
2520     return;
2521
2522   glXMakeCurrent(display, window, *pp->glx_context);
2523
2524   glClearColor(0.0f,0.0f,0.0f,1.0f);
2525   glClearDepth(1.0f);
2526   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
2527   glLoadIdentity();
2528
2529   display_romanboy(mi);
2530
2531   if (MI_IS_FPS(mi))
2532     do_fps (mi);
2533
2534   glFlush();
2535
2536   glXSwapBuffers(display,window);
2537 }
2538
2539
2540 #ifndef STANDALONE
2541 ENTRYPOINT void change_romanboy(ModeInfo *mi)
2542 {
2543   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
2544
2545   if (!pp->glx_context)
2546     return;
2547
2548   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *pp->glx_context);
2549   init(mi);
2550 }
2551 #endif /* !STANDALONE */
2552
2553
2554 ENTRYPOINT void free_romanboy(ModeInfo *mi)
2555 {
2556   romanboystruct *pp = &romanboy[MI_SCREEN(mi)];
2557
2558   if (!pp->glx_context) return;
2559   glXMakeCurrent (MI_DISPLAY(mi), MI_WINDOW(mi), *pp->glx_context);
2560
2561   if (pp->pp) free(pp->pp);
2562   if (pp->pn) free(pp->pn);
2563   if (pp->col) free(pp->col);
2564   if (pp->tex) free(pp->tex);
2565   gltrackball_free (pp->trackball);
2566   if (pp->tex_name) glDeleteTextures (1, &pp->tex_name);
2567 #ifdef HAVE_GLSL
2568   if (pp->uv) free(pp->uv);
2569   if (pp->indices) free(pp->indices);
2570   if (pp->use_shaders)
2571   {
2572     glDeleteBuffers(1,&pp->vertex_uv_buffer);
2573     glDeleteBuffers(1,&pp->vertex_t_buffer);
2574     glDeleteBuffers(1,&pp->color_buffer);
2575     glDeleteBuffers(1,&pp->indices_buffer);
2576     if (pp->shader_program != 0)
2577     {
2578       glUseProgram(0);
2579       glDeleteProgram(pp->shader_program);
2580     }
2581   }
2582 #endif /* HAVE_GLSL */
2583 }
2584
2585
2586 XSCREENSAVER_MODULE ("RomanBoy", romanboy)
2587
2588 #endif /* USE_GL */