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