diff options
Diffstat (limited to 'apps/plugins/sdl/progs/quake/r_sprite.c')
-rw-r--r-- | apps/plugins/sdl/progs/quake/r_sprite.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/apps/plugins/sdl/progs/quake/r_sprite.c b/apps/plugins/sdl/progs/quake/r_sprite.c new file mode 100644 index 0000000000..4ab8e0ef4d --- /dev/null +++ b/apps/plugins/sdl/progs/quake/r_sprite.c | |||
@@ -0,0 +1,401 @@ | |||
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 | VectorNormalizeNoRet (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 | VectorNormalizeNoRet (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 | VectorNormalizeNoRet (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 | |||