📄 hfield.c
字号:
#include <math.h>#include <jlib.h>/* Heightfield Renderer Written by Chris Egerter Placed in the public domain on September 29, 1995 Modified by Jon Griffiths to use JLib 1.0. Watcom assembly fragments converted to C, some simplifications, some optimisations. Now runs under DOS/Linux. Will run under X once keyboard support arrives.*/char AUTHORS[] = " HEIGHT FIELD DEMO\nCopyright 1995 Chris Egerter\nOriginally written with WGT 5.1\nFor Watcom C/C++\nModified for JLib 1.0 by Jon Griffths.\n";char KEYS[] = " Keys:\nESC - Quits\nH - Help\nW/S - Change Height\nE/D - Change Range\nR/F - Change eye distance\nF1,2,3 - Detail Level\n\nUse Cursor Keys to move.";/* General graphics info */image *ground_image; /* Contains the colors of the heightfield */UBYTE *ground_data; /* ground_image data */image *color_image; /* Contains the heightfield data */UBYTE *color_data; /* color_image data */image *cloud_image; /* Background clouds bitmap */UBYTE *pal; /* Palette for all of the above *//* View Variables */unsigned short playerx; /* Viewer's coordinates */unsigned short playery;int playerangle; /* Viewer's direction (0-1279) */int playerspeed; /* Viewer's speed */int elevation = 10170;int RAYDISTANCE = 64; /* Length of rays */int cloudx = 0; /* Cloud position */int eyedistance = 1; /* Changes perspective */int tiltx = 0; /* Amount of tilt */UBYTE *ydrawcoord; /* Keeps track of current y posistion when drawing each column *//* Heightfield Tables */int isin[1280];int icos[1280];int persptable [1024];int xanglestep[1280];int yanglestep[1280];int perscalc[1024];/* Detail levels */#define LOW_DETAIL 0#define LOW_WIDTH 80#define MEDIUM_DETAIL 1#define MEDIUM_WIDTH 160#define HIGH_DETAIL 2#define HIGH_WIDTH 320int draw_routine = MEDIUM_DETAIL;int view_width = MEDIUM_WIDTH;int pixelcount = 64; /* Length of rays (can change) */#define PIXPREC2 256L#define MAX_SPEED 1000 /* maximun speed *//* The following segments draw vertical lines of a solid color, in either 1, 2 or 4 pixel widths. *//* high detail - 1 pixel */UBYTE *wvlineh (UBYTE *scrcoord, USHORT length, UBYTE col){ do{ *scrcoord = col; scrcoord -= 320; }while(--length); return scrcoord;}/* medium detail - 2 pixels */UBYTE *wvlinem (UBYTE *scrcoord, USHORT length, UBYTE col){ USHORT *scrptr = (USHORT *)scrcoord; USHORT c = (col<<8)+col; do{ *scrptr = c; scrptr -= 160; }while(--length); return (UBYTE *)scrptr;}/* low detail - 4 pixels */UBYTE *wvlinel (UBYTE *scrcoord, USHORT length, UBYTE col){ unsigned int *scrptr = (unsigned int *)scrcoord; USHORT c = (col<<8)+col; unsigned int c2= (c<<16)+c; do{ *scrptr = c2; scrptr -= 80; }while(--length); return (UBYTE *)scrptr;}/* The following segments draw vertical lines copied from a bitmap, in either 1, 2 or 4 pixel widths. *//* high detail - 1 pixel */void wclineh (UBYTE *scrcoord, USHORT cldcoord, USHORT length){ UBYTE *cldptr = (cloud_image->data+cldcoord); do{ *scrcoord = *cldptr; scrcoord -= 320; cldptr -= 320; }while(--length);}/* med detail - 2 pixels */void wclinem (UBYTE *scrcoord, USHORT cldcoord, USHORT length){ USHORT *scrptr = (USHORT *)scrcoord; USHORT *cldptr = (USHORT *)(cloud_image->data+cldcoord); do{ *scrptr = *cldptr; scrptr -= 160; cldptr -= 160; }while(--length); }/* low detail - 4 pixels */void wclinel (UBYTE *scrcoord, USHORT cldcoord, USHORT length){ unsigned int *scrptr = (unsigned int *)scrcoord; unsigned int *cldptr = (unsigned int *)(cloud_image->data+cldcoord); do{ *scrptr = *cldptr; scrptr -= 80; cldptr -= 80; }while(--length); }/* Table creation */void h_create_sincos (void){int i;float realangle; for (i = 0; i < 1280; i++) { realangle = i / 640.0 * 3.14153254234216; icos[i] = cos(realangle) * PIXPREC2; isin[i] = sin(realangle) * PIXPREC2; }}void h_calculate_perspective (void){int i; for (i = 0; i < 1024; i++) persptable [i] = (2024L / (i + 1));}void recalc_perspective(void){ int y; for (y = 0; y < pixelcount; y++) perscalc[y] = 50 + ((elevation * persptable[y]) >> 12);}unsigned char col;unsigned char hgt;/* Reads the color and height from the two heightfield bitmaps */void h_read_heightfield (unsigned short ax, unsigned short ay, UBYTE *height, UBYTE *ground){ int index = ((ay>>8) * IMG_WIDTH(ground_image)) + (ax>>8); hgt = height[index]; col = ground[index];}void h_draw_heightfield (buffer_rec *virt){int pixel_width; /* Width of pixels (1,2,4) */int leftangle; /* Angle of left ray */int cloudcalc; /* Position of cloud bitmap */int x, y;unsigned short ax, ay; /* Position of initial rays */int gxstep, gystep; /* Fixed point step value of ray */int top_mountain; /* Tells where the lowest pixel has been drawn (starts at 199) */int oldpos, newpos; int tiltfactor;int linelength;int perscalc2; pixel_width = 320 / view_width; leftangle = (playerangle - 160); if (leftangle < 0) leftangle += 1280; cloudcalc = cloudx;switch (draw_routine){case 0: for (x = 0; x < view_width; x++){ ax = playerx + (icos [leftangle] * eyedistance); ay = playery + (isin [leftangle] * eyedistance); /* Get initial position of ray */ gxstep = xanglestep [leftangle]; /* Get ray step */ gystep = yanglestep [leftangle]; top_mountain = 199; tiltfactor = (tiltx * (x * pixel_width - 160)) >> 8; oldpos = 199; ydrawcoord = virt->buffer + 63680L + x * pixel_width; for (y = 1; y < pixelcount; y++){ h_read_heightfield (ax, ay, ground_data, color_data); ax += gxstep; ay += gystep; perscalc2 = perscalc[y] + tiltfactor; newpos = perscalc2 - (((int)hgt * (int)(persptable[y])) >> 5); if ((newpos < oldpos) && (newpos < top_mountain)){ if (oldpos > top_mountain) oldpos = top_mountain; if (newpos < 0) newpos = 0; linelength = oldpos - newpos; /* Draw each vertical line */ if(linelength > 0) ydrawcoord = wvlinel (ydrawcoord, linelength, col); top_mountain = newpos; } oldpos = newpos; } /* We need to draw the clouds */ if ((top_mountain > linelength) && (top_mountain > 0)){ linelength = top_mountain - linelength + 1; wclinel (ydrawcoord, cloudcalc + (top_mountain - tiltfactor + 39) * 320, top_mountain); } cloudcalc += pixel_width; if (cloudcalc > 320) cloudcalc -= 320; leftangle = (leftangle + pixel_width) % 1280; /* Advance to next angle */ } break;case 1: for (x = 0; x < view_width; x++){ ax = playerx + (icos [leftangle] * eyedistance); ay = playery + (isin [leftangle] * eyedistance); /* Get initial position of ray */ gxstep = xanglestep [leftangle]; /* Get ray step */ gystep = yanglestep [leftangle]; top_mountain = 199; tiltfactor = (tiltx * (x * pixel_width - 160)) >> 8; oldpos = 199; ydrawcoord = virt->buffer + 63680L + x * pixel_width; for (y = 1; y < pixelcount; y++){ h_read_heightfield (ax, ay, ground_data, color_data); ax += gxstep; ay += gystep; perscalc2 = perscalc[y] + tiltfactor; newpos = perscalc2 - (((int)hgt * (int)(persptable[y])) >> 5); if ((newpos < oldpos) && (newpos < top_mountain)){ if (oldpos > top_mountain) oldpos = top_mountain; if (newpos < 0) newpos = 0; linelength = oldpos - newpos; /* Draw each vertical line */ if(linelength > 0) ydrawcoord = wvlinem (ydrawcoord, linelength, col); top_mountain = newpos; } oldpos = newpos; } /* We need to draw the clouds */ if ((top_mountain > linelength) && (top_mountain > 0)){ linelength = top_mountain - linelength + 1; wclinem (ydrawcoord, cloudcalc + (top_mountain - tiltfactor + 39) * 320, top_mountain); } cloudcalc += pixel_width; if (cloudcalc > 320) cloudcalc -= 320; leftangle = (leftangle + pixel_width) % 1280; /* Advance to next angle */ } break;case 2: for (x = 0; x < view_width; x++){ ax = playerx + (icos [leftangle] * eyedistance); ay = playery + (isin [leftangle] * eyedistance); /* Get initial position of ray */ gxstep = xanglestep [leftangle]; /* Get ray step */ gystep = yanglestep [leftangle]; top_mountain = 199; tiltfactor = (tiltx * (x * pixel_width - 160)) >> 8; oldpos = 199; ydrawcoord = virt->buffer + 63680L + x * pixel_width; for (y = 1; y < pixelcount; y++){ h_read_heightfield (ax, ay, ground_data, color_data); ax += gxstep; ay += gystep; perscalc2 = perscalc[y] + tiltfactor; newpos = perscalc2 - (((int)hgt * (int)(persptable[y])) >> 5); if ((newpos < oldpos) && (newpos < top_mountain)){ if (oldpos > top_mountain) oldpos = top_mountain; if (newpos < 0) newpos = 0; linelength = oldpos - newpos; /* Draw each vertical line */ if(linelength > 0) ydrawcoord = wvlineh (ydrawcoord, linelength, col); top_mountain = newpos; } oldpos = newpos; } /* We need to draw the clouds */ if ((top_mountain > linelength) && (top_mountain > 0)){ linelength = top_mountain - linelength + 1; wclineh (ydrawcoord, cloudcalc + (top_mountain - tiltfactor + 39) * 320, top_mountain); } cloudcalc += pixel_width; if (cloudcalc > 320) cloudcalc -= 320; leftangle = (leftangle + pixel_width) % 1280; /* Advance to next angle */ } break; }}void h_calculate_distance_table (void){int i;int ax, ay, bx, by;int xdiff, ydiff; for (i = 0; i < 1280; i++) { ax = icos [i] * eyedistance; ay = isin [i] * eyedistance; bx = icos [i] * RAYDISTANCE; by = isin [i] * RAYDISTANCE; xdiff = bx - ax; ydiff = by - ay; xanglestep[i] = (xdiff / pixelcount); yanglestep[i] = (ydiff / pixelcount); }}void rotate_player (int cenx, int ceny, int angle){int x; int y;int x2, y2;int newx, newy; if (angle < 0) angle += 1280; x = (playerx - cenx); y = (playery - ceny); x2 = (x * icos[angle] - y * isin[angle]) >> 8; y2 = (x * isin[angle] + y * icos[angle]) >> 8; newx = x2; playerx = newx + cenx; newy = y2; playery = newy + ceny;}void process_keyboard (void){ if (kb_keydown(KEY_LEFT)) { rotate_player ((playerx + icos[playerangle] * 15), (playery + isin[playerangle] * 15), -10); playerangle -= 10; /* Update the viewer's angle */ if (playerangle < 0) playerangle += 1280; cloudx -= 8; /* Update the cloud position */ if (cloudx < 0) cloudx += 320; if (tiltx < 64) /* Tilt the screen */ tiltx += 8; } else if (kb_keydown(KEY_RIGHT)) { rotate_player ( (playerx + icos[playerangle] * 15), (playery + isin[playerangle] * 15), 10); playerangle += 10; /* Update the viewer's angle */ if (playerangle > 1279) playerangle -= 1280; cloudx += 8; /* Update the cloud position */ if (cloudx > 319) cloudx -= 320; if (tiltx > -64) /* Tilt the screen */ tiltx -= 8; } else { if (tiltx > 0) /* Slowly remove tilt */ tiltx -= 8; if (tiltx < 0) tiltx += 8; } if ((kb_keydown(KEY_UP)) && (playerspeed < MAX_SPEED)) playerspeed += 40; if ((kb_keydown(KEY_DOWN)) && (playerspeed > -MAX_SPEED)) playerspeed -= 40; if (playerspeed > 0) /* Add friction */ playerspeed--; if (playerspeed < 0) playerspeed++; playerx += (icos[playerangle] * playerspeed) >>8; playery += (isin[playerangle] * playerspeed) >>8; /* Move the player based on angle and speed */ if (kb_keydown(KEY_W)) /* Increase height */ { if (elevation < 14370){ elevation += 5 * 60; recalc_perspective(); } } if (kb_keydown(KEY_S)) /* Decrease height */ { if (elevation > 5){ elevation -= 5 * 60; recalc_perspective(); } } if (kb_keydown(KEY_E)){ pixelcount += 1; /* Increase length of ray */ recalc_perspective(); } if (kb_keydown(KEY_D)) /* Decrease length of ray */ { pixelcount -= 1; if (pixelcount < 1){ pixelcount = 1; } recalc_perspective(); } if (kb_keydown(KEY_R)) eyedistance += 1; if (kb_keydown(KEY_F)) eyedistance -= 1; /* Detail settings */ if (kb_keydown(KEY_F1)) /* Low detail */ { draw_routine = LOW_DETAIL; view_width = LOW_WIDTH; } if (kb_keydown(KEY_F2)) /* Medium detail */ { draw_routine = MEDIUM_DETAIL; view_width = MEDIUM_WIDTH; } if (kb_keydown(KEY_F3)) /* High detail */ { draw_routine = HIGH_DETAIL; view_width = HIGH_WIDTH; } /* help */ if(kb_keydown(KEY_H)){ popup_info(KEYS,250,15); } }int main (void){ buffer_rec *offscreen; /* an offscreen buffer */ if(SCREEN_WIDTH != 320){ puts("This demo only runs in 320 x 200 screen modes."); exit(0); } /* load images */ ground_image = image_load_pcx("height.pcx"); color_image = image_load_pcx("ground.pcx"); cloud_image = image_load_pcx("cloud.pcx"); ground_data = ground_image->data; color_data = color_image->data; /* set up graphics, palette, offscreen buffer */ screen_set_video_mode(); screen_block_set_pal(IMG_PALETTE(color_image)); offscreen = buff_init(SCREEN_WIDTH,SCREEN_HEIGHT); /* set up tables */ h_create_sincos(); h_calculate_perspective(); h_calculate_distance_table(); /* set player starting position/angle */ playerx = playery = (100L << 8); playerangle = playerspeed = 0; recalc_perspective(); kb_init(); /* draw one screen and pop up the author and key information */ h_draw_heightfield (offscreen); screen_blit_fs_buffer(offscreen); popup_info(AUTHORS,250,15); popup_info(KEYS,250,15); /* loop, updating until escape is pressed */ do{ h_draw_heightfield (offscreen); process_keyboard(); screen_blit_fs_buffer(offscreen); }while(!kb_keydown(KEY_ESC)); /* clean up and exit */ kb_closedown(); screen_restore_video_mode(); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -