2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
25 static int clip_current
;
26 static vec5_t clip_verts
[2][MAXWORKINGVERTS
];
27 static int sprite_width
, sprite_height
;
29 spritedesc_t r_spritedesc
;
37 void R_RotateSprite (float beamlength
)
41 if (beamlength
== 0.0)
44 VectorScale (r_spritedesc
.vpn
, -beamlength
, vec
);
45 VectorAdd (r_entorigin
, vec
, r_entorigin
);
46 VectorSubtract (modelorg
, vec
, modelorg
);
54 Clips the winding at clip_verts[clip_current] and changes clip_current
55 Throws out the back side
58 int R_ClipSpriteFace (int nump
, clipplane_t
*pclipplane
)
61 float dists
[MAXWORKINGVERTS
+1];
62 float frac
, clipdist
, *pclipnormal
;
63 float *in
, *instep
, *outstep
, *vert2
;
65 clipdist
= pclipplane
->dist
;
66 pclipnormal
= pclipplane
->normal
;
71 in
= clip_verts
[1][0];
72 outstep
= clip_verts
[0][0];
77 in
= clip_verts
[0][0];
78 outstep
= clip_verts
[1][0];
83 for (i
=0 ; i
<nump
; i
++, instep
+= sizeof (vec5_t
) / sizeof (float))
85 dists
[i
] = DotProduct (instep
, pclipnormal
) - clipdist
;
88 // handle wraparound case
89 dists
[nump
] = dists
[0];
90 Q_memcpy (instep
, in
, sizeof (vec5_t
));
97 for (i
=0 ; i
<nump
; i
++, instep
+= sizeof (vec5_t
) / sizeof (float))
101 Q_memcpy (outstep
, instep
, sizeof (vec5_t
));
102 outstep
+= sizeof (vec5_t
) / sizeof (float);
106 if (dists
[i
] == 0 || dists
[i
+1] == 0)
109 if ( (dists
[i
] > 0) == (dists
[i
+1] > 0) )
112 // split it into a new vertex
113 frac
= dists
[i
] / (dists
[i
] - dists
[i
+1]);
115 vert2
= instep
+ sizeof (vec5_t
) / sizeof (float);
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]);
123 outstep
+= sizeof (vec5_t
) / sizeof (float);
136 void R_SetupAndDrawSprite ()
139 float dot
, scale
, *pv
;
141 vec3_t left
, up
, right
, down
, transformed
, local
;
142 emitpoint_t outverts
[MAXWORKINGVERTS
+1], *pout
;
144 dot
= DotProduct (r_spritedesc
.vpn
, modelorg
);
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
);
156 pverts
= clip_verts
[0];
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];
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
;
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
;
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];
180 pverts
[3][4] = sprite_height
;
182 // clip to the frustum in worldspace
186 for (i
=0 ; i
<4 ; i
++)
188 nump
= R_ClipSpriteFace (nump
, &view_clipplanes
[i
]);
191 if (nump
>= MAXWORKINGVERTS
)
192 Sys_Error("R_SetupAndDrawSprite: too many points");
195 // transform vertices into viewspace and project
196 pv
= &clip_verts
[clip_current
][0][0];
197 r_spritedesc
.nearzi
= -999999;
199 for (i
=0 ; i
<nump
; i
++)
201 VectorSubtract (pv
, r_origin
, local
);
202 TransformVector (local
, transformed
);
204 if (transformed
[2] < NEAR_CLIP
)
205 transformed
[2] = NEAR_CLIP
;
208 pout
->zi
= 1.0 / transformed
[2];
209 if (pout
->zi
> r_spritedesc
.nearzi
)
210 r_spritedesc
.nearzi
= pout
->zi
;
215 scale
= xscale
* pout
->zi
;
216 pout
->u
= (xcenter
+ scale
* transformed
[0]);
218 scale
= yscale
* pout
->zi
;
219 pout
->v
= (ycenter
- scale
* transformed
[1]);
221 pv
+= sizeof (vec5_t
) / sizeof (*pv
);
225 r_spritedesc
.nump
= nump
;
226 r_spritedesc
.pverts
= outverts
;
236 mspriteframe_t
*R_GetSpriteframe (msprite_t
*psprite
)
238 mspritegroup_t
*pspritegroup
;
239 mspriteframe_t
*pspriteframe
;
240 int i
, numframes
, frame
;
241 float *pintervals
, fullinterval
, targettime
, time
;
243 frame
= currententity
->frame
;
245 if ((frame
>= psprite
->numframes
) || (frame
< 0))
247 Con_Printf ("R_DrawSprite: no such frame %d\n", frame
);
251 if (psprite
->frames
[frame
].type
== SPR_SINGLE
)
253 pspriteframe
= psprite
->frames
[frame
].frameptr
;
257 pspritegroup
= (mspritegroup_t
*)psprite
->frames
[frame
].frameptr
;
258 pintervals
= pspritegroup
->intervals
;
259 numframes
= pspritegroup
->numframes
;
260 fullinterval
= pintervals
[numframes
-1];
262 time
= cl
.time
+ currententity
->syncbase
;
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
;
268 for (i
=0 ; i
<(numframes
-1) ; i
++)
270 if (pintervals
[i
] > targettime
)
274 pspriteframe
= pspritegroup
->frames
[i
];
286 void R_DrawSprite (void)
291 float dot
, angle
, sr
, cr
;
293 psprite
= currententity
->model
->cache
.data
;
295 r_spritedesc
.pspriteframe
= R_GetSpriteframe (psprite
);
297 sprite_width
= r_spritedesc
.pspriteframe
->width
;
298 sprite_height
= r_spritedesc
.pspriteframe
->height
;
300 // TODO: make this caller-selectable
301 if (psprite
->type
== SPR_FACING_UPRIGHT
)
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
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,
332 else if (psprite
->type
== SPR_VP_PARALLEL
)
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
++)
339 r_spritedesc
.vup
[i
] = vup
[i
];
340 r_spritedesc
.vright
[i
] = vright
[i
];
341 r_spritedesc
.vpn
[i
] = vpn
[i
];
344 else if (psprite
->type
== SPR_VP_PARALLEL_UPRIGHT
)
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
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,
370 else if (psprite
->type
== SPR_ORIENTED
)
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
);
376 else if (psprite
->type
== SPR_VP_PARALLEL_ORIENTED
)
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);
385 for (i
=0 ; i
<3 ; i
++)
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
;
394 Sys_Error ("R_DrawSprite: Bad sprite type %d", psprite
->type
);
397 R_RotateSprite (psprite
->beamlength
);
399 R_SetupAndDrawSprite ();