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 | } |