|
|
1.1 root 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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.