Purely for amusements sake:
Here's the same source code for a spinny globe as before except
instead of in flash actionscript papervision3d it is in opengl as a
native app in c++.
This was a short hack in my spare time away from CTO'ing so it doesn't
talk to WMS servers, you have to dig up your own earth.bmp, and it
builds only on cygwin. I did however take a minute to clean up the
math slightly from the previous rev. Good luck.
I'm posting it here because the sphere generation tiling approach may
have some utility for developers hacking on say the iphone.
- a
//
// Modified version of nehe tutorial #6 for cygwin windows by anselm
hook - [EMAIL PROTECTED]
//
// most of this code was created by Jeff Molofee '99 (ported to
Linux/GLUT by Richard Campbell '99)
//
//
#include <GL/glut.h> // Header File For The GLUT Library
#include <GL/gl.h> // Header File For The OpenGL32 Library
#include <GL/glu.h> // Header File For The GLu32 Library
#include <unistd.h> // needed to sleep
#include <math.h>
#include <stdio.h>
#include <malloc.h>
/* ASCII code for the escape key. */
#define ESCAPE 27
/* The number of our GLUT window */
int window;
/* rotation angle for the triangle. */
float rtri = 0.0f;
/* rotation angle for the quadrilateral. */
float rquad = 0.0f;
/* storage for one texture */
GLuint texture[1];
/* Image type - contains height, width, and data */
struct Image {
unsigned long sizeX;
unsigned long sizeY;
char *data;
};
typedef struct Image Image;
// quick and dirty bitmap loader...for 24 bit bitmaps with 1 plane only.
// See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/BMP.txt for more info.
int ImageLoad(char *filename, Image *image) {
FILE *file;
unsigned long size; // size of the image in bytes.
unsigned long i; // standard counter.
unsigned short int planes; // number of planes in image
(must be 1)
unsigned short int bpp; // number of bits per pixel (must be 24)
char temp; // temporary color storage for
bgr-rgb conversion.
// make sure the file is there.
if ((file = fopen(filename, "rb"))==NULL)
{
printf("File Not Found : %s\n",filename);
return 0;
}
// seek through the bmp header, up to the width/height:
fseek(file, 18, SEEK_CUR);
// read the width
if ((i = fread(&image->sizeX, 4, 1, file)) != 1) {
printf("Error reading width from %s.\n", filename);
return 0;
}
printf("Width of %s: %lu\n", filename, image->sizeX);
// read the height
if ((i = fread(&image->sizeY, 4, 1, file)) != 1) {
printf("Error reading height from %s.\n", filename);
return 0;
}
printf("Height of %s: %lu\n", filename, image->sizeY);
// calculate the size (assuming 24 bits or 3 bytes per pixel).
size = image->sizeX * image->sizeY * 3;
// read the planes
if ((fread(&planes, 2, 1, file)) != 1) {
printf("Error reading planes from %s.\n", filename);
return 0;
}
if (planes != 1) {
printf("Planes from %s is not 1: %u\n", filename, planes);
return 0;
}
// read the bpp
if ((i = fread(&bpp, 2, 1, file)) != 1) {
printf("Error reading bpp from %s.\n", filename);
return 0;
}
if (bpp != 24) {
printf("Bpp from %s is not 24: %u\n", filename, bpp);
return 0;
}
// seek past the rest of the bitmap header.
fseek(file, 24, SEEK_CUR);
// read the data.
image->data = (char *) malloc(size);
if (image->data == NULL) {
printf("Error allocating memory for color-corrected image data");
return 0;
}
if ((i = fread(image->data, size, 1, file)) != 1) {
printf("Error reading image data from %s.\n", filename);
return 0;
}
for (i=0;i<size;i+=3) { // reverse all of the colors. (bgr -> rgb)
temp = image->data[i];
image->data[i] = image->data[i+2];
image->data[i+2] = temp;
}
// we're done.
return 1;
}
// Load Bitmaps And Convert To Textures
void LoadGLTextures() {
// Load Texture
Image *image1;
// allocate space for texture
image1 = (Image *) malloc(sizeof(Image));
if (image1 == NULL) {
printf("Error allocating space for image");
}
if (!ImageLoad("earth.bmp", image1)) {
return;
}
// Create Texture
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]); // 2d texture (x and y size)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); //
scale linearly when image bigger than texture
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); //
scale linearly when image smalled than texture
// 2d texture, level of detail 0 (normal), 3 components (red,
green, blue), x size from image, y size from image,
// border 0 (normal), rgb color data, unsigned byte data, and
finally the data itself.
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0,
GL_RGB, GL_UNSIGNED_BYTE, image1->data);
};
/* A general OpenGL initialization function. Sets all of the initial
parameters. */
void InitGL(int Width, int Height) // We call this right after our
OpenGL window is created.
{
LoadGLTextures();
glEnable(GL_TEXTURE_2D);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The
Background Color To Black
glClearDepth(1.0); // Enables Clearing Of The
Depth Buffer
glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); // Reset The Projection Matrix
gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f); //
Calculate The Aspect Ratio Of The Window
glMatrixMode(GL_MODELVIEW);
}
void draw_thing(
bool is_flat=false,
float surface=1,
float zoom=0,
float longitude=0,
float latitude=0,
int pw=512,
int ph=512,
int tilepw=256,
int tileph=128
)
{
// size of world
float wwidth = 360.0;
float wheight = 180.0;
// what is the world space tile ratio?
int tileratio = tileph/tilepw;
// how many tiles would we like to use to tile the world at
this zoom?
int tiles = (int)pow(2,zoom);
// how big are the tiles in world space?
float tilegw = wwidth/tiles;
float tilegh = wheight/tiles;
// get a positive tile index
int tilex = (int)round((longitude+(wwidth/2))/tilegw);
int tiley = (int)round((latitude+(wheight/2))/tilegh);
// pick a patch that defines the visible mesh extent at the
chosen
point... can wraparound axes.
int LT = (tilex-(int)round(pw/tilepw/2));
int RT = (tilex+(int)round(pw/tilepw/2));
int TP = (tiley-(int)round(ph/tileph/2));
int BT = (tiley+(int)round(ph/tileph/2));
// hack: due to wraparound at low zoom just show the whole
planet as one tile
if(zoom < 4) {
tiles = 1;
tilex = 0;
tiley = 0;
LT = 0;
RT = 1;
TP = 0;
BT = 1;
}
// decide on an integer subtiling regimen to provide a nice
sense of
curvature for the mesh
int subtile = (is_flat || tiles > 16 ) ? 1 :
((int)ceil(16/tiles));
// make vertices for the mesh; as a series of horizontal
strings of vertices
float m = 0;
int i;
int j;
float x;
float y;
float z = 0;
float t;
float rad1;
float rad2;
float *v;
int vertex_row_length = (RT-LT)*subtile+1;
int vertex_rows_total = (BT-TP)*subtile+1;
int vertices_length = vertex_row_length * vertex_rows_total * 3;
float vertices[vertices_length];
bool test = false;
if(test){glBegin(GL_POINTS);}
for(j=0;j<vertex_rows_total;j++) {
// visit vertical range of
subtile EDGES
if(!is_flat) {
rad1 = 1.0f*(j+TP)/(tiles*subtile);
// fractional degree along
the arc of latitude
y = -surface*cos(rad1*3.14159265f);
// might as well calculate y now
t = surface*sin(rad1*3.14159265f);
// convenience
}
v = 0;
// reset vertex to deal with poles
for(i=0;i<vertex_row_length;i++) {
// visit horizontal range
of subtile EDGES!
if(is_flat) {
x =
surface/wwidth*((i+LT)/subtile*tilegw-(wwidth/2));// scale to
scale size and center
y =
surface/wheight*((j+TP)/subtile*tilegh-(wheight/2))*tileratio;
} else {
rad2 = 2.0f*(i+LT)/(tiles*subtile);
// fractional degree along
arc of longitude
x = -t*sin(rad2*3.14159265f);
z = t*cos(rad2*3.14159265f);
}
// optional optimization
//if(is_flat==false && (rad1==0||rad1>=1 &&
v!=null) { // only
one vertex at pole
// TODO should really enable this to prevent
seams but it
requires tracking vertex_rows...
//} else
{
vertices[(j*vertex_row_length+i)*3+0] =
x;
vertices[(j*vertex_row_length+i)*3+1] =
y;
vertices[(j*vertex_row_length+i)*3+2] =
z;
if(test){glColor3f(m*2,1.0f,1.0f);glVertex3f(x+m,y+m,z+m); m = m + 0.001; }
}
}
}
if(test){glEnd();return;}
/*
// make textures
var key:String;
var m:BitmapFileMaterial;
var materials:MaterialsList = new MaterialsList();
var material_row:Array;
var material_rows:Array = new Array();
for(j=TP;j<BT;j++) {
// visit vertical major tiles range
material_row = new Array();
material_rows.push(material_row);
for(i=LT;i<RT;i++) {
// visit horizontal major tiles range
key = wmsurl
+ "&WIDTH="+tilepw
+ "&HEIGHT="+tileph
+ "&BBOX="
+ (i*tilegw-(wwidth/2))
// degrees right of prime meridian
+ ","
+ (j*tilegh-(wheight/2))
// degrees south of north pole
+ ","
+ (i*tilegw+tilegw-(wwidth/2))
// width
+ ","
+ (j*tilegh+tilegh-(wheight/2))
// height
;
m = new BitmapFileMaterial(key);
m.doubleSided = false;
m.smooth = true;
materials.addMaterial(m,key);
material_row.push(key);
}
}
*/
// make an idealized uv subdivision of a tile
float uv_rows[(subtile+1)*(subtile+1)*2];
for(j=0;j<=subtile;j++) {
// visit vertical subrange EDGES
for(i=0;i<=subtile;i++) {
// visit horizontal subrange EDGES
uv_rows[(j*subtile+j+i)*2+0]=1.0f*i/subtile;
uv_rows[(j*subtile+j+i)*2+1]=1.0f*j/subtile;
}
}
// glEnableClientState(GL_VERTEX_ARRAY);
// glEnableClientState(GL_COLOR_ARRAY);
// make polygons
// TODO: fix: note that there will be a seam in the case of a
sphere
due to lazy closure on wrap
/*
var v1:Vertex3D;
var v2:Vertex3D;
var v3:Vertex3D;
var v4:Vertex3D;
var uv1UV;
var uv2UV;
var uv3UV;
var uv4UV;
var f1:Face3D;
var f2:Face3D;
var faces:Array = new Array();
*/
glBindTexture(GL_TEXTURE_2D,texture[0]);
glBegin(GL_QUADS);
for(j=0;j<vertex_rows_total-1;j++) {
// visit vertical range of tiles
for(i=0;i<vertex_row_length-1;i++) {
// visit horizontal range of tiles
/*
v1 = vertex_rows[j][i];
v2 = vertex_rows[j][i+1];
v3 = vertex_rows[j+1][i+1];
v4 = vertex_rows[j+1][i];
uv1 = uv_rows[j%subtile+0][i%subtile+0];
uv2 = uv_rows[j%subtile+0][i%subtile+1];
uv3 = uv_rows[j%subtile+1][i%subtile+1];
uv4 = uv_rows[j%subtile+1][i%subtile+0];
f1 = new Face3D(new Array(v1,v2,v3), null, new
Array(uv1,uv2,uv3));
f2 = new Face3D(new Array(v3,v4,v1), null, new
Array(uv3,uv4,uv1));
f1.materialName =
material_rows[Math.floor(j/subtile)][Math.floor(i/subtile)];
f2.materialName =
material_rows[Math.floor(j/subtile)][Math.floor(i/subtile)];
faces.push(f1);
faces.push(f2);
*/
x = vertices[(j*vertex_row_length+i)*3+0];
y = vertices[(j*vertex_row_length+i)*3+1];
z = vertices[(j*vertex_row_length+i)*3+2];
glTexCoord2f(
uv_rows[((j%subtile)*(subtile+1)+(i%subtile))*2+0],
uv_rows[((j%subtile)*(subtile+1)+(i%subtile))*2+1]
);
//glColor3f(1.0f,1.0f,0.0f);
glVertex3f(x,y,z);
x = vertices[(j*vertex_row_length+i)*3+0+3];
y = vertices[(j*vertex_row_length+i)*3+1+3];
z = vertices[(j*vertex_row_length+i)*3+2+3];
glTexCoord2f(
uv_rows[((j%subtile)*(subtile+1)+(i%subtile+1))*2+0],
uv_rows[((j%subtile)*(subtile+1)+(i%subtile+1))*2+1]
);
//glColor3f(1.0f,0.5f,0.0f);
glVertex3f(x,y,z);
x =
vertices[(j*vertex_row_length+vertex_row_length+i)*3+0+3];
y =
vertices[(j*vertex_row_length+vertex_row_length+i)*3+1+3];
z =
vertices[(j*vertex_row_length+vertex_row_length+i)*3+2+3];
glTexCoord2f(
uv_rows[((j%subtile+1)*(subtile+1)+(i%subtile+1))*2+0],
uv_rows[((j%subtile+1)*(subtile+1)+(i%subtile+1))*2+1]
);
//glColor3f(0.0f,0.0f,1.0f);
glVertex3f(x,y,z);
x =
vertices[(j*vertex_row_length+vertex_row_length+i)*3+0];
y =
vertices[(j*vertex_row_length+vertex_row_length+i)*3+1];
z =
vertices[(j*vertex_row_length+vertex_row_length+i)*3+2];
glTexCoord2f(
uv_rows[((j%subtile+1)*(subtile+1)+(i%subtile))*2+0],
uv_rows[((j%subtile+1)*(subtile+1)+(i%subtile))*2+1]
);
//glColor3f(1.0f,0.0f,1.0f);
glVertex3f(x,y,z);
//glVertexPointer(3, GL_FLOAT, 0, vertices +
(j*vertex_rows_total+i)*3 );
//glColorPointer (4, GL_FLOAT, 0, colors + ((i*4)%4);
//glDrawArrays(GL_TRIANGLES, 0, 3);
}
}
glEnd();
/*
// apply geometry
this.geometry.ready = false;
this.materials = materials;
this.geometry.vertices = vertices;
this.geometry.faces = faces;
this.geometry.ready = true;
*/
}
/*
public function WMSLayer(wmsurl, is_flat:Boolean=false,scale=1000,zoom=0):void {
this.wmsurl = wmsurl;
if(!this.wmsurl) {
//this.wmsurl =
"http://civicmaps.org/tom/tilecache-1.4/tilecache.cgi?LAYERS=basic&FORMAT=image%2Fpng&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4326"
this.wmsurl =
"http://civicmaps.org/cgi-bin/mapserv?map=/www/sites/maps.civicactions.net/maps/world.map&service=WMS&WMTVER=1.0.0&REQUEST=map&SRS=EPSG:4326&LAYERS=bluemarble,landsat7,lakes,rivers,cities,majorroads,minorroads,tiger_polygon,tiger_landmarks,tiger_lakes,tiger_local_roads,tiger_major_roads,lowboundaries,boundaries,coastlines&FORMAT=image/jpeg&STYLES=&TRANSPARENT=TRUE"
}
this.is_flat = is_flat;
super( null, new Array(), new Array(), null, null );
rebuild(is_flat,scale,zoom);
}
}
}
*/
/* The function called when our window is resized (which shouldn't
happen, because we're fullscreen) */
void ReSizeGLScene(int Width, int Height)
{
if (Height==0) // Prevent A Divide By Zero If
The Window Is Too Small
Height=1;
glViewport(0, 0, Width, Height); // Reset The Current Viewport
And
Perspective Transformation
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);
}
/* The main drawing function. */
void DrawGLScene()
{
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // Clear The
Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
glTranslatef(0.0f,0.0f,-6.0f); // Move Into The Screen 6.0
glRotatef(rtri,rtri,rtri,0.0f); // Rotate The view a bit
draw_thing();
rtri+=3.0f; // Increase The Rotation
Variable For The Pyramid
rquad-=1.0f; // Decrease The Rotation
Variable For The Cube
// swap the buffers to display, since double buffering is used.
glutSwapBuffers();
usleep(10);
}
/* The function called whenever a key is pressed. */
void keyPressed(unsigned char key, int x, int y) {
usleep(100);
if (key == ESCAPE) {
glutDestroyWindow(window);
}
}
int main(int argc, char **argv)
{
/* Initialize GLUT state - glut will take any command line arguments
that pertain to it or
X Windows - look at its documentation at
http://reality.sgi.com/mjk/spec3/spec3.html */
glutInit(&argc, argv);
/* Select type of Display mode:
Double buffer
RGBA color
Alpha components supported
Depth buffered for automatic clipping */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);
/* get a 640 x 480 window */
glutInitWindowSize(640, 480);
/* the window starts at the upper left corner of the screen */
glutInitWindowPosition(0, 0);
/* Open a window */
window = glutCreateWindow("Jeff Molofee's GL Code Tutorial ... NeHe '99");
/* Register the function to do all our OpenGL drawing. */
glutDisplayFunc(&DrawGLScene);
/* Go fullscreen. This is as soon as possible. */
//glutFullScreen();
/* Even if there are no events, redraw our gl scene. */
glutIdleFunc(&DrawGLScene);
/* Register the function called when our window is resized. */
glutReshapeFunc(&ReSizeGLScene);
/* Register the function called when the keyboard is pressed. */
glutKeyboardFunc(&keyPressed);
/* Initialize our window. */
InitGL(640, 480);
/* Start Event Processing Engine */
glutMainLoop();
return 1;
}
_______________________________________________
Geowanking mailing list
[email protected]
http://lists.burri.to/mailman/listinfo/geowanking