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