summaryrefslogtreecommitdiff
path: root/hammer/mapkeyframe.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/mapkeyframe.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'hammer/mapkeyframe.cpp')
-rw-r--r--hammer/mapkeyframe.cpp604
1 files changed, 604 insertions, 0 deletions
diff --git a/hammer/mapkeyframe.cpp b/hammer/mapkeyframe.cpp
new file mode 100644
index 0000000..e884232
--- /dev/null
+++ b/hammer/mapkeyframe.cpp
@@ -0,0 +1,604 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "stdafx.h"
+#include "Box3D.h"
+#include "StockSolids.h"
+#include "GlobalFunctions.h"
+#include "hammer_mathlib.h"
+#include "MapDoc.h"
+#include "MapEntity.h"
+#include "MapWorld.h"
+#include "KeyFrame/KeyFrame.h"
+#include "MapKeyFrame.h"
+#include "MapAnimator.h"
+#include "Render3D.h"
+#include "TextureSystem.h"
+#include "materialsystem/imesh.h"
+#include "Material.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+IMPLEMENT_MAPCLASS( CMapKeyFrame );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Factory function. Used for creating a CMapKeyFrame from a set
+// of string parameters from the FGD file.
+// Input : *pInfo - Pointer to helper info class which gives us information
+// about how to create the class.
+// Output : Returns a pointer to the class, NULL if an error occurs.
+//-----------------------------------------------------------------------------
+CMapClass *CMapKeyFrame::CreateMapKeyFrame(CHelperInfo *pHelperInfo, CMapEntity *pParent)
+{
+ return(new CMapKeyFrame);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CMapKeyFrame::CMapKeyFrame()
+{
+ m_pAnimator = NULL;
+ m_pNextKeyFrame = NULL;
+ m_flMoveTime = 0;
+ m_flSpeed = 0;
+ m_bRebuildPath = false;
+ m_Angles.Init();
+
+ // setup the quaternion identity
+ m_qAngles[0] = m_qAngles[1] = m_qAngles[2] = 0;
+ m_qAngles[3] = 1;
+
+ m_pPositionInterpolator = NULL;
+ m_iPositionInterpolator = -1;
+ m_iChangeFrame = -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CMapKeyFrame::~CMapKeyFrame()
+{
+ if( m_pPositionInterpolator )
+ m_pPositionInterpolator->Release();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bFullUpdate -
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::CalcBounds(BOOL bFullUpdate)
+{
+ CMapClass::CalcBounds(bFullUpdate);
+
+ //
+ // Calculate the 3D bounds to include all points on our line.
+ //
+ m_CullBox.ResetBounds();
+ m_CullBox.UpdateBounds(m_Origin);
+
+ if( m_pNextKeyFrame )
+ {
+ // Expand the bbox by the target entity's origin.
+ Vector vNextOrigin;
+ m_pNextKeyFrame->GetOrigin( vNextOrigin );
+ m_CullBox.UpdateBounds(vNextOrigin);
+
+ // Expand the bbox by the points on our line.
+ for ( int i=0; i < MAX_LINE_POINTS; i++ )
+ {
+ m_CullBox.UpdateBounds(m_LinePoints[i]);
+ }
+ }
+
+ m_BoundingBox = m_CullBox;
+
+ //
+ // Our 2D bounds are just a point, because we don't render in 2D.
+ //
+ m_Render2DBox.ResetBounds();
+ m_Render2DBox.UpdateBounds(m_Origin, m_Origin);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CMapClass *
+//-----------------------------------------------------------------------------
+CMapClass *CMapKeyFrame::Copy(bool bUpdateDependencies)
+{
+ CMapKeyFrame *pNew = new CMapKeyFrame;
+ pNew->CopyFrom(this, bUpdateDependencies);
+ return pNew;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObj -
+// Output : CMapClass *
+//-----------------------------------------------------------------------------
+CMapClass *CMapKeyFrame::CopyFrom(CMapClass *pObj, bool bUpdateDependencies)
+{
+ CMapClass::CopyFrom(pObj, bUpdateDependencies);
+
+ CMapKeyFrame *pFrom = dynamic_cast<CMapKeyFrame*>( pObj );
+ Assert( pFrom != NULL );
+
+ m_qAngles = pFrom->m_qAngles;
+ m_Angles = pFrom->m_Angles;
+ m_flSpeed = pFrom->m_flSpeed;
+ m_flMoveTime = pFrom->m_flMoveTime;
+
+ if (bUpdateDependencies)
+ {
+ m_pNextKeyFrame = (CMapKeyFrame *)UpdateDependency(m_pNextKeyFrame, pFrom->m_pNextKeyFrame);
+ }
+ else
+ {
+ m_pNextKeyFrame = pFrom->m_pNextKeyFrame;
+ }
+
+ m_bRebuildPath = true;
+
+ return this;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: notifies the keyframe that it has been cloned
+// inserts the clone into the correct place in the keyframe list
+// Input : *pClone -
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::OnClone( CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList )
+{
+ CMapClass::OnClone( pClone, pWorld, OriginalList, NewList );
+
+ CMapKeyFrame *pNewKey = dynamic_cast<CMapKeyFrame*>( pClone );
+ Assert( pNewKey != NULL );
+ if ( !pNewKey )
+ return;
+
+ CMapEntity *pEntity = dynamic_cast<CMapEntity*>( m_pParent );
+ CMapEntity *pNewEntity = dynamic_cast<CMapEntity*>( pClone->GetParent() );
+
+ // insert the newly created keyframe into the sequence
+
+ // point the clone's next at what we were pointing at
+ const char *nextKey = pEntity->GetKeyValue( "NextKey" );
+ if ( nextKey )
+ {
+ pNewEntity->SetKeyValue( "NextKey", nextKey );
+ }
+
+ // create a new targetname for the clone
+ char newName[128];
+ const char *oldName = pEntity->GetKeyValue( "targetname" );
+ if ( !oldName || oldName[0] == 0 )
+ oldName = "keyframe";
+
+ pWorld->GenerateNewTargetname( oldName, newName, sizeof( newName ), true, NULL );
+ pNewEntity->SetKeyValue( "targetname", newName );
+
+ // point the current keyframe at the clone
+ pEntity->SetKeyValue( "NextKey", newName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called just after this object has been removed from the world so
+// that it can unlink itself from other objects in the world.
+// Input : pWorld - The world that we were just removed from.
+// bNotifyChildren - Whether we should forward notification to our children.
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren)
+{
+ CMapClass::OnRemoveFromWorld(pWorld, bNotifyChildren);
+
+ //
+ // Detach ourselves from the next keyframe in the path.
+ //
+ m_pNextKeyFrame = (CMapKeyFrame *)UpdateDependency(m_pNextKeyFrame, NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *outQuat -
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::GetQuatAngles( Quaternion &outQuat )
+{
+ outQuat = m_qAngles;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalulates timings based on the new position
+// Input : *pfOrigin -
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::SetOrigin( Vector& pfOrigin )
+{
+ CMapClass::SetOrigin(pfOrigin);
+ m_bRebuildPath = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Renders the connecting lines between the keyframes
+// Input : pRender -
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::Render3D( CRender3D *pRender )
+{
+ if ( m_bRebuildPath )
+ {
+ if (GetAnimator() != NULL)
+ {
+ GetAnimator()->RebuildPath();
+ }
+ }
+
+ // only draw if we have a valid connection
+ if ( m_pNextKeyFrame && m_flSpeed > 0 )
+ {
+ // only draw if we haven't already been drawn this frame
+ if ( GetRenderFrame() != pRender->GetRenderFrame() )
+ {
+ pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
+
+ SetRenderFrame( pRender->GetRenderFrame() );
+
+ Vector o1, o2;
+ GetOrigin( o1 );
+ m_pNextKeyFrame->GetOrigin( o2 );
+
+ CMeshBuilder meshBuilder;
+ CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+
+ // draw connecting line going from green to red
+ meshBuilder.Begin( pMesh, MATERIAL_LINE_STRIP, MAX_LINE_POINTS );
+
+ // start point
+ meshBuilder.Color3f( 0, 1.0f, 0 );
+ meshBuilder.Position3f( o1[0], o1[1], o1[2] );
+ meshBuilder.AdvanceVertex();
+
+ for ( int i = 0; i < MAX_LINE_POINTS; i++ )
+ {
+ float red = (float)(i+1) / (float)MAX_LINE_POINTS;
+ meshBuilder.Color3f( red, 1.0f - red, 0 );
+ meshBuilder.Position3f( m_LinePoints[i][0], m_LinePoints[i][1], m_LinePoints[i][2] );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRender->PopRenderMode();
+ }
+ }
+
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the total time remaining in the animation sequence in seconds.
+//-----------------------------------------------------------------------------
+float CMapKeyFrame::GetRemainingTime( CMapObjectList *pVisited )
+{
+ CMapObjectList Visited;
+ if ( pVisited == NULL )
+ {
+ pVisited = &Visited;
+ }
+
+ //
+ // Check for circularities.
+ //
+ if ( pVisited->Find( this ) != -1 )
+ {
+ return 0.0f;
+ }
+
+ pVisited->AddToTail( this );
+
+ if ( m_pNextKeyFrame )
+ {
+ return m_flMoveTime + m_pNextKeyFrame->GetRemainingTime( pVisited );
+ }
+
+ return 0.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CMapKeyFrame
+//-----------------------------------------------------------------------------
+CMapKeyFrame *CMapKeyFrame::NextKeyFrame( void )
+{
+ if ( !m_pNextKeyFrame )
+ return this;
+
+ return m_pNextKeyFrame;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Notifies that the entity this is attached to has had a key change
+// Input : key -
+// value -
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::OnParentKeyChanged( const char* key, const char* value )
+{
+ if ( !stricmp(key, "NextKey") )
+ {
+ m_bRebuildPath = true;
+ }
+ else if ( !stricmp(key, "NextTime") )
+ {
+ m_flMoveTime = atof( value );
+ }
+ else if ( !stricmp(key, "MoveSpeed") )
+ {
+ m_flSpeed = atof( value );
+ m_bRebuildPath = true;
+ }
+ else if (!stricmp(key, "angles"))
+ {
+ sscanf(value, "%f %f %f", &m_Angles[PITCH], &m_Angles[YAW], &m_Angles[ROLL]);
+ AngleQuaternion(m_Angles, m_qAngles);
+ }
+
+ if( m_pPositionInterpolator )
+ {
+ if( m_pPositionInterpolator->ProcessKey( key, value ) )
+ m_bRebuildPath = true;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: calculates the time the current key frame should take, given
+// the movement speed, and the distance to the next keyframe
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::RecalculateTimeFromSpeed( void )
+{
+ if ( m_flSpeed <= 0 )
+ return;
+
+ if ( !m_pNextKeyFrame )
+ return;
+
+ // calculate the distance to the next key
+ Vector o1;
+ m_pNextKeyFrame->GetOrigin( o1 );
+
+ Vector o2 = o1 - m_Origin;
+ float dist = VectorLength( o2 );
+
+ // couldn't get time from distance, get it from rotation instead
+ if ( !dist )
+ {
+ // speed is in degrees per second
+ // find the largest rotation component and use that
+ QAngle ang = m_Angles - m_pNextKeyFrame->m_Angles;
+ dist = 0;
+ for ( int i = 0; i < 3; i++ )
+ {
+ fixang( ang[i] );
+ if ( ang[i] > 180 )
+ ang[i] = ang[i] - 360;
+
+ if ( abs(ang[i]) > dist )
+ {
+ dist = abs(ang[i]);
+ }
+ }
+ }
+
+ // time = distance / speed
+ float newTime = dist / m_flSpeed;
+
+ // set the new speed (99.99% of the time this is the same so don't
+ // bother forcing it to rebuild the path).
+ if( m_flMoveTime != newTime )
+ {
+ m_flMoveTime = newTime;
+
+ // rebuild the path before we next render
+ m_bRebuildPath = true;
+ }
+
+ // "NextTime" key removed until we get a real-time updating entity properties dialog
+ /*
+ CMapEntity *ent = dynamic_cast<CMapEntity*>( Parent );
+ if ( ent )
+ {
+ char buf[16];
+ sprintf( buf, "%.2f", newTime );
+ ent->SetKeyValue( "NextTime", buf );
+ ent->OnParentKeyChanged( "NextTime", buf );
+ }
+ */
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Builds the spline points between this keyframe and the previous
+// keyframe.
+// Input : pPrev -
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::BuildPathSegment( CMapKeyFrame *pPrev )
+{
+ RecalculateTimeFromSpeed();
+
+ CMapAnimator *pAnim = GetAnimator();
+
+ Quaternion qAngles;
+ for ( int i = 0; i < MAX_LINE_POINTS; i++ )
+ {
+ if (pAnim != NULL)
+ {
+ CMapAnimator::GetAnimationAtTime( this, pPrev, MoveTime() * ( float )( i + 1 ) / (float)MAX_LINE_POINTS, m_LinePoints[i], qAngles, pAnim->m_iPositionInterpolator, pAnim->m_iRotationInterpolator );
+ }
+ else
+ {
+ // FIXME: If we aren't connected to an animator yet, just draw straight lines. This code is never hit, because
+ // BuildPathSegment is only called from CMapAnimator. To make matters worse, we can only reliably find
+ // pPrev through an animator.
+ CMapAnimator::GetAnimationAtTime( this, pPrev, MoveTime() * (float)( i + 1) / (float)MAX_LINE_POINTS, m_LinePoints[i], qAngles, 0, 0 );
+ }
+ }
+
+ // HACK: we shouldn't need to do this. CalcBounds alone should work (but it doesn't because of where we
+ // call RebuildPath from). Make this work more like other objects.
+ if ( m_pParent )
+ {
+ GetParent()->CalcBounds( true );
+ }
+ else
+ {
+ CalcBounds();
+ }
+
+ m_bRebuildPath = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when an object that we depend on has changed.
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType)
+{
+ CMapClass::OnNotifyDependent(pObject, eNotifyType);
+
+ if ((pObject == m_pAnimator) && (eNotifyType == Notify_Removed))
+ {
+ SetAnimator(NULL);
+ }
+
+ //
+ // If our next keyframe was deleted, try to link to the one after it.
+ //
+ if ((pObject == m_pNextKeyFrame) && (eNotifyType == Notify_Removed))
+ {
+ CMapEntity *pNextParent = m_pNextKeyFrame->GetParentEntity();
+ CMapEntity *pParent = GetParentEntity();
+
+ if ( pNextParent && pParent )
+ {
+ const char *szNext = pNextParent->GetKeyValue("NextKey");
+ pParent->SetKeyValue("NextKey", szNext);
+ }
+ }
+
+ m_bRebuildPath = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns a pointer to our parent entity
+// Output : CMapEntity
+//-----------------------------------------------------------------------------
+CMapEntity *CMapKeyFrame::GetParentEntity( void )
+{
+ return dynamic_cast<CMapEntity*>( m_pParent );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMapKeyFrame::IsAnyKeyInSequenceSelected( void )
+{
+ if ( m_pParent && m_pParent->IsSelected() )
+ {
+ return true;
+ }
+
+ // search forward
+ for ( CMapKeyFrame *find = m_pAnimator; find != NULL; find = find->m_pNextKeyFrame )
+ {
+ if ( find->m_pParent && find->m_pParent->IsSelected() )
+ {
+ return true;
+ }
+ }
+
+ // no selected items found
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iInterpolator -
+// Output : IPositionInterpolator
+//-----------------------------------------------------------------------------
+IPositionInterpolator* CMapKeyFrame::SetupPositionInterpolator( int iInterpolator )
+{
+ if( iInterpolator != m_iPositionInterpolator )
+ {
+ if( m_pPositionInterpolator )
+ m_pPositionInterpolator->Release();
+
+ m_pPositionInterpolator = Motion_GetPositionInterpolator( iInterpolator );
+ m_iPositionInterpolator = iInterpolator;
+
+ // Feed keys..
+ CMapEntity *pEnt = GetParentEntity();
+ if( pEnt )
+ {
+ for ( int i=pEnt->GetFirstKeyValue(); i != pEnt->GetInvalidKeyValue(); i=pEnt->GetNextKeyValue( i ) )
+ {
+ m_pPositionInterpolator->ProcessKey(
+ pEnt->GetKey( i ),
+ pEnt->GetKeyValue( i ) );
+ }
+ }
+ }
+
+
+ return m_pPositionInterpolator;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Marks that we need to relink any pointers defined by target/targetname pairs
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::UpdateDependencies(CMapWorld *pWorld, CMapClass *pObject)
+{
+ CMapClass::UpdateDependencies(pWorld, pObject);
+ m_bRebuildPath = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::SetAnimator(CMapAnimator *pAnimator)
+{
+ m_pAnimator = (CMapAnimator *)UpdateDependency(m_pAnimator, pAnimator);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMapKeyFrame::SetNextKeyFrame(CMapKeyFrame *pNext)
+{
+ m_pNextKeyFrame = (CMapKeyFrame *)UpdateDependency(m_pNextKeyFrame, pNext);
+}
+
+