1 /*
2 	d_sprite.c
3 
4 	software top-level rasterization driver module for drawing sprites
5 
6 	Copyright (C) 1996-1997  Id Software, Inc.
7 
8 	This program is free software; you can redistribute it and/or
9 	modify it under the terms of the GNU General Public License
10 	as published by the Free Software Foundation; either version 2
11 	of the License, or (at your option) any later version.
12 
13 	This program is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 	See the GNU General Public License for more details.
18 
19 	You should have received a copy of the GNU General Public License
20 	along with this program; if not, write to:
21 
22 		Free Software Foundation, Inc.
23 		59 Temple Place - Suite 330
24 		Boston, MA  02111-1307, USA
25 
26 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 
31 #include "QF/render.h"
32 
33 #include "d_local.h"
34 #include "r_internal.h"
35 
36 static int  sprite_height;
37 static int  minindex, maxindex;
38 static sspan_t *sprite_spans;
39 
40 
41 #ifdef PIC
42 #undef USE_INTEL_ASM //XXX asm pic hack
43 #endif
44 
45 
46 #ifndef USE_INTEL_ASM
47 void
D_SpriteDrawSpans(sspan_t * pspan)48 D_SpriteDrawSpans (sspan_t *pspan)
49 {
50 	int         count, spancount, izistep;
51 	int         izi;
52 	byte       *pbase, *pdest;
53 	fixed16_t   s, t, snext, tnext, sstep, tstep;
54 	float       sdivz, tdivz, zi, z, du, dv, spancountminus1;
55 	float       sdivz8stepu, tdivz8stepu, zi8stepu;
56 	byte        btemp;
57 	short      *pz;
58 
59 	sstep = 0;							// keep compiler happy
60 	tstep = 0;							// ditto
61 
62 	pbase = cacheblock;
63 
64 	sdivz8stepu = d_sdivzstepu * 8;
65 	tdivz8stepu = d_tdivzstepu * 8;
66 	zi8stepu = d_zistepu * 8;
67 
68 	// we count on FP exceptions being turned off to avoid range problems
69 	izistep = (int) (d_zistepu * 0x8000 * 0x10000);
70 
71 	do {
72 		pdest = (byte *) d_viewbuffer + (screenwidth * pspan->v) + pspan->u;
73 		pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u;
74 
75 		count = pspan->count;
76 
77 		if (count <= 0)
78 			goto NextSpan;
79 
80 		// calculate the initial s/z, t/z, 1/z, s, and t and clamp
81 		du = (float) pspan->u;
82 		dv = (float) pspan->v;
83 
84 		sdivz = d_sdivzorigin + dv * d_sdivzstepv + du * d_sdivzstepu;
85 		tdivz = d_tdivzorigin + dv * d_tdivzstepv + du * d_tdivzstepu;
86 		zi = d_ziorigin + dv * d_zistepv + du * d_zistepu;
87 		z = (float) 0x10000 / zi;		// prescale to 16.16 fixed-point
88 		// we count on FP exceptions being turned off to avoid range problems
89 		izi = (int) (zi * 0x8000 * 0x10000);
90 
91 		s = (int) (sdivz * z) + sadjust;
92 		if (s > bbextents)
93 			s = bbextents;
94 		else if (s < 0)
95 			s = 0;
96 
97 		t = (int) (tdivz * z) + tadjust;
98 		if (t > bbextentt)
99 			t = bbextentt;
100 		else if (t < 0)
101 			t = 0;
102 
103 		do {
104 			// calculate s and t at the far end of the span
105 			if (count >= 8)
106 				spancount = 8;
107 			else
108 				spancount = count;
109 
110 			count -= spancount;
111 
112 			if (count) {
113 				// calculate s/z, t/z, zi->fixed s and t at far end of span,
114 				// calculate s and t steps across span by shifting
115 				sdivz += sdivz8stepu;
116 				tdivz += tdivz8stepu;
117 				zi += zi8stepu;
118 				z = (float) 0x10000 / zi;	// prescale to 16.16 fixed-point
119 
120 				snext = (int) (sdivz * z) + sadjust;
121 				if (snext > bbextents)
122 					snext = bbextents;
123 				else if (snext < 8)
124 					snext = 8;			// prevent round-off error on <0
125 										// steps from
126 				// from causing overstepping & running off the
127 				// edge of the texture
128 
129 				tnext = (int) (tdivz * z) + tadjust;
130 				if (tnext > bbextentt)
131 					tnext = bbextentt;
132 				else if (tnext < 8)
133 					tnext = 8;			// guard against round-off error on
134 										// <0 steps
135 
136 				sstep = (snext - s) >> 3;
137 				tstep = (tnext - t) >> 3;
138 			} else {
139 				// calculate s/z, t/z, zi->fixed s and t at last pixel in
140 				// span (so can't step off polygon), clamp, calculate s and t
141 				// steps across span by division, biasing steps low so we
142 				// don't run off the texture
143 				spancountminus1 = (float) (spancount - 1);
144 				sdivz += d_sdivzstepu * spancountminus1;
145 				tdivz += d_tdivzstepu * spancountminus1;
146 				zi += d_zistepu * spancountminus1;
147 				z = (float) 0x10000 / zi;	// prescale to 16.16 fixed-point
148 				snext = (int) (sdivz * z) + sadjust;
149 				if (snext > bbextents)
150 					snext = bbextents;
151 				else if (snext < 8)
152 					snext = 8;			// prevent round-off error on <0 steps
153 										// from from causing overstepping &
154 										// running off the edge of the texture
155 
156 				tnext = (int) (tdivz * z) + tadjust;
157 				if (tnext > bbextentt)
158 					tnext = bbextentt;
159 				else if (tnext < 8)
160 					tnext = 8;			// guard against round-off error on
161 										// <0 steps
162 
163 				if (spancount > 1) {
164 					sstep = (snext - s) / (spancount - 1);
165 					tstep = (tnext - t) / (spancount - 1);
166 				}
167 			}
168 
169 			do {
170 				btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth);
171 				if (btemp != 255) {
172 					if (*pz <= (izi >> 16)) {
173 						*pz = izi >> 16;
174 						*pdest = btemp;
175 					}
176 				}
177 
178 				izi += izistep;
179 				pdest++;
180 				pz++;
181 				s += sstep;
182 				t += tstep;
183 			} while (--spancount > 0);
184 
185 			s = snext;
186 			t = tnext;
187 
188 		} while (count > 0);
189 
190 	  NextSpan:
191 		pspan++;
192 
193 	} while (pspan->count != DS_SPAN_LIST_END);
194 }
195 #endif
196 
197 static void
D_SpriteScanLeftEdge(void)198 D_SpriteScanLeftEdge (void)
199 {
200 	int         i, v, itop, ibottom, lmaxindex;
201 	emitpoint_t *pvert, *pnext;
202 	sspan_t    *pspan;
203 	float       du, dv, vtop, vbottom, slope;
204 	fixed16_t   u, u_step;
205 
206 	pspan = sprite_spans;
207 	i = minindex;
208 	if (i == 0)
209 		i = r_spritedesc.nump;
210 
211 	lmaxindex = maxindex;
212 	if (lmaxindex == 0)
213 		lmaxindex = r_spritedesc.nump;
214 
215 	vtop = ceil (r_spritedesc.pverts[i].v);
216 
217 	do {
218 		pvert = &r_spritedesc.pverts[i];
219 		pnext = pvert - 1;
220 
221 		vbottom = ceil (pnext->v);
222 
223 		if (vtop < vbottom) {
224 			du = pnext->u - pvert->u;
225 			dv = pnext->v - pvert->v;
226 			slope = du / dv;
227 			u_step = (int) (slope * 0x10000);
228 			// adjust u to ceil the integer portion
229 			u = (int) ((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) +
230 				(0x10000 - 1);
231 			itop = (int) vtop;
232 			ibottom = (int) vbottom;
233 
234 			for (v = itop; v < ibottom; v++) {
235 				pspan->u = u >> 16;
236 				pspan->v = v;
237 				u += u_step;
238 				pspan++;
239 			}
240 		}
241 
242 		vtop = vbottom;
243 
244 		i--;
245 		if (i == 0)
246 			i = r_spritedesc.nump;
247 
248 	} while (i != lmaxindex);
249 }
250 
251 static void
D_SpriteScanRightEdge(void)252 D_SpriteScanRightEdge (void)
253 {
254 	int         i, v, itop, ibottom;
255 	emitpoint_t *pvert, *pnext;
256 	sspan_t    *pspan;
257 	float       du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext;
258 	fixed16_t   u, u_step;
259 
260 	pspan = sprite_spans;
261 	i = minindex;
262 
263 	vvert = r_spritedesc.pverts[i].v;
264 	if (vvert < r_refdef.fvrecty_adj)
265 		vvert = r_refdef.fvrecty_adj;
266 	if (vvert > r_refdef.fvrectbottom_adj)
267 		vvert = r_refdef.fvrectbottom_adj;
268 
269 	vtop = ceil (vvert);
270 
271 	do {
272 		pvert = &r_spritedesc.pverts[i];
273 		pnext = pvert + 1;
274 
275 		vnext = pnext->v;
276 		if (vnext < r_refdef.fvrecty_adj)
277 			vnext = r_refdef.fvrecty_adj;
278 		if (vnext > r_refdef.fvrectbottom_adj)
279 			vnext = r_refdef.fvrectbottom_adj;
280 
281 		vbottom = ceil (vnext);
282 
283 		if (vtop < vbottom) {
284 			uvert = pvert->u;
285 			if (uvert < r_refdef.fvrectx_adj)
286 				uvert = r_refdef.fvrectx_adj;
287 			if (uvert > r_refdef.fvrectright_adj)
288 				uvert = r_refdef.fvrectright_adj;
289 
290 			unext = pnext->u;
291 			if (unext < r_refdef.fvrectx_adj)
292 				unext = r_refdef.fvrectx_adj;
293 			if (unext > r_refdef.fvrectright_adj)
294 				unext = r_refdef.fvrectright_adj;
295 
296 			du = unext - uvert;
297 			dv = vnext - vvert;
298 			slope = du / dv;
299 			u_step = (int) (slope * 0x10000);
300 			// adjust u to ceil the integer portion
301 			u = (int) ((uvert + (slope * (vtop - vvert))) * 0x10000) +
302 				(0x10000 - 1);
303 			itop = (int) vtop;
304 			ibottom = (int) vbottom;
305 
306 			for (v = itop; v < ibottom; v++) {
307 				pspan->count = (u >> 16) - pspan->u;
308 				u += u_step;
309 				pspan++;
310 			}
311 		}
312 
313 		vtop = vbottom;
314 		vvert = vnext;
315 
316 		i++;
317 		if (i == r_spritedesc.nump)
318 			i = 0;
319 
320 	} while (i != maxindex);
321 
322 	pspan->count = DS_SPAN_LIST_END;	// mark the end of the span list
323 }
324 
325 static void
D_SpriteCalculateGradients(void)326 D_SpriteCalculateGradients (void)
327 {
328 	vec3_t      p_normal, p_saxis, p_taxis, p_temp1;
329 	float       distinv;
330 
331 	TransformVector (r_spritedesc.vpn, p_normal);
332 	TransformVector (r_spritedesc.vright, p_saxis);
333 	TransformVector (r_spritedesc.vup, p_taxis);
334 	VectorNegate (p_taxis, p_taxis);
335 
336 	distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn));
337 
338 	d_sdivzstepu = p_saxis[0] * xscaleinv;
339 	d_tdivzstepu = p_taxis[0] * xscaleinv;
340 
341 	d_sdivzstepv = -p_saxis[1] * yscaleinv;
342 	d_tdivzstepv = -p_taxis[1] * yscaleinv;
343 
344 	d_zistepu = p_normal[0] * xscaleinv * distinv;
345 	d_zistepv = -p_normal[1] * yscaleinv * distinv;
346 
347 	d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu -
348 		ycenter * d_sdivzstepv;
349 	d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu -
350 		ycenter * d_tdivzstepv;
351 	d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu -
352 		ycenter * d_zistepv;
353 
354 	TransformVector (modelorg, p_temp1);
355 
356 	sadjust = ((fixed16_t) (DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) -
357 		(-(cachewidth >> 1) << 16);
358 	tadjust = ((fixed16_t) (DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) -
359 		(-(sprite_height >> 1) << 16);
360 
361 	// -1 (-epsilon) so we never wander off the edge of the texture
362 	bbextents = (cachewidth << 16) - 1;
363 	bbextentt = (sprite_height << 16) - 1;
364 }
365 
366 void
D_DrawSprite(void)367 D_DrawSprite (void)
368 {
369 	int         i, nump;
370 	float       ymin, ymax;
371 	emitpoint_t *pverts;
372 	sspan_t     spans[MAXHEIGHT + 1];
373 
374 	sprite_spans = spans;
375 
376 	// find the top and bottom vertices, and make sure there's at least one
377 	// scan to draw
378 	ymin = 999999.9;
379 	ymax = -999999.9;
380 	pverts = r_spritedesc.pverts;
381 
382 	for (i = 0; i < r_spritedesc.nump; i++) {
383 		if (pverts->v < ymin) {
384 			ymin = pverts->v;
385 			minindex = i;
386 		}
387 
388 		if (pverts->v > ymax) {
389 			ymax = pverts->v;
390 			maxindex = i;
391 		}
392 
393 		pverts++;
394 	}
395 
396 	ymin = ceil (ymin);
397 	ymax = ceil (ymax);
398 
399 	if (ymin >= ymax)
400 		return;							// doesn't cross any scans at all
401 
402 	cachewidth = r_spritedesc.pspriteframe->width;
403 	sprite_height = r_spritedesc.pspriteframe->height;
404 	cacheblock = &r_spritedesc.pspriteframe->pixels[0];
405 
406 	// copy the first vertex to the last vertex, so we don't have to deal with
407 	// wrapping
408 	nump = r_spritedesc.nump;
409 	pverts = r_spritedesc.pverts;
410 	pverts[nump] = pverts[0];
411 
412 	D_SpriteCalculateGradients ();
413 	D_SpriteScanLeftEdge ();
414 	D_SpriteScanRightEdge ();
415 	D_SpriteDrawSpans (sprite_spans);
416 }
417