Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5131 | clevermous | 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 |
||
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 |
||
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 |
||
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 | }3>>3>>(numframes-1)>>>>4> |
||
401 |