From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/public/jigglebones.cpp | 1550 ++++++++++++++++++++--------------------- 1 file changed, 775 insertions(+), 775 deletions(-) (limited to 'mp/src/public/jigglebones.cpp') diff --git a/mp/src/public/jigglebones.cpp b/mp/src/public/jigglebones.cpp index a8155d47..10038e58 100644 --- a/mp/src/public/jigglebones.cpp +++ b/mp/src/public/jigglebones.cpp @@ -1,775 +1,775 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//===========================================================================// - -#include "tier1/convar.h" -#include "jigglebones.h" - -#ifdef CLIENT_DLL -#include "engine/ivdebugoverlay.h" -#include "cdll_client_int.h" -#endif // CLIENT_DLL - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -#ifdef CLIENT_DLL -//----------------------------------------------------------------------------- -ConVar cl_jiggle_bone_debug( "cl_jiggle_bone_debug", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); -ConVar cl_jiggle_bone_debug_yaw_constraints( "cl_jiggle_bone_debug_yaw_constraints", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); -ConVar cl_jiggle_bone_debug_pitch_constraints( "cl_jiggle_bone_debug_pitch_constraints", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); -#endif // CLIENT_DLL - -ConVar cl_jiggle_bone_framerate_cutoff( "cl_jiggle_bone_framerate_cutoff", "45", 0, "Skip jiggle bone simulation if framerate drops below this value (frames/second)" ); - - -//----------------------------------------------------------------------------- -JiggleData * CJiggleBones::GetJiggleData( int bone, float currenttime, const Vector &initBasePos, const Vector &initTipPos ) -{ - FOR_EACH_LL( m_jiggleBoneState, it ) - { - if ( m_jiggleBoneState[it].bone == bone ) - { - return &m_jiggleBoneState[it]; - } - } - - JiggleData data; - data.Init( bone, currenttime, initBasePos, initTipPos ); - - int idx = m_jiggleBoneState.AddToHead( data ); - if ( idx == m_jiggleBoneState.InvalidIndex() ) - return NULL; - - return &m_jiggleBoneState[idx]; -} - - -//----------------------------------------------------------------------------- -/** - * Do spring physics calculations and update "jiggle bone" matrix - * (Michael Booth, Turtle Rock Studios) - */ -void CJiggleBones::BuildJiggleTransformations( int boneIndex, float currenttime, const mstudiojigglebone_t *jiggleInfo, const matrix3x4_t &goalMX, matrix3x4_t &boneMX ) -{ - Vector goalBasePosition; - MatrixPosition( goalMX, goalBasePosition ); - - Vector goalForward, goalUp, goalLeft; - MatrixGetColumn( goalMX, 0, goalLeft ); - MatrixGetColumn( goalMX, 1, goalUp ); - MatrixGetColumn( goalMX, 2, goalForward ); - - // compute goal tip position - Vector goalTip = goalBasePosition + jiggleInfo->length * goalForward; - - JiggleData *data = GetJiggleData( boneIndex, currenttime, goalBasePosition, goalTip ); - if ( !data ) - { - return; - } - - // if frames have been skipped since our last update, we were likely - // disabled and re-enabled, so re-init -#if defined(CLIENT_DLL) || defined(GAME_DLL) - float timeTolerance = 1.2f * gpGlobals->frametime; -#else - float timeTolerance = 0.5f; -#endif - - if ( currenttime - data->lastUpdate > timeTolerance ) - { - data->Init( boneIndex, currenttime, goalBasePosition, goalTip ); - } - - if ( data->lastLeft.IsZero() ) - { - data->lastLeft = goalLeft; - } - - // limit maximum deltaT to avoid simulation blowups - // if framerate is too low, skip jigglebones altogether, since movement will be too - // large between frames to simulate with a simple Euler integration - float deltaT = currenttime - data->lastUpdate; - - const float thousandHZ = 0.001f; - if ( deltaT < thousandHZ ) - { - deltaT = thousandHZ; - } - else if ( cl_jiggle_bone_framerate_cutoff.GetFloat() <= 0.0f || deltaT > ( 1.0f / cl_jiggle_bone_framerate_cutoff.GetFloat() ) ) - { - // disable jigglebone - just use goal matrix - boneMX = goalMX; - return; - } - - // we want lastUpdate here, so if jigglebones were skipped they get reinitialized if they turn back on - data->lastUpdate = currenttime; - - // - // Bone tip flex - // - if ( jiggleInfo->flags & ( JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID ) ) - { - // apply gravity in global space - data->tipAccel.z -= jiggleInfo->tipMass; - - if ( jiggleInfo->flags & JIGGLE_IS_FLEXIBLE ) - { - // decompose into local coordinates - Vector error = goalTip - data->tipPos; - - Vector localError; - localError.x = DotProduct( goalLeft, error ); - localError.y = DotProduct( goalUp, error ); - localError.z = DotProduct( goalForward, error ); - - Vector localVel; - localVel.x = DotProduct( goalLeft, data->tipVel ); - localVel.y = DotProduct( goalUp, data->tipVel ); - - // yaw spring - float yawAccel = jiggleInfo->yawStiffness * localError.x - jiggleInfo->yawDamping * localVel.x; - - // pitch spring - float pitchAccel = jiggleInfo->pitchStiffness * localError.y - jiggleInfo->pitchDamping * localVel.y; - - if ( jiggleInfo->flags & JIGGLE_HAS_LENGTH_CONSTRAINT ) - { - // drive tip towards goal tip position - data->tipAccel += yawAccel * goalLeft + pitchAccel * goalUp; - } - else - { - // allow flex along length of spring - localVel.z = DotProduct( goalForward, data->tipVel ); - - // along spring - float alongAccel = jiggleInfo->alongStiffness * localError.z - jiggleInfo->alongDamping * localVel.z; - - // drive tip towards goal tip position - data->tipAccel += yawAccel * goalLeft + pitchAccel * goalUp + alongAccel * goalForward; - } - } - - - // simple euler integration - data->tipVel += data->tipAccel * deltaT; - data->tipPos += data->tipVel * deltaT; - - // clear this timestep's accumulated accelerations - data->tipAccel = vec3_origin; - - // - // Apply optional constraints - // - if ( jiggleInfo->flags & ( JIGGLE_HAS_YAW_CONSTRAINT | JIGGLE_HAS_PITCH_CONSTRAINT ) ) - { - // find components of spring vector in local coordinate system - Vector along = data->tipPos - goalBasePosition; - Vector localAlong; - localAlong.x = DotProduct( goalLeft, along ); - localAlong.y = DotProduct( goalUp, along ); - localAlong.z = DotProduct( goalForward, along ); - - Vector localVel; - localVel.x = DotProduct( goalLeft, data->tipVel ); - localVel.y = DotProduct( goalUp, data->tipVel ); - localVel.z = DotProduct( goalForward, data->tipVel ); - - if ( jiggleInfo->flags & JIGGLE_HAS_YAW_CONSTRAINT ) - { - // enforce yaw constraints in local XZ plane - float yawError = atan2( localAlong.x, localAlong.z ); - - bool isAtLimit = false; - float yaw = 0.0f; - - if ( yawError < jiggleInfo->minYaw ) - { - // at angular limit - isAtLimit = true; - yaw = jiggleInfo->minYaw; - } - else if ( yawError > jiggleInfo->maxYaw ) - { - // at angular limit - isAtLimit = true; - yaw = jiggleInfo->maxYaw; - } - - if ( isAtLimit ) - { - float sy, cy; - SinCos( yaw, &sy, &cy ); - - // yaw matrix - matrix3x4_t yawMatrix; - - yawMatrix[0][0] = cy; - yawMatrix[1][0] = 0; - yawMatrix[2][0] = -sy; - - yawMatrix[0][1] = 0; - yawMatrix[1][1] = 1.0f; - yawMatrix[2][1] = 0; - - yawMatrix[0][2] = sy; - yawMatrix[1][2] = 0; - yawMatrix[2][2] = cy; - - yawMatrix[0][3] = 0; - yawMatrix[1][3] = 0; - yawMatrix[2][3] = 0; - - // global coordinates of limit - matrix3x4_t limitMatrix; - ConcatTransforms( goalMX, yawMatrix, limitMatrix ); - - Vector limitLeft( limitMatrix.m_flMatVal[0][0], - limitMatrix.m_flMatVal[1][0], - limitMatrix.m_flMatVal[2][0] ); - - Vector limitUp( limitMatrix.m_flMatVal[0][1], - limitMatrix.m_flMatVal[1][1], - limitMatrix.m_flMatVal[2][1] ); - - Vector limitForward( limitMatrix.m_flMatVal[0][2], - limitMatrix.m_flMatVal[1][2], - limitMatrix.m_flMatVal[2][2] ); - -#ifdef CLIENT_DLL - if ( cl_jiggle_bone_debug_yaw_constraints.GetBool() ) - { - float dT = 0.01f; - const float axisSize = 10.0f; - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitLeft, 0, 255, 255, true, dT ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitUp, 255, 255, 0, true, dT ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitForward, 255, 0, 255, true, dT ); - } -#endif // CLIENT_DLL - - Vector limitAlong( DotProduct( limitLeft, along ), - DotProduct( limitUp, along ), - DotProduct( limitForward, along ) ); - - // clip to limit plane - data->tipPos = goalBasePosition + limitAlong.y * limitUp + limitAlong.z * limitForward; - - // removed friction and velocity clipping against constraint - was causing simulation blowups (MSB 12/9/2010) - data->tipVel.Zero(); - - // update along vectors for use by pitch constraint - along = data->tipPos - goalBasePosition; - localAlong.x = DotProduct( goalLeft, along ); - localAlong.y = DotProduct( goalUp, along ); - localAlong.z = DotProduct( goalForward, along ); - - localVel.x = DotProduct( goalLeft, data->tipVel ); - localVel.y = DotProduct( goalUp, data->tipVel ); - localVel.z = DotProduct( goalForward, data->tipVel ); - } - } - - - if ( jiggleInfo->flags & JIGGLE_HAS_PITCH_CONSTRAINT ) - { - // enforce pitch constraints in local YZ plane - float pitchError = atan2( localAlong.y, localAlong.z ); - - bool isAtLimit = false; - float pitch = 0.0f; - - if ( pitchError < jiggleInfo->minPitch ) - { - // at angular limit - isAtLimit = true; - pitch = jiggleInfo->minPitch; - } - else if ( pitchError > jiggleInfo->maxPitch ) - { - // at angular limit - isAtLimit = true; - pitch = jiggleInfo->maxPitch; - } - - if ( isAtLimit ) - { - float sp, cp; - SinCos( pitch, &sp, &cp ); - - // pitch matrix - matrix3x4_t pitchMatrix; - - pitchMatrix[0][0] = 1.0f; - pitchMatrix[1][0] = 0; - pitchMatrix[2][0] = 0; - - pitchMatrix[0][1] = 0; - pitchMatrix[1][1] = cp; - pitchMatrix[2][1] = -sp; - - pitchMatrix[0][2] = 0; - pitchMatrix[1][2] = sp; - pitchMatrix[2][2] = cp; - - pitchMatrix[0][3] = 0; - pitchMatrix[1][3] = 0; - pitchMatrix[2][3] = 0; - - // global coordinates of limit - matrix3x4_t limitMatrix; - ConcatTransforms( goalMX, pitchMatrix, limitMatrix ); - - Vector limitLeft( limitMatrix.m_flMatVal[0][0], - limitMatrix.m_flMatVal[1][0], - limitMatrix.m_flMatVal[2][0] ); - - Vector limitUp( limitMatrix.m_flMatVal[0][1], - limitMatrix.m_flMatVal[1][1], - limitMatrix.m_flMatVal[2][1] ); - - Vector limitForward( limitMatrix.m_flMatVal[0][2], - limitMatrix.m_flMatVal[1][2], - limitMatrix.m_flMatVal[2][2] ); - -#ifdef CLIENT_DLL - if (cl_jiggle_bone_debug_pitch_constraints.GetBool()) - { - float dT = 0.01f; - const float axisSize = 10.0f; - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitLeft, 0, 255, 255, true, dT ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitUp, 255, 255, 0, true, dT ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitForward, 255, 0, 255, true, dT ); - } -#endif // CLIENT_DLL - - Vector limitAlong( DotProduct( limitLeft, along ), - DotProduct( limitUp, along ), - DotProduct( limitForward, along ) ); - - // clip to limit plane - data->tipPos = goalBasePosition + limitAlong.x * limitLeft + limitAlong.z * limitForward; - - // removed friction and velocity clipping against constraint - was causing simulation blowups (MSB 12/9/2010) - data->tipVel.Zero(); - } - } - } - - // needed for matrix assembly below - Vector forward = data->tipPos - goalBasePosition; - forward.NormalizeInPlace(); - - if ( jiggleInfo->flags & JIGGLE_HAS_ANGLE_CONSTRAINT ) - { - // enforce max angular error - Vector error = goalTip - data->tipPos; - float dot = DotProduct( forward, goalForward ); - float angleBetween = acos( dot ); - if ( dot < 0.0f ) - { - angleBetween = 2.0f * M_PI - angleBetween; - } - - if ( angleBetween > jiggleInfo->angleLimit ) - { - // at angular limit - float maxBetween = jiggleInfo->length * sin( jiggleInfo->angleLimit ); - - Vector delta = goalTip - data->tipPos; - delta.NormalizeInPlace(); - - data->tipPos = goalTip - maxBetween * delta; - - forward = data->tipPos - goalBasePosition; - forward.NormalizeInPlace(); - } - } - - if ( jiggleInfo->flags & JIGGLE_HAS_LENGTH_CONSTRAINT ) - { - // enforce spring length - data->tipPos = goalBasePosition + jiggleInfo->length * forward; - - // zero velocity along forward bone axis - data->tipVel -= DotProduct( data->tipVel, forward ) * forward; - } - - // - // Build bone matrix to align along current tip direction - // - Vector left = CrossProduct( goalUp, forward ); - left.NormalizeInPlace(); - - if ( DotProduct( left, data->lastLeft ) < 0.0f ) - { - // The bone has rotated so far its on the other side of the up vector - // resulting in the cross product result flipping 180 degrees around the up - // vector. Flip it back. - left = -left; - } - data->lastLeft = left; - -#ifdef CLIENT_DLL - if ( cl_jiggle_bone_debug.GetBool() ) - { - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 10.0f * data->lastLeft, 255, 0, 255, true, 0.01f ); - } -#endif - - Vector up = CrossProduct( forward, left ); - - boneMX[0][0] = left.x; - boneMX[1][0] = left.y; - boneMX[2][0] = left.z; - boneMX[0][1] = up.x; - boneMX[1][1] = up.y; - boneMX[2][1] = up.z; - boneMX[0][2] = forward.x; - boneMX[1][2] = forward.y; - boneMX[2][2] = forward.z; - - boneMX[0][3] = goalBasePosition.x; - boneMX[1][3] = goalBasePosition.y; - boneMX[2][3] = goalBasePosition.z; - } - - - // - // Bone base flex - // - if ( jiggleInfo->flags & JIGGLE_HAS_BASE_SPRING ) - { - // gravity - data->baseAccel.z -= jiggleInfo->baseMass; - - // simple spring - Vector error = goalBasePosition - data->basePos; - data->baseAccel += jiggleInfo->baseStiffness * error - jiggleInfo->baseDamping * data->baseVel; - - data->baseVel += data->baseAccel * deltaT; - data->basePos += data->baseVel * deltaT; - - // clear this timestep's accumulated accelerations - data->baseAccel = vec3_origin; - - // constrain to limits - error = data->basePos - goalBasePosition; - Vector localError; - localError.x = DotProduct( goalLeft, error ); - localError.y = DotProduct( goalUp, error ); - localError.z = DotProduct( goalForward, error ); - - Vector localVel; - localVel.x = DotProduct( goalLeft, data->baseVel ); - localVel.y = DotProduct( goalUp, data->baseVel ); - localVel.z = DotProduct( goalForward, data->baseVel ); - - // horizontal constraint - if ( localError.x < jiggleInfo->baseMinLeft ) - { - localError.x = jiggleInfo->baseMinLeft; - - // friction - data->baseAccel -= jiggleInfo->baseLeftFriction * (localVel.y * goalUp + localVel.z * goalForward); - } - else if ( localError.x > jiggleInfo->baseMaxLeft ) - { - localError.x = jiggleInfo->baseMaxLeft; - - // friction - data->baseAccel -= jiggleInfo->baseLeftFriction * (localVel.y * goalUp + localVel.z * goalForward); - } - - if ( localError.y < jiggleInfo->baseMinUp ) - { - localError.y = jiggleInfo->baseMinUp; - - // friction - data->baseAccel -= jiggleInfo->baseUpFriction * (localVel.x * goalLeft + localVel.z * goalForward); - } - else if ( localError.y > jiggleInfo->baseMaxUp ) - { - localError.y = jiggleInfo->baseMaxUp; - - // friction - data->baseAccel -= jiggleInfo->baseUpFriction * (localVel.x * goalLeft + localVel.z * goalForward); - } - - if ( localError.z < jiggleInfo->baseMinForward ) - { - localError.z = jiggleInfo->baseMinForward; - - // friction - data->baseAccel -= jiggleInfo->baseForwardFriction * (localVel.x * goalLeft + localVel.y * goalUp); - } - else if ( localError.z > jiggleInfo->baseMaxForward ) - { - localError.z = jiggleInfo->baseMaxForward; - - // friction - data->baseAccel -= jiggleInfo->baseForwardFriction * (localVel.x * goalLeft + localVel.y * goalUp); - } - - data->basePos = goalBasePosition + localError.x * goalLeft + localError.y * goalUp + localError.z * goalForward; - - - // fix up velocity - data->baseVel = (data->basePos - data->baseLastPos) / deltaT; - data->baseLastPos = data->basePos; - - - if ( !( jiggleInfo->flags & ( JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID ) ) ) - { - // no tip flex - use bone's goal orientation - boneMX = goalMX; - } - - // update bone position - MatrixSetColumn( data->basePos, 3, boneMX ); - } - else if ( jiggleInfo->flags & JIGGLE_IS_BOING ) - { - // estimate velocity - Vector vel = goalBasePosition - data->lastBoingPos; - -#ifdef CLIENT_DLL - if ( cl_jiggle_bone_debug.GetBool() ) - { - debugoverlay->AddLineOverlay( data->lastBoingPos, goalBasePosition, 0, 128, ( gpGlobals->framecount & 0x1 ) ? 0 : 200, true, 999.9f ); - } -#endif - - data->lastBoingPos = goalBasePosition; - - float speed = vel.NormalizeInPlace(); - if ( speed < 0.00001f ) - { - vel = Vector( 0, 0, 1.0f ); - speed = 0.0f; - } - else - { - speed /= deltaT; - } - - data->boingTime += deltaT; - - // if velocity changed a lot, we impacted and should *boing* - const float minSpeed = 5.0f; // 15.0f; - const float minReBoingTime = 0.5f; - if ( ( speed > minSpeed || data->boingSpeed > minSpeed ) && data->boingTime > minReBoingTime ) - { - if ( fabs( data->boingSpeed - speed ) > jiggleInfo->boingImpactSpeed || DotProduct( vel, data->boingVelDir ) < jiggleInfo->boingImpactAngle ) - { - data->boingTime = 0.0f; - data->boingDir = -vel; - -#ifdef CLIENT_DLL - if ( cl_jiggle_bone_debug.GetBool() ) - { - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 5.0f * data->boingDir, 255, 255, 0, true, 999.9f ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + Vector( 0.1, 0, 0 ), 128, 128, 0, true, 999.9f ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + Vector( 0, 0.1, 0 ), 128, 128, 0, true, 999.9f ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + Vector( 0, 0, 0.1 ), 128, 128, 0, true, 999.9f ); - } -#endif - } - } - - data->boingVelDir = vel; - data->boingSpeed = speed; - - float damping = 1.0f - ( jiggleInfo->boingDampingRate * data->boingTime ); - if ( damping < 0.01f ) - { - // boing has entirely damped out - boneMX = goalMX; - } - else - { - damping *= damping; - damping *= damping; - - float flex = jiggleInfo->boingAmplitude * cos( jiggleInfo->boingFrequency * data->boingTime ) * damping; - - float squash = 1.0f + flex; - float stretch = 1.0f - flex; - - - boneMX[0][0] = goalLeft.x; - boneMX[1][0] = goalLeft.y; - boneMX[2][0] = goalLeft.z; - - boneMX[0][1] = goalUp.x; - boneMX[1][1] = goalUp.y; - boneMX[2][1] = goalUp.z; - - boneMX[0][2] = goalForward.x; - boneMX[1][2] = goalForward.y; - boneMX[2][2] = goalForward.z; - - boneMX[0][3] = 0.0f; - boneMX[1][3] = 0.0f; - boneMX[2][3] = 0.0f; - - - // build transform into "boing space", where Z is along primary boing axis - Vector boingSide; - if ( fabs( data->boingDir.x ) < 0.9f ) - { - boingSide = CrossProduct( data->boingDir, Vector( 1.0f, 0, 0 ) ); - } - else - { - boingSide = CrossProduct( data->boingDir, Vector( 0, 0, 1.0f ) ); - } - boingSide.NormalizeInPlace(); - - Vector boingOtherSide = CrossProduct( data->boingDir, boingSide ); - - matrix3x4_t xfrmToBoingCoordsMX; - - xfrmToBoingCoordsMX[0][0] = boingSide.x; - xfrmToBoingCoordsMX[0][1] = boingSide.y; - xfrmToBoingCoordsMX[0][2] = boingSide.z; - - xfrmToBoingCoordsMX[1][0] = boingOtherSide.x; - xfrmToBoingCoordsMX[1][1] = boingOtherSide.y; - xfrmToBoingCoordsMX[1][2] = boingOtherSide.z; - - xfrmToBoingCoordsMX[2][0] = data->boingDir.x; - xfrmToBoingCoordsMX[2][1] = data->boingDir.y; - xfrmToBoingCoordsMX[2][2] = data->boingDir.z; - - xfrmToBoingCoordsMX[0][3] = 0.0f; - xfrmToBoingCoordsMX[1][3] = 0.0f; - xfrmToBoingCoordsMX[2][3] = 0.0f; - - // build squash and stretch transform in "boing space" - matrix3x4_t boingMX; - - boingMX[0][0] = squash; - boingMX[1][0] = 0.0f; - boingMX[2][0] = 0.0f; - - boingMX[0][1] = 0.0f; - boingMX[1][1] = squash; - boingMX[2][1] = 0.0f; - - boingMX[0][2] = 0.0f; - boingMX[1][2] = 0.0f; - boingMX[2][2] = stretch; - - boingMX[0][3] = 0.0f; - boingMX[1][3] = 0.0f; - boingMX[2][3] = 0.0f; - - // transform back from boing space (inverse is transpose since orthogonal) - matrix3x4_t xfrmFromBoingCoordsMX; - xfrmFromBoingCoordsMX[0][0] = xfrmToBoingCoordsMX[0][0]; - xfrmFromBoingCoordsMX[1][0] = xfrmToBoingCoordsMX[0][1]; - xfrmFromBoingCoordsMX[2][0] = xfrmToBoingCoordsMX[0][2]; - - xfrmFromBoingCoordsMX[0][1] = xfrmToBoingCoordsMX[1][0]; - xfrmFromBoingCoordsMX[1][1] = xfrmToBoingCoordsMX[1][1]; - xfrmFromBoingCoordsMX[2][1] = xfrmToBoingCoordsMX[1][2]; - - xfrmFromBoingCoordsMX[0][2] = xfrmToBoingCoordsMX[2][0]; - xfrmFromBoingCoordsMX[1][2] = xfrmToBoingCoordsMX[2][1]; - xfrmFromBoingCoordsMX[2][2] = xfrmToBoingCoordsMX[2][2]; - - xfrmFromBoingCoordsMX[0][3] = 0.0f; - xfrmFromBoingCoordsMX[1][3] = 0.0f; - xfrmFromBoingCoordsMX[2][3] = 0.0f; - - // put it all together - matrix3x4_t xfrmMX; - MatrixMultiply( xfrmToBoingCoordsMX, boingMX, xfrmMX ); - MatrixMultiply( xfrmMX, xfrmFromBoingCoordsMX, xfrmMX ); - MatrixMultiply( boneMX, xfrmMX, boneMX ); - -#ifdef CLIENT_DLL - if ( cl_jiggle_bone_debug.GetBool() ) - { - float dT = 0.01f; - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 50.0f * data->boingDir, 255, 255, 0, true, dT ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 50.0f * boingSide, 255, 0, 255, true, dT ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 50.0f * boingOtherSide, 0, 255, 255, true, dT ); - } -#endif - - boneMX[0][3] = goalBasePosition.x; - boneMX[1][3] = goalBasePosition.y; - boneMX[2][3] = goalBasePosition.z; - } - } - else if ( !( jiggleInfo->flags & ( JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID ) ) ) - { - // no flex at all - just use goal matrix - boneMX = goalMX; - } - -#ifdef CLIENT_DLL - // debug display for client only so server doesn't try to also draw it - if ( cl_jiggle_bone_debug.GetBool() ) - { - float dT = 0.01f; - const float axisSize = 5.0f; - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalLeft, 255, 0, 0, true, dT ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalUp, 0, 255, 0, true, dT ); - debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalForward, 0, 0, 255, true, dT ); - - if ( cl_jiggle_bone_debug.GetInt() > 1 ) - { - DevMsg( "Jiggle bone #%d, basePos( %3.2f, %3.2f, %3.2f ), tipPos( %3.2f, %3.2f, %3.2f ), left( %3.2f, %3.2f, %3.2f ), up( %3.2f, %3.2f, %3.2f ), forward( %3.2f, %3.2f, %3.2f )\n", - data->bone, - goalBasePosition.x, goalBasePosition.y, goalBasePosition.z, - data->tipPos.x, data->tipPos.y, data->tipPos.z, - goalLeft.x, goalLeft.y, goalLeft.z, - goalUp.x, goalUp.y, goalUp.z, - goalForward.x, goalForward.y, goalForward.z ); - } - - const float sz = 1.0f; - - if ( jiggleInfo->flags & ( JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID ) ) - { - debugoverlay->AddLineOverlay( goalBasePosition, - data->tipPos, 255, 255, 0, true, dT ); - - debugoverlay->AddLineOverlay( data->tipPos + Vector( -sz, 0, 0 ), - data->tipPos + Vector( sz, 0, 0 ), 0, 255, 255, true, dT ); - debugoverlay->AddLineOverlay( data->tipPos + Vector( 0, -sz, 0 ), - data->tipPos + Vector( 0, sz, 0 ), 0, 255, 255, true, dT ); - debugoverlay->AddLineOverlay( data->tipPos + Vector( 0, 0, -sz ), - data->tipPos + Vector( 0, 0, sz ), 0, 255, 255, true, dT ); - } - - if ( jiggleInfo->flags & JIGGLE_HAS_BASE_SPRING ) - { - debugoverlay->AddLineOverlay( data->basePos + Vector( -sz, 0, 0 ), - data->basePos + Vector( sz, 0, 0 ), 255, 0, 255, true, dT ); - debugoverlay->AddLineOverlay( data->basePos + Vector( 0, -sz, 0 ), - data->basePos + Vector( 0, sz, 0 ), 255, 0, 255, true, dT ); - debugoverlay->AddLineOverlay( data->basePos + Vector( 0, 0, -sz ), - data->basePos + Vector( 0, 0, sz ), 255, 0, 255, true, dT ); - } - - - if ( jiggleInfo->flags & JIGGLE_IS_BOING ) - { - if ( cl_jiggle_bone_debug.GetInt() > 2 ) - { - DevMsg( " boingSpeed = %3.2f, boingVelDir( %3.2f, %3.2f, %3.2f )\n", data->boingVelDir.Length() / deltaT, data->boingVelDir.x, data->boingVelDir.y, data->boingVelDir.z ); - } - } - } -#endif // CLIENT_DLL -} - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "tier1/convar.h" +#include "jigglebones.h" + +#ifdef CLIENT_DLL +#include "engine/ivdebugoverlay.h" +#include "cdll_client_int.h" +#endif // CLIENT_DLL + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#ifdef CLIENT_DLL +//----------------------------------------------------------------------------- +ConVar cl_jiggle_bone_debug( "cl_jiggle_bone_debug", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); +ConVar cl_jiggle_bone_debug_yaw_constraints( "cl_jiggle_bone_debug_yaw_constraints", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); +ConVar cl_jiggle_bone_debug_pitch_constraints( "cl_jiggle_bone_debug_pitch_constraints", "0", FCVAR_CHEAT, "Display physics-based 'jiggle bone' debugging information" ); +#endif // CLIENT_DLL + +ConVar cl_jiggle_bone_framerate_cutoff( "cl_jiggle_bone_framerate_cutoff", "45", 0, "Skip jiggle bone simulation if framerate drops below this value (frames/second)" ); + + +//----------------------------------------------------------------------------- +JiggleData * CJiggleBones::GetJiggleData( int bone, float currenttime, const Vector &initBasePos, const Vector &initTipPos ) +{ + FOR_EACH_LL( m_jiggleBoneState, it ) + { + if ( m_jiggleBoneState[it].bone == bone ) + { + return &m_jiggleBoneState[it]; + } + } + + JiggleData data; + data.Init( bone, currenttime, initBasePos, initTipPos ); + + int idx = m_jiggleBoneState.AddToHead( data ); + if ( idx == m_jiggleBoneState.InvalidIndex() ) + return NULL; + + return &m_jiggleBoneState[idx]; +} + + +//----------------------------------------------------------------------------- +/** + * Do spring physics calculations and update "jiggle bone" matrix + * (Michael Booth, Turtle Rock Studios) + */ +void CJiggleBones::BuildJiggleTransformations( int boneIndex, float currenttime, const mstudiojigglebone_t *jiggleInfo, const matrix3x4_t &goalMX, matrix3x4_t &boneMX ) +{ + Vector goalBasePosition; + MatrixPosition( goalMX, goalBasePosition ); + + Vector goalForward, goalUp, goalLeft; + MatrixGetColumn( goalMX, 0, goalLeft ); + MatrixGetColumn( goalMX, 1, goalUp ); + MatrixGetColumn( goalMX, 2, goalForward ); + + // compute goal tip position + Vector goalTip = goalBasePosition + jiggleInfo->length * goalForward; + + JiggleData *data = GetJiggleData( boneIndex, currenttime, goalBasePosition, goalTip ); + if ( !data ) + { + return; + } + + // if frames have been skipped since our last update, we were likely + // disabled and re-enabled, so re-init +#if defined(CLIENT_DLL) || defined(GAME_DLL) + float timeTolerance = 1.2f * gpGlobals->frametime; +#else + float timeTolerance = 0.5f; +#endif + + if ( currenttime - data->lastUpdate > timeTolerance ) + { + data->Init( boneIndex, currenttime, goalBasePosition, goalTip ); + } + + if ( data->lastLeft.IsZero() ) + { + data->lastLeft = goalLeft; + } + + // limit maximum deltaT to avoid simulation blowups + // if framerate is too low, skip jigglebones altogether, since movement will be too + // large between frames to simulate with a simple Euler integration + float deltaT = currenttime - data->lastUpdate; + + const float thousandHZ = 0.001f; + if ( deltaT < thousandHZ ) + { + deltaT = thousandHZ; + } + else if ( cl_jiggle_bone_framerate_cutoff.GetFloat() <= 0.0f || deltaT > ( 1.0f / cl_jiggle_bone_framerate_cutoff.GetFloat() ) ) + { + // disable jigglebone - just use goal matrix + boneMX = goalMX; + return; + } + + // we want lastUpdate here, so if jigglebones were skipped they get reinitialized if they turn back on + data->lastUpdate = currenttime; + + // + // Bone tip flex + // + if ( jiggleInfo->flags & ( JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID ) ) + { + // apply gravity in global space + data->tipAccel.z -= jiggleInfo->tipMass; + + if ( jiggleInfo->flags & JIGGLE_IS_FLEXIBLE ) + { + // decompose into local coordinates + Vector error = goalTip - data->tipPos; + + Vector localError; + localError.x = DotProduct( goalLeft, error ); + localError.y = DotProduct( goalUp, error ); + localError.z = DotProduct( goalForward, error ); + + Vector localVel; + localVel.x = DotProduct( goalLeft, data->tipVel ); + localVel.y = DotProduct( goalUp, data->tipVel ); + + // yaw spring + float yawAccel = jiggleInfo->yawStiffness * localError.x - jiggleInfo->yawDamping * localVel.x; + + // pitch spring + float pitchAccel = jiggleInfo->pitchStiffness * localError.y - jiggleInfo->pitchDamping * localVel.y; + + if ( jiggleInfo->flags & JIGGLE_HAS_LENGTH_CONSTRAINT ) + { + // drive tip towards goal tip position + data->tipAccel += yawAccel * goalLeft + pitchAccel * goalUp; + } + else + { + // allow flex along length of spring + localVel.z = DotProduct( goalForward, data->tipVel ); + + // along spring + float alongAccel = jiggleInfo->alongStiffness * localError.z - jiggleInfo->alongDamping * localVel.z; + + // drive tip towards goal tip position + data->tipAccel += yawAccel * goalLeft + pitchAccel * goalUp + alongAccel * goalForward; + } + } + + + // simple euler integration + data->tipVel += data->tipAccel * deltaT; + data->tipPos += data->tipVel * deltaT; + + // clear this timestep's accumulated accelerations + data->tipAccel = vec3_origin; + + // + // Apply optional constraints + // + if ( jiggleInfo->flags & ( JIGGLE_HAS_YAW_CONSTRAINT | JIGGLE_HAS_PITCH_CONSTRAINT ) ) + { + // find components of spring vector in local coordinate system + Vector along = data->tipPos - goalBasePosition; + Vector localAlong; + localAlong.x = DotProduct( goalLeft, along ); + localAlong.y = DotProduct( goalUp, along ); + localAlong.z = DotProduct( goalForward, along ); + + Vector localVel; + localVel.x = DotProduct( goalLeft, data->tipVel ); + localVel.y = DotProduct( goalUp, data->tipVel ); + localVel.z = DotProduct( goalForward, data->tipVel ); + + if ( jiggleInfo->flags & JIGGLE_HAS_YAW_CONSTRAINT ) + { + // enforce yaw constraints in local XZ plane + float yawError = atan2( localAlong.x, localAlong.z ); + + bool isAtLimit = false; + float yaw = 0.0f; + + if ( yawError < jiggleInfo->minYaw ) + { + // at angular limit + isAtLimit = true; + yaw = jiggleInfo->minYaw; + } + else if ( yawError > jiggleInfo->maxYaw ) + { + // at angular limit + isAtLimit = true; + yaw = jiggleInfo->maxYaw; + } + + if ( isAtLimit ) + { + float sy, cy; + SinCos( yaw, &sy, &cy ); + + // yaw matrix + matrix3x4_t yawMatrix; + + yawMatrix[0][0] = cy; + yawMatrix[1][0] = 0; + yawMatrix[2][0] = -sy; + + yawMatrix[0][1] = 0; + yawMatrix[1][1] = 1.0f; + yawMatrix[2][1] = 0; + + yawMatrix[0][2] = sy; + yawMatrix[1][2] = 0; + yawMatrix[2][2] = cy; + + yawMatrix[0][3] = 0; + yawMatrix[1][3] = 0; + yawMatrix[2][3] = 0; + + // global coordinates of limit + matrix3x4_t limitMatrix; + ConcatTransforms( goalMX, yawMatrix, limitMatrix ); + + Vector limitLeft( limitMatrix.m_flMatVal[0][0], + limitMatrix.m_flMatVal[1][0], + limitMatrix.m_flMatVal[2][0] ); + + Vector limitUp( limitMatrix.m_flMatVal[0][1], + limitMatrix.m_flMatVal[1][1], + limitMatrix.m_flMatVal[2][1] ); + + Vector limitForward( limitMatrix.m_flMatVal[0][2], + limitMatrix.m_flMatVal[1][2], + limitMatrix.m_flMatVal[2][2] ); + +#ifdef CLIENT_DLL + if ( cl_jiggle_bone_debug_yaw_constraints.GetBool() ) + { + float dT = 0.01f; + const float axisSize = 10.0f; + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitLeft, 0, 255, 255, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitUp, 255, 255, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitForward, 255, 0, 255, true, dT ); + } +#endif // CLIENT_DLL + + Vector limitAlong( DotProduct( limitLeft, along ), + DotProduct( limitUp, along ), + DotProduct( limitForward, along ) ); + + // clip to limit plane + data->tipPos = goalBasePosition + limitAlong.y * limitUp + limitAlong.z * limitForward; + + // removed friction and velocity clipping against constraint - was causing simulation blowups (MSB 12/9/2010) + data->tipVel.Zero(); + + // update along vectors for use by pitch constraint + along = data->tipPos - goalBasePosition; + localAlong.x = DotProduct( goalLeft, along ); + localAlong.y = DotProduct( goalUp, along ); + localAlong.z = DotProduct( goalForward, along ); + + localVel.x = DotProduct( goalLeft, data->tipVel ); + localVel.y = DotProduct( goalUp, data->tipVel ); + localVel.z = DotProduct( goalForward, data->tipVel ); + } + } + + + if ( jiggleInfo->flags & JIGGLE_HAS_PITCH_CONSTRAINT ) + { + // enforce pitch constraints in local YZ plane + float pitchError = atan2( localAlong.y, localAlong.z ); + + bool isAtLimit = false; + float pitch = 0.0f; + + if ( pitchError < jiggleInfo->minPitch ) + { + // at angular limit + isAtLimit = true; + pitch = jiggleInfo->minPitch; + } + else if ( pitchError > jiggleInfo->maxPitch ) + { + // at angular limit + isAtLimit = true; + pitch = jiggleInfo->maxPitch; + } + + if ( isAtLimit ) + { + float sp, cp; + SinCos( pitch, &sp, &cp ); + + // pitch matrix + matrix3x4_t pitchMatrix; + + pitchMatrix[0][0] = 1.0f; + pitchMatrix[1][0] = 0; + pitchMatrix[2][0] = 0; + + pitchMatrix[0][1] = 0; + pitchMatrix[1][1] = cp; + pitchMatrix[2][1] = -sp; + + pitchMatrix[0][2] = 0; + pitchMatrix[1][2] = sp; + pitchMatrix[2][2] = cp; + + pitchMatrix[0][3] = 0; + pitchMatrix[1][3] = 0; + pitchMatrix[2][3] = 0; + + // global coordinates of limit + matrix3x4_t limitMatrix; + ConcatTransforms( goalMX, pitchMatrix, limitMatrix ); + + Vector limitLeft( limitMatrix.m_flMatVal[0][0], + limitMatrix.m_flMatVal[1][0], + limitMatrix.m_flMatVal[2][0] ); + + Vector limitUp( limitMatrix.m_flMatVal[0][1], + limitMatrix.m_flMatVal[1][1], + limitMatrix.m_flMatVal[2][1] ); + + Vector limitForward( limitMatrix.m_flMatVal[0][2], + limitMatrix.m_flMatVal[1][2], + limitMatrix.m_flMatVal[2][2] ); + +#ifdef CLIENT_DLL + if (cl_jiggle_bone_debug_pitch_constraints.GetBool()) + { + float dT = 0.01f; + const float axisSize = 10.0f; + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitLeft, 0, 255, 255, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitUp, 255, 255, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * limitForward, 255, 0, 255, true, dT ); + } +#endif // CLIENT_DLL + + Vector limitAlong( DotProduct( limitLeft, along ), + DotProduct( limitUp, along ), + DotProduct( limitForward, along ) ); + + // clip to limit plane + data->tipPos = goalBasePosition + limitAlong.x * limitLeft + limitAlong.z * limitForward; + + // removed friction and velocity clipping against constraint - was causing simulation blowups (MSB 12/9/2010) + data->tipVel.Zero(); + } + } + } + + // needed for matrix assembly below + Vector forward = data->tipPos - goalBasePosition; + forward.NormalizeInPlace(); + + if ( jiggleInfo->flags & JIGGLE_HAS_ANGLE_CONSTRAINT ) + { + // enforce max angular error + Vector error = goalTip - data->tipPos; + float dot = DotProduct( forward, goalForward ); + float angleBetween = acos( dot ); + if ( dot < 0.0f ) + { + angleBetween = 2.0f * M_PI - angleBetween; + } + + if ( angleBetween > jiggleInfo->angleLimit ) + { + // at angular limit + float maxBetween = jiggleInfo->length * sin( jiggleInfo->angleLimit ); + + Vector delta = goalTip - data->tipPos; + delta.NormalizeInPlace(); + + data->tipPos = goalTip - maxBetween * delta; + + forward = data->tipPos - goalBasePosition; + forward.NormalizeInPlace(); + } + } + + if ( jiggleInfo->flags & JIGGLE_HAS_LENGTH_CONSTRAINT ) + { + // enforce spring length + data->tipPos = goalBasePosition + jiggleInfo->length * forward; + + // zero velocity along forward bone axis + data->tipVel -= DotProduct( data->tipVel, forward ) * forward; + } + + // + // Build bone matrix to align along current tip direction + // + Vector left = CrossProduct( goalUp, forward ); + left.NormalizeInPlace(); + + if ( DotProduct( left, data->lastLeft ) < 0.0f ) + { + // The bone has rotated so far its on the other side of the up vector + // resulting in the cross product result flipping 180 degrees around the up + // vector. Flip it back. + left = -left; + } + data->lastLeft = left; + +#ifdef CLIENT_DLL + if ( cl_jiggle_bone_debug.GetBool() ) + { + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 10.0f * data->lastLeft, 255, 0, 255, true, 0.01f ); + } +#endif + + Vector up = CrossProduct( forward, left ); + + boneMX[0][0] = left.x; + boneMX[1][0] = left.y; + boneMX[2][0] = left.z; + boneMX[0][1] = up.x; + boneMX[1][1] = up.y; + boneMX[2][1] = up.z; + boneMX[0][2] = forward.x; + boneMX[1][2] = forward.y; + boneMX[2][2] = forward.z; + + boneMX[0][3] = goalBasePosition.x; + boneMX[1][3] = goalBasePosition.y; + boneMX[2][3] = goalBasePosition.z; + } + + + // + // Bone base flex + // + if ( jiggleInfo->flags & JIGGLE_HAS_BASE_SPRING ) + { + // gravity + data->baseAccel.z -= jiggleInfo->baseMass; + + // simple spring + Vector error = goalBasePosition - data->basePos; + data->baseAccel += jiggleInfo->baseStiffness * error - jiggleInfo->baseDamping * data->baseVel; + + data->baseVel += data->baseAccel * deltaT; + data->basePos += data->baseVel * deltaT; + + // clear this timestep's accumulated accelerations + data->baseAccel = vec3_origin; + + // constrain to limits + error = data->basePos - goalBasePosition; + Vector localError; + localError.x = DotProduct( goalLeft, error ); + localError.y = DotProduct( goalUp, error ); + localError.z = DotProduct( goalForward, error ); + + Vector localVel; + localVel.x = DotProduct( goalLeft, data->baseVel ); + localVel.y = DotProduct( goalUp, data->baseVel ); + localVel.z = DotProduct( goalForward, data->baseVel ); + + // horizontal constraint + if ( localError.x < jiggleInfo->baseMinLeft ) + { + localError.x = jiggleInfo->baseMinLeft; + + // friction + data->baseAccel -= jiggleInfo->baseLeftFriction * (localVel.y * goalUp + localVel.z * goalForward); + } + else if ( localError.x > jiggleInfo->baseMaxLeft ) + { + localError.x = jiggleInfo->baseMaxLeft; + + // friction + data->baseAccel -= jiggleInfo->baseLeftFriction * (localVel.y * goalUp + localVel.z * goalForward); + } + + if ( localError.y < jiggleInfo->baseMinUp ) + { + localError.y = jiggleInfo->baseMinUp; + + // friction + data->baseAccel -= jiggleInfo->baseUpFriction * (localVel.x * goalLeft + localVel.z * goalForward); + } + else if ( localError.y > jiggleInfo->baseMaxUp ) + { + localError.y = jiggleInfo->baseMaxUp; + + // friction + data->baseAccel -= jiggleInfo->baseUpFriction * (localVel.x * goalLeft + localVel.z * goalForward); + } + + if ( localError.z < jiggleInfo->baseMinForward ) + { + localError.z = jiggleInfo->baseMinForward; + + // friction + data->baseAccel -= jiggleInfo->baseForwardFriction * (localVel.x * goalLeft + localVel.y * goalUp); + } + else if ( localError.z > jiggleInfo->baseMaxForward ) + { + localError.z = jiggleInfo->baseMaxForward; + + // friction + data->baseAccel -= jiggleInfo->baseForwardFriction * (localVel.x * goalLeft + localVel.y * goalUp); + } + + data->basePos = goalBasePosition + localError.x * goalLeft + localError.y * goalUp + localError.z * goalForward; + + + // fix up velocity + data->baseVel = (data->basePos - data->baseLastPos) / deltaT; + data->baseLastPos = data->basePos; + + + if ( !( jiggleInfo->flags & ( JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID ) ) ) + { + // no tip flex - use bone's goal orientation + boneMX = goalMX; + } + + // update bone position + MatrixSetColumn( data->basePos, 3, boneMX ); + } + else if ( jiggleInfo->flags & JIGGLE_IS_BOING ) + { + // estimate velocity + Vector vel = goalBasePosition - data->lastBoingPos; + +#ifdef CLIENT_DLL + if ( cl_jiggle_bone_debug.GetBool() ) + { + debugoverlay->AddLineOverlay( data->lastBoingPos, goalBasePosition, 0, 128, ( gpGlobals->framecount & 0x1 ) ? 0 : 200, true, 999.9f ); + } +#endif + + data->lastBoingPos = goalBasePosition; + + float speed = vel.NormalizeInPlace(); + if ( speed < 0.00001f ) + { + vel = Vector( 0, 0, 1.0f ); + speed = 0.0f; + } + else + { + speed /= deltaT; + } + + data->boingTime += deltaT; + + // if velocity changed a lot, we impacted and should *boing* + const float minSpeed = 5.0f; // 15.0f; + const float minReBoingTime = 0.5f; + if ( ( speed > minSpeed || data->boingSpeed > minSpeed ) && data->boingTime > minReBoingTime ) + { + if ( fabs( data->boingSpeed - speed ) > jiggleInfo->boingImpactSpeed || DotProduct( vel, data->boingVelDir ) < jiggleInfo->boingImpactAngle ) + { + data->boingTime = 0.0f; + data->boingDir = -vel; + +#ifdef CLIENT_DLL + if ( cl_jiggle_bone_debug.GetBool() ) + { + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 5.0f * data->boingDir, 255, 255, 0, true, 999.9f ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + Vector( 0.1, 0, 0 ), 128, 128, 0, true, 999.9f ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + Vector( 0, 0.1, 0 ), 128, 128, 0, true, 999.9f ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + Vector( 0, 0, 0.1 ), 128, 128, 0, true, 999.9f ); + } +#endif + } + } + + data->boingVelDir = vel; + data->boingSpeed = speed; + + float damping = 1.0f - ( jiggleInfo->boingDampingRate * data->boingTime ); + if ( damping < 0.01f ) + { + // boing has entirely damped out + boneMX = goalMX; + } + else + { + damping *= damping; + damping *= damping; + + float flex = jiggleInfo->boingAmplitude * cos( jiggleInfo->boingFrequency * data->boingTime ) * damping; + + float squash = 1.0f + flex; + float stretch = 1.0f - flex; + + + boneMX[0][0] = goalLeft.x; + boneMX[1][0] = goalLeft.y; + boneMX[2][0] = goalLeft.z; + + boneMX[0][1] = goalUp.x; + boneMX[1][1] = goalUp.y; + boneMX[2][1] = goalUp.z; + + boneMX[0][2] = goalForward.x; + boneMX[1][2] = goalForward.y; + boneMX[2][2] = goalForward.z; + + boneMX[0][3] = 0.0f; + boneMX[1][3] = 0.0f; + boneMX[2][3] = 0.0f; + + + // build transform into "boing space", where Z is along primary boing axis + Vector boingSide; + if ( fabs( data->boingDir.x ) < 0.9f ) + { + boingSide = CrossProduct( data->boingDir, Vector( 1.0f, 0, 0 ) ); + } + else + { + boingSide = CrossProduct( data->boingDir, Vector( 0, 0, 1.0f ) ); + } + boingSide.NormalizeInPlace(); + + Vector boingOtherSide = CrossProduct( data->boingDir, boingSide ); + + matrix3x4_t xfrmToBoingCoordsMX; + + xfrmToBoingCoordsMX[0][0] = boingSide.x; + xfrmToBoingCoordsMX[0][1] = boingSide.y; + xfrmToBoingCoordsMX[0][2] = boingSide.z; + + xfrmToBoingCoordsMX[1][0] = boingOtherSide.x; + xfrmToBoingCoordsMX[1][1] = boingOtherSide.y; + xfrmToBoingCoordsMX[1][2] = boingOtherSide.z; + + xfrmToBoingCoordsMX[2][0] = data->boingDir.x; + xfrmToBoingCoordsMX[2][1] = data->boingDir.y; + xfrmToBoingCoordsMX[2][2] = data->boingDir.z; + + xfrmToBoingCoordsMX[0][3] = 0.0f; + xfrmToBoingCoordsMX[1][3] = 0.0f; + xfrmToBoingCoordsMX[2][3] = 0.0f; + + // build squash and stretch transform in "boing space" + matrix3x4_t boingMX; + + boingMX[0][0] = squash; + boingMX[1][0] = 0.0f; + boingMX[2][0] = 0.0f; + + boingMX[0][1] = 0.0f; + boingMX[1][1] = squash; + boingMX[2][1] = 0.0f; + + boingMX[0][2] = 0.0f; + boingMX[1][2] = 0.0f; + boingMX[2][2] = stretch; + + boingMX[0][3] = 0.0f; + boingMX[1][3] = 0.0f; + boingMX[2][3] = 0.0f; + + // transform back from boing space (inverse is transpose since orthogonal) + matrix3x4_t xfrmFromBoingCoordsMX; + xfrmFromBoingCoordsMX[0][0] = xfrmToBoingCoordsMX[0][0]; + xfrmFromBoingCoordsMX[1][0] = xfrmToBoingCoordsMX[0][1]; + xfrmFromBoingCoordsMX[2][0] = xfrmToBoingCoordsMX[0][2]; + + xfrmFromBoingCoordsMX[0][1] = xfrmToBoingCoordsMX[1][0]; + xfrmFromBoingCoordsMX[1][1] = xfrmToBoingCoordsMX[1][1]; + xfrmFromBoingCoordsMX[2][1] = xfrmToBoingCoordsMX[1][2]; + + xfrmFromBoingCoordsMX[0][2] = xfrmToBoingCoordsMX[2][0]; + xfrmFromBoingCoordsMX[1][2] = xfrmToBoingCoordsMX[2][1]; + xfrmFromBoingCoordsMX[2][2] = xfrmToBoingCoordsMX[2][2]; + + xfrmFromBoingCoordsMX[0][3] = 0.0f; + xfrmFromBoingCoordsMX[1][3] = 0.0f; + xfrmFromBoingCoordsMX[2][3] = 0.0f; + + // put it all together + matrix3x4_t xfrmMX; + MatrixMultiply( xfrmToBoingCoordsMX, boingMX, xfrmMX ); + MatrixMultiply( xfrmMX, xfrmFromBoingCoordsMX, xfrmMX ); + MatrixMultiply( boneMX, xfrmMX, boneMX ); + +#ifdef CLIENT_DLL + if ( cl_jiggle_bone_debug.GetBool() ) + { + float dT = 0.01f; + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 50.0f * data->boingDir, 255, 255, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 50.0f * boingSide, 255, 0, 255, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + 50.0f * boingOtherSide, 0, 255, 255, true, dT ); + } +#endif + + boneMX[0][3] = goalBasePosition.x; + boneMX[1][3] = goalBasePosition.y; + boneMX[2][3] = goalBasePosition.z; + } + } + else if ( !( jiggleInfo->flags & ( JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID ) ) ) + { + // no flex at all - just use goal matrix + boneMX = goalMX; + } + +#ifdef CLIENT_DLL + // debug display for client only so server doesn't try to also draw it + if ( cl_jiggle_bone_debug.GetBool() ) + { + float dT = 0.01f; + const float axisSize = 5.0f; + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalLeft, 255, 0, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalUp, 0, 255, 0, true, dT ); + debugoverlay->AddLineOverlay( goalBasePosition, goalBasePosition + axisSize * goalForward, 0, 0, 255, true, dT ); + + if ( cl_jiggle_bone_debug.GetInt() > 1 ) + { + DevMsg( "Jiggle bone #%d, basePos( %3.2f, %3.2f, %3.2f ), tipPos( %3.2f, %3.2f, %3.2f ), left( %3.2f, %3.2f, %3.2f ), up( %3.2f, %3.2f, %3.2f ), forward( %3.2f, %3.2f, %3.2f )\n", + data->bone, + goalBasePosition.x, goalBasePosition.y, goalBasePosition.z, + data->tipPos.x, data->tipPos.y, data->tipPos.z, + goalLeft.x, goalLeft.y, goalLeft.z, + goalUp.x, goalUp.y, goalUp.z, + goalForward.x, goalForward.y, goalForward.z ); + } + + const float sz = 1.0f; + + if ( jiggleInfo->flags & ( JIGGLE_IS_FLEXIBLE | JIGGLE_IS_RIGID ) ) + { + debugoverlay->AddLineOverlay( goalBasePosition, + data->tipPos, 255, 255, 0, true, dT ); + + debugoverlay->AddLineOverlay( data->tipPos + Vector( -sz, 0, 0 ), + data->tipPos + Vector( sz, 0, 0 ), 0, 255, 255, true, dT ); + debugoverlay->AddLineOverlay( data->tipPos + Vector( 0, -sz, 0 ), + data->tipPos + Vector( 0, sz, 0 ), 0, 255, 255, true, dT ); + debugoverlay->AddLineOverlay( data->tipPos + Vector( 0, 0, -sz ), + data->tipPos + Vector( 0, 0, sz ), 0, 255, 255, true, dT ); + } + + if ( jiggleInfo->flags & JIGGLE_HAS_BASE_SPRING ) + { + debugoverlay->AddLineOverlay( data->basePos + Vector( -sz, 0, 0 ), + data->basePos + Vector( sz, 0, 0 ), 255, 0, 255, true, dT ); + debugoverlay->AddLineOverlay( data->basePos + Vector( 0, -sz, 0 ), + data->basePos + Vector( 0, sz, 0 ), 255, 0, 255, true, dT ); + debugoverlay->AddLineOverlay( data->basePos + Vector( 0, 0, -sz ), + data->basePos + Vector( 0, 0, sz ), 255, 0, 255, true, dT ); + } + + + if ( jiggleInfo->flags & JIGGLE_IS_BOING ) + { + if ( cl_jiggle_bone_debug.GetInt() > 2 ) + { + DevMsg( " boingSpeed = %3.2f, boingVelDir( %3.2f, %3.2f, %3.2f )\n", data->boingVelDir.Length() / deltaT, data->boingVelDir.x, data->boingVelDir.y, data->boingVelDir.z ); + } + } + } +#endif // CLIENT_DLL +} + -- cgit v1.2.3