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