git-svn-id: http://bladebattles.com/kurok/SVN@11 20cd92bb-ff49-0410-b73e-96a06e42c3b9
[kurok.git] / r_sprite.c
blob2a1596b9533ff963259ab51ff28e32e1eadb878b
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // r_sprite.c
22 #include "quakedef.h"
23 #include "r_local.h"
25 static int clip_current;
26 static vec5_t clip_verts[2][MAXWORKINGVERTS];
27 static int sprite_width, sprite_height;
29 spritedesc_t r_spritedesc;
33 ================
34 R_RotateSprite
35 ================
37 void R_RotateSprite (float beamlength)
39 vec3_t vec;
41 if (beamlength == 0.0)
42 return;
44 VectorScale (r_spritedesc.vpn, -beamlength, vec);
45 VectorAdd (r_entorigin, vec, r_entorigin);
46 VectorSubtract (modelorg, vec, modelorg);
51 =============
52 R_ClipSpriteFace
54 Clips the winding at clip_verts[clip_current] and changes clip_current
55 Throws out the back side
56 ==============
58 int R_ClipSpriteFace (int nump, clipplane_t *pclipplane)
60 int i, outcount;
61 float dists[MAXWORKINGVERTS+1];
62 float frac, clipdist, *pclipnormal;
63 float *in, *instep, *outstep, *vert2;
65 clipdist = pclipplane->dist;
66 pclipnormal = pclipplane->normal;
68 // calc dists
69 if (clip_current)
71 in = clip_verts[1][0];
72 outstep = clip_verts[0][0];
73 clip_current = 0;
75 else
77 in = clip_verts[0][0];
78 outstep = clip_verts[1][0];
79 clip_current = 1;
82 instep = in;
83 for (i=0 ; i<nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
85 dists[i] = DotProduct (instep, pclipnormal) - clipdist;
88 // handle wraparound case
89 dists[nump] = dists[0];
90 Q_memcpy (instep, in, sizeof (vec5_t));
93 // clip the winding
94 instep = in;
95 outcount = 0;
97 for (i=0 ; i<nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
99 if (dists[i] >= 0)
101 Q_memcpy (outstep, instep, sizeof (vec5_t));
102 outstep += sizeof (vec5_t) / sizeof (float);
103 outcount++;
106 if (dists[i] == 0 || dists[i+1] == 0)
107 continue;
109 if ( (dists[i] > 0) == (dists[i+1] > 0) )
110 continue;
112 // split it into a new vertex
113 frac = dists[i] / (dists[i] - dists[i+1]);
115 vert2 = instep + sizeof (vec5_t) / sizeof (float);
117 outstep[0] = instep[0] + frac*(vert2[0] - instep[0]);
118 outstep[1] = instep[1] + frac*(vert2[1] - instep[1]);
119 outstep[2] = instep[2] + frac*(vert2[2] - instep[2]);
120 outstep[3] = instep[3] + frac*(vert2[3] - instep[3]);
121 outstep[4] = instep[4] + frac*(vert2[4] - instep[4]);
123 outstep += sizeof (vec5_t) / sizeof (float);
124 outcount++;
127 return outcount;
132 ================
133 R_SetupAndDrawSprite
134 ================
136 void R_SetupAndDrawSprite ()
138 int i, nump;
139 float dot, scale, *pv;
140 vec5_t *pverts;
141 vec3_t left, up, right, down, transformed, local;
142 emitpoint_t outverts[MAXWORKINGVERTS+1], *pout;
144 dot = DotProduct (r_spritedesc.vpn, modelorg);
146 // backface cull
147 if (dot >= 0)
148 return;
150 // build the sprite poster in worldspace
151 VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->right, right);
152 VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->up, up);
153 VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->left, left);
154 VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->down, down);
156 pverts = clip_verts[0];
158 pverts[0][0] = r_entorigin[0] + up[0] + left[0];
159 pverts[0][1] = r_entorigin[1] + up[1] + left[1];
160 pverts[0][2] = r_entorigin[2] + up[2] + left[2];
161 pverts[0][3] = 0;
162 pverts[0][4] = 0;
164 pverts[1][0] = r_entorigin[0] + up[0] + right[0];
165 pverts[1][1] = r_entorigin[1] + up[1] + right[1];
166 pverts[1][2] = r_entorigin[2] + up[2] + right[2];
167 pverts[1][3] = sprite_width;
168 pverts[1][4] = 0;
170 pverts[2][0] = r_entorigin[0] + down[0] + right[0];
171 pverts[2][1] = r_entorigin[1] + down[1] + right[1];
172 pverts[2][2] = r_entorigin[2] + down[2] + right[2];
173 pverts[2][3] = sprite_width;
174 pverts[2][4] = sprite_height;
176 pverts[3][0] = r_entorigin[0] + down[0] + left[0];
177 pverts[3][1] = r_entorigin[1] + down[1] + left[1];
178 pverts[3][2] = r_entorigin[2] + down[2] + left[2];
179 pverts[3][3] = 0;
180 pverts[3][4] = sprite_height;
182 // clip to the frustum in worldspace
183 nump = 4;
184 clip_current = 0;
186 for (i=0 ; i<4 ; i++)
188 nump = R_ClipSpriteFace (nump, &view_clipplanes[i]);
189 if (nump < 3)
190 return;
191 if (nump >= MAXWORKINGVERTS)
192 Sys_Error("R_SetupAndDrawSprite: too many points");
195 // transform vertices into viewspace and project
196 pv = &clip_verts[clip_current][0][0];
197 r_spritedesc.nearzi = -999999;
199 for (i=0 ; i<nump ; i++)
201 VectorSubtract (pv, r_origin, local);
202 TransformVector (local, transformed);
204 if (transformed[2] < NEAR_CLIP)
205 transformed[2] = NEAR_CLIP;
207 pout = &outverts[i];
208 pout->zi = 1.0 / transformed[2];
209 if (pout->zi > r_spritedesc.nearzi)
210 r_spritedesc.nearzi = pout->zi;
212 pout->s = pv[3];
213 pout->t = pv[4];
215 scale = xscale * pout->zi;
216 pout->u = (xcenter + scale * transformed[0]);
218 scale = yscale * pout->zi;
219 pout->v = (ycenter - scale * transformed[1]);
221 pv += sizeof (vec5_t) / sizeof (*pv);
224 // draw it
225 r_spritedesc.nump = nump;
226 r_spritedesc.pverts = outverts;
227 D_DrawSprite ();
232 ================
233 R_GetSpriteframe
234 ================
236 mspriteframe_t *R_GetSpriteframe (msprite_t *psprite)
238 mspritegroup_t *pspritegroup;
239 mspriteframe_t *pspriteframe;
240 int i, numframes, frame;
241 float *pintervals, fullinterval, targettime, time;
243 frame = currententity->frame;
245 if ((frame >= psprite->numframes) || (frame < 0))
247 Con_Printf ("R_DrawSprite: no such frame %d\n", frame);
248 frame = 0;
251 if (psprite->frames[frame].type == SPR_SINGLE)
253 pspriteframe = psprite->frames[frame].frameptr;
255 else
257 pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
258 pintervals = pspritegroup->intervals;
259 numframes = pspritegroup->numframes;
260 fullinterval = pintervals[numframes-1];
262 time = cl.time + currententity->syncbase;
264 // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
265 // are positive, so we don't have to worry about division by 0
266 targettime = time - ((int)(time / fullinterval)) * fullinterval;
268 for (i=0 ; i<(numframes-1) ; i++)
270 if (pintervals[i] > targettime)
271 break;
274 pspriteframe = pspritegroup->frames[i];
277 return pspriteframe;
282 ================
283 R_DrawSprite
284 ================
286 void R_DrawSprite (void)
288 int i;
289 msprite_t *psprite;
290 vec3_t tvec;
291 float dot, angle, sr, cr;
293 psprite = currententity->model->cache.data;
295 r_spritedesc.pspriteframe = R_GetSpriteframe (psprite);
297 sprite_width = r_spritedesc.pspriteframe->width;
298 sprite_height = r_spritedesc.pspriteframe->height;
300 // TODO: make this caller-selectable
301 if (psprite->type == SPR_FACING_UPRIGHT)
303 // generate the sprite's axes, with vup straight up in worldspace, and
304 // r_spritedesc.vright perpendicular to modelorg.
305 // This will not work if the view direction is very close to straight up or
306 // down, because the cross product will be between two nearly parallel
307 // vectors and starts to approach an undefined state, so we don't draw if
308 // the two vectors are less than 1 degree apart
309 tvec[0] = -modelorg[0];
310 tvec[1] = -modelorg[1];
311 tvec[2] = -modelorg[2];
312 VectorNormalize (tvec);
313 dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because
314 // r_spritedesc.vup is 0, 0, 1
315 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
316 return;
317 r_spritedesc.vup[0] = 0;
318 r_spritedesc.vup[1] = 0;
319 r_spritedesc.vup[2] = 1;
320 r_spritedesc.vright[0] = tvec[1];
321 // CrossProduct(r_spritedesc.vup, -modelorg,
322 r_spritedesc.vright[1] = -tvec[0];
323 // r_spritedesc.vright)
324 r_spritedesc.vright[2] = 0;
325 VectorNormalize (r_spritedesc.vright);
326 r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
327 r_spritedesc.vpn[1] = r_spritedesc.vright[0];
328 r_spritedesc.vpn[2] = 0;
329 // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
330 // r_spritedesc.vpn)
332 else if (psprite->type == SPR_VP_PARALLEL)
334 // generate the sprite's axes, completely parallel to the viewplane. There
335 // are no problem situations, because the sprite is always in the same
336 // position relative to the viewer
337 for (i=0 ; i<3 ; i++)
339 r_spritedesc.vup[i] = vup[i];
340 r_spritedesc.vright[i] = vright[i];
341 r_spritedesc.vpn[i] = vpn[i];
344 else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT)
346 // generate the sprite's axes, with vup straight up in worldspace, and
347 // r_spritedesc.vright parallel to the viewplane.
348 // This will not work if the view direction is very close to straight up or
349 // down, because the cross product will be between two nearly parallel
350 // vectors and starts to approach an undefined state, so we don't draw if
351 // the two vectors are less than 1 degree apart
352 dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) because
353 // r_spritedesc.vup is 0, 0, 1
354 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
355 return;
356 r_spritedesc.vup[0] = 0;
357 r_spritedesc.vup[1] = 0;
358 r_spritedesc.vup[2] = 1;
359 r_spritedesc.vright[0] = vpn[1];
360 // CrossProduct (r_spritedesc.vup, vpn,
361 r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright)
362 r_spritedesc.vright[2] = 0;
363 VectorNormalize (r_spritedesc.vright);
364 r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
365 r_spritedesc.vpn[1] = r_spritedesc.vright[0];
366 r_spritedesc.vpn[2] = 0;
367 // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
368 // r_spritedesc.vpn)
370 else if (psprite->type == SPR_ORIENTED)
372 // generate the sprite's axes, according to the sprite's world orientation
373 AngleVectors (currententity->angles, r_spritedesc.vpn,
374 r_spritedesc.vright, r_spritedesc.vup);
376 else if (psprite->type == SPR_VP_PARALLEL_ORIENTED)
378 // generate the sprite's axes, parallel to the viewplane, but rotated in
379 // that plane around the center according to the sprite entity's roll
380 // angle. So vpn stays the same, but vright and vup rotate
381 angle = currententity->angles[ROLL] * (M_PI*2 / 360);
382 sr = sinf(angle);
383 cr = cosf(angle);
385 for (i=0 ; i<3 ; i++)
387 r_spritedesc.vpn[i] = vpn[i];
388 r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr;
389 r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr;
392 else
394 Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type);
397 R_RotateSprite (psprite->beamlength);
399 R_SetupAndDrawSprite ();