Home | History | Annotate | Download | only in angeles
      1 /* San Angeles Observation OpenGL ES version example
      2  * Copyright 2004-2005 Jetro Lauha
      3  * All rights reserved.
      4  * Web: http://iki.fi/jetro/
      5  *
      6  * This source is free software; you can redistribute it and/or
      7  * modify it under the terms of EITHER:
      8  *   (1) The GNU Lesser General Public License as published by the Free
      9  *       Software Foundation; either version 2.1 of the License, or (at
     10  *       your option) any later version. The text of the GNU Lesser
     11  *       General Public License is included with this source in the
     12  *       file LICENSE-LGPL.txt.
     13  *   (2) The BSD-style license that is included with this source in
     14  *       the file LICENSE-BSD.txt.
     15  *
     16  * This source is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
     19  * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
     20  *
     21  * $Id: demo.c,v 1.10 2005/02/08 20:54:39 tonic Exp $
     22  * $Revision: 1.10 $
     23  */
     24 
     25 #include <stdlib.h>
     26 #include <math.h>
     27 #include <float.h>
     28 #include <assert.h>
     29 
     30 #include <GLES/gl.h>
     31 
     32 #include "app.h"
     33 #include "shapes.h"
     34 #include "cams.h"
     35 
     36 
     37 // Total run length is 20 * camera track base unit length (see cams.h).
     38 #define RUN_LENGTH  (20 * CAMTRACK_LEN)
     39 #undef PI
     40 #define PI 3.1415926535897932f
     41 #define RANDOM_UINT_MAX 65535
     42 
     43 
     44 static unsigned long sRandomSeed = 0;
     45 
     46 static void seedRandom(unsigned long seed)
     47 {
     48     sRandomSeed = seed;
     49 }
     50 
     51 static unsigned long randomUInt()
     52 {
     53     sRandomSeed = sRandomSeed * 0x343fd + 0x269ec3;
     54     return sRandomSeed >> 16;
     55 }
     56 
     57 
     58 // Capped conversion from float to fixed.
     59 static long floatToFixed(float value)
     60 {
     61     if (value < -32768) value = -32768;
     62     if (value > 32767) value = 32767;
     63     return (long)(value * 65536);
     64 }
     65 
     66 #define FIXED(value) floatToFixed(value)
     67 
     68 
     69 // Definition of one GL object in this demo.
     70 typedef struct {
     71     /* Vertex array and color array are enabled for all objects, so their
     72      * pointers must always be valid and non-NULL. Normal array is not
     73      * used by the ground plane, so when its pointer is NULL then normal
     74      * array usage is disabled.
     75      *
     76      * Vertex array is supposed to use GL_FIXED datatype and stride 0
     77      * (i.e. tightly packed array). Color array is supposed to have 4
     78      * components per color with GL_UNSIGNED_BYTE datatype and stride 0.
     79      * Normal array is supposed to use GL_FIXED datatype and stride 0.
     80      */
     81     GLfixed *vertexArray;
     82     GLubyte *colorArray;
     83     GLfixed *normalArray;
     84     GLint vertexComponents;
     85     GLsizei count;
     86 } GLOBJECT;
     87 
     88 
     89 static long sStartTick = 0;
     90 static long sTick = 0;
     91 
     92 static int sCurrentCamTrack = 0;
     93 static long sCurrentCamTrackStartTick = 0;
     94 static long sNextCamTrackStartTick = 0x7fffffff;
     95 
     96 static GLOBJECT *sSuperShapeObjects[SUPERSHAPE_COUNT] = { NULL };
     97 static GLOBJECT *sGroundPlane = NULL;
     98 
     99 
    100 typedef struct {
    101     float x, y, z;
    102 } VECTOR3;
    103 
    104 
    105 static void freeGLObject(GLOBJECT *object)
    106 {
    107     if (object == NULL)
    108         return;
    109     free(object->normalArray);
    110     free(object->colorArray);
    111     free(object->vertexArray);
    112     free(object);
    113 }
    114 
    115 
    116 static GLOBJECT * newGLObject(long vertices, int vertexComponents,
    117                               int useNormalArray)
    118 {
    119     GLOBJECT *result;
    120     result = (GLOBJECT *)malloc(sizeof(GLOBJECT));
    121     if (result == NULL)
    122         return NULL;
    123     result->count = vertices;
    124     result->vertexComponents = vertexComponents;
    125     result->vertexArray = (GLfixed *)malloc(vertices * vertexComponents *
    126                                             sizeof(GLfixed));
    127     result->colorArray = (GLubyte *)malloc(vertices * 4 * sizeof(GLubyte));
    128     if (useNormalArray)
    129     {
    130         result->normalArray = (GLfixed *)malloc(vertices * 3 *
    131                                                 sizeof(GLfixed));
    132     }
    133     else
    134         result->normalArray = NULL;
    135     if (result->vertexArray == NULL ||
    136         result->colorArray == NULL ||
    137         (useNormalArray && result->normalArray == NULL))
    138     {
    139         freeGLObject(result);
    140         return NULL;
    141     }
    142     return result;
    143 }
    144 
    145 
    146 static void drawGLObject(GLOBJECT *object)
    147 {
    148     assert(object != NULL);
    149 
    150     glVertexPointer(object->vertexComponents, GL_FIXED,
    151                     0, object->vertexArray);
    152     glColorPointer(4, GL_UNSIGNED_BYTE, 0, object->colorArray);
    153 
    154     // Already done in initialization:
    155     //glEnableClientState(GL_VERTEX_ARRAY);
    156     //glEnableClientState(GL_COLOR_ARRAY);
    157 
    158     if (object->normalArray)
    159     {
    160         glNormalPointer(GL_FIXED, 0, object->normalArray);
    161         glEnableClientState(GL_NORMAL_ARRAY);
    162     }
    163     else
    164         glDisableClientState(GL_NORMAL_ARRAY);
    165     glDrawArrays(GL_TRIANGLES, 0, object->count);
    166 }
    167 
    168 
    169 static void vector3Sub(VECTOR3 *dest, VECTOR3 *v1, VECTOR3 *v2)
    170 {
    171     dest->x = v1->x - v2->x;
    172     dest->y = v1->y - v2->y;
    173     dest->z = v1->z - v2->z;
    174 }
    175 
    176 
    177 static void superShapeMap(VECTOR3 *point, float r1, float r2, float t, float p)
    178 {
    179     // sphere-mapping of supershape parameters
    180     point->x = (float)(cos(t) * cos(p) / r1 / r2);
    181     point->y = (float)(sin(t) * cos(p) / r1 / r2);
    182     point->z = (float)(sin(p) / r2);
    183 }
    184 
    185 
    186 static float ssFunc(const float t, const float *p)
    187 {
    188     return (float)(pow(pow(fabs(cos(p[0] * t / 4)) / p[1], p[4]) +
    189                        pow(fabs(sin(p[0] * t / 4)) / p[2], p[5]), 1 / p[3]));
    190 }
    191 
    192 
    193 // Creates and returns a supershape object.
    194 // Based on Paul Bourke's POV-Ray implementation.
    195 // http://astronomy.swin.edu.au/~pbourke/povray/supershape/
    196 static GLOBJECT * createSuperShape(const float *params)
    197 {
    198     const int resol1 = (int)params[SUPERSHAPE_PARAMS - 3];
    199     const int resol2 = (int)params[SUPERSHAPE_PARAMS - 2];
    200     // latitude 0 to pi/2 for no mirrored bottom
    201     // (latitudeBegin==0 for -pi/2 to pi/2 originally)
    202     const int latitudeBegin = resol2 / 4;
    203     const int latitudeEnd = resol2 / 2;    // non-inclusive
    204     const int longitudeCount = resol1;
    205     const int latitudeCount = latitudeEnd - latitudeBegin;
    206     const long triangleCount = longitudeCount * latitudeCount * 2;
    207     const long vertices = triangleCount * 3;
    208     GLOBJECT *result;
    209     float baseColor[3];
    210     int a, longitude, latitude;
    211     long currentVertex, currentQuad;
    212 
    213     result = newGLObject(vertices, 3, 1);
    214     if (result == NULL)
    215         return NULL;
    216 
    217     for (a = 0; a < 3; ++a)
    218         baseColor[a] = ((randomUInt() % 155) + 100) / 255.f;
    219 
    220     currentQuad = 0;
    221     currentVertex = 0;
    222 
    223     // longitude -pi to pi
    224     for (longitude = 0; longitude < longitudeCount; ++longitude)
    225     {
    226 
    227         // latitude 0 to pi/2
    228         for (latitude = latitudeBegin; latitude < latitudeEnd; ++latitude)
    229         {
    230             float t1 = -PI + longitude * 2 * PI / resol1;
    231             float t2 = -PI + (longitude + 1) * 2 * PI / resol1;
    232             float p1 = -PI / 2 + latitude * 2 * PI / resol2;
    233             float p2 = -PI / 2 + (latitude + 1) * 2 * PI / resol2;
    234             float r0, r1, r2, r3;
    235 
    236             r0 = ssFunc(t1, params);
    237             r1 = ssFunc(p1, &params[6]);
    238             r2 = ssFunc(t2, params);
    239             r3 = ssFunc(p2, &params[6]);
    240 
    241             if (r0 != 0 && r1 != 0 && r2 != 0 && r3 != 0)
    242             {
    243                 VECTOR3 pa, pb, pc, pd;
    244                 VECTOR3 v1, v2, n;
    245                 float ca;
    246                 int i;
    247                 //float lenSq, invLenSq;
    248 
    249                 superShapeMap(&pa, r0, r1, t1, p1);
    250                 superShapeMap(&pb, r2, r1, t2, p1);
    251                 superShapeMap(&pc, r2, r3, t2, p2);
    252                 superShapeMap(&pd, r0, r3, t1, p2);
    253 
    254                 // kludge to set lower edge of the object to fixed level
    255                 if (latitude == latitudeBegin + 1)
    256                     pa.z = pb.z = 0;
    257 
    258                 vector3Sub(&v1, &pb, &pa);
    259                 vector3Sub(&v2, &pd, &pa);
    260 
    261                 // Calculate normal with cross product.
    262                 /*   i    j    k      i    j
    263                  * v1.x v1.y v1.z | v1.x v1.y
    264                  * v2.x v2.y v2.z | v2.x v2.y
    265                  */
    266 
    267                 n.x = v1.y * v2.z - v1.z * v2.y;
    268                 n.y = v1.z * v2.x - v1.x * v2.z;
    269                 n.z = v1.x * v2.y - v1.y * v2.x;
    270 
    271                 /* Pre-normalization of the normals is disabled here because
    272                  * they will be normalized anyway later due to automatic
    273                  * normalization (GL_NORMALIZE). It is enabled because the
    274                  * objects are scaled with glScale.
    275                  */
    276                 /*
    277                 lenSq = n.x * n.x + n.y * n.y + n.z * n.z;
    278                 invLenSq = (float)(1 / sqrt(lenSq));
    279                 n.x *= invLenSq;
    280                 n.y *= invLenSq;
    281                 n.z *= invLenSq;
    282                 */
    283 
    284                 ca = pa.z + 0.5f;
    285 
    286                 for (i = currentVertex * 3;
    287                      i < (currentVertex + 6) * 3;
    288                      i += 3)
    289                 {
    290                     result->normalArray[i] = FIXED(n.x);
    291                     result->normalArray[i + 1] = FIXED(n.y);
    292                     result->normalArray[i + 2] = FIXED(n.z);
    293                 }
    294                 for (i = currentVertex * 4;
    295                      i < (currentVertex + 6) * 4;
    296                      i += 4)
    297                 {
    298                     int a, color[3];
    299                     for (a = 0; a < 3; ++a)
    300                     {
    301                         color[a] = (int)(ca * baseColor[a] * 255);
    302                         if (color[a] > 255) color[a] = 255;
    303                     }
    304                     result->colorArray[i] = (GLubyte)color[0];
    305                     result->colorArray[i + 1] = (GLubyte)color[1];
    306                     result->colorArray[i + 2] = (GLubyte)color[2];
    307                     result->colorArray[i + 3] = 0;
    308                 }
    309                 result->vertexArray[currentVertex * 3] = FIXED(pa.x);
    310                 result->vertexArray[currentVertex * 3 + 1] = FIXED(pa.y);
    311                 result->vertexArray[currentVertex * 3 + 2] = FIXED(pa.z);
    312                 ++currentVertex;
    313                 result->vertexArray[currentVertex * 3] = FIXED(pb.x);
    314                 result->vertexArray[currentVertex * 3 + 1] = FIXED(pb.y);
    315                 result->vertexArray[currentVertex * 3 + 2] = FIXED(pb.z);
    316                 ++currentVertex;
    317                 result->vertexArray[currentVertex * 3] = FIXED(pd.x);
    318                 result->vertexArray[currentVertex * 3 + 1] = FIXED(pd.y);
    319                 result->vertexArray[currentVertex * 3 + 2] = FIXED(pd.z);
    320                 ++currentVertex;
    321                 result->vertexArray[currentVertex * 3] = FIXED(pb.x);
    322                 result->vertexArray[currentVertex * 3 + 1] = FIXED(pb.y);
    323                 result->vertexArray[currentVertex * 3 + 2] = FIXED(pb.z);
    324                 ++currentVertex;
    325                 result->vertexArray[currentVertex * 3] = FIXED(pc.x);
    326                 result->vertexArray[currentVertex * 3 + 1] = FIXED(pc.y);
    327                 result->vertexArray[currentVertex * 3 + 2] = FIXED(pc.z);
    328                 ++currentVertex;
    329                 result->vertexArray[currentVertex * 3] = FIXED(pd.x);
    330                 result->vertexArray[currentVertex * 3 + 1] = FIXED(pd.y);
    331                 result->vertexArray[currentVertex * 3 + 2] = FIXED(pd.z);
    332                 ++currentVertex;
    333             } // r0 && r1 && r2 && r3
    334             ++currentQuad;
    335         } // latitude
    336     } // longitude
    337 
    338     // Set number of vertices in object to the actual amount created.
    339     result->count = currentVertex;
    340 
    341     return result;
    342 }
    343 
    344 
    345 static GLOBJECT * createGroundPlane()
    346 {
    347     const int scale = 4;
    348     const int yBegin = -15, yEnd = 15;    // ends are non-inclusive
    349     const int xBegin = -15, xEnd = 15;
    350     const long triangleCount = (yEnd - yBegin) * (xEnd - xBegin) * 2;
    351     const long vertices = triangleCount * 3;
    352     GLOBJECT *result;
    353     int x, y;
    354     long currentVertex, currentQuad;
    355 
    356     result = newGLObject(vertices, 2, 0);
    357     if (result == NULL)
    358         return NULL;
    359 
    360     currentQuad = 0;
    361     currentVertex = 0;
    362 
    363     for (y = yBegin; y < yEnd; ++y)
    364     {
    365         for (x = xBegin; x < xEnd; ++x)
    366         {
    367             GLubyte color;
    368             int i, a;
    369             color = (GLubyte)((randomUInt() & 0x5f) + 81);  // 101 1111
    370             for (i = currentVertex * 4; i < (currentVertex + 6) * 4; i += 4)
    371             {
    372                 result->colorArray[i] = color;
    373                 result->colorArray[i + 1] = color;
    374                 result->colorArray[i + 2] = color;
    375                 result->colorArray[i + 3] = 0;
    376             }
    377 
    378             // Axis bits for quad triangles:
    379             // x: 011100 (0x1c), y: 110001 (0x31)  (clockwise)
    380             // x: 001110 (0x0e), y: 100011 (0x23)  (counter-clockwise)
    381             for (a = 0; a < 6; ++a)
    382             {
    383                 const int xm = x + ((0x1c >> a) & 1);
    384                 const int ym = y + ((0x31 >> a) & 1);
    385                 const float m = (float)(cos(xm * 2) * sin(ym * 4) * 0.75f);
    386                 result->vertexArray[currentVertex * 2] =
    387                     FIXED(xm * scale + m);
    388                 result->vertexArray[currentVertex * 2 + 1] =
    389                     FIXED(ym * scale + m);
    390                 ++currentVertex;
    391             }
    392             ++currentQuad;
    393         }
    394     }
    395     return result;
    396 }
    397 
    398 
    399 static void drawGroundPlane()
    400 {
    401     glDisable(GL_CULL_FACE);
    402     glDisable(GL_DEPTH_TEST);
    403     glEnable(GL_BLEND);
    404     glBlendFunc(GL_ZERO, GL_SRC_COLOR);
    405     glDisable(GL_LIGHTING);
    406 
    407     drawGLObject(sGroundPlane);
    408 
    409     glEnable(GL_LIGHTING);
    410     glDisable(GL_BLEND);
    411     glEnable(GL_DEPTH_TEST);
    412 }
    413 
    414 
    415 static void drawFadeQuad()
    416 {
    417     static const GLfixed quadVertices[] = {
    418         -0x10000, -0x10000,
    419          0x10000, -0x10000,
    420         -0x10000,  0x10000,
    421          0x10000, -0x10000,
    422          0x10000,  0x10000,
    423         -0x10000,  0x10000
    424     };
    425 
    426     const int beginFade = sTick - sCurrentCamTrackStartTick;
    427     const int endFade = sNextCamTrackStartTick - sTick;
    428     const int minFade = beginFade < endFade ? beginFade : endFade;
    429 
    430     if (minFade < 1024)
    431     {
    432         const GLfixed fadeColor = minFade << 6;
    433         glColor4x(fadeColor, fadeColor, fadeColor, 0);
    434 
    435         glDisable(GL_DEPTH_TEST);
    436         glEnable(GL_BLEND);
    437         glBlendFunc(GL_ZERO, GL_SRC_COLOR);
    438         glDisable(GL_LIGHTING);
    439 
    440         glMatrixMode(GL_MODELVIEW);
    441         glLoadIdentity();
    442 
    443         glMatrixMode(GL_PROJECTION);
    444         glLoadIdentity();
    445 
    446         glDisableClientState(GL_COLOR_ARRAY);
    447         glDisableClientState(GL_NORMAL_ARRAY);
    448         glVertexPointer(2, GL_FIXED, 0, quadVertices);
    449         glDrawArrays(GL_TRIANGLES, 0, 6);
    450 
    451         glEnableClientState(GL_COLOR_ARRAY);
    452 
    453         glMatrixMode(GL_MODELVIEW);
    454 
    455         glEnable(GL_LIGHTING);
    456         glDisable(GL_BLEND);
    457         glEnable(GL_DEPTH_TEST);
    458     }
    459 }
    460 
    461 
    462 // Called from the app framework.
    463 void appInit()
    464 {
    465     int a;
    466 
    467     glEnable(GL_NORMALIZE);
    468     glEnable(GL_DEPTH_TEST);
    469     glDisable(GL_CULL_FACE);
    470     glShadeModel(GL_FLAT);
    471 
    472     glEnable(GL_LIGHTING);
    473     glEnable(GL_LIGHT0);
    474     glEnable(GL_LIGHT1);
    475     glEnable(GL_LIGHT2);
    476 
    477     glEnableClientState(GL_VERTEX_ARRAY);
    478     glEnableClientState(GL_COLOR_ARRAY);
    479 
    480     seedRandom(15);
    481 
    482     for (a = 0; a < SUPERSHAPE_COUNT; ++a)
    483     {
    484         sSuperShapeObjects[a] = createSuperShape(sSuperShapeParams[a]);
    485         assert(sSuperShapeObjects[a] != NULL);
    486     }
    487     sGroundPlane = createGroundPlane();
    488     assert(sGroundPlane != NULL);
    489 }
    490 
    491 
    492 // Called from the app framework.
    493 void appDeinit()
    494 {
    495     int a;
    496     for (a = 0; a < SUPERSHAPE_COUNT; ++a)
    497         freeGLObject(sSuperShapeObjects[a]);
    498     freeGLObject(sGroundPlane);
    499 }
    500 
    501 
    502 static void gluPerspective(GLfloat fovy, GLfloat aspect,
    503                            GLfloat zNear, GLfloat zFar)
    504 {
    505     GLfloat xmin, xmax, ymin, ymax;
    506 
    507     ymax = zNear * (GLfloat)tan(fovy * PI / 360);
    508     ymin = -ymax;
    509     xmin = ymin * aspect;
    510     xmax = ymax * aspect;
    511 
    512     glFrustumx((GLfixed)(xmin * 65536), (GLfixed)(xmax * 65536),
    513                (GLfixed)(ymin * 65536), (GLfixed)(ymax * 65536),
    514                (GLfixed)(zNear * 65536), (GLfixed)(zFar * 65536));
    515 }
    516 
    517 
    518 static void prepareFrame(int width, int height)
    519 {
    520     glViewport(0, 0, width, height);
    521 
    522     glClearColorx((GLfixed)(0.1f * 65536),
    523                   (GLfixed)(0.2f * 65536),
    524                   (GLfixed)(0.3f * 65536), 0x10000);
    525     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    526 
    527     glMatrixMode(GL_PROJECTION);
    528     glLoadIdentity();
    529     gluPerspective(45, (float)width / height, 0.5f, 150);
    530 
    531     glMatrixMode(GL_MODELVIEW);
    532 
    533     glLoadIdentity();
    534 }
    535 
    536 
    537 static void configureLightAndMaterial()
    538 {
    539     static GLfixed light0Position[] = { -0x40000, 0x10000, 0x10000, 0 };
    540     static GLfixed light0Diffuse[] = { 0x10000, 0x6666, 0, 0x10000 };
    541     static GLfixed light1Position[] = { 0x10000, -0x20000, -0x10000, 0 };
    542     static GLfixed light1Diffuse[] = { 0x11eb, 0x23d7, 0x5999, 0x10000 };
    543     static GLfixed light2Position[] = { -0x10000, 0, -0x40000, 0 };
    544     static GLfixed light2Diffuse[] = { 0x11eb, 0x2b85, 0x23d7, 0x10000 };
    545     static GLfixed materialSpecular[] = { 0x10000, 0x10000, 0x10000, 0x10000 };
    546 
    547     glLightxv(GL_LIGHT0, GL_POSITION, light0Position);
    548     glLightxv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse);
    549     glLightxv(GL_LIGHT1, GL_POSITION, light1Position);
    550     glLightxv(GL_LIGHT1, GL_DIFFUSE, light1Diffuse);
    551     glLightxv(GL_LIGHT2, GL_POSITION, light2Position);
    552     glLightxv(GL_LIGHT2, GL_DIFFUSE, light2Diffuse);
    553     glMaterialxv(GL_FRONT_AND_BACK, GL_SPECULAR, materialSpecular);
    554 
    555     glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, 60 << 16);
    556     glEnable(GL_COLOR_MATERIAL);
    557 }
    558 
    559 
    560 static void drawModels(float zScale)
    561 {
    562     const int translationScale = 9;
    563     int x, y;
    564 
    565     seedRandom(9);
    566 
    567     glScalex(1 << 16, 1 << 16, (GLfixed)(zScale * 65536));
    568 
    569     for (y = -5; y <= 5; ++y)
    570     {
    571         for (x = -5; x <= 5; ++x)
    572         {
    573             float buildingScale;
    574             GLfixed fixedScale;
    575 
    576             int curShape = randomUInt() % SUPERSHAPE_COUNT;
    577             buildingScale = sSuperShapeParams[curShape][SUPERSHAPE_PARAMS - 1];
    578             fixedScale = (GLfixed)(buildingScale * 65536);
    579 
    580             glPushMatrix();
    581             glTranslatex((x * translationScale) * 65536,
    582                          (y * translationScale) * 65536,
    583                          0);
    584             glRotatex((GLfixed)((randomUInt() % 360) << 16), 0, 0, 1 << 16);
    585             glScalex(fixedScale, fixedScale, fixedScale);
    586 
    587             drawGLObject(sSuperShapeObjects[curShape]);
    588             glPopMatrix();
    589         }
    590     }
    591 
    592     for (x = -2; x <= 2; ++x)
    593     {
    594         const int shipScale100 = translationScale * 500;
    595         const int offs100 = x * shipScale100 + (sTick % shipScale100);
    596         float offs = offs100 * 0.01f;
    597         GLfixed fixedOffs = (GLfixed)(offs * 65536);
    598         glPushMatrix();
    599         glTranslatex(fixedOffs, -4 * 65536, 2 << 16);
    600         drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]);
    601         glPopMatrix();
    602         glPushMatrix();
    603         glTranslatex(-4 * 65536, fixedOffs, 4 << 16);
    604         glRotatex(90 << 16, 0, 0, 1 << 16);
    605         drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]);
    606         glPopMatrix();
    607     }
    608 }
    609 
    610 
    611 /* Following gluLookAt implementation is adapted from the
    612  * Mesa 3D Graphics library. http://www.mesa3d.org
    613  */
    614 static void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
    615 	              GLfloat centerx, GLfloat centery, GLfloat centerz,
    616 	              GLfloat upx, GLfloat upy, GLfloat upz)
    617 {
    618     GLfloat m[16];
    619     GLfloat x[3], y[3], z[3];
    620     GLfloat mag;
    621 
    622     /* Make rotation matrix */
    623 
    624     /* Z vector */
    625     z[0] = eyex - centerx;
    626     z[1] = eyey - centery;
    627     z[2] = eyez - centerz;
    628     mag = (float)sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
    629     if (mag) {			/* mpichler, 19950515 */
    630         z[0] /= mag;
    631         z[1] /= mag;
    632         z[2] /= mag;
    633     }
    634 
    635     /* Y vector */
    636     y[0] = upx;
    637     y[1] = upy;
    638     y[2] = upz;
    639 
    640     /* X vector = Y cross Z */
    641     x[0] = y[1] * z[2] - y[2] * z[1];
    642     x[1] = -y[0] * z[2] + y[2] * z[0];
    643     x[2] = y[0] * z[1] - y[1] * z[0];
    644 
    645     /* Recompute Y = Z cross X */
    646     y[0] = z[1] * x[2] - z[2] * x[1];
    647     y[1] = -z[0] * x[2] + z[2] * x[0];
    648     y[2] = z[0] * x[1] - z[1] * x[0];
    649 
    650     /* mpichler, 19950515 */
    651     /* cross product gives area of parallelogram, which is < 1.0 for
    652      * non-perpendicular unit-length vectors; so normalize x, y here
    653      */
    654 
    655     mag = (float)sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
    656     if (mag) {
    657         x[0] /= mag;
    658         x[1] /= mag;
    659         x[2] /= mag;
    660     }
    661 
    662     mag = (float)sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
    663     if (mag) {
    664         y[0] /= mag;
    665         y[1] /= mag;
    666         y[2] /= mag;
    667     }
    668 
    669 #define M(row,col)  m[col*4+row]
    670     M(0, 0) = x[0];
    671     M(0, 1) = x[1];
    672     M(0, 2) = x[2];
    673     M(0, 3) = 0.0;
    674     M(1, 0) = y[0];
    675     M(1, 1) = y[1];
    676     M(1, 2) = y[2];
    677     M(1, 3) = 0.0;
    678     M(2, 0) = z[0];
    679     M(2, 1) = z[1];
    680     M(2, 2) = z[2];
    681     M(2, 3) = 0.0;
    682     M(3, 0) = 0.0;
    683     M(3, 1) = 0.0;
    684     M(3, 2) = 0.0;
    685     M(3, 3) = 1.0;
    686 #undef M
    687     {
    688         int a;
    689         GLfixed fixedM[16];
    690         for (a = 0; a < 16; ++a)
    691             fixedM[a] = (GLfixed)(m[a] * 65536);
    692         glMultMatrixx(fixedM);
    693     }
    694 
    695     /* Translate Eye to Origin */
    696     glTranslatex((GLfixed)(-eyex * 65536),
    697                  (GLfixed)(-eyey * 65536),
    698                  (GLfixed)(-eyez * 65536));
    699 }
    700 
    701 
    702 static void camTrack()
    703 {
    704     float lerp[5];
    705     float eX, eY, eZ, cX, cY, cZ;
    706     float trackPos;
    707     CAMTRACK *cam;
    708     long currentCamTick;
    709     int a;
    710 
    711     if (sNextCamTrackStartTick <= sTick)
    712     {
    713         ++sCurrentCamTrack;
    714         sCurrentCamTrackStartTick = sNextCamTrackStartTick;
    715     }
    716     sNextCamTrackStartTick = sCurrentCamTrackStartTick +
    717                              sCamTracks[sCurrentCamTrack].len * CAMTRACK_LEN;
    718 
    719     cam = &sCamTracks[sCurrentCamTrack];
    720     currentCamTick = sTick - sCurrentCamTrackStartTick;
    721     trackPos = (float)currentCamTick / (CAMTRACK_LEN * cam->len);
    722 
    723     for (a = 0; a < 5; ++a)
    724         lerp[a] = (cam->src[a] + cam->dest[a] * trackPos) * 0.01f;
    725 
    726     if (cam->dist)
    727     {
    728         float dist = cam->dist * 0.1f;
    729         cX = lerp[0];
    730         cY = lerp[1];
    731         cZ = lerp[2];
    732         eX = cX - (float)cos(lerp[3]) * dist;
    733         eY = cY - (float)sin(lerp[3]) * dist;
    734         eZ = cZ - lerp[4];
    735     }
    736     else
    737     {
    738         eX = lerp[0];
    739         eY = lerp[1];
    740         eZ = lerp[2];
    741         cX = eX + (float)cos(lerp[3]);
    742         cY = eY + (float)sin(lerp[3]);
    743         cZ = eZ + lerp[4];
    744     }
    745     gluLookAt(eX, eY, eZ, cX, cY, cZ, 0, 0, 1);
    746 }
    747 
    748 
    749 // Called from the app framework.
    750 /* The tick is current time in milliseconds, width and height
    751  * are the image dimensions to be rendered.
    752  */
    753 void appRender(long tick, int width, int height)
    754 {
    755     if (sStartTick == 0)
    756         sStartTick = tick;
    757     if (!gAppAlive)
    758         return;
    759 
    760     // Actual tick value is "blurred" a little bit.
    761     sTick = (sTick + tick - sStartTick) >> 1;
    762 
    763     // Terminate application after running through the demonstration once.
    764     if (sTick >= RUN_LENGTH)
    765     {
    766         gAppAlive = 0;
    767         return;
    768     }
    769 
    770     // Prepare OpenGL ES for rendering of the frame.
    771     prepareFrame(width, height);
    772 
    773     // Update the camera position and set the lookat.
    774     camTrack();
    775 
    776     // Configure environment.
    777     configureLightAndMaterial();
    778 
    779     // Draw the reflection by drawing models with negated Z-axis.
    780     glPushMatrix();
    781     drawModels(-1);
    782     glPopMatrix();
    783 
    784     // Blend the ground plane to the window.
    785     drawGroundPlane();
    786 
    787     // Draw all the models normally.
    788     drawModels(1);
    789 
    790     // Draw fade quad over whole window (when changing cameras).
    791     drawFadeQuad();
    792 }
    793