Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Portions of this file are copyright Rebirth contributors and licensed as
3
 * described in COPYING.txt.
4
 * Portions of this file are copyright Parallax Software and licensed
5
 * according to the Parallax license below.
6
 * See COPYING.txt for license details.
7
 
8
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Code to render cool external-scene terrain
23
 *
24
 */
25
 
26
#include <bitset>
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
 
31
#include "3d.h"
32
#include "dxxerror.h"
33
#include "gr.h"
34
#include "texmap.h"
35
#include "iff.h"
36
#include "u_mem.h"
37
#include "inferno.h"
38
#include "textures.h"
39
#include "object.h"
40
#include "endlevel.h"
41
#include "fireball.h"
42
#include "render.h"
43
#include "player.h"
44
#include "segment.h"
45
#include "terrain.h"
46
#include <memory>
47
 
48
#define GRID_MAX_SIZE   64
49
#define GRID_SCALE      i2f(2*20)
50
#define HEIGHT_SCALE    f1_0
51
 
52
static int grid_w,grid_h;
53
 
54
static RAIIdmem<ubyte[]> height_array;
55
static std::unique_ptr<uint8_t[]> light_array;
56
 
57
#define HEIGHT(_i,_j) (height_array[(_i)*grid_w+(_j)])
58
#define LIGHT(_i,_j) light_array[(_i)*grid_w+(_j)]
59
 
60
//!!#define HEIGHT(_i,_j)   height_array[(grid_h-1-j)*grid_w+(_i)]
61
//!!#define LIGHT(_i,_j)    light_array[(grid_h-1-j)*grid_w+(_i)]
62
 
63
#define LIGHTVAL(_i,_j) (static_cast<fix>(LIGHT(_i, _j)) << 8)
64
 
65
static grs_bitmap *terrain_bm;
66
static int terrain_outline=0;
67
static int org_i,org_j;
68
 
69
// LINT: adding function prototypes
70
static void build_light_table(void);
71
 
