|
|
//========= Copyright � 1996-2005, 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); }
|