|
Land
|
|
|
|
|
|
|
/*--------------------------------------------------*
Ray-Casting Heightfield Engine
Pre-SVGAlib code by Alex Chalfin
Usage: Use the mouse to move l, r, u, & d.
'+' and '-' control your forward speed.
*--------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <vga.h>
#include <vgagl.h>
#include <vgamouse.h>
/*------------------------------------------------------------------------*
Floating point related macros
*------------------------------------------------------------------------*/
#define FLT2INT(a) ((int)(a)) /* float to integer conversion */
#define FLT2FXD(a) (FLT2INT((a)*65536)) /* float to 16.16 fixed point */
/*------------------------------------------------------------------------*
Map size and indexing related macros
*------------------------------------------------------------------------*/
#define MAPSHIFT 8 /* log2(map_dimention) */
#define MAPSIZE (1 << MAPSHIFT) /* size of one side of the map */
#define BYTE(a) (a & (MAPSIZE-1)) /* map wrapping macro */
#define INDEX(x,y) ((BYTE(y)<<MAPSHIFT)+BYTE(x)) /* std index calculation */
#define fpINDEX(x,y) (INDEX(x>>16, y>>16)) /* fixed point 16.16 index calc */
#define iINDEX(x,y) (INDEX(FLT2INT(x), FLT2INT(y))) /* flt index calc */
/*------------------------------------------------------------------------*
A nice random macro (ranged)
*------------------------------------------------------------------------*/
#define RANDOM(range) (rand() % (range))
#define DEG2RAD(ang) ((ang)*2*3.141592/360)
/*------------------------------------------------------------------------*
Rendering related constants
*------------------------------------------------------------------------*/
#define FARDEPTH 100 /* how far into horizon to render */
#define HEIGHTMUL 48 /* perspective multiplication factor */
#define FIELD_OF_VIEW 90 /* 90 degree field of view */
#define RENDERWIDTH 320 /* width of the screen to render to */
#define RENDERHEIGHT 200 /* height of the screen to render to */
/*------------------------------------------------------------------------*
Bi-linear filter blend
*------------------------------------------------------------------------*/
#define BLEND(map,u,v,value) { \
c0 = (int)(map)[fpINDEX(u, v)]; \
c1 = (int)(map)[fpINDEX((u+65536), v)]; \
c2 = (int)(map)[fpINDEX(u, (v+65536))]; \
c3 = (int)(map)[fpINDEX((u+65536),(v+65536))]; \
c11 = (c0 << 8) + (((u & 0xffff) >> 8) * (c1 - c0)); \
c22 = (c2 << 8) + (((u & 0xffff) >> 8) * (c3 - c2)); \
(value) = (c11 << 8) + (((v & 0xffff) >> 8) * (c22 - c11)); \
}
/*------------------------------------------------------------------------*
Types used in rendering process
*------------------------------------------------------------------------*/
typedef struct tview
{
float cx, cy;
float angle;
int z;
} tview;
/*------------------------------------------------------------------------*
Global Variables
*------------------------------------------------------------------------*/
char heightmap[MAPSIZE * MAPSIZE]; // height field for the voxels
char colormap [MAPSIZE * MAPSIZE]; // colors for the voxels
char vpage [RENDERWIDTH * RENDERHEIGHT]; // a virtual screen page
/*------------------------------------------------------------------------*
Function Headers
*------------------------------------------------------------------------*/
void CreateMaps();
void CreateFractalMap(int x1, int y1, int x2, int y2);
void NewColor(int xa, int ya, int x, int y, int xb, int yb);
void SmoothMap();
void MakeColorMap();
void DrawScreen(tview *v);
void MakePalette();
void SetRGB(char n, char r, char g, char b);
void SetRenderView(float speed, float moveangle, tview *v);
short int InitMouse();
void ReadMouseCoords(short int *x, short int *y);
void CastRay(float x1, float y1, float x2, float y2, int viewer, int scr_x);
/*------------------------------------------------------------------------*
Da main funk!
*------------------------------------------------------------------------*/
int main(void)
{
tview mainview;
int x, y, key, quit = 0, height = -1;
float angle = 0, speed = 0;
printf("Use the '+' and '-' keys to change speed\n");
printf("and the mouse to fly around.\n");
getchar();
CreateMaps();
vga_init();
vga_setmousesupport(1);
vga_setmode(G320x200x256);
gl_setcontextvga(G320x200x256);
MakePalette();
while (!quit) {
mouse_update();
x = mouse_getx();
y = mouse_gety();
angle += (float)(x - (RENDERWIDTH / 2)) / 1024.0f;
height += ((RENDERHEIGHT / 2) - (int)(y))*16;
if (height > 1000*256)
height = 1000*256;
if (height < ((int)heightmap[iINDEX(mainview.cx, mainview.cy)] << 8))
height = ((int)heightmap[iINDEX(mainview.cx, mainview.cy)]) << 8;
mainview.z = height;
if ((key = vga_getkey()) > 0)
{
switch (key)
{
case '+' : speed += 1.0 / 4.0;
if (speed > 16) speed = 16;
break;
case '-' : speed -= 1.0 / 4.0;
if (speed < 0) speed = 0;
break;
case 27 : if ((key = vga_getkey()) == 0) quit = 1;
break;
}
}
SetRenderView(speed, angle, &mainview);
DrawScreen(&mainview);
vga_waitretrace();
gl_putbox(0, 0, RENDERWIDTH, RENDERHEIGHT, vpage);
memset(vpage, 0, RENDERWIDTH * RENDERHEIGHT);
}
vga_setmode(TEXT);
return 0;
}
/*------------------------------------------------------------------------*
SetRenderView - assigns view values based on speed and angle of movement
*------------------------------------------------------------------------*/
void SetRenderView(float speed, float moveangle, tview *v)
{
v->cx += speed * cos(moveangle);
v->cy += speed * sin(moveangle);
v->angle = moveangle;
}
/*------------------------------------------------------------------------*
DrawScreen - given a view structure, render the screen to the virtual buf
*------------------------------------------------------------------------*/
void DrawScreen(tview *v)
{
int i_x;
float angle;
float target_x, target_y;
for (i_x = 0; i_x < RENDERWIDTH; i_x++)
{
angle = FIELD_OF_VIEW * ((float)i_x / (float)(RENDERWIDTH - 1)) - (FIELD_OF_VIEW / 2);
target_x = v->cx + FARDEPTH * cos(v->angle + DEG2RAD(angle));
target_y = v->cy + FARDEPTH * sin(v->angle + DEG2RAD(angle));
CastRay(v->cx, v->cy, target_x, target_y, (v->z+(20<<8))<<8, i_x);
}
}
/*------------------------------------------------------------------------*
CastRay - casts a ray into the landscape and draws the height sliver
*------------------------------------------------------------------------*/
void CastRay(float x1, float y1, float x2, float y2, int viewer, int scr_x)
{
int c0,c1,c2,c3,c11,c22; /* vars for bi-linear interpolation */
int fxd_x, fxd_dx;
int fxd_y, fxd_dy;
int miny;
int depth;
int per;
int start_h, start_c;
int end_h, end_c;
int scr_y1, scr_y2;
int i_c, i_dc;
int i_y;
/* store the traversing vars in 16.16 fixed point */
fxd_x = FLT2FXD(x1);
fxd_y = FLT2FXD(y1);
fxd_dx = FLT2FXD((x2 - x1) / FARDEPTH);
fxd_dy = FLT2FXD((y2 - y1) / FARDEPTH);
miny = RENDERHEIGHT;
/* create the starting height and color */
start_h = 199;
BLEND(colormap, fxd_x, fxd_y, start_c);
for (depth = 1; depth < FARDEPTH; depth++)
{
per = (HEIGHTMUL << 16) / depth;
/* calculate the color and height in map space */
BLEND(heightmap, fxd_x, fxd_y, end_h);
BLEND(colormap, fxd_x, fxd_y, end_c);
/* convert the height to screen space */
end_h = (RENDERHEIGHT / 2) - (long)(((long long)(end_h - viewer) * per) >> 32);
/* only draw if the new height is less than the old height */
if (end_h < start_h)
{
/* only draw if the new height is less than the minimum reached height */
if (end_h < miny)
{
scr_y1 = end_h;
scr_y2 = miny - 1;
i_c = end_c;
i_dc = (start_c - end_c) / (start_h - end_h);
miny = end_h; /* reset the min y value to the new highest value */
if (scr_y1 < 0) /* perform some clipping so we don't go off the top of the screen */
{
i_c += i_dc * (-scr_y1);
scr_y1 = 0;
}
scr_y1 = ( (int)(vpage) + scr_y1 * RENDERWIDTH + scr_x);
scr_y2 = ( (int)(vpage) + scr_y2 * RENDERWIDTH + scr_x);
for (i_y = scr_y1; i_y <= scr_y2; i_y += RENDERWIDTH, i_c += i_dc)
*(char *)(i_y) = i_c >> 16;
}
}
fxd_x += fxd_dx;
fxd_y += fxd_dy;
start_h = end_h;
start_c = end_c;
}
}
/*------------------------------------------------------------------------*
MakePalette - Makes a sandy dark red to red palette
*------------------------------------------------------------------------*/
void MakePalette()
{
int i;
for (i = 1; i < 64; i++)
{
gl_setpalettecolor(i, 16 + (i/4), i/8, 0);
}
for (i = 64; i < 128; i++)
{
gl_setpalettecolor(i, 32 + ((i-64)/2), i/8, 0);
}
/* gl_setpalettecolor(255, 63, 63, 63); */
}
/*------------------------------------------------------------------------*
CreateMaps - calculates heightmap and color map. Allocates draw buffers
*------------------------------------------------------------------------*/
void CreateMaps()
{
int i;
srand(getpid());
/* initialize some of the memory */
memset(heightmap, 0, MAPSIZE*MAPSIZE);
memset(vpage, 0, RENDERWIDTH * RENDERHEIGHT);
printf("Creating %dx%d fractal terrain\n", MAPSIZE, MAPSIZE);
heightmap[0] = (rand() % 128) + 64; // initialize starting point on map
CreateFractalMap(0, 0, MAPSIZE, MAPSIZE);
printf("Smoothing terrain\n");
for (i = 0; i < 5; i++)
SmoothMap();
MakeColorMap();
}
/*------------------------------------------------------------------------*
CreateFractalMap - builds a fractal landscape
*------------------------------------------------------------------------*/
void CreateFractalMap(int x1, int y1, int x2, int y2)
{
int x, y, c;
if (((x2-x1) < 2) && ((y2-y1) < 2))
return;
x = (x1 + x2) >> 1;
y = (y1 + y2) >> 1;
NewColor(x1, y1, x, y1, x2, y1);
NewColor(x2, y1, x2, y, x2, y2);
NewColor(x1, y2, x, y2, x2, y2);
NewColor(x1, y1, x1, y, x1, y2);
c = (heightmap[INDEX(x1,y1)] + heightmap[INDEX(x1,y2)] +
heightmap[INDEX(x2,y1)] + heightmap[INDEX(x2,y2)]) >> 2;
if (heightmap[INDEX(x, y)] == 0)
heightmap[INDEX(x, y)] = c;
CreateFractalMap(x1, y1, x, y);
CreateFractalMap(x, y1, x2, y);
CreateFractalMap(x, y, x2, y2);
CreateFractalMap(x1, y, x, y2);
}
/*------------------------------------------------------------------------*
NewColor - new color evaluation (fractal landscape function)
*------------------------------------------------------------------------*/
void NewColor(int xa, int ya, int x, int y, int xb, int yb)
{
int color;
color = abs(xa - xb) + abs(ya - yb);
color = RANDOM(color << 1) - color;
color = color + ((heightmap[INDEX(xa, ya)] + heightmap[INDEX(xb, yb)] + 1) >> 1);
if (color < 1) color = 1;
if (color > 255) color = 255;
if (heightmap[INDEX(x, y)] == 0)
heightmap[INDEX(x, y)] = color;
}
/*------------------------------------------------------------------------*
SmoothMap - smooth the landscape via 3x3 box filter
*------------------------------------------------------------------------*/
void SmoothMap()
{
int x,y;
for (x = 0; x < MAPSIZE; x++)
for (y = 0; y < MAPSIZE; y++)
heightmap[INDEX(x,y)] = (heightmap[INDEX((x-1),(y-1))] +
heightmap[INDEX((x-1),(y+1))] +
heightmap[INDEX((x+1),(y-1))] +
heightmap[INDEX((x+1),(y+1))] +
heightmap[INDEX(x, (y-1))] +
heightmap[INDEX(x, (y+1))] +
heightmap[INDEX((x-1), y )] +
heightmap[INDEX( x, y )] +
heightmap[INDEX((x+1), y )]) / 9;
}
/*------------------------------------------------------------------------*
MakeColorMap - tries to build a creative color map for the landscape
*------------------------------------------------------------------------*/
void MakeColorMap()
{
int x,y,temp;
for (x = 0; x < MAPSIZE; x++)
for (y = 0; y < MAPSIZE; y++)
{
temp = heightmap[INDEX(x,y)] >> 1;
if (heightmap[INDEX((x-1),y)] < heightmap[INDEX(x,y)])
{
temp -= 8*(heightmap[INDEX(x,y)] - heightmap[INDEX((x-1),y)]);
if (temp < 2)
temp = 2;
}
else
{
if (heightmap[INDEX((x-1),y)] > heightmap[INDEX(x,y)])
{
temp += 3*(heightmap[INDEX((x-1),y)] - heightmap[INDEX(x,y)]);
if (temp > 127)
temp = 127;
}
}
colormap[INDEX(x,y)] = temp;
}
}