1 /*
2  * r_sprite.c
3  * $Id: r_sprite.c 4767 2012-06-16 20:48:51Z sezero $
4  *
5  * Copyright (C) 1996-1997  Id Software, Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or (at
10  * your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15  *
16  * See the GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include "quakedef.h"
24 #include "r_local.h"
25 
26 static int		clip_current;
27 static vec5_t		clip_verts[2][MAXWORKINGVERTS];
28 static int		sprite_width, sprite_height;
29 
30 spritedesc_t		r_spritedesc;
31 
32 
33 /*
34 ================
35 R_RotateSprite
36 ================
37 */
R_RotateSprite(float beamlength)38 static void R_RotateSprite (float beamlength)
39 {
40 	vec3_t	vec;
41 
42 	if (beamlength == 0.0)
43 		return;
44 
45 	VectorScale (r_spritedesc.vpn, -beamlength, vec);
46 	VectorAdd (r_entorigin, vec, r_entorigin);
47 	VectorSubtract (modelorg, vec, modelorg);
48 }
49 
50 
51 /*
52 =============
53 R_ClipSpriteFace
54 
55 Clips the winding at clip_verts[clip_current] and changes clip_current
56 Throws out the back side
57 ==============
58 */
R_ClipSpriteFace(int nump,clipplane_t * pclipplane)59 static int R_ClipSpriteFace (int nump, clipplane_t *pclipplane)
60 {
61 	int		i, outcount;
62 	float	dists[MAXWORKINGVERTS+1];
63 	float	frac, clipdist, *pclipnormal;
64 	float	*in, *instep, *outstep, *vert2;
65 
66 	clipdist = pclipplane->dist;
67 	pclipnormal = pclipplane->normal;
68 
69 // calc dists
70 	if (clip_current)
71 	{
72 		in = clip_verts[1][0];
73 		outstep = clip_verts[0][0];
74 		clip_current = 0;
75 	}
76 	else
77 	{
78 		in = clip_verts[0][0];
79 		outstep = clip_verts[1][0];
80 		clip_current = 1;
81 	}
82 
83 	instep = in;
84 	for (i = 0 ; i < nump ; i++, instep += sizeof (vec5_t) / sizeof (float))
85 	{
86 		dists[i] = DotProduct (instep, pclipnormal) - clipdist;
87 	}
88 
89 // handle wraparound case
90 	dists[nump] = dists[0];
91 	memcpy (instep, in, sizeof (vec5_t));
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 			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 */
R_SetupAndDrawSprite(void)136 static void R_SetupAndDrawSprite (void)
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("%s: too many points", __thisfunc__);
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 */
R_GetSpriteframe(msprite_t * psprite)236 static 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_DPrintf ("%s: no such frame %d\n", __thisfunc__, 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
265 	// values 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 */
R_DrawSprite(void)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 = (msprite_t *) 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 ("%s: Bad sprite type %d", __thisfunc__, psprite->type);
395 	}
396 
397 	R_RotateSprite (psprite->beamlength);
398 
399 	R_SetupAndDrawSprite ();
400 }
401 
402