home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Magazin: Amiga-CD 2000 April & May
/
AMIGA_2000_04.iso
/
amiga-magazin
/
cybergl-workshop
/
fractal.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-04-28
|
24KB
|
602 lines
/*
** $VER: fractal.c 1.0 (20.04.1997)
**
** This is an example program for CyberGL
**
** Written by Frank Gerberding
**
** Copyright © 1996-1997 by phase5 digital products
** All Rights reserved.
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <clib/exec_protos.h>
#include <dos/dos.h>
#include <dos/dosasl.h>
#include <clib/dos_protos.h>
#include <string.h>
#define CYBERGLNAME "cybergl.library"
#define CYBERGLVERSION 39L
#define SHARED /* we use the cybergl shared library */
#define GL_APICOMPATIBLE /* we use stubs from cybergl.lib */
#include "cybergl.h"
#include "cybergl_protos.h"
#include "cybergl_display.h"
#ifdef SHARED
LONG __oslibversion = 39L;
LONG __CGLlibversion = CYBERGLVERSION;
#include "cybergl_pragmas.h"
extern struct Library *CyberGLBase;
#endif
#define MAX_STEPS 8 /* maximum number of genesis steps */
/* 2^(2*MAX_STEPS)*6*sizeof(float) mem needed */
#define GRID_SIZE (1 << MAX_STEPS) /* fractal grid size */
#define WIDTH 0 /* width parameter array index */
#define HEIGHT 1 /* height parameter array index */
#define STEPS 2 /* steps parameter array index */
#define MAX_HEIGHT 3 /* max height parameter array index */
#define ROCK_REPEAT 4 /* rock repeat parameter array index */
#define WAVE_REPEAT 5 /* wave repeat parameter array index */
#define SIZE 6 /* fractal size parameter array index */
#define SEA_LEVEL 7 /* sea level parameter array index */
#define SEED 8 /* random seed parameter array index */
#define FOG 9 /* fog enable parameter array index */
#define TEXTURE 10 /* texture enable parameter array index */
/* this sets the default values for all parameters: */
/* width: initial window width */
/* height: initial window height */
/* steps: genesis step number (1..MAX_STEPS) */
/* maxHeight: maximum fractal mountain height */
/* rockRepeat: rock texture repeat count */
/* waveRepeat: wave texture repeat count */
/* size: fractal mountain x/z size */
/* seaLevel: height of sea level */
/* seed: random seed */
/* fog: fog enable/disable */
/* texture: texture enable/disable */
/* width height steps maxHeight rockRepeat waveRepeat size seaLevel seed fog texture */
int params [] = {320, 240, 6, 5, 3, 3, 20, -1, 100, 0, 0};
typedef struct {
GLfloat x; /* point x coordinate */
GLfloat y; /* point y coordinate */
GLfloat z; /* point z coordinate */
GLfloat nx; /* normal x coordinate */
GLfloat ny; /* normal y coordinate */
GLfloat nz; /* normal z coordinate */
} point; /* grid point structure */
far point grid [GRID_SIZE + 1][GRID_SIZE + 1]; /* global fractal grid */
/************************************************************************/
/* texture minification routine. This function computes a texture of */
/* half the original size using a simple box filter and returns a */
/* pointer to a new allocated texture. */
/* parameters: */
/* texture: pointer to original texture */
/* size: size of original texture */
/* result: new minified texture (or NULL for failure) */
/************************************************************************/
GLubyte *minifyTexture (GLubyte *texture, GLint size)
{
GLubyte *newTexture = NULL; /* pointer to minified texture */
GLint newSize; /* minified texture size */
GLint x, y; /* x and y texture coordinates */
newSize = size / 2;
if (newTexture = malloc (newSize * newSize * 3))
{
for (x = 0; x < newSize; x++) /* for all rows */
{
for (y = 0; y < newSize; y++) /* for all columns */
{
newTexture [(x + y * newSize) * 3 + 0] =
(texture [(x + x + (y + y) * size) * 3 + 0] +
texture [(x + x + 1 + (y + y) * size) * 3 + 0] +
texture [(x + x + (y + y + 1) * size) * 3 + 0] +
texture [(x + x + 1 + (y + y + 1) * size) * 3 + 0]) / 4; /* red pixel value */
newTexture [(x + y * newSize) * 3 + 1] =
(texture [(x + x + (y + y) * size) * 3 + 1] +
texture [(x + x + 1 + (y + y) * size) * 3 + 1] +
texture [(x + x + (y + y + 1) * size) * 3 + 1] +
texture [(x + x + 1 + (y + y + 1) * size) * 3 + 1]) / 4; /* green pixel value */
newTexture [(x + y * newSize) * 3 + 2] =
(texture [(x + x + (y + y) * size) * 3 + 2] +
texture [(x + x + 1 + (y + y) * size) * 3 + 2] +
texture [(x + x + (y + y + 1) * size) * 3 + 2] +
texture [(x + x + 1 + (y + y + 1) * size) * 3 + 2]) / 4; /* blue pixel value */
}
}
}
return newTexture; /* return new texture */
}
/************************************************************************/
/* load a simple texture map from disk. The texture has to be organized */
/* as RGBRGBRGBRGB... and has to be of square size. The size has to be */
/* a power of two. All mip mapping texture sizes are computed */
/* automatically. */
/* parameters: */
/* name: full file name */
/* size: texture width/height in pixels, so file size is: size*size*3 */
/************************************************************************/
void loadTexMipMap (char *name, GLint size)
{
FILE *file; /* file pointer */
GLubyte *texture = NULL; /* texture map pointer */
GLubyte *newTexture = NULL; /* new texture (for mip map sizes */
int level = 0; /* actual mip mapping texture level */
if (texture = malloc (size * size * 3)) /* alloc texture map */
{
if (file = fopen (name, "r")) /* open texture file */
{
fread (texture, size * size * 3, 1, file);
glTexImage2D (GL_TEXTURE_2D, level++, 3, size, size, 0, GL_RGB, GL_UNSIGNED_BYTE, texture);
fclose (file);
while (size > 1) /* mip map sizes remaining ? */
{
if (newTexture = minifyTexture (texture, size)) /* compute next mip map */
{
free (texture); /* free old texture */
texture = newTexture;
}
glTexImage2D (GL_TEXTURE_2D, level++, 3, size / 2, size / 2, 0, GL_RGB, GL_UNSIGNED_BYTE, texture);
size /= 2; /* next size is half original */
}
}
free (texture);
}
}
/************************************************************************/
/* compute a random height within -range..range. */
/* parameters: */
/* range: range to compute a random value within */
/* result: random height within -range..range */
/************************************************************************/
GLfloat randomHeight (GLfloat range)
{
return (GLfloat) (range * ((GLfloat) (2.0 * rand () / RAND_MAX) - 1.0));
}
/************************************************************************/
/* normalize a single vector. */
/* parameters: */
/* point: pointer to point structure to normalize */
/************************************************************************/
void normalize (point *p)
{
GLfloat length = p->nx * p->nx + p->ny * p->ny + p->nz * p->nz;
if (length != 0.0)
{
length = 1.0 / sqrt (length);
p->nx *= length;
p->ny *= length;
p->nz *= length;
}
}
/************************************************************************/
/* calc vertex normals for all fractal mountain grid positions. This is */
/* done by computing all fractal patch normals and then averaging all */
/* adjacent patch normals. */
/* parameters: */
/* steps: number of fractal genesis steps */
/* size: x/z size of fractal mountain */
/************************************************************************/
void calcNormals (int steps, GLfloat size)
{
int gridStep = 1 << (MAX_STEPS - steps); /* grid point index distance */
int x, z; /* x/z index */
GLfloat diff = size / (1 << steps); /* x/z grid distance */
GLfloat nx, ny, nz; /* actual normal coordinates */
GLfloat y1, y2; /* actual y coordinates */
for (x = 0; x < GRID_SIZE; x += gridStep) /* for all rows */
{
for (z = 0; z < GRID_SIZE; z += gridStep) /* for all columns */
{
grid [x][z].nx = 0.0; /* init normal x coordinate */
grid [x][z].ny = 0.0; /* init normal y coordinate */
grid [x][z].nz = 0.0; /* init normal z coordinate */
}
}
for (x = 0; x < GRID_SIZE; x += gridStep) /* for all rows */
{
for (z = 0; z < GRID_SIZE; z += gridStep) /* for all columns */
{
y1 = grid [x + gridStep][z].y - grid[x][z].y; /* edge vector y1 */
y2 = grid [x][z + gridStep].y - grid[x][z].y; /* edge vector y2 */
nx = y2 * 0.0 - diff * y1; /* normal computation */
ny = diff * diff - 0.0 * 0.0; /* using edge vector */
nz = 0.0 * y1 - y2 * diff; /* cross product */
grid [x ][z ].nx += nx; /* add actual normal */
grid [x ][z ].ny += ny;
grid [x ][z ].nz += nz;
grid [x ][z + gridStep].nx += nx; /* add actual normal */
grid [x ][z + gridStep].ny += ny;
grid [x ][z + gridStep].nz += nz;
grid [x + gridStep][z ].nx += nx; /* add actual normal */
grid [x + gridStep][z ].ny += ny;
grid [x + gridStep][z ].nz += nz;
grid [x + gridStep][z + gridStep].nx += nx; /* add actual normal */
grid [x + gridStep][z + gridStep].ny += ny;
grid [x + gridStep][z + gridStep].nz += nz;
}
}
for (x = 0; x <= GRID_SIZE; x += gridStep) /* for all rows */
{
for (z = 0; z <= GRID_SIZE; z += gridStep) /* for all columns */
{
normalize (&(grid [x][z])); /* normalize all vectors */
}
}
}
/************************************************************************/
/* fractal mountain genesis. This is done by the popular fractal */
/* mountain generation algorithm. */
/* parameters: */
/* seed: random seed */
/* steps: number of generation steps (1..MAX_STEPS) */
/* maxHeight: maximum mountain height */
/* size: mountain x/z size */
/************************************************************************/
void genesis (int seed, int steps, GLfloat maxHeight, GLfloat size)
{
int x, z; /* x/z index */
int gridStep; /* actual grid index distance */
int step; /* actual generation step */
srand (seed);
grid [ 0][ 0].y = randomHeight (maxHeight); /* corner height */
grid [GRID_SIZE][ 0].y = randomHeight (maxHeight); /* corner height */
grid [ 0][GRID_SIZE].y = randomHeight (maxHeight); /* corner height */
grid [GRID_SIZE][GRID_SIZE].y = randomHeight (maxHeight); /* corner height */
gridStep = GRID_SIZE / 2; /* init grid step */
for (step = 1; step <= steps; step++) /* for all generation steps */
{
maxHeight /= 2.0; /* new max height */
for (x = gridStep / 2; x < GRID_SIZE; x += gridStep) /* for all rows */
{
int xl = x - gridStep / 2; /* x neighbour index left */
int xr = x + gridStep / 2; /* x neighbour index right */
for (z = gridStep / 2; z < GRID_SIZE; z += gridStep) /* for all columns */
{
int zl = z - gridStep / 2; /* z neighbour index left */
int zr = z + gridStep / 2; /* z neighbour index right */
grid [x][z].y = (grid [xl][zl].y +
grid [xl][zr].y +
grid [xr][zl].y +
grid [xr][zr].y) / 4.0 + randomHeight (maxHeight);
grid [xl][z].y = (grid [xl][zl].y +
grid [xl][zr].y) / 2.0 + randomHeight (maxHeight);
grid [x][zl].y = (grid [xl][zl].y +
grid [xr][zl].y) / 2.0 + randomHeight (maxHeight);
}
grid [x][GRID_SIZE].y = (grid [xl][GRID_SIZE].y +
grid [xr][GRID_SIZE].y) / 2.0 + randomHeight (maxHeight);
}
for (z = gridStep / 2; z < GRID_SIZE; z += gridStep) /* right border */
{
grid [GRID_SIZE][z].y = (grid [GRID_SIZE][z - gridStep / 2].y +
grid [GRID_SIZE][z + gridStep / 2].y) / 2.0 + randomHeight (maxHeight);
}
gridStep /= 2; /* next grid index distance */
}
{
GLfloat offset = - size / 2.0; /* offset for x/z coordinate computation */
GLfloat factor = size / GRID_SIZE; /* factor for x/z coordinate computation */
gridStep = 1 << (MAX_STEPS - steps); /* grid index step size */
for (x = 0; x <= GRID_SIZE; x += gridStep) /* for all rows */
{
for (z = 0; z <= GRID_SIZE; z += gridStep) /* for all columns */
{
grid [x][z].x = factor * x + offset; /* x coordinate computation */
grid [x][z].z = factor * z + offset; /* z coordinate computation */
}
}
}
calcNormals (steps, size); /* calc all vertex normals */
}
/************************************************************************/
/* draw the fractal mountain. This is done by drawing several adjacent */
/* triangle strips. */
/* parameters: */
/* step: number of generation steps performed before */
/* texRepeat: rock texture repeat count */
/************************************************************************/
void drawFractal (int step, GLfloat texRepeat)
{
GLint x, z; /* x/z index */
GLint gridStep; /* grid index distance */
GLfloat rock [] = {1.0, 0.8, 0.6, 1.0}; /* rock color */
GLdouble texScale; /* texture coordinate factor */
gridStep = 1 << (MAX_STEPS - step);
texScale = texRepeat / GRID_SIZE;
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, rock);
if (params [TEXTURE])
{
loadTexMipMap ("rock", 256);
}
for (z = 0; z < GRID_SIZE; z += gridStep) /* for all rows */
{
glBegin (GL_TRIANGLE_STRIP); /* draw a triangle strip */
for (x = 0; x <= GRID_SIZE; x += gridStep) /* for all columns */
{
glNormal3f (grid [x][z].nx, grid [x][z].ny, grid [x][z].nz);
glTexCoord2d (x * texScale, z * texScale);
glVertex3f (grid [x][z].x, grid [x][z].y, grid [x][z].z);
glNormal3f (grid [x][z + gridStep].nx, grid [x][z + gridStep].ny, grid [x][z + gridStep].nz);
glTexCoord2d (x * texScale, (z + gridStep) * texScale);
glVertex3f (grid [x][z + gridStep].x, grid [x][z + gridStep].y, grid [x][z + gridStep].z);
}
glEnd ();
}
}
/************************************************************************/
/* draw thesea level as one big polygon. */
/* parameters: */
/* level: sea level */
/* size: size of fractal mountain in x/z direction */
/* texRepeat: sea texture repeat count */
/************************************************************************/
void drawSea (GLfloat level, GLfloat size, GLfloat texRepeat)
{
GLfloat water [] = {0.2, 0.5, 1.0, 1.0}; /* water colr */
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, water);
if (params [TEXTURE])
{
loadTexMipMap ("waves", 256);
}
glBegin (GL_POLYGON); /* draw one big polygon */
glNormal3d (0.0, 1.0, 0.0);
glTexCoord2f (0.0F, 0.0F);
glVertex3d (-size / 2, level, size / 2);
glTexCoord2f (texRepeat, 0.0F);
glVertex3d ( size / 2, level, size / 2);
glTexCoord2f (texRepeat, texRepeat);
glVertex3d ( size / 2, level, -size / 2);
glTexCoord2f (0.0F, texRepeat);
glVertex3d (-size / 2, level, -size / 2);
glEnd ();
}
/************************************************************************/
/* handle all window events for the given window. */
/* parameters: */
/* win: pointer to CyberGL window */
/************************************************************************/
void handle_window_events (void *win)
{
struct IntuiMessage *msg; /* intuition message */
int done = 0; /* all done ? */
struct Window *window; /* Intuition window pointer */
window = getWindow (win); /* get Intuition window */
while (!done) /* whilenot done */
{
Wait (1L << window->UserPort->mp_SigBit);
while ((!done) && (msg = (struct IntuiMessage *) GetMsg (window->UserPort)))
{
switch (msg->Class)
{
case IDCMP_CLOSEWINDOW:
done = 1; /* all done */
break;
case IDCMP_NEWSIZE:
resizeGLWindow (win, window->GZZWidth, window->GZZHeight);
glClear (GL_COLOR_BUFFER_BIT);
drawFractal (params [STEPS], (GLfloat) params [ROCK_REPEAT]);
drawSea ((GLfloat) params [SEA_LEVEL], (GLfloat) params [SIZE],
(GLfloat) params [WAVE_REPEAT]);
break;
}
ReplyMsg ((struct Message *) msg);
}
}
}
/************************************************************************/
/* set up a directional light source with direction xyz and color rgb. */
/* parameters: */
/* x: x coordinate of light direction */
/* y: y coordinate of light direction */
/* z: z coordinate of light direction */
/* r: red color component of light color */
/* g: green color component of light color */
/* b: blue color component of light color */
/* light: symbolic light name (GL_LIGHT0..GL_LIGHT7) */
/************************************************************************/
void dirLight (GLdouble x, GLdouble y, GLdouble z,
GLdouble r, GLdouble g, GLdouble b, GLenum light)
{
GLfloat pos [4]; /* virtual light position */
GLfloat color [4]; /* light color */
pos [0] = (GLfloat) x;
pos [1] = (GLfloat) y;
pos [2] = (GLfloat) z;
pos [3] = (GLfloat) 0.0;
color [0] = (GLfloat) r;
color [1] = (GLfloat) g;
color [2] = (GLfloat) b;
color [3] = (GLfloat) 0.0;
glEnable (GL_LIGHTING); /* enable lighting */
glEnable (light); /* enable light */
glLightfv (light, GL_POSITION, pos); /* set position */
glLightfv (light, GL_DIFFUSE, color); /* set diffuse color */
glLightfv (light, GL_SPECULAR, color); /* set specular color */
}
void main (void)
{
void *window; /* CyberGL window */
GLfloat fog [] = {1.0, 1.0, 1.0, 1.0}; /* fog color */
struct RDArgs *args;
char template [] = "W=WIDTH/K/N,H=HEIGHT/K/N,S=STEPS/K/N,M=MAXHEIGHT/K/N,ROCK/K/N,WAVE/K/N,SIZE/K/N,L=SEALEVEL/K/N,R=RANDOM/K/N,F=FOG/K/S,T=TEXTURE/K/S";
long *arglist [11] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
int count;
if (args = ReadArgs (template, (long *) arglist, NULL))
{
for (count = 0; count < 9; count++)
{
if (arglist [count])
{
params [count] = *(arglist [count]);
}
}
params [FOG] = (int) arglist [FOG];
params [TEXTURE] = (int) arglist [TEXTURE];
FreeArgs (args);
}
window = openGLWindowTags (params [WIDTH], params [HEIGHT],
GLWA_Title, "Fractal",
GLWA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE,
GLWA_CloseGadget, TRUE,
GLWA_DepthGadget, TRUE,
GLWA_DragBar, TRUE,
GLWA_Activate, TRUE,
GLWA_RGBAMode, GL_TRUE,
GLWA_SizeGadget, TRUE,
GLWA_MinWidth, 320,
GLWA_MinHeight, 240,
GLWA_MaxWidth, 1280,
GLWA_MaxHeight, 1024,
TAG_DONE);
if (window)
{
glEnable (GL_DITHER); /* enable dithering */
glEnable (GL_CULL_FACE); /* enable back face culling */
glShadeModel (GL_SMOOTH); /* enable gouraud shading */
glEnable (GL_DEPTH_TEST); /* enable depth testing */
glLightModeli (GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
glMatrixMode (GL_PROJECTION);
glPerspective (45.0, 1.333, 0.5, (GLdouble) params [SIZE] + 2.5); /* projection */
glMatrixMode (GL_MODELVIEW);
glLookAt (0.0, 10.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); /* camera position */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* minification filter */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* magnification filter */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); /* texture repeating */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); /* texture repeating */
glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); /* modulate color */
glFogi (GL_FOG_MODE, GL_LINEAR); /* linear fog mode */
glFogf (GL_FOG_START, (GLfloat) 0.0); /* fog minimum distance */
glFogf (GL_FOG_END, (GLfloat) 20.0); /* fog maximum distance */
glFogfv (GL_FOG_COLOR, fog); /* fog color */
if (params [FOG])
{
glClearColor (fog [0], fog [1], fog [2], fog [3]);
glEnable (GL_FOG); /* enable fog */
}
else
{
glDisable (GL_FOG); /* enable fog */
}
if (params [TEXTURE])
{
glEnable (GL_TEXTURE_2D); /* enable texturing */
}
else
{
glDisable (GL_TEXTURE_2D); /* disable texturing */
}
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
dirLight (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, GL_LIGHT0); /* setup light */
genesis (params [SEED], params [STEPS], (GLfloat) params [MAX_HEIGHT], (GLfloat) params [SIZE]);
drawFractal (params [STEPS], (GLfloat) params [ROCK_REPEAT]);
drawSea ((GLfloat) params [SEA_LEVEL], (GLfloat) params [SIZE], (GLfloat) params [WAVE_REPEAT]);
handle_window_events (window);
closeGLWindow (window);
}
}