summaryrefslogtreecommitdiff
path: root/apps/plugins/sdl/progs/quake/r_sprite.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/sdl/progs/quake/r_sprite.c')
-rw-r--r--apps/plugins/sdl/progs/quake/r_sprite.c401
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/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
25static int clip_current;
26static vec5_t clip_verts[2][MAXWORKINGVERTS];
27static int sprite_width, sprite_height;
28
29spritedesc_t r_spritedesc;
30
31
32/*
33================
34R_RotateSprite
35================
36*/
37void 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=============
52R_ClipSpriteFace
53
54Clips the winding at clip_verts[clip_current] and changes clip_current
55Throws out the back side
56==============
57*/
58int 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================
133R_SetupAndDrawSprite
134================
135*/
136void 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================
233R_GetSpriteframe
234================
235*/
236mspriteframe_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================
283R_DrawSprite
284================
285*/
286void 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