]> icculus.org git repositories - btb/d2x.git/blob - main/terrain.c
utilize hardware multitexturing support if possible (requires GL_NV_texture_env_combi...
[btb/d2x.git] / main / terrain.c
1 /* $Id: terrain.c,v 1.4 2003-10-10 09:36:35 btb Exp $ */
2 /*
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.
13 */
14
15 /*
16  *
17  * Code to render cool external-scene terrain
18  *
19  * Old Log:
20  * Revision 1.1  1995/05/16  15:31:29  allender
21  * Initial revision
22  *
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.
26  *
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)
30  *
31  * Revision 1.11  1994/11/27  23:13:46  matt
32  * Made changes for new mprintf calling convention
33  *
34  * Revision 1.10  1994/11/21  18:04:36  matt
35  * Fixed alloc/free problem with height array
36  *
37  * Revision 1.9  1994/11/21  17:30:42  matt
38  * Properly free light array
39  *
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.
43  *
44  * Revision 1.7  1994/11/16  11:49:44  matt
45  * Added code to rotate terrain to match mine
46  *
47  * Revision 1.6  1994/11/02  16:22:59  matt
48  * Killed mprintf
49  *
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.
53  *
54  * Revision 1.4  1994/10/27  21:15:07  matt
55  * Added better error handling
56  *
57  * Revision 1.3  1994/10/27  01:03:17  matt
58  * Made terrain renderer use aribtary point in height array as origin
59  *
60  * Revision 1.2  1994/08/19  20:09:44  matt
61  * Added end-of-level cut scene with external scene
62  *
63  * Revision 1.1  1994/08/17  20:20:49  matt
64  * Initial revision
65  *
66  *
67  */
68
69
70 #ifdef HAVE_CONFIG_H
71 #include <conf.h>
72 #endif
73
74 #include <stdio.h>
75 #include <stdlib.h>
76 #include <string.h>
77
78 #include "3d.h"
79 #include "error.h"
80 #include "gr.h"
81 #include "texmap.h"
82 #include "iff.h"
83 #include "u_mem.h"
84 #include "mono.h"
85
86 #include "inferno.h"
87 #include "textures.h"
88 #include "object.h"
89 #include "endlevel.h"
90 #include "fireball.h"
91 #include "render.h"
92
93 #define GRID_MAX_SIZE   64
94 #define GRID_SCALE      i2f(2*20)
95 #define HEIGHT_SCALE    f1_0
96
97 int grid_w,grid_h;
98
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} };
101
102 ubyte *height_array;
103 ubyte *light_array;
104
105 #define HEIGHT(_i,_j) (height_array[(_i)*grid_w+(_j)])
106 #define LIGHT(_i,_j) light_array[(_i)*grid_w+(_j)]
107
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)]
110
111 #define LIGHTVAL(_i,_j) (((fix) LIGHT(_i,_j))<<8)
112
113 g3s_point save_row[GRID_MAX_SIZE];
114
115 vms_vector start_point;
116
117 grs_bitmap *terrain_bm;
118
119 int terrain_outline=0;
120
121 int org_i,org_j;
122
123 int mine_tiles_drawn;    //flags to tell if all 4 tiles under mine have drawn
124
125
126 // LINT: adding function prototypes
127 void build_light_table(void);
128 void free_light_table(void);
129
130 // ------------------------------------------------------------------------
131 void draw_cell(int i,int j,g3s_point *p0,g3s_point *p1,g3s_point *p2,g3s_point *p3)
132 {
133         g3s_point *pointlist[3];
134
135         pointlist[0] = p0;
136         pointlist[1] = p1;
137         pointlist[2] = p3;
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);
141
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;
145
146         g3_check_and_draw_tmap(3,pointlist,uvl_list1,terrain_bm,NULL,NULL);
147         if (terrain_outline) {
148                 int lsave=Lighting_on;
149                 Lighting_on=0;
150                 gr_setcolor(BM_XRGB(31,0,0));
151                 g3_draw_line(pointlist[0],pointlist[1]);
152                 g3_draw_line(pointlist[2],pointlist[0]);
153                 Lighting_on=lsave;
154         }
155
156         pointlist[0] = p1;
157         pointlist[1] = p2;
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);
161
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;
165
166         g3_check_and_draw_tmap(3,pointlist,uvl_list2,terrain_bm,NULL,NULL);
167         if (terrain_outline) {
168                 int lsave=Lighting_on;
169                 Lighting_on=0;
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]);
174                 Lighting_on=lsave;
175         }
176
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;
185
186         if (mine_tiles_drawn == 0xf) {
187                 render_mine(exit_segnum, 0, 0);
188                 //draw_exit_model();
189                 mine_tiles_drawn=-1;
190                 //if (ext_expl_playing)
191                 //      draw_fireball(&external_explosion);
192         }
193
194 }
195
196 vms_vector y_cache[256];
197 ubyte yc_flags[256];
198
199 extern vms_matrix surface_orient;
200
201 vms_vector *get_dy_vec(int h)
202 {
203         vms_vector *dyp;
204
205         dyp = &y_cache[h];
206
207         if (!yc_flags[h]) {
208                 vms_vector tv;
209
210                 //@@g3_rotate_delta_y(dyp,h*HEIGHT_SCALE);
211
212                 vm_vec_copy_scale(&tv,&surface_orient.uvec,h*HEIGHT_SCALE);
213                 g3_rotate_delta_vec(dyp,&tv);
214
215                 yc_flags[h] = 1;
216         }
217
218         return dyp;
219
220 }
221
222 int im=1;
223
224 void render_terrain(vms_vector *org_point,int org_2dx,int org_2dy)
225 {
226         vms_vector delta_i,delta_j;             //delta_y;
227         g3s_point p,last_p,save_p_low,save_p_high;
228         g3s_point last_p2;
229         int i,j;
230         int low_i,high_i,low_j,high_j;
231         int viewer_i,viewer_j;
232         vms_vector tv;
233
234         mine_tiles_drawn = 0;   //clear flags
235
236         org_i = org_2dy;
237         org_j = org_2dx;
238
239         low_i = 0;  high_i = grid_w-1;
240         low_j = 0;  high_j = grid_h-1;
241
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;
245
246         memset(yc_flags,0,256);
247
248         //Lighting_on = 0;
249         Interpolation_method = im;
250
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);
255
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);
258
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;
262
263 //mprintf((0,"viewer_i,j = %d,%d\n",viewer_i,viewer_j));
264
265         g3_rotate_point(&last_p,&start_point);
266         save_p_low = last_p;
267
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)));
270                 if (j==high_j)
271                         save_p_high = last_p;
272                 else
273                         g3_add_delta_vec(&last_p,&last_p,&delta_j);
274         }
275
276         for (i=low_i;i<viewer_i;i++) {
277
278                 g3_add_delta_vec(&save_p_low,&save_p_low,&delta_i);
279                 last_p = save_p_low;
280                 g3_add_delta_vec(&last_p2,&last_p,get_dy_vec(HEIGHT(i+1,low_j)));
281                 
282                 for (j=low_j;j<viewer_j;j++) {
283                         g3s_point p2;
284
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)));
287
288                         draw_cell(i,j,&save_row[j],&save_row[j+1],&p2,&last_p2);
289
290                         last_p = p;
291                         save_row[j] = last_p2;
292                         last_p2 = p2;
293
294                 }
295
296                 vm_vec_negate(&delta_j);                        //don't have a delta sub...
297
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)));
301                 
302                 for (j=high_j-1;j>=viewer_j;j--) {
303                         g3s_point p2;
304
305                         g3_add_delta_vec(&p,&last_p,&delta_j);
306                         g3_add_delta_vec(&p2,&p,get_dy_vec(HEIGHT(i+1,j)));
307
308                         draw_cell(i,j,&save_row[j],&save_row[j+1],&last_p2,&p2);
309
310                         last_p = p;
311                         save_row[j+1] = last_p2;
312                         last_p2 = p2;
313
314                 }
315
316                 save_row[j+1] = last_p2;
317
318                 vm_vec_negate(&delta_j);                //restore sign of j
319
320         }
321
322         //now do i from other end
323
324         vm_vec_negate(&delta_i);                //going the other way now...
325
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);
329         save_p_low = last_p;
330
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)));
333                 if (j==high_j)
334                         save_p_high = last_p;
335                 else
336                         g3_add_delta_vec(&last_p,&last_p,&delta_j);
337         }
338
339         for (i=high_i-1;i>=viewer_i;i--) {
340
341                 g3_add_delta_vec(&save_p_low,&save_p_low,&delta_i);
342                 last_p = save_p_low;
343                 g3_add_delta_vec(&last_p2,&last_p,get_dy_vec(HEIGHT(i,low_j)));
344                 
345                 for (j=low_j;j<viewer_j;j++) {
346                         g3s_point p2;
347
348                         g3_add_delta_vec(&p,&last_p,&delta_j);
349                         g3_add_delta_vec(&p2,&p,get_dy_vec(HEIGHT(i,j+1)));
350
351                         draw_cell(i,j,&last_p2,&p2,&save_row[j+1],&save_row[j]);
352
353                         last_p = p;
354                         save_row[j] = last_p2;
355                         last_p2 = p2;
356
357                 }
358
359                 vm_vec_negate(&delta_j);                        //don't have a delta sub...
360
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)));
364                 
365                 for (j=high_j-1;j>=viewer_j;j--) {
366                         g3s_point p2;
367
368                         g3_add_delta_vec(&p,&last_p,&delta_j);
369                         g3_add_delta_vec(&p2,&p,get_dy_vec(HEIGHT(i,j)));
370
371                         draw_cell(i,j,&p2,&last_p2,&save_row[j+1],&save_row[j]);
372
373                         last_p = p;
374                         save_row[j+1] = last_p2;
375                         last_p2 = p2;
376
377                 }
378
379                 save_row[j+1] = last_p2;
380
381                 vm_vec_negate(&delta_j);                //restore sign of j
382
383         }
384
385 }
386
387 void free_height_array()
388 {
389         d_free(height_array);
390 }
391
392 void load_terrain(char *filename)
393 {
394         grs_bitmap height_bitmap;
395         int iff_error;
396         int i,j;
397         ubyte h,min_h,max_h;
398
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));
403         }
404
405         if (height_array)
406                 d_free(height_array);
407         else
408                 atexit(free_height_array);              //first time
409
410         grid_w = height_bitmap.bm_w;
411         grid_h = height_bitmap.bm_h;
412
413         Assert(grid_w <= GRID_MAX_SIZE);
414         Assert(grid_h <= GRID_MAX_SIZE);
415
416         height_array = height_bitmap.bm_data;
417
418         max_h=0; min_h=255;
419         for (i=0;i<grid_w;i++)
420                 for (j=0;j<grid_h;j++) {
421
422                         h = HEIGHT(i,j);
423
424                         if (h > max_h)
425                                 max_h = h;
426
427                         if (h < min_h)
428                                 min_h = h;
429                 }
430
431         for (i=0;i<grid_w;i++)
432                 for (j=0;j<grid_h;j++)
433                         HEIGHT(i,j) -= min_h;
434         
435
436 //      d_free(height_bitmap.bm_data);
437
438         terrain_bm = terrain_bitmap;
439
440         build_light_table();
441 }
442
443
444 void get_pnt(vms_vector *p,int i,int j)
445 {
446         p->x = GRID_SCALE*i;
447         p->z = GRID_SCALE*j;
448         p->y = HEIGHT(i,j)*HEIGHT_SCALE;
449 }
450
451 vms_vector light = {0x2e14,0xe8f5,0x5eb8};
452
453 fix get_face_light(vms_vector *p0,vms_vector *p1,vms_vector *p2)
454 {
455         vms_vector norm;
456
457         vm_vec_normal(&norm,p0,p1,p2);
458
459         return -vm_vec_dot(&norm,&light);
460
461 }
462
463
464 fix get_avg_light(int i,int j)
465 {
466         vms_vector pp,p[6];
467         fix sum;
468         int f;
469
470         get_pnt(&pp,i,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);
477
478         for (f=0,sum=0;f<6;f++)
479                 sum += get_face_light(&pp,&p[f],&p[(f+1)%5]);
480
481         return sum/6;
482 }
483
484 void free_light_table()
485 {
486         if (light_array)
487                 d_free(light_array);
488
489 }
490
491 void build_light_table()
492 {
493         int i,j;
494         fix l,l2,min_l=0x7fffffff,max_l=0;
495
496
497         if (light_array)
498                 d_free(light_array);
499         else
500                 atexit(free_light_table);               //first time
501
502         MALLOC(light_array,ubyte,grid_w*grid_h);
503
504         for (i=1;i<grid_w;i++)
505                 for (j=1;j<grid_h;j++) {
506                         l = get_avg_light(i,j);
507
508                         if (l > max_l)
509                                 max_l = l;
510
511                         if (l < min_l)
512                                 min_l = l;
513
514                         //printf("light %2d,%2d = %8x\n",i,j,l);
515                 }
516
517         for (i=1;i<grid_w;i++)
518                 for (j=1;j<grid_h;j++) {
519
520                         l = get_avg_light(i,j);
521
522                         if (min_l == max_l) {
523                                 LIGHT(i,j) = l>>8;
524                                 continue;
525                         }
526
527                         l2 = fixdiv((l-min_l),(max_l-min_l));
528
529                         if (l2==f1_0)
530                                 l2--;
531
532                         LIGHT(i,j) = l2>>8;
533
534                         //printf("light %2d,%2d = %4x\n",i,j,l2>>8);
535
536                 }
537 }