1 /* $Id: terrain.c,v 1.4 2003-10-10 09:36:35 btb Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
17 * Code to render cool external-scene terrain
20 * Revision 1.1 1995/05/16 15:31:29 allender
23 * Revision 2.0 1995/02/27 11:31:27 john
24 * New version 2.0, which has no anonymous unions, builds with
25 * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
27 * Revision 1.12 1994/12/03 00:18:00 matt
28 * Made endlevel sequence cut off early
29 * Made exit model and bit explosion always plot last (after all terrain)
31 * Revision 1.11 1994/11/27 23:13:46 matt
32 * Made changes for new mprintf calling convention
34 * Revision 1.10 1994/11/21 18:04:36 matt
35 * Fixed alloc/free problem with height array
37 * Revision 1.9 1994/11/21 17:30:42 matt
38 * Properly free light array
40 * Revision 1.8 1994/11/19 12:40:55 matt
41 * Added system to read endlevel data from file, and to make it work
42 * with any exit tunnel.
44 * Revision 1.7 1994/11/16 11:49:44 matt
45 * Added code to rotate terrain to match mine
47 * Revision 1.6 1994/11/02 16:22:59 matt
50 * Revision 1.5 1994/10/30 20:09:19 matt
51 * For endlevel: added big explosion at tunnel exit; made lights in tunnel
52 * go out; made more explosions on walls.
54 * Revision 1.4 1994/10/27 21:15:07 matt
55 * Added better error handling
57 * Revision 1.3 1994/10/27 01:03:17 matt
58 * Made terrain renderer use aribtary point in height array as origin
60 * Revision 1.2 1994/08/19 20:09:44 matt
61 * Added end-of-level cut scene with external scene
63 * Revision 1.1 1994/08/17 20:20:49 matt
93 #define GRID_MAX_SIZE 64
94 #define GRID_SCALE i2f(2*20)
95 #define HEIGHT_SCALE f1_0
99 g3s_uvl uvl_list1[] = { {0,0,0}, {f1_0,0,0}, {0,f1_0,0} };
100 g3s_uvl uvl_list2[] = { {f1_0,0,0}, {f1_0,f1_0,0}, {0,f1_0,0} };
105 #define HEIGHT(_i,_j) (height_array[(_i)*grid_w+(_j)])
106 #define LIGHT(_i,_j) light_array[(_i)*grid_w+(_j)]
108 //!!#define HEIGHT(_i,_j) height_array[(grid_h-1-j)*grid_w+(_i)]
109 //!!#define LIGHT(_i,_j) light_array[(grid_h-1-j)*grid_w+(_i)]
111 #define LIGHTVAL(_i,_j) (((fix) LIGHT(_i,_j))<<8)
113 g3s_point save_row[GRID_MAX_SIZE];
115 vms_vector start_point;
117 grs_bitmap *terrain_bm;
119 int terrain_outline=0;
123 int mine_tiles_drawn; //flags to tell if all 4 tiles under mine have drawn
126 // LINT: adding function prototypes
127 void build_light_table(void);
128 void free_light_table(void);
130 // ------------------------------------------------------------------------
131 void draw_cell(int i,int j,g3s_point *p0,g3s_point *p1,g3s_point *p2,g3s_point *p3)
133 g3s_point *pointlist[3];
138 uvl_list1[0].l = LIGHTVAL(i,j);
139 uvl_list1[1].l = LIGHTVAL(i,j+1);
140 uvl_list1[2].l = LIGHTVAL(i+1,j);
142 uvl_list1[0].u = (i)*f1_0/4; uvl_list1[0].v = (j)*f1_0/4;
143 uvl_list1[1].u = (i)*f1_0/4; uvl_list1[1].v = (j+1)*f1_0/4;
144 uvl_list1[2].u = (i+1)*f1_0/4; uvl_list1[2].v = (j)*f1_0/4;
146 g3_check_and_draw_tmap(3,pointlist,uvl_list1,terrain_bm,NULL,NULL);
147 if (terrain_outline) {
148 int lsave=Lighting_on;
150 gr_setcolor(BM_XRGB(31,0,0));
151 g3_draw_line(pointlist[0],pointlist[1]);
152 g3_draw_line(pointlist[2],pointlist[0]);
158 uvl_list2[0].l = LIGHTVAL(i,j+1);
159 uvl_list2[1].l = LIGHTVAL(i+1,j+1);
160 uvl_list2[2].l = LIGHTVAL(i+1,j);
162 uvl_list2[0].u = (i)*f1_0/4; uvl_list2[0].v = (j+1)*f1_0/4;
163 uvl_list2[1].u = (i+1)*f1_0/4; uvl_list2[1].v = (j+1)*f1_0/4;
164 uvl_list2[2].u = (i+1)*f1_0/4; uvl_list2[2].v = (j)*f1_0/4;
166 g3_check_and_draw_tmap(3,pointlist,uvl_list2,terrain_bm,NULL,NULL);
167 if (terrain_outline) {
168 int lsave=Lighting_on;
170 gr_setcolor(BM_XRGB(31,0,0));
171 g3_draw_line(pointlist[0],pointlist[1]);
172 g3_draw_line(pointlist[1],pointlist[2]);
173 g3_draw_line(pointlist[2],pointlist[0]);
177 if (i==org_i && j==org_j)
178 mine_tiles_drawn |= 1;
179 if (i==org_i-1 && j==org_j)
180 mine_tiles_drawn |= 2;
181 if (i==org_i && j==org_j-1)
182 mine_tiles_drawn |= 4;
183 if (i==org_i-1 && j==org_j-1)
184 mine_tiles_drawn |= 8;
186 if (mine_tiles_drawn == 0xf) {
187 render_mine(exit_segnum, 0, 0);
190 //if (ext_expl_playing)
191 // draw_fireball(&external_explosion);
196 vms_vector y_cache[256];
199 extern vms_matrix surface_orient;
201 vms_vector *get_dy_vec(int h)
210 //@@g3_rotate_delta_y(dyp,h*HEIGHT_SCALE);
212 vm_vec_copy_scale(&tv,&surface_orient.uvec,h*HEIGHT_SCALE);
213 g3_rotate_delta_vec(dyp,&tv);
224 void render_terrain(vms_vector *org_point,int org_2dx,int org_2dy)
226 vms_vector delta_i,delta_j; //delta_y;
227 g3s_point p,last_p,save_p_low,save_p_high;
230 int low_i,high_i,low_j,high_j;
231 int viewer_i,viewer_j;
234 mine_tiles_drawn = 0; //clear flags
239 low_i = 0; high_i = grid_w-1;
240 low_j = 0; high_j = grid_h-1;
242 //@@start_point.x = org_point->x - GRID_SCALE*(org_i - low_i);
243 //@@start_point.z = org_point->z - GRID_SCALE*(org_j - low_j);
244 //@@start_point.y = org_point->y;
246 memset(yc_flags,0,256);
249 Interpolation_method = im;
251 vm_vec_copy_scale(&tv,&surface_orient.rvec,GRID_SCALE);
252 g3_rotate_delta_vec(&delta_i,&tv);
253 vm_vec_copy_scale(&tv,&surface_orient.fvec,GRID_SCALE);
254 g3_rotate_delta_vec(&delta_j,&tv);
256 vm_vec_scale_add(&start_point,org_point,&surface_orient.rvec,-(org_i - low_i)*GRID_SCALE);
257 vm_vec_scale_add2(&start_point,&surface_orient.fvec,-(org_j - low_j)*GRID_SCALE);
259 vm_vec_sub(&tv,&Viewer->pos,&start_point);
260 viewer_i = vm_vec_dot(&tv,&surface_orient.rvec) / GRID_SCALE;
261 viewer_j = vm_vec_dot(&tv,&surface_orient.fvec) / GRID_SCALE;
263 //mprintf((0,"viewer_i,j = %d,%d\n",viewer_i,viewer_j));
265 g3_rotate_point(&last_p,&start_point);
268 for (j=low_j;j<=high_j;j++) {
269 g3_add_delta_vec(&save_row[j],&last_p,get_dy_vec(HEIGHT(low_i,j)));
271 save_p_high = last_p;
273 g3_add_delta_vec(&last_p,&last_p,&delta_j);
276 for (i=low_i;i<viewer_i;i++) {
278 g3_add_delta_vec(&save_p_low,&save_p_low,&delta_i);
280 g3_add_delta_vec(&last_p2,&last_p,get_dy_vec(HEIGHT(i+1,low_j)));
282 for (j=low_j;j<viewer_j;j++) {
285 g3_add_delta_vec(&p,&last_p,&delta_j);
286 g3_add_delta_vec(&p2,&p,get_dy_vec(HEIGHT(i+1,j+1)));
288 draw_cell(i,j,&save_row[j],&save_row[j+1],&p2,&last_p2);
291 save_row[j] = last_p2;
296 vm_vec_negate(&delta_j); //don't have a delta sub...
298 g3_add_delta_vec(&save_p_high,&save_p_high,&delta_i);
299 last_p = save_p_high;
300 g3_add_delta_vec(&last_p2,&last_p,get_dy_vec(HEIGHT(i+1,high_j)));
302 for (j=high_j-1;j>=viewer_j;j--) {
305 g3_add_delta_vec(&p,&last_p,&delta_j);
306 g3_add_delta_vec(&p2,&p,get_dy_vec(HEIGHT(i+1,j)));
308 draw_cell(i,j,&save_row[j],&save_row[j+1],&last_p2,&p2);
311 save_row[j+1] = last_p2;
316 save_row[j+1] = last_p2;
318 vm_vec_negate(&delta_j); //restore sign of j
322 //now do i from other end
324 vm_vec_negate(&delta_i); //going the other way now...
326 //@@start_point.x += (high_i-low_i)*GRID_SCALE;
327 vm_vec_scale_add2(&start_point,&surface_orient.rvec,(high_i-low_i)*GRID_SCALE);
328 g3_rotate_point(&last_p,&start_point);
331 for (j=low_j;j<=high_j;j++) {
332 g3_add_delta_vec(&save_row[j],&last_p,get_dy_vec(HEIGHT(high_i,j)));
334 save_p_high = last_p;
336 g3_add_delta_vec(&last_p,&last_p,&delta_j);
339 for (i=high_i-1;i>=viewer_i;i--) {
341 g3_add_delta_vec(&save_p_low,&save_p_low,&delta_i);
343 g3_add_delta_vec(&last_p2,&last_p,get_dy_vec(HEIGHT(i,low_j)));
345 for (j=low_j;j<viewer_j;j++) {
348 g3_add_delta_vec(&p,&last_p,&delta_j);
349 g3_add_delta_vec(&p2,&p,get_dy_vec(HEIGHT(i,j+1)));
351 draw_cell(i,j,&last_p2,&p2,&save_row[j+1],&save_row[j]);
354 save_row[j] = last_p2;
359 vm_vec_negate(&delta_j); //don't have a delta sub...
361 g3_add_delta_vec(&save_p_high,&save_p_high,&delta_i);
362 last_p = save_p_high;
363 g3_add_delta_vec(&last_p2,&last_p,get_dy_vec(HEIGHT(i,high_j)));
365 for (j=high_j-1;j>=viewer_j;j--) {
368 g3_add_delta_vec(&p,&last_p,&delta_j);
369 g3_add_delta_vec(&p2,&p,get_dy_vec(HEIGHT(i,j)));
371 draw_cell(i,j,&p2,&last_p2,&save_row[j+1],&save_row[j]);
374 save_row[j+1] = last_p2;
379 save_row[j+1] = last_p2;
381 vm_vec_negate(&delta_j); //restore sign of j
387 void free_height_array()
389 d_free(height_array);
392 void load_terrain(char *filename)
394 grs_bitmap height_bitmap;
399 iff_error = iff_read_bitmap(filename,&height_bitmap,BM_LINEAR,NULL);
400 if (iff_error != IFF_NO_ERROR) {
401 mprintf((1, "File %s - IFF error: %s",filename,iff_errormsg(iff_error)));
402 Error("File %s - IFF error: %s",filename,iff_errormsg(iff_error));
406 d_free(height_array);
408 atexit(free_height_array); //first time
410 grid_w = height_bitmap.bm_w;
411 grid_h = height_bitmap.bm_h;
413 Assert(grid_w <= GRID_MAX_SIZE);
414 Assert(grid_h <= GRID_MAX_SIZE);
416 height_array = height_bitmap.bm_data;
419 for (i=0;i<grid_w;i++)
420 for (j=0;j<grid_h;j++) {
431 for (i=0;i<grid_w;i++)
432 for (j=0;j<grid_h;j++)
433 HEIGHT(i,j) -= min_h;
436 // d_free(height_bitmap.bm_data);
438 terrain_bm = terrain_bitmap;
444 void get_pnt(vms_vector *p,int i,int j)
448 p->y = HEIGHT(i,j)*HEIGHT_SCALE;
451 vms_vector light = {0x2e14,0xe8f5,0x5eb8};
453 fix get_face_light(vms_vector *p0,vms_vector *p1,vms_vector *p2)
457 vm_vec_normal(&norm,p0,p1,p2);
459 return -vm_vec_dot(&norm,&light);
464 fix get_avg_light(int i,int j)
471 get_pnt(&p[0],i-1,j);
472 get_pnt(&p[1],i,j-1);
473 get_pnt(&p[2],i+1,j-1);
474 get_pnt(&p[3],i+1,j);
475 get_pnt(&p[4],i,j+1);
476 get_pnt(&p[5],i-1,j+1);
478 for (f=0,sum=0;f<6;f++)
479 sum += get_face_light(&pp,&p[f],&p[(f+1)%5]);
484 void free_light_table()
491 void build_light_table()
494 fix l,l2,min_l=0x7fffffff,max_l=0;
500 atexit(free_light_table); //first time
502 MALLOC(light_array,ubyte,grid_w*grid_h);
504 for (i=1;i<grid_w;i++)
505 for (j=1;j<grid_h;j++) {
506 l = get_avg_light(i,j);
514 //printf("light %2d,%2d = %8x\n",i,j,l);
517 for (i=1;i<grid_w;i++)
518 for (j=1;j<grid_h;j++) {
520 l = get_avg_light(i,j);
522 if (min_l == max_l) {
527 l2 = fixdiv((l-min_l),(max_l-min_l));
534 //printf("light %2d,%2d = %4x\n",i,j,l2>>8);