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