void CBaseHelicopter::DoWashPushOnAirboat( CBaseEntity *pAirboat, const Vector &vecWashToAirboat, float flWashAmount ) { // For the airboat, simply produce a small roll and a push outwards. // But don't produce a roll if we're too rolled in that direction already. // Get the actual up direction vector Vector vecUp; pAirboat->GetVectors( NULL, NULL, &vecUp ); if ( vecUp.z < MAX_AIRBOAT_ROLL_COSANGLE ) return; // Compute roll direction so that we get pushed down on the side where the rotor wash is. Vector vecRollNormal; CrossProduct( vecWashToAirboat, Vector( 0, 0, 1 ), vecRollNormal ); // Project it into the plane of the roll normal VectorMA( vecUp, -DotProduct( vecUp, vecRollNormal ), vecRollNormal, vecUp ); VectorNormalize( vecUp ); // Compute a vector which is the max direction we can roll given the roll constraint Vector vecExtremeUp; VMatrix rot; MatrixBuildRotationAboutAxis( rot, vecRollNormal, MAX_AIRBOAT_ROLL_ANGLE ); MatrixGetColumn( rot, 2, &vecExtremeUp ); // Find the angle between how vertical we are and how vertical we should be float flCosDelta = DotProduct( vecExtremeUp, vecUp ); float flDelta = acos(flCosDelta) * 180.0f / M_PI; flDelta = clamp( flDelta, 0.0f, MAX_AIRBOAT_ROLL_ANGLE ); flDelta = SimpleSplineRemapVal( flDelta, 0.0f, MAX_AIRBOAT_ROLL_ANGLE, 0.0f, 1.0f ); float flForce = 12.0f * flWashAmount * flDelta; Vector vecWashOrigin; Vector vecForce; VectorMultiply( Vector( 0, 0, -1 ), flForce, vecForce ); VectorMA( pAirboat->GetAbsOrigin(), -200.0f, vecWashToAirboat, vecWashOrigin ); pAirboat->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, vecWashOrigin, flWashAmount, DMG_BLAST ) ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCraneServerVehicle::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles, float *pFOV /*= NULL*/ ) { // FIXME: This needs to be reconciled with the other versions of this function! Assert( nRole == VEHICLE_ROLE_DRIVER ); CBasePlayer *pPlayer = ToBasePlayer( GetDrivableVehicle()->GetDriver() ); Assert( pPlayer ); *pAbsAngles = pPlayer->EyeAngles(); // yuck. this is an in/out parameter. float flPitchFactor = 1.0; matrix3x4_t vehicleEyePosToWorld; Vector vehicleEyeOrigin; QAngle vehicleEyeAngles; GetCrane()->GetAttachment( "vehicle_driver_eyes", vehicleEyeOrigin, vehicleEyeAngles ); AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld ); // Compute the relative rotation between the unperterbed eye attachment + the eye angles matrix3x4_t cameraToWorld; AngleMatrix( *pAbsAngles, cameraToWorld ); matrix3x4_t worldToEyePos; MatrixInvert( vehicleEyePosToWorld, worldToEyePos ); matrix3x4_t vehicleCameraToEyePos; ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos ); // Now perterb the attachment point vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * flPitchFactor, PITCH_CURVE_LINEAR, vehicleEyeAngles.x ); vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * flPitchFactor, ROLL_CURVE_LINEAR, vehicleEyeAngles.z ); AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld ); // Now treat the relative eye angles as being relative to this new, perterbed view position... matrix3x4_t newCameraToWorld; ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld ); // output new view abs angles MatrixAngles( newCameraToWorld, *pAbsAngles ); // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin ); }
static void RagdollAddSolids( IPhysicsEnvironment *pPhysEnv, ragdoll_t &ragdoll, const ragdollparams_t ¶ms, cache_ragdollsolid_t *pSolids, int solidCount, const cache_ragdollconstraint_t *pConstraints, int constraintCount ) { const char *pszName = params.pStudioHdr->pszName(); Vector position; matrix3x4_t xform; // init parent index for ( int i = 0; i < solidCount; i++ ) { ragdoll.list[i].parentIndex = -1; } // now set from constraints for ( int i = 0; i < constraintCount; i++ ) { // save parent index ragdoll.list[pConstraints[i].childIndex].parentIndex = pConstraints[i].parentIndex; MatrixGetColumn( pConstraints[i].constraintToAttached, 3, ragdoll.list[pConstraints[i].childIndex].originParentSpace ); } // now setup the solids, using parent indices for ( int i = 0; i < solidCount; i++ ) { ragdoll.boneIndex[i] = pSolids[i].boneIndex; pSolids[i].params.pName = pszName; pSolids[i].params.pGameData = params.pGameData; ragdoll.list[i].pObject = pPhysEnv->CreatePolyObject( params.pCollide->solids[pSolids[i].collideIndex], pSolids[i].surfacePropIndex, vec3_origin, vec3_angle, &pSolids[i].params ); ragdoll.list[i].pObject->SetGameIndex( i ); int parentIndex = ragdoll.list[i].parentIndex; MatrixCopy( params.pCurrentBones[ragdoll.boneIndex[i]], xform ); if ( parentIndex >= 0 ) { Assert(parentIndex<i); ragdoll.list[parentIndex].pObject->LocalToWorld( &position, ragdoll.list[i].originParentSpace ); MatrixSetColumn( position, 3, xform ); } ragdoll.list[i].pObject->SetPositionMatrix( xform, true ); PhysSetGameFlags( ragdoll.list[i].pObject, FVPHYSICS_PART_OF_RAGDOLL ); } ragdoll.listCount = solidCount; }
//----------------------------------------------------------------------------- // Shared code to compute the vehicle view position //----------------------------------------------------------------------------- void CFourWheelVehiclePhysics::GetVehicleViewPosition( const char *pViewAttachment, float flPitchFactor, Vector *pAbsOrigin, QAngle *pAbsAngles ) { matrix3x4_t vehicleEyePosToWorld; Vector vehicleEyeOrigin; QAngle vehicleEyeAngles; GetAttachment( pViewAttachment, vehicleEyeOrigin, vehicleEyeAngles ); AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld ); #ifdef HL2_DLL // View dampening. if ( r_VehicleViewDampen.GetInt() ) { m_pOuterServerVehicle->GetFourWheelVehicle()->DampenEyePosition( vehicleEyeOrigin, vehicleEyeAngles ); } #endif // Compute the relative rotation between the unperterbed eye attachment + the eye angles matrix3x4_t cameraToWorld; AngleMatrix( *pAbsAngles, cameraToWorld ); matrix3x4_t worldToEyePos; MatrixInvert( vehicleEyePosToWorld, worldToEyePos ); matrix3x4_t vehicleCameraToEyePos; ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos ); // Now perterb the attachment point vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * flPitchFactor, PITCH_CURVE_LINEAR, vehicleEyeAngles.x ); vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * flPitchFactor, ROLL_CURVE_LINEAR, vehicleEyeAngles.z ); AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld ); // Now treat the relative eye angles as being relative to this new, perterbed view position... matrix3x4_t newCameraToWorld; ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld ); // output new view abs angles MatrixAngles( newCameraToWorld, *pAbsAngles ); // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin ); }
static void MatrixOrthogonalize( matrix3x4_t &matrix, int column ) { Vector columns[3]; int i; for ( i = 0; i < 3; i++ ) { MatrixGetColumn( matrix, i, columns[i] ); } int index0 = column; int index1 = (column+1)%3; int index2 = (column+2)%3; columns[index2] = CrossProduct( columns[index0], columns[index1] ); columns[index1] = CrossProduct( columns[index2], columns[index0] ); VectorNormalize( columns[index2] ); VectorNormalize( columns[index1] ); MatrixSetColumn( columns[index1], index1, matrix ); MatrixSetColumn( columns[index2], index2, matrix ); }
//----------------------------------------------------------------------------- // Purpose: Create the matrix by which we'll transform the particle's local // space into world space, via the attachment's transform //----------------------------------------------------------------------------- void CLocalSpaceEmitter::SetupTransformMatrix( void ) { IClientRenderable *pRenderable = ClientEntityList().GetClientRenderableFromHandle( m_hEntity ); if ( pRenderable ) { matrix3x4_t mat; if ( pRenderable->GetAttachment( m_nAttachment, mat ) == false ) { // This attachment is bogus! Assert(0); } // Tell the particle effect so it knows Vector origin; MatrixGetColumn( mat, 3, origin ); m_ParticleEffect.SetLocalSpaceTransform( mat ); SetSortOrigin( origin ); C_BaseEntity *pEnt = pRenderable->GetIClientUnknown()->GetBaseEntity(); if ( pEnt ) { Vector vWorldMins, vWorldMaxs; float scale = pEnt->CollisionProp()->BoundingRadius(); vWorldMins[0] = origin[0] - scale; vWorldMins[1] = origin[1] - scale; vWorldMins[2] = origin[2] - scale; vWorldMaxs[0] = origin[0] + scale; vWorldMaxs[1] = origin[1] + scale; vWorldMaxs[2] = origin[2] + scale; GetBinding().SetBBox( vWorldMins, vWorldMaxs, true ); } } // We preapply the local transform because we need to squash it for viewmodel FOV. m_ParticleEffect.SetAutoApplyLocalTransform( false ); }
//----------------------------------------------------------------------------- // This is called by the base object when it's time to spawn the control panels //----------------------------------------------------------------------------- void CPlantedC4::SpawnControlPanels() { char buf[64]; // FIXME: Deal with dynamically resizing control panels? // If we're attached to an entity, spawn control panels on it instead of use CBaseAnimating *pEntityToSpawnOn = this; char *pOrgLL = "controlpanel%d_ll"; char *pOrgUR = "controlpanel%d_ur"; char *pAttachmentNameLL = pOrgLL; char *pAttachmentNameUR = pOrgUR; Assert( pEntityToSpawnOn ); // Lookup the attachment point... int nPanel; for ( nPanel = 0; true; ++nPanel ) { Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel ); int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nLLAttachmentIndex <= 0) { // Try and use my panels then pEntityToSpawnOn = this; Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel ); nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nLLAttachmentIndex <= 0) return; } Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel ); int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nURAttachmentIndex <= 0) { // Try and use my panels then Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel ); nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nURAttachmentIndex <= 0) return; } const char *pScreenName; GetControlPanelInfo( nPanel, pScreenName ); if (!pScreenName) continue; const char *pScreenClassname; GetControlPanelClassName( nPanel, pScreenClassname ); if ( !pScreenClassname ) continue; // Compute the screen size from the attachment points... matrix3x4_t panelToWorld; pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld ); matrix3x4_t worldToPanel; MatrixInvert( panelToWorld, worldToPanel ); // Now get the lower right position + transform into panel space Vector lr, lrlocal; pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld ); MatrixGetColumn( panelToWorld, 3, lr ); VectorTransform( lr, worldToPanel, lrlocal ); float flWidth = lrlocal.x; float flHeight = lrlocal.y; CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex ); pScreen->ChangeTeam( GetTeamNumber() ); pScreen->SetActualSize( flWidth, flHeight ); pScreen->SetActive( true ); pScreen->MakeVisibleOnlyToTeammates( false ); int nScreen = m_hScreens.AddToTail( ); m_hScreens[nScreen].Set( pScreen ); } }
void CWeaponIFMSteadyCam::UpdateRelativeOrientation() { if ( m_bIsLocked ) return; if ( m_bInDirectMode ) { UpdateDirectRelativeOrientation(); return; } if ( ( m_vecViewOffset.x == 0.0f ) && ( m_vecViewOffset.y == 0.0f ) ) return; // Compute a player to steadycam matrix VMatrix steadyCamToPlayer; MatrixFromAngles( m_angRelativeAngles, steadyCamToPlayer ); MatrixSetColumn( steadyCamToPlayer, 3, m_vecRelativePosition ); Vector vecCurrentForward; MatrixGetColumn( steadyCamToPlayer, 0, &vecCurrentForward ); // Create a ray in steadycam space float flMaxD = 1.0f / tan( M_PI * m_flFOV / 360.0f ); // Remap offsets into normalized space float flViewX = m_vecViewOffset.x / ( 384 / 2 ); float flViewY = m_vecViewOffset.y / ( 288 / 2 ); flViewX *= flMaxD * ifm_steadycam_mousefactor.GetFloat(); flViewY *= flMaxD * ifm_steadycam_mousefactor.GetFloat(); Vector vecSelectionDir( 1.0f, -flViewX, -flViewY ); VectorNormalize( vecSelectionDir ); // Rotate the ray into player coordinates Vector vecDesiredDirection; Vector3DMultiply( steadyCamToPlayer, vecSelectionDir, vecDesiredDirection ); float flDot = DotProduct( vecDesiredDirection, vecCurrentForward ); flDot = clamp( flDot, -1.0f, 1.0f ); float flAngle = 180.0f * acos( flDot ) / M_PI; if ( flAngle < 1e-3 ) { matrix3x4_t mat; MatrixFromForwardDirection( vecDesiredDirection, mat ); MatrixAngles( mat, m_angRelativeAngles ); return; } Vector vecAxis; CrossProduct( vecCurrentForward, vecDesiredDirection, vecAxis ); VectorNormalize( vecAxis ); float flRotateRate = ifm_steadycam_rotaterate.GetFloat(); if ( flRotateRate < 1.0f ) { flRotateRate = 1.0f; } float flRateFactor = flAngle / flRotateRate; flRateFactor *= flRateFactor * flRateFactor; float flRate = flRateFactor * 30.0f; float flMaxAngle = gpGlobals->frametime * flRate; flAngle = clamp( flAngle, 0.0f, flMaxAngle ); Vector vecNewForard; VMatrix rotation; MatrixBuildRotationAboutAxis( rotation, vecAxis, flAngle ); Vector3DMultiply( rotation, vecCurrentForward, vecNewForard ); matrix3x4_t mat; MatrixFromForwardDirection( vecNewForard, mat ); MatrixAngles( mat, m_angRelativeAngles ); Assert( m_angRelativeAngles.IsValid() ); }
inline void MatrixPosition(const matrix3x4_t &matrix, Vector &position) { MatrixGetColumn(matrix, 3, position); }
void PropBreakableCreateAll( int modelindex, IPhysicsObject *pPhysics, const breakablepropparams_t ¶ms, CBaseEntity *pEntity, int iPrecomputedBreakableCount, bool bIgnoreGibLimit, bool defaultLocation ) { // Check for prop breakable count reset. int nPropCount = props_break_max_pieces_perframe.GetInt(); if ( nPropCount != -1 ) { if ( nFrameNumber != gpGlobals->framecount ) { nPropBreakablesPerFrameCount = 0; nFrameNumber = gpGlobals->framecount; } // Check for max breakable count for the frame. if ( nPropBreakablesPerFrameCount >= nPropCount ) return; } int iMaxBreakCount = bIgnoreGibLimit ? -1 : props_break_max_pieces.GetInt(); if ( iMaxBreakCount != -1 ) { if ( iPrecomputedBreakableCount != -1 ) { iPrecomputedBreakableCount = MIN( iMaxBreakCount, iPrecomputedBreakableCount ); } else { iPrecomputedBreakableCount = iMaxBreakCount; } } #ifdef GAME_DLL // On server limit break model creation if ( !PropBreakableCapEdictsOnCreateAll(modelindex, pPhysics, params, pEntity, iPrecomputedBreakableCount ) ) { DevMsg( "Failed to create PropBreakable: would exceed MAX_EDICTS\n" ); return; } #endif vcollide_t *pCollide = modelinfo->GetVCollide( modelindex ); if ( !pCollide ) return; int nSkin = 0; CBaseEntity *pOwnerEntity = pEntity; CBaseAnimating *pOwnerAnim = NULL; if ( pPhysics ) { pOwnerEntity = static_cast<CBaseEntity *>(pPhysics->GetGameData()); } if ( pOwnerEntity ) { pOwnerAnim = pOwnerEntity->GetBaseAnimating(); if ( pOwnerAnim ) { nSkin = pOwnerAnim->m_nSkin; } } matrix3x4_t localToWorld; CStudioHdr studioHdr; const model_t *model = modelinfo->GetModel( modelindex ); if ( model ) { studioHdr.Init( modelinfo->GetStudiomodel( model ) ); } Vector parentOrigin = vec3_origin; int parentAttachment = Studio_FindAttachment( &studioHdr, "placementOrigin" ) + 1; if ( parentAttachment > 0 ) { GetAttachmentLocalSpace( &studioHdr, parentAttachment-1, localToWorld ); MatrixGetColumn( localToWorld, 3, parentOrigin ); } else { AngleMatrix( vec3_angle, localToWorld ); } CUtlVector<breakmodel_t> list; BreakModelList( list, modelindex, params.defBurstScale, params.defCollisionGroup ); if ( list.Count() ) { for ( int i = 0; i < list.Count(); i++ ) { int modelIndex = modelinfo->GetModelIndex( list[i].modelName ); if ( modelIndex <= 0 ) continue; // Skip multiplayer pieces that should be spawning on the other dll #ifdef GAME_DLL if ( gpGlobals->maxClients > 1 && breakable_multiplayer.GetBool() ) #else if ( gpGlobals->maxClients > 1 ) #endif { #ifdef GAME_DLL if ( list[i].mpBreakMode == MULTIPLAYER_BREAK_CLIENTSIDE ) continue; #else if ( list[i].mpBreakMode == MULTIPLAYER_BREAK_SERVERSIDE ) continue; #endif if ( !defaultLocation && list[i].mpBreakMode == MULTIPLAYER_BREAK_DEFAULT ) continue; } if ( ( nPropCount != -1 ) && ( nPropBreakablesPerFrameCount > nPropCount ) ) break; if ( ( iPrecomputedBreakableCount != -1 ) && ( i >= iPrecomputedBreakableCount ) ) break; matrix3x4_t matrix; AngleMatrix( params.angles, params.origin, matrix ); CStudioHdr studioHdr; const model_t *model = modelinfo->GetModel( modelIndex ); if ( model ) { studioHdr.Init( modelinfo->GetStudiomodel( model ) ); } // Increment the number of breakable props this frame. ++nPropBreakablesPerFrameCount; Vector position = vec3_origin; QAngle angles = params.angles; if ( pOwnerAnim && list[i].placementName[0] ) { if ( list[i].placementIsBone ) { int boneIndex = pOwnerAnim->LookupBone( list[i].placementName ); if ( boneIndex >= 0 ) { pOwnerAnim->GetBonePosition( boneIndex, position, angles ); AngleMatrix( angles, position, matrix ); } } else { int attachmentIndex = Studio_FindAttachment( &studioHdr, list[i].placementName ) + 1; if ( attachmentIndex > 0 ) { pOwnerAnim->GetAttachment( attachmentIndex, matrix ); MatrixAngles( matrix, angles ); } } } else { int placementIndex = Studio_FindAttachment( &studioHdr, "placementOrigin" ) + 1; Vector placementOrigin = parentOrigin; if ( placementIndex > 0 ) { GetAttachmentLocalSpace( &studioHdr, placementIndex-1, localToWorld ); MatrixGetColumn( localToWorld, 3, placementOrigin ); placementOrigin -= parentOrigin; } VectorTransform( list[i].offset - placementOrigin, matrix, position ); } Vector objectVelocity = params.velocity; if (pPhysics) { pPhysics->GetVelocityAtPoint( position, &objectVelocity ); } int nActualSkin = nSkin; if ( nActualSkin > studioHdr.numskinfamilies() ) nActualSkin = 0; CBaseEntity *pBreakable = NULL; #ifdef GAME_DLL if ( GetGibManager() == NULL || GetGibManager()->AllowedToSpawnGib() ) #endif { pBreakable = BreakModelCreateSingle( pOwnerEntity, &list[i], position, angles, objectVelocity, params.angularVelocity, nActualSkin, params ); } if ( pBreakable ) { #ifdef GAME_DLL if ( GetGibManager() ) { GetGibManager()->AddGibToLRU( pBreakable->GetBaseAnimating() ); } #endif if ( pOwnerEntity && pOwnerEntity->IsEffectActive( EF_NOSHADOW ) ) { pBreakable->AddEffects( EF_NOSHADOW ); } // If burst scale is set, this piece should 'burst' away from // the origin in addition to travelling in the wished velocity. if ( list[i].burstScale != 0.0 ) { Vector vecBurstDir = position - params.origin; // If $autocenter wasn't used, try the center of the piece if ( vecBurstDir == vec3_origin ) { vecBurstDir = pBreakable->WorldSpaceCenter() - params.origin; } VectorNormalize( vecBurstDir ); pBreakable->ApplyAbsVelocityImpulse( vecBurstDir * list[i].burstScale ); } // If this piece is supposed to be motion disabled, disable it if ( list[i].isMotionDisabled ) { IPhysicsObject *pPhysicsObject = pBreakable->VPhysicsGetObject(); if ( pPhysicsObject != NULL ) { pPhysicsObject->EnableMotion( false ); } } } } } // Then see if the propdata specifies any breakable pieces else if ( pEntity ) { IBreakableWithPropData *pBreakableInterface = dynamic_cast<IBreakableWithPropData*>(pEntity); if ( pBreakableInterface && pBreakableInterface->GetBreakableModel() != NULL_STRING && pBreakableInterface->GetBreakableCount() ) { breakmodel_t breakModel; for ( int i = 0; i < pBreakableInterface->GetBreakableCount(); i++ ) { if ( ( iPrecomputedBreakableCount != -1 ) && ( i >= iPrecomputedBreakableCount ) ) break; Q_strncpy( breakModel.modelName, g_PropDataSystem.GetRandomChunkModel(STRING(pBreakableInterface->GetBreakableModel()), pBreakableInterface->GetMaxBreakableSize()), sizeof(breakModel.modelName) ); breakModel.health = 1; breakModel.fadeTime = RandomFloat(5,10); breakModel.fadeMinDist = 0.0f; breakModel.fadeMaxDist = 0.0f; breakModel.burstScale = params.defBurstScale; breakModel.collisionGroup = COLLISION_GROUP_DEBRIS; breakModel.isRagdoll = false; breakModel.isMotionDisabled = false; breakModel.placementName[0] = 0; breakModel.placementIsBone = false; Vector vecObbSize = pEntity->CollisionProp()->OBBSize(); // Find a random point on the plane of the original's two largest axis int smallestAxis = SmallestAxis( vecObbSize ); Vector vecMins(0,0,0); Vector vecMaxs(1,1,1); vecMins[smallestAxis] = 0.5; vecMaxs[smallestAxis] = 0.5; pEntity->CollisionProp()->RandomPointInBounds( vecMins, vecMaxs, &breakModel.offset ); // Push all chunks away from the center Vector vecBurstDir = breakModel.offset - params.origin; VectorNormalize( vecBurstDir ); Vector vecVelocity = vecBurstDir * params.defBurstScale; QAngle vecAngles = pEntity->GetAbsAngles(); int iSkin = pBreakableInterface->GetBreakableSkin(); CBaseEntity *pBreakable = NULL; #ifdef GAME_DLL if ( GetGibManager() == NULL || GetGibManager()->AllowedToSpawnGib() ) #endif { pBreakable = BreakModelCreateSingle( pOwnerEntity, &breakModel, breakModel.offset, vecAngles, vecVelocity, vec3_origin/*params.angularVelocity*/, iSkin, params ); if ( !pBreakable ) { DevWarning( "PropBreakableCreateAll: Could not create model %s\n", breakModel.modelName ); } } if ( pBreakable ) { #ifdef GAME_DLL if ( GetGibManager() ) { GetGibManager()->AddGibToLRU( pBreakable->GetBaseAnimating() ); } #endif Vector vecBreakableObbSize = pBreakable->CollisionProp()->OBBSize(); // Try to align the gibs along the original axis matrix3x4_t matrix; AngleMatrix( vecAngles, matrix ); AlignBoxes( &matrix, vecObbSize, vecBreakableObbSize ); MatrixAngles( matrix, vecAngles ); if ( pBreakable->VPhysicsGetObject() ) { Vector pos; pBreakable->VPhysicsGetObject()->GetPosition( &pos, NULL ); pBreakable->VPhysicsGetObject()->SetPosition( pos, vecAngles, true ); } pBreakable->SetAbsAngles( vecAngles ); if ( pOwnerEntity->IsEffectActive( EF_NOSHADOW ) ) { pBreakable->AddEffects( EF_NOSHADOW ); } } } } } }
//----------------------------------------------------------------------------- // Purpose: // Input : *pFluid - // *pObject - // *pEntity - //----------------------------------------------------------------------------- void PhysicsSplash( IPhysicsFluidController *pFluid, IPhysicsObject *pObject, CBaseEntity *pEntity ) { //FIXME: For now just allow ragdolls for E3 - jdw if ( ( pObject->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL ) == false ) return; Vector velocity; pObject->GetVelocity( &velocity, NULL ); float impactSpeed = velocity.Length(); if ( impactSpeed < 25.0f ) return; Vector normal; float dist; pFluid->GetSurfacePlane( &normal, &dist ); matrix3x4_t &matrix = pEntity->EntityToWorldTransform(); // Find the local axis that best matches the water surface normal int bestAxis = BestAxisMatchingNormal( matrix, normal ); Vector tangent, binormal; MatrixGetColumn( matrix, (bestAxis+1)%3, tangent ); binormal = CrossProduct( normal, tangent ); VectorNormalize( binormal ); tangent = CrossProduct( binormal, normal ); VectorNormalize( tangent ); // Now we have a basis tangent to the surface that matches the object's local orientation as well as possible // compute an OBB using this basis // Get object extents in basis Vector tanPts[2], binPts[2]; tanPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -tangent ); tanPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), tangent ); binPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -binormal ); binPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), binormal ); // now compute the centered bbox float mins[2], maxs[2], center[2], extents[2]; mins[0] = DotProduct( tanPts[0], tangent ); maxs[0] = DotProduct( tanPts[1], tangent ); mins[1] = DotProduct( binPts[0], binormal ); maxs[1] = DotProduct( binPts[1], binormal ); center[0] = 0.5 * (mins[0] + maxs[0]); center[1] = 0.5 * (mins[1] + maxs[1]); extents[0] = maxs[0] - center[0]; extents[1] = maxs[1] - center[1]; Vector centerPoint = center[0] * tangent + center[1] * binormal + dist * normal; Vector axes[2]; axes[0] = (maxs[0] - center[0]) * tangent; axes[1] = (maxs[1] - center[1]) * binormal; // visualize OBB hit /* Vector corner1 = centerPoint - axes[0] - axes[1]; Vector corner2 = centerPoint + axes[0] - axes[1]; Vector corner3 = centerPoint + axes[0] + axes[1]; Vector corner4 = centerPoint - axes[0] + axes[1]; NDebugOverlay::Line( corner1, corner2, 0, 0, 255, false, 10 ); NDebugOverlay::Line( corner2, corner3, 0, 0, 255, false, 10 ); NDebugOverlay::Line( corner3, corner4, 0, 0, 255, false, 10 ); NDebugOverlay::Line( corner4, corner1, 0, 0, 255, false, 10 ); */ Vector corner[4]; corner[0] = centerPoint - axes[0] - axes[1]; corner[1] = centerPoint + axes[0] - axes[1]; corner[2] = centerPoint + axes[0] + axes[1]; corner[3] = centerPoint - axes[0] + axes[1]; int contents = enginetrace->GetPointContents( centerPoint-Vector(0,0,2), MASK_WATER ); bool bInSlime = ( contents & CONTENTS_SLIME ) ? true : false; Vector color = vec3_origin; float luminosity = 1.0f; if ( !bInSlime ) { // Get our lighting information FX_GetSplashLighting( centerPoint + ( normal * 8.0f ), &color, &luminosity ); } if ( impactSpeed > 150 ) { if ( bInSlime ) { FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) ); } else { FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 8, 10 ) ); } } else if ( !bInSlime ) { FX_WaterRipple( centerPoint, 1.5f, &color, 1.5f, luminosity ); } int splashes = 4; Vector point; for ( int i = 0; i < splashes; i++ ) { point = RandomVector( -32.0f, 32.0f ); point[2] = 0.0f; point += corner[i]; if ( impactSpeed > 150 ) { if ( bInSlime ) { FX_GunshotSlimeSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) ); } else { FX_GunshotSplash( centerPoint, normal, random->RandomFloat( 4, 6 ) ); } } else if ( !bInSlime ) { FX_WaterRipple( point, random->RandomFloat( 0.25f, 0.5f ), &color, luminosity, random->RandomFloat( 0.5f, 1.0f ) ); } } }
//----------------------------------------------------------------------------- // This is called by the base object when it's time to spawn the control panels //----------------------------------------------------------------------------- void CBaseViewModel::SpawnControlPanels() { #if defined( VGUI_CONTROL_PANELS ) char buf[64]; // Destroy existing panels DestroyControlPanels(); CBaseCombatWeapon *weapon = m_hWeapon.Get(); if ( weapon == NULL ) { return; } MDLCACHE_CRITICAL_SECTION(); // FIXME: Deal with dynamically resizing control panels? // If we're attached to an entity, spawn control panels on it instead of use CBaseAnimating *pEntityToSpawnOn = this; char *pOrgLL = "controlpanel%d_ll"; char *pOrgUR = "controlpanel%d_ur"; char *pAttachmentNameLL = pOrgLL; char *pAttachmentNameUR = pOrgUR; /* if ( IsBuiltOnAttachment() ) { pEntityToSpawnOn = dynamic_cast<CBaseAnimating*>((CBaseEntity*)m_hBuiltOnEntity.Get()); if ( pEntityToSpawnOn ) { char sBuildPointLL[64]; char sBuildPointUR[64]; Q_snprintf( sBuildPointLL, sizeof( sBuildPointLL ), "bp%d_controlpanel%%d_ll", m_iBuiltOnPoint ); Q_snprintf( sBuildPointUR, sizeof( sBuildPointUR ), "bp%d_controlpanel%%d_ur", m_iBuiltOnPoint ); pAttachmentNameLL = sBuildPointLL; pAttachmentNameUR = sBuildPointUR; } else { pEntityToSpawnOn = this; } } */ Assert( pEntityToSpawnOn ); // Lookup the attachment point... int nPanel; for ( nPanel = 0; true; ++nPanel ) { Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel ); int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nLLAttachmentIndex <= 0) { // Try and use my panels then pEntityToSpawnOn = this; Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel ); nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nLLAttachmentIndex <= 0) return; } Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel ); int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nURAttachmentIndex <= 0) { // Try and use my panels then Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel ); nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf); if (nURAttachmentIndex <= 0) return; } const char *pScreenName; weapon->GetControlPanelInfo( nPanel, pScreenName ); if (!pScreenName) continue; const char *pScreenClassname; weapon->GetControlPanelClassName( nPanel, pScreenClassname ); if ( !pScreenClassname ) continue; // Compute the screen size from the attachment points... matrix3x4_t panelToWorld; pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld ); matrix3x4_t worldToPanel; MatrixInvert( panelToWorld, worldToPanel ); // Now get the lower right position + transform into panel space Vector lr, lrlocal; pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld ); MatrixGetColumn( panelToWorld, 3, lr ); VectorTransform( lr, worldToPanel, lrlocal ); float flWidth = lrlocal.x; float flHeight = lrlocal.y; CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex ); pScreen->ChangeTeam( GetTeamNumber() ); pScreen->SetActualSize( flWidth, flHeight ); pScreen->SetActive( false ); pScreen->MakeVisibleOnlyToTeammates( false ); #ifdef INVASION_DLL pScreen->SetOverlayMaterial( SCREEN_OVERLAY_MATERIAL ); #endif pScreen->SetAttachedToViewModel( true ); int nScreen = m_hScreens.AddToTail( ); m_hScreens[nScreen].Set( pScreen ); } #endif }
//----------------------------------------------------------------------------- // Airboat muzzle flashes //----------------------------------------------------------------------------- void MuzzleFlash_Airboat( ClientEntityHandle_t hEntity, int attachmentIndex ) { VPROF_BUDGET( "MuzzleFlash_Airboat", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash", hEntity, attachmentIndex ); SimpleParticle *pParticle; Vector forward(1,0,0), offset; //NOTENOTE: All coords are in local space float flScale = random->RandomFloat( 0.75f, IsXbox() ? 2.0f : 2.5f ); PMaterialHandle pMuzzle[2]; pMuzzle[0] = pSimple->GetPMaterial( "effects/combinemuzzle1" ); pMuzzle[1] = pSimple->GetPMaterial( "effects/combinemuzzle2" ); // Flash for ( int i = 1; i < 7; i++ ) { offset = (forward * (i*6.0f*flScale)); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pMuzzle[random->RandomInt(0,1)], offset ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = IsXbox() ? 0.0001f : 0.01f; pParticle->m_vecVelocity.Init(); pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 128; pParticle->m_uchStartSize = ( (random->RandomFloat( 6.0f, 8.0f ) * (9-(i))/7) * flScale ); pParticle->m_uchEndSize = pParticle->m_uchStartSize; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.0f; } // Tack on the smoke pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "sprites/ar2_muzzle1" ), vec3_origin ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = 0.05f; pParticle->m_vecVelocity.Init(); pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 128; pParticle->m_uchStartSize = random->RandomFloat( 16.0f, 24.0f ); pParticle->m_uchEndSize = pParticle->m_uchStartSize; float spokePos = random->RandomInt( 0, 5 ); pParticle->m_flRoll = (360.0/6.0f)*spokePos; pParticle->m_flRollDelta = 0.0f; // Grab the origin out of the transform for the attachment if ( muzzleflash_light.GetInt() ) { // If the client hasn't seen this entity yet, bail. matrix3x4_t matAttachment; if ( FX_GetAttachmentTransform( hEntity, attachmentIndex, matAttachment ) ) { Vector origin; MatrixGetColumn( matAttachment, 3, &origin ); CreateMuzzleflashELight( origin, 5, 64, 128, hEntity ); } } }
static int vmatrix_MatrixGetColumn (lua_State *L) { MatrixGetColumn(luaL_checkvmatrix(L, 1), luaL_checkint(L, 2), &luaL_checkvector(L, 3)); return 0; }
//----------------------------------------------------------------------------- // Purpose: Vehicle dampening shared between server and client //----------------------------------------------------------------------------- void SharedVehicleViewSmoothing(CBasePlayer *pPlayer, Vector *pAbsOrigin, QAngle *pAbsAngles, bool bEnterAnimOn, bool bExitAnimOn, const Vector &vecEyeExitEndpoint, ViewSmoothingData_t *pData, float *pFOV ) { int eyeAttachmentIndex = pData->pVehicle->LookupAttachment( "vehicle_driver_eyes" ); matrix3x4_t vehicleEyePosToWorld; Vector vehicleEyeOrigin; QAngle vehicleEyeAngles; pData->pVehicle->GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles ); AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld ); // Dampen the eye positional change as we drive around. *pAbsAngles = pPlayer->EyeAngles(); if ( r_VehicleViewDampen.GetInt() && pData->bDampenEyePosition ) { CPropVehicleDriveable *pDriveable = assert_cast<CPropVehicleDriveable*>(pData->pVehicle); pDriveable->DampenEyePosition( vehicleEyeOrigin, vehicleEyeAngles ); } // Started running an entry or exit anim? bool bRunningAnim = ( bEnterAnimOn || bExitAnimOn ); if ( bRunningAnim && !pData->bWasRunningAnim ) { pData->bRunningEnterExit = true; pData->flEnterExitStartTime = gpGlobals->curtime; pData->flEnterExitDuration = pData->pVehicle->SequenceDuration( pData->pVehicle->GetSequence() ); #ifdef CLIENT_DLL pData->vecOriginSaved = PrevMainViewOrigin(); pData->vecAnglesSaved = PrevMainViewAngles(); #endif // Save our initial angular error, which we will blend out over the length of the animation. pData->vecAngleDiffSaved.x = AngleDiff( vehicleEyeAngles.x, pData->vecAnglesSaved.x ); pData->vecAngleDiffSaved.y = AngleDiff( vehicleEyeAngles.y, pData->vecAnglesSaved.y ); pData->vecAngleDiffSaved.z = AngleDiff( vehicleEyeAngles.z, pData->vecAnglesSaved.z ); pData->vecAngleDiffMin = pData->vecAngleDiffSaved; } pData->bWasRunningAnim = bRunningAnim; float frac = 0; float flFracFOV = 0; // If we're in an enter/exit animation, blend the player's eye angles to the attachment's if ( bRunningAnim || pData->bRunningEnterExit ) { *pAbsAngles = vehicleEyeAngles; // Forward integrate to determine the elapsed time in this entry/exit anim. frac = ( gpGlobals->curtime - pData->flEnterExitStartTime ) / pData->flEnterExitDuration; frac = clamp( frac, 0.0f, 1.0f ); flFracFOV = ( gpGlobals->curtime - pData->flEnterExitStartTime ) / ( pData->flEnterExitDuration * 0.85f ); flFracFOV = clamp( flFracFOV, 0.0f, 1.0f ); //Msg("Frac: %f\n", frac ); if ( frac < 1.0 ) { // Blend to the desired vehicle eye origin //Vector vecToView = (vehicleEyeOrigin - PrevMainViewOrigin()); //vehicleEyeOrigin = PrevMainViewOrigin() + (vecToView * SimpleSpline(frac)); //debugoverlay->AddBoxOverlay( vehicleEyeOrigin, -Vector(1,1,1), Vector(1,1,1), vec3_angle, 0,255,255, 64, 10 ); } else { pData->bRunningEnterExit = false; // Enter animation has finished, align view with the eye attachment point // so they can start mouselooking around. #if !defined ( HL2MP_DEV_DLL ) && !defined ( HL2MP_DEV_VEHICLE_FIX ) if ( !bExitAnimOn ) { Vector localEyeOrigin; QAngle localEyeAngles; pData->pVehicle->GetAttachmentLocal( eyeAttachmentIndex, localEyeOrigin, localEyeAngles ); #ifdef CLIENT_DLL engine->SetViewAngles( localEyeAngles ); #endif } #else #ifdef CLIENT_DLL pPlayer = C_BasePlayer::GetLocalPlayer(); #endif if ( pPlayer ) { if ( !bExitAnimOn ) { Vector localEyeOrigin; QAngle localEyeAngles; pData->pVehicle->GetAttachmentLocal( eyeAttachmentIndex, localEyeOrigin, localEyeAngles ); #ifdef CLIENT_DLL engine->SetViewAngles( localEyeAngles ); #endif } } #endif } } // Compute the relative rotation between the unperturbed eye attachment + the eye angles matrix3x4_t cameraToWorld; AngleMatrix( *pAbsAngles, cameraToWorld ); matrix3x4_t worldToEyePos; MatrixInvert( vehicleEyePosToWorld, worldToEyePos ); matrix3x4_t vehicleCameraToEyePos; ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos ); // Damp out some of the vehicle motion (neck/head would do this) if ( pData->bClampEyeAngles ) { RemapViewAngles( pData, vehicleEyeAngles ); } AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld ); // Now treat the relative eye angles as being relative to this new, perturbed view position... matrix3x4_t newCameraToWorld; ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld ); // output new view abs angles MatrixAngles( newCameraToWorld, *pAbsAngles ); // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin ); float flDefaultFOV; #ifdef CLIENT_DLL flDefaultFOV = default_fov.GetFloat(); #else flDefaultFOV = pPlayer->GetDefaultFOV(); #endif // If we're playing an entry or exit animation... if ( bRunningAnim || pData->bRunningEnterExit ) { float flSplineFrac = clamp( SimpleSpline( frac ), 0.f, 1.f ); // Blend out the error between the player's initial eye angles and the animation's initial // eye angles over the duration of the animation. QAngle vecAngleDiffBlend = ( ( 1 - flSplineFrac ) * pData->vecAngleDiffSaved ); // If our current error is less than the error amount that we're blending // out, use that. This lets the angles converge as quickly as possible. QAngle vecAngleDiffCur; vecAngleDiffCur.x = AngleDiff( vehicleEyeAngles.x, pData->vecAnglesSaved.x ); vecAngleDiffCur.y = AngleDiff( vehicleEyeAngles.y, pData->vecAnglesSaved.y ); vecAngleDiffCur.z = AngleDiff( vehicleEyeAngles.z, pData->vecAnglesSaved.z ); // In either case, never increase the error, so track the minimum error and clamp to that. for (int i = 0; i < 3; i++) { if ( fabs(vecAngleDiffCur[i] ) < fabs( pData->vecAngleDiffMin[i] ) ) { pData->vecAngleDiffMin[i] = vecAngleDiffCur[i]; } if ( fabs(vecAngleDiffBlend[i] ) < fabs( pData->vecAngleDiffMin[i] ) ) { pData->vecAngleDiffMin[i] = vecAngleDiffBlend[i]; } } // Add the error to the animation's eye angles. *pAbsAngles -= pData->vecAngleDiffMin; // Use this as the basis for the next error calculation. pData->vecAnglesSaved = *pAbsAngles; //if ( gpGlobals->frametime ) //{ // Msg("Angle : %.2f %.2f %.2f\n", target.x, target.y, target.z ); //} //Msg("Prev: %.2f %.2f %.2f\n", pData->vecAnglesSaved.x, pData->vecAnglesSaved.y, pData->vecAnglesSaved.z ); Vector vecAbsOrigin = *pAbsOrigin; // If we're exiting, our desired position is the server-sent exit position if ( bExitAnimOn ) { //debugoverlay->AddBoxOverlay( vecEyeExitEndpoint, -Vector(1,1,1), Vector(1,1,1), vec3_angle, 255,255,255, 64, 10 ); // Blend to the exit position *pAbsOrigin = Lerp( flSplineFrac, vecAbsOrigin, vecEyeExitEndpoint ); if ( pFOV != NULL ) { if ( pData->flFOV > flDefaultFOV ) { *pFOV = Lerp( flFracFOV, pData->flFOV, flDefaultFOV ); } } } else { // Blend from our starting position to the desired origin *pAbsOrigin = Lerp( flSplineFrac, pData->vecOriginSaved, vecAbsOrigin ); if ( pFOV != NULL ) { if ( pData->flFOV > flDefaultFOV ) { *pFOV = Lerp( flFracFOV, flDefaultFOV, pData->flFOV ); } } } } else if ( pFOV != NULL ) { if ( pData->flFOV > flDefaultFOV ) { // Not running an entry/exit anim. Just use the vehicle's FOV. *pFOV = pData->flFOV; } } }
//----------------------------------------------------------------------------- // Compute the bounding box's center, size, and basis //----------------------------------------------------------------------------- void C_EntityDissolve::ComputeRenderInfo( mstudiobbox_t *pHitBox, const matrix3x4_t &hitboxToWorld, Vector *pVecAbsOrigin, Vector *pXVec, Vector *pYVec ) { // Compute the center of the hitbox in worldspace Vector vecHitboxCenter; VectorAdd( pHitBox->bbmin, pHitBox->bbmax, vecHitboxCenter ); vecHitboxCenter *= 0.5f; VectorTransform( vecHitboxCenter, hitboxToWorld, *pVecAbsOrigin ); // Get the object's basis Vector vec[3]; MatrixGetColumn( hitboxToWorld, 0, vec[0] ); MatrixGetColumn( hitboxToWorld, 1, vec[1] ); MatrixGetColumn( hitboxToWorld, 2, vec[2] ); // vec[1] *= -1.0f; Vector vecViewDir; VectorSubtract( CurrentViewOrigin(), *pVecAbsOrigin, vecViewDir ); VectorNormalize( vecViewDir ); // Project the shadow casting direction into the space of the hitbox Vector localViewDir; localViewDir[0] = DotProduct( vec[0], vecViewDir ); localViewDir[1] = DotProduct( vec[1], vecViewDir ); localViewDir[2] = DotProduct( vec[2], vecViewDir ); // Figure out which vector has the largest component perpendicular // to the view direction... // Sort by how perpendicular it is int vecIdx[3]; SortAbsVectorComponents( localViewDir, vecIdx ); // Here's our hitbox basis vectors; namely the ones that are // most perpendicular to the view direction *pXVec = vec[vecIdx[0]]; *pYVec = vec[vecIdx[1]]; // Project them into a plane perpendicular to the view direction *pXVec -= vecViewDir * DotProduct( vecViewDir, *pXVec ); *pYVec -= vecViewDir * DotProduct( vecViewDir, *pYVec ); VectorNormalize( *pXVec ); VectorNormalize( *pYVec ); // Compute the hitbox size Vector boxSize; VectorSubtract( pHitBox->bbmax, pHitBox->bbmin, boxSize ); // We project the two longest sides into the vectors perpendicular // to the projection direction, then add in the projection of the perp direction Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] ); size.x *= fabs( DotProduct( vec[vecIdx[0]], *pXVec ) ); size.y *= fabs( DotProduct( vec[vecIdx[1]], *pYVec ) ); // Add the third component into x and y size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pXVec ) ); size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pYVec ) ); // Bloat a bit, since the shadow wants to extend outside the model a bit size *= 2.0f; // Clamp the minimum size Vector2DMax( size, Vector2D(10.0f, 10.0f), size ); // Factor the size into the xvec + yvec (*pXVec) *= size.x * 0.5f; (*pYVec) *= size.y * 0.5f; }
void CNPC_Portal_FloorTurret::ActiveThink( void ) { LaserOn(); //Allow descended classes a chance to do something before the think function if ( PreThink( TURRET_ACTIVE ) ) return; HackFindEnemy(); //Update our think time SetNextThink( gpGlobals->curtime + 0.1f ); CBaseEntity *pEnemy = GetEnemy(); //If we've become inactive, go back to searching if ( ( m_bActive == false ) || ( pEnemy == NULL ) ) { SetEnemy( NULL ); m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_MAX_WAIT; SetThink( &CNPC_FloorTurret::SearchThink ); m_vecGoalAngles = GetAbsAngles(); return; } //Get our shot positions Vector vecMid = EyePosition(); Vector vecMidEnemy = pEnemy->BodyTarget( vecMid ); // Store off our last seen location so we can suppress it later m_vecEnemyLKP = vecMidEnemy; //Look for our current enemy bool bEnemyInFOV = FInViewCone( pEnemy ); bool bEnemyVisible = FVisible( pEnemy ) && pEnemy->IsAlive(); //Calculate dir and dist to enemy Vector vecDirToEnemy = vecMidEnemy - vecMid; m_flDistToEnemy = VectorNormalize( vecDirToEnemy ); // If the enemy isn't in the normal fov, check the fov through portals CProp_Portal *pPortal = NULL; if ( pEnemy->IsAlive() ) { pPortal = FInViewConeThroughPortal( pEnemy ); if ( pPortal && FVisibleThroughPortal( pPortal, pEnemy ) ) { // Translate our target across the portal Vector vecMidEnemyTransformed; UTIL_Portal_PointTransform( pPortal->m_hLinkedPortal->MatrixThisToLinked(), vecMidEnemy, vecMidEnemyTransformed ); //Calculate dir and dist to enemy Vector vecDirToEnemyTransformed = vecMidEnemyTransformed - vecMid; float flDistToEnemyTransformed = VectorNormalize( vecDirToEnemyTransformed ); // If it's not visible through normal means or the enemy is closer through the portal, use the translated info if ( !bEnemyInFOV || !bEnemyVisible || flDistToEnemyTransformed < m_flDistToEnemy ) { bEnemyInFOV = true; bEnemyVisible = true; vecMidEnemy = vecMidEnemyTransformed; vecDirToEnemy = vecDirToEnemyTransformed; m_flDistToEnemy = flDistToEnemyTransformed; } else { pPortal = NULL; } } else { pPortal = NULL; } } //Draw debug info if ( g_debug_turret.GetBool() ) { NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); NDebugOverlay::Cross3D( GetEnemy()->WorldSpaceCenter(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); NDebugOverlay::Line( vecMid, GetEnemy()->WorldSpaceCenter(), 0, 255, 0, false, 0.05 ); NDebugOverlay::Cross3D( vecMid, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); NDebugOverlay::Cross3D( vecMidEnemy, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, false, 0.05 ); NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.05f ); } //See if they're past our FOV of attack if ( bEnemyInFOV == false ) { // Should we look for a new target? ClearEnemyMemory(); SetEnemy( NULL ); if ( m_spawnflags & SF_FLOOR_TURRET_FASTRETIRE ) { // Retire quickly in this case. (The case where we saw the player, but he hid again). m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_SHORT_WAIT; } else { m_flLastSight = gpGlobals->curtime + FLOOR_TURRET_MAX_WAIT; } SetThink( &CNPC_FloorTurret::SearchThink ); m_vecGoalAngles = GetAbsAngles(); SpinDown(); return; } //Current enemy is not visible if ( ( bEnemyVisible == false ) || ( m_flDistToEnemy > PORTAL_FLOOR_TURRET_RANGE )) { m_flLastSight = gpGlobals->curtime + 2.0f; ClearEnemyMemory(); SetEnemy( NULL ); SetThink( &CNPC_FloorTurret::SuppressThink ); return; } if ( g_debug_turret.GetBool() ) { Vector vecMuzzle, vecMuzzleDir; UpdateMuzzleMatrix(); MatrixGetColumn( m_muzzleToWorld, 0, vecMuzzleDir ); MatrixGetColumn( m_muzzleToWorld, 3, vecMuzzle ); // Visualize vertical firing ranges for ( int i = 0; i < 4; i++ ) { QAngle angMaxDownPitch = GetAbsAngles(); switch( i ) { case 0: angMaxDownPitch.x -= 15; break; case 1: angMaxDownPitch.x += 15; break; case 2: angMaxDownPitch.x -= 25; break; case 3: angMaxDownPitch.x += 25; break; default: break; } Vector vecMaxDownPitch; AngleVectors( angMaxDownPitch, &vecMaxDownPitch ); NDebugOverlay::Line( vecMuzzle, vecMuzzle + (vecMaxDownPitch*256), 255, 255, 255, false, 0.1 ); } } if ( m_flShotTime < gpGlobals->curtime ) { Vector vecMuzzle, vecMuzzleDir; UpdateMuzzleMatrix(); MatrixGetColumn( m_muzzleToWorld, 0, vecMuzzleDir ); MatrixGetColumn( m_muzzleToWorld, 3, vecMuzzle ); Vector2D vecDirToEnemy2D = vecDirToEnemy.AsVector2D(); Vector2D vecMuzzleDir2D = vecMuzzleDir.AsVector2D(); bool bCanShoot = true; float minCos3d = DOT_10DEGREE; // 10 degrees slop if ( m_flDistToEnemy < 60.0 ) { vecDirToEnemy2D.NormalizeInPlace(); vecMuzzleDir2D.NormalizeInPlace(); bCanShoot = ( vecDirToEnemy2D.Dot(vecMuzzleDir2D) >= DOT_10DEGREE ); minCos3d = 0.7071; // 45 degrees } //Fire the gun if ( bCanShoot ) // 10 degree slop XY { float dot3d = DotProduct( vecDirToEnemy, vecMuzzleDir ); if( m_bOutOfAmmo ) { DryFire(); } else { if ( dot3d >= minCos3d ) { SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN_IDLE ); SetActivity( (Activity)( ( m_bShootWithBottomBarrels ) ? ( ACT_FLOOR_TURRET_FIRE2 ) : ( ACT_FLOOR_TURRET_FIRE ) ) ); //Fire the weapon #if !DISABLE_SHOT Shoot( vecMuzzle, vecMuzzleDir, (dot3d < DOT_10DEGREE) ); #endif } } } } else { SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN_IDLE ); } //If we can see our enemy, face it if ( bEnemyVisible ) { //We want to look at the enemy's eyes so we don't jitter Vector vEnemyWorldSpaceCenter = pEnemy->WorldSpaceCenter(); if ( pPortal && pPortal->IsActivedAndLinked() ) { // Translate our target across the portal UTIL_Portal_PointTransform( pPortal->m_hLinkedPortal->MatrixThisToLinked(), vEnemyWorldSpaceCenter, vEnemyWorldSpaceCenter ); } Vector vecDirToEnemyEyes = vEnemyWorldSpaceCenter - vecMid; VectorNormalize( vecDirToEnemyEyes ); QAngle vecAnglesToEnemy; VectorAngles( vecDirToEnemyEyes, vecAnglesToEnemy ); m_vecGoalAngles.y = vecAnglesToEnemy.y; m_vecGoalAngles.x = vecAnglesToEnemy.x; } //Turn to face UpdateFacing(); }
void CReplayRenderer::SetupDOFMatrixSkewView( const Vector &pos, const QAngle &angles, int nSample, CViewSetup& viewSetup ) { Vector vPosition = pos; matrix3x4_t matViewMatrix; // Get transform AngleMatrix( angles, matViewMatrix ); Vector vViewDirection, vViewLeft, vViewUp; MatrixGetColumn( matViewMatrix, 0, vViewDirection ); MatrixGetColumn( matViewMatrix, 1, vViewLeft ); MatrixGetColumn( matViewMatrix, 2, vViewUp ); // Be sure these are normalized vViewDirection.NormalizeInPlace(); vViewLeft.NormalizeInPlace(); vViewUp.NormalizeInPlace(); // Set up a non-skewed off-center projection matrix to start with... (Posters already have this set up) viewSetup.m_bOffCenter = true; viewSetup.m_flOffCenterBottom = 0.0f; viewSetup.m_flOffCenterTop = 1.0f; viewSetup.m_flOffCenterLeft = 0.0f; viewSetup.m_flOffCenterRight = 1.0f; if ( IsAntialiasingEnabled() && !IsDepthOfFieldEnabled() && !m_bForceCheapDoF ) // AA jitter but no DoF { Vector2D vAAJitter = m_pJitterTable[nSample % m_nNumJitterSamples]; const float fHalfPixelRadius = 0.65; viewSetup.m_flOffCenterBottom += (vAAJitter.y / (float) viewSetup.height) * fHalfPixelRadius; viewSetup.m_flOffCenterTop += (vAAJitter.y / (float) viewSetup.height) * fHalfPixelRadius; viewSetup.m_flOffCenterLeft += (vAAJitter.x / (float) viewSetup.width) * fHalfPixelRadius; viewSetup.m_flOffCenterRight += (vAAJitter.x / (float) viewSetup.width) * fHalfPixelRadius; viewSetup.origin = vPosition; } #if 0 if ( IsDepthOfFieldEnabled() || m_bForceCheapDoF ) // DoF (independent of AA jitter) { // Try to match the amount of blurriness from legacy fulcrum method const float flDoFHack = 0.0008f; Vector2D vDoFJitter = DepthOfFieldJitter( nSample ) * pCamera->GetAperture() * flDoFHack; float fov43 = pCamera->GetFOVx(); float fHalfAngleRadians43 = DEG2RAD( 0.5f * fov43 ); float t = tan( fHalfAngleRadians43 ) * (viewSetup.m_flAspectRatio / ( 4.0f / 3.0f )); float flZFocalWidth = t * pCamera->GetFocalDistance() * 2.0f; // Width of Viewport at Focal plane Vector2D vFocalZJitter = vDoFJitter * flZFocalWidth; viewSetup.m_flOffCenterBottom += vDoFJitter.y; viewSetup.m_flOffCenterTop += vDoFJitter.y; viewSetup.m_flOffCenterLeft += vDoFJitter.x; viewSetup.m_flOffCenterRight += vDoFJitter.x; viewSetup.origin = vPosition + vViewLeft * vFocalZJitter.x - vViewUp * vFocalZJitter.y * (1.0f / viewSetup.m_flAspectRatio); if ( !m_bForceCheapDoF ) { Vector2D vAAJitter = g_vJitterTable32[nSample % 32]; // Jitter in addition to DoF offset const float fHalfPixelRadius = 0.6f; viewSetup.m_flOffCenterBottom += (vAAJitter.y / (float) viewSetup.height) * fHalfPixelRadius; viewSetup.m_flOffCenterTop += (vAAJitter.y / (float) viewSetup.height) * fHalfPixelRadius; viewSetup.m_flOffCenterLeft += (vAAJitter.x / (float) viewSetup.width) * fHalfPixelRadius; viewSetup.m_flOffCenterRight += (vAAJitter.x / (float) viewSetup.width) * fHalfPixelRadius; } } #endif MatrixAngles( matViewMatrix, viewSetup.angles ); }
inline void MatrixPosition(const matrix3x4 &matrix, Vector &vec) { MatrixGetColumn(matrix, 3, vec); }
void CRagdollPropAttached::InitRagdollAttached( IPhysicsObject *pAttached, const Vector &forceVector, int forceBone, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, CBaseAnimating *pFollow, int boneIndexRoot, const Vector &boneLocalOrigin, int parentBoneAttach, const Vector &worldAttachOrigin ) { int ragdollAttachedIndex = 0; if ( parentBoneAttach > 0 ) { CStudioHdr *pStudioHdr = GetModelPtr(); mstudiobone_t *pBone = pStudioHdr->pBone( parentBoneAttach ); ragdollAttachedIndex = pBone->physicsbone; } InitRagdoll( forceVector, forceBone, vec3_origin, pPrevBones, pBoneToWorld, dt, collisionGroup, false ); IPhysicsObject *pRefObject = m_ragdoll.list[ragdollAttachedIndex].pObject; Vector attachmentPointRagdollSpace; pRefObject->WorldToLocal( &attachmentPointRagdollSpace, worldAttachOrigin ); constraint_ragdollparams_t constraint; constraint.Defaults(); matrix3x4_t tmp, worldToAttached, worldToReference, constraintToWorld; Vector offsetWS; pAttached->LocalToWorld( &offsetWS, boneLocalOrigin ); AngleMatrix( QAngle(0, pFollow->GetAbsAngles().y, 0 ), offsetWS, constraintToWorld ); constraint.axes[0].SetAxisFriction( -2, 2, 20 ); constraint.axes[1].SetAxisFriction( 0, 0, 0 ); constraint.axes[2].SetAxisFriction( -15, 15, 20 ); // Exaggerate the bone's ability to pull the mass of the ragdoll around constraint.constraint.bodyMassScale[1] = 50.0f; pAttached->GetPositionMatrix( &tmp ); MatrixInvert( tmp, worldToAttached ); pRefObject->GetPositionMatrix( &tmp ); MatrixInvert( tmp, worldToReference ); ConcatTransforms( worldToReference, constraintToWorld, constraint.constraintToReference ); ConcatTransforms( worldToAttached, constraintToWorld, constraint.constraintToAttached ); // for now, just slam this to be the passed in value MatrixSetColumn( attachmentPointRagdollSpace, 3, constraint.constraintToReference ); PhysDisableEntityCollisions( pAttached, m_ragdoll.list[0].pObject ); m_pAttachConstraint = physenv->CreateRagdollConstraint( pRefObject, pAttached, m_ragdoll.pGroup, constraint ); SetParent( pFollow ); SetOwnerEntity( pFollow ); RagdollActivate( m_ragdoll, modelinfo->GetVCollide( GetModelIndex() ), GetModelIndex() ); // add a bunch of dampening to the ragdoll for ( int i = 0; i < m_ragdoll.listCount; i++ ) { float damping, rotdamping; m_ragdoll.list[i].pObject->GetDamping( &damping, &rotdamping ); damping *= ATTACHED_DAMPING_SCALE; rotdamping *= ATTACHED_DAMPING_SCALE; m_ragdoll.list[i].pObject->SetDamping( &damping, &rotdamping ); } m_boneIndexAttached = boneIndexRoot; m_ragdollAttachedObjectIndex = ragdollAttachedIndex; m_attachmentPointBoneSpace = boneLocalOrigin; Vector vTemp; MatrixGetColumn( constraint.constraintToReference, 3, vTemp ); m_attachmentPointRagdollSpace = vTemp; }
CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, const CTakeDamageInfo &info, int collisionGroup, bool bUseLRURetirement ) { SyncAnimatingWithPhysics( pAnimating ); CRagdollProp *pRagdoll = (CRagdollProp *)CBaseEntity::CreateNoSpawn( "prop_ragdoll", pAnimating->GetAbsOrigin(), vec3_angle, NULL ); pRagdoll->CopyAnimationDataFrom( pAnimating ); pRagdoll->SetOwnerEntity( pAnimating ); pRagdoll->InitRagdollAnimation(); matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES]; const float dt = 0.1f; // Copy over dissolve state... if ( pAnimating->IsEFlagSet( EFL_NO_DISSOLVE ) ) { pRagdoll->AddEFlags( EFL_NO_DISSOLVE ); } // NOTE: This currently is only necessary to prevent manhacks from // colliding with server ragdolls they kill pRagdoll->SetKiller( info.GetInflictor() ); pRagdoll->SetSourceClassName( pAnimating->GetClassname() ); // NPC_STATE_DEAD npc's will have their COND_IN_PVS cleared, so this needs to force SetupBones to happen unsigned short fPrevFlags = pAnimating->GetBoneCacheFlags(); pAnimating->SetBoneCacheFlags( BCF_NO_ANIMATION_SKIP ); // UNDONE: Extract velocity from bones via animation (like we do on the client) // UNDONE: For now, just move each bone by the total entity velocity if set. pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING ); // Reset previous bone flags pAnimating->ClearBoneCacheFlags( BCF_NO_ANIMATION_SKIP ); pAnimating->SetBoneCacheFlags( fPrevFlags ); memcpy( pBoneToWorldNext, pBoneToWorld, sizeof(pBoneToWorld) ); Vector vel = pAnimating->GetAbsVelocity(); if ( vel.LengthSqr() > 0 ) { int numbones = pAnimating->GetModelPtr()->numbones(); for ( int i = 0; i < numbones; i++ ) { Vector pos; MatrixGetColumn( pBoneToWorldNext[i], 3, pos ); pos += vel * dt; MatrixSetColumn( pos, 3, pBoneToWorldNext[i] ); } } // Is this a vehicle / NPC collision? if ( (info.GetDamageType() & DMG_VEHICLE) && pAnimating->MyNPCPointer() ) { // init the ragdoll with no forces pRagdoll->InitRagdoll( vec3_origin, -1, vec3_origin, pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true ); // apply vehicle forces // Get a list of bones with hitboxes below the plane of impact int boxList[128]; Vector normal(0,0,-1); int count = pAnimating->GetHitboxesFrontside( boxList, ARRAYSIZE(boxList), normal, DotProduct( normal, info.GetDamagePosition() ) ); // distribute force over mass of entire character float massScale = Studio_GetMass(pAnimating->GetModelPtr()); massScale = clamp( massScale, 1, 1e4 ); massScale = 1 / massScale; // distribute the force // BUGBUG: This will hit the same bone twice if it has two hitboxes!!!! ragdoll_t *pRagInfo = pRagdoll->GetRagdoll(); for ( int i = 0; i < count; i++ ) { int physBone = pAnimating->GetPhysicsBone( pAnimating->GetHitboxBone( boxList[i] ) ); IPhysicsObject *pPhysics = pRagInfo->list[physBone].pObject; pPhysics->ApplyForceCenter( info.GetDamageForce() * pPhysics->GetMass() * massScale ); } } else { pRagdoll->InitRagdoll( info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true ); } // Are we dissolving? if ( pAnimating->IsDissolving() ) { pRagdoll->TransferDissolveFrom( pAnimating ); } else if ( bUseLRURetirement ) { pRagdoll->AddSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ); s_RagdollLRU.MoveToTopOfLRU( pRagdoll ); } // Tracker 22598: If we don't set the OBB mins/maxs to something valid here, then the client will have a zero sized hull // for the ragdoll for one frame until Vphysics updates the real obb bounds after the first simulation frame. Having // a zero sized hull makes the ragdoll think it should be faded/alpha'd to zero for a frame, so you get a blink where // the ragdoll doesn't draw initially. Vector mins, maxs; mins = pAnimating->CollisionProp()->OBBMins(); maxs = pAnimating->CollisionProp()->OBBMaxs(); pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs ); return pRagdoll; }
//----------------------------------------------------------------------------- // Purpose: Tesla effect //----------------------------------------------------------------------------- void C_EntityDissolve::BuildTeslaEffect( mstudiobbox_t *pHitBox, const matrix3x4_t &hitboxToWorld, bool bRandom, float flYawOffset ) { Vector vecOrigin; QAngle vecAngles; MatrixGetColumn( hitboxToWorld, 3, vecOrigin ); MatrixAngles( hitboxToWorld, vecAngles.Base() ); C_BaseEntity *pEntity = GetMoveParent(); // Make a couple of tries at it int iTries = -1; Vector vecForward; trace_t tr; do { iTries++; // Some beams are deliberatly aimed around the point, the rest are random. if ( !bRandom ) { QAngle vecTemp = vecAngles; vecTemp[YAW] += flYawOffset; AngleVectors( vecTemp, &vecForward ); // Randomly angle it up or down vecForward.z = RandomFloat( -1, 1 ); } else { vecForward = RandomVector( -1, 1 ); } UTIL_TraceLine( vecOrigin, vecOrigin + (vecForward * 192), MASK_SHOT, pEntity, COLLISION_GROUP_NONE, &tr ); } while ( tr.fraction >= 1.0 && iTries < 3 ); Vector vecEnd = tr.endpos - (vecForward * 8); // Only spark & glow if we hit something if ( tr.fraction < 1.0 ) { if ( !EffectOccluded( tr.endpos ) ) { // Move it towards the camera Vector vecFlash = tr.endpos; Vector vecForward; AngleVectors( MainViewAngles(), &vecForward ); vecFlash -= (vecForward * 8); g_pEffects->EnergySplash( vecFlash, -vecForward, false ); // End glow CSmartPtr<CSimpleEmitter> pSimple = CSimpleEmitter::Create( "dust" ); pSimple->SetSortOrigin( vecFlash ); SimpleParticle *pParticle; pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/tesla_glow_noz" ), vecFlash ); if ( pParticle != NULL ) { pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = RandomFloat( 0.5, 1 ); pParticle->m_vecVelocity = vec3_origin; Vector color( 1,1,1 ); float colorRamp = RandomFloat( 0.75f, 1.25f ); pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; pParticle->m_uchStartSize = RandomFloat( 6,13 ); pParticle->m_uchEndSize = pParticle->m_uchStartSize - 2; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 10; pParticle->m_flRoll = RandomFloat( 0,360 ); pParticle->m_flRollDelta = 0; } } } // Build the tesla FX_BuildTesla( pEntity, vecOrigin, tr.endpos ); }
//----------------------------------------------------------------------------- // Draws the mesh //----------------------------------------------------------------------------- void CDmeMDL::Draw( const matrix3x4_t &shapeToWorld, CDmeDrawSettings *pDrawSettings /* = NULL */ ) { if ( !g_pMaterialSystem || !g_pMDLCache || !g_pStudioRender ) return; if ( m_MDLHandle == MDLHANDLE_INVALID ) return; // Color + alpha modulation Vector white( m_Color.r() / 255.0f, m_Color.g() / 255.0f, m_Color.b() / 255.0f ); g_pStudioRender->SetColorModulation( white.Base() ); g_pStudioRender->SetAlphaModulation( m_Color.a() / 255.0f ); DrawModelInfo_t info; info.m_pStudioHdr = g_pMDLCache->GetStudioHdr( m_MDLHandle ); info.m_pHardwareData = g_pMDLCache->GetHardwareData( m_MDLHandle ); info.m_Decals = STUDIORENDER_DECAL_INVALID; info.m_Skin = m_nSkin; info.m_Body = m_nBody; info.m_HitboxSet = 0; info.m_pClientEntity = NULL; info.m_pColorMeshes = NULL; info.m_bStaticLighting = false; info.m_Lod = m_nLOD; // FIXME: Deal with lighting for ( int i = 0; i < 6; ++ i ) { info.m_vecAmbientCube[i].Init( 1, 1, 1 ); } info.m_nLocalLightCount = 0; // info.m_LocalLightDescs; matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( info.m_pStudioHdr->numbones ); SetUpBones( shapeToWorld, info.m_pStudioHdr->numbones, pBoneToWorld ); g_pStudioRender->UnlockBoneMatrices(); Vector vecWorldViewTarget; if ( m_bWorldSpaceViewTarget ) { vecWorldViewTarget = m_vecViewTarget; } else { VectorTransform( m_vecViewTarget, shapeToWorld, vecWorldViewTarget ); } g_pStudioRender->SetEyeViewTarget( info.m_pStudioHdr, info.m_Body, vecWorldViewTarget ); // FIXME: Why is this necessary!?!?!? CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); if ( !m_bDrawInEngine ) { pRenderContext->CullMode(MATERIAL_CULLMODE_CCW); } // Set default flex values float *pFlexWeights = NULL; const int nFlexDescCount = info.m_pStudioHdr->numflexdesc; if ( nFlexDescCount ) { CStudioHdr cStudioHdr( info.m_pStudioHdr, g_pMDLCache ); float flexDefaults[ MAXSTUDIOFLEXCTRL * 4 ]; memset( flexDefaults, 0, MAXSTUDIOFLEXCTRL * 4 * sizeof( float ) ); g_pStudioRender->LockFlexWeights( info.m_pStudioHdr->numflexdesc, &pFlexWeights ); cStudioHdr.RunFlexRules( flexDefaults, pFlexWeights ); g_pStudioRender->UnlockFlexWeights(); } Vector vecModelOrigin; MatrixGetColumn( shapeToWorld, 3, vecModelOrigin ); g_pStudioRender->DrawModel( NULL, info, pBoneToWorld, pFlexWeights, NULL, vecModelOrigin, STUDIORENDER_DRAW_ENTIRE_MODEL ); // FIXME: Why is this necessary!?!?!? if ( !m_bDrawInEngine ) { pRenderContext->CullMode( MATERIAL_CULLMODE_CW ); } }
static void RagdollCreateObjects( IPhysicsCollision *pPhysCollision, IPhysicsEnvironment *pPhysEnv, IPhysicsSurfaceProps *pSurfaceDatabase, ragdoll_t &ragdoll, const ragdollparams_t ¶ms ) { ragdoll.listCount = 0; ragdoll.pGroup = NULL; memset( ragdoll.list, 0, sizeof(ragdoll.list) ); if ( !params.pCollide || params.pCollide->solidCount > RAGDOLL_MAX_ELEMENTS ) return; IVPhysicsKeyParser *pParse = pPhysCollision->VPhysicsKeyParserCreate( params.pCollide->pKeyValues ); ragdoll.pGroup = pPhysEnv->CreateConstraintGroup(); while ( !pParse->Finished() ) { const char *pBlock = pParse->GetCurrentBlockName(); if ( !strcmpi( pBlock, "solid" ) ) { solid_t solid; // collisions off by default pParse->ParseSolid( &solid, NULL ); if ( solid.index >= 0 && solid.index < params.pCollide->solidCount) { Assert( ragdoll.listCount == solid.index ); int boneIndex = Studio_BoneIndexByName( params.pStudioHdr, solid.name ); ragdoll.boneIndex[ragdoll.listCount] = boneIndex; if ( boneIndex >= 0 ) { solid.params.rotInertiaLimit = 0.5; solid.params.pGameData = params.pGameData; int surfaceData = pSurfaceDatabase->GetSurfaceIndex( solid.surfaceprop ); if ( surfaceData < 0 ) surfaceData = pSurfaceDatabase->GetSurfaceIndex( "default" ); solid.params.pName = params.pStudioHdr->name; ragdoll.list[ragdoll.listCount].pObject = pPhysEnv->CreatePolyObject( params.pCollide->solids[solid.index], surfaceData, vec3_origin, vec3_angle, &solid.params ); ragdoll.list[ragdoll.listCount].pObject->SetPositionMatrix( params.pCurrentBones[boneIndex], true ); ragdoll.list[ragdoll.listCount].parentIndex = -1; ragdoll.listCount++; } else { Msg( "CRagdollProp::CreateObjects: Couldn't Lookup Bone %s\n", solid.name ); } } } else if ( !strcmpi( pBlock, "ragdollconstraint" ) ) { constraint_ragdollparams_t constraint; pParse->ParseRagdollConstraint( &constraint, NULL ); if ( constraint.childIndex >= 0 && constraint.parentIndex >= 0 ) { Assert(constraint.childIndex<ragdoll.listCount); ragdollelement_t &childElement = ragdoll.list[constraint.childIndex]; // save parent index childElement.parentIndex = constraint.parentIndex; if ( params.jointFrictionScale > 0 ) { for ( int k = 0; k < 3; k++ ) { constraint.axes[k].torque *= params.jointFrictionScale; } } // this parent/child pair is not usually a parent/child pair in the skeleton. There // are often bones in between that are collapsed for simulation. So we need to compute // the transform. Studio_CalcBoneToBoneTransform( params.pStudioHdr, ragdoll.boneIndex[constraint.childIndex], ragdoll.boneIndex[constraint.parentIndex], constraint.constraintToAttached ); MatrixGetColumn( constraint.constraintToAttached, 3, childElement.originParentSpace ); // UNDONE: We could transform the constraint limit axes relative to the bone space // using this data. Do we need that feature? SetIdentityMatrix( constraint.constraintToReference ); pPhysEnv->DisableCollisions( ragdoll.list[constraint.parentIndex].pObject, childElement.pObject ); childElement.pConstraint = pPhysEnv->CreateRagdollConstraint( childElement.pObject, ragdoll.list[constraint.parentIndex].pObject, ragdoll.pGroup, constraint ); } } else { pParse->SkipBlock(); } } pPhysCollision->VPhysicsKeyParserDestroy( pParse ); }
//----------------------------------------------------------------------------- // Strider muzzle flashes //----------------------------------------------------------------------------- void MuzzleFlash_Strider( ClientEntityHandle_t hEntity, int attachmentIndex ) { VPROF_BUDGET( "MuzzleFlash_Strider", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); // If the client hasn't seen this entity yet, bail. matrix3x4_t matAttachment; if ( !FX_GetAttachmentTransform( hEntity, attachmentIndex, matAttachment ) ) return; CSmartPtr<CLocalSpaceEmitter> pSimple = CLocalSpaceEmitter::Create( "MuzzleFlash_Strider", hEntity, attachmentIndex ); SimpleParticle *pParticle; Vector forward(1,0,0), offset; //NOTENOTE: All coords are in local space float flScale = random->RandomFloat( 3.0f, 4.0f ); float burstSpeed = random->RandomFloat( 400.0f, 600.0f ); #define FRONT_LENGTH 12 // Front flash for ( int i = 1; i < FRONT_LENGTH; i++ ) { offset = (forward * (i*2.0f*flScale)); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = 0.1f; pParticle->m_vecVelocity = forward * burstSpeed; pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; pParticle->m_uchStartAlpha = 255.0f; pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = ( (random->RandomFloat( 6.0f, 8.0f ) * (FRONT_LENGTH-(i))/(FRONT_LENGTH*0.75f)) * flScale ); pParticle->m_uchEndSize = pParticle->m_uchStartSize; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.0f; } Vector right(0,1,0), up(0,0,1); Vector dir = right - up; #define SIDE_LENGTH 8 burstSpeed = random->RandomFloat( 400.0f, 600.0f ); // Diagonal flash for ( int i = 1; i < SIDE_LENGTH; i++ ) { offset = (dir * (i*flScale)); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = 0.2f; pParticle->m_vecVelocity = dir * burstSpeed * 0.25f; pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale ); pParticle->m_uchEndSize = pParticle->m_uchStartSize; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.0f; } dir = right + up; burstSpeed = random->RandomFloat( 400.0f, 600.0f ); // Diagonal flash for ( int i = 1; i < SIDE_LENGTH; i++ ) { offset = (-dir * (i*flScale)); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = 0.2f; pParticle->m_vecVelocity = dir * -burstSpeed * 0.25f; pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale ); pParticle->m_uchEndSize = pParticle->m_uchStartSize; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.0f; } dir = up; burstSpeed = random->RandomFloat( 400.0f, 600.0f ); // Top flash for ( int i = 1; i < SIDE_LENGTH; i++ ) { offset = (dir * (i*flScale)); pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( VarArgs( "effects/combinemuzzle%d", random->RandomInt(1,2) ) ), offset ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = 0.2f; pParticle->m_vecVelocity = dir * burstSpeed * 0.25f; pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = ( (random->RandomFloat( 2.0f, 4.0f ) * (SIDE_LENGTH-(i))/(SIDE_LENGTH*0.5f)) * flScale ); pParticle->m_uchEndSize = pParticle->m_uchStartSize; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.0f; } pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/strider_muzzle" ), vec3_origin ); if ( pParticle == NULL ) return; pParticle->m_flLifetime = 0.0f; pParticle->m_flDieTime = random->RandomFloat( 0.3f, 0.4f ); pParticle->m_vecVelocity.Init(); pParticle->m_uchColor[0] = 255; pParticle->m_uchColor[1] = 255; pParticle->m_uchColor[2] = 255; pParticle->m_uchStartAlpha = 255; pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = flScale * random->RandomFloat( 12.0f, 16.0f ); pParticle->m_uchEndSize = 0.0f; pParticle->m_flRoll = random->RandomInt( 0, 360 ); pParticle->m_flRollDelta = 0.0f; Vector origin; MatrixGetColumn( matAttachment, 3, &origin ); int entityIndex = ClientEntityList().HandleToEntIndex( hEntity ); if ( entityIndex >= 0 ) { dlight_t *el = effects->CL_AllocElight( LIGHT_INDEX_MUZZLEFLASH + entityIndex ); el->origin = origin; el->color.r = 64; el->color.g = 128; el->color.b = 255; el->color.exponent = 5; el->radius = random->RandomInt( 100, 150 ); el->decay = el->radius / 0.05f; el->die = gpGlobals->curtime + 0.1f; } }