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-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. |
||
18 | */ |
||
19 | |||
20 | /* |
||
21 | * |
||
22 | * Start of conversion to new texture mapper. |
||
23 | * |
||
24 | */ |
||
25 | |||
26 | #include "pstypes.h" |
||
27 | #include "maths.h" |
||
28 | #include "vecmat.h" |
||
29 | #include "gr.h" |
||
30 | #include "3d.h" |
||
31 | #include "dxxerror.h" |
||
32 | #include "render.h" |
||
33 | #include "texmap.h" |
||
34 | #include "texmapl.h" |
||
35 | #include "rle.h" |
||
36 | #include "scanline.h" |
||
37 | #include "u_mem.h" |
||
38 | |||
39 | #include "dxxsconf.h" |
||
40 | #include "dsx-ns.h" |
||
41 | #include <utility> |
||
42 | |||
43 | namespace dcx { |
||
44 | |||
45 | #if DXX_USE_EDITOR |
||
46 | #define EDITOR_TMAP 1 //if in, include extra stuff |
||
47 | #endif |
||
48 | |||
49 | // Temporary texture map, interface from Matt's 3d system to Mike's texture mapper. |
||
50 | |||
51 | int Lighting_on=1; // initialize to no lighting |
||
52 | unsigned Current_seg_depth; // HACK INTERFACE: how far away the current segment (& thus texture) is |
||
53 | |||
54 | // These variables are the interface to assembler. They get set for each texture map, which is a real waste of time. |
||
55 | // They should be set only when they change, which is generally when the window bounds change. And, even still, it's |
||
56 | // a pretty bad interface. |
||
57 | int bytes_per_row=-1; |
||
58 | unsigned char *write_buffer; |
||
59 | |||
60 | fix fx_l, fx_u, fx_v, fx_z, fx_du_dx, fx_dv_dx, fx_dz_dx, fx_dl_dx; |
||
61 | int fx_xleft, fx_xright, fx_y; |
||
62 | const unsigned char *pixptr; |
||
63 | uint8_t Transparency_on = 0; |
||
64 | uint8_t tmap_flat_color; |
||
65 | |||
66 | int Interpolation_method; // 0 = choose best method |
||
67 | // ------------------------------------------------------------------------------------- |
||
68 | template <std::size_t... N> |
||
69 | static inline constexpr const std::array<fix, 1 + sizeof...(N)> init_fix_recip_table(std::index_sequence<0, N...>) |
||
70 | { |
||
71 | /* gcc 4.5 fails on bare initializer list */ |
||
72 | return std::array<fix, 1 + sizeof...(N)>{{F1_0, (F1_0 / N)...}}; |
||
73 | } |
||
74 | |||
75 | constexpr std::array<fix, FIX_RECIP_TABLE_SIZE> fix_recip_table = init_fix_recip_table(std::make_index_sequence<FIX_RECIP_TABLE_SIZE>()); |
||
76 | |||
77 | // ------------------------------------------------------------------------------------- |
||
78 | // Initialize interface variables to assembler. |
||
79 | // These things used to be constants. This routine is now (10/6/93) getting called for |
||
80 | // every texture map. It should get called whenever the window changes, or, preferably, |
||
81 | // not at all. I'm pretty sure these variables are only being used for range checking. |
||
82 | void init_interface_vars_to_assembler(void) |
||
83 | { |
||
84 | grs_bitmap *bp; |
||
85 | bp = &grd_curcanv->cv_bitmap; |
||
86 | |||
87 | Assert(bp!=NULL); |
||
88 | Assert(bp->bm_data!=NULL); |
||
89 | // If bytes_per_row has changed, create new table of pointers. |
||
90 | if (bytes_per_row != static_cast<int>(bp->bm_rowsize)) { |
||
91 | bytes_per_row = static_cast<int>(bp->bm_rowsize); |
||
92 | } |
||
93 | |||
94 | write_buffer = bp->bm_mdata; |
||
95 | |||
96 | Window_clip_left = 0; |
||
97 | Window_clip_right = static_cast<int>(bp->bm_w)-1; |
||
98 | Window_clip_top = 0; |
||
99 | Window_clip_bot = static_cast<int>(bp->bm_h)-1; |
||
100 | } |
||
101 | |||
102 | static int Lighting_enabled; |
||
103 | // ------------------------------------------------------------------------------------- |
||
104 | // VARIABLES |
||
105 | |||
106 | // ------------------------------------------------------------------------------------- |
||
107 | // Returns number preceding val modulo modulus. |
||
108 | // prevmod(3,4) = 2 |
||
109 | // prevmod(0,4) = 3 |
||
110 | int prevmod(int val,int modulus) |
||
111 | { |
||
112 | if (val > 0) |
||
113 | return val-1; |
||
114 | else |
||
115 | return modulus-1; |
||
116 | // return (val + modulus - 1) % modulus; |
||
117 | } |
||
118 | |||
119 | |||
120 | // Returns number succeeding val modulo modulus. |
||
121 | // succmod(3,4) = 0 |
||
122 | // succmod(0,4) = 1 |
||
123 | int succmod(int val,int modulus) |
||
124 | { |
||
125 | if (val < modulus-1) |
||
126 | return val+1; |
||
127 | else |
||
128 | return 0; |
||
129 | |||
130 | // return (val + 1) % modulus; |
||
131 | } |
||
132 | |||
133 | // ------------------------------------------------------------------------------------- |
||
134 | // Select topmost vertex (minimum y coordinate) and bottommost (maximum y coordinate) in |
||
135 | // texture map. If either is part of a horizontal edge, then select leftmost vertex for |
||
136 | // top, rightmost vertex for bottom. |
||
137 | // Important: Vertex is selected with integer precision. So, if there are vertices at |
||
138 | // (0.0,0.7) and (0.5,0.3), the first vertex is selected, because they y coordinates are |
||
139 | // considered the same, so the smaller x is favored. |
||
140 | // Parameters: |
||
141 | // nv number of vertices |
||
142 | // v3d pointer to 3d vertices containing u,v,x2d,y2d coordinates |
||
143 | // Results in: |
||
144 | // *min_y_ind |
||
145 | // *max_y_ind |
||
146 | // ------------------------------------------------------------------------------------- |
||
147 | void compute_y_bounds(const g3ds_tmap &t, int &vlt, int &vlb, int &vrt, int &vrb,int &bottom_y_ind) |
||
148 | { |
||
149 | int min_y,max_y; |
||
150 | int min_y_ind; |
||
151 | int original_vrt; |
||
152 | fix min_x; |
||
153 | |||
154 | // Scan all vertices, set min_y_ind to vertex with smallest y coordinate. |
||
155 | min_y = f2i(t.verts[0].y2d); |
||
156 | max_y = min_y; |
||
157 | min_y_ind = 0; |
||
158 | min_x = f2i(t.verts[0].x2d); |
||
159 | bottom_y_ind = 0; |
||
160 | |||
161 | for (int i=1; i<t.nv; i++) { |
||
162 | if (f2i(t.verts[i].y2d) < min_y) { |
||
163 | min_y = f2i(t.verts[i].y2d); |
||
164 | min_y_ind = i; |
||
165 | min_x = f2i(t.verts[i].x2d); |
||
166 | } else if (f2i(t.verts[i].y2d) == min_y) { |
||
167 | if (f2i(t.verts[i].x2d) < min_x) { |
||
168 | min_y_ind = i; |
||
169 | min_x = f2i(t.verts[i].x2d); |
||
170 | } |
||
171 | } |
||
172 | if (f2i(t.verts[i].y2d) > max_y) { |
||
173 | max_y = f2i(t.verts[i].y2d); |
||
174 | bottom_y_ind = i; |
||
175 | } |
||
176 | } |
||
177 | |||
178 | //--removed mk, 11/27/94-- // Check for a non-upright-hourglass polygon and fix, if necessary, by bashing a y coordinate. |
||
179 | //--removed mk, 11/27/94-- // min_y_ind = index of minimum y coordinate, *bottom_y_ind = index of maximum y coordinate |
||
180 | //--removed mk, 11/27/94--{ |
||
181 | //--removed mk, 11/27/94-- int max_temp, min_temp; |
||
182 | //--removed mk, 11/27/94-- |
||
183 | //--removed mk, 11/27/94-- max_temp = *bottom_y_ind; |
||
184 | //--removed mk, 11/27/94-- if (*bottom_y_ind < min_y_ind) |
||
185 | //--removed mk, 11/27/94-- max_temp += t->nv; |
||
186 | //--removed mk, 11/27/94-- |
||
187 | //--removed mk, 11/27/94-- for (i=min_y_ind; i<max_temp; i++) { |
||
188 | //--removed mk, 11/27/94-- if (f2i(t->verts[i%t->nv].y2d) > f2i(t->verts[(i+1)%t->nv].y2d)) { |
||
189 | //--removed mk, 11/27/94-- Int3(); |
||
190 | //--removed mk, 11/27/94-- t->verts[(i+1)%t->nv].y2d = t->verts[i%t->nv].y2d; |
||
191 | //--removed mk, 11/27/94-- } |
||
192 | //--removed mk, 11/27/94-- } |
||
193 | //--removed mk, 11/27/94-- |
||
194 | //--removed mk, 11/27/94-- min_temp = min_y_ind; |
||
195 | //--removed mk, 11/27/94-- if (min_y_ind < *bottom_y_ind) |
||
196 | //--removed mk, 11/27/94-- min_temp += t->nv; |
||
197 | //--removed mk, 11/27/94-- |
||
198 | //--removed mk, 11/27/94-- for (i=*bottom_y_ind; i<min_temp; i++) { |
||
199 | //--removed mk, 11/27/94-- if (f2i(t->verts[i%t->nv].y2d) < f2i(t->verts[(i+1)%t->nv].y2d)) { |
||
200 | //--removed mk, 11/27/94-- Int3(); |
||
201 | //--removed mk, 11/27/94-- t->verts[(i+1)%t->nv].y2d = t->verts[i%t->nv].y2d; |
||
202 | //--removed mk, 11/27/94-- } |
||
203 | //--removed mk, 11/27/94-- } |
||
204 | //--removed mk, 11/27/94--} |
||
205 | |||
206 | // Set "vertex left top", etc. based on vertex with topmost y coordinate |
||
207 | vlb = prevmod(vlt = min_y_ind,t.nv); |
||
208 | vrb = succmod(vrt = vlt,t.nv); |
||
209 | |||
210 | // If right edge is horizontal, then advance along polygon bound until it no longer is or until all |
||
211 | // vertices have been examined. |
||
212 | // (Left edge cannot be horizontal, because *vlt is set to leftmost point with highest y coordinate.) |
||
213 | |||
214 | original_vrt = vrt; |
||
215 | |||
216 | while (f2i(t.verts[vrt].y2d) == f2i(t.verts[vrb].y2d)) { |
||
217 | if (succmod(vrt,t.nv) == original_vrt) { |
||
218 | break; |
||
219 | } |
||
220 | vrt = succmod(vrt,t.nv); |
||
221 | vrb = succmod(vrt,t.nv); |
||
222 | } |
||
223 | } |
||
224 | |||
225 | // ------------------------------------------------------------------------------------- |
||
226 | // Returns dx/dy given two vertices. |
||
227 | // If dy == 0, returns 0.0 |
||
228 | // ------------------------------------------------------------------------------------- |
||
229 | //--fix compute_dx_dy_lin(g3ds_tmap *t, int top_vertex,int bottom_vertex) |
||
230 | //--{ |
||
231 | //-- int dy; |
||
232 | //-- |
||
233 | //-- // compute delta x with respect to y for any edge |
||
234 | //-- dy = f2i(t->verts[bottom_vertex].y2d - t->verts[top_vertex].y2d) + 1; |
||
235 | //-- if (dy) |
||
236 | //-- return (t->verts[bottom_vertex].x2d - t->verts[top_vertex].x2d) / dy; |
||
237 | //-- else |
||
238 | //-- return 0; |
||
239 | //-- |
||
240 | //--} |
||
241 | |||
242 | //#if !DXX_USE_OGL |
||
243 | static fix compute_du_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy) |
||
244 | { |
||
245 | return fixmul(t.verts[bottom_vertex].u - t.verts[top_vertex].u, recip_dy); |
||
246 | } |
||
247 | |||
248 | |||
249 | static fix compute_dv_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy) |
||
250 | { |
||
251 | return fixmul(t.verts[bottom_vertex].v - t.verts[top_vertex].v, recip_dy); |
||
252 | } |
||
253 | |||
254 | static fix compute_dl_dy_lin(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy) |
||
255 | { |
||
256 | return fixmul(t.verts[bottom_vertex].l - t.verts[top_vertex].l, recip_dy); |
||
257 | } |
||
258 | |||
259 | fix compute_dx_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy) |
||
260 | { |
||
261 | return fixmul(t.verts[bottom_vertex].x2d - t.verts[top_vertex].x2d, recip_dy); |
||
262 | } |
||
263 | |||
264 | static fix compute_du_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy) |
||
265 | { |
||
266 | return fixmul(fixmul(t.verts[bottom_vertex].u,t.verts[bottom_vertex].z) - fixmul(t.verts[top_vertex].u,t.verts[top_vertex].z), recip_dy); |
||
267 | } |
||
268 | |||
269 | static fix compute_dv_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy) |
||
270 | { |
||
271 | return fixmul(fixmul(t.verts[bottom_vertex].v,t.verts[bottom_vertex].z) - fixmul(t.verts[top_vertex].v,t.verts[top_vertex].z), recip_dy); |
||
272 | } |
||
273 | |||
274 | static fix compute_dz_dy(const g3ds_tmap &t, int top_vertex,int bottom_vertex, fix recip_dy) |
||
275 | { |
||
276 | return fixmul(t.verts[bottom_vertex].z - t.verts[top_vertex].z, recip_dy); |
||
277 | |||
278 | } |
||
279 | |||
280 | // ------------------------------------------------------------------------------------- |
||
281 | // Texture map current scanline in perspective. |
||
282 | // ------------------------------------------------------------------------------------- |
||
283 | static void ntmap_scanline_lighted(const grs_bitmap &srcb, int y, fix xleft, fix xright, fix uleft, fix uright, fix vleft, fix vright, fix zleft, fix zright, fix lleft, fix lright) |
||
284 | { |
||
285 | fix dx,recip_dx; |
||
286 | |||
287 | fx_xright = f2i(xright); |
||
288 | //edited 06/27/99 Matt Mueller - moved these tests up from within the switch so as not to do a bunch of needless calculations when we are just gonna return anyway. Slight fps boost? |
||
289 | if (fx_xright < Window_clip_left) |
||
290 | return; |
||
291 | fx_xleft = f2i(xleft); |
||
292 | if (fx_xleft > Window_clip_right) |
||
293 | return; |
||
294 | //end edit -MM |
||
295 | |||
296 | dx = fx_xright - fx_xleft; |
||
297 | if ((dx < 0) || (xright < 0) || (xleft > xright)) // the (xleft > xright) term is not redundant with (dx < 0) because dx is computed using integers |
||
298 | return; |
||
299 | |||
300 | // setup to call assembler scanline renderer |
||
301 | recip_dx = fix_recip(dx); |
||
302 | |||
303 | fx_u = uleft; |
||
304 | fx_v = vleft; |
||
305 | fx_z = zleft; |
||
306 | |||
307 | fx_du_dx = fixmul(uright - uleft,recip_dx); |
||
308 | fx_dv_dx = fixmul(vright - vleft,recip_dx); |
||
309 | fx_dz_dx = fixmul(zright - zleft,recip_dx); |
||
310 | fx_y = y; |
||
311 | pixptr = srcb.bm_data; |
||
312 | |||
313 | switch (Lighting_enabled) { |
||
314 | case 0: |
||
315 | //added 05/17/99 Matt Mueller - prevent writing before the buffer |
||
316 | if ((fx_y == 0) && (fx_xleft < 0)) |
||
317 | fx_xleft = 0; |
||
318 | //end addition -MM |
||
319 | if (fx_xright > Window_clip_right) |
||
320 | fx_xright = Window_clip_right; |
||
321 | |||
322 | cur_tmap_scanline_per(); |
||
323 | break; |
||
324 | case 1: { |
||
325 | fix mul_thing; |
||
326 | |||
327 | if (lleft < 0) lleft = 0; |
||
328 | if (lright < 0) lright = 0; |
||
329 | if (lleft > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) lleft = (NUM_LIGHTING_LEVELS*F1_0-F1_0/2); |
||
330 | if (lright > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) lright = (NUM_LIGHTING_LEVELS*F1_0-F1_0/2); |
||
331 | |||
332 | fx_l = lleft; |
||
333 | fx_dl_dx = fixmul(lright - lleft,recip_dx); |
||
334 | |||
335 | // This is a pretty ugly hack to prevent lighting overflows. |
||
336 | mul_thing = dx * fx_dl_dx; |
||
337 | if (lleft + mul_thing < 0) |
||
338 | fx_dl_dx += 12; |
||
339 | else if (lleft + mul_thing > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) |
||
340 | fx_dl_dx -= 12; |
||
341 | |||
342 | //added 05/17/99 Matt Mueller - prevent writing before the buffer |
||
343 | if ((fx_y == 0) && (fx_xleft < 0)) |
||
344 | fx_xleft = 0; |
||
345 | //end addition -MM |
||
346 | if (fx_xright > Window_clip_right) |
||
347 | fx_xright = Window_clip_right; |
||
348 | |||
349 | cur_tmap_scanline_per(); |
||
350 | break; |
||
351 | } |
||
352 | case 2: |
||
353 | #ifdef EDITOR_TMAP |
||
354 | fx_xright = f2i(xright); |
||
355 | fx_xleft = f2i(xleft); |
||
356 | |||
357 | tmap_flat_color = 1; |
||
358 | cur_tmap_scanline_flat(); |
||
359 | #else |
||
360 | Int3(); // Illegal, called an editor only routine! |
||
361 | #endif |
||
362 | break; |
||
363 | } |
||
364 | |||
365 | } |
||
366 | |||
367 | // ------------------------------------------------------------------------------------- |
||
368 | // Render a texture map with lighting using perspective interpolation in inner and outer loops. |
||
369 | // ------------------------------------------------------------------------------------- |
||
370 | static void ntexture_map_lighted(const grs_bitmap &srcb, const g3ds_tmap &t) |
||
371 | { |
||
372 | int vlt,vrt,vlb,vrb; // vertex left top, vertex right top, vertex left bottom, vertex right bottom |
||
373 | int topy,boty,dy; |
||
374 | fix dx_dy_left,dx_dy_right; |
||
375 | fix du_dy_left,du_dy_right; |
||
376 | fix dv_dy_left,dv_dy_right; |
||
377 | fix dz_dy_left,dz_dy_right; |
||
378 | fix dl_dy_left,dl_dy_right; |
||
379 | fix recip_dyl, recip_dyr; |
||
380 | int max_y_vertex; |
||
381 | fix xleft,xright,uleft,vleft,uright,vright,zleft,zright,lleft,lright; |
||
382 | int next_break_left, next_break_right; |
||
383 | |||
384 | //remove stupid warnings in compile |
||
385 | dl_dy_left = F1_0; |
||
386 | dl_dy_right = F1_0; |
||
387 | lleft = F1_0; |
||
388 | lright = F1_0; |
||
389 | |||
390 | auto &v3d = t.verts; |
||
391 | |||
392 | // Determine top and bottom y coords. |
||
393 | compute_y_bounds(t,vlt,vlb,vrt,vrb,max_y_vertex); |
||
394 | |||
395 | // Set top and bottom (of entire texture map) y coordinates. |
||
396 | topy = f2i(v3d[vlt].y2d); |
||
397 | boty = f2i(v3d[max_y_vertex].y2d); |
||
398 | if (topy > Window_clip_bot) |
||
399 | return; |
||
400 | if (boty > Window_clip_bot) |
||
401 | boty = Window_clip_bot; |
||
402 | |||
403 | // Set amount to change x coordinate for each advance to next scanline. |
||
404 | dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d); |
||
405 | recip_dyl = fix_recip(dy); |
||
406 | |||
407 | dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dyl); |
||
408 | du_dy_left = compute_du_dy(t,vlt,vlb, recip_dyl); |
||
409 | dv_dy_left = compute_dv_dy(t,vlt,vlb, recip_dyl); |
||
410 | dz_dy_left = compute_dz_dy(t,vlt,vlb, recip_dyl); |
||
411 | |||
412 | dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d); |
||
413 | recip_dyr = fix_recip(dy); |
||
414 | |||
415 | du_dy_right = compute_du_dy(t,vrt,vrb, recip_dyr); |
||
416 | dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dyr); |
||
417 | dv_dy_right = compute_dv_dy(t,vrt,vrb, recip_dyr); |
||
418 | dz_dy_right = compute_dz_dy(t,vrt,vrb, recip_dyr); |
||
419 | |||
420 | if (Lighting_enabled) { |
||
421 | dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dyl); |
||
422 | dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dyr); |
||
423 | |||
424 | lleft = v3d[vlt].l; |
||
425 | lright = v3d[vrt].l; |
||
426 | } |
||
427 | |||
428 | // Set initial values for x, u, v |
||
429 | xleft = v3d[vlt].x2d; |
||
430 | xright = v3d[vrt].x2d; |
||
431 | |||
432 | zleft = v3d[vlt].z; |
||
433 | zright = v3d[vrt].z; |
||
434 | |||
435 | uleft = fixmul(v3d[vlt].u,zleft); |
||
436 | uright = fixmul(v3d[vrt].u,zright); |
||
437 | vleft = fixmul(v3d[vlt].v,zleft); |
||
438 | vright = fixmul(v3d[vrt].v,zright); |
||
439 | |||
440 | // scan all rows in texture map from top through first break. |
||
441 | next_break_left = f2i(v3d[vlb].y2d); |
||
442 | next_break_right = f2i(v3d[vrb].y2d); |
||
443 | |||
444 | for (int y = topy; y < boty; y++) { |
||
445 | |||
446 | // See if we have reached the end of the current left edge, and if so, set |
||
447 | // new values for dx_dy and x,u,v |
||
448 | if (y == next_break_left) { |
||
449 | fix recip_dy; |
||
450 | |||
451 | // Handle problem of double points. Search until y coord is different. Cannot get |
||
452 | // hung in an infinite loop because we know there is a vertex with a lower y coordinate |
||
453 | // because in the for loop, we don't scan all spanlines. |
||
454 | while (y == f2i(v3d[vlb].y2d)) { |
||
455 | vlt = vlb; |
||
456 | vlb = prevmod(vlb,t.nv); |
||
457 | } |
||
458 | next_break_left = f2i(v3d[vlb].y2d); |
||
459 | |||
460 | dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d); |
||
461 | recip_dy = fix_recip(dy); |
||
462 | |||
463 | dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dy); |
||
464 | |||
465 | xleft = v3d[vlt].x2d; |
||
466 | zleft = v3d[vlt].z; |
||
467 | uleft = fixmul(v3d[vlt].u,zleft); |
||
468 | vleft = fixmul(v3d[vlt].v,zleft); |
||
469 | lleft = v3d[vlt].l; |
||
470 | |||
471 | du_dy_left = compute_du_dy(t,vlt,vlb, recip_dy); |
||
472 | dv_dy_left = compute_dv_dy(t,vlt,vlb, recip_dy); |
||
473 | dz_dy_left = compute_dz_dy(t,vlt,vlb, recip_dy); |
||
474 | |||
475 | if (Lighting_enabled) { |
||
476 | dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dy); |
||
477 | lleft = v3d[vlt].l; |
||
478 | } |
||
479 | } |
||
480 | |||
481 | // See if we have reached the end of the current left edge, and if so, set |
||
482 | // new values for dx_dy and x. Not necessary to set new values for u,v. |
||
483 | if (y == next_break_right) { |
||
484 | fix recip_dy; |
||
485 | |||
486 | while (y == f2i(v3d[vrb].y2d)) { |
||
487 | vrt = vrb; |
||
488 | vrb = succmod(vrb,t.nv); |
||
489 | } |
||
490 | |||
491 | next_break_right = f2i(v3d[vrb].y2d); |
||
492 | |||
493 | dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d); |
||
494 | recip_dy = fix_recip(dy); |
||
495 | |||
496 | dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dy); |
||
497 | |||
498 | xright = v3d[vrt].x2d; |
||
499 | zright = v3d[vrt].z; |
||
500 | uright = fixmul(v3d[vrt].u,zright); |
||
501 | vright = fixmul(v3d[vrt].v,zright); |
||
502 | |||
503 | du_dy_right = compute_du_dy(t,vrt,vrb, recip_dy); |
||
504 | dv_dy_right = compute_dv_dy(t,vrt,vrb, recip_dy); |
||
505 | dz_dy_right = compute_dz_dy(t,vrt,vrb, recip_dy); |
||
506 | |||
507 | if (Lighting_enabled) { |
||
508 | dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dy); |
||
509 | lright = v3d[vrt].l; |
||
510 | } |
||
511 | } |
||
512 | |||
513 | if (Lighting_enabled) { |
||
514 | if (y >= Window_clip_top) |
||
515 | ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright); |
||
516 | lleft += dl_dy_left; |
||
517 | lright += dl_dy_right; |
||
518 | } else |
||
519 | if (y >= Window_clip_top) |
||
520 | ntmap_scanline_lighted(srcb,y,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright); |
||
521 | |||
522 | uleft += du_dy_left; |
||
523 | vleft += dv_dy_left; |
||
524 | |||
525 | uright += du_dy_right; |
||
526 | vright += dv_dy_right; |
||
527 | |||
528 | xleft += dx_dy_left; |
||
529 | xright += dx_dy_right; |
||
530 | |||
531 | zleft += dz_dy_left; |
||
532 | zright += dz_dy_right; |
||
533 | |||
534 | } |
||
535 | |||
536 | // We can get lleft or lright out of bounds here because we compute dl_dy using fixed point values, |
||
537 | // but we plot an integer number of scanlines, therefore doing an integer number of additions of the delta. |
||
538 | |||
539 | ntmap_scanline_lighted(srcb,boty,xleft,xright,uleft,uright,vleft,vright,zleft,zright,lleft,lright); |
||
540 | } |
||
541 | |||
542 | |||
543 | // ------------------------------------------------------------------------------------- |
||
544 | // Texture map current scanline using linear interpolation. |
||
545 | // ------------------------------------------------------------------------------------- |
||
546 | static void ntmap_scanline_lighted_linear(const grs_bitmap &srcb, int y, fix xleft, fix xright, fix uleft, fix uright, fix vleft, fix vright, fix lleft, fix lright) |
||
547 | { |
||
548 | fix dx,recip_dx,du_dx,dv_dx,dl_dx; |
||
549 | |||
550 | dx = f2i(xright) - f2i(xleft); |
||
551 | if ((dx < 0) || (xright < 0) || (xleft > xright)) // the (xleft > xright) term is not redundant with (dx < 0) because dx is computed using integers |
||
552 | return; |
||
553 | |||
554 | // setup to call assembler scanline renderer |
||
555 | recip_dx = fix_recip(dx); |
||
556 | |||
557 | du_dx = fixmul(uright - uleft,recip_dx); |
||
558 | dv_dx = fixmul(vright - vleft,recip_dx); |
||
559 | |||
560 | fx_u = uleft; |
||
561 | fx_v = vleft; |
||
562 | fx_du_dx = du_dx; |
||
563 | fx_dv_dx = dv_dx; |
||
564 | fx_y = y; |
||
565 | fx_xright = f2i(xright); |
||
566 | fx_xleft = f2i(xleft); |
||
567 | pixptr = srcb.bm_data; |
||
568 | |||
569 | switch (Lighting_enabled) { |
||
570 | case 0: |
||
571 | //added 07/11/99 adb - prevent writing before the buffer |
||
572 | if (fx_xleft < 0) |
||
573 | fx_xleft = 0; |
||
574 | //end addition -adb |
||
575 | |||
576 | cur_tmap_scanline_lin_nolight(); |
||
577 | break; |
||
578 | case 1: |
||
579 | if (lleft < F1_0/2) |
||
580 | lleft = F1_0/2; |
||
581 | if (lright < F1_0/2) |
||
582 | lright = F1_0/2; |
||
583 | |||
584 | if (lleft > MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS) |
||
585 | lleft = MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS; |
||
586 | if (lright > MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS) |
||
587 | lright = MAX_LIGHTING_VALUE*NUM_LIGHTING_LEVELS; |
||
588 | |||
589 | //added 07/11/99 adb - prevent writing before the buffer |
||
590 | if (fx_xleft < 0) |
||
591 | fx_xleft = 0; |
||
592 | //end addition -adb |
||
593 | |||
594 | { |
||
595 | fix mul_thing; |
||
596 | |||
597 | fx_l = lleft; |
||
598 | fx_dl_dx = fixmul(lright - lleft,recip_dx); |
||
599 | |||
600 | // This is a pretty ugly hack to prevent lighting overflows. |
||
601 | mul_thing = dx * fx_dl_dx; |
||
602 | if (lleft + mul_thing < 0) |
||
603 | fx_dl_dx += 12; |
||
604 | else if (lleft + mul_thing > (NUM_LIGHTING_LEVELS*F1_0-F1_0/2)) |
||
605 | fx_dl_dx -= 12; |
||
606 | } |
||
607 | |||
608 | fx_l = lleft; |
||
609 | dl_dx = fixmul(lright - lleft,recip_dx); |
||
610 | fx_dl_dx = dl_dx; |
||
611 | cur_tmap_scanline_lin(); |
||
612 | break; |
||
613 | case 2: |
||
614 | #ifdef EDITOR_TMAP |
||
615 | fx_xright = f2i(xright); |
||
616 | fx_xleft = f2i(xleft); |
||
617 | tmap_flat_color = 1; |
||
618 | cur_tmap_scanline_flat(); |
||
619 | #else |
||
620 | Int3(); // Illegal, called an editor only routine! |
||
621 | #endif |
||
622 | break; |
||
623 | } |
||
624 | } |
||
625 | |||
626 | // ------------------------------------------------------------------------------------- |
||
627 | // Render a texture map with lighting using perspective interpolation in inner and outer loops. |
||
628 | // ------------------------------------------------------------------------------------- |
||
629 | static void ntexture_map_lighted_linear(const grs_bitmap &srcb, const g3ds_tmap &t) |
||
630 | { |
||
631 | int vlt,vrt,vlb,vrb; // vertex left top, vertex right top, vertex left bottom, vertex right bottom |
||
632 | int topy,boty,dy; |
||
633 | fix dx_dy_left,dx_dy_right; |
||
634 | fix du_dy_left,du_dy_right; |
||
635 | fix dv_dy_left,dv_dy_right; |
||
636 | fix dl_dy_left,dl_dy_right; |
||
637 | int max_y_vertex; |
||
638 | fix xleft,xright,uleft,vleft,uright,vright,lleft,lright; |
||
639 | int next_break_left, next_break_right; |
||
640 | fix recip_dyl, recip_dyr; |
||
641 | |||
642 | //remove stupid warnings in compile |
||
643 | dl_dy_left = F1_0; |
||
644 | dl_dy_right = F1_0; |
||
645 | lleft = F1_0; |
||
646 | lright = F1_0; |
||
647 | |||
648 | auto &v3d = t.verts; |
||
649 | |||
650 | // Determine top and bottom y coords. |
||
651 | compute_y_bounds(t,vlt,vlb,vrt,vrb,max_y_vertex); |
||
652 | |||
653 | // Set top and bottom (of entire texture map) y coordinates. |
||
654 | topy = f2i(v3d[vlt].y2d); |
||
655 | boty = f2i(v3d[max_y_vertex].y2d); |
||
656 | |||
657 | if (topy > Window_clip_bot) |
||
658 | return; |
||
659 | if (boty > Window_clip_bot) |
||
660 | boty = Window_clip_bot; |
||
661 | |||
662 | dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d); |
||
663 | recip_dyl = fix_recip(dy); |
||
664 | |||
665 | dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d); |
||
666 | recip_dyr = fix_recip(dy); |
||
667 | |||
668 | // Set amount to change x coordinate for each advance to next scanline. |
||
669 | dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dyl); |
||
670 | dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dyr); |
||
671 | |||
672 | du_dy_left = compute_du_dy_lin(t,vlt,vlb, recip_dyl); |
||
673 | du_dy_right = compute_du_dy_lin(t,vrt,vrb, recip_dyr); |
||
674 | |||
675 | dv_dy_left = compute_dv_dy_lin(t,vlt,vlb, recip_dyl); |
||
676 | dv_dy_right = compute_dv_dy_lin(t,vrt,vrb, recip_dyr); |
||
677 | |||
678 | if (Lighting_enabled) { |
||
679 | dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dyl); |
||
680 | dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dyr); |
||
681 | |||
682 | lleft = v3d[vlt].l; |
||
683 | lright = v3d[vrt].l; |
||
684 | } |
||
685 | |||
686 | // Set initial values for x, u, v |
||
687 | xleft = v3d[vlt].x2d; |
||
688 | xright = v3d[vrt].x2d; |
||
689 | |||
690 | uleft = v3d[vlt].u; |
||
691 | uright = v3d[vrt].u; |
||
692 | vleft = v3d[vlt].v; |
||
693 | vright = v3d[vrt].v; |
||
694 | |||
695 | // scan all rows in texture map from top through first break. |
||
696 | next_break_left = f2i(v3d[vlb].y2d); |
||
697 | next_break_right = f2i(v3d[vrb].y2d); |
||
698 | |||
699 | for (int y = topy; y < boty; y++) { |
||
700 | |||
701 | // See if we have reached the end of the current left edge, and if so, set |
||
702 | // new values for dx_dy and x,u,v |
||
703 | if (y == next_break_left) { |
||
704 | fix recip_dy; |
||
705 | |||
706 | // Handle problem of double points. Search until y coord is different. Cannot get |
||
707 | // hung in an infinite loop because we know there is a vertex with a lower y coordinate |
||
708 | // because in the for loop, we don't scan all spanlines. |
||
709 | while (y == f2i(v3d[vlb].y2d)) { |
||
710 | vlt = vlb; |
||
711 | vlb = prevmod(vlb,t.nv); |
||
712 | } |
||
713 | next_break_left = f2i(v3d[vlb].y2d); |
||
714 | |||
715 | dy = f2i(t.verts[vlb].y2d) - f2i(t.verts[vlt].y2d); |
||
716 | recip_dy = fix_recip(dy); |
||
717 | |||
718 | dx_dy_left = compute_dx_dy(t,vlt,vlb, recip_dy); |
||
719 | |||
720 | xleft = v3d[vlt].x2d; |
||
721 | uleft = v3d[vlt].u; |
||
722 | vleft = v3d[vlt].v; |
||
723 | lleft = v3d[vlt].l; |
||
724 | |||
725 | du_dy_left = compute_du_dy_lin(t,vlt,vlb, recip_dy); |
||
726 | dv_dy_left = compute_dv_dy_lin(t,vlt,vlb, recip_dy); |
||
727 | |||
728 | if (Lighting_enabled) { |
||
729 | dl_dy_left = compute_dl_dy_lin(t,vlt,vlb, recip_dy); |
||
730 | lleft = v3d[vlt].l; |
||
731 | } |
||
732 | } |
||
733 | |||
734 | // See if we have reached the end of the current left edge, and if so, set |
||
735 | // new values for dx_dy and x. Not necessary to set new values for u,v. |
||
736 | if (y == next_break_right) { |
||
737 | fix recip_dy; |
||
738 | |||
739 | while (y == f2i(v3d[vrb].y2d)) { |
||
740 | vrt = vrb; |
||
741 | vrb = succmod(vrb,t.nv); |
||
742 | } |
||
743 | |||
744 | dy = f2i(t.verts[vrb].y2d) - f2i(t.verts[vrt].y2d); |
||
745 | recip_dy = fix_recip(dy); |
||
746 | |||
747 | next_break_right = f2i(v3d[vrb].y2d); |
||
748 | dx_dy_right = compute_dx_dy(t,vrt,vrb, recip_dy); |
||
749 | |||
750 | xright = v3d[vrt].x2d; |
||
751 | uright = v3d[vrt].u; |
||
752 | vright = v3d[vrt].v; |
||
753 | |||
754 | du_dy_right = compute_du_dy_lin(t,vrt,vrb, recip_dy); |
||
755 | dv_dy_right = compute_dv_dy_lin(t,vrt,vrb, recip_dy); |
||
756 | |||
757 | if (Lighting_enabled) { |
||
758 | dl_dy_right = compute_dl_dy_lin(t,vrt,vrb, recip_dy); |
||
759 | lright = v3d[vrt].l; |
||
760 | } |
||
761 | } |
||
762 | |||
763 | if (Lighting_enabled) { |
||
764 | ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright); |
||
765 | lleft += dl_dy_left; |
||
766 | lright += dl_dy_right; |
||
767 | } else |
||
768 | ntmap_scanline_lighted_linear(srcb,y,xleft,xright,uleft,uright,vleft,vright,lleft,lright); |
||
769 | |||
770 | uleft += du_dy_left; |
||
771 | vleft += dv_dy_left; |
||
772 | |||
773 | uright += du_dy_right; |
||
774 | vright += dv_dy_right; |
||
775 | |||
776 | xleft += dx_dy_left; |
||
777 | xright += dx_dy_right; |
||
778 | |||
779 | } |
||
780 | |||
781 | // We can get lleft or lright out of bounds here because we compute dl_dy using fixed point values, |
||
782 | // but we plot an integer number of scanlines, therefore doing an integer number of additions of the delta. |
||
783 | |||
784 | ntmap_scanline_lighted_linear(srcb,boty,xleft,xright,uleft,uright,vleft,vright,lleft,lright); |
||
785 | } |
||
786 | |||
787 | // fix DivNum = F1_0*12; |
||
788 | |||
789 | // ------------------------------------------------------------------------------------- |
||
790 | // Interface from Matt's data structures to Mike's texture mapper. |
||
791 | // ------------------------------------------------------------------------------------- |
||
792 | void draw_tmap(grs_canvas &canvas, const grs_bitmap &rbp, uint_fast32_t nverts, const g3s_point *const *vertbuf) |
||
793 | { |
||
794 | // These variables are used in system which renders texture maps which lie on one scanline as a line. |
||
795 | // fix div_numerator; |
||
796 | int lighting_on_save = Lighting_on; |
||
797 | |||
798 | Assert(nverts <= MAX_TMAP_VERTS); |
||
799 | |||
800 | const grs_bitmap *bp = &rbp; |
||
801 | // If no transparency and seg depth is large, render as flat shaded. |
||
802 | if ((Current_seg_depth > Max_linear_depth) && ((bp->get_flag_mask(3)) == 0)) { |
||
803 | draw_tmap_flat(canvas, rbp, nverts, vertbuf); |
||
804 | return; |
||
805 | } |
||
806 | |||
807 | bp = rle_expand_texture(*bp); // Expand if rle'd |
||
808 | |||
809 | Transparency_on = bp->get_flag_mask(BM_FLAG_TRANSPARENT); |
||
810 | if (bp->get_flag_mask(BM_FLAG_NO_LIGHTING)) |
||
811 | Lighting_on = 0; |
||
812 | |||
813 | |||
814 | // Setup texture map in Tmap1 |
||
815 | g3ds_tmap Tmap1; |
||
816 | Tmap1.nv = nverts; // Initialize number of vertices |
||
817 | |||
818 | // div_numerator = DivNum; //f1_0*3; |
||
819 | |||
820 | for (int i=0; i<nverts; i++) { |
||
821 | g3ds_vertex *tvp = &Tmap1.verts[i]; |
||
822 | auto vp = vertbuf[i]; |
||
823 | |||
824 | tvp->x2d = vp->p3_sx; |
||
825 | tvp->y2d = vp->p3_sy; |
||
826 | |||
827 | // Check for overflow on fixdiv. Will overflow on vp->z <= something small. Allow only as low as 256. |
||
828 | auto clipped_p3_z = std::max(256, vp->p3_z); |
||
829 | tvp->z = fixdiv(F1_0*12, clipped_p3_z); |
||
830 | tvp->u = vp->p3_u << 6; //* bp->bm_w; |
||
831 | tvp->v = vp->p3_v << 6; //* bp->bm_h; |
||
832 | |||
833 | Assert(Lighting_on < 3); |
||
834 | |||
835 | if (Lighting_on) |
||
836 | tvp->l = vp->p3_l * NUM_LIGHTING_LEVELS; |
||
837 | } |
||
838 | |||
839 | |||
840 | Lighting_enabled = Lighting_on; |
||
841 | |||
842 | // Now, call my texture mapper. |
||
843 | switch (Interpolation_method) { // 0 = choose, 1 = linear, 2 = /8 perspective, 3 = full perspective |
||
844 | case 0: // choose best interpolation |
||
845 | if (Current_seg_depth > Max_perspective_depth) |
||
846 | { |
||
847 | case 1: // linear interpolation |
||
848 | ntexture_map_lighted_linear(*bp, Tmap1); |
||
849 | } |
||
850 | else |
||
851 | { |
||
852 | DXX_BOOST_FALLTHROUGH; |
||
853 | case 2: // perspective every 8th pixel interpolation |
||
854 | case 3: // perspective every pixel interpolation |
||
855 | ntexture_map_lighted(*bp, Tmap1); |
||
856 | } |
||
857 | break; |
||
858 | default: |
||
859 | Assert(0); // Illegal value for Interpolation_method, must be 0,1,2,3 |
||
860 | } |
||
861 | |||
862 | Lighting_on = lighting_on_save; |
||
863 | |||
864 | } |
||
865 | |||
866 | } |