123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- /*
- Copyright (C) 1996-1997 Id Software, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- // r_sprite.c
- #include "quakedef.h"
- #include "r_local.h"
- static int clip_current;
- static vec5_t clip_verts[2][MAXWORKINGVERTS];
- static int sprite_width, sprite_height;
- spritedesc_t r_spritedesc;
-
- /*
- ================
- R_RotateSprite
- ================
- */
- void R_RotateSprite (float beamlength)
- {
- vec3_t vec;
-
- if (beamlength == 0.0)
- return;
- VectorScale (r_spritedesc.vpn, -beamlength, vec);
- VectorAdd (r_entorigin, vec, r_entorigin);
- VectorSubtract (modelorg, vec, modelorg);
- }
- /*
- =============
- R_ClipSpriteFace
- Clips the winding at clip_verts[clip_current] and changes clip_current
- Throws out the back side
- ==============
- */
- int R_ClipSpriteFace (int nump, clipplane_t *pclipplane)
- {
- int i, outcount;
- float dists[MAXWORKINGVERTS+1];
- float frac, clipdist, *pclipnormal;
- float *in, *instep, *outstep, *vert2;
- clipdist = pclipplane->dist;
- pclipnormal = pclipplane->normal;
-
- // calc dists
- if (clip_current)
- {
- in = clip_verts[1][0];
- outstep = clip_verts[0][0];
- clip_current = 0;
- }
- else
- {
- in = clip_verts[0][0];
- outstep = clip_verts[1][0];
- clip_current = 1;
- }
-
- instep = in;
- for (i=0 ; i<nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
- {
- dists[i] = DotProduct (instep, pclipnormal) - clipdist;
- }
-
- // handle wraparound case
- dists[nump] = dists[0];
- Q_memcpy (instep, in, sizeof (vec5_t));
- // clip the winding
- instep = in;
- outcount = 0;
- for (i=0 ; i<nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
- {
- if (dists[i] >= 0)
- {
- Q_memcpy (outstep, instep, sizeof (vec5_t));
- outstep += sizeof (vec5_t) / sizeof (float);
- outcount++;
- }
- if (dists[i] == 0 || dists[i+1] == 0)
- continue;
- if ( (dists[i] > 0) == (dists[i+1] > 0) )
- continue;
-
- // split it into a new vertex
- frac = dists[i] / (dists[i] - dists[i+1]);
-
- vert2 = instep + sizeof (vec5_t) / sizeof (float);
-
- outstep[0] = instep[0] + frac*(vert2[0] - instep[0]);
- outstep[1] = instep[1] + frac*(vert2[1] - instep[1]);
- outstep[2] = instep[2] + frac*(vert2[2] - instep[2]);
- outstep[3] = instep[3] + frac*(vert2[3] - instep[3]);
- outstep[4] = instep[4] + frac*(vert2[4] - instep[4]);
- outstep += sizeof (vec5_t) / sizeof (float);
- outcount++;
- }
-
- return outcount;
- }
- /*
- ================
- R_SetupAndDrawSprite
- ================
- */
- void R_SetupAndDrawSprite ()
- {
- int i, nump;
- float dot, scale, *pv;
- vec5_t *pverts;
- vec3_t left, up, right, down, transformed, local;
- emitpoint_t outverts[MAXWORKINGVERTS+1], *pout;
- dot = DotProduct (r_spritedesc.vpn, modelorg);
- // backface cull
- if (dot >= 0)
- return;
- // build the sprite poster in worldspace
- VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->right, right);
- VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->up, up);
- VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->left, left);
- VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->down, down);
- pverts = clip_verts[0];
- pverts[0][0] = r_entorigin[0] + up[0] + left[0];
- pverts[0][1] = r_entorigin[1] + up[1] + left[1];
- pverts[0][2] = r_entorigin[2] + up[2] + left[2];
- pverts[0][3] = 0;
- pverts[0][4] = 0;
- pverts[1][0] = r_entorigin[0] + up[0] + right[0];
- pverts[1][1] = r_entorigin[1] + up[1] + right[1];
- pverts[1][2] = r_entorigin[2] + up[2] + right[2];
- pverts[1][3] = sprite_width;
- pverts[1][4] = 0;
- pverts[2][0] = r_entorigin[0] + down[0] + right[0];
- pverts[2][1] = r_entorigin[1] + down[1] + right[1];
- pverts[2][2] = r_entorigin[2] + down[2] + right[2];
- pverts[2][3] = sprite_width;
- pverts[2][4] = sprite_height;
- pverts[3][0] = r_entorigin[0] + down[0] + left[0];
- pverts[3][1] = r_entorigin[1] + down[1] + left[1];
- pverts[3][2] = r_entorigin[2] + down[2] + left[2];
- pverts[3][3] = 0;
- pverts[3][4] = sprite_height;
- // clip to the frustum in worldspace
- nump = 4;
- clip_current = 0;
- for (i=0 ; i<4 ; i++)
- {
- nump = R_ClipSpriteFace (nump, &view_clipplanes[i]);
- if (nump < 3)
- return;
- if (nump >= MAXWORKINGVERTS)
- Sys_Error("R_SetupAndDrawSprite: too many points");
- }
- // transform vertices into viewspace and project
- pv = &clip_verts[clip_current][0][0];
- r_spritedesc.nearzi = -999999;
- for (i=0 ; i<nump ; i++)
- {
- VectorSubtract (pv, r_origin, local);
- TransformVector (local, transformed);
- if (transformed[2] < NEAR_CLIP)
- transformed[2] = NEAR_CLIP;
- pout = &outverts[i];
- pout->zi = 1.0 / transformed[2];
- if (pout->zi > r_spritedesc.nearzi)
- r_spritedesc.nearzi = pout->zi;
- pout->s = pv[3];
- pout->t = pv[4];
-
- scale = xscale * pout->zi;
- pout->u = (xcenter + scale * transformed[0]);
- scale = yscale * pout->zi;
- pout->v = (ycenter - scale * transformed[1]);
- pv += sizeof (vec5_t) / sizeof (pv);
- }
- // draw it
- r_spritedesc.nump = nump;
- r_spritedesc.pverts = outverts;
- D_DrawSprite ();
- }
- /*
- ================
- R_GetSpriteframe
- ================
- */
- mspriteframe_t *R_GetSpriteframe (msprite_t *psprite)
- {
- mspritegroup_t *pspritegroup;
- mspriteframe_t *pspriteframe;
- int i, numframes, frame;
- float *pintervals, fullinterval, targettime, time;
- frame = currententity->frame;
- if ((frame >= psprite->numframes) || (frame < 0))
- {
- Con_Printf ("R_DrawSprite: no such frame %d\n", frame);
- frame = 0;
- }
- if (psprite->frames[frame].type == SPR_SINGLE)
- {
- pspriteframe = psprite->frames[frame].frameptr;
- }
- else
- {
- pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
- pintervals = pspritegroup->intervals;
- numframes = pspritegroup->numframes;
- fullinterval = pintervals[numframes-1];
- time = cl.time + currententity->syncbase;
- // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
- // are positive, so we don't have to worry about division by 0
- targettime = time - ((int)(time / fullinterval)) * fullinterval;
- for (i=0 ; i<(numframes-1) ; i++)
- {
- if (pintervals[i] > targettime)
- break;
- }
- pspriteframe = pspritegroup->frames[i];
- }
- return pspriteframe;
- }
- /*
- ================
- R_DrawSprite
- ================
- */
- void R_DrawSprite (void)
- {
- int i;
- msprite_t *psprite;
- vec3_t tvec;
- float dot, angle, sr, cr;
- psprite = currententity->model->cache.data;
- r_spritedesc.pspriteframe = R_GetSpriteframe (psprite);
- sprite_width = r_spritedesc.pspriteframe->width;
- sprite_height = r_spritedesc.pspriteframe->height;
- // TODO: make this caller-selectable
- if (psprite->type == SPR_FACING_UPRIGHT)
- {
- // generate the sprite's axes, with vup straight up in worldspace, and
- // r_spritedesc.vright perpendicular to modelorg.
- // This will not work if the view direction is very close to straight up or
- // down, because the cross product will be between two nearly parallel
- // vectors and starts to approach an undefined state, so we don't draw if
- // the two vectors are less than 1 degree apart
- tvec[0] = -modelorg[0];
- tvec[1] = -modelorg[1];
- tvec[2] = -modelorg[2];
- VectorNormalize (tvec);
- dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because
- // r_spritedesc.vup is 0, 0, 1
- if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
- return;
- r_spritedesc.vup[0] = 0;
- r_spritedesc.vup[1] = 0;
- r_spritedesc.vup[2] = 1;
- r_spritedesc.vright[0] = tvec[1];
- // CrossProduct(r_spritedesc.vup, -modelorg,
- r_spritedesc.vright[1] = -tvec[0];
- // r_spritedesc.vright)
- r_spritedesc.vright[2] = 0;
- VectorNormalize (r_spritedesc.vright);
- r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
- r_spritedesc.vpn[1] = r_spritedesc.vright[0];
- r_spritedesc.vpn[2] = 0;
- // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
- // r_spritedesc.vpn)
- }
- else if (psprite->type == SPR_VP_PARALLEL)
- {
- // generate the sprite's axes, completely parallel to the viewplane. There
- // are no problem situations, because the sprite is always in the same
- // position relative to the viewer
- for (i=0 ; i<3 ; i++)
- {
- r_spritedesc.vup[i] = vup[i];
- r_spritedesc.vright[i] = vright[i];
- r_spritedesc.vpn[i] = vpn[i];
- }
- }
- else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT)
- {
- // generate the sprite's axes, with vup straight up in worldspace, and
- // r_spritedesc.vright parallel to the viewplane.
- // This will not work if the view direction is very close to straight up or
- // down, because the cross product will be between two nearly parallel
- // vectors and starts to approach an undefined state, so we don't draw if
- // the two vectors are less than 1 degree apart
- dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) because
- // r_spritedesc.vup is 0, 0, 1
- if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848
- return;
- r_spritedesc.vup[0] = 0;
- r_spritedesc.vup[1] = 0;
- r_spritedesc.vup[2] = 1;
- r_spritedesc.vright[0] = vpn[1];
- // CrossProduct (r_spritedesc.vup, vpn,
- r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright)
- r_spritedesc.vright[2] = 0;
- VectorNormalize (r_spritedesc.vright);
- r_spritedesc.vpn[0] = -r_spritedesc.vright[1];
- r_spritedesc.vpn[1] = r_spritedesc.vright[0];
- r_spritedesc.vpn[2] = 0;
- // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
- // r_spritedesc.vpn)
- }
- else if (psprite->type == SPR_ORIENTED)
- {
- // generate the sprite's axes, according to the sprite's world orientation
- AngleVectors (currententity->angles, r_spritedesc.vpn,
- r_spritedesc.vright, r_spritedesc.vup);
- }
- else if (psprite->type == SPR_VP_PARALLEL_ORIENTED)
- {
- // generate the sprite's axes, parallel to the viewplane, but rotated in
- // that plane around the center according to the sprite entity's roll
- // angle. So vpn stays the same, but vright and vup rotate
- angle = currententity->angles[ROLL] * (M_PI*2 / 360);
- sr = sin(angle);
- cr = cos(angle);
- for (i=0 ; i<3 ; i++)
- {
- r_spritedesc.vpn[i] = vpn[i];
- r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr;
- r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr;
- }
- }
- else
- {
- Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type);
- }
- R_RotateSprite (psprite->beamlength);
- R_SetupAndDrawSprite ();
- }
|