1 /*
2 * d_sprite.c --
3 * software top-level rasterization driver module for drawing sprites
4 * $Id: d_sprite.c 5947 2017-07-30 17:20:03Z sezero $
5 *
6 * Copyright (C) 1996-1997 Id Software, Inc.
7 * Copyright (C) 1997-1998 Raven Software Corp.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 *
18 * See the GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "quakedef.h"
26 #include "d_local.h"
27
28 static int sprite_height;
29 static int minindex, maxindex;
30 static sspan_t *sprite_spans;
31
32 #if !id386 && !id68k
33
34 /*
35 =====================
36 D_SpriteDrawSpans
37 =====================
38 */
D_SpriteDrawSpans(sspan_t * pspan)39 static void D_SpriteDrawSpans (sspan_t *pspan)
40 {
41 int count, spancount, izistep;
42 int izi;
43 byte *pbase, *pdest;
44 fixed16_t s, t, snext, tnext, sstep, tstep;
45 float sdivz, tdivz, zi, z, du, dv, spancountminus1;
46 float sdivz8stepu, tdivz8stepu, zi8stepu;
47 byte btemp;
48 short *pz;
49
50 sstep = 0; // keep compiler happy
51 tstep = 0; // ditto
52
53 pbase = cacheblock;
54
55 sdivz8stepu = d_sdivzstepu * 8;
56 tdivz8stepu = d_tdivzstepu * 8;
57 zi8stepu = d_zistepu * 8;
58
59 // we count on FP exceptions being turned off to avoid range problems
60 izistep = (int)(d_zistepu * 0x8000 * 0x10000);
61
62 do
63 {
64 pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u;
65 pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u;
66
67 count = pspan->count;
68
69 if (count <= 0)
70 goto NextSpan;
71
72 // calculate the initial s/z, t/z, 1/z, s, and t and clamp
73 du = (float)pspan->u;
74 dv = (float)pspan->v;
75
76 sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu;
77 tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu;
78 zi = d_ziorigin + dv*d_zistepv + du*d_zistepu;
79 z = (float)0x10000 / zi; // prescale to 16.16 fixed-point
80 // we count on FP exceptions being turned off to avoid range problems
81 izi = (int)(zi * 0x8000 * 0x10000);
82
83 s = (int)(sdivz * z) + sadjust;
84 if (s > bbextents)
85 s = bbextents;
86 else if (s < 0)
87 s = 0;
88
89 t = (int)(tdivz * z) + tadjust;
90 if (t > bbextentt)
91 t = bbextentt;
92 else if (t < 0)
93 t = 0;
94
95 do
96 {
97 // calculate s and t at the far end of the span
98 if (count >= 8)
99 spancount = 8;
100 else
101 spancount = count;
102
103 count -= spancount;
104
105 if (count)
106 {
107 // calculate s/z, t/z, zi->fixed s and t at far end of span,
108 // calculate s and t steps across span by shifting
109 sdivz += sdivz8stepu;
110 tdivz += tdivz8stepu;
111 zi += zi8stepu;
112 z = (float)0x10000 / zi; // prescale to 16.16 fixed-point
113
114 snext = (int)(sdivz * z) + sadjust;
115 if (snext > bbextents)
116 snext = bbextents;
117 else if (snext < 8)
118 snext = 8; // prevent round-off error on <0 steps from
119 // from causing overstepping & running off the
120 // edge of the texture
121
122 tnext = (int)(tdivz * z) + tadjust;
123 if (tnext > bbextentt)
124 tnext = bbextentt;
125 else if (tnext < 8)
126 tnext = 8; // guard against round-off error on <0 steps
127
128 sstep = (snext - s) >> 3;
129 tstep = (tnext - t) >> 3;
130 }
131 else
132 {
133 // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so
134 // can't step off polygon), clamp, calculate s and t steps across
135 // span by division, biasing steps low so we don't run off the
136 // texture
137 spancountminus1 = (float)(spancount - 1);
138 sdivz += d_sdivzstepu * spancountminus1;
139 tdivz += d_tdivzstepu * spancountminus1;
140 zi += d_zistepu * spancountminus1;
141 z = (float)0x10000 / zi; // prescale to 16.16 fixed-point
142 snext = (int)(sdivz * z) + sadjust;
143 if (snext > bbextents)
144 snext = bbextents;
145 else if (snext < 8)
146 snext = 8; // prevent round-off error on <0 steps from
147 // from causing overstepping & running off the
148 // edge of the texture
149
150 tnext = (int)(tdivz * z) + tadjust;
151 if (tnext > bbextentt)
152 tnext = bbextentt;
153 else if (tnext < 8)
154 tnext = 8; // guard against round-off error on <0 steps
155
156 if (spancount > 1)
157 {
158 sstep = (snext - s) / (spancount - 1);
159 tstep = (tnext - t) / (spancount - 1);
160 }
161 }
162
163 do
164 {
165 btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth);
166 if (btemp != 255)
167 {
168 if (*pz <= (izi >> 16))
169 {
170 *pz = izi >> 16;
171 *pdest = btemp;
172 }
173 }
174
175 izi += izistep;
176 pdest++;
177 pz++;
178 s += sstep;
179 t += tstep;
180 } while (--spancount > 0);
181
182 s = snext;
183 t = tnext;
184
185 } while (count > 0);
186
187 NextSpan:
188 pspan++;
189
190 } while (pspan->count != DS_SPAN_LIST_END);
191 }
192
193 #endif
194
195 #if !id386
196
197 #define D_SpriteDrawSpansT D_SpriteDrawTransSpans
198 #define D_SpriteDrawSpansT2 D_SpriteDrawTransSpans
199
D_SpriteDrawTransSpans(sspan_t * pspan)200 static void D_SpriteDrawTransSpans (sspan_t *pspan)
201 {
202 int count, spancount, izistep;
203 int izi;
204 byte *pbase, *pdest;
205 fixed16_t s, t, snext, tnext, sstep, tstep;
206 float sdivz, tdivz, zi, z, du, dv, spancountminus1;
207 float sdivz8stepu, tdivz8stepu, zi8stepu;
208 byte btemp;
209 short *pz;
210
211 sstep = 0; // keep compiler happy
212 tstep = 0; // ditto
213
214 pbase = cacheblock;
215
216 sdivz8stepu = d_sdivzstepu * 8;
217 tdivz8stepu = d_tdivzstepu * 8;
218 zi8stepu = d_zistepu * 8;
219
220 // we count on FP exceptions being turned off to avoid range problems
221 izistep = (int)(d_zistepu * 0x8000 * 0x10000);
222
223 do
224 {
225 pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u;
226 pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u;
227
228 count = pspan->count;
229
230 if (count <= 0)
231 goto NextSpan;
232
233 // calculate the initial s/z, t/z, 1/z, s, and t and clamp
234 du = (float)pspan->u;
235 dv = (float)pspan->v;
236
237 sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu;
238 tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu;
239 zi = d_ziorigin + dv*d_zistepv + du*d_zistepu;
240 z = (float)0x10000 / zi; // prescale to 16.16 fixed-point
241 // we count on FP exceptions being turned off to avoid range problems
242 izi = (int)(zi * 0x8000 * 0x10000);
243
244 s = (int)(sdivz * z) + sadjust;
245 if (s > bbextents)
246 s = bbextents;
247 else if (s < 0)
248 s = 0;
249
250 t = (int)(tdivz * z) + tadjust;
251 if (t > bbextentt)
252 t = bbextentt;
253 else if (t < 0)
254 t = 0;
255
256 do
257 {
258 // calculate s and t at the far end of the span
259 if (count >= 8)
260 spancount = 8;
261 else
262 spancount = count;
263
264 count -= spancount;
265
266 if (count)
267 {
268 // calculate s/z, t/z, zi->fixed s and t at far end of span,
269 // calculate s and t steps across span by shifting
270 sdivz += sdivz8stepu;
271 tdivz += tdivz8stepu;
272 zi += zi8stepu;
273 z = (float)0x10000 / zi; // prescale to 16.16 fixed-point
274
275 snext = (int)(sdivz * z) + sadjust;
276 if (snext > bbextents)
277 snext = bbextents;
278 else if (snext < 8)
279 snext = 8; // prevent round-off error on <0 steps from
280 // from causing overstepping & running off the
281 // edge of the texture
282
283 tnext = (int)(tdivz * z) + tadjust;
284 if (tnext > bbextentt)
285 tnext = bbextentt;
286 else if (tnext < 8)
287 tnext = 8; // guard against round-off error on <0 steps
288
289 sstep = (snext - s) >> 3;
290 tstep = (tnext - t) >> 3;
291 }
292 else
293 {
294 // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so
295 // can't step off polygon), clamp, calculate s and t steps across
296 // span by division, biasing steps low so we don't run off the
297 // texture
298 spancountminus1 = (float)(spancount - 1);
299 sdivz += d_sdivzstepu * spancountminus1;
300 tdivz += d_tdivzstepu * spancountminus1;
301 zi += d_zistepu * spancountminus1;
302 z = (float)0x10000 / zi; // prescale to 16.16 fixed-point
303 snext = (int)(sdivz * z) + sadjust;
304 if (snext > bbextents)
305 snext = bbextents;
306 else if (snext < 8)
307 snext = 8; // prevent round-off error on <0 steps from
308 // from causing overstepping & running off the
309 // edge of the texture
310
311 tnext = (int)(tdivz * z) + tadjust;
312 if (tnext > bbextentt)
313 tnext = bbextentt;
314 else if (tnext < 8)
315 tnext = 8; // guard against round-off error on <0 steps
316
317 if (spancount > 1)
318 {
319 sstep = (snext - s) / (spancount - 1);
320 tstep = (tnext - t) / (spancount - 1);
321 }
322 }
323
324 do
325 {
326 btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth);
327 if (btemp != 255)
328 {
329 if (*pz <= (izi >> 16))
330 {
331 //*pz = izi >> 16;
332 *pdest = mainTransTable[(btemp<<8) + (*pdest)];
333 }
334 }
335
336 izi += izistep;
337 pdest++;
338 pz++;
339 s += sstep;
340 t += tstep;
341 } while (--spancount > 0);
342
343 s = snext;
344 t = tnext;
345
346 } while (count > 0);
347
348 NextSpan:
349 pspan++;
350
351 } while (pspan->count != DS_SPAN_LIST_END);
352 }
353
354 #endif
355
356 /*
357 =====================
358 D_SpriteScanLeftEdge
359 =====================
360 */
D_SpriteScanLeftEdge(void)361 static void D_SpriteScanLeftEdge (void)
362 {
363 int i, v, itop, ibottom, lmaxindex;
364 emitpoint_t *pvert, *pnext;
365 sspan_t *pspan;
366 float du, dv, vtop, vbottom, slope;
367 fixed16_t u, u_step;
368
369 pspan = sprite_spans;
370 i = minindex;
371 if (i == 0)
372 i = r_spritedesc.nump;
373
374 lmaxindex = maxindex;
375 if (lmaxindex == 0)
376 lmaxindex = r_spritedesc.nump;
377
378 vtop = ceil (r_spritedesc.pverts[i].v);
379
380 do
381 {
382 pvert = &r_spritedesc.pverts[i];
383 pnext = pvert - 1;
384
385 vbottom = ceil (pnext->v);
386
387 if (vtop < vbottom)
388 {
389 du = pnext->u - pvert->u;
390 dv = pnext->v - pvert->v;
391 slope = du / dv;
392 u_step = (int)(slope * 0x10000);
393 // adjust u to ceil the integer portion
394 u = (int)((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) +
395 (0x10000 - 1);
396 itop = (int)vtop;
397 ibottom = (int)vbottom;
398
399 for (v=itop ; v<ibottom ; v++)
400 {
401 pspan->u = u >> 16;
402 pspan->v = v;
403 u += u_step;
404 pspan++;
405 }
406 }
407
408 vtop = vbottom;
409
410 i--;
411 if (i == 0)
412 i = r_spritedesc.nump;
413
414 } while (i != lmaxindex);
415 }
416
417
418 /*
419 =====================
420 D_SpriteScanRightEdge
421 =====================
422 */
D_SpriteScanRightEdge(void)423 static void D_SpriteScanRightEdge (void)
424 {
425 int i, v, itop, ibottom;
426 emitpoint_t *pvert, *pnext;
427 sspan_t *pspan;
428 float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext;
429 fixed16_t u, u_step;
430
431 pspan = sprite_spans;
432 i = minindex;
433
434 vvert = r_spritedesc.pverts[i].v;
435 if (vvert < r_refdef.fvrecty_adj)
436 vvert = r_refdef.fvrecty_adj;
437 if (vvert > r_refdef.fvrectbottom_adj)
438 vvert = r_refdef.fvrectbottom_adj;
439
440 vtop = ceil (vvert);
441
442 do
443 {
444 pvert = &r_spritedesc.pverts[i];
445 pnext = pvert + 1;
446
447 vnext = pnext->v;
448 if (vnext < r_refdef.fvrecty_adj)
449 vnext = r_refdef.fvrecty_adj;
450 if (vnext > r_refdef.fvrectbottom_adj)
451 vnext = r_refdef.fvrectbottom_adj;
452
453 vbottom = ceil (vnext);
454
455 if (vtop < vbottom)
456 {
457 uvert = pvert->u;
458 if (uvert < r_refdef.fvrectx_adj)
459 uvert = r_refdef.fvrectx_adj;
460 if (uvert > r_refdef.fvrectright_adj)
461 uvert = r_refdef.fvrectright_adj;
462
463 unext = pnext->u;
464 if (unext < r_refdef.fvrectx_adj)
465 unext = r_refdef.fvrectx_adj;
466 if (unext > r_refdef.fvrectright_adj)
467 unext = r_refdef.fvrectright_adj;
468
469 du = unext - uvert;
470 dv = vnext - vvert;
471 slope = du / dv;
472 u_step = (int)(slope * 0x10000);
473 // adjust u to ceil the integer portion
474 u = (int)((uvert + (slope * (vtop - vvert))) * 0x10000) +
475 (0x10000 - 1);
476 itop = (int)vtop;
477 ibottom = (int)vbottom;
478
479 for (v = itop; v < ibottom; v++)
480 {
481 pspan->count = (u >> 16) - pspan->u;
482 u += u_step;
483 pspan++;
484 }
485 }
486
487 vtop = vbottom;
488 vvert = vnext;
489
490 i++;
491 if (i == r_spritedesc.nump)
492 i = 0;
493
494 } while (i != maxindex);
495
496 pspan->count = DS_SPAN_LIST_END; // mark the end of the span list
497 }
498
499
500 /*
501 =====================
502 D_SpriteCalculateGradients
503 =====================
504 */
D_SpriteCalculateGradients(void)505 static void D_SpriteCalculateGradients (void)
506 {
507 vec3_t p_normal, p_saxis, p_taxis, p_temp1;
508 float distinv;
509
510 TransformVector (r_spritedesc.vpn, p_normal);
511 TransformVector (r_spritedesc.vright, p_saxis);
512 TransformVector (r_spritedesc.vup, p_taxis);
513 VectorInverse (p_taxis);
514
515 distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn));
516
517 d_sdivzstepu = p_saxis[0] * xscaleinv;
518 d_tdivzstepu = p_taxis[0] * xscaleinv;
519
520 d_sdivzstepv = -p_saxis[1] * yscaleinv;
521 d_tdivzstepv = -p_taxis[1] * yscaleinv;
522
523 d_zistepu = p_normal[0] * xscaleinv * distinv;
524 d_zistepv = -p_normal[1] * yscaleinv * distinv;
525
526 d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu -
527 ycenter * d_sdivzstepv;
528 d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu -
529 ycenter * d_tdivzstepv;
530 d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu -
531 ycenter * d_zistepv;
532
533 TransformVector (modelorg, p_temp1);
534
535 sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) -
536 (-(cachewidth >> 1) << 16);
537 tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) -
538 (-(sprite_height >> 1) << 16);
539
540 // -1 (-epsilon) so we never wander off the edge of the texture
541 bbextents = (cachewidth << 16) - 1;
542 bbextentt = (sprite_height << 16) - 1;
543 }
544
545
546 /*
547 =====================
548 D_DrawSprite
549 =====================
550 */
D_DrawSprite(void)551 void D_DrawSprite (void)
552 {
553 int i, nump;
554 float ymin, ymax;
555 emitpoint_t *pverts;
556 sspan_t spans[MAXHEIGHT+1];
557
558 sprite_spans = spans;
559
560 // find the top and bottom vertices, and make sure there's at least one scan to
561 // draw
562 ymin = 999999.9;
563 ymax = -999999.9;
564 pverts = r_spritedesc.pverts;
565
566 for (i = 0; i < r_spritedesc.nump; i++)
567 {
568 if (pverts->v < ymin)
569 {
570 ymin = pverts->v;
571 minindex = i;
572 }
573
574 if (pverts->v > ymax)
575 {
576 ymax = pverts->v;
577 maxindex = i;
578 }
579
580 pverts++;
581 }
582
583 ymin = ceil (ymin);
584 ymax = ceil (ymax);
585
586 if (ymin >= ymax)
587 return; // doesn't cross any scans at all
588
589 if (ymin < 0) {
590 /* happens when R_SetupAndDrawSprite() sets pout->v to a value <= -1
591 * with sprites rendered very close to the camera origin such as the
592 * teleporter puff. this makes the screen pointer pz to point to an
593 * invalid address in D_SpriteDrawTransSpans(), leading to an access
594 * violation. -- see uHexen2 bug #3562290:
595 * http://sourceforge.net/tracker/?func=detail&atid=701006&aid=3562290&group_id=124987
596 */
597 Con_DPrintf ("%s: ymin: %f\n", __thisfunc__, ymin);
598 return;
599 }
600
601 cachewidth = r_spritedesc.pspriteframe->width;
602 sprite_height = r_spritedesc.pspriteframe->height;
603 cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0];
604
605 // copy the first vertex to the last vertex, so we don't have to deal with
606 // wrapping
607 nump = r_spritedesc.nump;
608 pverts = r_spritedesc.pverts;
609 pverts[nump] = pverts[0];
610
611 D_SpriteCalculateGradients ();
612 D_SpriteScanLeftEdge ();
613 D_SpriteScanRightEdge ();
614
615 if (currententity->drawflags & DRF_TRANSLUCENT)
616 D_SpriteDrawSpansT (sprite_spans);
617 else if (currententity->model->flags & EF_TRANSPARENT)
618 D_SpriteDrawSpansT2 (sprite_spans);
619 else
620 D_SpriteDrawSpans (sprite_spans);
621 }
622
623