72
// ------------------------------------------------------------------------
73
static void draw_cell(grs_canvas &canvas, const vms_vector &Viewer_eye, const int i, const int j, cg3s_point &p0, cg3s_point &p1, cg3s_point &p2, cg3s_point &p3, int &mine_tiles_drawn)
74
{
75
        std::array<cg3s_point *, 3> pointlist;
76
 
77
        pointlist[0] = &p0;
78
        pointlist[1] = &p1;
79
        pointlist[2] = &p3;
80
        std::array<g3s_lrgb, 3> lrgb_list1;
81
        std::array<g3s_uvl, 3> uvl_list1;
82
        lrgb_list1[0].r = lrgb_list1[0].g = lrgb_list1[0].b = uvl_list1[0].l = LIGHTVAL(i,j);
83
        lrgb_list1[1].r = lrgb_list1[1].g = lrgb_list1[1].b = uvl_list1[1].l = LIGHTVAL(i,j+1);
84
        lrgb_list1[2].r = lrgb_list1[2].g = lrgb_list1[2].b = uvl_list1[2].l = LIGHTVAL(i+1,j);
85
 
86
        uvl_list1[0].u = (i)*f1_0/4; uvl_list1[0].v = (j)*f1_0/4;
87
        uvl_list1[1].u = (i)*f1_0/4; uvl_list1[1].v = (j+1)*f1_0/4;
88
        uvl_list1[2].u = (i+1)*f1_0/4;   uvl_list1[2].v = (j)*f1_0/4;
89
 
90
        g3_check_and_draw_tmap(canvas, pointlist, uvl_list1, lrgb_list1, *terrain_bm);
91
        if (terrain_outline) {
92
#if !DXX_USE_OGL
93
                const int lsave = Lighting_on;
94
                Lighting_on=0;
95
#endif
96
                const uint8_t color = BM_XRGB(31, 0, 0);
97
                g3_draw_line(canvas, *pointlist[0],*pointlist[1], color);
98
                g3_draw_line(canvas, *pointlist[2],*pointlist[0], color);
99
#if !DXX_USE_OGL
100
                Lighting_on=lsave;
101
#endif
102
        }
103
 
104
        pointlist[0] = &p1;
105
        pointlist[1] = &p2;
106
        std::array<g3s_uvl, 3> uvl_list2;
107
        std::array<g3s_lrgb, 3> lrgb_list2;
108
        lrgb_list2[0].r = lrgb_list2[0].g = lrgb_list2[0].b = uvl_list2[0].l = LIGHTVAL(i,j+1);
109
        lrgb_list2[1].r = lrgb_list2[1].g = lrgb_list2[1].b = uvl_list2[1].l = LIGHTVAL(i+1,j+1);
110
        lrgb_list2[2].r = lrgb_list2[2].g = lrgb_list2[2].b = uvl_list2[2].l = LIGHTVAL(i+1,j);
111
 
112
        uvl_list2[0].u = (i)*f1_0/4; uvl_list2[0].v = (j+1)*f1_0/4;
113
        uvl_list2[1].u = (i+1)*f1_0/4;   uvl_list2[1].v = (j+1)*f1_0/4;
114
        uvl_list2[2].u = (i+1)*f1_0/4;   uvl_list2[2].v = (j)*f1_0/4;
115
 
116
        g3_check_and_draw_tmap(canvas, pointlist, uvl_list2, lrgb_list2, *terrain_bm);
117
        if (terrain_outline) {
118
#if !DXX_USE_OGL
119
                const int lsave = Lighting_on;
120
                Lighting_on=0;
121
#endif
122
                const uint8_t color = BM_XRGB(31, 0, 0);
123
                g3_draw_line(canvas, *pointlist[0],*pointlist[1], color);
124
                g3_draw_line(canvas, *pointlist[1],*pointlist[2], color);
125
                g3_draw_line(canvas, *pointlist[2],*pointlist[0], color);
126
#if !DXX_USE_OGL
127
                Lighting_on=lsave;
128
#endif
129
        }
130
 
131
        if (i==org_i && j==org_j)
132
                mine_tiles_drawn |= 1;
133
        if (i==org_i-1 && j==org_j)
134
                mine_tiles_drawn |= 2;
135
        if (i==org_i && j==org_j-1)
136
                mine_tiles_drawn |= 4;
137
        if (i==org_i-1 && j==org_j-1)
138
                mine_tiles_drawn |= 8;
139
 
140
        if (mine_tiles_drawn == 0xf) {
141
                //draw_exit_model();
142
                mine_tiles_drawn=-1;
143
                window_rendered_data window;
144
                render_mine(canvas, Viewer_eye, PlayerUniqueEndlevelState.exit_segnum, 0, window);
145
                //if (ext_expl_playing)
146
                //      draw_fireball(&external_explosion);
147
        }
148
 
149
}
150
 
151
namespace {
152
 
153
class terrain_y_cache
154
{
155
        static const std::size_t cache_size = 256;
156
        std::bitset<cache_size> yc_flags;
157
        std::array<vms_vector, cache_size> y_cache;
158
public:
159
        vms_vector &operator()(uint_fast32_t h);
160
};
161
 
162
}
163
 
164
vms_vector &terrain_y_cache::operator()(uint_fast32_t h)
165
{
166
        auto &dyp = y_cache[h];
167
        if (auto &&ycf = yc_flags[h])
168
        {
169
        }
170
        else
171
        {
172
                ycf = 1;
173
                const auto tv = vm_vec_copy_scale(surface_orient.uvec,h*HEIGHT_SCALE);
174
                g3_rotate_delta_vec(dyp,tv);
175
        }
176
        return dyp;
177
}
178
 
