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 // r_sprite.c
21 
22 #include "console.h"
23 #include "model.h"
24 #include "quakedef.h"
25 #include "r_local.h"
26 #include "sys.h"
27 
28 static int clip_current;
29 static vec5_t clip_verts[2][MAXWORKINGVERTS];
30 static int sprite_width, sprite_height;
31 
32 spritedesc_t r_spritedesc;
33 
34 int
R_SpriteDataSize(int pixels)35 R_SpriteDataSize(int pixels)
36 {
37     return pixels * r_pixbytes;
38 }
39 
R_SpriteDataStore(mspriteframe_t * frame,const char * modelname,int framenum,byte * pixels)40 void R_SpriteDataStore(mspriteframe_t *frame, const char *modelname,
41       int framenum, byte *pixels)
42 {
43    int i;
44    int size = frame->width * frame->height;
45 
46    if (r_pixbytes == 1)
47    {
48       memcpy(&frame->rdata[0], pixels, size);
49    }
50    else if (r_pixbytes == 2)
51    {
52       unsigned short *pixout = (unsigned short *)&frame->rdata[0];
53       for (i = 0; i < size; i++)
54          pixout[i] = d_8to16table[pixels[i]];
55    }
56    else
57    {
58       Sys_Error("%s: driver set invalid r_pixbytes: %d", __func__,
59             r_pixbytes);
60    }
61 }
62 
63 /*
64 ================
65 R_RotateSprite
66 ================
67 */
R_RotateSprite(float beamlength)68 static void R_RotateSprite(float beamlength)
69 {
70    vec3_t vec;
71 
72    if (beamlength == 0.0)
73       return;
74 
75    VectorScale(r_spritedesc.vpn, -beamlength, vec);
76    VectorAdd(r_entorigin, vec, r_entorigin);
77    VectorSubtract(modelorg, vec, modelorg);
78 }
79 
80 
81 /*
82 =============
83 R_ClipSpriteFace
84 
85 Clips the winding at clip_verts[clip_current] and changes clip_current
86 Throws out the back side
87 ==============
88 */
R_ClipSpriteFace(int nump,clipplane_t * pclipplane)89 static int R_ClipSpriteFace(int nump, clipplane_t *pclipplane)
90 {
91    int i, outcount;
92    float dists[MAXWORKINGVERTS + 1];
93    float frac;
94    float *in, *instep, *outstep, *vert2;
95    float clipdist = pclipplane->plane.dist;
96    float *pclipnormal = pclipplane->plane.normal;
97 
98    // calc dists
99    if (clip_current)
100    {
101       in = clip_verts[1][0];
102       outstep = clip_verts[0][0];
103       clip_current = 0;
104    } else {
105       in = clip_verts[0][0];
106       outstep = clip_verts[1][0];
107       clip_current = 1;
108    }
109 
110    instep = in;
111    for (i = 0; i < nump; i++, instep += sizeof(vec5_t) / sizeof(float))
112       dists[i] = DotProduct(instep, pclipnormal) - clipdist;
113 
114    // handle wraparound case
115    dists[nump] = dists[0];
116    memcpy(instep, in, sizeof(vec5_t));
117 
118 
119    // clip the winding
120    instep = in;
121    outcount = 0;
122 
123    for (i = 0; i < nump; i++, instep += sizeof(vec5_t) / sizeof(float))
124    {
125       if (dists[i] >= 0)
126       {
127          memcpy(outstep, instep, sizeof(vec5_t));
128          outstep += sizeof(vec5_t) / sizeof(float);
129          outcount++;
130       }
131 
132       if (dists[i] == 0 || dists[i + 1] == 0)
133          continue;
134 
135       if ((dists[i] > 0) == (dists[i + 1] > 0))
136          continue;
137 
138       // split it into a new vertex
139       frac = dists[i] / (dists[i] - dists[i + 1]);
140 
141       vert2 = instep + sizeof(vec5_t) / sizeof(float);
142 
143       outstep[0] = instep[0] + frac * (vert2[0] - instep[0]);
144       outstep[1] = instep[1] + frac * (vert2[1] - instep[1]);
145       outstep[2] = instep[2] + frac * (vert2[2] - instep[2]);
146       outstep[3] = instep[3] + frac * (vert2[3] - instep[3]);
147       outstep[4] = instep[4] + frac * (vert2[4] - instep[4]);
148 
149       outstep += sizeof(vec5_t) / sizeof(float);
150       outcount++;
151    }
152 
153    return outcount;
154 }
155 
156 
157 /*
158 ================
159 R_SetupAndDrawSprite
160 ================
161 */
R_SetupAndDrawSprite(void)162 static void R_SetupAndDrawSprite(void)
163 {
164    int i, nump;
165    float scale, *pv;
166    vec5_t *pverts;
167    vec3_t left, up, right, down, transformed, local;
168    emitpoint_t	*outverts = malloc(sizeof(emitpoint_t)*MAXWORKINGVERTS+1);
169    emitpoint_t *pout;
170    float dot = DotProduct(r_spritedesc.vpn, modelorg);
171 
172    // backface cull
173    if (dot >= 0)
174       return;
175 
176    // build the sprite poster in worldspace
177    VectorScale(r_spritedesc.vright, r_spritedesc.pspriteframe->right, right);
178    VectorScale(r_spritedesc.vup, r_spritedesc.pspriteframe->up, up);
179    VectorScale(r_spritedesc.vright, r_spritedesc.pspriteframe->left, left);
180    VectorScale(r_spritedesc.vup, r_spritedesc.pspriteframe->down, down);
181 
182    pverts = clip_verts[0];
183 
184    pverts[0][0] = r_entorigin[0] + up[0] + left[0];
185    pverts[0][1] = r_entorigin[1] + up[1] + left[1];
186    pverts[0][2] = r_entorigin[2] + up[2] + left[2];
187    pverts[0][3] = 0;
188    pverts[0][4] = 0;
189 
190    pverts[1][0] = r_entorigin[0] + up[0] + right[0];
191    pverts[1][1] = r_entorigin[1] + up[1] + right[1];
192    pverts[1][2] = r_entorigin[2] + up[2] + right[2];
193    pverts[1][3] = sprite_width;
194    pverts[1][4] = 0;
195 
196    pverts[2][0] = r_entorigin[0] + down[0] + right[0];
197    pverts[2][1] = r_entorigin[1] + down[1] + right[1];
198    pverts[2][2] = r_entorigin[2] + down[2] + right[2];
199    pverts[2][3] = sprite_width;
200    pverts[2][4] = sprite_height;
201 
202    pverts[3][0] = r_entorigin[0] + down[0] + left[0];
203    pverts[3][1] = r_entorigin[1] + down[1] + left[1];
204    pverts[3][2] = r_entorigin[2] + down[2] + left[2];
205    pverts[3][3] = 0;
206    pverts[3][4] = sprite_height;
207 
208    // clip to the frustum in worldspace
209    nump = 4;
210    clip_current = 0;
211 
212    for (i = 0; i < 4; i++) {
213       nump = R_ClipSpriteFace(nump, &view_clipplanes[i]);
214       if (nump < 3)
215          return;
216       if (nump >= MAXWORKINGVERTS)
217          Sys_Error("%s: too many points", __func__);
218    }
219 
220    // transform vertices into viewspace and project
221    pv = &clip_verts[clip_current][0][0];
222    r_spritedesc.nearzi = -999999;
223 
224    for (i = 0; i < nump; i++) {
225       VectorSubtract(pv, r_origin, local);
226       TransformVector(local, transformed);
227 
228       if (transformed[2] < NEAR_CLIP)
229          transformed[2] = NEAR_CLIP;
230 
231       pout = &outverts[i];
232       pout->zi = 1.0 / transformed[2];
233       if (pout->zi > r_spritedesc.nearzi)
234          r_spritedesc.nearzi = pout->zi;
235 
236       pout->s = pv[3];
237       pout->t = pv[4];
238 
239       scale = xscale * pout->zi;
240       pout->u = (xcenter + scale * transformed[0]);
241 
242       scale = yscale * pout->zi;
243       pout->v = (ycenter - scale * transformed[1]);
244 
245       pv += sizeof(vec5_t) / sizeof(*pv);
246    }
247 
248    // draw it
249    r_spritedesc.nump = nump;
250    r_spritedesc.pverts = outverts;
251    D_DrawSprite();
252    free(outverts);
253 }
254 
255 /*
256 ================
257 R_DrawSprite
258 ================
259 */
R_DrawSprite(const entity_t * e)260 void R_DrawSprite(const entity_t *e)
261 {
262    int i;
263    vec3_t tvec;
264    float dot, angle, sr, cr;
265    msprite_t *psprite = (msprite_t*)e->model->cache.data;
266 
267    r_spritedesc.pspriteframe = Mod_GetSpriteFrame(e, psprite, cl.time + e->syncbase);
268 
269    sprite_width = r_spritedesc.pspriteframe->width;
270    sprite_height = r_spritedesc.pspriteframe->height;
271 
272    // TODO: make this caller-selectable
273    if (psprite->type == SPR_FACING_UPRIGHT) {
274       // generate the sprite's axes, with vup straight up in worldspace, and
275       // r_spritedesc.vright perpendicular to modelorg.
276       // This will not work if the view direction is very close to straight up or
277       // down, because the cross product will be between two nearly parallel
278       // vectors and starts to approach an undefined state, so we don't draw if
279       // the two vectors are less than 1 degree apart
280       tvec[0] = -modelorg[0];
281       tvec[1] = -modelorg[1];
282       tvec[2] = -modelorg[2];
283       VectorNormalize(tvec);
284       dot = tvec[2];		// same as DotProduct (tvec, r_spritedesc.vup) because
285       //  r_spritedesc.vup is 0, 0, 1
286       if ((dot > 0.999848) || (dot < -0.999848))	// cos(1 degree) = 0.999848
287          return;
288       r_spritedesc.vup[0] = 0;
289       r_spritedesc.vup[1] = 0;
290       r_spritedesc.vup[2] = 1;
291       r_spritedesc.vright[0] = tvec[1];
292       // CrossProduct(r_spritedesc.vup, -modelorg,
293       r_spritedesc.vright[1] = -tvec[0];
294       //              r_spritedesc.vright)
295       r_spritedesc.vright[2] = 0;
296       VectorNormalize(r_spritedesc.vright);
297       r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
298       r_spritedesc.vpn[1] = r_spritedesc.vright[0];
299       r_spritedesc.vpn[2] = 0;
300       // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
301       //  r_spritedesc.vpn)
302    } else if (psprite->type == SPR_VP_PARALLEL) {
303       // generate the sprite's axes, completely parallel to the viewplane. There
304       // are no problem situations, because the sprite is always in the same
305       // position relative to the viewer
306       for (i = 0; i < 3; i++) {
307          r_spritedesc.vup[i] = vup[i];
308          r_spritedesc.vright[i] = vright[i];
309          r_spritedesc.vpn[i] = vpn[i];
310       }
311    } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) {
312       // generate the sprite's axes, with vup straight up in worldspace, and
313       // r_spritedesc.vright parallel to the viewplane.
314       // This will not work if the view direction is very close to straight up or
315       // down, because the cross product will be between two nearly parallel
316       // vectors and starts to approach an undefined state, so we don't draw if
317       // the two vectors are less than 1 degree apart
318       dot = vpn[2];		// same as DotProduct (vpn, r_spritedesc.vup) because
319       //  r_spritedesc.vup is 0, 0, 1
320       if ((dot > 0.999848) || (dot < -0.999848))	// cos(1 degree) = 0.999848
321          return;
322       r_spritedesc.vup[0] = 0;
323       r_spritedesc.vup[1] = 0;
324       r_spritedesc.vup[2] = 1;
325       r_spritedesc.vright[0] = vpn[1];
326       // CrossProduct (r_spritedesc.vup, vpn,
327       r_spritedesc.vright[1] = -vpn[0];	//  r_spritedesc.vright)
328       r_spritedesc.vright[2] = 0;
329       VectorNormalize(r_spritedesc.vright);
330       r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
331       r_spritedesc.vpn[1] = r_spritedesc.vright[0];
332       r_spritedesc.vpn[2] = 0;
333       // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
334       //  r_spritedesc.vpn)
335    } else if (psprite->type == SPR_ORIENTED) {
336       // generate the sprite's axes, according to the sprite's world orientation
337       AngleVectors(e->angles, r_spritedesc.vpn, r_spritedesc.vright,
338             r_spritedesc.vup);
339    } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) {
340       // generate the sprite's axes, parallel to the viewplane, but rotated in
341       // that plane around the center according to the sprite entity's roll
342       // angle. So vpn stays the same, but vright and vup rotate
343       angle = e->angles[ROLL] * (M_PI * 2 / 360);
344       sr = sin(angle);
345       cr = cos(angle);
346 
347       for (i = 0; i < 3; i++) {
348          r_spritedesc.vpn[i] = vpn[i];
349          r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr;
350          r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr;
351       }
352    } else {
353       Sys_Error("%s: Bad sprite type %d", __func__, psprite->type);
354    }
355 
356    R_RotateSprite(psprite->beamlength);
357 
358    R_SetupAndDrawSprite();
359 }
360