|  | /* San Angeles Observation OpenGL ES version example | 
|  | * Copyright 2004-2005 Jetro Lauha | 
|  | * All rights reserved. | 
|  | * Web: http://iki.fi/jetro/ | 
|  | * | 
|  | * This source is free software; you can redistribute it and/or | 
|  | * modify it under the terms of EITHER: | 
|  | *   (1) The GNU Lesser General Public License as published by the Free | 
|  | *       Software Foundation; either version 2.1 of the License, or (at | 
|  | *       your option) any later version. The text of the GNU Lesser | 
|  | *       General Public License is included with this source in the | 
|  | *       file LICENSE-LGPL.txt. | 
|  | *   (2) The BSD-style license that is included with this source in | 
|  | *       the file LICENSE-BSD.txt. | 
|  | * | 
|  | * This source is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files | 
|  | * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details. | 
|  | * | 
|  | * $Id: demo.c,v 1.10 2005/02/08 20:54:39 tonic Exp $ | 
|  | * $Revision: 1.10 $ | 
|  | */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <math.h> | 
|  | #include <float.h> | 
|  | #include <assert.h> | 
|  |  | 
|  | #include <GLES/gl.h> | 
|  |  | 
|  | #include "app.h" | 
|  | #include "shapes.h" | 
|  | #include "cams.h" | 
|  |  | 
|  |  | 
|  | // Total run length is 20 * camera track base unit length (see cams.h). | 
|  | #define RUN_LENGTH  (20 * CAMTRACK_LEN) | 
|  | #undef PI | 
|  | #define PI 3.1415926535897932f | 
|  | #define RANDOM_UINT_MAX 65535 | 
|  |  | 
|  |  | 
|  | static unsigned long sRandomSeed = 0; | 
|  |  | 
|  | static void seedRandom(unsigned long seed) | 
|  | { | 
|  | sRandomSeed = seed; | 
|  | } | 
|  |  | 
|  | static unsigned long randomUInt() | 
|  | { | 
|  | sRandomSeed = sRandomSeed * 0x343fd + 0x269ec3; | 
|  | return sRandomSeed >> 16; | 
|  | } | 
|  |  | 
|  |  | 
|  | // Capped conversion from float to fixed. | 
|  | static long floatToFixed(float value) | 
|  | { | 
|  | if (value < -32768) value = -32768; | 
|  | if (value > 32767) value = 32767; | 
|  | return (long)(value * 65536); | 
|  | } | 
|  |  | 
|  | #define FIXED(value) floatToFixed(value) | 
|  |  | 
|  |  | 
|  | // Definition of one GL object in this demo. | 
|  | typedef struct { | 
|  | /* Vertex array and color array are enabled for all objects, so their | 
|  | * pointers must always be valid and non-NULL. Normal array is not | 
|  | * used by the ground plane, so when its pointer is NULL then normal | 
|  | * array usage is disabled. | 
|  | * | 
|  | * Vertex array is supposed to use GL_FIXED datatype and stride 0 | 
|  | * (i.e. tightly packed array). Color array is supposed to have 4 | 
|  | * components per color with GL_UNSIGNED_BYTE datatype and stride 0. | 
|  | * Normal array is supposed to use GL_FIXED datatype and stride 0. | 
|  | */ | 
|  | GLfixed *vertexArray; | 
|  | GLubyte *colorArray; | 
|  | GLfixed *normalArray; | 
|  | GLint vertexComponents; | 
|  | GLsizei count; | 
|  | } GLOBJECT; | 
|  |  | 
|  |  | 
|  | static long sStartTick = 0; | 
|  | static long sTick = 0; | 
|  |  | 
|  | static int sCurrentCamTrack = 0; | 
|  | static long sCurrentCamTrackStartTick = 0; | 
|  | static long sNextCamTrackStartTick = 0x7fffffff; | 
|  |  | 
|  | static GLOBJECT *sSuperShapeObjects[SUPERSHAPE_COUNT] = { NULL }; | 
|  | static GLOBJECT *sGroundPlane = NULL; | 
|  |  | 
|  |  | 
|  | typedef struct { | 
|  | float x, y, z; | 
|  | } VECTOR3; | 
|  |  | 
|  |  | 
|  | static void freeGLObject(GLOBJECT *object) | 
|  | { | 
|  | if (object == NULL) | 
|  | return; | 
|  | free(object->normalArray); | 
|  | free(object->colorArray); | 
|  | free(object->vertexArray); | 
|  | free(object); | 
|  | } | 
|  |  | 
|  |  | 
|  | static GLOBJECT * newGLObject(long vertices, int vertexComponents, | 
|  | int useNormalArray) | 
|  | { | 
|  | GLOBJECT *result; | 
|  | result = (GLOBJECT *)malloc(sizeof(GLOBJECT)); | 
|  | if (result == NULL) | 
|  | return NULL; | 
|  | result->count = vertices; | 
|  | result->vertexComponents = vertexComponents; | 
|  | result->vertexArray = (GLfixed *)malloc(vertices * vertexComponents * | 
|  | sizeof(GLfixed)); | 
|  | result->colorArray = (GLubyte *)malloc(vertices * 4 * sizeof(GLubyte)); | 
|  | if (useNormalArray) | 
|  | { | 
|  | result->normalArray = (GLfixed *)malloc(vertices * 3 * | 
|  | sizeof(GLfixed)); | 
|  | } | 
|  | else | 
|  | result->normalArray = NULL; | 
|  | if (result->vertexArray == NULL || | 
|  | result->colorArray == NULL || | 
|  | (useNormalArray && result->normalArray == NULL)) | 
|  | { | 
|  | freeGLObject(result); | 
|  | return NULL; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void drawGLObject(GLOBJECT *object) | 
|  | { | 
|  | assert(object != NULL); | 
|  |  | 
|  | glVertexPointer(object->vertexComponents, GL_FIXED, | 
|  | 0, object->vertexArray); | 
|  | glColorPointer(4, GL_UNSIGNED_BYTE, 0, object->colorArray); | 
|  |  | 
|  | // Already done in initialization: | 
|  | //glEnableClientState(GL_VERTEX_ARRAY); | 
|  | //glEnableClientState(GL_COLOR_ARRAY); | 
|  |  | 
|  | if (object->normalArray) | 
|  | { | 
|  | glNormalPointer(GL_FIXED, 0, object->normalArray); | 
|  | glEnableClientState(GL_NORMAL_ARRAY); | 
|  | } | 
|  | else | 
|  | glDisableClientState(GL_NORMAL_ARRAY); | 
|  | glDrawArrays(GL_TRIANGLES, 0, object->count); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void vector3Sub(VECTOR3 *dest, VECTOR3 *v1, VECTOR3 *v2) | 
|  | { | 
|  | dest->x = v1->x - v2->x; | 
|  | dest->y = v1->y - v2->y; | 
|  | dest->z = v1->z - v2->z; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void superShapeMap(VECTOR3 *point, float r1, float r2, float t, float p) | 
|  | { | 
|  | // sphere-mapping of supershape parameters | 
|  | point->x = (float)(cos(t) * cos(p) / r1 / r2); | 
|  | point->y = (float)(sin(t) * cos(p) / r1 / r2); | 
|  | point->z = (float)(sin(p) / r2); | 
|  | } | 
|  |  | 
|  |  | 
|  | static float ssFunc(const float t, const float *p) | 
|  | { | 
|  | return (float)(pow(pow(fabs(cos(p[0] * t / 4)) / p[1], p[4]) + | 
|  | pow(fabs(sin(p[0] * t / 4)) / p[2], p[5]), 1 / p[3])); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Creates and returns a supershape object. | 
|  | // Based on Paul Bourke's POV-Ray implementation. | 
|  | // http://astronomy.swin.edu.au/~pbourke/povray/supershape/ | 
|  | static GLOBJECT * createSuperShape(const float *params) | 
|  | { | 
|  | const int resol1 = (int)params[SUPERSHAPE_PARAMS - 3]; | 
|  | const int resol2 = (int)params[SUPERSHAPE_PARAMS - 2]; | 
|  | // latitude 0 to pi/2 for no mirrored bottom | 
|  | // (latitudeBegin==0 for -pi/2 to pi/2 originally) | 
|  | const int latitudeBegin = resol2 / 4; | 
|  | const int latitudeEnd = resol2 / 2;    // non-inclusive | 
|  | const int longitudeCount = resol1; | 
|  | const int latitudeCount = latitudeEnd - latitudeBegin; | 
|  | const long triangleCount = longitudeCount * latitudeCount * 2; | 
|  | const long vertices = triangleCount * 3; | 
|  | GLOBJECT *result; | 
|  | float baseColor[3]; | 
|  | int a, longitude, latitude; | 
|  | long currentVertex, currentQuad; | 
|  |  | 
|  | result = newGLObject(vertices, 3, 1); | 
|  | if (result == NULL) | 
|  | return NULL; | 
|  |  | 
|  | for (a = 0; a < 3; ++a) | 
|  | baseColor[a] = ((randomUInt() % 155) + 100) / 255.f; | 
|  |  | 
|  | currentQuad = 0; | 
|  | currentVertex = 0; | 
|  |  | 
|  | // longitude -pi to pi | 
|  | for (longitude = 0; longitude < longitudeCount; ++longitude) | 
|  | { | 
|  |  | 
|  | // latitude 0 to pi/2 | 
|  | for (latitude = latitudeBegin; latitude < latitudeEnd; ++latitude) | 
|  | { | 
|  | float t1 = -PI + longitude * 2 * PI / resol1; | 
|  | float t2 = -PI + (longitude + 1) * 2 * PI / resol1; | 
|  | float p1 = -PI / 2 + latitude * 2 * PI / resol2; | 
|  | float p2 = -PI / 2 + (latitude + 1) * 2 * PI / resol2; | 
|  | float r0, r1, r2, r3; | 
|  |  | 
|  | r0 = ssFunc(t1, params); | 
|  | r1 = ssFunc(p1, ¶ms[6]); | 
|  | r2 = ssFunc(t2, params); | 
|  | r3 = ssFunc(p2, ¶ms[6]); | 
|  |  | 
|  | if (r0 != 0 && r1 != 0 && r2 != 0 && r3 != 0) | 
|  | { | 
|  | VECTOR3 pa, pb, pc, pd; | 
|  | VECTOR3 v1, v2, n; | 
|  | float ca; | 
|  | int i; | 
|  | //float lenSq, invLenSq; | 
|  |  | 
|  | superShapeMap(&pa, r0, r1, t1, p1); | 
|  | superShapeMap(&pb, r2, r1, t2, p1); | 
|  | superShapeMap(&pc, r2, r3, t2, p2); | 
|  | superShapeMap(&pd, r0, r3, t1, p2); | 
|  |  | 
|  | // kludge to set lower edge of the object to fixed level | 
|  | if (latitude == latitudeBegin + 1) | 
|  | pa.z = pb.z = 0; | 
|  |  | 
|  | vector3Sub(&v1, &pb, &pa); | 
|  | vector3Sub(&v2, &pd, &pa); | 
|  |  | 
|  | // Calculate normal with cross product. | 
|  | /*   i    j    k      i    j | 
|  | * v1.x v1.y v1.z | v1.x v1.y | 
|  | * v2.x v2.y v2.z | v2.x v2.y | 
|  | */ | 
|  |  | 
|  | n.x = v1.y * v2.z - v1.z * v2.y; | 
|  | n.y = v1.z * v2.x - v1.x * v2.z; | 
|  | n.z = v1.x * v2.y - v1.y * v2.x; | 
|  |  | 
|  | /* Pre-normalization of the normals is disabled here because | 
|  | * they will be normalized anyway later due to automatic | 
|  | * normalization (GL_NORMALIZE). It is enabled because the | 
|  | * objects are scaled with glScale. | 
|  | */ | 
|  | /* | 
|  | lenSq = n.x * n.x + n.y * n.y + n.z * n.z; | 
|  | invLenSq = (float)(1 / sqrt(lenSq)); | 
|  | n.x *= invLenSq; | 
|  | n.y *= invLenSq; | 
|  | n.z *= invLenSq; | 
|  | */ | 
|  |  | 
|  | ca = pa.z + 0.5f; | 
|  |  | 
|  | for (i = currentVertex * 3; | 
|  | i < (currentVertex + 6) * 3; | 
|  | i += 3) | 
|  | { | 
|  | result->normalArray[i] = FIXED(n.x); | 
|  | result->normalArray[i + 1] = FIXED(n.y); | 
|  | result->normalArray[i + 2] = FIXED(n.z); | 
|  | } | 
|  | for (i = currentVertex * 4; | 
|  | i < (currentVertex + 6) * 4; | 
|  | i += 4) | 
|  | { | 
|  | int a, color[3]; | 
|  | for (a = 0; a < 3; ++a) | 
|  | { | 
|  | color[a] = (int)(ca * baseColor[a] * 255); | 
|  | if (color[a] > 255) color[a] = 255; | 
|  | } | 
|  | result->colorArray[i] = (GLubyte)color[0]; | 
|  | result->colorArray[i + 1] = (GLubyte)color[1]; | 
|  | result->colorArray[i + 2] = (GLubyte)color[2]; | 
|  | result->colorArray[i + 3] = 0; | 
|  | } | 
|  | result->vertexArray[currentVertex * 3] = FIXED(pa.x); | 
|  | result->vertexArray[currentVertex * 3 + 1] = FIXED(pa.y); | 
|  | result->vertexArray[currentVertex * 3 + 2] = FIXED(pa.z); | 
|  | ++currentVertex; | 
|  | result->vertexArray[currentVertex * 3] = FIXED(pb.x); | 
|  | result->vertexArray[currentVertex * 3 + 1] = FIXED(pb.y); | 
|  | result->vertexArray[currentVertex * 3 + 2] = FIXED(pb.z); | 
|  | ++currentVertex; | 
|  | result->vertexArray[currentVertex * 3] = FIXED(pd.x); | 
|  | result->vertexArray[currentVertex * 3 + 1] = FIXED(pd.y); | 
|  | result->vertexArray[currentVertex * 3 + 2] = FIXED(pd.z); | 
|  | ++currentVertex; | 
|  | result->vertexArray[currentVertex * 3] = FIXED(pb.x); | 
|  | result->vertexArray[currentVertex * 3 + 1] = FIXED(pb.y); | 
|  | result->vertexArray[currentVertex * 3 + 2] = FIXED(pb.z); | 
|  | ++currentVertex; | 
|  | result->vertexArray[currentVertex * 3] = FIXED(pc.x); | 
|  | result->vertexArray[currentVertex * 3 + 1] = FIXED(pc.y); | 
|  | result->vertexArray[currentVertex * 3 + 2] = FIXED(pc.z); | 
|  | ++currentVertex; | 
|  | result->vertexArray[currentVertex * 3] = FIXED(pd.x); | 
|  | result->vertexArray[currentVertex * 3 + 1] = FIXED(pd.y); | 
|  | result->vertexArray[currentVertex * 3 + 2] = FIXED(pd.z); | 
|  | ++currentVertex; | 
|  | } // r0 && r1 && r2 && r3 | 
|  | ++currentQuad; | 
|  | } // latitude | 
|  | } // longitude | 
|  |  | 
|  | // Set number of vertices in object to the actual amount created. | 
|  | result->count = currentVertex; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | static GLOBJECT * createGroundPlane() | 
|  | { | 
|  | const int scale = 4; | 
|  | const int yBegin = -15, yEnd = 15;    // ends are non-inclusive | 
|  | const int xBegin = -15, xEnd = 15; | 
|  | const long triangleCount = (yEnd - yBegin) * (xEnd - xBegin) * 2; | 
|  | const long vertices = triangleCount * 3; | 
|  | GLOBJECT *result; | 
|  | int x, y; | 
|  | long currentVertex, currentQuad; | 
|  |  | 
|  | result = newGLObject(vertices, 2, 0); | 
|  | if (result == NULL) | 
|  | return NULL; | 
|  |  | 
|  | currentQuad = 0; | 
|  | currentVertex = 0; | 
|  |  | 
|  | for (y = yBegin; y < yEnd; ++y) | 
|  | { | 
|  | for (x = xBegin; x < xEnd; ++x) | 
|  | { | 
|  | GLubyte color; | 
|  | int i, a; | 
|  | color = (GLubyte)((randomUInt() & 0x5f) + 81);  // 101 1111 | 
|  | for (i = currentVertex * 4; i < (currentVertex + 6) * 4; i += 4) | 
|  | { | 
|  | result->colorArray[i] = color; | 
|  | result->colorArray[i + 1] = color; | 
|  | result->colorArray[i + 2] = color; | 
|  | result->colorArray[i + 3] = 0; | 
|  | } | 
|  |  | 
|  | // Axis bits for quad triangles: | 
|  | // x: 011100 (0x1c), y: 110001 (0x31)  (clockwise) | 
|  | // x: 001110 (0x0e), y: 100011 (0x23)  (counter-clockwise) | 
|  | for (a = 0; a < 6; ++a) | 
|  | { | 
|  | const int xm = x + ((0x1c >> a) & 1); | 
|  | const int ym = y + ((0x31 >> a) & 1); | 
|  | const float m = (float)(cos(xm * 2) * sin(ym * 4) * 0.75f); | 
|  | result->vertexArray[currentVertex * 2] = | 
|  | FIXED(xm * scale + m); | 
|  | result->vertexArray[currentVertex * 2 + 1] = | 
|  | FIXED(ym * scale + m); | 
|  | ++currentVertex; | 
|  | } | 
|  | ++currentQuad; | 
|  | } | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void drawGroundPlane() | 
|  | { | 
|  | glDisable(GL_CULL_FACE); | 
|  | glDisable(GL_DEPTH_TEST); | 
|  | glEnable(GL_BLEND); | 
|  | glBlendFunc(GL_ZERO, GL_SRC_COLOR); | 
|  | glDisable(GL_LIGHTING); | 
|  |  | 
|  | drawGLObject(sGroundPlane); | 
|  |  | 
|  | glEnable(GL_LIGHTING); | 
|  | glDisable(GL_BLEND); | 
|  | glEnable(GL_DEPTH_TEST); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void drawFadeQuad() | 
|  | { | 
|  | static const GLfixed quadVertices[] = { | 
|  | -0x10000, -0x10000, | 
|  | 0x10000, -0x10000, | 
|  | -0x10000,  0x10000, | 
|  | 0x10000, -0x10000, | 
|  | 0x10000,  0x10000, | 
|  | -0x10000,  0x10000 | 
|  | }; | 
|  |  | 
|  | const int beginFade = sTick - sCurrentCamTrackStartTick; | 
|  | const int endFade = sNextCamTrackStartTick - sTick; | 
|  | const int minFade = beginFade < endFade ? beginFade : endFade; | 
|  |  | 
|  | if (minFade < 1024) | 
|  | { | 
|  | const GLfixed fadeColor = minFade << 6; | 
|  | glColor4x(fadeColor, fadeColor, fadeColor, 0); | 
|  |  | 
|  | glDisable(GL_DEPTH_TEST); | 
|  | glEnable(GL_BLEND); | 
|  | glBlendFunc(GL_ZERO, GL_SRC_COLOR); | 
|  | glDisable(GL_LIGHTING); | 
|  |  | 
|  | glMatrixMode(GL_MODELVIEW); | 
|  | glLoadIdentity(); | 
|  |  | 
|  | glMatrixMode(GL_PROJECTION); | 
|  | glLoadIdentity(); | 
|  |  | 
|  | glDisableClientState(GL_COLOR_ARRAY); | 
|  | glDisableClientState(GL_NORMAL_ARRAY); | 
|  | glVertexPointer(2, GL_FIXED, 0, quadVertices); | 
|  | glDrawArrays(GL_TRIANGLES, 0, 6); | 
|  |  | 
|  | glEnableClientState(GL_COLOR_ARRAY); | 
|  |  | 
|  | glMatrixMode(GL_MODELVIEW); | 
|  |  | 
|  | glEnable(GL_LIGHTING); | 
|  | glDisable(GL_BLEND); | 
|  | glEnable(GL_DEPTH_TEST); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Called from the app framework. | 
|  | void appInit() | 
|  | { | 
|  | int a; | 
|  |  | 
|  | glEnable(GL_NORMALIZE); | 
|  | glEnable(GL_DEPTH_TEST); | 
|  | glDisable(GL_CULL_FACE); | 
|  | glShadeModel(GL_FLAT); | 
|  |  | 
|  | glEnable(GL_LIGHTING); | 
|  | glEnable(GL_LIGHT0); | 
|  | glEnable(GL_LIGHT1); | 
|  | glEnable(GL_LIGHT2); | 
|  |  | 
|  | glEnableClientState(GL_VERTEX_ARRAY); | 
|  | glEnableClientState(GL_COLOR_ARRAY); | 
|  |  | 
|  | seedRandom(15); | 
|  |  | 
|  | for (a = 0; a < SUPERSHAPE_COUNT; ++a) | 
|  | { | 
|  | sSuperShapeObjects[a] = createSuperShape(sSuperShapeParams[a]); | 
|  | assert(sSuperShapeObjects[a] != NULL); | 
|  | } | 
|  | sGroundPlane = createGroundPlane(); | 
|  | assert(sGroundPlane != NULL); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Called from the app framework. | 
|  | void appDeinit() | 
|  | { | 
|  | int a; | 
|  | for (a = 0; a < SUPERSHAPE_COUNT; ++a) | 
|  | freeGLObject(sSuperShapeObjects[a]); | 
|  | freeGLObject(sGroundPlane); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void gluPerspective(GLfloat fovy, GLfloat aspect, | 
|  | GLfloat zNear, GLfloat zFar) | 
|  | { | 
|  | GLfloat xmin, xmax, ymin, ymax; | 
|  |  | 
|  | ymax = zNear * (GLfloat)tan(fovy * PI / 360); | 
|  | ymin = -ymax; | 
|  | xmin = ymin * aspect; | 
|  | xmax = ymax * aspect; | 
|  |  | 
|  | glFrustumx((GLfixed)(xmin * 65536), (GLfixed)(xmax * 65536), | 
|  | (GLfixed)(ymin * 65536), (GLfixed)(ymax * 65536), | 
|  | (GLfixed)(zNear * 65536), (GLfixed)(zFar * 65536)); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void prepareFrame(int width, int height) | 
|  | { | 
|  | glViewport(0, 0, width, height); | 
|  |  | 
|  | glClearColorx((GLfixed)(0.1f * 65536), | 
|  | (GLfixed)(0.2f * 65536), | 
|  | (GLfixed)(0.3f * 65536), 0x10000); | 
|  | glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); | 
|  |  | 
|  | glMatrixMode(GL_PROJECTION); | 
|  | glLoadIdentity(); | 
|  | gluPerspective(45, (float)width / height, 0.5f, 150); | 
|  |  | 
|  | glMatrixMode(GL_MODELVIEW); | 
|  |  | 
|  | glLoadIdentity(); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void configureLightAndMaterial() | 
|  | { | 
|  | static GLfixed light0Position[] = { -0x40000, 0x10000, 0x10000, 0 }; | 
|  | static GLfixed light0Diffuse[] = { 0x10000, 0x6666, 0, 0x10000 }; | 
|  | static GLfixed light1Position[] = { 0x10000, -0x20000, -0x10000, 0 }; | 
|  | static GLfixed light1Diffuse[] = { 0x11eb, 0x23d7, 0x5999, 0x10000 }; | 
|  | static GLfixed light2Position[] = { -0x10000, 0, -0x40000, 0 }; | 
|  | static GLfixed light2Diffuse[] = { 0x11eb, 0x2b85, 0x23d7, 0x10000 }; | 
|  | static GLfixed materialSpecular[] = { 0x10000, 0x10000, 0x10000, 0x10000 }; | 
|  |  | 
|  | glLightxv(GL_LIGHT0, GL_POSITION, light0Position); | 
|  | glLightxv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse); | 
|  | glLightxv(GL_LIGHT1, GL_POSITION, light1Position); | 
|  | glLightxv(GL_LIGHT1, GL_DIFFUSE, light1Diffuse); | 
|  | glLightxv(GL_LIGHT2, GL_POSITION, light2Position); | 
|  | glLightxv(GL_LIGHT2, GL_DIFFUSE, light2Diffuse); | 
|  | glMaterialxv(GL_FRONT_AND_BACK, GL_SPECULAR, materialSpecular); | 
|  |  | 
|  | glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, 60 << 16); | 
|  | glEnable(GL_COLOR_MATERIAL); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void drawModels(float zScale) | 
|  | { | 
|  | const int translationScale = 9; | 
|  | int x, y; | 
|  |  | 
|  | seedRandom(9); | 
|  |  | 
|  | glScalex(1 << 16, 1 << 16, (GLfixed)(zScale * 65536)); | 
|  |  | 
|  | for (y = -5; y <= 5; ++y) | 
|  | { | 
|  | for (x = -5; x <= 5; ++x) | 
|  | { | 
|  | float buildingScale; | 
|  | GLfixed fixedScale; | 
|  |  | 
|  | int curShape = randomUInt() % SUPERSHAPE_COUNT; | 
|  | buildingScale = sSuperShapeParams[curShape][SUPERSHAPE_PARAMS - 1]; | 
|  | fixedScale = (GLfixed)(buildingScale * 65536); | 
|  |  | 
|  | glPushMatrix(); | 
|  | glTranslatex((x * translationScale) * 65536, | 
|  | (y * translationScale) * 65536, | 
|  | 0); | 
|  | glRotatex((GLfixed)((randomUInt() % 360) << 16), 0, 0, 1 << 16); | 
|  | glScalex(fixedScale, fixedScale, fixedScale); | 
|  |  | 
|  | drawGLObject(sSuperShapeObjects[curShape]); | 
|  | glPopMatrix(); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (x = -2; x <= 2; ++x) | 
|  | { | 
|  | const int shipScale100 = translationScale * 500; | 
|  | const int offs100 = x * shipScale100 + (sTick % shipScale100); | 
|  | float offs = offs100 * 0.01f; | 
|  | GLfixed fixedOffs = (GLfixed)(offs * 65536); | 
|  | glPushMatrix(); | 
|  | glTranslatex(fixedOffs, -4 * 65536, 2 << 16); | 
|  | drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]); | 
|  | glPopMatrix(); | 
|  | glPushMatrix(); | 
|  | glTranslatex(-4 * 65536, fixedOffs, 4 << 16); | 
|  | glRotatex(90 << 16, 0, 0, 1 << 16); | 
|  | drawGLObject(sSuperShapeObjects[SUPERSHAPE_COUNT - 1]); | 
|  | glPopMatrix(); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Following gluLookAt implementation is adapted from the | 
|  | * Mesa 3D Graphics library. http://www.mesa3d.org | 
|  | */ | 
|  | static void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez, | 
|  | GLfloat centerx, GLfloat centery, GLfloat centerz, | 
|  | GLfloat upx, GLfloat upy, GLfloat upz) | 
|  | { | 
|  | GLfloat m[16]; | 
|  | GLfloat x[3], y[3], z[3]; | 
|  | GLfloat mag; | 
|  |  | 
|  | /* Make rotation matrix */ | 
|  |  | 
|  | /* Z vector */ | 
|  | z[0] = eyex - centerx; | 
|  | z[1] = eyey - centery; | 
|  | z[2] = eyez - centerz; | 
|  | mag = (float)sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); | 
|  | if (mag) {			/* mpichler, 19950515 */ | 
|  | z[0] /= mag; | 
|  | z[1] /= mag; | 
|  | z[2] /= mag; | 
|  | } | 
|  |  | 
|  | /* Y vector */ | 
|  | y[0] = upx; | 
|  | y[1] = upy; | 
|  | y[2] = upz; | 
|  |  | 
|  | /* X vector = Y cross Z */ | 
|  | x[0] = y[1] * z[2] - y[2] * z[1]; | 
|  | x[1] = -y[0] * z[2] + y[2] * z[0]; | 
|  | x[2] = y[0] * z[1] - y[1] * z[0]; | 
|  |  | 
|  | /* Recompute Y = Z cross X */ | 
|  | y[0] = z[1] * x[2] - z[2] * x[1]; | 
|  | y[1] = -z[0] * x[2] + z[2] * x[0]; | 
|  | y[2] = z[0] * x[1] - z[1] * x[0]; | 
|  |  | 
|  | /* mpichler, 19950515 */ | 
|  | /* cross product gives area of parallelogram, which is < 1.0 for | 
|  | * non-perpendicular unit-length vectors; so normalize x, y here | 
|  | */ | 
|  |  | 
|  | mag = (float)sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); | 
|  | if (mag) { | 
|  | x[0] /= mag; | 
|  | x[1] /= mag; | 
|  | x[2] /= mag; | 
|  | } | 
|  |  | 
|  | mag = (float)sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); | 
|  | if (mag) { | 
|  | y[0] /= mag; | 
|  | y[1] /= mag; | 
|  | y[2] /= mag; | 
|  | } | 
|  |  | 
|  | #define M(row,col)  m[(col)*4+(row)] | 
|  | M(0, 0) = x[0]; | 
|  | M(0, 1) = x[1]; | 
|  | M(0, 2) = x[2]; | 
|  | M(0, 3) = 0.0; | 
|  | M(1, 0) = y[0]; | 
|  | M(1, 1) = y[1]; | 
|  | M(1, 2) = y[2]; | 
|  | M(1, 3) = 0.0; | 
|  | M(2, 0) = z[0]; | 
|  | M(2, 1) = z[1]; | 
|  | M(2, 2) = z[2]; | 
|  | M(2, 3) = 0.0; | 
|  | M(3, 0) = 0.0; | 
|  | M(3, 1) = 0.0; | 
|  | M(3, 2) = 0.0; | 
|  | M(3, 3) = 1.0; | 
|  | #undef M | 
|  | { | 
|  | int a; | 
|  | GLfixed fixedM[16]; | 
|  | for (a = 0; a < 16; ++a) | 
|  | fixedM[a] = (GLfixed)(m[a] * 65536); | 
|  | glMultMatrixx(fixedM); | 
|  | } | 
|  |  | 
|  | /* Translate Eye to Origin */ | 
|  | glTranslatex((GLfixed)(-eyex * 65536), | 
|  | (GLfixed)(-eyey * 65536), | 
|  | (GLfixed)(-eyez * 65536)); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void camTrack() | 
|  | { | 
|  | float lerp[5]; | 
|  | float eX, eY, eZ, cX, cY, cZ; | 
|  | float trackPos; | 
|  | CAMTRACK *cam; | 
|  | long currentCamTick; | 
|  | int a; | 
|  |  | 
|  | if (sNextCamTrackStartTick <= sTick) | 
|  | { | 
|  | ++sCurrentCamTrack; | 
|  | sCurrentCamTrackStartTick = sNextCamTrackStartTick; | 
|  | } | 
|  | sNextCamTrackStartTick = sCurrentCamTrackStartTick + | 
|  | sCamTracks[sCurrentCamTrack].len * CAMTRACK_LEN; | 
|  |  | 
|  | cam = &sCamTracks[sCurrentCamTrack]; | 
|  | currentCamTick = sTick - sCurrentCamTrackStartTick; | 
|  | trackPos = (float)currentCamTick / (CAMTRACK_LEN * cam->len); | 
|  |  | 
|  | for (a = 0; a < 5; ++a) | 
|  | lerp[a] = (cam->src[a] + cam->dest[a] * trackPos) * 0.01f; | 
|  |  | 
|  | if (cam->dist) | 
|  | { | 
|  | float dist = cam->dist * 0.1f; | 
|  | cX = lerp[0]; | 
|  | cY = lerp[1]; | 
|  | cZ = lerp[2]; | 
|  | eX = cX - (float)cos(lerp[3]) * dist; | 
|  | eY = cY - (float)sin(lerp[3]) * dist; | 
|  | eZ = cZ - lerp[4]; | 
|  | } | 
|  | else | 
|  | { | 
|  | eX = lerp[0]; | 
|  | eY = lerp[1]; | 
|  | eZ = lerp[2]; | 
|  | cX = eX + (float)cos(lerp[3]); | 
|  | cY = eY + (float)sin(lerp[3]); | 
|  | cZ = eZ + lerp[4]; | 
|  | } | 
|  | gluLookAt(eX, eY, eZ, cX, cY, cZ, 0, 0, 1); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Called from the app framework. | 
|  | /* The tick is current time in milliseconds, width and height | 
|  | * are the image dimensions to be rendered. | 
|  | */ | 
|  | void appRender(long tick, int width, int height) | 
|  | { | 
|  | if (sStartTick == 0) | 
|  | sStartTick = tick; | 
|  | if (!gAppAlive) | 
|  | return; | 
|  |  | 
|  | // Actual tick value is "blurred" a little bit. | 
|  | sTick = (sTick + tick - sStartTick) >> 1; | 
|  |  | 
|  | // Terminate application after running through the demonstration once. | 
|  | if (sTick >= RUN_LENGTH) | 
|  | { | 
|  | gAppAlive = 0; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Prepare OpenGL ES for rendering of the frame. | 
|  | prepareFrame(width, height); | 
|  |  | 
|  | // Update the camera position and set the lookat. | 
|  | camTrack(); | 
|  |  | 
|  | // Configure environment. | 
|  | configureLightAndMaterial(); | 
|  |  | 
|  | // Draw the reflection by drawing models with negated Z-axis. | 
|  | glPushMatrix(); | 
|  | drawModels(-1); | 
|  | glPopMatrix(); | 
|  |  | 
|  | // Blend the ground plane to the window. | 
|  | drawGroundPlane(); | 
|  |  | 
|  | // Draw all the models normally. | 
|  | drawModels(1); | 
|  |  | 
|  | // Draw fade quad over whole window (when changing cameras). | 
|  | drawFadeQuad(); | 
|  | } |