Home | History | Annotate | Download | only in src
      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 // The GLES2 implementation is adapted from the javascript implementation
     26 // upon WebGL by kwaters@.
     27 
     28 // The OpenGL implementation uses VBO extensions instead.
     29 
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <math.h>
     33 #include <float.h>
     34 #include <assert.h>
     35 
     36 #ifdef SAN_ANGELES_OBSERVATION_GLES
     37 #undef IMPORTGL_API
     38 #undef IMPORTGL_FNPTRINIT
     39 #include "importgl.h"
     40 #include "matrixop.h"
     41 #include "shader.h"
     42 #else  // SAN_ANGELES_OBSERVATION_GLES
     43 #undef IMPORTVBO_API
     44 #undef IMPORTVBO_FNPTRINIT
     45 #include "importvbo.h"
     46 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
     47 
     48 #include "app.h"
     49 #include "shapes.h"
     50 #include "cams.h"
     51 
     52 
     53 // Total run length is 20 * camera track base unit length (see cams.h).
     54 #define RUN_LENGTH  (20 * CAMTRACK_LEN)
     55 #undef PI
     56 #define PI 3.1415926535897932f
     57 #define RANDOM_UINT_MAX 65535
     58 
     59 
     60 static unsigned long sRandomSeed = 0;
     61 
     62 static void seedRandom(unsigned long seed)
     63 {
     64     sRandomSeed = seed;
     65 }
     66 
     67 static unsigned long randomUInt()
     68 {
     69     sRandomSeed = sRandomSeed * 0x343fd + 0x269ec3;
     70     return sRandomSeed >> 16;
     71 }
     72 
     73 
     74 // Definition of one GL object in this demo.
     75 typedef struct {
     76     /* Vertex array and color array are enabled for all objects, so their
     77      * pointers must always be valid and non-NULL. Normal array is not
     78      * used by the ground plane, so when its pointer is NULL then normal
     79      * array usage is disabled.
     80      *
     81      * Vertex array is supposed to use GL_FIXED datatype and stride 0
     82      * (i.e. tightly packed array). Color array is supposed to have 4
     83      * components per color with GL_UNSIGNED_BYTE datatype and stride 0.
     84      * Normal array is supposed to use GL_FIXED datatype and stride 0.
     85      */
     86     GLfloat *vertexArray;
     87     GLint vertexArraySize;
     88     GLintptr vertexArrayOffset;
     89     GLubyte *colorArray;
     90     GLint colorArraySize;
     91     GLintptr colorArrayOffset;
     92     GLfloat *normalArray;
     93     GLint normalArraySize;
     94     GLintptr normalArrayOffset;
     95     GLint vertexComponents;
     96     GLsizei count;
     97 #ifdef SAN_ANGELES_OBSERVATION_GLES
     98     GLuint shaderProgram;
     99 #endif  // SAN_ANGELES_OBSERVATION_GLES
    100 } GLOBJECT;
    101 
    102 
    103 static long sStartTick = 0;
    104 static long sTick = 0;
    105 
    106 static int sCurrentCamTrack = 0;
    107 static long sCurrentCamTrackStartTick = 0;
    108 static long sNextCamTrackStartTick = 0x7fffffff;
    109 
    110 static GLOBJECT *sSuperShapeObjects[SUPERSHAPE_COUNT] = { NULL };
    111 static GLOBJECT *sGroundPlane = NULL;
    112 static GLOBJECT *sFadeQuad = NULL;
    113 
    114 static GLuint sVBO = 0;
    115 
    116 typedef struct {
    117     float x, y, z;
    118 } VECTOR3;
    119 
    120 
    121 static void freeGLObject(GLOBJECT *object)
    122 {
    123     if (object == NULL)
    124         return;
    125 
    126     free(object->normalArray);
    127     free(object->colorArray);
    128     free(object->vertexArray);
    129 
    130     free(object);
    131 }
    132 
    133 
    134 static GLOBJECT * newGLObject(long vertices, int vertexComponents,
    135                               int useColorArray, int useNormalArray)
    136 {
    137     GLOBJECT *result;
    138     result = malloc(sizeof(GLOBJECT));
    139     if (result == NULL)
    140         return NULL;
    141     result->count = vertices;
    142     result->vertexComponents = vertexComponents;
    143     result->vertexArraySize = vertices * vertexComponents * sizeof(GLfloat);
    144     result->vertexArray = malloc(result->vertexArraySize);
    145     result->vertexArrayOffset = 0;
    146     if (useColorArray)
    147     {
    148         result->colorArraySize = vertices * 4 * sizeof(GLubyte);
    149         result->colorArray = malloc(result->colorArraySize);
    150     }
    151     else
    152     {
    153         result->colorArraySize = 0;
    154         result->colorArray = NULL;
    155     }
    156     result->colorArrayOffset = result->vertexArrayOffset +
    157                                result->vertexArraySize;
    158     if (useNormalArray)
    159     {
    160         result->normalArraySize = vertices * 3 * sizeof(GLfloat);
    161         result->normalArray = malloc(result->normalArraySize);
    162     }
    163     else
    164     {
    165         result->normalArraySize = 0;
    166         result->normalArray = NULL;
    167     }
    168     result->normalArrayOffset = result->colorArrayOffset +
    169                                 result->colorArraySize;
    170     if (result->vertexArray == NULL ||
    171         (useColorArray && result->colorArray == NULL) ||
    172         (useNormalArray && result->normalArray == NULL))
    173     {
    174         freeGLObject(result);
    175         return NULL;
    176     }
    177 #ifdef SAN_ANGELES_OBSERVATION_GLES
    178     result->shaderProgram = 0;
    179 #endif  // SAN_ANGELES_OBSERVATION_GLES
    180     return result;
    181 }
    182 
    183 
    184 static void appendObjectVBO(GLOBJECT *object, GLint *offset)
    185 {
    186     assert(object != NULL);
    187 
    188     object->vertexArrayOffset += *offset;
    189     object->colorArrayOffset += *offset;
    190     object->normalArrayOffset += *offset;
    191     *offset += object->vertexArraySize + object->colorArraySize +
    192                object->normalArraySize;
    193 
    194     glBufferSubData(GL_ARRAY_BUFFER, object->vertexArrayOffset,
    195                     object->vertexArraySize, object->vertexArray);
    196     if (object->colorArray)
    197         glBufferSubData(GL_ARRAY_BUFFER, object->colorArrayOffset,
    198                         object->colorArraySize, object->colorArray);
    199     if (object->normalArray)
    200         glBufferSubData(GL_ARRAY_BUFFER, object->normalArrayOffset,
    201                         object->normalArraySize, object->normalArray);
    202 
    203     free(object->normalArray);
    204     object->normalArray = NULL;
    205     free(object->colorArray);
    206     object->colorArray = NULL;
    207     free(object->vertexArray);
    208     object->vertexArray = NULL;
    209 }
    210 
    211 
    212 static GLuint createVBO(GLOBJECT **superShapes, int superShapeCount,
    213                         GLOBJECT *groundPlane, GLOBJECT *fadeQuad)
    214 {
    215     GLuint vbo;
    216     GLint totalSize = 0;
    217     int a;
    218     for (a = 0; a < superShapeCount; ++a)
    219     {
    220         assert(superShapes[a] != NULL);
    221         totalSize += superShapes[a]->vertexArraySize +
    222                      superShapes[a]->colorArraySize +
    223                      superShapes[a]->normalArraySize;
    224     }
    225     totalSize += groundPlane->vertexArraySize +
    226                  groundPlane->colorArraySize +
    227                  groundPlane->normalArraySize;
    228     totalSize += fadeQuad->vertexArraySize +
    229                  fadeQuad->colorArraySize +
    230                  fadeQuad->normalArraySize;
    231     glGenBuffers(1, &vbo);
    232     glBindBuffer(GL_ARRAY_BUFFER, vbo);
    233     glBufferData(GL_ARRAY_BUFFER, totalSize, 0, GL_STATIC_DRAW);
    234     GLint offset = 0;
    235     for (a = 0; a < superShapeCount; ++a)
    236         appendObjectVBO(superShapes[a], &offset);
    237     appendObjectVBO(groundPlane, &offset);
    238     appendObjectVBO(fadeQuad, &offset);
    239     assert(offset == totalSize);
    240     return vbo;
    241 }
    242 
    243 
    244 static void drawGLObject(GLOBJECT *object)
    245 {
    246 #ifdef SAN_ANGELES_OBSERVATION_GLES
    247     int loc_pos = -1;
    248     int loc_colorIn = -1;
    249     int loc_normal = -1;
    250 #endif  // SAN_ANGELES_OBSERVATION_GLES
    251 
    252     assert(object != NULL);
    253 
    254 #ifdef SAN_ANGELES_OBSERVATION_GLES
    255     bindShaderProgram(object->shaderProgram);
    256     if (object->shaderProgram == sShaderLit.program)
    257     {
    258         loc_pos = sShaderLit.pos;
    259         loc_colorIn = sShaderLit.colorIn;
    260         loc_normal = sShaderLit.normal;
    261     }
    262     else if (object->shaderProgram == sShaderFlat.program)
    263     {
    264         loc_pos = sShaderFlat.pos;
    265         loc_colorIn = sShaderFlat.colorIn;
    266     }
    267     else
    268     {
    269         assert(0);
    270     }
    271     glVertexAttribPointer(loc_pos, object->vertexComponents, GL_FLOAT,
    272                           GL_FALSE, 0, (GLvoid *)object->vertexArrayOffset);
    273     glEnableVertexAttribArray(loc_pos);
    274     glVertexAttribPointer(loc_colorIn, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0,
    275                           (GLvoid *)object->colorArrayOffset);
    276     glEnableVertexAttribArray(loc_colorIn);
    277     if (object->normalArraySize > 0)
    278     {
    279         glVertexAttribPointer(loc_normal, 3, GL_FLOAT, GL_FALSE, 0,
    280                               (GLvoid *)object->normalArrayOffset);
    281         glEnableVertexAttribArray(loc_normal);
    282     }
    283     glDrawArrays(GL_TRIANGLES, 0, object->count);
    284 
    285     if (object->normalArraySize > 0)
    286         glDisableVertexAttribArray(loc_normal);
    287     glDisableVertexAttribArray(loc_colorIn);
    288     glDisableVertexAttribArray(loc_pos);
    289 #else  // !SAN_ANGELES_OBSERVATION_GLES
    290     glVertexPointer(object->vertexComponents, GL_FLOAT, 0,
    291                     (GLvoid *)object->vertexArrayOffset);
    292     glColorPointer(4, GL_UNSIGNED_BYTE, 0, (GLvoid *)object->colorArrayOffset);
    293     if (object->normalArraySize > 0)
    294     {
    295         glNormalPointer(GL_FLOAT, 0, (GLvoid *)object->normalArrayOffset);
    296         glEnableClientState(GL_NORMAL_ARRAY);
    297     }
    298     else
    299         glDisableClientState(GL_NORMAL_ARRAY);
    300     glDrawArrays(GL_TRIANGLES, 0, object->count);
    301 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    302 }
    303 
    304 
    305 static void vector3Sub(VECTOR3 *dest, VECTOR3 *v1, VECTOR3 *v2)
    306 {
    307     dest->x = v1->x - v2->x;
    308     dest->y = v1->y - v2->y;
    309     dest->z = v1->z - v2->z;
    310 }
    311 
    312 
    313 static void superShapeMap(VECTOR3 *point, float r1, float r2, float t, float p)
    314 {
    315     // sphere-mapping of supershape parameters
    316     point->x = (float)(cos(t) * cos(p) / r1 / r2);
    317     point->y = (float)(sin(t) * cos(p) / r1 / r2);
    318     point->z = (float)(sin(p) / r2);
    319 }
    320 
    321 
    322 static float ssFunc(const float t, const float *p)
    323 {
    324     return (float)(pow(pow(fabs(cos(p[0] * t / 4)) / p[1], p[4]) +
    325                        pow(fabs(sin(p[0] * t / 4)) / p[2], p[5]), 1 / p[3]));
    326 }
    327 
    328 
    329 // Creates and returns a supershape object.
    330 // Based on Paul Bourke's POV-Ray implementation.
    331 // http://astronomy.swin.edu.au/~pbourke/povray/supershape/
    332 static GLOBJECT * createSuperShape(const float *params)
    333 {
    334     const int resol1 = (int)params[SUPERSHAPE_PARAMS - 3];
    335     const int resol2 = (int)params[SUPERSHAPE_PARAMS - 2];
    336     // latitude 0 to pi/2 for no mirrored bottom
    337     // (latitudeBegin==0 for -pi/2 to pi/2 originally)
    338     const int latitudeBegin = resol2 / 4;
    339     const int latitudeEnd = resol2 / 2;    // non-inclusive
    340     const int longitudeCount = resol1;
    341     const int latitudeCount = latitudeEnd - latitudeBegin;
    342     const long triangleCount = longitudeCount * latitudeCount * 2;
    343     const long vertices = triangleCount * 3;
    344     GLOBJECT *result;
    345     float baseColor[3];
    346     int a, longitude, latitude;
    347     long currentVertex, currentQuad;
    348 
    349     result = newGLObject(vertices, 3, 1, 1);
    350     if (result == NULL)
    351         return NULL;
    352 
    353     for (a = 0; a < 3; ++a)
    354         baseColor[a] = ((randomUInt() % 155) + 100) / 255.f;
    355 
    356     currentQuad = 0;
    357     currentVertex = 0;
    358 
    359     // longitude -pi to pi
    360     for (longitude = 0; longitude < longitudeCount; ++longitude)
    361     {
    362 
    363         // latitude 0 to pi/2
    364         for (latitude = latitudeBegin; latitude < latitudeEnd; ++latitude)
    365         {
    366             float t1 = -PI + longitude * 2 * PI / resol1;
    367             float t2 = -PI + (longitude + 1) * 2 * PI / resol1;
    368             float p1 = -PI / 2 + latitude * 2 * PI / resol2;
    369             float p2 = -PI / 2 + (latitude + 1) * 2 * PI / resol2;
    370             float r0, r1, r2, r3;
    371 
    372             r0 = ssFunc(t1, params);
    373             r1 = ssFunc(p1, &params[6]);
    374             r2 = ssFunc(t2, params);
    375             r3 = ssFunc(p2, &params[6]);
    376 
    377             if (r0 != 0 && r1 != 0 && r2 != 0 && r3 != 0)
    378             {
    379                 VECTOR3 pa, pb, pc, pd;
    380                 VECTOR3 v1, v2, n;
    381                 float ca;
    382                 int i;
    383                 //float lenSq, invLenSq;
    384 
    385                 superShapeMap(&pa, r0, r1, t1, p1);
    386                 superShapeMap(&pb, r2, r1, t2, p1);
    387                 superShapeMap(&pc, r2, r3, t2, p2);
    388                 superShapeMap(&pd, r0, r3, t1, p2);
    389 
    390                 // kludge to set lower edge of the object to fixed level
    391                 if (latitude == latitudeBegin + 1)
    392                     pa.z = pb.z = 0;
    393 
    394                 vector3Sub(&v1, &pb, &pa);
    395                 vector3Sub(&v2, &pd, &pa);
    396 
    397                 // Calculate normal with cross product.
    398                 /*   i    j    k      i    j
    399                  * v1.x v1.y v1.z | v1.x v1.y
    400                  * v2.x v2.y v2.z | v2.x v2.y
    401                  */
    402 
    403                 n.x = v1.y * v2.z - v1.z * v2.y;
    404                 n.y = v1.z * v2.x - v1.x * v2.z;
    405                 n.z = v1.x * v2.y - v1.y * v2.x;
    406 
    407                 /* Pre-normalization of the normals is disabled here because
    408                  * they will be normalized anyway later due to automatic
    409                  * normalization (GL_NORMALIZE). It is enabled because the
    410                  * objects are scaled with glScale.
    411                  */
    412                 /*
    413                 lenSq = n.x * n.x + n.y * n.y + n.z * n.z;
    414                 invLenSq = (float)(1 / sqrt(lenSq));
    415                 n.x *= invLenSq;
    416                 n.y *= invLenSq;
    417                 n.z *= invLenSq;
    418                 */
    419 
    420                 ca = pa.z + 0.5f;
    421 
    422                 for (i = currentVertex * 3;
    423                      i < (currentVertex + 6) * 3;
    424                      i += 3)
    425                 {
    426                     result->normalArray[i] = n.x;
    427                     result->normalArray[i + 1] = n.y;
    428                     result->normalArray[i + 2] = n.z;
    429                 }
    430                 for (i = currentVertex * 4;
    431                      i < (currentVertex + 6) * 4;
    432                      i += 4)
    433                 {
    434                     int a, color[3];
    435                     for (a = 0; a < 3; ++a)
    436                     {
    437                         color[a] = (int)(ca * baseColor[a] * 255);
    438                         if (color[a] > 255) color[a] = 255;
    439                     }
    440                     result->colorArray[i] = (GLubyte)color[0];
    441                     result->colorArray[i + 1] = (GLubyte)color[1];
    442                     result->colorArray[i + 2] = (GLubyte)color[2];
    443                     result->colorArray[i + 3] = 0;
    444                 }
    445                 result->vertexArray[currentVertex * 3] = pa.x;
    446                 result->vertexArray[currentVertex * 3 + 1] = pa.y;
    447                 result->vertexArray[currentVertex * 3 + 2] = pa.z;
    448                 ++currentVertex;
    449                 result->vertexArray[currentVertex * 3] = pb.x;
    450                 result->vertexArray[currentVertex * 3 + 1] = pb.y;
    451                 result->vertexArray[currentVertex * 3 + 2] = pb.z;
    452                 ++currentVertex;
    453                 result->vertexArray[currentVertex * 3] = pd.x;
    454                 result->vertexArray[currentVertex * 3 + 1] = pd.y;
    455                 result->vertexArray[currentVertex * 3 + 2] = pd.z;
    456                 ++currentVertex;
    457                 result->vertexArray[currentVertex * 3] = pb.x;
    458                 result->vertexArray[currentVertex * 3 + 1] = pb.y;
    459                 result->vertexArray[currentVertex * 3 + 2] = pb.z;
    460                 ++currentVertex;
    461                 result->vertexArray[currentVertex * 3] = pc.x;
    462                 result->vertexArray[currentVertex * 3 + 1] = pc.y;
    463                 result->vertexArray[currentVertex * 3 + 2] = pc.z;
    464                 ++currentVertex;
    465                 result->vertexArray[currentVertex * 3] = pd.x;
    466                 result->vertexArray[currentVertex * 3 + 1] = pd.y;
    467                 result->vertexArray[currentVertex * 3 + 2] = pd.z;
    468                 ++currentVertex;
    469             } // r0 && r1 && r2 && r3
    470             ++currentQuad;
    471         } // latitude
    472     } // longitude
    473 
    474     // Set number of vertices in object to the actual amount created.
    475     result->count = currentVertex;
    476 #ifdef SAN_ANGELES_OBSERVATION_GLES
    477     result->shaderProgram = sShaderLit.program;
    478 #endif  // SAN_ANGELES_OBSERVATION_GLES
    479     return result;
    480 }
    481 
    482 
    483 static GLOBJECT * createGroundPlane()
    484 {
    485     const int scale = 4;
    486     const int yBegin = -15, yEnd = 15;    // ends are non-inclusive
    487     const int xBegin = -15, xEnd = 15;
    488     const long triangleCount = (yEnd - yBegin) * (xEnd - xBegin) * 2;
    489     const long vertices = triangleCount * 3;
    490     GLOBJECT *result;
    491     int x, y;
    492     long currentVertex, currentQuad;
    493 
    494     result = newGLObject(vertices, 2, 1, 0);
    495     if (result == NULL)
    496         return NULL;
    497 
    498     currentQuad = 0;
    499     currentVertex = 0;
    500 
    501     for (y = yBegin; y < yEnd; ++y)
    502     {
    503         for (x = xBegin; x < xEnd; ++x)
    504         {
    505             GLubyte color;
    506             int i, a;
    507             color = (GLubyte)((randomUInt() & 0x5f) + 81);  // 101 1111
    508             for (i = currentVertex * 4; i < (currentVertex + 6) * 4; i += 4)
    509             {
    510                 result->colorArray[i] = color;
    511                 result->colorArray[i + 1] = color;
    512                 result->colorArray[i + 2] = color;
    513                 result->colorArray[i + 3] = 0;
    514             }
    515 
    516             // Axis bits for quad triangles:
    517             // x: 011100 (0x1c), y: 110001 (0x31)  (clockwise)
    518             // x: 001110 (0x0e), y: 100011 (0x23)  (counter-clockwise)
    519             for (a = 0; a < 6; ++a)
    520             {
    521                 const int xm = x + ((0x1c >> a) & 1);
    522                 const int ym = y + ((0x31 >> a) & 1);
    523                 const float m = (float)(cos(xm * 2) * sin(ym * 4) * 0.75f);
    524                 result->vertexArray[currentVertex * 2] = xm * scale + m;
    525                 result->vertexArray[currentVertex * 2 + 1] = ym * scale + m;
    526                 ++currentVertex;
    527             }
    528             ++currentQuad;
    529         }
    530     }
    531 #ifdef SAN_ANGELES_OBSERVATION_GLES
    532     result->shaderProgram = sShaderFlat.program;
    533 #endif  // SAN_ANGELES_OBSERVATION_GLES
    534     return result;
    535 }
    536 
    537 
    538 static void drawGroundPlane()
    539 {
    540     glDisable(GL_CULL_FACE);
    541     glDisable(GL_DEPTH_TEST);
    542     glEnable(GL_BLEND);
    543     glBlendFunc(GL_ZERO, GL_SRC_COLOR);
    544 #ifndef SAN_ANGELES_OBSERVATION_GLES
    545     glDisable(GL_LIGHTING);
    546 #endif  // !SAN_ANGELES_OBSERVATION_GLES
    547 
    548     drawGLObject(sGroundPlane);
    549 
    550 #ifndef SAN_ANGELES_OBSERVATION_GLES
    551     glEnable(GL_LIGHTING);
    552 #endif  // !SAN_ANGELES_OBSERVATION_GLES
    553     glDisable(GL_BLEND);
    554     glEnable(GL_DEPTH_TEST);
    555 }
    556 
    557 
    558 static GLOBJECT * createFadeQuad()
    559 {
    560     static const GLfloat quadVertices[] = {
    561         -1, -1,
    562          1, -1,
    563         -1,  1,
    564          1, -1,
    565          1,  1,
    566         -1,  1
    567     };
    568 
    569     GLOBJECT *result;
    570     int i;
    571 
    572     result = newGLObject(6, 2, 0, 0);
    573     if (result == NULL)
    574         return NULL;
    575 
    576     for (i = 0; i < 12; ++i)
    577         result->vertexArray[i] = quadVertices[i];
    578 
    579 #ifdef SAN_ANGELES_OBSERVATION_GLES
    580     result->shaderProgram = sShaderFade.program;
    581 #endif  // SAN_ANGELES_OBSERVATION_GLES
    582     return result;
    583 }
    584 
    585 
    586 static void drawFadeQuad()
    587 {
    588     const int beginFade = sTick - sCurrentCamTrackStartTick;
    589     const int endFade = sNextCamTrackStartTick - sTick;
    590     const int minFade = beginFade < endFade ? beginFade : endFade;
    591 
    592     if (minFade < 1024)
    593     {
    594         const GLfloat fadeColor = minFade / 1024.f;
    595         glDisable(GL_DEPTH_TEST);
    596         glEnable(GL_BLEND);
    597         glBlendFunc(GL_ZERO, GL_SRC_COLOR);
    598 #ifdef SAN_ANGELES_OBSERVATION_GLES
    599         bindShaderProgram(sShaderFade.program);
    600         glUniform1f(sShaderFade.minFade, fadeColor);
    601         glVertexAttribPointer(sShaderFade.pos, 2, GL_FLOAT, GL_FALSE, 0,
    602                               (GLvoid *)sFadeQuad->vertexArrayOffset);
    603         glEnableVertexAttribArray(sShaderFade.pos);
    604         glDrawArrays(GL_TRIANGLES, 0, 6);
    605         glDisableVertexAttribArray(sShaderFade.pos);
    606 #else  // !SAN_ANGELES_OBSERVATION_GLES
    607         glColor4f(fadeColor, fadeColor, fadeColor, 0);
    608 
    609         glDisable(GL_LIGHTING);
    610 
    611         glMatrixMode(GL_MODELVIEW);
    612         glLoadIdentity();
    613 
    614         glMatrixMode(GL_PROJECTION);
    615         glLoadIdentity();
    616 
    617         glDisableClientState(GL_COLOR_ARRAY);
    618         glDisableClientState(GL_NORMAL_ARRAY);
    619         glVertexPointer(2, GL_FLOAT, 0, (GLvoid *)sFadeQuad->vertexArrayOffset);
    620 
    621         glDrawArrays(GL_TRIANGLES, 0, 6);
    622 
    623         glEnableClientState(GL_COLOR_ARRAY);
    624 
    625         glMatrixMode(GL_MODELVIEW);
    626 
    627         glEnable(GL_LIGHTING);
    628 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    629         glDisable(GL_BLEND);
    630         glEnable(GL_DEPTH_TEST);
    631     }
    632 }
    633 
    634 
    635 // Called from the app framework.
    636 int appInit()
    637 {
    638     int a;
    639     static GLfloat light0Diffuse[] = { 1.f, 0.4f, 0, 1.f };
    640     static GLfloat light1Diffuse[] = { 0.07f, 0.14f, 0.35f, 1.f };
    641     static GLfloat light2Diffuse[] = { 0.07f, 0.17f, 0.14f, 1.f };
    642     static GLfloat materialSpecular[] = { 1.f, 1.f, 1.f, 1.f };
    643 #ifdef SAN_ANGELES_OBSERVATION_GLES
    644     static GLfloat lightAmbient[] = { 0.2f, 0.2f, 0.2f, 1.f };
    645 #endif  // SAN_ANGELES_OBSERVATION_GLES
    646 
    647     glDisable(GL_CULL_FACE);
    648     glEnable(GL_DEPTH_TEST);
    649 #ifdef SAN_ANGELES_OBSERVATION_GLES
    650     if (initShaderPrograms() == 0)
    651     {
    652         fprintf(stderr, "Error: initShaderPrograms failed\n");
    653         return 0;
    654     }
    655 #else  // !SAN_ANGELES_OBSERVATION_GLES
    656     glShadeModel(GL_FLAT);
    657     glEnable(GL_NORMALIZE);
    658 
    659     glEnable(GL_LIGHTING);
    660     glEnable(GL_LIGHT0);
    661     glEnable(GL_LIGHT1);
    662     glEnable(GL_LIGHT2);
    663 
    664     glEnableClientState(GL_VERTEX_ARRAY);
    665     glEnableClientState(GL_COLOR_ARRAY);
    666 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    667     seedRandom(15);
    668 
    669     for (a = 0; a < SUPERSHAPE_COUNT; ++a)
    670     {
    671         sSuperShapeObjects[a] = createSuperShape(sSuperShapeParams[a]);
    672         assert(sSuperShapeObjects[a] != NULL);
    673     }
    674     sGroundPlane = createGroundPlane();
    675     assert(sGroundPlane != NULL);
    676     sFadeQuad = createFadeQuad();
    677     assert(sFadeQuad != NULL);
    678     sVBO = createVBO(sSuperShapeObjects, SUPERSHAPE_COUNT,
    679                      sGroundPlane, sFadeQuad);
    680 
    681     // setup non-changing lighting parameters
    682 #ifdef SAN_ANGELES_OBSERVATION_GLES
    683     bindShaderProgram(sShaderLit.program);
    684     glUniform4fv(sShaderLit.ambient, 1, lightAmbient);
    685     glUniform4fv(sShaderLit.light_0_diffuse, 1, light0Diffuse);
    686     glUniform4fv(sShaderLit.light_1_diffuse, 1, light1Diffuse);
    687     glUniform4fv(sShaderLit.light_2_diffuse, 1, light2Diffuse);
    688     glUniform4fv(sShaderLit.light_0_specular, 1, materialSpecular);
    689     glUniform1f(sShaderLit.shininess, 60.f);
    690 #else  // !SAN_ANGELES_OBSERVATION_GLES
    691     glLightfv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse);
    692     glLightfv(GL_LIGHT1, GL_DIFFUSE, light1Diffuse);
    693     glLightfv(GL_LIGHT2, GL_DIFFUSE, light2Diffuse);
    694     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, materialSpecular);
    695     glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 60);
    696 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    697     return 1;
    698 }
    699 
    700 
    701 // Called from the app framework.
    702 void appDeinit()
    703 {
    704     int a;
    705     for (a = 0; a < SUPERSHAPE_COUNT; ++a)
    706         freeGLObject(sSuperShapeObjects[a]);
    707     freeGLObject(sGroundPlane);
    708     freeGLObject(sFadeQuad);
    709     glDeleteBuffers(1, &sVBO);
    710 #ifdef SAN_ANGELES_OBSERVATION_GLES
    711     deInitShaderPrograms();
    712 #endif  // SAN_ANGELES_OBSERVATION_GLES
    713 }
    714 
    715 #ifndef SAN_ANGELES_OBSERVATION_GLES
    716 static void gluPerspective(GLfloat fovy, GLfloat aspect,
    717                            GLfloat zNear, GLfloat zFar)
    718 {
    719     GLfloat xmin, xmax, ymin, ymax;
    720 
    721     ymax = zNear * (GLfloat)tan(fovy * PI / 360);
    722     ymin = -ymax;
    723     xmin = ymin * aspect;
    724     xmax = ymax * aspect;
    725 
    726     glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
    727 }
    728 #endif  // !SAN_ANGELES_OBSERVATION_GLES
    729 
    730 static void prepareFrame(int width, int height)
    731 {
    732     glViewport(0, 0, width, height);
    733 
    734     glClearColor(0.1f, 0.2f, 0.3f, 1.f);
    735     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    736 
    737 #ifdef SAN_ANGELES_OBSERVATION_GLES
    738     Matrix4x4_LoadIdentity(sProjection);
    739     Matrix4x4_Perspective(sProjection,
    740                           45.f, (float)width / height, 0.5f, 150);
    741 
    742     Matrix4x4_LoadIdentity(sModelView);
    743 #else  // !SAN_ANGELES_OBSERVATION_GLES
    744     glMatrixMode(GL_PROJECTION);
    745     glLoadIdentity();
    746     gluPerspective(45, (float)width / height, 0.5f, 150);
    747 
    748     glMatrixMode(GL_MODELVIEW);
    749     glLoadIdentity();
    750 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    751 }
    752 
    753 
    754 static void configureLightAndMaterial()
    755 {
    756     GLfloat light0Position[] = { -4.f, 1.f, 1.f, 0 };
    757     GLfloat light1Position[] = { 1.f, -2.f, -1.f, 0 };
    758     GLfloat light2Position[] = { -1.f, 0, -4.f, 0 };
    759 
    760 #ifdef SAN_ANGELES_OBSERVATION_GLES
    761     Matrix4x4_Transform(sModelView,
    762                         light0Position, light0Position + 1, light0Position + 2);
    763     Matrix4x4_Transform(sModelView,
    764                         light1Position, light1Position + 1, light1Position + 2);
    765     Matrix4x4_Transform(sModelView,
    766                         light2Position, light2Position + 1, light2Position + 2);
    767 
    768     bindShaderProgram(sShaderLit.program);
    769     glUniform3fv(sShaderLit.light_0_direction, 1, light0Position);
    770     glUniform3fv(sShaderLit.light_1_direction, 1, light1Position);
    771     glUniform3fv(sShaderLit.light_2_direction, 1, light2Position);
    772 #else  // !SAN_ANGELES_OBSERVATION_GLES
    773     glLightfv(GL_LIGHT0, GL_POSITION, light0Position);
    774     glLightfv(GL_LIGHT1, GL_POSITION, light1Position);
    775     glLightfv(GL_LIGHT2, GL_POSITION, light2Position);
    776 
    777     glEnable(GL_COLOR_MATERIAL);
    778 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    779 }
    780 
    781 
    782 static void drawModels(float zScale)
    783 {
    784     const int translationScale = 9;
    785     int x, y;
    786 
    787     seedRandom(9);
    788 
    789 #ifdef SAN_ANGELES_OBSERVATION_GLES
    790     Matrix4x4_Scale(sModelView, 1.f, 1.f, zScale);
    791 #else  // !SAN_ANGELES_OBSERVATION_GLES
    792     glScalef(1.f, 1.f, zScale);
    793 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    794 
    795     for (y = -5; y <= 5; ++y)
    796     {
    797         for (x = -5; x <= 5; ++x)
    798         {
    799             float buildingScale;
    800 #ifdef SAN_ANGELES_OBSERVATION_GLES
    801             Matrix4x4 tmp;
    802 #endif  // SAN_ANGELES_OBSERVATION_GLES
    803 
    804             int curShape = randomUInt() % SUPERSHAPE_COUNT;
    805             buildingScale = sSuperShapeParams[curShape][SUPERSHAPE_PARAMS - 1];
    806 #ifdef SAN_ANGELES_OBSERVATION_GLES
    807             Matrix4x4_Copy(tmp, sModelView);
    808             Matrix4x4_Translate(sModelView, x * translationScale,
    809                                 y * translationScale, 0);
    810             Matrix4x4_Rotate(sModelView, randomUInt() % 360, 0, 0, 1.f);
    811             Matrix4x4_Scale(sModelView,
    812                             buildingScale, buildingScale, buildingScale);
    813 
    814             drawGLObject(sSuperShapeObjects[curShape]);
    815             Matrix4x4_Copy(sModelView, tmp);
    816 #else  // !SAN_ANGELES_OBSERVATION_GLES
    817             glPushMatrix();
    818             glTranslatef(x * translationScale, y * translationScale, 0);
    819             glRotatef(randomUInt() % 360, 0, 0, 1.f);
    820             glScalef(buildingScale, buildingScale, buildingScale);
    821 
    822             drawGLObject(sSuperShapeObjects[curShape]);
    823             glPopMatrix();
    824 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    825         }
    826     }
    827 
    828     for (x = -2; x <= 2; ++x)
    829     {
    830         const int shipScale100 = translationScale * 500;
    831         const int offs100 = x * shipScale100 + (sTick % shipScale100);
    832         float offs = offs100 * 0.01f;
    833 #ifdef SAN_ANGELES_OBSERVATION_GLES
    834         Matrix4x4 tmp;
    835         Matrix4x4_Copy(tmp, sModelView);
    836         Matrix4x4_Translate(sModelView, offs, -4.f, 2.f);
    837         drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]);
    838         Matrix4x4_Copy(sModelView, tmp);
    839         Matrix4x4_Translate(sModelView, -4.f, offs, 4.f);
    840         Matrix4x4_Rotate(sModelView, 90.f, 0, 0, 1.f);
    841         drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]);
    842         Matrix4x4_Copy(sModelView, tmp);
    843 #else  // !SAN_ANGELES_OBSERVATION_GLES
    844         glPushMatrix();
    845         glTranslatef(offs, -4.f, 2.f);
    846         drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]);
    847         glPopMatrix();
    848         glPushMatrix();
    849         glTranslatef(-4.f, offs, 4.f);
    850         glRotatef(90.f, 0, 0, 1.f);
    851         drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]);
    852         glPopMatrix();
    853 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    854     }
    855 }
    856 
    857 /* Following gluLookAt implementation is adapted from the
    858  * Mesa 3D Graphics library. http://www.mesa3d.org
    859  */
    860 static void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
    861 	              GLfloat centerx, GLfloat centery, GLfloat centerz,
    862 	              GLfloat upx, GLfloat upy, GLfloat upz)
    863 {
    864 #ifdef SAN_ANGELES_OBSERVATION_GLES
    865     Matrix4x4 m;
    866 #else  // !SAN_ANGELES_OBSERVATION_GLES
    867     GLfloat m[16];
    868 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    869     GLfloat x[3], y[3], z[3];
    870     GLfloat mag;
    871 
    872     /* Make rotation matrix */
    873 
    874     /* Z vector */
    875     z[0] = eyex - centerx;
    876     z[1] = eyey - centery;
    877     z[2] = eyez - centerz;
    878     mag = (float)sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
    879     if (mag) {			/* mpichler, 19950515 */
    880         z[0] /= mag;
    881         z[1] /= mag;
    882         z[2] /= mag;
    883     }
    884 
    885     /* Y vector */
    886     y[0] = upx;
    887     y[1] = upy;
    888     y[2] = upz;
    889 
    890     /* X vector = Y cross Z */
    891     x[0] = y[1] * z[2] - y[2] * z[1];
    892     x[1] = -y[0] * z[2] + y[2] * z[0];
    893     x[2] = y[0] * z[1] - y[1] * z[0];
    894 
    895     /* Recompute Y = Z cross X */
    896     y[0] = z[1] * x[2] - z[2] * x[1];
    897     y[1] = -z[0] * x[2] + z[2] * x[0];
    898     y[2] = z[0] * x[1] - z[1] * x[0];
    899 
    900     /* mpichler, 19950515 */
    901     /* cross product gives area of parallelogram, which is < 1.0 for
    902      * non-perpendicular unit-length vectors; so normalize x, y here
    903      */
    904 
    905     mag = (float)sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
    906     if (mag) {
    907         x[0] /= mag;
    908         x[1] /= mag;
    909         x[2] /= mag;
    910     }
    911 
    912     mag = (float)sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]);
    913     if (mag) {
    914         y[0] /= mag;
    915         y[1] /= mag;
    916         y[2] /= mag;
    917     }
    918 
    919 #ifdef SAN_ANGELES_OBSERVATION_GLES
    920 #define M(row, col) m[col*4 + row]
    921     M(0, 0) = x[0];
    922     M(0, 1) = x[1];
    923     M(0, 2) = x[2];
    924     M(0, 3) = 0.0;
    925     M(1, 0) = y[0];
    926     M(1, 1) = y[1];
    927     M(1, 2) = y[2];
    928     M(1, 3) = 0.0;
    929     M(2, 0) = z[0];
    930     M(2, 1) = z[1];
    931     M(2, 2) = z[2];
    932     M(2, 3) = 0.0;
    933     M(3, 0) = 0.0;
    934     M(3, 1) = 0.0;
    935     M(3, 2) = 0.0;
    936     M(3, 3) = 1.0;
    937 #undef M
    938 
    939     Matrix4x4_Multiply(sModelView, m, sModelView);
    940 
    941     Matrix4x4_Translate(sModelView, -eyex, -eyey, -eyez);
    942 #else  // !SAN_ANGELES_OBSERVATION_GLES
    943 #define M(row, col)  m[col*4 + row]
    944     M(0, 0) = x[0];
    945     M(0, 1) = x[1];
    946     M(0, 2) = x[2];
    947     M(0, 3) = 0.0;
    948     M(1, 0) = y[0];
    949     M(1, 1) = y[1];
    950     M(1, 2) = y[2];
    951     M(1, 3) = 0.0;
    952     M(2, 0) = z[0];
    953     M(2, 1) = z[1];
    954     M(2, 2) = z[2];
    955     M(2, 3) = 0.0;
    956     M(3, 0) = 0.0;
    957     M(3, 1) = 0.0;
    958     M(3, 2) = 0.0;
    959     M(3, 3) = 1.0;
    960 #undef M
    961 
    962     glMultMatrixf(m);
    963 
    964     /* Translate Eye to Origin */
    965     glTranslatef(-eyex, -eyey, -eyez);
    966 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
    967 }
    968 
    969 static void camTrack()
    970 {
    971     float lerp[5];
    972     float eX, eY, eZ, cX, cY, cZ;
    973     float trackPos;
    974     CAMTRACK *cam;
    975     long currentCamTick;
    976     int a;
    977 
    978     if (sNextCamTrackStartTick <= sTick)
    979     {
    980         ++sCurrentCamTrack;
    981         sCurrentCamTrackStartTick = sNextCamTrackStartTick;
    982     }
    983     sNextCamTrackStartTick = sCurrentCamTrackStartTick +
    984                              sCamTracks[sCurrentCamTrack].len * CAMTRACK_LEN;
    985 
    986     cam = &sCamTracks[sCurrentCamTrack];
    987     currentCamTick = sTick - sCurrentCamTrackStartTick;
    988     trackPos = (float)currentCamTick / (CAMTRACK_LEN * cam->len);
    989 
    990     for (a = 0; a < 5; ++a)
    991         lerp[a] = (cam->src[a] + cam->dest[a] * trackPos) * 0.01f;
    992 
    993     if (cam->dist)
    994     {
    995         float dist = cam->dist * 0.1f;
    996         cX = lerp[0];
    997         cY = lerp[1];
    998         cZ = lerp[2];
    999         eX = cX - (float)cos(lerp[3]) * dist;
   1000         eY = cY - (float)sin(lerp[3]) * dist;
   1001         eZ = cZ - lerp[4];
   1002     }
   1003     else
   1004     {
   1005         eX = lerp[0];
   1006         eY = lerp[1];
   1007         eZ = lerp[2];
   1008         cX = eX + (float)cos(lerp[3]);
   1009         cY = eY + (float)sin(lerp[3]);
   1010         cZ = eZ + lerp[4];
   1011     }
   1012     gluLookAt(eX, eY, eZ, cX, cY, cZ, 0, 0, 1);
   1013 }
   1014 
   1015 
   1016 // Called from the app framework.
   1017 /* The tick is current time in milliseconds, width and height
   1018  * are the image dimensions to be rendered.
   1019  */
   1020 void appRender(long tick, int width, int height)
   1021 {
   1022 #ifdef SAN_ANGELES_OBSERVATION_GLES
   1023     Matrix4x4 tmp;
   1024 #endif  // SAN_ANGELES_OBSERVATION_GLES
   1025 
   1026     if (sStartTick == 0)
   1027         sStartTick = tick;
   1028     if (!gAppAlive)
   1029         return;
   1030 
   1031     // Actual tick value is "blurred" a little bit.
   1032     sTick = (sTick + tick - sStartTick) >> 1;
   1033 
   1034     // Terminate application after running through the demonstration once.
   1035     if (sTick >= RUN_LENGTH)
   1036     {
   1037         gAppAlive = 0;
   1038         return;
   1039     }
   1040 
   1041     // Prepare OpenGL ES for rendering of the frame.
   1042     prepareFrame(width, height);
   1043 
   1044     // Update the camera position and set the lookat.
   1045     camTrack();
   1046 
   1047     // Configure environment.
   1048     configureLightAndMaterial();
   1049 
   1050     // Draw the reflection by drawing models with negated Z-axis.
   1051 #ifdef SAN_ANGELES_OBSERVATION_GLES
   1052     Matrix4x4_Copy(tmp, sModelView);
   1053     drawModels(-1);
   1054     Matrix4x4_Copy(sModelView, tmp);
   1055 #else  // !SAN_ANGELES_OBSERVATION_GLES
   1056     glPushMatrix();
   1057     drawModels(-1);
   1058     glPopMatrix();
   1059 #endif  // SAN_ANGELES_OBSERVATION_GLES | !SAN_ANGELES_OBSERVATION_GLES
   1060 
   1061     // Blend the ground plane to the window.
   1062     drawGroundPlane();
   1063 
   1064     // Draw all the models normally.
   1065     drawModels(1);
   1066 
   1067     // Draw fade quad over whole window (when changing cameras).
   1068     drawFadeQuad();
   1069 }
   1070 
   1071