/* * CG_ViewWeapon_UpdateProjectionSource */ static void CG_ViewWeapon_UpdateProjectionSource( vec3_t hand_origin, mat3_t hand_axis, vec3_t weap_origin, mat3_t weap_axis ) { orientation_t *tag_result = &cg.weapon.projectionSource; orientation_t tag_weapon; weaponinfo_t *weaponInfo; VectorCopy( vec3_origin, tag_weapon.origin ); Matrix3_Copy( axis_identity, tag_weapon.axis ); // move to tag_weapon CG_MoveToTag( tag_weapon.origin, tag_weapon.axis, hand_origin, hand_axis, weap_origin, weap_axis ); weaponInfo = CG_GetWeaponInfo( cg.weapon.weapon ); // move to projectionSource tag if( weaponInfo ) { VectorCopy( vec3_origin, tag_result->origin ); Matrix3_Copy( axis_identity, tag_result->axis ); CG_MoveToTag( tag_result->origin, tag_result->axis, tag_weapon.origin, tag_weapon.axis, weaponInfo->tag_projectionsource.origin, weaponInfo->tag_projectionsource.axis ); return; } // fall back: copy gun origin and move it front by 16 units and 8 up VectorCopy( tag_weapon.origin, tag_result->origin ); Matrix3_Copy( tag_weapon.axis, tag_result->axis ); VectorMA( tag_result->origin, 16, &tag_result->axis[AXIS_FORWARD], tag_result->origin ); VectorMA( tag_result->origin, 8, &tag_result->axis[AXIS_UP], tag_result->origin ); }
/* * CG_AddWeaponOnTag * * Add weapon model(s) positioned at the tag */ void CG_AddWeaponOnTag( entity_t *ent, orientation_t *tag, int weaponid, int effects, orientation_t *projectionSource, unsigned int flash_time, unsigned int barrel_time ) { entity_t weapon; weaponinfo_t *weaponInfo; float intensity; //don't try without base model if( !ent->model ) return; //don't try without a tag if( !tag ) return; weaponInfo = CG_GetWeaponInfo( weaponid ); if( !weaponInfo ) return; //if( ent->renderfx & RF_WEAPONMODEL ) // effects &= ~EF_RACEGHOST; //weapon memset( &weapon, 0, sizeof( weapon ) ); Vector4Set( weapon.shaderRGBA, 255, 255, 255, 255 ); weapon.scale = ent->scale; weapon.renderfx = ent->renderfx; weapon.frame = 0; weapon.oldframe = 0; weapon.model = weaponInfo->model[WEAPON]; CG_PlaceModelOnTag( &weapon, ent, tag ); CG_AddColoredOutLineEffect( &weapon, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &weapon ); if( !weapon.model ) return; CG_AddShellEffects( &weapon, effects ); // update projection source if( projectionSource != NULL ) { VectorCopy( vec3_origin, projectionSource->origin ); Matrix3_Copy( axis_identity, projectionSource->axis ); CG_MoveToTag( projectionSource->origin, projectionSource->axis, weapon.origin, weapon.axis, weaponInfo->tag_projectionsource.origin, weaponInfo->tag_projectionsource.axis ); } //expansion if( ( effects & EF_STRONG_WEAPON ) && weaponInfo->model[EXPANSION] ) { if( CG_GrabTag( tag, &weapon, "tag_expansion" ) ) { entity_t expansion; memset( &expansion, 0, sizeof( expansion ) ); Vector4Set( expansion.shaderRGBA, 255, 255, 255, 255 ); expansion.model = weaponInfo->model[EXPANSION]; expansion.scale = ent->scale; expansion.renderfx = ent->renderfx; expansion.frame = 0; expansion.oldframe = 0; CG_PlaceModelOnTag( &expansion, &weapon, tag ); CG_AddColoredOutLineEffect( &expansion, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &expansion ); // skelmod CG_AddShellEffects( &expansion, effects ); } } // barrel if( weaponInfo->model[BARREL] ) { if( CG_GrabTag( tag, &weapon, "tag_barrel" ) ) { orientation_t barrel_recoiled; vec3_t rotangles = { 0, 0, 0 }; entity_t barrel; memset( &barrel, 0, sizeof( barrel ) ); Vector4Set( barrel.shaderRGBA, 255, 255, 255, 255 ); barrel.model = weaponInfo->model[BARREL]; barrel.scale = ent->scale; barrel.renderfx = ent->renderfx; barrel.frame = 0; barrel.oldframe = 0; // rotation if( barrel_time > cg.time ) { intensity = (float)( barrel_time - cg.time ) / (float)weaponInfo->barrelTime; rotangles[2] = ( 360.0f * weaponInfo->barrelSpeed * intensity * intensity ); while( rotangles[2] > 360 ) rotangles[2] -= 360; // Check for tag_recoil if( CG_GrabTag( &barrel_recoiled, &weapon, "tag_recoil" ) ) VectorLerp( tag->origin, intensity, barrel_recoiled.origin, tag->origin ); } AnglesToAxis( rotangles, barrel.axis ); // barrel requires special tagging CG_PlaceRotatedModelOnTag( &barrel, &weapon, tag ); CG_AddColoredOutLineEffect( &barrel, effects, 0, 0, 0, 255 ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &barrel ); // skelmod CG_AddShellEffects( &barrel, effects ); } } if( flash_time < cg.time ) return; // flash if( !CG_GrabTag( tag, &weapon, "tag_flash" ) ) return; if( weaponInfo->model[FLASH] ) { entity_t flash; qbyte c; if( weaponInfo->flashFade ) { intensity = (float)( flash_time - cg.time )/(float)weaponInfo->flashTime; c = ( qbyte )( 255 * intensity ); } else { intensity = 1.0f; c = 255; } memset( &flash, 0, sizeof( flash ) ); Vector4Set( flash.shaderRGBA, c, c, c, c ); flash.model = weaponInfo->model[FLASH]; flash.scale = ent->scale; flash.renderfx = ent->renderfx | RF_NOSHADOW; flash.frame = 0; flash.oldframe = 0; CG_PlaceModelOnTag( &flash, &weapon, tag ); if( !( effects & EF_RACEGHOST ) ) CG_AddEntityToScene( &flash ); CG_AddLightToScene( flash.origin, weaponInfo->flashRadius * intensity, weaponInfo->flashColor[0], weaponInfo->flashColor[1], weaponInfo->flashColor[2] ); } }
/* * CG_FireWeaponEvent */ static void CG_FireWeaponEvent( int entNum, int weapon, int fireMode ) { float attenuation; struct sfx_s *sound = NULL; weaponinfo_t *weaponInfo; if( !weapon ) return; // hack idle attenuation on the plasmagun to reduce sound flood on the scene if( weapon == WEAP_PLASMAGUN ) attenuation = ATTN_IDLE; else attenuation = ATTN_NORM; weaponInfo = CG_GetWeaponInfo( weapon ); // sound if( fireMode == FIRE_MODE_STRONG ) { if( weaponInfo->num_strongfire_sounds ) sound = weaponInfo->sound_strongfire[(int)brandom( 0, weaponInfo->num_strongfire_sounds )]; } else { if( weaponInfo->num_fire_sounds ) sound = weaponInfo->sound_fire[(int)brandom( 0, weaponInfo->num_fire_sounds )]; } if( sound ) { if( ISVIEWERENTITY( entNum ) ) trap_S_StartGlobalSound( sound, CHAN_MUZZLEFLASH, cg_volume_effects->value ); else // fixed position is better for location, but the channels are used from worldspawn // and openal runs out of channels quick on cheap cards. Relative sound uses per-entity channels. trap_S_StartRelativeSound( sound, entNum, CHAN_MUZZLEFLASH, cg_volume_effects->value, attenuation ); if( ( cg_entities[entNum].current.effects & EF_QUAD ) && ( weapon != WEAP_LASERGUN ) ) { struct sfx_s *quadSfx = CG_MediaSfx( cgs.media.sfxQuadFireSound ); if( ISVIEWERENTITY( entNum ) ) trap_S_StartGlobalSound( quadSfx, CHAN_AUTO, cg_volume_effects->value ); else trap_S_StartRelativeSound( quadSfx, entNum, CHAN_AUTO, cg_volume_effects->value, attenuation ); } } // flash and barrel effects if( weapon == WEAP_GUNBLADE ) // gunblade is special { if( fireMode == FIRE_MODE_STRONG ) { // light flash if( cg_weaponFlashes->integer && weaponInfo->flashTime ) cg_entPModels[entNum].flash_time = cg.time + weaponInfo->flashTime; } else { // start barrel rotation or offsetting if( weaponInfo->barrelTime ) cg_entPModels[entNum].barrel_time = cg.time + weaponInfo->barrelTime; } } else { // light flash if( cg_weaponFlashes->integer && weaponInfo->flashTime ) cg_entPModels[entNum].flash_time = cg.time + weaponInfo->flashTime; // start barrel rotation or offsetting if( weaponInfo->barrelTime ) cg_entPModels[entNum].barrel_time = cg.time + weaponInfo->barrelTime; } // add animation to the player model switch( weapon ) { case WEAP_NONE: break; case WEAP_GUNBLADE: if( fireMode == FIRE_MODE_WEAK ) CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_BLADE, 0, EVENT_CHANNEL ); else CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_PISTOL, 0, EVENT_CHANNEL ); break; case WEAP_LASERGUN: CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_PISTOL, 0, EVENT_CHANNEL ); break; default: case WEAP_RIOTGUN: case WEAP_PLASMAGUN: CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_LIGHTWEAPON, 0, EVENT_CHANNEL ); break; case WEAP_ROCKETLAUNCHER: case WEAP_GRENADELAUNCHER: CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_HEAVYWEAPON, 0, EVENT_CHANNEL ); break; case WEAP_ELECTROBOLT: CG_PModel_AddAnimation( entNum, 0, TORSO_SHOOT_AIMWEAPON, 0, EVENT_CHANNEL ); break; } // add animation to the view weapon model if( ISVIEWERENTITY( entNum ) && !cg.view.thirdperson ) CG_ViewWeapon_StartAnimationEvent( fireMode == FIRE_MODE_STRONG ? WEAPMODEL_ATTACK_STRONG : WEAPMODEL_ATTACK_WEAK ); }
void CG_LaserBeamEffect( centity_t *cent ) { struct sfx_s *sound = NULL; float range; trace_t trace; orientation_t projectsource; vec4_t color; vec3_t laserOrigin, laserAngles, laserPoint; int i, j; if( cent->localEffects[LOCALEFFECT_LASERBEAM] <= cg.time ) return; laserOwner = cent; if( cg_teamColoredBeams->integer && ( cent->current.team == TEAM_ALPHA || cent->current.team == TEAM_BETA ) ) CG_TeamColor( cent->current.team, color ); else Vector4Set( color, 1, 1, 1, 1 ); // interpolate the positions if( ISVIEWERENTITY( cent->current.number ) && !cg.view.thirdperson ) { VectorCopy( cg.predictedPlayerState.pmove.origin, laserOrigin ); laserOrigin[2] += cg.predictedPlayerState.viewheight; VectorCopy( cg.predictedPlayerState.viewangles, laserAngles ); VectorLerp( cent->laserPointOld, cg.lerpfrac, cent->laserPoint, laserPoint ); } else { VectorLerp( cent->laserOriginOld, cg.lerpfrac, cent->laserOrigin, laserOrigin ); VectorLerp( cent->laserPointOld, cg.lerpfrac, cent->laserPoint, laserPoint ); if( !cent->laserCurved ) { vec3_t dir; // make up the angles from the start and end points (s->angles is not so precise) VectorSubtract( laserPoint, laserOrigin, dir ); VecToAngles( dir, laserAngles ); } else // use player entity angles { for( i = 0; i < 3; i++ ) laserAngles[i] = LerpAngle( cent->prev.angles[i], cent->current.angles[i], cg.lerpfrac ); } } if( !cent->laserCurved ) { range = GS_GetWeaponDef( WEAP_LASERGUN )->firedef.timeout; if( cent->current.effects & EF_QUAD ) sound = CG_MediaSfx( cgs.media.sfxLasergunStrongQuadHum ); else sound = CG_MediaSfx( cgs.media.sfxLasergunStrongHum ); // trace the beam: for tracing we use the real beam origin GS_TraceLaserBeam( &trace, laserOrigin, laserAngles, range, cent->current.number, 0, _LaserImpact ); // draw the beam: for drawing we use the weapon projection source (already handles the case of viewer entity) if( !CG_PModel_GetProjectionSource( cent->current.number, &projectsource ) ) VectorCopy( laserOrigin, projectsource.origin ); CG_KillPolyBeamsByTag( cent->current.number ); CG_LaserGunPolyBeam( projectsource.origin, trace.endpos, color, cent->current.number ); } else { float frac, subdivisions = cg_laserBeamSubdivisions->integer; vec3_t from, dir, end, blendPoint; int passthrough = cent->current.number; vec3_t tmpangles, blendAngles; range = GS_GetWeaponDef( WEAP_LASERGUN )->firedef_weak.timeout; if( cent->current.effects & EF_QUAD ) sound = CG_MediaSfx( cgs.media.sfxLasergunWeakQuadHum ); else sound = CG_MediaSfx( cgs.media.sfxLasergunWeakHum ); // trace the beam: for tracing we use the real beam origin GS_TraceCurveLaserBeam( &trace, laserOrigin, laserAngles, laserPoint, cent->current.number, 0, _LaserImpact ); // draw the beam: for drawing we use the weapon projection source (already handles the case of viewer entity) if( !CG_PModel_GetProjectionSource( cent->current.number, &projectsource ) ) VectorCopy( laserOrigin, projectsource.origin ); if( subdivisions < CURVELASERBEAM_SUBDIVISIONS ) subdivisions = CURVELASERBEAM_SUBDIVISIONS; CG_KillPolyBeamsByTag( cent->current.number ); // we redraw the full beam again, and trace each segment for stop dead impact VectorCopy( laserPoint, blendPoint ); VectorCopy( projectsource.origin, from ); VectorSubtract( blendPoint, projectsource.origin, dir ); VecToAngles( dir, blendAngles ); for( i = 1; i <= (int)subdivisions; i++ ) { frac = ( ( range/subdivisions )*(float)i ) / (float)range; for( j = 0; j < 3; j++ ) tmpangles[j] = LerpAngle( laserAngles[j], blendAngles[j], frac ); AngleVectors( tmpangles, dir, NULL, NULL ); VectorMA( projectsource.origin, range * frac, dir, end ); GS_TraceLaserBeam( &trace, from, tmpangles, DistanceFast( from, end ), passthrough, 0, NULL ); CG_LaserGunPolyBeam( from, trace.endpos, color, cent->current.number ); if( trace.fraction != 1.0f ) break; passthrough = trace.ent; VectorCopy( trace.endpos, from ); } } // enable continuous flash on the weapon owner if( cg_weaponFlashes->integer ) cg_entPModels[cent->current.number].flash_time = cg.time + CG_GetWeaponInfo( WEAP_LASERGUN )->flashTime; if( sound ) { if( ISVIEWERENTITY( cent->current.number ) ) trap_S_AddLoopSound( sound, cent->current.number, 1.0, ATTN_NONE ); else trap_S_AddLoopSound( sound, cent->current.number, 1.0, ATTN_STATIC ); } laserOwner = NULL; }
/* * CG_CalcViewWeapon */ void CG_CalcViewWeapon( cg_viewweapon_t *viewweapon ) { orientation_t tag; weaponinfo_t *weaponInfo; vec3_t gunAngles; vec3_t gunOffset; float fallfrac, fallkick; CG_ViewWeapon_RefreshAnimation( viewweapon ); //if( cg.view.thirdperson ) // return; weaponInfo = CG_GetWeaponInfo( viewweapon->weapon ); viewweapon->ent.model = weaponInfo->model[HAND]; viewweapon->ent.renderfx = RF_MINLIGHT|RF_WEAPONMODEL|RF_FORCENOLOD|(cg_shadows->integer < 2 ? RF_NOSHADOW : 0); viewweapon->ent.scale = 1.0f; viewweapon->ent.customShader = NULL; viewweapon->ent.customSkin = NULL; viewweapon->ent.rtype = RT_MODEL; Vector4Set( viewweapon->ent.shaderRGBA, 255, 255, 255, 255 ); // calculate the entity position #if 1 VectorCopy( cg.view.origin, viewweapon->ent.origin ); #else VectorCopy( cg.predictedPlayerState.pmove.origin, viewweapon->ent.origin ); viewweapon->ent.origin[2] += cg.predictedPlayerState.viewheight; #endif // weapon config offsets VectorAdd( weaponInfo->handpositionAngles, cg.predictedPlayerState.viewangles, gunAngles ); gunOffset[FORWARD] = cg_gunz->value + weaponInfo->handpositionOrigin[FORWARD]; gunOffset[RIGHT] = cg_gunx->value + weaponInfo->handpositionOrigin[RIGHT]; gunOffset[UP] = cg_guny->value + weaponInfo->handpositionOrigin[UP]; // hand cvar offset if( cgs.demoPlaying ) { if( hand->integer == 0 ) gunOffset[RIGHT] += cg_handOffset->value; else if( hand->integer == 1 ) gunOffset[RIGHT] -= cg_handOffset->value; } else { if( cgs.clientInfo[cg.view.POVent-1].hand == 0 ) gunOffset[RIGHT] += cg_handOffset->value; else if( cgs.clientInfo[cg.view.POVent-1].hand == 1 ) gunOffset[RIGHT] -= cg_handOffset->value; } // fallkick offset if( cg.weapon.fallEff_Time > cg.time ) { fallfrac = (float)( cg.time - cg.weapon.fallEff_rebTime ) / (float)( cg.weapon.fallEff_Time - cg.weapon.fallEff_rebTime ); fallkick = sin( DEG2RAD( fallfrac*180 ) ) * ( ( cg.weapon.fallEff_Time - cg.weapon.fallEff_rebTime ) * 0.01f ); } else { cg.weapon.fallEff_Time = cg.weapon.fallEff_rebTime = 0; fallkick = fallfrac = 0; } gunOffset[UP] -= fallkick; // apply the offsets #if 1 VectorMA( viewweapon->ent.origin, gunOffset[FORWARD], &cg.view.axis[AXIS_FORWARD], viewweapon->ent.origin ); VectorMA( viewweapon->ent.origin, gunOffset[RIGHT], &cg.view.axis[AXIS_RIGHT], viewweapon->ent.origin ); VectorMA( viewweapon->ent.origin, gunOffset[UP], &cg.view.axis[AXIS_UP], viewweapon->ent.origin ); #else Matrix3_FromAngles( cg.predictedPlayerState.viewangles, offsetAxis ); VectorMA( viewweapon->ent.origin, gunOffset[FORWARD], &offsetAxis[AXIS_FORWARD], viewweapon->ent.origin ); VectorMA( viewweapon->ent.origin, gunOffset[RIGHT], &offsetAxis[AXIS_RIGHT], viewweapon->ent.origin ); VectorMA( viewweapon->ent.origin, gunOffset[UP], &offsetAxis[AXIS_UP], viewweapon->ent.origin ); #endif // add angles effects CG_ViewWeapon_AddAngleEffects( gunAngles ); // finish AnglesToAxis( gunAngles, viewweapon->ent.axis ); if( cg_gun_fov->integer && !cg.predictedPlayerState.pmove.stats[PM_STAT_ZOOMTIME] ) { float gun_fov = bound( 20, cg_gun_fov->value, 160 ); float fracWeapFOV = ( 1.0f / cg.view.fracDistFOV ) * tan( gun_fov * ( M_PI/180 ) * 0.5f ); VectorScale( &viewweapon->ent.axis[AXIS_FORWARD], fracWeapFOV, &viewweapon->ent.axis[AXIS_FORWARD] ); } // if the player doesn't want to view the weapon we still have to build the projection source if( CG_GrabTag( &tag, &viewweapon->ent, "tag_weapon" ) ) CG_ViewWeapon_UpdateProjectionSource( viewweapon->ent.origin, viewweapon->ent.axis, tag.origin, tag.axis ); else CG_ViewWeapon_UpdateProjectionSource( viewweapon->ent.origin, viewweapon->ent.axis, vec3_origin, axis_identity ); }
/* * CG_ViewWeapon_RefreshAnimation */ void CG_ViewWeapon_RefreshAnimation( cg_viewweapon_t *viewweapon ) { int baseAnim; weaponinfo_t *weaponInfo; int curframe = 0; float framefrac; bool nolerp = false; // if the pov changed, or weapon changed, force restart if( viewweapon->POVnum != cg.predictedPlayerState.POVnum || viewweapon->weapon != cg.predictedPlayerState.stats[STAT_WEAPON] ) { nolerp = true; viewweapon->eventAnim = 0; viewweapon->eventAnimStartTime = 0; viewweapon->baseAnim = 0; viewweapon->baseAnimStartTime = 0; } viewweapon->POVnum = cg.predictedPlayerState.POVnum; viewweapon->weapon = cg.predictedPlayerState.stats[STAT_WEAPON]; // hack cause of missing animation config if( viewweapon->weapon == WEAP_NONE ) { viewweapon->ent.frame = viewweapon->ent.oldframe = 0; viewweapon->ent.backlerp = 0.0f; viewweapon->eventAnim = 0; viewweapon->eventAnimStartTime = 0; return; } baseAnim = CG_ViewWeapon_baseanimFromWeaponState( cg.predictedPlayerState.weaponState ); weaponInfo = CG_GetWeaponInfo( viewweapon->weapon ); // Full restart if( !viewweapon->baseAnim || !viewweapon->baseAnimStartTime ) { viewweapon->baseAnim = baseAnim; viewweapon->baseAnimStartTime = cg.time; nolerp = true; } // base animation changed? if( baseAnim != viewweapon->baseAnim ) { viewweapon->baseAnim = baseAnim; viewweapon->baseAnimStartTime = cg.time; } // if a eventual animation is running override the baseAnim if( viewweapon->eventAnim ) { if( !viewweapon->eventAnimStartTime ) viewweapon->eventAnimStartTime = cg.time; framefrac = GS_FrameForTime( &curframe, cg.time, viewweapon->eventAnimStartTime, weaponInfo->frametime[viewweapon->eventAnim], weaponInfo->firstframe[viewweapon->eventAnim], weaponInfo->lastframe[viewweapon->eventAnim], weaponInfo->loopingframes[viewweapon->eventAnim], qfalse ); if( curframe >= 0 ) goto setupframe; // disable event anim and fall through viewweapon->eventAnim = 0; viewweapon->eventAnimStartTime = 0; } // find new frame for the current animation framefrac = GS_FrameForTime( &curframe, cg.time, viewweapon->baseAnimStartTime, weaponInfo->frametime[viewweapon->baseAnim], weaponInfo->firstframe[viewweapon->baseAnim], weaponInfo->lastframe[viewweapon->baseAnim], weaponInfo->loopingframes[viewweapon->baseAnim], qtrue ); if( curframe < 0 ) CG_Error( "CG_ViewWeapon_UpdateAnimation(2): Base Animation without a defined loop.\n" ); setupframe: if( nolerp ) { framefrac = 0; viewweapon->ent.oldframe = curframe; } else { clamp( framefrac, 0, 1 ); if( curframe != viewweapon->ent.frame ) viewweapon->ent.oldframe = viewweapon->ent.frame; } viewweapon->ent.frame = curframe; viewweapon->ent.backlerp = 1.0f - framefrac; }