//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #include "movieobjects/dmedag.h" #include "movieobjects/dmeshape.h" #include "datamodel/dmelementfactoryhelper.h" #include "movieobjects/dmetransform.h" #include "movieobjects_interfaces.h" #include "movieobjects/dmedrawsettings.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Expose this class to the scene database //----------------------------------------------------------------------------- IMPLEMENT_ELEMENT_FACTORY( DmeDag, CDmeDag ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CUtlStack CDmeDag::s_TransformStack; bool CDmeDag::s_bDrawUsingEngineCoordinates = false; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CDmeDag::OnConstruction() { m_Transform.InitAndCreate( this, "transform", GetName() ); m_Shape.Init( this, "shape" ); m_Visible.InitAndSet( this, "visible", true, FATTRIB_HAS_CALLBACK ); m_Children.Init( this, "children" ); } void CDmeDag::OnDestruction() { g_pDataModel->DestroyElement( m_Transform.Get() ); } //----------------------------------------------------------------------------- // Accessors //----------------------------------------------------------------------------- CDmeTransform *CDmeDag::GetTransform() { return m_Transform.GetElement(); } CDmeShape *CDmeDag::GetShape() { return m_Shape.GetElement(); } void CDmeDag::SetShape( CDmeShape *pShape ) { m_Shape = pShape; } bool CDmeDag::IsVisible() const { return m_Visible; } void CDmeDag::SetVisible( bool bVisible ) { m_Visible = bVisible; } //----------------------------------------------------------------------------- // Returns the visibility attribute for DmeRenderable support //----------------------------------------------------------------------------- CDmAttribute *CDmeDag::GetVisibilityAttribute() { return m_Visible.GetAttribute(); } //----------------------------------------------------------------------------- // child helpers //----------------------------------------------------------------------------- const CUtlVector< DmElementHandle_t > &CDmeDag::GetChildren() const { return m_Children.Get(); } int CDmeDag::GetChildCount() const { return m_Children.Count(); } CDmeDag *CDmeDag::GetChild( int i ) const { if ( i < 0 || i >= m_Children.Count() ) return NULL; return m_Children.Get( i ); } void CDmeDag::AddChild( CDmeDag* pDag ) { m_Children.AddToTail( pDag ); } void CDmeDag::RemoveChild( int i ) { m_Children.FastRemove( i ); } void CDmeDag::RemoveChild( const CDmeDag *pChild, bool bRecurse ) { int i = FindChild( pChild ); if ( i >= 0 ) { RemoveChild( i ); } } int CDmeDag::FindChild( const CDmeDag *pChild ) const { return m_Children.Find( pChild->GetHandle() ); } // recursive int CDmeDag::FindChild( CDmeDag *&pParent, const CDmeDag *pChild ) { int index = FindChild( pChild ); if ( index >= 0 ) { pParent = this; return index; } int nChildren = m_Children.Count(); for ( int ci = 0; ci < nChildren; ++ci ) { index = m_Children[ ci ]->FindChild( pParent, pChild ); if ( index >= 0 ) return index; } pParent = NULL; return -1; } int CDmeDag::FindChild( const char *name ) const { int nChildren = m_Children.Count(); for ( int ci = 0; ci < nChildren; ++ci ) { if ( V_strcmp( m_Children[ ci ]->GetName(), name ) == 0 ) return ci; } return -1; } CDmeDag *CDmeDag::FindOrAddChild( const char *name ) { int i = FindChild( name ); if ( i >= 0 ) return GetChild( i ); CDmeDag *pChild = CreateElement< CDmeDag >( name, GetFileId() ); AddChild( pChild ); return pChild; } //----------------------------------------------------------------------------- // Recursively render the Dag hierarchy //----------------------------------------------------------------------------- void CDmeDag::PushDagTransform() { int i = s_TransformStack.Push(); TransformInfo_t &info = s_TransformStack[i]; info.m_pTransform = GetTransform(); info.m_bComputedDagToWorld = false; } void CDmeDag::PopDagTransform() { Assert( s_TransformStack.Top().m_pTransform == GetTransform() ); s_TransformStack.Pop(); } //----------------------------------------------------------------------------- // Transform from DME to engine coordinates //----------------------------------------------------------------------------- void CDmeDag::DmeToEngineMatrix( matrix3x4_t& dmeToEngine ) { VMatrix rotation, rotationZ; MatrixBuildRotationAboutAxis( rotation, Vector( 1, 0, 0 ), 90 ); MatrixBuildRotationAboutAxis( rotationZ, Vector( 0, 1, 0 ), 90 ); ConcatTransforms( rotation.As3x4(), rotationZ.As3x4(), dmeToEngine ); } //----------------------------------------------------------------------------- // Transform from engine to DME coordinates //----------------------------------------------------------------------------- void CDmeDag::EngineToDmeMatrix( matrix3x4_t& engineToDme ) { VMatrix rotation, rotationZ; MatrixBuildRotationAboutAxis( rotation, Vector( 1, 0, 0 ), -90 ); MatrixBuildRotationAboutAxis( rotationZ, Vector( 0, 1, 0 ), -90 ); ConcatTransforms( rotationZ.As3x4(), rotation.As3x4(), engineToDme ); } void CDmeDag::GetShapeToWorldTransform( matrix3x4_t &mat ) { int nCount = s_TransformStack.Count(); if ( nCount == 0 ) { if ( !s_bDrawUsingEngineCoordinates ) { SetIdentityMatrix( mat ); } else { DmeToEngineMatrix( mat ); } return; } if ( s_TransformStack.Top().m_bComputedDagToWorld ) { MatrixCopy( s_TransformStack.Top().m_DagToWorld, mat ); return; } // Compute all uncomputed dag to worls int i; for ( i = 0; i < nCount; ++i ) { TransformInfo_t &info = s_TransformStack[i]; if ( !info.m_bComputedDagToWorld ) break; } // Set up the initial transform if ( i == 0 ) { if ( !s_bDrawUsingEngineCoordinates ) { SetIdentityMatrix( mat ); } else { DmeToEngineMatrix( mat ); } } else { MatrixCopy( s_TransformStack[i-1].m_DagToWorld, mat ); } // Compute all transforms for ( ; i < nCount; ++i ) { matrix3x4_t localToParent; TransformInfo_t &info = s_TransformStack[i]; info.m_pTransform->GetTransform( localToParent ); ConcatTransforms( mat, localToParent, info.m_DagToWorld ); info.m_bComputedDagToWorld = true; MatrixCopy( info.m_DagToWorld, mat ); } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CDmeDag::GetLocalMatrix( matrix3x4_t &m ) { CDmeTransform *pTransform = GetTransform(); if ( pTransform ) { pTransform->GetTransform( m ); } else { SetIdentityMatrix( m ); } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CDmeDag::GetWorldMatrix( matrix3x4_t &m ) { GetLocalMatrix( m ); const static UtlSymId_t symChildren = g_pDataModel->GetSymbol( "children" ); CDmeDag *pParent = FindReferringElement< CDmeDag >( this, symChildren ); if ( pParent ) { matrix3x4_t localMatrix; GetLocalMatrix( localMatrix ); matrix3x4_t parentWorldMatrix; pParent->GetWorldMatrix( parentWorldMatrix ); ConcatTransforms( parentWorldMatrix, localMatrix, m ); } else { GetLocalMatrix( m ); } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CDmeDag::GetParentWorldMatrix( matrix3x4_t &m ) { const static UtlSymId_t symChildren = g_pDataModel->GetSymbol( "children" ); CDmeDag *pParent = FindReferringElement< CDmeDag >( this, symChildren ); if ( pParent ) { pParent->GetWorldMatrix( m ); } else { SetIdentityMatrix( m ); } } //----------------------------------------------------------------------------- // Recursively render the Dag hierarchy //----------------------------------------------------------------------------- void CDmeDag::DrawUsingEngineCoordinates( bool bEnable ) { s_bDrawUsingEngineCoordinates = bEnable; } //----------------------------------------------------------------------------- // Recursively render the Dag hierarchy //----------------------------------------------------------------------------- void CDmeDag::Draw( CDmeDrawSettings *pDrawSettings ) { if ( !m_Visible ) return; PushDagTransform(); CDmeShape *pShape = GetShape(); if ( pShape ) { matrix3x4_t shapeToWorld; GetShapeToWorldTransform( shapeToWorld ); pShape->Draw( shapeToWorld, pDrawSettings ); } uint cn = m_Children.Count(); for ( uint ci = 0; ci < cn; ++ci ) { m_Children[ ci ]->Draw( pDrawSettings ); } PopDagTransform(); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CDmeDag::GetBoundingSphere( Vector &c0, float &r0, const matrix3x4_t &pMat ) const { matrix3x4_t lMat; m_Transform.GetElement()->GetTransform( lMat ); matrix3x4_t wMat; ConcatTransforms( pMat, lMat, wMat ); c0.Zero(); r0 = 0.0f; const CDmeShape *pShape = m_Shape.GetElement(); if ( pShape ) { pShape->GetBoundingSphere( c0, r0 ); } // No scale in Dme! :) Vector vTemp; VectorTransform( c0, pMat, vTemp ); const int nChildren = m_Children.Count(); if ( nChildren > 0 ) { Vector c1; // Child center float r1; // Child radius Vector v01; // c1 - c0 float l01; // |v01| for ( int i = 0; i < nChildren; ++i ) { m_Children[ i ]->GetBoundingSphere( c1, r1, wMat ); if ( r0 == 0.0f ) { c0 = c1; r0 = r1; continue; } v01 = c1 - c0; l01 = v01.NormalizeInPlace(); if ( r0 < l01 + r1 ) { // Current sphere doesn't contain both spheres if ( r1 < l01 + r0 ) { // Child sphere doesn't contain both spheres c0 = c0 + 0.5f * ( r1 + l01 - r0 ) * v01; r0 = 0.5f * ( r0 + l01 + r1 ); } else { // Child sphere contains both spheres c0 = c1; r0 = r1; } } } } }