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
60static unsigned long sRandomSeed = 0;
61
62static void seedRandom(unsigned long seed)
63{
64    sRandomSeed = seed;
65}
66
67static 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.
75typedef 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
103static long sStartTick = 0;
104static long sTick = 0;
105
106static int sCurrentCamTrack = 0;
107static long sCurrentCamTrackStartTick = 0;
108static long sNextCamTrackStartTick = 0x7fffffff;
109
110static GLOBJECT *sSuperShapeObjects[SUPERSHAPE_COUNT] = { NULL };
111static GLOBJECT *sGroundPlane = NULL;
112static GLOBJECT *sFadeQuad = NULL;
113
114static GLuint sVBO = 0;
115
116typedef struct {
117    float x, y, z;
118} VECTOR3;
119
120
121static 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
134static 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
184static 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
212static 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
244static 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
305static 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
313static 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
322static 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/
332static 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
483static 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
538static 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
558static 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
586static 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.
636int 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.
702void 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
716static 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
730static 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
754static 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
782static 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 */
860static 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
969static 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 */
1020void 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