179
void render_terrain(grs_canvas &canvas, const vms_vector &Viewer_eye, const vms_vector &org_point,int org_2dx,int org_2dy)
180
{
181
        vms_vector delta_i,delta_j;             //delta_y;
182
        g3s_point p,save_p_low,save_p_high;
183
        g3s_point last_p2;
184
        int i,j;
185
        int low_i,high_i,low_j,high_j;
186
        int viewer_i,viewer_j;
187
        org_i = org_2dy;
188
        org_j = org_2dx;
189
 
190
        low_i = 0;  high_i = grid_w-1;
191
        low_j = 0;  high_j = grid_h-1;
192
 
193
        //@@start_point.x = org_point->x - GRID_SCALE*(org_i - low_i);
194
        //@@start_point.z = org_point->z - GRID_SCALE*(org_j - low_j);
195
        //@@start_point.y = org_point->y;
196
        terrain_y_cache get_dy_vec;
197
 
198
#if !DXX_USE_OGL
199
        Interpolation_method = 1;
200
#endif
201
 
202
        {
203
        const auto tv = vm_vec_copy_scale(surface_orient.rvec,GRID_SCALE);
204
        g3_rotate_delta_vec(delta_i,tv);
205
        }
206
        {
207
        const auto tv = vm_vec_copy_scale(surface_orient.fvec,GRID_SCALE);
208
        g3_rotate_delta_vec(delta_j,tv);
209
        }
210
 
211
        auto start_point = vm_vec_scale_add(org_point,surface_orient.rvec,-(org_i - low_i)*GRID_SCALE);
212
        vm_vec_scale_add2(start_point,surface_orient.fvec,-(org_j - low_j)*GRID_SCALE);
213
 
214
        {
215
                const auto tv = vm_vec_sub(Viewer->pos,start_point);
216
        viewer_i = vm_vec_dot(tv,surface_orient.rvec) / GRID_SCALE;
217
        viewer_j = vm_vec_dot(tv,surface_orient.fvec) / GRID_SCALE;
218
        }
219
 
220
        auto last_p = g3_rotate_point(start_point);
221
        save_p_low = last_p;
222
 
223
        g3s_point save_row[GRID_MAX_SIZE]{};
224
        // Is this needed?
225
        for (j=low_j;j<=high_j;j++) {
226
                g3_add_delta_vec(save_row[j],last_p,get_dy_vec(HEIGHT(low_i,j)));
227
                if (j==high_j)
228
                        save_p_high = last_p;
229
                else
230
                        g3_add_delta_vec(last_p,last_p,delta_j);
231
        }
232
 
233
        int mine_tiles_drawn = 0;    //flags to tell if all 4 tiles under mine have drawn
234
        for (i=low_i;i<viewer_i;i++) {
235
 
236
                g3_add_delta_vec(save_p_low,save_p_low,delta_i);
237
                last_p = save_p_low;
238
                g3_add_delta_vec(last_p2,last_p,get_dy_vec(HEIGHT(i+1,low_j)));
239
 
240
                for (j=low_j;j<viewer_j;j++) {
241
                        g3s_point p2;
242
 
243
                        g3_add_delta_vec(p,last_p,delta_j);
244
                        g3_add_delta_vec(p2,p,get_dy_vec(HEIGHT(i+1,j+1)));
245
 
246
                        draw_cell(canvas, Viewer_eye, i, j, save_row[j], save_row[j+1], p2, last_p2, mine_tiles_drawn);
247
 
248
                        last_p = p;
249
                        save_row[j] = last_p2;
250
                        last_p2 = p2;
251
 
252
                }
253
 
254
                vm_vec_negate(delta_j);                 //don't have a delta sub...
255
 
256
                g3_add_delta_vec(save_p_high,save_p_high,delta_i);
257
                last_p = save_p_high;
258
                g3_add_delta_vec(last_p2,last_p,get_dy_vec(HEIGHT(i+1,high_j)));
259
 
260
                for (j=high_j-1;j>=viewer_j;j--) {
261
                        g3s_point p2;
262
 
263
                        g3_add_delta_vec(p,last_p,delta_j);
264
                        g3_add_delta_vec(p2,p,get_dy_vec(HEIGHT(i+1,j)));
265
 
266
                        draw_cell(canvas, Viewer_eye, i, j, save_row[j], save_row[j+1], last_p2, p2, mine_tiles_drawn);
267
 
268
                        last_p = p;
269
                        save_row[j+1] = last_p2;
270
                        last_p2 = p2;
271
 
272
                }
273
 
274
                save_row[j+1] = last_p2;
275
 
276
                vm_vec_negate(delta_j);         //restore sign of j
277
 
278
        }
279
 
280
        //now do i from other end
281
 
282
        vm_vec_negate(delta_i);         //going the other way now...
283
 
284
        //@@start_point.x += (high_i-low_i)*GRID_SCALE;
285
        vm_vec_scale_add2(start_point,surface_orient.rvec,(high_i-low_i)*GRID_SCALE);
286
        g3_rotate_point(last_p,start_point);
287
        save_p_low = last_p;
288
 
289
        for (j=low_j;j<=high_j;j++) {
290
                g3_add_delta_vec(save_row[j],last_p,get_dy_vec(HEIGHT(high_i,j)));
291
                if (j==high_j)
292
                        save_p_high = last_p;
293
                else
294
                        g3_add_delta_vec(last_p,last_p,delta_j);
295
        }
296
 
297
        for (i=high_i-1;i>=viewer_i;i--) {
298
 
299
                g3_add_delta_vec(save_p_low,save_p_low,delta_i);
300
                last_p = save_p_low;
301
                g3_add_delta_vec(last_p2,last_p,get_dy_vec(HEIGHT(i,low_j)));
302
 
303
                for (j=low_j;j<viewer_j;j++) {
304
                        g3s_point p2;
305
 
306
                        g3_add_delta_vec(p,last_p,delta_j);
307
                        g3_add_delta_vec(p2,p,get_dy_vec(HEIGHT(i,j+1)));
308
 
309
                        draw_cell(canvas, Viewer_eye, i, j, last_p2, p2, save_row[j+1], save_row[j], mine_tiles_drawn);
310
 
311
                        last_p = p;
312
                        save_row[j] = last_p2;
313
                        last_p2 = p2;
314
 
315
                }
316
 
317
                vm_vec_negate(delta_j);                 //don't have a delta sub...
318
 
319
                g3_add_delta_vec(save_p_high,save_p_high,delta_i);
320
                last_p = save_p_high;
321
                g3_add_delta_vec(last_p2,last_p,get_dy_vec(HEIGHT(i,high_j)));
322
 
323
                for (j=high_j-1;j>=viewer_j;j--) {
324
                        g3s_point p2;
325
 
326
                        g3_add_delta_vec(p,last_p,delta_j);
327
                        g3_add_delta_vec(p2,p,get_dy_vec(HEIGHT(i,j)));
328
 
329
                        draw_cell(canvas, Viewer_eye, i, j, p2, last_p2, save_row[j+1], save_row[j], mine_tiles_drawn);
330
 
331
                        last_p = p;
332
                        save_row[j+1] = last_p2;
333
                        last_p2 = p2;
334
 
335
                }
336
 
337
                save_row[j+1] = last_p2;
338
 
339
                vm_vec_negate(delta_j);         //restore sign of j
340
 
341
        }
342
 
343
}
344
 
