//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // //===========================================================================// #include "stdafx.h" #include "hammer_mathlib.h" #include "MainFrm.h" #include "ObjectProperties.h" #include "Box3D.h" #include "BSPFile.h" #include "const.h" #include "MapDefs.h" // dvs: For COORD_NOTINIT #include "MapEntity.h" #include "MapInstance.h" #include "Manifest.h" #include "Render2D.h" #include "Render3D.h" #include "MapView2D.h" #include "MapView3D.h" #include "hammer.h" #include "Texture.h" #include "TextureSystem.h" #include "materialsystem/IMesh.h" #include "Material.h" #include "Options.h" #include "camera.h" #include "MapWorld.h" #include "mapview.h" #include "p4lib/ip4.h" #define __IN_HAMMER 1 #include "instancing_helper.h" // memdbgon must be the last include file in a .cpp file!!! #include IMPLEMENT_MAPCLASS(CMapInstance) char CMapInstance::m_InstancePath[ MAX_PATH ] = ""; //----------------------------------------------------------------------------- // Purpose: Factory function. Used for creating a CMapInstance. // Input : pHelperInfo - Pointer to helper info class which gives us information // about how to create the class. // pParent - the owning entity ( func_instance ) // Output : Returns a pointer to the class, NULL if an error occurs. //----------------------------------------------------------------------------- CMapClass *CMapInstance::Create( CHelperInfo *pHelperInfo, CMapEntity *pParent ) { char FileName[ MAX_PATH ]; const char *FileNameKey = pParent->GetKeyValue( "file" ); CMapDoc *pDoc = CMapDoc::GetActiveMapDoc(); if ( FileNameKey ) { strcpy( FileName, pParent->GetKeyValue( "file" ) ); } else { FileName[ 0 ] = 0; } CMapInstance *pInstance = new CMapInstance( pDoc->GetPathName(), FileName ); return pInstance; } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- void CMapInstance::SetInstancePath( const char *pszInstancePath ) { strcpy( m_InstancePath, pszInstancePath ); V_strlower( m_InstancePath ); V_FixSlashes( m_InstancePath ); } //----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- bool CMapInstance::IsMapInVersionControl( const char *pszFileName ) { if ( p4 != NULL && Options.general.bEnablePerforceIntegration == TRUE ) { if ( p4->IsFileInPerforce( pszFileName ) == true ) { char szMessage[ MAX_PATH + MAX_PATH+ 256 ]; sprintf( szMessage, "This instance is not local but exists in perforce. Would you like to sync to get the file?\n\n%s", pszFileName ); if ( AfxMessageBox( szMessage, MB_ICONHAND | MB_YESNO ) == IDYES ) { if ( p4->SyncFile( pszFileName ) == true ) { if ( g_pFullFileSystem->FileExists( pszFileName ) ) { return true; } } AfxMessageBox( "Sync operation was NOT successful!", MB_OK ) ; } } } return false; } //----------------------------------------------------------------------------- // Purpose: Constructor. //----------------------------------------------------------------------------- CMapInstance::CMapInstance( void ) { Initialize(); } //----------------------------------------------------------------------------- // Purpose: Constructor. // Input : pszBaseFileName - the root path of where the instance will be loaded from. // pszInstanceFileName - the relative name of the instance to be loaded. // Output : //----------------------------------------------------------------------------- CMapInstance::CMapInstance( const char *pszBaseFileName, const char *pszInstanceFileName ) { Initialize(); if ( pszInstanceFileName[ 0 ] && CInstancingHelper::ResolveInstancePath( g_pFullFileSystem, pszBaseFileName, pszInstanceFileName, m_InstancePath, m_FileName, MAX_PATH ) ) { bool bSaveVisible = CHammer::IsNewDocumentVisible(); CMapDoc *activeDoc = CMapDoc::GetActiveMapDoc(); CHammer::SetIsNewDocumentVisible( false ); m_pInstancedMap = ( CMapDoc * )APP()->OpenDocumentOrInstanceFile( m_FileName ); if ( m_pInstancedMap ) { m_pInstancedMap->AddReference(); m_pInstancedMap->Update(); } CMapDoc::SetActiveMapDoc( activeDoc ); CHammer::SetIsNewDocumentVisible( bSaveVisible ); } } //----------------------------------------------------------------------------- // Purpose: Destructor. //----------------------------------------------------------------------------- CMapInstance::~CMapInstance(void) { if ( m_pInstancedMap ) { m_pInstancedMap->RemoveReference(); m_pInstancedMap = NULL; } } GDIV_TYPE CMapInstance::GetFieldType( const char *pInstanceValue ) { CMapEntityList entityList; GDinputvariable TempVar; m_pInstancedMap->FindEntitiesByClassName( entityList, "func_instance_parms", false ); if ( entityList.Count() != 1 ) { return ivBadType; } CMapEntity *pInstanceParmsEntity = entityList.Element( 0 ); const char *InstancePos = strchr( pInstanceValue, ' ' ); if ( InstancePos == NULL ) { return ivBadType; } int len = InstancePos - pInstanceValue; for ( int i = pInstanceParmsEntity->GetFirstKeyValue(); i != pInstanceParmsEntity->GetInvalidKeyValue(); i = pInstanceParmsEntity->GetNextKeyValue( i ) ) { LPCTSTR pKey = pInstanceParmsEntity->GetKey( i ); LPCTSTR pValue = pInstanceParmsEntity->GetKeyValue( i ); if ( strnicmp( pKey, "parm", strlen( "parm" ) ) == 0 ) { const char *InstanceParmsPos = strchr( pValue, ' ' ); if ( InstanceParmsPos == NULL ) { continue; } if ( strnicmp( pInstanceValue, pValue, len ) == 0 ) { return TempVar.GetTypeFromToken( InstanceParmsPos + 1 ); } } } return ivBadType; } void CMapInstance::FindTargetNames( CUtlVector< const char * > &Names ) { CMapEntity *pEntity = dynamic_cast< CMapEntity * >( GetParent() ); for ( int j = pEntity->GetFirstKeyValue(); j != pEntity->GetInvalidKeyValue(); j = pEntity->GetNextKeyValue( j ) ) { LPCTSTR pInstanceKey = pEntity->GetKey( j ); LPCTSTR pInstanceValue = pEntity->GetKeyValue( j ); if ( strnicmp( pInstanceKey, "replace", strlen( "replace" ) ) == 0 ) { GDIV_TYPE FieldType = GetFieldType( pInstanceValue ); if ( FieldType == ivStringInstanced || FieldType == ivTargetDest || FieldType == ivTargetNameOrClass || FieldType == ivTargetSrc ) { const char *pszInstancePos = strchr( pInstanceValue, ' ' ); if ( pszInstancePos ) { pszInstancePos++; char *temp = new char[ strlen( pszInstancePos ) + 1 ]; strcpy( temp, pszInstancePos ); Names.AddToTail( temp ); } } } } } void CMapInstance::ReplaceTargetname( const char *szOldName, const char *szNewName ) { BaseClass::ReplaceTargetname( szOldName, szNewName ); CMapEntity *pEntity = dynamic_cast< CMapEntity * >( GetParent() ); for ( int j = pEntity->GetFirstKeyValue(); j != pEntity->GetInvalidKeyValue(); j = pEntity->GetNextKeyValue( j ) ) { LPCTSTR pInstanceKey = pEntity->GetKey( j ); LPCTSTR pInstanceValue = pEntity->GetKeyValue( j ); if ( strnicmp( pInstanceKey, "replace", strlen( "replace" ) ) == 0 ) { const char *InstancePos = strchr( pInstanceValue, ' ' ); if ( InstancePos == NULL ) { continue; } int nLen = InstancePos - pInstanceValue; if ( strcmp( szOldName, InstancePos + 1 ) == 0 ) { nLen++; char *pszResult = ( char * )stackalloc( nLen + strlen( szNewName ) + 1 ); strncpy( pszResult, pInstanceValue, nLen ); strcpy( &pszResult[ nLen ], szNewName ); pEntity->SetKeyValue( pInstanceKey, pszResult ); } } } } //----------------------------------------------------------------------------- // Purpose: This function is called when the owning entity's Key/Value pairs have // been updated. This will attempt to load a new instance if the map has // been changed. // Input : none. // Output : none. //----------------------------------------------------------------------------- bool CMapInstance::OnApply( void ) { CString MapFileName; char FileName[ MAX_PATH ]; CMapDoc *activeDoc = CMapDoc::GetActiveMapDoc(); MapFileName = activeDoc->GetPathName(); CMapEntity *ent = dynamic_cast< CMapEntity * >( GetParent() ); if ( m_pInstancedMap ) { m_pInstancedMap->RemoveReference(); } if ( ent && ent->GetKeyValue( "file" ) ) { CInstancingHelper::ResolveInstancePath( g_pFullFileSystem, MapFileName, ent->GetKeyValue( "file" ), m_InstancePath, FileName, MAX_PATH ); if ( strcmpi( FileName, m_FileName ) != 0 ) { bool bSaveVisible = CHammer::IsNewDocumentVisible(); CHammer::SetIsNewDocumentVisible( false ); strcpy( m_FileName, FileName ); m_pInstancedMap = ( CMapDoc * )APP()->OpenDocumentOrInstanceFile( m_FileName ); CHammer::SetIsNewDocumentVisible( bSaveVisible ); } } else { m_pInstancedMap = NULL; } if ( m_pInstancedMap == NULL ) { m_FileName[ 0 ] = 0; } else { m_pInstancedMap->AddReference(); m_pInstancedMap->Update(); } GetMainWnd()->pObjectProperties->MarkDataDirty(); // loading this instance will bring it forward in the MDI - we want to show the original map though CMapDoc::ActivateMapDoc( activeDoc ); return true; } //----------------------------------------------------------------------------- // Purpose: Calculates our bounding box based on instance map's dimensions. // Input : bFullUpdate - Whether we should recalculate our children's bounds. // Output : none. //----------------------------------------------------------------------------- void CMapInstance::CalcBounds(BOOL bFullUpdate) { CMapClass::CalcBounds(bFullUpdate); // // Build our bounds for frustum culling in the 3D view. // if ( m_pInstancedMap && GetParent() && m_pInstancedMap->GetMapWorld() ) { Vector vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs; matrix3x4_t Instance3x4Matrix; CMapClass *pParent = GetParent(); pParent->GetOrigin( m_Origin ); AngleMatrix( m_Angles, m_Origin, Instance3x4Matrix ); m_pInstancedMap->GetMapWorld()->CalcBounds( true ); #if 0 m_pInstancedMap->GetMapWorld()->GetCullBox( vecMins, vecMaxs ); m_pInstancedMap->GetMapWorld()->GetBoundingBox( vecMins, vecMaxs ); TransformAABB( Instance3x4Matrix, vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs ); m_CullBox.UpdateBounds( vecExpandedMins, vecExpandedMaxs ); #endif m_pInstancedMap->GetMapWorld()->GetBoundingBox( vecMins, vecMaxs ); TransformAABB( Instance3x4Matrix, vecMins, vecMaxs, vecExpandedMins, vecExpandedMaxs ); m_CullBox.UpdateBounds( vecExpandedMins, vecExpandedMaxs ); m_BoundingBox.UpdateBounds( vecExpandedMins, vecExpandedMaxs ); m_Render2DBox.UpdateBounds( vecExpandedMins, vecExpandedMaxs ); } else { Vector vecExpandedMins, vecExpandedMaxs; vecExpandedMins.Init( -32.0f, -32.0f, -32.0f ); vecExpandedMins += m_Origin; vecExpandedMaxs.Init( 32.0f, 32.0f, 32.0f ); vecExpandedMaxs += m_Origin; m_CullBox.UpdateBounds( vecExpandedMins, vecExpandedMaxs ); m_BoundingBox.UpdateBounds( vecExpandedMins, vecExpandedMaxs ); m_Render2DBox.UpdateBounds( vecExpandedMins, vecExpandedMaxs ); } } //----------------------------------------------------------------------------- // Purpose: Will calculate the bounding box of the instance as a child has changed // Input : pChild - Pointer to the object that changed. //----------------------------------------------------------------------------- void CMapInstance::UpdateChild(CMapClass *pChild) { CalcBounds( TRUE ); } //----------------------------------------------------------------------------- // Purpose: this function will attempt to find a child inside of the instance. If the bool and matrix // are supplied, the localized matrix will be built. // Input : key - the key field to lookup // value - the value to find // Output : returns the entity found // bIsInInstance - optional parameter to indicate if the found entity is inside of an instance // InstanceMatrix - optional parameter to set the localized matrix of the instance stack //----------------------------------------------------------------------------- CMapEntity *CMapInstance::FindChildByKeyValue( const char* key, const char* value, bool *bIsInInstance, VMatrix *InstanceMatrix ) { if ( m_pInstancedMap && bIsInInstance ) { CMapEntity *result = m_pInstancedMap->GetMapWorld()->FindChildByKeyValue( key, value ); if ( result ) { if ( ( *bIsInInstance ) == false ) { *bIsInInstance = true; if ( InstanceMatrix ) { InstanceMatrix->Identity(); } } if ( InstanceMatrix ) { VMatrix LocalInstanceMatrix, Result; LocalInstanceMatrix.Identity(); AngleMatrix( m_Angles, m_Origin, LocalInstanceMatrix.As3x4() ); Result = ( *InstanceMatrix ) * LocalInstanceMatrix; *InstanceMatrix = Result; } } return result; } return NULL; } //----------------------------------------------------------------------------- // Purpose: this function is called for when an instance has moved //----------------------------------------------------------------------------- void CMapInstance::InstanceMoved( void ) { if ( m_pInstancedMap ) { m_pInstancedMap->InstanceMoved(); } } //----------------------------------------------------------------------------- // Purpose: Returns a copy of this object. // Output : Pointer to the new object. //----------------------------------------------------------------------------- CMapClass *CMapInstance::Copy(bool bUpdateDependencies) { CMapInstance *pCopy = new CMapInstance; if (pCopy != NULL) { pCopy->CopyFrom(this, bUpdateDependencies); } return(pCopy); } //----------------------------------------------------------------------------- // Purpose: Turns this into a duplicate of the given object. // Input : pObject - Pointer to the object to copy from. // Output : Returns a pointer to this object. //----------------------------------------------------------------------------- CMapClass *CMapInstance::CopyFrom(CMapClass *pObject, bool bUpdateDependencies) { CMapInstance *pFrom = dynamic_cast(pObject); Assert(pObject != NULL); if (pObject != NULL) { CMapClass::CopyFrom(pObject, bUpdateDependencies); m_Angles = pFrom->m_Angles; strcpy( m_FileName, pFrom->m_FileName ); m_pInstancedMap = pFrom->m_pInstancedMap; if ( m_pInstancedMap ) { m_pInstancedMap->AddReference(); m_pInstancedMap->Update(); } } return(this); } //----------------------------------------------------------------------------- // Purpose: Set's the map instance's angles // Input : Angles - the angles to set to //----------------------------------------------------------------------------- void CMapInstance::GetAngles(QAngle &Angles) { Angles = m_Angles; } //----------------------------------------------------------------------------- // Purpose: Initialized the map instance //----------------------------------------------------------------------------- void CMapInstance::Initialize(void) { m_Angles.Init(); m_pInstancedMap = NULL; m_FileName[ 0 ] = 0; m_pManifestMap = NULL; } //----------------------------------------------------------------------------- // Purpose: Sets the manifest that loaded this instance // Input : pManifestMap - the manifest //----------------------------------------------------------------------------- void CMapInstance::SetManifest( CManifestMap *pManifestMap ) { Initialize(); m_pManifestMap = pManifestMap; m_pInstancedMap = m_pManifestMap->m_Map; strcpy( m_FileName, m_pManifestMap->m_AbsoluteMapFileName ); } //----------------------------------------------------------------------------- // Purpose: This will render the map instance into the 3d view. // Input : pRender - the 3d render //----------------------------------------------------------------------------- void CMapInstance::Render3D(CRender3D *pRender) { if ( m_pInstancedMap ) { pRender->RenderInstanceMapClass( this, m_pInstancedMap->GetMapWorld(), m_Origin, m_Angles ); } } //----------------------------------------------------------------------------- // Purpose: this function will display an instance map window. it will also set the // views to the approx same camera position. //----------------------------------------------------------------------------- void CMapInstance::SwitchTo( void ) { if ( !m_pInstancedMap ) { return; } CMapDoc *pCurrentDoc = CMapDoc::GetActiveMapDoc(); m_pInstancedMap->ShowWindow( true ); CMapDoc::ActivateMapDoc( m_pInstancedMap ); POSITION PositionCurrentView = pCurrentDoc->GetFirstViewPosition(); POSITION PositionNewView = m_pInstancedMap->GetFirstViewPosition(); while ( PositionCurrentView && PositionNewView ) { CMapView *pViewCurrent = dynamic_cast< CMapView * >( pCurrentDoc->GetNextView( PositionCurrentView ) ); CMapView2D *pViewCurrent2D = dynamic_cast< CMapView2D * >( pViewCurrent ); CMapView3D *pViewCurrent3D = dynamic_cast< CMapView3D * >( pViewCurrent ); CMapView *pViewNew = dynamic_cast< CMapView * >( m_pInstancedMap->GetNextView( PositionNewView ) ); CMapView2D *pViewNew2D = dynamic_cast< CMapView2D * >( pViewNew ); CMapView3D *pViewNew3D = dynamic_cast< CMapView3D * >( pViewNew ); if ( ( !pViewCurrent2D || !pViewNew2D ) && ( !pViewCurrent3D || !pViewNew3D ) ) { continue; } Vector CameraVector; CCamera *CurrentCamera; CurrentCamera = pViewCurrent->GetCamera(); CurrentCamera->GetViewPoint( CameraVector ); if ( pViewCurrent2D ) { CameraVector = CameraVector - m_Origin; pViewNew2D->GetCamera()->SetViewPoint( CameraVector ); pViewNew2D->GetCamera()->SetZoom( pViewCurrent2D->GetZoom() ); } else { matrix3x4_t Camera3x4Matrix, InstanceMatrix, InstanceInverseMatrix; matrix3x4_t ResultMatrix; QAngle InstanceAngles, CameraAngles; CameraAngles.Init( CurrentCamera->GetPitch(), CurrentCamera->GetYaw(), CurrentCamera->GetRoll() ); InstanceAngles = m_Angles; InstanceAngles.x = 0; InstanceAngles.z = 0; AngleMatrix( InstanceAngles, m_Origin, InstanceMatrix ); MatrixInvert( InstanceMatrix, InstanceInverseMatrix ); AngleMatrix( CameraAngles, CameraVector, Camera3x4Matrix ); MatrixMultiply( InstanceInverseMatrix, Camera3x4Matrix, ResultMatrix ); MatrixPosition( ResultMatrix, CameraVector ); MatrixMultiply( InstanceMatrix, Camera3x4Matrix, ResultMatrix ); MatrixAngles( ResultMatrix, CameraAngles ); pViewNew3D->GetCamera()->SetViewPoint( CameraVector ); pViewNew3D->GetCamera()->SetPitch( CameraAngles.x ); pViewNew3D->GetCamera()->SetYaw( CameraAngles.y ); // pViewNew3D->GetCamera()->SetRoll( CameraAngles.z ); we probably don't want to set this! } pViewNew->UpdateView( MAPVIEW_OPTIONS_CHANGED ); } } //----------------------------------------------------------------------------- // Purpose: we do not want to serialize this // Input : &File - // bRMF - // Output : int //----------------------------------------------------------------------------- int CMapInstance::SerializeRMF(std::fstream &File, BOOL bRMF) { return(0); } //----------------------------------------------------------------------------- // Purpose: we do not want to serialize this // Input : &File - // bRMF - // Output : int //----------------------------------------------------------------------------- int CMapInstance::SerializeMAP(std::fstream &File, BOOL bRMF) { return(0); } //----------------------------------------------------------------------------- // Purpose: Canculate angles based upon the transform // Input : pTransBox - //----------------------------------------------------------------------------- void CMapInstance::DoTransform(const VMatrix &matrix) { CMapClass *pParent = GetParent(); pParent->GetOrigin( m_Origin ); // BaseClass::DoTransform(matrix); matrix3x4_t fCurrentMatrix,fMatrixNew; AngleMatrix(m_Angles, fCurrentMatrix); ConcatTransforms(matrix.As3x4(), fCurrentMatrix, fMatrixNew); MatrixAngles(fMatrixNew, m_Angles); CMapEntity *pEntity = dynamic_cast< CMapEntity * >( m_pParent ); if (pEntity != NULL) { char szValue[ 80 ]; sprintf( szValue, "%g %g %g", m_Angles[ 0 ], m_Angles[ 1 ], m_Angles[ 2 ] ); pEntity->NotifyChildKeyChanged( this, "angles", szValue ); } InstanceMoved(); } //----------------------------------------------------------------------------- // Purpose: Notifies that this object's parent entity has had a key value change. // Input : szKey - The key that changed. // szValue - The new value of the key. //----------------------------------------------------------------------------- void CMapInstance::OnParentKeyChanged(const char* szKey, const char* szValue) { if (!stricmp(szKey, "angles")) { sscanf(szValue, "%f %f %f", &m_Angles[PITCH], &m_Angles[YAW], &m_Angles[ROLL]); PostUpdate(Notify_Changed); } } //----------------------------------------------------------------------------- // Purpose: indicates that we should not render last // Output : returns false. //----------------------------------------------------------------------------- bool CMapInstance::ShouldRenderLast(void) { return( false ); } //----------------------------------------------------------------------------- // Purpose: This will render the map instance into the 2d view as well as a // bounding box. // Input : pRender - the 2d render //----------------------------------------------------------------------------- void CMapInstance::Render2D(CRender2D *pRender) { CMapView2D *pView = ( CMapView2D * )pRender->GetView(); if ( m_pInstancedMap ) { pView->RenderInstance( this, m_pInstancedMap->GetMapWorld(), m_Origin, m_Angles ); } if ( m_pManifestMap ) { return; } Vector vecMins; Vector vecMaxs; GetRender2DBox(vecMins, vecMaxs); Vector2D pt,pt2; pRender->TransformPoint(pt, vecMins); pRender->TransformPoint(pt2, vecMaxs); if (!IsSelected()) { pRender->SetDrawColor( r, g, b ); pRender->SetHandleColor( r, g, b ); } else { pRender->SetDrawColor( GetRValue(Options.colors.clrSelection), GetGValue(Options.colors.clrSelection), GetBValue(Options.colors.clrSelection) ); pRender->SetHandleColor( GetRValue(Options.colors.clrSelection), GetGValue(Options.colors.clrSelection), GetBValue(Options.colors.clrSelection) ); } // Draw the bounding box. pRender->DrawBox( vecMins, vecMaxs ); // // Draw center handle. // if ( pRender->IsActiveView() ) { int sizex = abs(pt.x - pt2.x)+1; int sizey = abs(pt.y - pt2.y)+1; // dont draw handle if object is too small if ( sizex > 6 && sizey > 6 ) { pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_CROSS ); pRender->DrawHandle( (vecMins+vecMaxs)/2 ); } } } //----------------------------------------------------------------------------- // Called by entity code to render sprites //----------------------------------------------------------------------------- void CMapInstance::RenderLogicalAt(CRender2D *pRender, const Vector2D &vecMins, const Vector2D &vecMaxs ) { } //----------------------------------------------------------------------------- // Purpose: Returns if this instance is editable. A pure instance is not editable. // If it is part of a manifest, it must be the primary map of the manifest // in order for it to be editable. //----------------------------------------------------------------------------- bool CMapInstance::IsEditable( void ) { if ( m_pManifestMap ) { return m_pManifestMap->IsEditable(); } return false; } //----------------------------------------------------------------------------- // Purpose: this function checks to see if the instance is visible. // Output : returns true if the instance is visible. //----------------------------------------------------------------------------- bool CMapInstance::IsInstanceVisible( void ) { if ( IsInstance() ) { if ( CMapDoc::GetActiveMapDoc() && CMapDoc::GetActiveMapDoc()->GetShowInstance() == INSTANCES_HIDE ) { return false; } } else { if ( GetManifestMap() && GetManifestMap()->m_bVisible == false ) { return false; } } return true; } //----------------------------------------------------------------------------- // Purpose: this function will recalculate its bounds because map has changed //----------------------------------------------------------------------------- void CMapInstance::UpdateInstanceMap( void ) { CalcBounds(); }