/* ================= R_SetupEntityLightingGrid ================= */ static void R_SetupEntityLightingGrid( trRefEntity_t *ent ) { vec3_t lightOrigin; int pos[3]; int i, j; float frac[3]; int gridStep[3]; vec3_t direction; float totalFactor; unsigned short *startGridPos; if (r_fullbright->integer || tr.refdef.doFullbright ) { ent->ambientLight[0] = ent->ambientLight[1] = ent->ambientLight[2] = 255.0; ent->directedLight[0] = ent->directedLight[1] = ent->directedLight[2] = 255.0; VectorCopy( tr.sunDirection, ent->lightDir ); return; } if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { // seperate lightOrigins are needed so an object that is // sinking into the ground can still be lit, and so // multi-part models can be lit identically VectorCopy( ent->e.lightingOrigin, lightOrigin ); } else { VectorCopy( ent->e.origin, lightOrigin ); } #define ACCURATE_LIGHTGRID_SAMPLING 1 #if ACCURATE_LIGHTGRID_SAMPLING vec3_t startLightOrigin; VectorCopy( lightOrigin, startLightOrigin ); #endif VectorSubtract( lightOrigin, tr.world->lightGridOrigin, lightOrigin ); for ( i = 0 ; i < 3 ; i++ ) { float v; v = lightOrigin[i]*tr.world->lightGridInverseSize[i]; pos[i] = floor( v ); frac[i] = v - pos[i]; if ( pos[i] < 0 ) { pos[i] = 0; } else if ( pos[i] >= tr.world->lightGridBounds[i] - 1 ) { pos[i] = tr.world->lightGridBounds[i] - 1; } } VectorClear( ent->ambientLight ); VectorClear( ent->directedLight ); VectorClear( direction ); // trilerp the light value gridStep[0] = 1; gridStep[1] = tr.world->lightGridBounds[0]; gridStep[2] = tr.world->lightGridBounds[0] * tr.world->lightGridBounds[1]; startGridPos = tr.world->lightGridArray + pos[0] * gridStep[0] + pos[1] * gridStep[1] + pos[2] * gridStep[2]; #if ACCURATE_LIGHTGRID_SAMPLING vec3_t startGridOrg; VectorCopy( tr.world->lightGridOrigin, startGridOrg ); startGridOrg[0] += pos[0] * tr.world->lightGridSize[0]; startGridOrg[1] += pos[1] * tr.world->lightGridSize[1]; startGridOrg[2] += pos[2] * tr.world->lightGridSize[2]; #endif totalFactor = 0; for ( i = 0 ; i < 8 ; i++ ) { float factor; mgrid_t *data; unsigned short *gridPos; int lat, lng; vec3_t normal; #if ACCURATE_LIGHTGRID_SAMPLING vec3_t gridOrg; VectorCopy( startGridOrg, gridOrg ); #endif factor = 1.0; gridPos = startGridPos; for ( j = 0 ; j < 3 ; j++ ) { if ( i & (1<<j) ) { factor *= frac[j]; gridPos += gridStep[j]; #if ACCURATE_LIGHTGRID_SAMPLING gridOrg[j] += tr.world->lightGridSize[j]; #endif } else { factor *= (1.0 - frac[j]); } } if (gridPos >= tr.world->lightGridArray + tr.world->numGridArrayElements) {//we've gone off the array somehow continue; } data = tr.world->lightGridData + *gridPos; if ( data->styles[0] == LS_NONE ) { continue; // ignore samples in walls } #if 0 if ( !SV_inPVS( startLightOrigin, gridOrg ) ) { continue; } #endif totalFactor += factor; for(j=0;j<MAXLIGHTMAPS;j++) { if (data->styles[j] != LS_NONE) { const byte style= data->styles[j]; ent->ambientLight[0] += factor * data->ambientLight[j][0] * styleColors[style][0] / 255.0f; ent->ambientLight[1] += factor * data->ambientLight[j][1] * styleColors[style][1] / 255.0f; ent->ambientLight[2] += factor * data->ambientLight[j][2] * styleColors[style][2] / 255.0f; ent->directedLight[0] += factor * data->directLight[j][0] * styleColors[style][0] / 255.0f; ent->directedLight[1] += factor * data->directLight[j][1] * styleColors[style][1] / 255.0f; ent->directedLight[2] += factor * data->directLight[j][2] * styleColors[style][2] / 255.0f; } else { break; } } lat = data->latLong[1]; lng = data->latLong[0]; lat *= (FUNCTABLE_SIZE/256); lng *= (FUNCTABLE_SIZE/256); // decode X as cos( lat ) * sin( long ) // decode Y as sin( lat ) * sin( long ) // decode Z as cos( long ) normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; VectorMA( direction, factor, normal, direction ); #if ACCURATE_LIGHTGRID_SAMPLING if ( r_debugLight->integer && ent->e.hModel == -1 ) { //draw refEntity_t refEnt; refEnt.hModel = 0; refEnt.ghoul2 = NULL; refEnt.renderfx = 0; VectorCopy( gridOrg, refEnt.origin ); vectoangles( normal, refEnt.angles ); AnglesToAxis( refEnt.angles, refEnt.axis ); refEnt.reType = RT_MODEL; RE_AddRefEntityToScene( &refEnt ); refEnt.renderfx = RF_DEPTHHACK; refEnt.reType = RT_SPRITE; refEnt.customShader = RE_RegisterShader( "gfx/misc/debugAmbient" ); refEnt.shaderRGBA[0] = data->ambientLight[0][0]; refEnt.shaderRGBA[1] = data->ambientLight[0][1]; refEnt.shaderRGBA[2] = data->ambientLight[0][2]; refEnt.shaderRGBA[3] = 255; refEnt.radius = factor*50+2.0f; // maybe always give it a minimum size? refEnt.rotation = 0; // don't let the sprite wobble around RE_AddRefEntityToScene( &refEnt ); refEnt.reType = RT_LINE; refEnt.customShader = RE_RegisterShader( "gfx/misc/debugArrow" ); refEnt.shaderRGBA[0] = data->directLight[0][0]; refEnt.shaderRGBA[1] = data->directLight[0][1]; refEnt.shaderRGBA[2] = data->directLight[0][2]; refEnt.shaderRGBA[3] = 255; VectorCopy( refEnt.origin, refEnt.oldorigin ); VectorMA( gridOrg, (factor*-255) - 2.0f, normal, refEnt.origin ); // maybe always give it a minimum length refEnt.radius = 1.5f; RE_AddRefEntityToScene( &refEnt ); } #endif } if ( totalFactor > 0 && totalFactor < 0.99 ) { totalFactor = 1.0 / totalFactor; VectorScale( ent->ambientLight, totalFactor, ent->ambientLight ); VectorScale( ent->directedLight, totalFactor, ent->directedLight ); } VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight ); VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight ); VectorNormalize2( direction, ent->lightDir ); }
/* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if (le->forceAlpha) { le->refEntity.renderfx |= RF_FORCE_ENT_ALPHA; le->refEntity.shaderRGBA[3] = le->forceAlpha; } if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float t_e; t = le->endTime - cg.time; if ( t < (SINK_TIME*2) ) { le->refEntity.renderfx |= RF_FORCE_ENT_ALPHA; t_e = (float)((float)(le->endTime - cg.time)/(SINK_TIME*2)); t_e = (int)((t_e)*255); if (t_e > 255) { t_e = 255; } if (t_e < 1) { t_e = 1; } if (le->refEntity.shaderRGBA[3] && t_e > le->refEntity.shaderRGBA[3]) { t_e = le->refEntity.shaderRGBA[3]; } le->refEntity.shaderRGBA[3] = t_e; trap_R_AddRefEntityToScene( &le->refEntity ); } else { trap_R_AddRefEntityToScene( &le->refEntity ); } return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); } trap_R_AddRefEntityToScene( &le->refEntity ); // add a blood trail if ( le->leBounceSoundType == LEBS_BLOOD ) { CG_BloodTrail( le ); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } if (!trace.startsolid) { // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); if (le->bounceSound) { //specified bounce sound (debris) trap_S_StartSound(le->pos.trBase, ENTITYNUM_WORLD, CHAN_AUTO, le->bounceSound); } // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); trap_R_AddRefEntityToScene( &le->refEntity ); } }
/* ==================== CG_AddKamikaze ==================== */ void CG_AddKamikaze( localEntity_t *le ) { refEntity_t *re; refEntity_t shockwave; float c; vec3_t test, axis[3]; int t; re = &le->refEntity; t = cg.time - le->startTime; VectorClear( test ); AnglesToAxis( test, axis ); if (t > KAMI_SHOCKWAVE_STARTTIME && t < KAMI_SHOCKWAVE_ENDTIME) { if (!(le->leFlags & LEF_SOUND1)) { // trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeExplodeSound ); trap_S_StartLocalSound(cgs.media.kamikazeExplodeSound, CHAN_AUTO); le->leFlags |= LEF_SOUND1; } // 1st kamikaze shockwave memset(&shockwave, 0, sizeof(shockwave)); shockwave.hModel = cgs.media.kamikazeShockWave; shockwave.reType = RT_MODEL; shockwave.shaderTime = re->shaderTime; VectorCopy(re->origin, shockwave.origin); c = (float)(t - KAMI_SHOCKWAVE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME); VectorScale( axis[0], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] ); VectorScale( axis[1], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] ); VectorScale( axis[2], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] ); shockwave.nonNormalizedAxes = qtrue; if (t > KAMI_SHOCKWAVEFADE_STARTTIME) { c = (float)(t - KAMI_SHOCKWAVEFADE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVEFADE_STARTTIME); } else { c = 0; } c *= 0xff; shockwave.shaderRGBA[0] = 0xff - c; shockwave.shaderRGBA[1] = 0xff - c; shockwave.shaderRGBA[2] = 0xff - c; shockwave.shaderRGBA[3] = 0xff - c; trap_R_AddRefEntityToScene( &shockwave ); } if (t > KAMI_EXPLODE_STARTTIME && t < KAMI_IMPLODE_ENDTIME) { // explosion and implosion c = ( le->endTime - cg.time ) * le->lifeRate; c *= 0xff; re->shaderRGBA[0] = le->color[0] * c; re->shaderRGBA[1] = le->color[1] * c; re->shaderRGBA[2] = le->color[2] * c; re->shaderRGBA[3] = le->color[3] * c; if( t < KAMI_IMPLODE_STARTTIME ) { c = (float)(t - KAMI_EXPLODE_STARTTIME) / (float)(KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME); } else { if (!(le->leFlags & LEF_SOUND2)) { // trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeImplodeSound ); trap_S_StartLocalSound(cgs.media.kamikazeImplodeSound, CHAN_AUTO); le->leFlags |= LEF_SOUND2; } c = (float)(KAMI_IMPLODE_ENDTIME - t) / (float) (KAMI_IMPLODE_ENDTIME - KAMI_IMPLODE_STARTTIME); } VectorScale( axis[0], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[0] ); VectorScale( axis[1], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[1] ); VectorScale( axis[2], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[2] ); re->nonNormalizedAxes = qtrue; trap_R_AddRefEntityToScene( re ); // add the dlight trap_R_AddLightToScene( re->origin, c * 1000.0, 1.0, 1.0, c ); } if (t > KAMI_SHOCKWAVE2_STARTTIME && t < KAMI_SHOCKWAVE2_ENDTIME) { // 2nd kamikaze shockwave if (le->angles.trBase[0] == 0 && le->angles.trBase[1] == 0 && le->angles.trBase[2] == 0) { le->angles.trBase[0] = random() * 360; le->angles.trBase[1] = random() * 360; le->angles.trBase[2] = random() * 360; } else { c = 0; } memset(&shockwave, 0, sizeof(shockwave)); shockwave.hModel = cgs.media.kamikazeShockWave; shockwave.reType = RT_MODEL; shockwave.shaderTime = re->shaderTime; VectorCopy(re->origin, shockwave.origin); test[0] = le->angles.trBase[0]; test[1] = le->angles.trBase[1]; test[2] = le->angles.trBase[2]; AnglesToAxis( test, axis ); c = (float)(t - KAMI_SHOCKWAVE2_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2_STARTTIME); VectorScale( axis[0], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] ); VectorScale( axis[1], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] ); VectorScale( axis[2], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] ); shockwave.nonNormalizedAxes = qtrue; if (t > KAMI_SHOCKWAVE2FADE_STARTTIME) { c = (float)(t - KAMI_SHOCKWAVE2FADE_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2FADE_STARTTIME); } else { c = 0; } c *= 0xff; shockwave.shaderRGBA[0] = 0xff - c; shockwave.shaderRGBA[1] = 0xff - c; shockwave.shaderRGBA[2] = 0xff - c; shockwave.shaderRGBA[3] = 0xff - c; trap_R_AddRefEntityToScene( &shockwave ); } }
/* =============== CG_LightningBolt Origin will be the exact tag point, which is slightly different than the muzzle point used for determining hits. The cent should be the non-predicted cent if it is from the player, so the endpoint will reflect the simulated strike (lagging the predicted angle) =============== */ static void CG_LightningBolt( centity_t *cent, vec3_t origin, int viewHeight ) { trace_t trace; refEntity_t beam; vec3_t forward; vec3_t muzzlePoint, endPoint; fxParent_t fxParent; if (cent->currentState.weapon != WP_LIGHTNING) { return; } memset( &beam, 0, sizeof( beam ) ); // CPMA "true" lightning if (cg.playerCent && (cent->currentState.number == cg.playerCent->currentState.number) && (cg_trueLightning.value != 0)) { vec3_t angle; int i; for (i = 0; i < 3; i++) { float a = cent->lerpAngles[i] - cg.refdefViewAngles[i]; if (a > 180) { a -= 360; } if (a < -180) { a += 360; } angle[i] = cg.refdefViewAngles[i] + a * (1.0 - cg_trueLightning.value); if (angle[i] < 0) { angle[i] += 360; } if (angle[i] > 360) { angle[i] -= 360; } } AngleVectors(angle, forward, NULL, NULL ); VectorCopy(cent->lerpOrigin, muzzlePoint ); // VectorCopy(cg.refdef.vieworg, muzzlePoint ); } else { // !CPMA AngleVectors( cent->lerpAngles, forward, NULL, NULL ); VectorCopy(cent->lerpOrigin, muzzlePoint ); } // FIXME: crouch //Cana viewheight fix // muzzlePoint[2] += DEFAULT_VIEWHEIGHT; muzzlePoint[2] += viewHeight; VectorMA( muzzlePoint, 14, forward, muzzlePoint ); // project forward by the lightning range VectorMA( muzzlePoint, LIGHTNING_RANGE, forward, endPoint ); // see if it hit a wall CG_Trace( &trace, muzzlePoint, vec3_origin, vec3_origin, endPoint, cent->currentState.number, MASK_SHOT ); #if 1 VectorCopy( origin, fxParent.origin ); VectorSubtract( trace.endpos, origin, fxParent.dir ); fxParent.flags = FXP_ORIGIN | FXP_DIR; trap_FX_Run( cgs.fx.lightning.trail, &fxParent, cent ); #else // this is the endpoint VectorCopy( trace.endpos, beam.oldorigin ); // use the provided origin, even though it may be slightly // different than the muzzle origin VectorCopy( origin, beam.origin ); beam.reType = RT_LIGHTNING; beam.customShader = cgs.media.lightningShader; trap_R_AddRefEntityToScene( &beam ); // add the impact flare if it hit something if ( trace.fraction < 1.0 ) { vec3_t angles; vec3_t dir; VectorSubtract( beam.oldorigin, beam.origin, dir ); VectorNormalize( dir ); memset( &beam, 0, sizeof( beam ) ); beam.hModel = cgs.media.lightningExplosionModel; VectorMA( trace.endpos, -16, dir, beam.origin ); // make a random orientation angles[0] = rand() % 360; angles[1] = rand() % 360; angles[2] = rand() % 360; AnglesToAxis( angles, beam.axis ); trap_R_AddRefEntityToScene( &beam ); } #endif }
/* * CG_SkeletalPoseGetAttachment * Get the tag from the interpolated and transformed pose */ qboolean CG_SkeletalPoseGetAttachment( orientation_t *orient, cgs_skeleton_t *skel, bonepose_t *boneposes, char *bonename ) { int i; quat_t quat; cgs_bone_t *bone; bonepose_t *bonepose; cg_tagmask_t *tagmask; if( !boneposes || !skel ) { CG_Printf( "CG_SkeletalPoseLerpAttachment: Wrong model or boneposes %s\n", bonename ); return qfalse; } tagmask = CG_TagMask( bonename, skel ); // find the appropriate attachment bone if( tagmask ) { bone = skel->bones; for( i = 0; i < skel->numBones; i++, bone++ ) { if( !Q_stricmp( bone->name, tagmask->bonename ) ) break; } } else { bone = skel->bones; for( i = 0; i < skel->numBones; i++, bone++ ) { if( !Q_stricmp( bone->name, bonename ) ) break; } } if( i == skel->numBones ) { CG_Printf( "CG_SkeletalPoseLerpAttachment: no such bone %s\n", bonename ); return qfalse; } // get the desired bone bonepose = boneposes + i; // copy the inverted bone into the tag Quat_Inverse( &bonepose->dualquat[0], quat ); // inverse the tag direction Quat_Matrix( quat, orient->axis ); DualQuat_GetVector( bonepose->dualquat, orient->origin ); // normalize each axis for( i = 0; i < 3; i++ ) VectorNormalizeFast( orient->axis[i] ); // do the offseting if having a tagmask if( tagmask ) { // we want to place a rotated model over this tag, not to rotate the tag, // because all rotations would move. So we create a new orientation for the // model and we position the new orientation in tag space if( tagmask->rotate[YAW] || tagmask->rotate[PITCH] || tagmask->rotate[ROLL] ) { orientation_t modOrient, newOrient; VectorCopy( tagmask->offset, modOrient.origin ); AnglesToAxis( tagmask->rotate, modOrient.axis ); VectorCopy( vec3_origin, newOrient.origin ); Matrix_Identity( newOrient.axis ); CG_MoveToTag( newOrient.origin, newOrient.axis, orient->origin, orient->axis, modOrient.origin, modOrient.axis ); Matrix_Copy( newOrient.axis, orient->axis ); VectorCopy( newOrient.origin, orient->origin ); } else { // offset for( i = 0; i < 3; i++ ) { if( tagmask->offset[i] ) VectorMA( orient->origin, tagmask->offset[i], orient->axis[i], orient->origin ); } } } return qtrue; }
/* =============== Main_MenuDraw =============== */ static void Main_MenuDraw( void ) { refdef_t refdef; refEntity_t ent; vec3_t origin; vec3_t angles; float adjust; float x, y, w, h; vec4_t color = {0.5, 0, 0, 1}; // setup the refdef memset( &refdef, 0, sizeof( refdef ) ); refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); x = 0; y = 0; w = 640; h = 120; UI_AdjustFrom640( &x, &y, &w, &h ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; adjust = 0; // JDC: Kenneth asked me to stop this 1.0 * sin( (float)uis.realtime / 1000 ); refdef.fov_x = 70 + adjust; refdef.fov_y = 19.6875 + adjust; refdef.time = uis.realtime; origin[0] = 300; origin[1] = 0; origin[2] = -32; trap_R_ClearScene(); // add the model memset( &ent, 0, sizeof(ent) ); adjust = 20.0 * sin( (float)uis.realtime / 3000 ); VectorSet( angles, 0, 180 + adjust, 0 ); AnglesToAxis( angles, ent.axis ); ent.hModel = s_main.bannerModel; VectorCopy( origin, ent.origin ); VectorCopy( origin, ent.lightingOrigin ); ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; VectorCopy( ent.origin, ent.oldorigin ); trap_R_AddRefEntityToScene( &ent ); // standard menu drawing Menu_Draw( &s_main.menu ); trap_R_RenderScene( &refdef ); /* if (uis.demoversion) { UI_DrawProportionalString( 320, 372, "DEMO FOR MATURE AUDIENCES DEMO", UI_CENTER|UI_SMALLFONT, color ); UI_DrawString( 320, 400, "Quake III Arena(c) 1999-2000, Id Software, Inc. All Rights Reserved", UI_CENTER|UI_SMALLFONT, color ); } else { UI_DrawString( 320, 450, "Quake III Arena(c) 1999-2000, Id Software, Inc. All Rights Reserved", UI_CENTER|UI_SMALLFONT, color ); }*/ }
static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) { localEntity_t *le; refEntity_t *re; entityState_t *es; vec3_t velocity, xvelocity, origin; vec3_t offset, xoffset; vec3_t v[3]; int t, startTime, step; float waterScale = 1.0f; if ( cg_noProjectileTrail.integer || cg_oldPlasma.integer ) { return; } step = 50; es = ¢->currentState; startTime = cent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 60 - 120 * crandom(); velocity[1] = 40 - 80 * crandom(); velocity[2] = 100 - 200 * crandom(); le->leType = LE_MOVE_SCALE_FADE; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_NONE; le->leMarkType = LEMT_NONE; le->startTime = cg.time; le->endTime = le->startTime + 600; le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time; AnglesToAxis( cent->lerpAngles, v ); offset[0] = 2; offset[1] = 2; offset[2] = 2; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( origin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->shaderTime = cg.time / 1000.0f; re->reType = RT_SPRITE; re->radius = 0.25f; re->customShader = cgs.media.railRingsShader; le->bounceFactor = 0.3f; re->shaderRGBA[0] = wi->flashDlightColor[0] * 63; re->shaderRGBA[1] = wi->flashDlightColor[1] * 63; re->shaderRGBA[2] = wi->flashDlightColor[2] * 63; re->shaderRGBA[3] = 63; le->color[0] = wi->flashDlightColor[0] * 0.2; le->color[1] = wi->flashDlightColor[1] * 0.2; le->color[2] = wi->flashDlightColor[2] * 0.2; le->color[3] = 0.25f; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 1; le->angles.trDelta[1] = 0.5; le->angles.trDelta[2] = 0; }
/* ================= R_LoadMDC ================= */ qboolean R_LoadMDC(model_t * mod, int lod, void *buffer, int bufferSize, const char *modName) { int i, j, k, l; mdcHeader_t *mdcModel; md3Frame_t *mdcFrame; mdcSurface_t *mdcSurf; md3Shader_t *mdcShader; md3Triangle_t *mdcTri; md3St_t *mdcst; md3XyzNormal_t *mdcxyz; mdcXyzCompressed_t *mdcxyzComp; mdcTag_t *mdcTag; mdcTagName_t *mdcTagName; mdvModel_t *mdvModel; mdvFrame_t *frame; mdvSurface_t *surf, *surface; srfTriangle_t *tri; mdvVertex_t *v; mdvSt_t *st; mdvTag_t *tag; mdvTagName_t *tagName; short *ps; int version; int size; mdcModel = (mdcHeader_t *) buffer; version = LittleLong(mdcModel->version); if(version != MDC_VERSION) { ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MDC_VERSION); return qfalse; } mod->type = MOD_MESH; size = LittleLong(mdcModel->ofsEnd); mod->dataSize += size; mdvModel = mod->mdv[lod] = ri.Hunk_Alloc(sizeof(mdvModel_t), h_low); LL(mdcModel->ident); LL(mdcModel->version); LL(mdcModel->numFrames); LL(mdcModel->numTags); LL(mdcModel->numSurfaces); LL(mdcModel->ofsFrames); LL(mdcModel->ofsTags); LL(mdcModel->ofsSurfaces); LL(mdcModel->ofsEnd); LL(mdcModel->ofsEnd); LL(mdcModel->flags); LL(mdcModel->numSkins); if(mdcModel->numFrames < 1) { ri.Printf(PRINT_WARNING, "R_LoadMDC: '%s' has no frames\n", modName); return qfalse; } // swap all the frames mdvModel->numFrames = mdcModel->numFrames; mdvModel->frames = frame = ri.Hunk_Alloc(sizeof(*frame) * mdcModel->numFrames, h_low); mdcFrame = (md3Frame_t *) ((byte *) mdcModel + mdcModel->ofsFrames); for(i = 0; i < mdcModel->numFrames; i++, frame++, mdcFrame++) { #if 1 // RB: ET HACK if(strstr(mod->name, "sherman") || strstr(mod->name, "mg42")) { frame->radius = 256; for(j = 0; j < 3; j++) { frame->bounds[0][j] = 128; frame->bounds[1][j] = -128; frame->localOrigin[j] = LittleFloat(mdcFrame->localOrigin[j]); } } else #endif { frame->radius = LittleFloat(mdcFrame->radius); for(j = 0; j < 3; j++) { frame->bounds[0][j] = LittleFloat(mdcFrame->bounds[0][j]); frame->bounds[1][j] = LittleFloat(mdcFrame->bounds[1][j]); frame->localOrigin[j] = LittleFloat(mdcFrame->localOrigin[j]); } } } // swap all the tags mdvModel->numTags = mdcModel->numTags; mdvModel->tags = tag = ri.Hunk_Alloc(sizeof(*tag) * (mdcModel->numTags * mdcModel->numFrames), h_low); mdcTag = (mdcTag_t *) ((byte *) mdcModel + mdcModel->ofsTags); for(i = 0; i < mdcModel->numTags * mdcModel->numFrames; i++, tag++, mdcTag++) { vec3_t angles; for(j = 0; j < 3; j++) { tag->origin[j] = (float)LittleShort(mdcTag->xyz[j]) * MD3_XYZ_SCALE; angles[j] = (float)LittleShort(mdcTag->angles[j]) * MDC_TAG_ANGLE_SCALE; } AnglesToAxis(angles, tag->axis); } mdvModel->tagNames = tagName = ri.Hunk_Alloc(sizeof(*tagName) * (mdcModel->numTags), h_low); mdcTagName = (mdcTagName_t *) ((byte *) mdcModel + mdcModel->ofsTagNames); for(i = 0; i < mdcModel->numTags; i++, tagName++, mdcTagName++) { Q_strncpyz(tagName->name, mdcTagName->name, sizeof(tagName->name)); } // swap all the surfaces mdvModel->numSurfaces = mdcModel->numSurfaces; mdvModel->surfaces = surf = ri.Hunk_Alloc(sizeof(*surf) * mdcModel->numSurfaces, h_low); mdcSurf = (mdcSurface_t *) ((byte *) mdcModel + mdcModel->ofsSurfaces); for(i = 0; i < mdcModel->numSurfaces; i++) { LL(mdcSurf->ident); LL(mdcSurf->flags); LL(mdcSurf->numBaseFrames); LL(mdcSurf->numCompFrames); LL(mdcSurf->numShaders); LL(mdcSurf->numTriangles); LL(mdcSurf->ofsTriangles); LL(mdcSurf->numVerts); LL(mdcSurf->ofsShaders); LL(mdcSurf->ofsSt); LL(mdcSurf->ofsXyzNormals); LL(mdcSurf->ofsXyzNormals); LL(mdcSurf->ofsXyzCompressed); LL(mdcSurf->ofsFrameBaseFrames); LL(mdcSurf->ofsFrameCompFrames); LL(mdcSurf->ofsEnd); if(mdcSurf->numVerts > SHADER_MAX_VERTEXES) { ri.Error(ERR_DROP, "R_LoadMDC: %s has more than %i verts on a surface (%i)", modName, SHADER_MAX_VERTEXES, mdcSurf->numVerts); } if(mdcSurf->numTriangles > SHADER_MAX_TRIANGLES) { ri.Error(ERR_DROP, "R_LoadMDC: %s has more than %i triangles on a surface (%i)", modName, SHADER_MAX_TRIANGLES, mdcSurf->numTriangles); } // change to surface identifier surf->surfaceType = SF_MDV; // give pointer to model for Tess_SurfaceMDX surf->model = mdvModel; // copy surface name Q_strncpyz(surf->name, mdcSurf->name, sizeof(surf->name)); // lowercase the surface name so skin compares are faster Q_strlwr(surf->name); // strip off a trailing _1 or _2 // this is a crutch for q3data being a mess j = strlen(surf->name); if(j > 2 && surf->name[j - 2] == '_') { surf->name[j - 2] = 0; } // register the shaders /* surf->numShaders = md3Surf->numShaders; surf->shaders = shader = ri.Hunk_Alloc(sizeof(*shader) * md3Surf->numShaders, h_low); md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders); for(j = 0; j < md3Surf->numShaders; j++, shader++, md3Shader++) { shader_t *sh; sh = R_FindShader(md3Shader->name, SHADER_3D_DYNAMIC, qtrue); if(sh->defaultShader) { shader->shaderIndex = 0; } else { shader->shaderIndex = sh->index; } } */ // only consider the first shader mdcShader = (md3Shader_t *) ((byte *) mdcSurf + mdcSurf->ofsShaders); surf->shader = R_FindShader(mdcShader->name, SHADER_3D_DYNAMIC, qtrue); // swap all the triangles surf->numTriangles = mdcSurf->numTriangles; surf->triangles = tri = ri.Hunk_Alloc(sizeof(*tri) * mdcSurf->numTriangles, h_low); mdcTri = (md3Triangle_t *) ((byte *) mdcSurf + mdcSurf->ofsTriangles); for(j = 0; j < mdcSurf->numTriangles; j++, tri++, mdcTri++) { tri->indexes[0] = LittleLong(mdcTri->indexes[0]); tri->indexes[1] = LittleLong(mdcTri->indexes[1]); tri->indexes[2] = LittleLong(mdcTri->indexes[2]); } R_CalcSurfaceTriangleNeighbors(surf->numTriangles, surf->triangles); // swap all the XyzNormals mdcxyz = (md3XyzNormal_t *) ((byte *) mdcSurf + mdcSurf->ofsXyzNormals); for(j = 0; j < mdcSurf->numVerts * mdcSurf->numBaseFrames; j++, mdcxyz++) { mdcxyz->xyz[0] = LittleShort(mdcxyz->xyz[0]); mdcxyz->xyz[1] = LittleShort(mdcxyz->xyz[1]); mdcxyz->xyz[2] = LittleShort(mdcxyz->xyz[2]); mdcxyz->normal = LittleShort(mdcxyz->normal); } // swap all the XyzCompressed mdcxyzComp = (mdcXyzCompressed_t *) ((byte *) mdcSurf + mdcSurf->ofsXyzCompressed); for(j = 0; j < mdcSurf->numVerts * mdcSurf->numCompFrames; j++, mdcxyzComp++) { LL(mdcxyzComp->ofsVec); } // swap the frameBaseFrames ps = (short *)((byte *) mdcSurf + mdcSurf->ofsFrameBaseFrames); for(j = 0; j < mdcModel->numFrames; j++, ps++) { *ps = LittleShort(*ps); } // swap the frameCompFrames ps = (short *)((byte *) mdcSurf + mdcSurf->ofsFrameCompFrames); for(j = 0; j < mdcModel->numFrames; j++, ps++) { *ps = LittleShort(*ps); } surf->numVerts = mdcSurf->numVerts; surf->verts = v = ri.Hunk_Alloc(sizeof(*v) * (mdcSurf->numVerts * mdcModel->numFrames), h_low); for(j = 0; j < mdcModel->numFrames; j++) { int baseFrame; int compFrame; baseFrame = (int) *((short *)((byte *) mdcSurf + mdcSurf->ofsFrameBaseFrames) + j); mdcxyz = (md3XyzNormal_t *)((byte *) mdcSurf + mdcSurf->ofsXyzNormals + baseFrame * mdcSurf->numVerts * sizeof(md3XyzNormal_t)); if(mdcSurf->numCompFrames > 0) { compFrame = (int) *((short *)((byte *) mdcSurf + mdcSurf->ofsFrameCompFrames) + j); if(compFrame >= 0) { mdcxyzComp = (mdcXyzCompressed_t *) ((byte *) mdcSurf + mdcSurf->ofsXyzCompressed + compFrame * mdcSurf->numVerts * sizeof(mdcXyzCompressed_t)); } } for(k = 0; k < mdcSurf->numVerts; k++, v++, mdcxyz++) { v->xyz[0] = LittleShort(mdcxyz->xyz[0]) * MD3_XYZ_SCALE; v->xyz[1] = LittleShort(mdcxyz->xyz[1]) * MD3_XYZ_SCALE; v->xyz[2] = LittleShort(mdcxyz->xyz[2]) * MD3_XYZ_SCALE; if(mdcSurf->numCompFrames > 0 && compFrame >= 0) { vec3_t ofsVec; vec3_t normal; R_MDC_DecodeXyzCompressed(LittleShort(mdcxyzComp->ofsVec), ofsVec, normal); VectorAdd(v->xyz, ofsVec, v->xyz); mdcxyzComp++; } } } // swap all the ST surf->st = st = ri.Hunk_Alloc(sizeof(*st) * mdcSurf->numVerts, h_low); mdcst = (md3St_t *) ((byte *) mdcSurf + mdcSurf->ofsSt); for(j = 0; j < mdcSurf->numVerts; j++, mdcst++, st++) { st->st[0] = LittleFloat(mdcst->st[0]); st->st[1] = LittleFloat(mdcst->st[1]); } // find the next surface mdcSurf = (mdcSurface_t *) ((byte *) mdcSurf + mdcSurf->ofsEnd); surf++; } #if 1 // create VBO surfaces from md3 surfaces { growList_t vboSurfaces; srfVBOMDVMesh_t *vboSurf; byte *data; int dataSize; int dataOfs; vec4_t tmp; GLuint ofsTexCoords; GLuint ofsTangents; GLuint ofsBinormals; GLuint ofsNormals; GLuint sizeXYZ; GLuint sizeTangents; GLuint sizeBinormals; GLuint sizeNormals; int vertexesNum; int f; Com_InitGrowList(&vboSurfaces, 10); for(i = 0, surf = mdvModel->surfaces; i < mdvModel->numSurfaces; i++, surf++) { // calc tangent spaces { const float *v0, *v1, *v2; const float *t0, *t1, *t2; vec3_t tangent; vec3_t binormal; vec3_t normal; for(j = 0, v = surf->verts; j < (surf->numVerts * mdvModel->numFrames); j++, v++) { VectorClear(v->tangent); VectorClear(v->binormal); VectorClear(v->normal); } for(f = 0; f < mdvModel->numFrames; f++) { for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) { v0 = surf->verts[surf->numVerts * f + tri->indexes[0]].xyz; v1 = surf->verts[surf->numVerts * f + tri->indexes[1]].xyz; v2 = surf->verts[surf->numVerts * f + tri->indexes[2]].xyz; t0 = surf->st[tri->indexes[0]].st; t1 = surf->st[tri->indexes[1]].st; t2 = surf->st[tri->indexes[2]].st; #if 1 R_CalcTangentSpace(tangent, binormal, normal, v0, v1, v2, t0, t1, t2); #else R_CalcNormalForTriangle(normal, v0, v1, v2); R_CalcTangentsForTriangle(tangent, binormal, v0, v1, v2, t0, t1, t2); #endif for(k = 0; k < 3; k++) { float *v; v = surf->verts[surf->numVerts * f + tri->indexes[k]].tangent; VectorAdd(v, tangent, v); v = surf->verts[surf->numVerts * f + tri->indexes[k]].binormal; VectorAdd(v, binormal, v); v = surf->verts[surf->numVerts * f + tri->indexes[k]].normal; VectorAdd(v, normal, v); } } } for(j = 0, v = surf->verts; j < (surf->numVerts * mdvModel->numFrames); j++, v++) { VectorNormalize(v->tangent); VectorNormalize(v->binormal); VectorNormalize(v->normal); } } //ri.Printf(PRINT_ALL, "...calculating MDC mesh VBOs ( '%s', %i verts %i tris )\n", surf->name, surf->numVerts, surf->numTriangles); // create surface vboSurf = ri.Hunk_Alloc(sizeof(*vboSurf), h_low); Com_AddToGrowList(&vboSurfaces, vboSurf); vboSurf->surfaceType = SF_VBO_MDVMESH; vboSurf->mdvModel = mdvModel; vboSurf->mdvSurface = surf; vboSurf->numIndexes = surf->numTriangles * 3; vboSurf->numVerts = surf->numVerts; /* vboSurf->vbo = R_CreateVBO2(va("staticWorldMesh_vertices %i", vboSurfaces.currentElements), numVerts, optimizedVerts, ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BINORMAL | ATTR_NORMAL | ATTR_COLOR); */ vboSurf->ibo = R_CreateIBO2(va("staticMDCMesh_IBO %s", surf->name), surf->numTriangles, surf->triangles, VBO_USAGE_STATIC); // create VBO vertexesNum = surf->numVerts; dataSize = (surf->numVerts * mdvModel->numFrames * sizeof(vec4_t) * 4) + // xyz, tangent, binormal, normal (surf->numVerts * sizeof(vec4_t)); // texcoords data = ri.Malloc(dataSize); dataOfs = 0; // feed vertex XYZ for(f = 0; f < mdvModel->numFrames; f++) { for(j = 0; j < vertexesNum; j++) { for(k = 0; k < 3; k++) { tmp[k] = surf->verts[f * vertexesNum + j].xyz[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } if(f == 0) { sizeXYZ = dataOfs; } } // feed vertex texcoords ofsTexCoords = dataOfs; for(j = 0; j < vertexesNum; j++) { for(k = 0; k < 2; k++) { tmp[k] = surf->st[j].st[k]; } tmp[2] = 0; tmp[3] = 1; Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } // feed vertex tangents ofsTangents = dataOfs; for(f = 0; f < mdvModel->numFrames; f++) { for(j = 0; j < vertexesNum; j++) { for(k = 0; k < 3; k++) { tmp[k] = surf->verts[f * vertexesNum + j].tangent[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } if(f == 0) { sizeTangents = dataOfs - ofsTangents; } } // feed vertex binormals ofsBinormals = dataOfs; for(f = 0; f < mdvModel->numFrames; f++) { for(j = 0; j < vertexesNum; j++) { for(k = 0; k < 3; k++) { tmp[k] = surf->verts[f * vertexesNum + j].binormal[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } if(f == 0) { sizeBinormals = dataOfs - ofsBinormals; } } // feed vertex normals ofsNormals = dataOfs; for(f = 0; f < mdvModel->numFrames; f++) { for(j = 0; j < vertexesNum; j++) { for(k = 0; k < 3; k++) { tmp[k] = surf->verts[f * vertexesNum + j].normal[k]; } tmp[3] = 1; Com_Memcpy(data + dataOfs, (vec_t *) tmp, sizeof(vec4_t)); dataOfs += sizeof(vec4_t); } if(f == 0) { sizeNormals = dataOfs - ofsNormals; } } vboSurf->vbo = R_CreateVBO(va("staticMDCMesh_VBO '%s'", surf->name), data, dataSize, VBO_USAGE_STATIC); vboSurf->vbo->ofsXYZ = 0; vboSurf->vbo->ofsTexCoords = ofsTexCoords; vboSurf->vbo->ofsLightCoords = ofsTexCoords; vboSurf->vbo->ofsTangents = ofsTangents; vboSurf->vbo->ofsBinormals = ofsBinormals; vboSurf->vbo->ofsNormals = ofsNormals; vboSurf->vbo->sizeXYZ = sizeXYZ; vboSurf->vbo->sizeTangents = sizeTangents; vboSurf->vbo->sizeBinormals = sizeBinormals; vboSurf->vbo->sizeNormals = sizeNormals; ri.Free(data); } // move VBO surfaces list to hunk mdvModel->numVBOSurfaces = vboSurfaces.currentElements; mdvModel->vboSurfaces = ri.Hunk_Alloc(mdvModel->numVBOSurfaces * sizeof(*mdvModel->vboSurfaces), h_low); for(i = 0; i < mdvModel->numVBOSurfaces; i++) { mdvModel->vboSurfaces[i] = (srfVBOMDVMesh_t *) Com_GrowListElement(&vboSurfaces, i); } Com_DestroyGrowList(&vboSurfaces); } #endif return qtrue; }
void CGCam_Update( void ) { int i; qboolean checkFollow = qfalse; qboolean checkTrack = qfalse; // Apply new roff data to the camera as needed if ( client_camera.info_state & CAMERA_ROFFING ) { CGCam_Roff(); } //Check for a zoom if (client_camera.info_state & CAMERA_ACCEL) { // x = x0 + vt + 0.5*a*t*t float actualFOV_X = client_camera.FOV; float sanityMin = 1, sanityMax = 180; float t = (cg.time - client_camera.FOV_time)*0.001; // mult by 0.001 cuz otherwise t is too darned big float fovDuration = client_camera.FOV_duration; #ifndef FINAL_BUILD if (cg_roffval4.integer) { fovDuration = cg_roffval4.integer; } #endif if ( client_camera.FOV_time + fovDuration < cg.time ) { client_camera.info_state &= ~CAMERA_ACCEL; } else { float initialPosVal = client_camera.FOV2; float velVal = client_camera.FOV_vel; float accVal = client_camera.FOV_acc; #ifndef FINAL_BUILD if (cg_roffdebug.integer) { if (fabs(cg_roffval1.value) > 0.001f) { initialPosVal = cg_roffval1.value; } if (fabs(cg_roffval2.value) > 0.001f) { velVal = cg_roffval2.value; } if (fabs(cg_roffval3.value) > 0.001f) { accVal = cg_roffval3.value; } } #endif float initialPos = initialPosVal; float vel = velVal*t; float acc = 0.5*accVal*t*t; actualFOV_X = initialPos + vel + acc; if (cg_roffdebug.integer) { Com_Printf("%d: fovaccel from %2.1f using vel = %2.4f, acc = %2.4f (current fov calc = %5.6f)\n", cg.time, initialPosVal, velVal, accVal, actualFOV_X); } if (actualFOV_X < sanityMin) { actualFOV_X = sanityMin; } else if (actualFOV_X > sanityMax) { actualFOV_X = sanityMax; } client_camera.FOV = actualFOV_X; } CG_CalcFOVFromX( actualFOV_X ); } else if ( client_camera.info_state & CAMERA_ZOOMING ) { float actualFOV_X; if ( client_camera.FOV_time + client_camera.FOV_duration < cg.time ) { actualFOV_X = client_camera.FOV = client_camera.FOV2; client_camera.info_state &= ~CAMERA_ZOOMING; } else { actualFOV_X = client_camera.FOV + (( ( client_camera.FOV2 - client_camera.FOV ) ) / client_camera.FOV_duration ) * ( cg.time - client_camera.FOV_time ); } CG_CalcFOVFromX( actualFOV_X ); } else { CG_CalcFOVFromX( client_camera.FOV ); } //Check for roffing angles if ( (client_camera.info_state & CAMERA_ROFFING) && !(client_camera.info_state & CAMERA_FOLLOWING) ) { if (client_camera.info_state & CAMERA_CUT) { // we're doing a cut, so just go to the new angles. none of this hifalutin lerping business. for ( i = 0; i < 3; i++ ) { cg.refdefViewAngles[i] = AngleNormalize360( ( client_camera.angles[i] + client_camera.angles2[i] ) ); } } else { for ( i = 0; i < 3; i++ ) { cg.refdefViewAngles[i] = client_camera.angles[i] + ( client_camera.angles2[i] / client_camera.pan_duration ) * ( cg.time - client_camera.pan_time ); } } } else if ( client_camera.info_state & CAMERA_PANNING ) { if (client_camera.info_state & CAMERA_CUT) { // we're doing a cut, so just go to the new angles. none of this hifalutin lerping business. for ( i = 0; i < 3; i++ ) { cg.refdefViewAngles[i] = AngleNormalize360( ( client_camera.angles[i] + client_camera.angles2[i] ) ); } } else { //Note: does not actually change the camera's angles until the pan time is done! if ( client_camera.pan_time + client_camera.pan_duration < cg.time ) {//finished panning for ( i = 0; i < 3; i++ ) { client_camera.angles[i] = AngleNormalize360( ( client_camera.angles[i] + client_camera.angles2[i] ) ); } client_camera.info_state &= ~CAMERA_PANNING; VectorCopy(client_camera.angles, cg.refdefViewAngles ); } else {//still panning for ( i = 0; i < 3; i++ ) { //NOTE: does not store the resultant angle in client_camera.angles until pan is done cg.refdefViewAngles[i] = client_camera.angles[i] + ( client_camera.angles2[i] / client_camera.pan_duration ) * ( cg.time - client_camera.pan_time ); } } } } else { checkFollow = qtrue; } //Check for movement if ( client_camera.info_state & CAMERA_MOVING ) { //NOTE: does not actually move the camera until the movement time is done! if ( client_camera.move_time + client_camera.move_duration < cg.time ) { VectorCopy( client_camera.origin2, client_camera.origin ); client_camera.info_state &= ~CAMERA_MOVING; VectorCopy( client_camera.origin, cg.refdef.vieworg ); } else { if (client_camera.info_state & CAMERA_CUT) { // we're doing a cut, so just go to the new origin. none of this fancypants lerping stuff. for ( i = 0; i < 3; i++ ) { cg.refdef.vieworg[i] = client_camera.origin2[i]; } } else { for ( i = 0; i < 3; i++ ) { cg.refdef.vieworg[i] = client_camera.origin[i] + (( ( client_camera.origin2[i] - client_camera.origin[i] ) ) / client_camera.move_duration ) * ( cg.time - client_camera.move_time ); } } } } else { checkTrack = qtrue; } if ( checkFollow ) { if ( client_camera.info_state & CAMERA_FOLLOWING ) {//This needs to be done after camera movement CGCam_FollowUpdate(); } VectorCopy(client_camera.angles, cg.refdefViewAngles ); } if ( checkTrack ) { if ( client_camera.info_state & CAMERA_TRACKING ) {//This has to run AFTER Follow if the camera is following a cameraGroup CGCam_TrackUpdate(); } VectorCopy( client_camera.origin, cg.refdef.vieworg ); } //Bar fading if ( client_camera.info_state & CAMERA_BAR_FADING ) { CGCam_UpdateBarFade(); } //Normal fading - separate call because can finish after camera is disabled CGCam_UpdateFade(); //Update shaking if there's any //CGCam_UpdateSmooth( cg.refdef.vieworg, cg.refdefViewAngles ); CGCam_UpdateShake( cg.refdef.vieworg, cg.refdefViewAngles ); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); }
void Wolfcam_AddViewWeapon (void) { vec3_t origin; centity_t *cent; const entityState_t *es; refEntity_t hand; const clientInfo_t *ci; float fovOffset; vec3_t angles; const weaponInfo_t *weapon; float gunX; int fov; if (!wolfcam_following) { return; } cent = &cg_entities[wcg.clientNum]; es = ¢->currentState; if (!cg_drawGun.integer) { if (es->eFlags & EF_FIRING && es->weapon == WP_LIGHTNING) { // special hack for lightning gun... VectorCopy( cg.refdef.vieworg, origin ); VectorMA( origin, -8, cg.refdef.viewaxis[2], origin ); CG_LightningBolt( &cg_entities[es->number], origin ); //FIXME is this adding the sound twice? CG_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cg_weapons[es->weapon].firingSound ); } return; } // don't draw if testing a gun model if (cg.testGun) { return; } gunX = cg_gun_x.value; if (es->weapon == WP_GRAPPLING_HOOK) { gunX += 8.9; } fov = cg_fov.integer; //FIXME option // drop gun lower at higher fov if (fov > 90) { fovOffset = -0.2 * (fov - 90); } else { fovOffset = 0; } CG_RegisterWeapon(es->weapon); weapon = &cg_weapons[es->weapon]; memset(&hand, 0, sizeof(hand)); // set up gun position Wolfcam_CalculateWeaponPosition(hand.origin, angles); VectorMA( hand.origin, gunX, cg.refdef.viewaxis[0], hand.origin ); VectorMA( hand.origin, cg_gun_y.value, cg.refdef.viewaxis[1], hand.origin ); VectorMA( hand.origin, (cg_gun_z.value+fovOffset), cg.refdef.viewaxis[2], hand.origin ); AnglesToAxis( angles, hand.axis ); // map torso animations to weapon animations //cg_gun_frame.integer = 1; if ( cg_gun_frame.integer ) { // development tool hand.frame = hand.oldframe = cg_gun_frame.integer; hand.backlerp = 0; } else { //if (0) { // these are just for calling CG_PlayerAnimation() int legsOld; int legs; float legsBackLerp; int torsoOld; int torso; float torsoBackLerp; // get clientinfo for animation map ci = &cgs.clientinfo[ cent->currentState.clientNum ]; // animations weren't run for /follow'ed player CG_PlayerAnimation(cent, &legsOld, &legs, &legsBackLerp, &torsoOld, &torso, &torsoBackLerp); hand.frame = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.frame ); hand.oldframe = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.oldFrame ); hand.backlerp = cent->pe.torso.backlerp; } hand.hModel = weapon->handsModel; hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON | RF_MINLIGHT; // add everything onto the hand Wolfcam_AddPlayerWeapon(&hand, cent, cgs.clientinfo[cent->currentState.clientNum].team); }
//---------------------------- void CEmitter::UpdateAngles() { VectorMA( mAngles, theFxHelper.mFrameTime * 0.01f, mAngleDelta, mAngles ); // was 0.001f, but then you really have to jack up the delta to even notice anything AnglesToAxis( mAngles, mRefEnt.axis ); }
void Wolfcam_AddPlayerWeapon (const refEntity_t *parent, centity_t *cent, int team) { refEntity_t gun; refEntity_t barrel; refEntity_t flash; vec3_t angles; weapon_t weaponNum; const weaponInfo_t *weapon; centity_t *nonPredictedCent; // int col clientInfo_t *ci; float flashSize; float dlight[3]; float f; qboolean revertColors = qfalse; vec3_t origColor1; vec3_t origColor2; if (!cent->inCurrentSnapshot) { // this can happen with /follow which can stay in victim position // (frag hover) return; } ci = &cgs.clientinfo[ cent->currentState.clientNum ]; weaponNum = cent->currentState.weapon; if (weaponNum <= WP_NONE || weaponNum >= WP_NUM_WEAPONS) { return; } CG_RegisterWeapon( weaponNum ); weapon = &cg_weapons[weaponNum]; // add the weapon memset( &gun, 0, sizeof( gun ) ); VectorCopy( parent->lightingOrigin, gun.lightingOrigin ); gun.shadowPlane = parent->shadowPlane; gun.renderfx = parent->renderfx; // set custom shading for railgun refire rate if (0) { //( ps ) { } else { if (weaponNum == WP_RAILGUN) { qboolean teamRail; qboolean enemyRail; if (cg_railUseOwnColors.integer && CG_IsUs(ci)) { VectorCopy(ci->color1, origColor1); VectorCopy(ci->color2, origColor2); VectorCopy(cg.color1, ci->color1); VectorCopy(cg.color2, ci->color2); revertColors = qtrue; } teamRail = CG_IsTeammate(ci); enemyRail = CG_IsEnemy(ci); if (cgs.gametype < GT_TEAM) { if (!CG_IsUs(ci)) { if (*cg_enemyRailItemColor.string) { SC_ByteVec3ColorFromCvar(gun.shaderRGBA, &cg_enemyRailItemColor); gun.shaderRGBA[3] = 255; } else { gun.shaderRGBA[0] = 255 * ci->color1[0]; gun.shaderRGBA[1] = 255 * ci->color1[1]; gun.shaderRGBA[2] = 255 * ci->color1[2]; gun.shaderRGBA[3] = 255; } } else { gun.shaderRGBA[0] = 255 * ci->color1[0]; gun.shaderRGBA[1] = 255 * ci->color1[1]; gun.shaderRGBA[2] = 255 * ci->color1[2]; gun.shaderRGBA[3] = 255; } } else { // team game if (!CG_IsUs(ci) && teamRail) { if (cg_teamRailItemColorTeam.integer) { if (ci->team == TEAM_RED) { SC_ByteVec3ColorFromCvar(gun.shaderRGBA, &cg_weaponRedTeamColor); } else { SC_ByteVec3ColorFromCvar(gun.shaderRGBA, &cg_weaponBlueTeamColor); } gun.shaderRGBA[3] = 255; } else if (*cg_teamRailItemColor.string) { SC_ByteVec3ColorFromCvar(gun.shaderRGBA, &cg_teamRailItemColor); gun.shaderRGBA[3] = 255; } else { gun.shaderRGBA[0] = 255 * ci->color1[0]; gun.shaderRGBA[1] = 255 * ci->color1[1]; gun.shaderRGBA[2] = 255 * ci->color1[2]; gun.shaderRGBA[3] = 255; } } else if (!CG_IsUs(ci) && enemyRail) { if (cg_enemyRailItemColorTeam.integer) { if (ci->team == TEAM_RED) { SC_ByteVec3ColorFromCvar(gun.shaderRGBA, &cg_weaponRedTeamColor); } else { SC_ByteVec3ColorFromCvar(gun.shaderRGBA, &cg_weaponBlueTeamColor); } gun.shaderRGBA[3] = 255; } else if (*cg_enemyRailItemColor.string) { SC_ByteVec3ColorFromCvar(gun.shaderRGBA, &cg_enemyRailItemColor); gun.shaderRGBA[3] = 255; } else { gun.shaderRGBA[0] = 255 * ci->color1[0]; gun.shaderRGBA[1] = 255 * ci->color1[1]; gun.shaderRGBA[2] = 255 * ci->color1[2]; gun.shaderRGBA[3] = 255; } } else { // us gun.shaderRGBA[0] = 255 * ci->color1[0]; gun.shaderRGBA[1] = 255 * ci->color1[1]; gun.shaderRGBA[2] = 255 * ci->color1[2]; gun.shaderRGBA[3] = 255; } } // end weapon == WP_RAILGUN //cent->pe.muzzleFlashTime //Com_Printf("yes....\n"); //f = cg.time - (cent->pe.muzzleFlashTime + 1500); f = cg.time - (cent->pe.muzzleFlashTime + 1460); // hack //Com_Printf("f %f\n", f); if (f < 0) { f = 1.0 - (f / -1500); gun.shaderRGBA[0] *= 0.314 * f; gun.shaderRGBA[1] *= 0.314 * f; gun.shaderRGBA[2] *= 0.314 * f; } } } gun.hModel = weapon->weaponModel; if (!gun.hModel) { //Com_Printf("no gun model '%s'\n", weapNamesCasual[weaponNum]); //FIXME grapple returns here //FIXME fx //CG_PositionEntityOnTag(&gun, parent, parent->hModel, "tag_weapon"); //CG_CheckFxWeaponFlash(cent, weaponNum, gun.origin); //return; } CG_PositionEntityOnTag( &gun, parent, parent->hModel, "tag_weapon"); CG_ScaleModel(&gun, cg_gunSize.value); // custom weapon shaders { vmCvar_t *firstPersonShaders[MAX_WEAPONS] = { NULL, &cg_firstPersonShaderWeaponGauntlet, &cg_firstPersonShaderWeaponMachineGun, &cg_firstPersonShaderWeaponShotgun, &cg_firstPersonShaderWeaponGrenadeLauncher, &cg_firstPersonShaderWeaponRocketLauncher, &cg_firstPersonShaderWeaponLightningGun, &cg_firstPersonShaderWeaponRailGun, &cg_firstPersonShaderWeaponPlasmaGun, &cg_firstPersonShaderWeaponBFG, &cg_firstPersonShaderWeaponGrapplingHook, &cg_firstPersonShaderWeaponNailGun, &cg_firstPersonShaderWeaponProximityLauncher, &cg_firstPersonShaderWeaponChainGun, &cg_firstPersonShaderWeaponHeavyMachineGun }; if (firstPersonShaders[weaponNum] && *(firstPersonShaders[weaponNum]->string)) { gun.customShader = trap_R_RegisterShader(firstPersonShaders[weaponNum]->string); } } if (gun.hModel) { if (cg_drawGun.integer > 2) { gun.customShader = cgs.media.ghostWeaponShader; gun.shaderRGBA[0] = 255; gun.shaderRGBA[1] = 255; gun.shaderRGBA[2] = 255; gun.shaderRGBA[3] = 255; } CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups ); } // add the spinning barrel if ( weapon->barrelModel ) { memset( &barrel, 0, sizeof( barrel ) ); VectorCopy( parent->lightingOrigin, barrel.lightingOrigin ); barrel.shadowPlane = parent->shadowPlane; barrel.renderfx = parent->renderfx; barrel.hModel = weapon->barrelModel; angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = CG_MachinegunSpinAngle( cent ); AnglesToAxis( angles, barrel.axis ); CG_PositionRotatedEntityOnTag( &barrel, &gun, weapon->weaponModel, "tag_barrel" ); CG_ScaleModel(&barrel, cg_gunSize.value); if (cg_drawGun.integer > 2) { barrel.customShader = cgs.media.ghostWeaponShader; barrel.shaderRGBA[0] = 255; barrel.shaderRGBA[1] = 255; barrel.shaderRGBA[2] = 255; barrel.shaderRGBA[3] = 255; } CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups ); } // make sure we aren't looking at cg.predictedPlayerEntity for LG nonPredictedCent = &cg_entities[cent->currentState.clientNum]; #if 0 // if the index of the nonPredictedCent is not the same as the clientNum // then this is a fake player (like on teh single player podiums), so // go ahead and use the cent if( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) { nonPredictedCent = cent; //Com_Printf("fake player %d -> %d\n", nonPredictedCent - cg_entities, cent->currentState.clientNum); } #endif // add the flash //if ( ( weaponNum == WP_LIGHTNING || weaponNum == WP_GAUNTLET || weaponNum == WP_GRAPPLING_HOOK ) && (cent->currentState.eFlags & EF_FIRING)) { if ( ( weaponNum == WP_LIGHTNING || weaponNum == WP_GAUNTLET || weaponNum == WP_GRAPPLING_HOOK ) && (nonPredictedCent->currentState.eFlags & EF_FIRING)) { // && ( nonPredictedCent->currentState.eFlags & EF_FIRING ) ) { // continuous flash } else { //int ftime; if (weaponNum == WP_LIGHTNING && cent->currentState.eFlags & EF_FIRING) { //Com_Printf("%f wtf ps %p\n", cg.ftime, ps); } // impulse flash //if ( cg.time - cent->pe.muzzleFlashTime > MUZZLE_FLASH_TIME && !cent->pe.railgunFlash ) { if ( cg.time - nonPredictedCent->pe.muzzleFlashTime > MUZZLE_FLASH_TIME && !nonPredictedCent->pe.railgunFlash ) { //Com_Printf("returning for %d (%d)\n", cent - cg_entities, cent->currentState.number); //goto bolt; // not called, in case code changes if (revertColors) { VectorCopy(origColor1, ci->color1); VectorCopy(origColor2, ci->color2); } return; } } memset( &flash, 0, sizeof( flash ) ); VectorCopy( parent->lightingOrigin, flash.lightingOrigin ); flash.shadowPlane = parent->shadowPlane; flash.renderfx = parent->renderfx; flash.hModel = weapon->flashModel; /* if (weaponNum == WP_HEAVY_MACHINEGUN) { flash.hModel = cg_weapons[WP_MACHINEGUN].flashModel; } */ if (!flash.hModel) { //Com_Printf("no flash model '%s'\n", weapNamesCasual[weaponNum]); //FIXME fx //return; } angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = crandom() * 10; AnglesToAxis( angles, flash.axis ); // colorize the railgun blast if ( weaponNum == WP_RAILGUN ) { //clientInfo_t *ci; //ci = &cgs.clientinfo[ cent->currentState.clientNum ]; if (cg_railUseOwnColors.integer && CG_IsUs(ci)) { flash.shaderRGBA[0] = 255 * cg.color1[0]; flash.shaderRGBA[1] = 255 * cg.color1[1]; flash.shaderRGBA[2] = 255 * cg.color1[2]; } else { flash.shaderRGBA[0] = 255 * ci->color1[0]; flash.shaderRGBA[1] = 255 * ci->color1[1]; flash.shaderRGBA[2] = 255 * ci->color1[2]; } } if (0) { //(weapon->hasFlashScript) { //CG_RunQ3mmeFlashScript(weapon, dlight, flash.shaderRGBA, &flashSize); //VectorCopy(flash.origin, ScriptVars.origin); //CG_RunQ3mmeScript((char *)weapon->flashScript); //return; } else { dlight[0] = weapon->flashDlightColor[0]; dlight[1] = weapon->flashDlightColor[1]; dlight[2] = weapon->flashDlightColor[2]; /* flash.shaderRGBA[0] = 255; flash.shaderRGBA[1] = 255; flash.shaderRGBA[2] = 255; flash.shaderRGBA[3] = 0; */ flashSize = 300 + (rand()&31); } CG_PositionRotatedEntityOnTag( &flash, &gun, weapon->weaponModel, "tag_flash"); //Com_Printf("ps:%d %p\n", ps != NULL, cent); if (0) { //(cent == &cg.predictedPlayerEntity && !cg.renderingThirdPerson && !ps) { // don't run flash script twice for first person view } else if (EffectScripts.weapons[weaponNum].hasFlashScript) { //CG_RunQ3mmeFlashScript(weapon, dlight, flash.shaderRGBA, &flashSize); //memset(&ScriptVars, 0, sizeof(ScriptVars)); //CG_Printf("addplayerweapon() flash script cent %d\n", cent - cg_entities); CG_ResetScriptVars(); CG_CopyPlayerDataToScriptData(cent); VectorCopy(flash.origin, ScriptVars.origin); VectorCopy(flash.origin, ScriptVars.parentOrigin); VectorCopy(cent->lastFlashIntervalPosition, ScriptVars.lastIntervalPosition); ScriptVars.lastIntervalTime = cent->lastFlashIntervalTime; VectorCopy(cent->lastFlashDistancePosition, ScriptVars.lastDistancePosition); ScriptVars.lastDistanceTime = cent->lastFlashDistanceTime; CG_RunQ3mmeScript((char *)EffectScripts.weapons[weaponNum].flashScript, NULL); VectorCopy(ScriptVars.lastIntervalPosition, cent->lastFlashIntervalPosition); cent->lastFlashIntervalTime = ScriptVars.lastIntervalTime; VectorCopy(ScriptVars.lastDistancePosition, cent->lastFlashDistancePosition); cent->lastFlashDistanceTime = ScriptVars.lastDistanceTime; //return; } if (!cg_muzzleFlash.integer) { // pass } else { if (flash.hModel) { CG_AddRefEntity(&flash); } } // bolt: if (1) { // add lightning bolt if (1) { CG_LightningBolt( nonPredictedCent, flash.origin ); //Com_Printf("adding bolt\n"); // add rail trail CG_SpawnRailTrail( cent, flash.origin ); //if ((dlight[0] || dlight[1] || dlight[2]) && !weapon->hasFlashScript) { if ((dlight[0] || dlight[1] || dlight[2]) && !EffectScripts.weapons[weaponNum].hasFlashScript) { trap_R_AddLightToScene(flash.origin, flashSize, dlight[0], dlight[1], dlight[2]); } } } else { //Com_Printf("%f no...\n", cg.ftime); } if (revertColors) { VectorCopy(origColor1, ci->color1); VectorCopy(origColor2, ci->color2); } }
/* ================== CG_Buildable ================== */ void CG_Buildable( centity_t *cent ) { refEntity_t ent; entityState_t *es = ¢->currentState; vec3_t angles; vec3_t surfNormal, xNormal, mins, maxs; vec3_t refNormal = { 0.0f, 0.0f, 1.0f }; float rotAngle; buildableTeam_t team = (buildableTeam_t)BG_FindTeamForBuildable( es->modelindex ); float scale; int health; float healthScale; //must be before EF_NODRAW check if( team == BIT_ALIENS ) CG_Creep( cent ); // if set to invisible, skip if( es->eFlags & EF_NODRAW ) { if( CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); return; } memset ( &ent, 0, sizeof( ent ) ); VectorCopy( cent->lerpOrigin, ent.origin ); VectorCopy( cent->lerpOrigin, ent.oldorigin ); VectorCopy( cent->lerpOrigin, ent.lightingOrigin ); VectorCopy( es->origin2, surfNormal ); VectorCopy( es->angles, angles ); BG_FindBBoxForBuildable( es->modelindex, mins, maxs ); if( es->pos.trType == TR_STATIONARY ) CG_PositionAndOrientateBuildable( angles, ent.origin, surfNormal, es->number, mins, maxs, ent.axis, ent.origin ); //offset on the Z axis if required VectorMA( ent.origin, BG_FindZOffsetForBuildable( es->modelindex ), surfNormal, ent.origin ); VectorCopy( ent.origin, ent.oldorigin ); // don't positionally lerp at all VectorCopy( ent.origin, ent.lightingOrigin ); ent.hModel = cg_buildables[ es->modelindex ].models[ 0 ]; if( !( es->generic1 & B_SPAWNED_TOGGLEBIT ) ) { sfxHandle_t prebuildSound = cgs.media.humanBuildablePrebuild; if( team == BIT_HUMANS ) { ent.customShader = cgs.media.humanSpawningShader; prebuildSound = cgs.media.humanBuildablePrebuild; } else if( team == BIT_ALIENS ) prebuildSound = cgs.media.alienBuildablePrebuild; //trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, prebuildSound, 0 ); } CG_BuildableAnimation( cent, &ent.oldframe, &ent.frame, &ent.backlerp ); //rescale the model scale = BG_FindModelScaleForBuildable( es->modelindex ); if( scale != 1.0f ) { VectorScale( ent.axis[ 0 ], scale, ent.axis[ 0 ] ); VectorScale( ent.axis[ 1 ], scale, ent.axis[ 1 ] ); VectorScale( ent.axis[ 2 ], scale, ent.axis[ 2 ] ); ent.nonNormalizedAxes = qtrue; } else ent.nonNormalizedAxes = qfalse; //add to refresh list trap_R_AddRefEntityToScene( &ent ); CrossProduct( surfNormal, refNormal, xNormal ); VectorNormalize( xNormal ); rotAngle = RAD2DEG( acos( DotProduct( surfNormal, refNormal ) ) ); //turret barrel bit if( cg_buildables[ es->modelindex ].models[ 1 ] ) { refEntity_t turretBarrel; vec3_t flatAxis[ 3 ]; memset( &turretBarrel, 0, sizeof( turretBarrel ) ); turretBarrel.hModel = cg_buildables[ es->modelindex ].models[ 1 ]; CG_PositionEntityOnTag( &turretBarrel, &ent, ent.hModel, "tag_turret" ); VectorCopy( cent->lerpOrigin, turretBarrel.lightingOrigin ); AnglesToAxis( es->angles2, flatAxis ); RotatePointAroundVector( turretBarrel.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); RotatePointAroundVector( turretBarrel.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); RotatePointAroundVector( turretBarrel.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); turretBarrel.oldframe = ent.oldframe; turretBarrel.frame = ent.frame; turretBarrel.backlerp = ent.backlerp; turretBarrel.customShader = ent.customShader; if( scale != 1.0f ) { VectorScale( turretBarrel.axis[ 0 ], scale, turretBarrel.axis[ 0 ] ); VectorScale( turretBarrel.axis[ 1 ], scale, turretBarrel.axis[ 1 ] ); VectorScale( turretBarrel.axis[ 2 ], scale, turretBarrel.axis[ 2 ] ); turretBarrel.nonNormalizedAxes = qtrue; } else turretBarrel.nonNormalizedAxes = qfalse; trap_R_AddRefEntityToScene( &turretBarrel ); } //turret barrel bit if( cg_buildables[ es->modelindex ].models[ 2 ] ) { refEntity_t turretTop; vec3_t flatAxis[ 3 ]; vec3_t swivelAngles; memset( &turretTop, 0, sizeof( turretTop ) ); VectorCopy( es->angles2, swivelAngles ); swivelAngles[ PITCH ] = 0.0f; turretTop.hModel = cg_buildables[ es->modelindex ].models[ 2 ]; CG_PositionRotatedEntityOnTag( &turretTop, &ent, ent.hModel, "tag_turret" ); VectorCopy( cent->lerpOrigin, turretTop.lightingOrigin ); AnglesToAxis( swivelAngles, flatAxis ); RotatePointAroundVector( turretTop.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); RotatePointAroundVector( turretTop.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); RotatePointAroundVector( turretTop.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); turretTop.oldframe = ent.oldframe; turretTop.frame = ent.frame; turretTop.backlerp = ent.backlerp; turretTop.customShader = ent.customShader; if( scale != 1.0f ) { VectorScale( turretTop.axis[ 0 ], scale, turretTop.axis[ 0 ] ); VectorScale( turretTop.axis[ 1 ], scale, turretTop.axis[ 1 ] ); VectorScale( turretTop.axis[ 2 ], scale, turretTop.axis[ 2 ] ); turretTop.nonNormalizedAxes = qtrue; } else turretTop.nonNormalizedAxes = qfalse; trap_R_AddRefEntityToScene( &turretTop ); } switch( cg.predictedPlayerState.weapon ) { case WP_ABUILD: case WP_ABUILD2: case WP_HBUILD: case WP_HBUILD2: if( BG_FindTeamForBuildable( es->modelindex ) == BG_FindTeamForWeapon( cg.predictedPlayerState.weapon ) ) CG_BuildableHealthBar( cent ); break; default: break; } //weapon effects for turrets if( es->eFlags & EF_FIRING ) { weaponInfo_t *weapon = &cg_weapons[ es->weapon ]; if( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME || BG_FindProjTypeForBuildable( es->modelindex ) == WP_TESLAGEN ) { if( weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ] || weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ] || weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ) { trap_R_AddLightToScene( cent->lerpOrigin, 320, 1.25 + (rand() & 31) / 128, weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ], weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ], weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ], 0, 0 ); } } if( weapon->wim[ WPM_PRIMARY ].firingSound ) { /*trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->wim[ WPM_PRIMARY ].firingSound, 0 );*/ } else if( weapon->readySound ) { //trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->readySound, 0 ); } } health = es->generic1 & ~( B_POWERED_TOGGLEBIT | B_DCCED_TOGGLEBIT | B_SPAWNED_TOGGLEBIT ); healthScale = (float)health / B_HEALTH_SCALE; if( healthScale < cent->lastBuildableHealthScale && ( es->generic1 & B_SPAWNED_TOGGLEBIT ) ) { if( cent->lastBuildableDamageSoundTime + BUILDABLE_SOUND_PERIOD < cg.time ) { if( team == BIT_HUMANS ) { int i = rand( ) % 4; trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.humanBuildableDamage[ i ] ); } else if( team == BIT_ALIENS ) trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienBuildableDamage ); cent->lastBuildableDamageSoundTime = cg.time; } } cent->lastBuildableHealthScale = healthScale; //smoke etc for damaged buildables CG_BuildableParticleEffects( cent ); }
/* ================== CG_Buildable ================== */ void CG_Buildable( centity_t *cent ) { refEntity_t ent; entityState_t *es = ¢->currentState; vec3_t angles; vec3_t surfNormal, xNormal, mins, maxs; vec3_t refNormal = { 0.0f, 0.0f, 1.0f }; float rotAngle; team_t team = BG_Buildable( es->modelindex )->team; float scale; int health; //must be before EF_NODRAW check if( team == TEAM_ALIENS ) CG_Creep( cent ); // if set to invisible, skip if( es->eFlags & EF_NODRAW ) { if( CG_IsParticleSystemValid( ¢->buildablePS ) ) CG_DestroyParticleSystem( ¢->buildablePS ); return; } memset ( &ent, 0, sizeof( ent ) ); VectorCopy( cent->lerpOrigin, ent.origin ); VectorCopy( cent->lerpOrigin, ent.oldorigin ); VectorCopy( cent->lerpOrigin, ent.lightingOrigin ); VectorCopy( es->origin2, surfNormal ); VectorCopy( es->angles, angles ); BG_BuildableBoundingBox( es->modelindex, mins, maxs ); if( es->pos.trType == TR_STATIONARY ) { // Positioning a buildable involves potentially up to two traces, and // seeing as buildables rarely move, we cache the results and recalculate // only if the buildable moves or changes orientation if( VectorCompare( cent->buildableCache.cachedOrigin, cent->lerpOrigin ) && VectorCompare( cent->buildableCache.cachedNormal, surfNormal ) ) { VectorCopy( cent->buildableCache.axis[ 0 ], ent.axis[ 0 ] ); VectorCopy( cent->buildableCache.axis[ 1 ], ent.axis[ 1 ] ); VectorCopy( cent->buildableCache.axis[ 2 ], ent.axis[ 2 ] ); VectorCopy( cent->buildableCache.origin, ent.origin ); } else { CG_PositionAndOrientateBuildable( angles, ent.origin, surfNormal, es->number, mins, maxs, ent.axis, ent.origin ); VectorCopy( ent.axis[ 0 ], cent->buildableCache.axis[ 0 ] ); VectorCopy( ent.axis[ 1 ], cent->buildableCache.axis[ 1 ] ); VectorCopy( ent.axis[ 2 ], cent->buildableCache.axis[ 2 ] ); VectorCopy( ent.origin, cent->buildableCache.origin ); VectorCopy( cent->lerpOrigin, cent->buildableCache.cachedOrigin ); VectorCopy( surfNormal, cent->buildableCache.cachedNormal ); } } //offset on the Z axis if required VectorMA( ent.origin, BG_BuildableConfig( es->modelindex )->zOffset, surfNormal, ent.origin ); VectorCopy( ent.origin, ent.oldorigin ); // don't positionally lerp at all VectorCopy( ent.origin, ent.lightingOrigin ); ent.hModel = cg_buildables[ es->modelindex ].models[ 0 ]; if( !( es->eFlags & EF_B_SPAWNED ) ) { sfxHandle_t prebuildSound = cgs.media.humanBuildablePrebuild; if( team == TEAM_HUMANS ) { ent.customShader = cgs.media.humanSpawningShader; prebuildSound = cgs.media.humanBuildablePrebuild; } else if( team == TEAM_ALIENS ) prebuildSound = cgs.media.alienBuildablePrebuild; trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, prebuildSound ); } CG_BuildableAnimation( cent, &ent.oldframe, &ent.frame, &ent.backlerp ); //rescale the model scale = BG_BuildableConfig( es->modelindex )->modelScale; if( scale != 1.0f ) { VectorScale( ent.axis[ 0 ], scale, ent.axis[ 0 ] ); VectorScale( ent.axis[ 1 ], scale, ent.axis[ 1 ] ); VectorScale( ent.axis[ 2 ], scale, ent.axis[ 2 ] ); ent.nonNormalizedAxes = qtrue; } else ent.nonNormalizedAxes = qfalse; if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) ent.customShader = cgs.media.redBuildShader; //add to refresh list trap_R_AddRefEntityToScene( &ent ); CrossProduct( surfNormal, refNormal, xNormal ); VectorNormalize( xNormal ); rotAngle = RAD2DEG( acos( DotProduct( surfNormal, refNormal ) ) ); //turret barrel bit if( cg_buildables[ es->modelindex ].models[ 1 ] ) { refEntity_t turretBarrel; vec3_t flatAxis[ 3 ]; memset( &turretBarrel, 0, sizeof( turretBarrel ) ); turretBarrel.hModel = cg_buildables[ es->modelindex ].models[ 1 ]; CG_PositionEntityOnTag( &turretBarrel, &ent, ent.hModel, "tag_turret" ); VectorCopy( cent->lerpOrigin, turretBarrel.lightingOrigin ); AnglesToAxis( es->angles2, flatAxis ); RotatePointAroundVector( turretBarrel.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); RotatePointAroundVector( turretBarrel.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); RotatePointAroundVector( turretBarrel.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); turretBarrel.oldframe = ent.oldframe; turretBarrel.frame = ent.frame; turretBarrel.backlerp = ent.backlerp; turretBarrel.customShader = ent.customShader; if( scale != 1.0f ) { VectorScale( turretBarrel.axis[ 0 ], scale, turretBarrel.axis[ 0 ] ); VectorScale( turretBarrel.axis[ 1 ], scale, turretBarrel.axis[ 1 ] ); VectorScale( turretBarrel.axis[ 2 ], scale, turretBarrel.axis[ 2 ] ); turretBarrel.nonNormalizedAxes = qtrue; } else turretBarrel.nonNormalizedAxes = qfalse; if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) turretBarrel.customShader = cgs.media.redBuildShader; trap_R_AddRefEntityToScene( &turretBarrel ); } //turret barrel bit if( cg_buildables[ es->modelindex ].models[ 2 ] ) { refEntity_t turretTop; vec3_t flatAxis[ 3 ]; vec3_t swivelAngles; memset( &turretTop, 0, sizeof( turretTop ) ); VectorCopy( es->angles2, swivelAngles ); swivelAngles[ PITCH ] = 0.0f; turretTop.hModel = cg_buildables[ es->modelindex ].models[ 2 ]; CG_PositionRotatedEntityOnTag( &turretTop, &ent, ent.hModel, "tag_turret" ); VectorCopy( cent->lerpOrigin, turretTop.lightingOrigin ); AnglesToAxis( swivelAngles, flatAxis ); RotatePointAroundVector( turretTop.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); RotatePointAroundVector( turretTop.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); RotatePointAroundVector( turretTop.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); turretTop.oldframe = ent.oldframe; turretTop.frame = ent.frame; turretTop.backlerp = ent.backlerp; turretTop.customShader = ent.customShader; if( scale != 1.0f ) { VectorScale( turretTop.axis[ 0 ], scale, turretTop.axis[ 0 ] ); VectorScale( turretTop.axis[ 1 ], scale, turretTop.axis[ 1 ] ); VectorScale( turretTop.axis[ 2 ], scale, turretTop.axis[ 2 ] ); turretTop.nonNormalizedAxes = qtrue; } else turretTop.nonNormalizedAxes = qfalse; if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) turretTop.customShader = cgs.media.redBuildShader; trap_R_AddRefEntityToScene( &turretTop ); } //weapon effects for turrets if( es->eFlags & EF_FIRING ) { weaponInfo_t *weapon = &cg_weapons[ es->weapon ]; if( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME || BG_Buildable( es->modelindex )->turretProjType == WP_TESLAGEN ) { if( weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ] || weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ] || weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ) { trap_R_AddLightToScene( cent->lerpOrigin, 300 + ( rand( ) & 31 ), weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ], weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ], weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ); } } if( weapon->wim[ WPM_PRIMARY ].firingSound ) { trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->wim[ WPM_PRIMARY ].firingSound ); } else if( weapon->readySound ) trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->readySound ); } health = es->generic1; if( health < cent->lastBuildableHealth && ( es->eFlags & EF_B_SPAWNED ) ) { if( cent->lastBuildableDamageSoundTime + BUILDABLE_SOUND_PERIOD < cg.time ) { if( team == TEAM_HUMANS ) { int i = rand( ) % 4; trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.humanBuildableDamage[ i ] ); } else if( team == TEAM_ALIENS ) trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienBuildableDamage ); cent->lastBuildableDamageSoundTime = cg.time; } } cent->lastBuildableHealth = health; //smoke etc for damaged buildables CG_BuildableParticleEffects( cent ); }
void CG_Spotlight( centity_t *cent, float *color, vec3_t realstart, vec3_t lightDir, int segs, float range, int startWidth, float coneAngle, int flags ) { int i, j; vec3_t start, traceEnd, proj; vec3_t right, up; vec3_t v1, v2; vec3_t startvec, endvec; // the vectors to rotate around lightDir to create the circles vec3_t conevec; vec3_t start_points[MAX_SPOT_SEGS], end_points[MAX_SPOT_SEGS]; vec3_t coreright; polyVert_t verts[MAX_SPOT_SEGS * 4]; // x4 for 4 verts per poly polyVert_t plugVerts[MAX_SPOT_SEGS]; vec3_t endCenter; polyVert_t coreverts[4]; trace_t tr; float alpha; float radius = 0.0; // TTimo might be used uninitialized float coreEndRadius; qboolean capStart = qtrue; float hitDist; // the actual distance of the trace impact (0 is no hit) float beamLen; // actual distance of the drawn beam float endAlpha = 0.0; vec4_t colorNorm; // normalized color vector refEntity_t ent; vec3_t angles; VectorCopy( realstart, start ); // normalize color colorNorm[3] = 0; // store normalize multiplier in alpha index for ( i = 0; i < 3; i++ ) { if ( color[i] > colorNorm[3] ) { colorNorm[3] = color[i]; // find largest color value in RGB } } if ( colorNorm[3] != 1 ) { // it needs to be boosted VectorMA( color, 1.0 / colorNorm[3], color, colorNorm ); // FIXME: div by 0 } else { VectorCopy( color, colorNorm ); } colorNorm[3] = color[3]; if ( flags & SL_NOSTARTCAP ) { capStart = qfalse; } if ( startWidth == 0 ) { // cone, not cylinder capStart = qfalse; } if ( flags & SL_LOCKTRACETORANGE ) { VectorMA( start, range, lightDir, traceEnd ); // trace out to 'range' } else { VectorMA( start, MAX_SPOT_RANGE, lightDir, traceEnd ); // trace all the way out to max dist } // first trace to see if anything is hit if ( flags & SL_NOTRACE ) { tr.fraction = 1.0; // force no hit } else { if ( flags & SL_TRACEWORLDONLY ) { CG_Trace( &tr, start, NULL, NULL, traceEnd, -1, CONTENTS_SOLID ); } else { CG_Trace( &tr, start, NULL, NULL, traceEnd, -1, MASK_SHOT ); } // CG_Trace( &tr, start, NULL, NULL, traceEnd, -1, MASK_ALL &~(CONTENTS_MONSTERCLIP|CONTENTS_AREAPORTAL|CONTENTS_CLUSTERPORTAL)); } if ( tr.fraction < 1.0 ) { hitDist = beamLen = MAX_SPOT_RANGE * tr.fraction; if ( beamLen > range ) { beamLen = range; } } else { hitDist = 0; beamLen = range; } if ( flags & SL_LOCKUV ) { if ( beamLen < range ) { endAlpha = 255.0f * ( color[3] - ( color[3] * beamLen / range ) ); } } if ( segs >= MAX_SPOT_SEGS ) { segs = MAX_SPOT_SEGS - 1; } // TODO: adjust segs based on r_lodbias // TODO: move much of this to renderer // model at base if ( cent->currentState.modelindex ) { memset( &ent, 0, sizeof( ent ) ); ent.frame = 0; ent.oldframe = 0; ent.backlerp = 0; VectorCopy( cent->lerpOrigin, ent.origin ); VectorCopy( cent->lerpOrigin, ent.oldorigin ); ent.hModel = cgs.gameModels[cent->currentState.modelindex]; // AnglesToAxis( cent->lerpAngles, ent.axis ); vectoangles( lightDir, angles ); AnglesToAxis( angles, ent.axis ); trap_R_AddRefEntityToScene( &ent ); memcpy( ¢->refEnt, &ent, sizeof( refEntity_t ) ); // push start out a bit so the beam fits to the front of the base model VectorMA( start, 14, lightDir, start ); } //// BEAM PerpendicularVector( up, lightDir ); CrossProduct( lightDir, up, right ); // find first vert of the start VectorScale( right, startWidth, startvec ); // find the first vert of the end RotatePointAroundVector( conevec, up, lightDir, -coneAngle ); VectorMA( startvec, beamLen, conevec, endvec ); // this applies the offset of the start diameter before finding the end points VectorScale( lightDir, beamLen, endCenter ); VectorSubtract( endCenter, endvec, coreverts[3].xyz ); // get a vector of the radius out at the end for the core to use coreEndRadius = VectorLength( coreverts[3].xyz ); #define CORESCALE 0.6f // // generate the flat beam 'core' // if ( !( flags & SL_NOCORE ) ) { VectorSubtract( start, cg.refdef.vieworg, v1 ); VectorNormalize( v1 ); VectorSubtract( traceEnd, cg.refdef.vieworg, v2 ); VectorNormalize( v2 ); CrossProduct( v1, v2, coreright ); VectorNormalize( coreright ); memset( &coreverts[0], 0, 4 * sizeof( polyVert_t ) ); VectorMA( start, startWidth * 0.5f, coreright, coreverts[0].xyz ); VectorMA( start, -startWidth * 0.5f, coreright, coreverts[1].xyz ); VectorMA( endCenter, -coreEndRadius * CORESCALE, coreright, coreverts[2].xyz ); VectorAdd( start, coreverts[2].xyz, coreverts[2].xyz ); VectorMA( endCenter, coreEndRadius * CORESCALE, coreright, coreverts[3].xyz ); VectorAdd( start, coreverts[3].xyz, coreverts[3].xyz ); for ( i = 0; i < 4; i++ ) { coreverts[i].modulate[0] = color[0] * 200.0f; coreverts[i].modulate[1] = color[1] * 200.0f; coreverts[i].modulate[2] = color[2] * 200.0f; coreverts[i].modulate[3] = color[3] * 200.0f; if ( i > 1 ) { coreverts[i].modulate[3] = 0; } } trap_R_AddPolyToScene( cgs.media.spotLightBeamShader, 4, &coreverts[0] ); } // // generate the beam cylinder // for ( i = 0; i <= segs; i++ ) { RotatePointAroundVector( start_points[i], lightDir, startvec, ( 360.0f / (float)segs ) * i ); VectorAdd( start_points[i], start, start_points[i] ); RotatePointAroundVector( end_points[i], lightDir, endvec, ( 360.0f / (float)segs ) * i ); VectorAdd( end_points[i], start, end_points[i] ); } for ( i = 0; i < segs; i++ ) { j = ( i * 4 ); VectorCopy( start_points[i], verts[( i * 4 )].xyz ); verts[j].st[0] = 0; verts[j].st[1] = 1; verts[j].modulate[0] = color[0] * 255.0f; verts[j].modulate[1] = color[1] * 255.0f; verts[j].modulate[2] = color[2] * 255.0f; verts[j].modulate[3] = color[3] * 255.0f; j++; VectorCopy( end_points[i], verts[j].xyz ); verts[j].st[0] = 0; verts[j].st[1] = 0; verts[j].modulate[0] = color[0] * 255.0f; verts[j].modulate[1] = color[1] * 255.0f; verts[j].modulate[2] = color[2] * 255.0f; verts[j].modulate[3] = endAlpha; j++; VectorCopy( end_points[i + 1], verts[j].xyz ); verts[j].st[0] = 1; verts[j].st[1] = 0; verts[j].modulate[0] = color[0] * 255.0f; verts[j].modulate[1] = color[1] * 255.0f; verts[j].modulate[2] = color[2] * 255.0f; verts[j].modulate[3] = endAlpha; j++; VectorCopy( start_points[i + 1], verts[j].xyz ); verts[j].st[0] = 1; verts[j].st[1] = 1; verts[j].modulate[0] = color[0] * 255.0f; verts[j].modulate[1] = color[1] * 255.0f; verts[j].modulate[2] = color[2] * 255.0f; verts[j].modulate[3] = color[3] * 255.0f; if ( capStart ) { VectorCopy( start_points[i], plugVerts[i].xyz ); plugVerts[i].st[0] = 0; plugVerts[i].st[1] = 0; plugVerts[i].modulate[0] = color[0] * 255.0f; plugVerts[i].modulate[1] = color[1] * 255.0f; plugVerts[i].modulate[2] = color[2] * 255.0f; plugVerts[i].modulate[3] = color[3] * 255.0f; } } trap_R_AddPolysToScene( cgs.media.spotLightBeamShader, 4, &verts[0], segs ); // plug up the start circle if ( capStart ) { trap_R_AddPolyToScene( cgs.media.spotLightBeamShader, segs, &plugVerts[0] ); } // show the endpoint if ( !( flags & SL_NOIMPACT ) ) { if ( hitDist ) { VectorMA( startvec, hitDist, conevec, endvec ); alpha = 0.3f; radius = coreEndRadius * ( hitDist / beamLen ); VectorNegate( lightDir, proj ); CG_ImpactMark( cgs.media.spotLightShader, tr.endpos, proj, 0, colorNorm[0], colorNorm[1], colorNorm[2], alpha, qfalse, radius, qtrue, -1 ); } } // add d light at end if ( !( flags & SL_NODLIGHT ) ) { vec3_t dlightLoc; // VectorMA(tr.endpos, -60, lightDir, dlightLoc); // back away from the hit // trap_R_AddLightToScene(dlightLoc, 200, colorNorm[0], colorNorm[1], colorNorm[2], 0); // ,REF_JUNIOR_DLIGHT); VectorMA( tr.endpos, 0, lightDir, dlightLoc ); // back away from the hit // trap_R_AddLightToScene(dlightLoc, radius*2, colorNorm[0], colorNorm[1], colorNorm[2], 0); // ,REF_JUNIOR_DLIGHT); trap_R_AddLightToScene( dlightLoc, radius * 2, 0.3, 0.3, 0.3, 0 ); // ,REF_JUNIOR_DLIGHT); } // draw flare at source if ( !( flags & SL_NOFLARE ) ) { qboolean lightInEyes = qfalse; vec3_t camloc, dirtolight; float dot, deg, dist; float flarescale = 0.0; // TTimo: might be used uninitialized // get camera position and direction to lightsource VectorCopy( cg.snap->ps.origin, camloc ); camloc[2] += cg.snap->ps.viewheight; VectorSubtract( start, camloc, dirtolight ); dist = VectorNormalize( dirtolight ); // first use dot to determine if it's facing the camera dot = DotProduct( lightDir, dirtolight ); // it's facing the camera, find out how closely and trace to see if the source can be seen deg = RAD2DEG( M_PI - acos( dot ) ); if ( deg <= 35 ) { // start flare a bit before the camera gets inside the cylinder lightInEyes = qtrue; flarescale = 1 - ( deg / 35 ); } if ( lightInEyes ) { // the dot check succeeded, now do a trace CG_Trace( &tr, start, NULL, NULL, camloc, -1, MASK_ALL & ~( CONTENTS_MONSTERCLIP | CONTENTS_AREAPORTAL | CONTENTS_CLUSTERPORTAL ) ); if ( tr.fraction != 1 ) { lightInEyes = qfalse; } } if ( lightInEyes ) { float coronasize = flarescale; if ( dist < 512 ) { // make even bigger if you're close enough coronasize *= ( 512.0f / dist ); } trap_R_AddCoronaToScene( start, colorNorm[0], colorNorm[1], colorNorm[2], coronasize, cent->currentState.number, qtrue ); } else { // even though it's off, still need to add it, but turned off so it can fade in/out properly trap_R_AddCoronaToScene( start, colorNorm[0], colorNorm[1], colorNorm[2], 0, cent->currentState.number, qfalse ); } } }
/* =============== CG_GroundVehicle =============== */ void CG_LQM( centity_t *cent, clientInfo_t *ci ) { vec3_t velocity; vec3_t right, up, temp, start; DrawInfo_LQM_t drawInfo; int ONOFF = cent->currentState.ONOFF; memset( &drawInfo, 0, sizeof(drawInfo) ); drawInfo.basicInfo.vehicleIndex = ci->vehicle; drawInfo.basicInfo.ONOFF = ONOFF; // Copy Weapon Index drawInfo.weaponIndex = cent->currentState.weaponIndex; // Copy animation state drawInfo.anim = cent->currentState.vehicleAnim; // Copy animation frames drawInfo.lastTorsoAngle = cent->bayAnim; drawInfo.lastLegsAngle = cent->gearAnim; drawInfo.torsoFrame = cent->bayAnimFrame; drawInfo.legsFrame = cent->gearAnimFrame; drawInfo.torsoTime = cent->bayAnimStartTime; drawInfo.legsTime = cent->gearAnimStartTime; // get speed VectorCopy( cent->currentState.pos.trDelta, velocity ); drawInfo.basicInfo.speed = VectorLength( velocity ); // entitynum drawInfo.basicInfo.entityNum = cent->currentState.number; // get the rotation information VectorCopy( cent->currentState.angles, cent->lerpAngles ); AnglesToAxis( cent->lerpAngles, drawInfo.basicInfo.axis ); // position and orientation VectorCopy( cent->lerpOrigin, drawInfo.basicInfo.origin ); VectorCopy( cent->lerpAngles, drawInfo.basicInfo.angles ); // throttle drawInfo.basicInfo.throttle = cent->currentState.frame; // loadout drawInfo.basicInfo.usedLoadout = 0;//&cg_loadouts[cent->currentState.number]; // muzzleflash if( cg.time - cent->muzzleFlashTime <= MUZZLE_FLASH_TIME ) { drawInfo.basicInfo.drawMuzzleFlash = true; drawInfo.basicInfo.flashWeaponIndex = cent->muzzleFlashWeapon; } // draw lqm CG_DrawLQM(&drawInfo); // return frames cent->bayAnim = drawInfo.lastTorsoAngle; cent->gearAnim = drawInfo.lastLegsAngle; cent->bayAnimFrame = drawInfo.torsoFrame; cent->bayAnimStartTime = drawInfo.torsoTime; cent->gearAnimFrame = drawInfo.legsFrame; cent->gearAnimStartTime = drawInfo.legsTime; // flags CG_LQMFlags( cent ); // reticles if( cent == &cg.predictedPlayerEntity ) { refEntity_t reticle; vec3_t forward, ang, end; trace_t tr; // playerState_t * ps = &cg.snap->ps; float len; float mindist = cg_thirdPersonRange.integer + availableVehicles[ci->vehicle].cam_dist[ CAMERA_V_DEFAULT ] + availableVehicles[ci->vehicle].maxs[0] + 20; memset( &reticle, 0, sizeof(reticle) ); CG_ResetReticles(); reticle.customShader = availableWeapons[availableVehicles[ci->vehicle].weapons[cent->currentState.weaponNum]].crosshair; reticle.hModel = cgs.media.reticle[availableWeapons[availableVehicles[ci->vehicle].weapons[cent->currentState.weaponNum]].crosshair]; AngleVectors( cent->currentState.angles, forward, right, up ); RotatePointAroundVector( temp, up, forward, cent->currentState.angles2[ROLL] ); CrossProduct( up, temp, right ); RotatePointAroundVector( forward, right, temp, cent->currentState.angles2[PITCH] ); VectorMA( cent->lerpOrigin, availableVehicles[ci->vehicle].gunoffset[0], forward, start ); VectorMA( start, availableVehicles[ci->vehicle].gunoffset[1], right, start ); VectorMA( start, availableVehicles[ci->vehicle].gunoffset[2], up, start ); VectorMA( start, 2000, forward, end ); vectoangles( forward, ang ); ang[2] = 0; AnglesToAxis( ang, reticle.axis ); CG_Trace( &tr, start, 0, 0, end, cg.snap->ps.clientNum, MASK_SOLID ); if( tr.entityNum < MAX_CLIENTS ) { cg.crosshairClientNum = tr.entityNum; cg.crosshairClientTime = cg.time; } VectorCopy( tr.endpos, end ); CG_Trace( &tr, cg.refdef.vieworg, 0, 0, end, cg.snap->ps.clientNum, MASK_SOLID ); VectorSubtract( tr.endpos, cg.refdef.vieworg, forward ); len = VectorNormalize(forward); if( len > mindist ) { VectorMA( cg.refdef.vieworg, mindist, forward, reticle.origin ); } else { VectorMA( cg.refdef.vieworg, len - 5, forward, reticle.origin ); } VectorCopy( reticle.origin, reticle.lightingOrigin ); reticle.renderfx = RF_LIGHTING_ORIGIN|RF_SHADOW_PLANE; CG_AddReticleEntityToScene( &reticle, NULL ); } }
/* ================== CG_LaunchGib ================== */ void CG_LaunchGib( centity_t *cent, vec3_t origin, vec3_t angles, vec3_t velocity, qhandle_t hModel, float sizeScale, int breakCount ) { localEntity_t *le; refEntity_t *re; int i; if ( !cg_blood.integer ) { return; } le = CG_AllocLocalEntity(); re = &le->refEntity; le->leType = LE_FRAGMENT; le->startTime = cg.time; // le->endTime = le->startTime + 60000 + random() * 60000; le->endTime = le->startTime + 20000 + ( crandom() * 5000 ); le->breakCount = breakCount; le->sizeScale = sizeScale; VectorCopy( angles, le->angles.trBase ); VectorCopy( origin, re->origin ); AnglesToAxis( angles, re->axis ); if ( sizeScale != 1.0 ) { for ( i = 0; i < 3; i++ ) VectorScale( re->axis[i], sizeScale, re->axis[i] ); } re->hModel = hModel; // re->fadeStartTime = le->endTime - 3000; re->fadeStartTime = le->endTime - 1000; re->fadeEndTime = le->endTime; switch ( cent->currentState.aiChar ) { case AICHAR_ZOMBIE: le->pos.trType = TR_GRAVITY_LOW; le->angles.trDelta[0] = 400 * crandom(); le->angles.trDelta[1] = 400 * crandom(); le->angles.trDelta[2] = 400 * crandom(); le->leBounceSoundType = LEBS_BONE; le->bounceFactor = 0.5; break; default: le->leBounceSoundType = LEBS_BLOOD; le->leMarkType = LEMT_BLOOD; le->pos.trType = TR_GRAVITY; le->angles.trDelta[0] = ( 10 + ( rand() & 50 ) ) - 30; // le->angles.trDelta[0] = (100 + (rand()&500)) - 300; // pitch le->angles.trDelta[1] = ( 100 + ( rand() & 500 ) ) - 300; // (SA) this is the safe one right now (yaw) turn the others up when I have tumbling things landing properly le->angles.trDelta[2] = ( 10 + ( rand() & 50 ) ) - 30; // le->angles.trDelta[2] = (100 + (rand()&500)) - 300; // roll le->bounceFactor = 0.3; break; } VectorCopy( origin, le->pos.trBase ); VectorCopy( velocity, le->pos.trDelta ); le->pos.trTime = cg.time; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->ownerNum = cent->currentState.number; // Ridah, if the player is on fire, then spawn some flaming gibs if ( cent && CG_EntOnFire( cent ) ) { le->onFireStart = cent->currentState.onFireStart; le->onFireEnd = re->fadeEndTime + 1000; } else if ( ( cent->currentState.aiChar == AICHAR_ZOMBIE ) && IS_FLAMING_ZOMBIE( cent->currentState ) ) { le->onFireStart = cg.time - 1000; le->onFireEnd = re->fadeEndTime + 1000; } }
/* ============== R_CalcBone ============== */ void R_CalcBone(mdsHeader_t *header, const refEntity_t *refent, int boneNum) { thisBoneInfo = &boneInfo[boneNum]; if (thisBoneInfo->torsoWeight) { cTBonePtr = &cBoneListTorso[boneNum]; isTorso = qtrue; if (thisBoneInfo->torsoWeight == 1.0f) { fullTorso = qtrue; } } else { isTorso = qfalse; fullTorso = qfalse; } cBonePtr = &cBoneList[boneNum]; bonePtr = &bones[boneNum]; // we can assume the parent has already been uncompressed for this frame + lerp if (thisBoneInfo->parent >= 0) { parentBone = &bones[thisBoneInfo->parent]; parentBoneInfo = &boneInfo[thisBoneInfo->parent]; } else { parentBone = NULL; parentBoneInfo = NULL; } #ifdef HIGH_PRECISION_BONES // rotation if (fullTorso) { VectorCopy(cTBonePtr->angles, angles); } else { VectorCopy(cBonePtr->angles, angles); if (isTorso) { VectorCopy(cTBonePtr->angles, tangles); // blend the angles together for (j = 0; j < 3; j++) { diff = tangles[j] - angles[j]; if (Q_fabs(diff) > 180) { diff = AngleNormalize180(diff); } angles[j] = angles[j] + thisBoneInfo->torsoWeight * diff; } } } #else // rotation if (fullTorso) { sh = (short *)cTBonePtr->angles; pf = angles; ANGLES_SHORT_TO_FLOAT(pf, sh); } else { sh = (short *)cBonePtr->angles; pf = angles; ANGLES_SHORT_TO_FLOAT(pf, sh); if (isTorso) { int j; sh = (short *)cTBonePtr->angles; pf = tangles; ANGLES_SHORT_TO_FLOAT(pf, sh); // blend the angles together for (j = 0; j < 3; j++) { diff = tangles[j] - angles[j]; if (Q_fabs(diff) > 180) { diff = AngleNormalize180(diff); } angles[j] = angles[j] + thisBoneInfo->torsoWeight * diff; } } } #endif AnglesToAxis(angles, bonePtr->matrix); // translation if (parentBone) { #ifdef HIGH_PRECISION_BONES if (fullTorso) { angles[0] = cTBonePtr->ofsAngles[0]; angles[1] = cTBonePtr->ofsAngles[1]; angles[2] = 0; LocalAngleVector(angles, vec); LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } else { angles[0] = cBonePtr->ofsAngles[0]; angles[1] = cBonePtr->ofsAngles[1]; angles[2] = 0; LocalAngleVector(angles, vec); if (isTorso) { tangles[0] = cTBonePtr->ofsAngles[0]; tangles[1] = cTBonePtr->ofsAngles[1]; tangles[2] = 0; LocalAngleVector(tangles, v2); // blend the angles together SLerp_Normal(vec, v2, thisBoneInfo->torsoWeight, vec); LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } else // legs bone { LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } } #else if (fullTorso) { sh = (short *)cTBonePtr->ofsAngles; pf = angles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(angles, vec); LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } else { sh = (short *)cBonePtr->ofsAngles; pf = angles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(angles, vec); if (isTorso) { sh = (short *)cTBonePtr->ofsAngles; pf = tangles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(tangles, v2); // blend the angles together SLerp_Normal(vec, v2, thisBoneInfo->torsoWeight, vec); LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } else // legs bone { LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } } #endif } else // just use the frame position { bonePtr->translation[0] = frame->parentOffset[0]; bonePtr->translation[1] = frame->parentOffset[1]; bonePtr->translation[2] = frame->parentOffset[2]; } if (boneNum == header->torsoParent) // this is the torsoParent { VectorCopy(bonePtr->translation, torsoParentOffset); } validBones[boneNum] = 1; rawBones[boneNum] = *bonePtr; newBones[boneNum] = 1; }
/* * SV_Push * * Objects need to be moved back on a failed push, * otherwise riders would continue to slide. */ static bool SV_Push( edict_t *pusher, vec3_t move, vec3_t amove ) { int i, e; edict_t *check, *block; vec3_t mins, maxs; pushed_t *p; mat3_t axis; vec3_t org, org2, move2; // find the bounding box for( i = 0; i < 3; i++ ) { mins[i] = pusher->r.absmin[i] + move[i]; maxs[i] = pusher->r.absmax[i] + move[i]; } // we need this for pushing things later VectorNegate( amove, org ); AnglesToAxis( org, axis ); // save the pusher's original position pushed_p->ent = pusher; VectorCopy( pusher->s.origin, pushed_p->origin ); VectorCopy( pusher->s.angles, pushed_p->angles ); if( pusher->r.client ) { VectorCopy( pusher->r.client->ps.pmove.velocity, pushed_p->pmove_origin ); pushed_p->yaw = pusher->r.client->ps.viewangles[YAW]; } pushed_p++; // move the pusher to its final position VectorAdd( pusher->s.origin, move, pusher->s.origin ); VectorAdd( pusher->s.angles, amove, pusher->s.angles ); GClip_LinkEntity( pusher ); // see if any solid entities are inside the final position check = game.edicts + 1; for( e = 1; e < game.numentities; e++, check++ ) { if( !check->r.inuse ) { continue; } if( check->movetype == MOVETYPE_PUSH || check->movetype == MOVETYPE_STOP || check->movetype == MOVETYPE_NONE || check->movetype == MOVETYPE_NOCLIP ) { continue; } if( !check->areagrid[0].prev ) { continue; // not linked in anywhere } // if the entity is standing on the pusher, it will definitely be moved if( check->groundentity != pusher ) { // see if the ent needs to be tested if( check->r.absmin[0] >= maxs[0] || check->r.absmin[1] >= maxs[1] || check->r.absmin[2] >= maxs[2] || check->r.absmax[0] <= mins[0] || check->r.absmax[1] <= mins[1] || check->r.absmax[2] <= mins[2] ) { continue; } // see if the ent's bbox is inside the pusher's final position if( !SV_TestEntityPosition( check ) ) { continue; } } if( ( pusher->movetype == MOVETYPE_PUSH ) || ( check->groundentity == pusher ) ) { // move this entity pushed_p->ent = check; VectorCopy( check->s.origin, pushed_p->origin ); VectorCopy( check->s.angles, pushed_p->angles ); pushed_p++; // try moving the contacted entity VectorAdd( check->s.origin, move, check->s.origin ); if( check->r.client ) { // FIXME: doesn't rotate monsters? VectorAdd( check->r.client->ps.pmove.origin, move, check->r.client->ps.pmove.origin ); check->r.client->ps.viewangles[YAW] += amove[YAW]; } // figure movement due to the pusher's amove VectorSubtract( check->s.origin, pusher->s.origin, org ); Matrix3_TransformVector( axis, org, org2 ); VectorSubtract( org2, org, move2 ); VectorAdd( check->s.origin, move2, check->s.origin ); if( check->movetype != MOVETYPE_BOUNCEGRENADE ) { // may have pushed them off an edge if( check->groundentity != pusher ) { check->groundentity = NULL; } } block = SV_TestEntityPosition( check ); if( !block ) { // pushed ok GClip_LinkEntity( check ); // impact? continue; } else { // try to fix block // if it is ok to leave in the old position, do it // this is only relevant for riding entities, not pushed VectorSubtract( check->s.origin, move, check->s.origin ); VectorSubtract( check->s.origin, move2, check->s.origin ); block = SV_TestEntityPosition( check ); if( !block ) { pushed_p--; continue; } } } // save off the obstacle so we can call the block function obstacle = check; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for( p = pushed_p - 1; p >= pushed; p-- ) { VectorCopy( p->origin, p->ent->s.origin ); VectorCopy( p->angles, p->ent->s.angles ); if( p->ent->r.client ) { VectorCopy( p->pmove_origin, p->ent->r.client->ps.pmove.origin ); p->ent->r.client->ps.viewangles[YAW] = p->yaw; } GClip_LinkEntity( p->ent ); } return false; } //FIXME: is there a better way to handle this? // see if anything we moved has touched a trigger for( p = pushed_p - 1; p >= pushed; p-- ) GClip_TouchTriggers( p->ent ); return true; }
/* ============== R_CalcBoneLerp ============== */ void R_CalcBoneLerp(mdsHeader_t *header, const refEntity_t *refent, int boneNum) { if (!refent || !header || boneNum < 0 || boneNum >= MDS_MAX_BONES) { return; } thisBoneInfo = &boneInfo[boneNum]; if (!thisBoneInfo) { return; } if (thisBoneInfo->parent >= 0) { parentBone = &bones[thisBoneInfo->parent]; parentBoneInfo = &boneInfo[thisBoneInfo->parent]; } else { parentBone = NULL; parentBoneInfo = NULL; } if (thisBoneInfo->torsoWeight) { cTBonePtr = &cBoneListTorso[boneNum]; cOldTBonePtr = &cOldBoneListTorso[boneNum]; isTorso = qtrue; if (thisBoneInfo->torsoWeight == 1.0f) { fullTorso = qtrue; } } else { isTorso = qfalse; fullTorso = qfalse; } cBonePtr = &cBoneList[boneNum]; cOldBonePtr = &cOldBoneList[boneNum]; bonePtr = &bones[boneNum]; newBones[boneNum] = 1; // rotation (take into account 170 to -170 lerps, which need to take the shortest route) if (fullTorso) { sh = (short *)cTBonePtr->angles; sh2 = (short *)cOldTBonePtr->angles; pf = angles; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; } else { sh = (short *)cBonePtr->angles; sh2 = (short *)cOldBonePtr->angles; pf = angles; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - backlerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - backlerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - backlerp * diff; if (isTorso) { int j; sh = (short *)cTBonePtr->angles; sh2 = (short *)cOldTBonePtr->angles; pf = tangles; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; // blend the angles together for (j = 0; j < 3; j++) { diff = tangles[j] - angles[j]; if (Q_fabs(diff) > 180) { diff = AngleNormalize180(diff); } angles[j] = angles[j] + thisBoneInfo->torsoWeight * diff; } } } AnglesToAxis(angles, bonePtr->matrix); if (parentBone) { if (fullTorso) { sh = (short *)cTBonePtr->ofsAngles; sh2 = (short *)cOldTBonePtr->ofsAngles; } else { sh = (short *)cBonePtr->ofsAngles; sh2 = (short *)cOldBonePtr->ofsAngles; } pf = angles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(angles, v2); // new pf = angles; *(pf++) = SHORT2ANGLE(*(sh2++)); *(pf++) = SHORT2ANGLE(*(sh2++)); *(pf++) = 0; LocalAngleVector(angles, vec); // old // blend the angles together if (fullTorso) { SLerp_Normal(vec, v2, torsoFrontlerp, dir); } else { SLerp_Normal(vec, v2, frontlerp, dir); } // translation if (!fullTorso && isTorso) // partial legs/torso, need to lerp according to torsoWeight { // calc the torso frame sh = (short *)cTBonePtr->ofsAngles; sh2 = (short *)cOldTBonePtr->ofsAngles; pf = angles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(angles, v2); // new pf = angles; *(pf++) = SHORT2ANGLE(*(sh2++)); *(pf++) = SHORT2ANGLE(*(sh2++)); *(pf++) = 0; LocalAngleVector(angles, vec); // old // blend the angles together SLerp_Normal(vec, v2, torsoFrontlerp, v2); // blend the torso/legs together SLerp_Normal(dir, v2, thisBoneInfo->torsoWeight, dir); } LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, dir, bonePtr->translation); } else // just interpolate the frame positions { bonePtr->translation[0] = frontlerp * frame->parentOffset[0] + backlerp * oldFrame->parentOffset[0]; bonePtr->translation[1] = frontlerp * frame->parentOffset[1] + backlerp * oldFrame->parentOffset[1]; bonePtr->translation[2] = frontlerp * frame->parentOffset[2] + backlerp * oldFrame->parentOffset[2]; } if (boneNum == header->torsoParent) // this is the torsoParent { VectorCopy(bonePtr->translation, torsoParentOffset); } validBones[boneNum] = 1; rawBones[boneNum] = *bonePtr; newBones[boneNum] = 1; }
/* ========================== CG_MachineGunEjectBrass ========================== */ static void CG_MachineGunEjectBrass( centity_t *cent ) { localEntity_t *le; refEntity_t *re; vec3_t velocity, xvelocity; vec3_t offset, xoffset; float waterScale = 1.0f; vec3_t v[3]; if ( cg_brassTime.integer <= 0 ) { return; } le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 0; velocity[1] = -50 + 40 * crandom(); velocity[2] = 100 + 50 * crandom(); le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random(); le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time - (rand()&15); AnglesToAxis( cent->lerpAngles, v ); offset[0] = 8; offset[1] = -4; offset[2] = 24; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( cent->lerpOrigin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->hModel = cgs.media.machinegunBrassModel; le->bounceFactor = 0.4 * waterScale; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 2; le->angles.trDelta[1] = 1; le->angles.trDelta[2] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_BRASS; le->leMarkType = LEMT_NONE; }
/* =============== CG_CalcViewValues Sets cg.refdef view values =============== */ static int CG_CalcViewValues( void ) { playerState_t *ps; memset( &cg.refdef, 0, sizeof( cg.refdef ) ); // strings for in game rendering // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) ); // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) ); // calculate size of 3D view CG_CalcVrect(); ps = &cg.cur_lc->predictedPlayerState; /* if (cg.cameraMode) { vec3_t origin, angles; if (trap_getCameraInfo(cg.time, &origin, &angles)) { VectorCopy(origin, cg.refdef.vieworg); angles[ROLL] = 0; VectorCopy(angles, cg.refdefViewAngles); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); return CG_CalcFov(); } else { cg.cameraMode = qfalse; } } */ // intermission view if ( ps->pm_type == PM_INTERMISSION ) { VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); return CG_CalcFov(); } cg.bobcycle = ( ps->bobCycle & 128 ) >> 7; cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) ); cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] + ps->velocity[1] * ps->velocity[1] ); VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); if (cg_cameraOrbit.integer) { if (cg.time > cg.nextOrbitTime) { cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer; cg_thirdPersonAngle[cg.cur_localClientNum].value += cg_cameraOrbit.value; } } // add error decay if ( cg_errorDecay.value > 0 ) { int t; float f; t = cg.time - cg.cur_lc->predictedErrorTime; f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; if ( f > 0 && f < 1 ) { VectorMA( cg.refdef.vieworg, f, cg.cur_lc->predictedError, cg.refdef.vieworg ); } else { cg.cur_lc->predictedErrorTime = 0; } } if ( cg.cur_lc->renderingThirdPerson ) { // back away from character CG_OffsetThirdPersonView(); } else { // offset for local bobbing and kicks CG_OffsetFirstPersonView(); } // position eye relative to origin AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); if ( cg.cur_lc->hyperspace ) { cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE; } // field of view return CG_CalcFov(); }
/* =============== CG_CalcViewValues Sets cg.refdef view values =============== */ static int CG_CalcViewValues( void ) { playerState_t *ps; memset( &cg.refdef, 0, sizeof( cg.refdef ) ); // strings for in game rendering // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) ); // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) ); // calculate size of 3D view CG_CalcVrect(); ps = &cg.predictedPlayerState; // intermission view if ( ps->pm_type == PM_INTERMISSION ) { VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); return CG_CalcFov(); } cg.bobcycle = ( ps->bobCycle & 128 ) >> 7; cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) ); cg.bobfraccos = fabs( cos( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) ); cg.bobfracsin2 = fabs( sin( ( ps->bobCycle & 127) / 127.0 * (M_PI) )); cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] + ps->velocity[1] * ps->velocity[1] ); VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); if (cg_cameraOrbit.integer) { if (cg.time > cg.nextOrbitTime) { cg.nextOrbitTime = cg.time + cg_cameraOrbitDelay.integer; cg_thirdPersonAngle.value += cg_cameraOrbit.value; } } // add error decay if ( cg_errorDecay.value > 0 ) { int t; float f; t = cg.time - cg.predictedErrorTime; f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; if ( f > 0 && f < 1 ) { VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg ); } else { cg.predictedErrorTime = 0; } } if ( cg.renderingThirdPerson ) { // back away from character CG_OffsetThirdPersonView(); } else { // offset for local bobbing and kicks CG_OffsetFirstPersonView(); } // leilei - View-from-the-model-eyes feature, aka "fullbody awareness" lol if (cg_cameraEyes.integer && !cg.renderingThirdPerson){ vec3_t forward, right, up; cg.refdefViewAngles[ROLL] = headang[ROLL]; cg.refdefViewAngles[PITCH] = headang[PITCH]; cg.refdefViewAngles[YAW] = headang[YAW]; AngleVectors( headang, forward, NULL, up ); if (cg_cameraEyes.integer == 2){ VectorMA( headpos, 0, forward, headpos ); VectorMA( headpos, 4, up, headpos ); } else { VectorMA( headpos, cg_cameraEyes_Fwd.value, forward, headpos ); VectorMA( headpos, cg_cameraEyes_Up.value, up, headpos ); } cg.refdef.vieworg[0] = ps->origin[0] + headpos[0]; cg.refdef.vieworg[1] = ps->origin[1] + headpos[1]; cg.refdef.vieworg[2] = ps->origin[2] + headpos[2]; } // position eye reletive to origin AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); if ( cg.hyperspace ) { cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE; } // field of view return CG_CalcFov(); }
// Used for both the view weapon (ps is valid) and the world modelother character models (ps is NULL) // The main player will have this called for BOTH cases, so effects like light and sound should only be done on the // world model case. void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent, int team, vector3 *newAngles, qboolean thirdPerson ) { refEntity_t gun, barrel, flash; vector3 angles; weapon_t weaponNum; weaponInfo_t *weapon; centity_t *nonPredictedCent; if ( !thirdPerson && cg_fakeGun.integer ) { weaponNum = (weapon_t)cg_fakeGun.integer; } else { weaponNum = (weapon_t)cent->currentState.weapon; } if ( weaponNum == WP_EMPLACED_GUN ) return; // spectator mode, don't draw it... if ( cg.predictedPlayerState.pm_type == PM_SPECTATOR && cent->currentState.number == cg.predictedPlayerState.clientNum ) { return; } CG_RegisterWeapon( weaponNum ); weapon = &cg_weapons[weaponNum]; memset( &gun, 0, sizeof(gun) ); // only do this if we are in first person, since world weapons are now handled on the server by Ghoul2 if ( !thirdPerson ) { // add the weapon VectorCopy( &parent->lightingOrigin, &gun.lightingOrigin ); gun.shadowPlane = parent->shadowPlane; gun.renderfx = parent->renderfx; // this player, in first person view if ( ps ) gun.hModel = weapon->viewModel; else gun.hModel = weapon->weaponModel; if ( !gun.hModel ) return; if ( !ps ) { // add weapon ready sound if ( (cent->currentState.eFlags & EF_FIRING) && weapon->firingSound ) { trap->S_AddLoopingSound( cent->currentState.number, ¢->lerpOrigin, &vec3_origin, weapon->firingSound ); } else if ( weapon->readySound ) { trap->S_AddLoopingSound( cent->currentState.number, ¢->lerpOrigin, &vec3_origin, weapon->readySound ); } } CG_PositionEntityOnTag( &gun, parent, parent->hModel, "tag_weapon" ); if ( !CG_IsMindTricked( cent->currentState.trickedEntIndex, cg.snap->ps.clientNum ) ) { // don't draw the weapon if the player is invisible CG_AddWeaponWithPowerups( &gun, cent->currentState.powerups ); } if ( weaponNum == WP_STUN_BATON ) { int i; for ( i = 0; i < 3; i++ ) { memset( &barrel, 0, sizeof(barrel) ); VectorCopy( &parent->lightingOrigin, &barrel.lightingOrigin ); barrel.shadowPlane = parent->shadowPlane; barrel.renderfx = parent->renderfx; if ( i == 0 ) { barrel.hModel = trap->R_RegisterModel( "models/weapons2/stun_baton/baton_barrel.md3" ); } else if ( i == 1 ) { barrel.hModel = trap->R_RegisterModel( "models/weapons2/stun_baton/baton_barrel2.md3" ); } else { barrel.hModel = trap->R_RegisterModel( "models/weapons2/stun_baton/baton_barrel3.md3" ); } angles.yaw = 0; angles.pitch = 0; angles.roll = 0; AnglesToAxis( &angles, barrel.axis ); if ( i == 0 ) { CG_PositionRotatedEntityOnTag( &barrel, parent, weapon->handsModel, "tag_barrel" ); } else if ( i == 1 ) { CG_PositionRotatedEntityOnTag( &barrel, parent, weapon->handsModel, "tag_barrel2" ); } else { CG_PositionRotatedEntityOnTag( &barrel, parent, weapon->handsModel, "tag_barrel3" ); } CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups ); } } else { // add the spinning barrel if ( weapon->barrelModel ) { memset( &barrel, 0, sizeof(barrel) ); VectorCopy( &parent->lightingOrigin, &barrel.lightingOrigin ); barrel.shadowPlane = parent->shadowPlane; barrel.renderfx = parent->renderfx; barrel.hModel = weapon->barrelModel; angles.yaw = 0; angles.pitch = 0; angles.roll = 0; AnglesToAxis( &angles, barrel.axis ); CG_PositionRotatedEntityOnTag( &barrel, parent, weapon->handsModel, "tag_barrel" ); CG_AddWeaponWithPowerups( &barrel, cent->currentState.powerups ); } } } memset( &flash, 0, sizeof(flash) ); CG_PositionEntityOnTag( &flash, &gun, gun.hModel, "tag_flash" ); VectorCopy( &flash.origin, &cg.lastFPFlashPoint ); // Do special charge bits // Make the guns do their charging visual in True View. if ( (ps || cg.renderingThirdPerson || cg.predictedPlayerState.clientNum != cent->currentState.number || cg_trueGuns.integer) && ((cent->currentState.modelindex2 == WEAPON_CHARGING_ALT && weaponNum == WP_BRYAR_PISTOL) || (cent->currentState.modelindex2 == WEAPON_CHARGING_ALT && weaponNum == WP_BRYAR_OLD) || (weaponNum == WP_BOWCASTER && cent->currentState.modelindex2 == WEAPON_CHARGING) || (weaponNum == WP_DEMP2 && cent->currentState.modelindex2 == WEAPON_CHARGING_ALT)) ) { int shader = 0; float val = 0.0f; float scale = 1.0f; addspriteArgStruct_t fxSArgs; vector3 flashorigin, flashdir; if ( !thirdPerson ) { VectorCopy( &flash.origin, &flashorigin ); VectorCopy( &flash.axis[0], &flashdir ); } else { mdxaBone_t boltMatrix; // it's quite possible that we may have have no weapon model and be in a valid state, so return here if this is the case if ( !trap->G2API_HasGhoul2ModelOnIndex( &(cent->ghoul2), 1 ) ) return; // Couldn't find bolt point. if ( !trap->G2API_GetBoltMatrix( cent->ghoul2, 1, 0, &boltMatrix, newAngles, ¢->lerpOrigin, cg.time, cgs.gameModels, ¢->modelScale ) ) { return; } BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, &flashorigin ); BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_X, &flashdir ); } if ( weaponNum == WP_BRYAR_PISTOL || weaponNum == WP_BRYAR_OLD ) { // Hardcoded max charge time of 1 second val = (cg.time - cent->currentState.constantLight) * 0.001f; shader = media.gfx.world.bryarFrontFlash; } else if ( weaponNum == WP_BOWCASTER ) { // Hardcoded max charge time of 1 second val = (cg.time - cent->currentState.constantLight) * 0.001f; shader = media.gfx.world.greenFrontFlash; } else if ( weaponNum == WP_DEMP2 ) { val = (cg.time - cent->currentState.constantLight) * 0.001f; shader = media.gfx.world.lightningFlash; scale = 1.75f; } if ( val < 0.0f ) val = 0.0f; else if ( val > 1.0f ) { val = 1.0f; if ( ps && cent->currentState.number == ps->clientNum ) CGCam_Shake( 0.2f, 100 ); } else if ( ps && cent->currentState.number == ps->clientNum ) CGCam_Shake( val * val * 0.6f, 100 ); val += random() * 0.5f; VectorCopy( &flashorigin, &fxSArgs.origin ); VectorClear( &fxSArgs.vel ); VectorClear( &fxSArgs.accel ); fxSArgs.scale = 3.0f*val*scale; fxSArgs.dscale = 0.0f; fxSArgs.sAlpha = 0.7f; fxSArgs.eAlpha = 0.7f; fxSArgs.rotation = random() * 360; fxSArgs.bounce = 0.0f; fxSArgs.life = 1.0f; fxSArgs.shader = shader; fxSArgs.flags = 0x08000000; trap->FX_AddSprite( &fxSArgs ); } // make sure we aren't looking at cg.predictedPlayerEntity for LG nonPredictedCent = &cg_entities[cent->currentState.clientNum]; // if the index of the nonPredictedCent is not the same as the clientNum then this is a fake player (like on the // single player podiums), so go ahead and use the cent if ( nonPredictedCent - cg_entities != cent->currentState.clientNum ) nonPredictedCent = cent; // add the flash if ( weaponNum != WP_DEMP2 || (nonPredictedCent->currentState.eFlags & EF_FIRING) ) { // impulse flash if ( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME ) return; } if ( ps || cg.renderingThirdPerson || cg_trueGuns.integer || cent->currentState.number != cg.predictedPlayerState.clientNum ) { // Make sure we don't do the thirdperson model effects for the local player if we're in first person vector3 flashorigin, flashdir; refEntity_t tpflash; memset( &tpflash, 0, sizeof(tpflash) ); if ( !thirdPerson ) { CG_PositionEntityOnTag( &tpflash, &gun, gun.hModel, "tag_flash" ); VectorCopy( &tpflash.origin, &flashorigin ); VectorCopy( &tpflash.axis[0], &flashdir ); } else { mdxaBone_t boltMatrix; // it's quite possible that we may have have no weapon model and be in a valid state, so return here if this is the case if ( !trap->G2API_HasGhoul2ModelOnIndex( &(cent->ghoul2), 1 ) ) return; // Couldn't find bolt point. if ( !trap->G2API_GetBoltMatrix( cent->ghoul2, 1, 0, &boltMatrix, newAngles, ¢->lerpOrigin, cg.time, cgs.gameModels, ¢->modelScale ) ) return; BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, &flashorigin ); BG_GiveMeVectorFromMatrix( &boltMatrix, POSITIVE_X, &flashdir ); } if ( cg.time - cent->muzzleFlashTime <= MUZZLE_FLASH_TIME + 10 ) { // Handle muzzle flashes if ( cent->currentState.eFlags & EF_ALT_FIRING ) { // Check the alt firing first. if ( weapon->altMuzzleEffect ) { if ( !thirdPerson ) trap->FX_PlayEntityEffectID( weapon->altMuzzleEffect, &flashorigin, tpflash.axis, -1, -1, -1, -1 ); else trap->FX_PlayEffectID( weapon->altMuzzleEffect, &flashorigin, &flashdir, -1, -1, qfalse ); } } else { // Regular firing if ( weapon->muzzleEffect ) { if ( !thirdPerson ) trap->FX_PlayEntityEffectID( weapon->muzzleEffect, &flashorigin, tpflash.axis, -1, -1, -1, -1 ); else trap->FX_PlayEffectID( weapon->muzzleEffect, &flashorigin, &flashdir, -1, -1, qfalse ); } } } if ( weapon->flashDlightColor.r || weapon->flashDlightColor.g || weapon->flashDlightColor.b ) { trap->R_AddLightToScene( &flashorigin, 300 + (rand() & 31), weapon->flashDlightColor.r, weapon->flashDlightColor.g, weapon->flashDlightColor.b ); } } }
/* =============== UI_DrawPlayer =============== */ void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ) { refdef_t refdef; refEntity_t legs; refEntity_t torso; refEntity_t head; refEntity_t gun; refEntity_t barrel; refEntity_t flash; vec3_t origin; int renderfx; vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; float len; float xx; if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) { return; } // this allows the ui to cache the player model on the main menu if (w == 0 || h == 0) { return; } dp_realtime = time; if ( pi->pendingWeapon != WP_NUM_WEAPONS && dp_realtime > pi->weaponTimer ) { pi->weapon = pi->pendingWeapon; pi->lastWeapon = pi->pendingWeapon; pi->pendingWeapon = WP_NUM_WEAPONS; pi->weaponTimer = 0; if( pi->currentWeapon != pi->weapon ) { trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL ); } } UI_AdjustFrom640( &x, &y, &w, &h ); y -= jumpHeight; memset( &refdef, 0, sizeof( refdef ) ); memset( &legs, 0, sizeof(legs) ); memset( &torso, 0, sizeof(torso) ); memset( &head, 0, sizeof(head) ); refdef.rdflags = RDF_NOWORLDMODEL; AxisClear( refdef.viewaxis ); refdef.x = x; refdef.y = y; refdef.width = w; refdef.height = h; refdef.fov_x = (int)((float)refdef.width / uiInfo.uiDC.xscale / 640.0f * 90.0f); xx = refdef.width / uiInfo.uiDC.xscale / tan( refdef.fov_x / 360 * M_PI ); refdef.fov_y = atan2( refdef.height / uiInfo.uiDC.yscale, xx ); refdef.fov_y *= ( 360 / (float)M_PI ); // calculate distance so the player nearly fills the box len = 0.7 * ( maxs[2] - mins[2] ); origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.5 ); origin[1] = 0.5 * ( mins[1] + maxs[1] ); origin[2] = -0.5 * ( mins[2] + maxs[2] ); refdef.time = dp_realtime; trap_R_ClearScene(); // get the rotation information UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis ); // get the animation state (after rotation, to allow feet shuffle) UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp, &torso.oldframe, &torso.frame, &torso.backlerp ); renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; // // add the legs // legs.hModel = pi->legsModel; legs.customSkin = pi->legsSkin; VectorCopy( origin, legs.origin ); VectorCopy( origin, legs.lightingOrigin ); legs.renderfx = renderfx; VectorCopy (legs.origin, legs.oldorigin); trap_R_AddRefEntityToScene( &legs ); if (!legs.hModel) { return; } // // add the torso // torso.hModel = pi->torsoModel; if (!torso.hModel) { return; } torso.customSkin = pi->torsoSkin; VectorCopy( origin, torso.lightingOrigin ); UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso"); torso.renderfx = renderfx; trap_R_AddRefEntityToScene( &torso ); // // add the head // head.hModel = pi->headModel; if (!head.hModel) { return; } head.customSkin = pi->headSkin; VectorCopy( origin, head.lightingOrigin ); UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head"); head.renderfx = renderfx; trap_R_AddRefEntityToScene( &head ); // // add the gun // if ( pi->currentWeapon != WP_NONE ) { memset( &gun, 0, sizeof(gun) ); gun.hModel = pi->weaponModel; VectorCopy( origin, gun.lightingOrigin ); UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon"); gun.renderfx = renderfx; trap_R_AddRefEntityToScene( &gun ); } // // add the spinning barrel // if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { vec3_t angles; memset( &barrel, 0, sizeof(barrel) ); VectorCopy( origin, barrel.lightingOrigin ); barrel.renderfx = renderfx; barrel.hModel = pi->barrelModel; angles[YAW] = 0; angles[PITCH] = 0; angles[ROLL] = UI_MachinegunSpinAngle( pi ); if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { angles[PITCH] = angles[ROLL]; angles[ROLL] = 0; } AnglesToAxis( angles, barrel.axis ); UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel"); trap_R_AddRefEntityToScene( &barrel ); } // // add muzzle flash // if ( dp_realtime <= pi->muzzleFlashTime ) { if ( pi->flashModel ) { memset( &flash, 0, sizeof(flash) ); flash.hModel = pi->flashModel; VectorCopy( origin, flash.lightingOrigin ); UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash"); flash.renderfx = renderfx; trap_R_AddRefEntityToScene( &flash ); } // make a dlight for the flash if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) { trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0], pi->flashDlightColor[1], pi->flashDlightColor[2] ); } } // // add the chat icon // if ( pi->chat ) { UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) ); } // // add an accent light // origin[0] -= 100; // + = behind, - = in front origin[1] += 100; // + = left, - = right origin[2] += 100; // + = above, - = below trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 ); origin[0] -= 100; origin[1] -= 100; origin[2] -= 100; trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 ); trap_R_RenderScene( &refdef ); }
// Add the weapon, and flash for the player's view void CG_AddViewWeapon( playerState_t *ps ) { // no gun if in third person view or a camera is active if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR || ps->pm_type == PM_INTERMISSION || cg.renderingThirdPerson ) { return; } const int weap = cg_fakeGun.integer ? cg_fakeGun.integer : ps->weapon; float desiredFov = 0.0f; if ( !cg.renderingThirdPerson && (cg_trueGuns.integer || weap == WP_SABER || weap == WP_MELEE) && cg_trueFOV.value && cg.predictedPlayerState.pm_type != PM_SPECTATOR && cg.predictedPlayerState.pm_type != PM_INTERMISSION ) { desiredFov = cg_fovViewmodel.integer ? cg_fovViewmodel.value : cg_trueFOV.value; } else { desiredFov = cg_fovViewmodel.integer ? cg_fovViewmodel.value : cg_fov.value; } desiredFov = Q_clampi( 1, desiredFov, 180 ); // allow the gun to be completely removed if ( !cg_fakeGun.integer && (!cg_drawGun.integer || cg.predictedPlayerState.zoomMode || cg_trueGuns.integer || weap == WP_SABER || weap == WP_MELEE) ) { return; } // don't draw if testing a gun model if ( cg.testGun ) { return; } centity_t *cent = &cg_entities[cg.predictedPlayerState.clientNum]; CG_RegisterWeapon( weap ); refEntity_t hand; memset( &hand, 0, sizeof(hand) ); // set up gun position vector3 angles; CG_CalculateWeaponPosition( &hand.origin, &angles ); refdef_t *refdef = CG_GetRefdef(); VectorMA( &hand.origin, cg.gunAlign.x, &refdef->viewaxis[0], &hand.origin ); VectorMA( &hand.origin, cg.gunAlign.y, &refdef->viewaxis[1], &hand.origin ); VectorMA( &hand.origin, cg.gunAlign.z, &refdef->viewaxis[2], &hand.origin ); AnglesToAxis( &angles, hand.axis ); if ( cg_fovViewmodel.integer ) { float fracDistFOV, fracWeapFOV; float fov = desiredFov; if ( cg_fovAspectAdjust.integer ) { // Based on LordHavoc's code for Darkplaces // http://www.quakeworld.nu/forum/topic/53/what-does-your-qw-look-like/page/30 const float baseAspect = 0.75f; // 3/4 const float aspect = (float)cgs.glconfig.vidWidth / (float)cgs.glconfig.vidHeight; fov = atanf( tanf( desiredFov*M_PI / 360.0f ) * baseAspect*aspect )*360.0f / M_PI; } fracDistFOV = tanf( refdef->fov_x * M_PI / 360.0f ); fracWeapFOV = (1.0f / fracDistFOV) * tanf( fov * M_PI / 360.0f ); VectorScale( &hand.axis[0], fracWeapFOV, &hand.axis[0] ); } // map torso animations to weapon animations if ( cg_debugGunFrame.integer ) { // development tool hand.frame = hand.oldframe = cg_debugGunFrame.integer; hand.backlerp = 0; } else { float currentFrame; // get clientinfo for animation map clientInfo_t *ci = nullptr; if ( cent->currentState.eType == ET_NPC ) { if ( !cent->npcClient ) { return; } ci = cent->npcClient; } else { ci = &cgs.clientinfo[cent->currentState.clientNum]; } // smoother first-person anims by eezstreet http://jkhub.org/topic/1499-/ // actually ported from SP #if 1 // Sil's fix trap->G2API_GetBoneFrame( cent->ghoul2, "lower_lumbar", cg.time, ¤tFrame, cgs.gameModels, 0 ); hand.frame = CG_MapTorsoToWeaponFrame( ci, ceilf( currentFrame ), cent->currentState.torsoAnim ); hand.oldframe = CG_MapTorsoToWeaponFrame( ci, floorf( currentFrame ), cent->currentState.torsoAnim ); hand.backlerp = 1.0f - (currentFrame - floorf( currentFrame )); #else // basejka style hand.frame = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.frame, cent->currentState.torsoAnim ); hand.oldframe = CG_MapTorsoToWeaponFrame( ci, cent->pe.torso.oldFrame, cent->currentState.torsoAnim ); hand.backlerp = cent->pe.torso.backlerp; #endif // Handle the fringe situation where oldframe is invalid if ( hand.frame == -1 ) { hand.frame = 0; hand.oldframe = 0; hand.backlerp = 0; } else if ( hand.oldframe == -1 ) { hand.oldframe = hand.frame; hand.backlerp = 0; } } weaponInfo_t *wi = &cg_weapons[weap]; hand.hModel = wi->handsModel; hand.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON; // add everything onto the hand CG_AddPlayerWeapon( &hand, ps, &cg_entities[cg.predictedPlayerState.clientNum], ps->persistant[PERS_TEAM], &angles, qfalse ); }
/* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float oldZ; t = le->endTime - cg.time; if ( t < SINK_TIME ) { // we must use an explicit lighting origin, otherwise the // lighting would be lost as soon as the origin went // into the ground VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin ); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; oldZ = le->refEntity.origin[2]; le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME ); trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.origin[2] = oldZ; } else { trap_R_AddRefEntityToScene( &le->refEntity ); } return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); } trap_R_AddRefEntityToScene( &le->refEntity ); // add a blood trail if ( le->leBounceSoundType == LEBS_BLOOD ) { CG_BloodTrail( le ); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); trap_R_AddRefEntityToScene( &le->refEntity ); }
/* =============== CG_CalcViewValues Sets cg.refdef view values =============== */ static int CG_CalcViewValues( void ) { playerState_t *ps; memset( &cg.refdef, 0, sizeof( cg.refdef ) ); // strings for in game rendering // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) ); // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) ); // calculate size of 3D view CG_CalcVrect(); ps = &cg.predictedPlayerState; if ( cg.cameraMode ) { vec3_t origin, angles; float fov = 90; float x; if ( trap_getCameraInfo( CAM_PRIMARY, cg.time, &origin, &angles, &fov ) ) { VectorCopy( origin, cg.refdef.vieworg ); angles[ROLL] = 0; angles[PITCH] = -angles[PITCH]; // (SA) compensate for reversed pitch (this makes the game match the editor, however I'm guessing the real fix is to be done there) VectorCopy( angles, cg.refdefViewAngles ); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); x = cg.refdef.width / tan( fov / 360 * M_PI ); cg.refdef.fov_y = atan2( cg.refdef.height, x ); cg.refdef.fov_y = cg.refdef.fov_y * 360 / M_PI; cg.refdef.fov_x = fov; trap_SendClientCommand( va( "setCameraOrigin %f %f %f", origin[0], origin[1], origin[2] ) ); return 0; } else { cg.cameraMode = qfalse; trap_Cvar_Set( "cg_letterbox", "0" ); trap_SendClientCommand( "stopCamera" ); CG_Fade( 0, 0, 0, 255, 0 ); // go black CG_Fade( 0, 0, 0, 0, 1500 ); // then fadeup } } // intermission view if ( ps->pm_type == PM_INTERMISSION ) { VectorCopy( ps->origin, cg.refdef.vieworg ); VectorCopy( ps->viewangles, cg.refdefViewAngles ); AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); return CG_CalcFov(); } cg.bobcycle = ( ps->bobCycle & 128 ) >> 7; cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) ); cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] + ps->velocity[1] * ps->velocity[1] ); // VectorCopy( ps->origin, cg.refdef.vieworg ); // Arnout: see if we're attached to a gun if ( cg.renderingThirdPerson && ps->eFlags & EF_MG42_ACTIVE ) { centity_t *mg42 = &cg_entities[ps->viewlocked_entNum]; vec3_t forward, right, up; AngleVectors( ps->viewangles, forward, right, up ); VectorMA( mg42->currentState.pos.trBase, -36, forward, cg.refdef.vieworg ); cg.refdef.vieworg[2] = ps->origin[2]; } else { VectorCopy( ps->origin, cg.refdef.vieworg ); } VectorCopy( ps->viewangles, cg.refdefViewAngles ); // add error decay if ( cg_errorDecay.value > 0 ) { int t; float f; t = cg.time - cg.predictedErrorTime; f = ( cg_errorDecay.value - t ) / cg_errorDecay.value; if ( f > 0 && f < 1 ) { VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg ); } else { cg.predictedErrorTime = 0; } } // Ridah, lock the viewangles if the game has told us to if ( ps->viewlocked ) { /* if (ps->viewlocked == 4) { centity_t *tent; tent = &cg_entities[ps->viewlocked_entNum]; VectorCopy (tent->currentState.apos.trBase, cg.refdefViewAngles); } else */ // DHM - Nerve :: don't bother evaluating if set to 7 (look at medic) if ( ps->viewlocked != 7 && ps->viewlocked != 3 && ps->viewlocked != 2 ) { BG_EvaluateTrajectory( &cg_entities[ps->viewlocked_entNum].currentState.apos, cg.time, cg.refdefViewAngles ); } if ( ps->viewlocked == 2 ) { cg.refdefViewAngles[0] += crandom(); cg.refdefViewAngles[1] += crandom(); } } // done. if ( cg.renderingThirdPerson ) { // back away from character CG_OffsetThirdPersonView(); } else { // offset for local bobbing and kicks CG_OffsetFirstPersonView(); // Ridah, lock the viewangles if the game has told us to if ( ps->viewlocked == 7 ) { centity_t *tent; vec3_t vec; tent = &cg_entities[ps->viewlocked_entNum]; VectorCopy( tent->lerpOrigin, vec ); VectorSubtract( vec, cg.refdef.vieworg, vec ); vectoangles( vec, cg.refdefViewAngles ); } else if ( ps->viewlocked == 4 ) { vec3_t fwd; AngleVectors( cg.refdefViewAngles, fwd, NULL, NULL ); VectorMA( cg_entities[ps->viewlocked_entNum].currentState.pos.trBase, 16, fwd, cg.refdef.vieworg ); } else if ( ps->viewlocked ) { vec3_t fwd; float oldZ; // set our position to be behind it oldZ = cg.refdef.vieworg[2]; AngleVectors( cg.refdefViewAngles, fwd, NULL, NULL ); VectorMA( cg_entities[ps->viewlocked_entNum].currentState.pos.trBase, -34, fwd, cg.refdef.vieworg ); cg.refdef.vieworg[2] = oldZ; // CG_Printf( "ps->origin[2]: %f\n", ps->origin[2] ); // CG_Printf( "fwd: %f %f %f\n", fwd[0], fwd[1], fwd[2] ); // CG_Printf( "base: %f %f %f\n", cg_entities[ps->viewlocked_entNum].currentState.pos.trBase[0], cg_entities[ps->viewlocked_entNum].currentState.pos.trBase[1], cg_entities[ps->viewlocked_entNum].currentState.pos.trBase[2] ); } // done. } // position eye relative to origin AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis ); if ( cg.hyperspace ) { cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE; } // field of view return CG_CalcFov(); }
static void CG_DistortionOrb( centity_t *cent ) { refEntity_t ent; vec3_t ang; float scale = 0.5f; float vLen; if (!cg_renderToTextureFX.integer) { return; } memset( &ent, 0, sizeof( ent ) ); VectorCopy( cent->lerpOrigin, ent.origin ); VectorSubtract(ent.origin, cg.refdef.vieworg, ent.axis[0]); vLen = VectorLength(ent.axis[0]); if (VectorNormalize(ent.axis[0]) <= 0.1f) { // Entity is right on vieworg. quit. return; } // VectorCopy(cg.refdef.viewaxis[2], ent.axis[2]); // CrossProduct(ent.axis[0], ent.axis[2], ent.axis[1]); vectoangles(ent.axis[0], ang); ang[ROLL] = cent->trickAlpha; cent->trickAlpha += 16; //spin the half-sphere to give a "screwdriver" effect AnglesToAxis(ang, ent.axis); //radius must be a power of 2, and is the actual captured texture size if (vLen < 128) { ent.radius = 256; } else if (vLen < 256) { ent.radius = 128; } else if (vLen < 512) { ent.radius = 64; } else { ent.radius = 32; } VectorScale(ent.axis[0], scale, ent.axis[0]); VectorScale(ent.axis[1], scale, ent.axis[1]); VectorScale(ent.axis[2], -scale, ent.axis[2]); ent.hModel = cgs.media.halfShieldModel; ent.customShader = 0;//cgs.media.halfShieldShader; #if 1 ent.renderfx = (RF_DISTORTION|RF_RGB_TINT); //tint the whole thing a shade of blue ent.shaderRGBA[0] = 200.0f; ent.shaderRGBA[1] = 200.0f; ent.shaderRGBA[2] = 255.0f; #else //no tint ent.renderfx = RF_DISTORTION; #endif trap->R_AddRefEntityToScene( &ent ); }
void SP_misc_gamemodel(void) { char *model; vec_t angle; vec3_t angles; vec_t scale; vec3_t vScale; vec3_t org; cg_gamemodel_t *gamemodel; int i; if (CG_SpawnString("targetname", "", &model) || CG_SpawnString("scriptname", "", &model) || CG_SpawnString("spawnflags", "", &model)) { // Gordon: this model may not be static, so let the server handle it return; } if (cg.numMiscGameModels >= MAX_STATIC_GAMEMODELS) { CG_Error("^1MAX_STATIC_GAMEMODELS(%i) hit", MAX_STATIC_GAMEMODELS); } CG_SpawnString("model", "", &model); CG_SpawnVector("origin", "0 0 0", org); if (!CG_SpawnVector("angles", "0 0 0", angles)) { if (CG_SpawnFloat("angle", "0", &angle)) { angles[YAW] = angle; } } if (!CG_SpawnVector("modelscale_vec", "1 1 1", vScale)) { if (CG_SpawnFloat("modelscale", "1", &scale)) { VectorSet(vScale, scale, scale, scale); } } gamemodel = &cgs.miscGameModels[cg.numMiscGameModels++]; gamemodel->model = trap_R_RegisterModel(model); AnglesToAxis(angles, gamemodel->axes); for (i = 0; i < 3; i++) { VectorScale(gamemodel->axes[i], vScale[i], gamemodel->axes[i]); } VectorCopy(org, gamemodel->org); if (gamemodel->model) { vec3_t mins, maxs; trap_R_ModelBounds(gamemodel->model, mins, maxs); for (i = 0; i < 3; i++) { mins[i] *= vScale[i]; maxs[i] *= vScale[i]; } gamemodel->radius = RadiusFromBounds(mins, maxs); } else { gamemodel->radius = 0; } }