345
void free_height_array()
346
{
347
        height_array.reset();
348
}
349
 
350
void load_terrain(const char *filename)
351
{
352
        grs_bitmap height_bitmap;
353
        int iff_error;
354
        int i,j;
355
        ubyte h,min_h,max_h;
356
 
357
        iff_error = iff_read_bitmap(filename, height_bitmap, NULL);
358
        if (iff_error != IFF_NO_ERROR) {
359
                Error("File %s - IFF error: %s",filename,iff_errormsg(iff_error));
360
        }
361
        grid_w = height_bitmap.bm_w;
362
        grid_h = height_bitmap.bm_h;
363
 
364
        Assert(grid_w <= GRID_MAX_SIZE);
365
        Assert(grid_h <= GRID_MAX_SIZE);
366
 
367
        height_array.reset(height_bitmap.get_bitmap_data());
368
 
369
        max_h=0; min_h=255;
370
        for (i=0;i<grid_w;i++)
371
                for (j=0;j<grid_h;j++) {
372
 
373
                        h = HEIGHT(i,j);
374
 
375
                        if (h > max_h)
376
                                max_h = h;
377
 
378
                        if (h < min_h)
379
                                min_h = h;
380
                }
381
 
382
        for (i=0;i<grid_w;i++)
383
                for (j=0;j<grid_h;j++)
384
                        HEIGHT(i,j) -= min_h;
385
 
386
 
387
//      d_free(height_bitmap.bm_data);
388
 
389
        terrain_bm = terrain_bitmap;
390
 
391
        build_light_table();
392
}
393
 
