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 402