Home | History | Annotate | Download | only in client
      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 "quakedef.h"
     23 #include "r_local.h"
     24 
     25 static int				clip_current;
     26 static vec5_t			clip_verts[2][MAXWORKINGVERTS];
     27 static int				sprite_width, sprite_height;
     28 
     29 spritedesc_t			r_spritedesc;
     30 
     31 
     32 /*
     33 ================
     34 R_RotateSprite
     35 ================
     36 */
     37 void R_RotateSprite (float beamlength)
     38 {
     39 	vec3_t	vec;
     40 
     41 	if (beamlength == 0.0)
     42 		return;
     43 
     44 	VectorScale (r_spritedesc.vpn, -beamlength, vec);
     45 	VectorAdd (r_entorigin, vec, r_entorigin);
     46 	VectorSubtract (modelorg, vec, modelorg);
     47 }
     48 
     49 
     50 /*
     51 =============
     52 R_ClipSpriteFace
     53 
     54 Clips the winding at clip_verts[clip_current] and changes clip_current
     55 Throws out the back side
     56 ==============
     57 */
     58 int R_ClipSpriteFace (int nump, clipplane_t *pclipplane)
     59 {
     60 	int		i, outcount;
     61 	float	dists[MAXWORKINGVERTS+1];
     62 	float	frac, clipdist, *pclipnormal;
     63 	float	*in, *instep, *outstep, *vert2;
     64 
     65 	clipdist = pclipplane->dist;
     66 	pclipnormal = pclipplane->normal;
     67 
     68 // calc dists
     69 	if (clip_current)
     70 	{
     71 		in = clip_verts[1][0];
     72 		outstep = clip_verts[0][0];
     73 		clip_current = 0;
     74 	}
     75 	else
     76 	{
     77 		in = clip_verts[0][0];
     78 		outstep = clip_verts[1][0];
     79 		clip_current = 1;
     80 	}
     81 
     82 	instep = in;
     83 	for (i=0 ; i<nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
     84 	{
     85 		dists[i] = DotProduct (instep, pclipnormal) - clipdist;
     86 	}
     87 
     88 // handle wraparound case
     89 	dists[nump] = dists[0];
     90 	Q_memcpy (instep, in, sizeof (vec5_t));
     91 
     92 
     93 // clip the winding
     94 	instep = in;
     95 	outcount = 0;
     96 
     97 	for (i=0 ; i<nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
     98 	{
     99 		if (dists[i] >= 0)
    100 		{
    101 			Q_memcpy (outstep, instep, sizeof (vec5_t));
    102 			outstep += sizeof (vec5_t) / sizeof (float);
    103 			outcount++;
    104 		}
    105 
    106 		if (dists[i] == 0 || dists[i+1] == 0)
    107 			continue;
    108 
    109 		if ( (dists[i] > 0) == (dists[i+1] > 0) )
    110 			continue;
    111 
    112 	// split it into a new vertex
    113 		frac = dists[i] / (dists[i] - dists[i+1]);
    114 
    115 		vert2 = instep + sizeof (vec5_t) / sizeof (float);
    116 
    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]);
    122 
    123 		outstep += sizeof (vec5_t) / sizeof (float);
    124 		outcount++;
    125 	}
    126 
    127 	return outcount;
    128 }
    129 
    130 
    131 /*
    132 ================
    133 R_SetupAndDrawSprite
    134 ================
    135 */
    136 void R_SetupAndDrawSprite ()
    137 {
    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;
    143 
    144 	dot = DotProduct (r_spritedesc.vpn, modelorg);
    145 
    146 // backface cull
    147 	if (dot >= 0)
    148 		return;
    149 
    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);
    155 
    156 	pverts = clip_verts[0];
    157 
    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;
    163 
    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;
    169 
    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;
    175 
    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;
    181 
    182 // clip to the frustum in worldspace
    183 	nump = 4;
    184 	clip_current = 0;
    185 
    186 	for (i=0 ; i<4 ; i++)
    187 	{
    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");
    193 	}
    194 
    195 // transform vertices into viewspace and project
    196 	pv = &clip_verts[clip_current][0][0];
    197 	r_spritedesc.nearzi = -999999;
    198 
    199 	for (i=0 ; i<nump ; i++)
    200 	{
    201 		VectorSubtract (pv, r_origin, local);
    202 		TransformVector (local, transformed);
    203 
    204 		if (transformed[2] < NEAR_CLIP)
    205 			transformed[2] = NEAR_CLIP;
    206 
    207 		pout = &outverts[i];
    208 		pout->zi = 1.0 / transformed[2];
    209 		if (pout->zi > r_spritedesc.nearzi)
    210 			r_spritedesc.nearzi = pout->zi;
    211 
    212 		pout->s = pv[3];
    213 		pout->t = pv[4];
    214 
    215 		scale = xscale * pout->zi;
    216 		pout->u = (xcenter + scale * transformed[0]);
    217 
    218 		scale = yscale * pout->zi;
    219 		pout->v = (ycenter - scale * transformed[1]);
    220 
    221 		pv += sizeof (vec5_t) / sizeof (pv);
    222 	}
    223 
    224 // draw it
    225 	r_spritedesc.nump = nump;
    226 	r_spritedesc.pverts = outverts;
    227 	D_DrawSprite ();
    228 }
    229 
    230 
    231 /*
    232 ================
    233 R_GetSpriteframe
    234 ================
    235 */
    236 mspriteframe_t *R_GetSpriteframe (msprite_t *psprite)
    237 {
    238 	mspritegroup_t	*pspritegroup;
    239 	mspriteframe_t	*pspriteframe;
    240 	int				i, numframes, frame;
    241 	float			*pintervals, fullinterval, targettime, time;
    242 
    243 	frame = currententity->frame;
    244 
    245 	if ((frame >= psprite->numframes) || (frame < 0))
    246 	{
    247 		Con_Printf ("R_DrawSprite: no such frame %d\n", frame);
    248 		frame = 0;
    249 	}
    250 
    251 	if (psprite->frames[frame].type == SPR_SINGLE)
    252 	{
    253 		pspriteframe = psprite->frames[frame].frameptr;
    254 	}
    255 	else
    256 	{
    257 		pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
    258 		pintervals = pspritegroup->intervals;
    259 		numframes = pspritegroup->numframes;
    260 		fullinterval = pintervals[numframes-1];
    261 
    262 		time = cl.time + currententity->syncbase;
    263 
    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;
    267 
    268 		for (i=0 ; i<(numframes-1) ; i++)
    269 		{
    270 			if (pintervals[i] > targettime)
    271 				break;
    272 		}
    273 
    274 		pspriteframe = pspritegroup->frames[i];
    275 	}
    276 
    277 	return pspriteframe;
    278 }
    279 
    280 
    281 /*
    282 ================
    283 R_DrawSprite
    284 ================
    285 */
    286 void R_DrawSprite (void)
    287 {
    288 	int				i;
    289 	msprite_t		*psprite;
    290 	vec3_t			tvec;
    291 	float			dot, angle, sr, cr;
    292 
    293 	psprite = currententity->model->cache.data;
    294 
    295 	r_spritedesc.pspriteframe = R_GetSpriteframe (psprite);
    296 
    297 	sprite_width = r_spritedesc.pspriteframe->width;
    298 	sprite_height = r_spritedesc.pspriteframe->height;
    299 
    300 // TODO: make this caller-selectable
    301 	if (psprite->type == SPR_FACING_UPRIGHT)
    302 	{
    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)
    331 	}
    332 	else if (psprite->type == SPR_VP_PARALLEL)
    333 	{
    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++)
    338 		{
    339 			r_spritedesc.vup[i] = vup[i];
    340 			r_spritedesc.vright[i] = vright[i];
    341 			r_spritedesc.vpn[i] = vpn[i];
    342 		}
    343 	}
    344 	else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT)
    345 	{
    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)
    369 	}
    370 	else if (psprite->type == SPR_ORIENTED)
    371 	{
    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);
    375 	}
    376 	else if (psprite->type == SPR_VP_PARALLEL_ORIENTED)
    377 	{
    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 = sin(angle);
    383 		cr = cos(angle);
    384 
    385 		for (i=0 ; i<3 ; i++)
    386 		{
    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;
    390 		}
    391 	}
    392 	else
    393 	{
    394 		Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite->type);
    395 	}
    396 
    397 	R_RotateSprite (psprite->beamlength);
    398 
    399 	R_SetupAndDrawSprite ();
    400 }
    401 
    402