394
 
395
static void get_pnt(vms_vector &p,int i,int j)
396
{
397
        // added on 02/20/99 by adb to prevent overflow
398
        if (i >= grid_h) i = grid_h - 1;
399
        if (i == grid_h - 1 && j >= grid_w) j = grid_w - 1;
400
        // end additions by adb
401
        p.x = GRID_SCALE*i;
402
        p.z = GRID_SCALE*j;
403
        p.y = HEIGHT(i,j)*HEIGHT_SCALE;
404
}
405
 
406
constexpr vms_vector light{0x2e14,0xe8f5,0x5eb8};
407
 
408
static fix get_face_light(const vms_vector &p0,const vms_vector &p1,const vms_vector &p2)
409
{
410
        const auto norm = vm_vec_normal(p0,p1,p2);
411
        return -vm_vec_dot(norm,light);
412
}
413
 
414
static fix get_avg_light(int i,int j)
415
{
416
        vms_vector pp,p[6];
417
        fix sum;
418
        int f;
419
 
420
        get_pnt(pp,i,j);
421
        get_pnt(p[0],i-1,j);
422
        get_pnt(p[1],i,j-1);
423
        get_pnt(p[2],i+1,j-1);
424
        get_pnt(p[3],i+1,j);
425
        get_pnt(p[4],i,j+1);
426
        get_pnt(p[5],i-1,j+1);
427
 
428
        for (f=0,sum=0;f<6;f++)
429
                sum += get_face_light(pp,p[f],p[(f+1)%5]);
430
 
431
        return sum/6;
432
}
433
 
434
void free_light_table()
435
{
436
        light_array.reset();
437
}
438
 
439
static void build_light_table()
440
{
441
        std::size_t alloc = grid_w*grid_h;
442
        light_array = std::make_unique<uint8_t[]>(alloc);
443
        memset(light_array.get(), 0, alloc);
444
        int i,j;
445
        fix l, l2, min_l = INT32_MAX, max_l = 0;
446
        for (i=1;i<grid_w;i++)
447
                for (j=1;j<grid_h;j++) {
448
                        l = get_avg_light(i,j);
449
 
450
                        if (l > max_l)
451
                                max_l = l;
452
 
453
                        if (l < min_l)
454
                                min_l = l;
455
                }
456
 
457
        for (i=1;i<grid_w;i++)
458
                for (j=1;j<grid_h;j++) {
459
 
460
                        l = get_avg_light(i,j);
461
 
462
                        if (min_l == max_l) {
463
                                LIGHT(i,j) = l>>8;
464
                                continue;
465
                        }
466
 
467
                        l2 = fixdiv((l-min_l),(max_l-min_l));
468
 
469
                        if (l2==f1_0)
470
                                l2--;
471
 
472
                        LIGHT(i,j) = l2>>8;
473
 
474
                }
475
}