/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // WorldEditorDoc.cpp : implementation of the CWorldEditorDoc class // #include "stdafx.h" #include "WorldEditor.h" #include "WorldEditorDoc.h" #include #include #include #ifdef _DEBUG #undef new #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #pragma optimize("p", on) // this is in effect for entire file! extern COLOR acol_ColorizePallete[]; ///////////////////////////////////////////////////////////////////////////// // CWorldEditorDoc IMPLEMENT_DYNCREATE(CWorldEditorDoc, CDocument) BEGIN_MESSAGE_MAP(CWorldEditorDoc, CDocument) //{{AFX_MSG_MAP(CWorldEditorDoc) ON_COMMAND(ID_CSG_SPLIT_SECTORS, OnCsgSplitSectors) ON_UPDATE_COMMAND_UI(ID_CSG_SPLIT_SECTORS, OnUpdateCsgSplitSectors) ON_COMMAND(ID_CSG_CANCEL, OnCsgCancel) ON_COMMAND(ID_SHOW_ORIENTATION, OnShowOrientation) ON_UPDATE_COMMAND_UI(ID_SHOW_ORIENTATION, OnUpdateShowOrientation) ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) ON_COMMAND(ID_EDIT_REDO, OnEditRedo) ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo) ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo) ON_COMMAND(ID_WORLD_SETTINGS, OnWorldSettings) ON_COMMAND(ID_CSG_JOIN_SECTORS, OnCsgJoinSectors) ON_UPDATE_COMMAND_UI(ID_CSG_JOIN_SECTORS, OnUpdateCsgJoinSectors) ON_COMMAND(ID_AUTO_SNAP, OnAutoSnap) ON_COMMAND(ID_CSG_ADD, OnCsgAdd) ON_UPDATE_COMMAND_UI(ID_CSG_ADD, OnUpdateCsgAdd) ON_COMMAND(ID_CSG_REMOVE, OnCsgRemove) ON_UPDATE_COMMAND_UI(ID_CSG_REMOVE, OnUpdateCsgRemove) ON_COMMAND(ID_CSG_SPLIT_POLYGONS, OnCsgSplitPolygons) ON_UPDATE_COMMAND_UI(ID_CSG_SPLIT_POLYGONS, OnUpdateCsgSplitPolygons) ON_COMMAND(ID_CSG_JOIN_POLYGONS, OnCsgJoinPolygons) ON_UPDATE_COMMAND_UI(ID_CSG_JOIN_POLYGONS, OnUpdateCsgJoinPolygons) ON_COMMAND(ID_CALCULATESHADOWS, OnCalculateShadows) ON_COMMAND(ID_BROWSE_ENTITIES_MODE, OnBrowseEntitiesMode) ON_UPDATE_COMMAND_UI(ID_BROWSE_ENTITIES_MODE, OnUpdateBrowseEntitiesMode) ON_COMMAND(ID_PREVIOUS_SELECTED_ENTITY, OnPreviousSelectedEntity) ON_UPDATE_COMMAND_UI(ID_PREVIOUS_SELECTED_ENTITY, OnUpdatePreviousSelectedEntity) ON_COMMAND(ID_NEXT_SELECTED_ENTITY, OnNextSelectedEntity) ON_UPDATE_COMMAND_UI(ID_NEXT_SELECTED_ENTITY, OnUpdateNextSelectedEntity) ON_COMMAND(ID_JOIN_LAYERS, OnJoinLayers) ON_UPDATE_COMMAND_UI(ID_AUTO_SNAP, OnUpdateAutoSnap) ON_COMMAND(ID_SELECT_BY_CLASS, OnSelectByClass) ON_UPDATE_COMMAND_UI(ID_SELECT_BY_CLASS, OnUpdateSelectByClass) ON_COMMAND(ID_CSG_JOIN_ALL_POLYGONS, OnCsgJoinAllPolygons) ON_UPDATE_COMMAND_UI(ID_CSG_JOIN_ALL_POLYGONS, OnUpdateCsgJoinAllPolygons) ON_COMMAND(ID_TEXTURE_1, OnTexture1) ON_UPDATE_COMMAND_UI(ID_TEXTURE_1, OnUpdateTexture1) ON_COMMAND(ID_TEXTURE_2, OnTexture2) ON_UPDATE_COMMAND_UI(ID_TEXTURE_2, OnUpdateTexture2) ON_COMMAND(ID_TEXTURE_3, OnTexture3) ON_UPDATE_COMMAND_UI(ID_TEXTURE_3, OnUpdateTexture3) ON_COMMAND(ID_TEXTURE_MODE_1, OnTextureMode1) ON_COMMAND(ID_TEXTURE_MODE_2, OnTextureMode2) ON_COMMAND(ID_TEXTURE_MODE_3, OnTextureMode3) ON_COMMAND(ID_SAVE_THUMBNAIL, OnSaveThumbnail) ON_COMMAND(ID_UPDATE_LINKS, OnUpdateLinks) ON_COMMAND(ID_SNAPSHOT, OnSnapshot) ON_COMMAND(ID_MIRROR_AND_STRETCH, OnMirrorAndStretch) ON_COMMAND(ID_FLIP_LAYER, OnFlipLayer) ON_UPDATE_COMMAND_UI(ID_FLIP_LAYER, OnUpdateFlipLayer) ON_COMMAND(ID_FILTER_SELECTION, OnFilterSelection) ON_COMMAND(ID_UPDATE_CLONES, OnUpdateClones) ON_UPDATE_COMMAND_UI(ID_UPDATE_CLONES, OnUpdateUpdateClones) ON_COMMAND(ID_HIDE_SELECTED, OnHideSelected) ON_UPDATE_COMMAND_UI(ID_HIDE_SELECTED, OnUpdateHideSelected) ON_COMMAND(ID_HIDE_UNSELECTED, OnHideUnselected) ON_COMMAND(ID_SHOW_ALL, OnShowAll) ON_COMMAND(ID_CHECK_EDIT, OnCheckEdit) ON_COMMAND(ID_CHECK_ADD, OnCheckAdd) ON_COMMAND(ID_CHECK_DELETE, OnCheckDelete) ON_UPDATE_COMMAND_UI(ID_CHECK_EDIT, OnUpdateCheckEdit) ON_UPDATE_COMMAND_UI(ID_CHECK_ADD, OnUpdateCheckAdd) ON_UPDATE_COMMAND_UI(ID_CHECK_DELETE, OnUpdateCheckDelete) ON_COMMAND(ID_UPDATE_BRUSHES, OnUpdateBrushes) ON_COMMAND(ID_SELECT_BY_CLASS_IMPORTANT, OnSelectByClassImportant) ON_COMMAND(ID_INSERT_3D_OBJECT, OnInsert3dObject) ON_COMMAND(ID_EXPORT_3D_OBJECT, OnExport3dObject) ON_UPDATE_COMMAND_UI(ID_EXPORT_3D_OBJECT, OnUpdateExport3dObject) ON_COMMAND(ID_CROSSROAD_FOR_N, OnCrossroadForN) ON_COMMAND(ID_POPUP_VTX_ALLIGN, OnPopupVtxAllign) ON_COMMAND(ID_POPUP_VTX_FILTER, OnPopupVtxFilter) ON_COMMAND(ID_POPUP_VTX_NUMERIC, OnPopupVtxNumeric) ON_COMMAND(ID_HIDE_SELECTED_SECTORS, OnHideSelectedSectors) ON_COMMAND(ID_HIDE_UNSELECTED_SECTORS, OnHideUnselectedSectors) ON_COMMAND(ID_SHOW_ALL_SECTORS, OnShowAllSectors) ON_COMMAND(ID_TEXTURE_MODE_4, OnTextureMode4) ON_COMMAND(ID_TEXTURE_MODE_5, OnTextureMode5) ON_COMMAND(ID_TEXTURE_MODE_6, OnTextureMode6) ON_COMMAND(ID_TEXTURE_MODE_7, OnTextureMode7) ON_COMMAND(ID_TEXTURE_MODE_8, OnTextureMode8) ON_COMMAND(ID_TEXTURE_MODE_9, OnTextureMode9) ON_COMMAND(ID_TEXTURE_MODE_10, OnTextureMode10) //}}AFX_MSG_MAP ON_COMMAND(ID_EXPORT_PLACEMENTS, OnExportPlacements) ON_COMMAND(ID_EXPORT_ENTITIES, OnExportEntities) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CWorldEditorDoc construction/destruction CWorldEditorDoc::CWorldEditorDoc() { m_iCurrentTerrainUndo=-1; m_ptrSelectedTerrain=NULL; m_slDisplaceTexTime=0; m_bAskedToCheckOut = FALSE; m_pCutLineView = NULL; m_iMirror = 0; m_bWasEverSaved = FALSE; m_iSelectedEntityInVolume = 0; m_iTexture = 0; m_ctLastPrimitiveVertices = -1; m_bPrimitiveCreatedFirstTime = TRUE; m_fLastPrimitiveWidth = 0.0; m_fLastPrimitiveLenght = 0.0; m_bLastIfOuter = FALSE; m_ttLastTriangularisationType = theApp.m_vfpCurrent.vfp_ttTriangularisationType; m_bAutoSnap = TRUE; m_bPrimitiveMode = FALSE; m_pwoSecondLayer = NULL; m_penPrimitive = NULL; m_bOrientationIcons=AfxGetApp()->GetProfileInt( L"World editor", L"Orientation icons", FALSE); m_bBrowseEntitiesMode = FALSE; m_bReadOnly = FALSE; m_csgtLastUsedCSGOperation = CSG_ILLEGAL; m_csgtPreLastUsedCSGOperation = CSG_ILLEGAL; m_bPreLastUsedPrimitiveMode = TRUE; m_bLastUsedPrimitiveMode = TRUE; m_fnLastDroppedTemplate = CTString(""); // initialize grid placement m_plGrid.pl_PositionVector = FLOAT3D(0.0f,0.0f,0.0f); m_plGrid.pl_OrientationAngle = ANGLE3D(0,0,0); // initialize delta placement m_plDeltaPlacement.pl_PositionVector = FLOAT3D(0.0f,0.0f,0.0f); m_plDeltaPlacement.pl_OrientationAngle = ANGLE3D(0,0,0); // initialize last placement m_plLastPlacement.pl_PositionVector = FLOAT3D(0.0f,0.0f,0.0f); m_plLastPlacement.pl_OrientationAngle = ANGLE3D(0,0,0); // initialize create box vertices char strIni[ 128]; strcpy( strIni, CStringA(theApp.GetProfileString( L"World editor", L"Volume box min", L"0.0 0.0 0.0"))); sscanf( strIni, "%f %f %f", &m_vCreateBoxVertice0(1), &m_vCreateBoxVertice0(2), &m_vCreateBoxVertice0(3)); strcpy( strIni, CStringA(theApp.GetProfileString( L"World editor", L"Volume box max", L"1.0 1.0 1.0"))); sscanf( strIni, "%f %f %f", &m_vCreateBoxVertice1(1), &m_vCreateBoxVertice1(2), &m_vCreateBoxVertice1(3)); // set default editing mode - polygon mode INDEX iMode=AfxGetApp()->GetProfileInt( L"World editor", L"Last editing mode", POLYGON_MODE); if(iMode==POLYGON_MODE || iMode==VERTEX_MODE || iMode==SECTOR_MODE || iMode==ENTITY_MODE || iMode==TERRAIN_MODE) { SetEditingMode( iMode); } else { SetEditingMode( POLYGON_MODE); } } CWorldEditorDoc::~CWorldEditorDoc() { DeleteTerrainUndo(this); if(m_iMode==POLYGON_MODE || m_iMode==VERTEX_MODE || m_iMode==SECTOR_MODE || m_iMode==ENTITY_MODE || m_iMode==TERRAIN_MODE) { theApp.WriteProfileInt(L"World editor", L"Last editing mode", m_iMode); } else { theApp.WriteProfileInt(L"World editor", L"Last editing mode", POLYGON_MODE); } if( m_pwoSecondLayer != NULL) delete m_pwoSecondLayer; // delete stored undo members FORDELETELIST(CUndo, m_lnListNode, m_lhUndo, itUndo) { delete &itUndo.Current(); } // delete redo FORDELETELIST(CUndo, m_lnListNode, m_lhRedo, itRedo) { delete &itRedo.Current(); } } void CWorldEditorDoc::ClearSelections(ESelectionType stExcept /*=ST_NONE*/) { if( stExcept != ST_VERTEX) { m_selVertexSelection.Clear(); m_chSelections.MarkChanged();} if( stExcept != ST_ENTITY) { m_selEntitySelection.Clear(); m_chSelections.MarkChanged();} if( stExcept != ST_VOLUME) { m_cenEntitiesSelectedByVolume.Clear(); m_chSelections.MarkChanged();} if( stExcept != ST_SECTOR) { m_selSectorSelection.Clear(); m_chSelections.MarkChanged();} if( stExcept != ST_POLYGON) { m_selPolygonSelection.Clear(); m_chSelections.MarkChanged();} } /* * Sets message about current mode and selected members */ void CWorldEditorDoc::SetStatusLineModeInfoMessage( void) { BOOL bAlt = (GetKeyState( VK_MENU)&0x8000) != 0; BOOL bCtrl = (GetKeyState( VK_CONTROL)&0x8000) != 0; // initialy, we are in polygon edit mode CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); HICON hIcon; char strModeName[ 32]; // write mode change on status line switch( m_iMode) { case POLYGON_MODE: { sprintf( strModeName, "%d polys, layer %d", m_selPolygonSelection.Count(), m_iTexture+1); hIcon = theApp.LoadIcon( IDR_ICON_PANE_POLYGON); break; }; case SECTOR_MODE: { sprintf( strModeName, "%d sectors", m_selSectorSelection.Count()); hIcon = theApp.LoadIcon( IDR_ICON_PANE_SECTOR); break; }; case ENTITY_MODE: { if( m_bBrowseEntitiesMode) { sprintf( strModeName, "%d in volume", m_cenEntitiesSelectedByVolume.Count()); } else { sprintf( strModeName, "%d entities", m_selEntitySelection.Count()); }; hIcon = theApp.LoadIcon( IDR_ICON_PANE_ENTITY); break; } case CSG_MODE: { sprintf( strModeName, "CSG mode"); hIcon = theApp.LoadIcon( IDR_ICON_PANE_CSG); break; }; case VERTEX_MODE: { sprintf( strModeName, "%d vertices", m_selVertexSelection.Count()); hIcon = theApp.LoadIcon( IDR_ICON_PANE_VERTEX); break; }; case TERRAIN_MODE: { if(bCtrl&&bAlt) { if(theApp.m_iTerrainEditMode==TEM_HEIGHTMAP) { sprintf( strModeName, "%s", "Pick altitude"); } else { sprintf( strModeName, "%s", "Pick layer"); } } else if(theApp.m_iTerrainEditMode==TEM_HEIGHTMAP) { INDEX iIcon; CTString strText; if(theApp.m_iTerrainBrushMode==TBM_FILTER) { sprintf( strModeName, "%s", GetFilterName(theApp.m_iFilter)); } else { GetBrushModeInfo(INDEX(theApp.m_iTerrainBrushMode), iIcon, strText); sprintf( strModeName, "%s", strText); } } else { sprintf( strModeName, "Layer %d", GetLayerIndex()); } hIcon = theApp.LoadIcon( IDR_ICON_PANE_TERRAIN); break; }; default: { FatalError("Unknown editing mode."); break;}; } pMainFrame->m_wndStatusBar.GetStatusBarCtrl().SetIcon( EDITING_MODE_ICON_PANE, hIcon); pMainFrame->m_wndStatusBar.SetPaneText( EDITING_MODE_PANE, CString(strModeName), TRUE); } /* * Changes editing mode */ void CWorldEditorDoc::SetEditingMode( INDEX iNewMode) { #if !ALLOW_TERRAINS if(iNewMode==TERRAIN_MODE) { return; } #endif // exit cut mode theApp.m_bCutModeOn = FALSE; // cancel browse entities mode if( m_bBrowseEntitiesMode) { OnBrowseEntitiesMode(); } // set new mode m_iMode = iNewMode; SetStatusLineModeInfoMessage(); UpdateAllViews( NULL); // send message for mode change PostThreadMessage( GetCurrentThreadId(), WM_CHANGE_EDITING_MODE, TRUE, 0); } BOOL CWorldEditorDoc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; // create the World entity CPlacement3D plWorld; plWorld.pl_PositionVector = FLOAT3D(0.0f,0.0f,0.0f); plWorld.pl_OrientationAngle = ANGLE3D(0,0,0); CEntity *penWorldBase; try { penWorldBase = m_woWorld.CreateEntity_t(plWorld, CTFILENAME("Classes\\WorldBase.ecl")); } catch( char *err_str) { AfxMessageBox( CString(err_str)); return FALSE; } // prepare the entity penWorldBase->Initialize(); EFirstWorldBase eFirstWorldBase; penWorldBase->SendEvent( eFirstWorldBase); CEntity::HandleSentEvents(); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CWorldEditorDoc serialization void CWorldEditorDoc::Serialize(CArchive& ar) { // must not get here ASSERT(FALSE); } ///////////////////////////////////////////////////////////////////////////// // CWorldEditorDoc diagnostics #ifdef _DEBUG void CWorldEditorDoc::AssertValid() const { CDocument::AssertValid(); } void CWorldEditorDoc::Dump(CDumpContext& dc) const { CDocument::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CWorldEditorDoc commands ///////////////////////////////////////////////////////////////////////////// void CWorldEditorDoc::SetupBackdropTextureObject( CTFileName fnPicture, CTextureObject &to) { CImageInfo iiImageInfo; try { iiImageInfo.LoadAnyGfxFormat_t( fnPicture); // both dimension must be potentions of 2 if( (iiImageInfo.ii_Width == 1<<((int)Log2( (FLOAT)iiImageInfo.ii_Width))) && (iiImageInfo.ii_Height == 1<<((int)Log2( (FLOAT)iiImageInfo.ii_Height))) ) { CTFileName fnTexture = fnPicture.FileDir()+fnPicture.FileName()+".tex"; // creates new texture with one frame CTextureData tdPicture; tdPicture.Create_t( &iiImageInfo, iiImageInfo.ii_Width, 1, FALSE); tdPicture.Save_t( fnTexture); to.SetData_t( fnTexture); } } catch( char *strError) { (void) strError; } } BOOL CWorldEditorDoc::OnOpenDocument(LPCTSTR lpszPathName) { CTFileName fnOpenFileName; // open the world fnOpenFileName = CTString(CStringA(lpszPathName)); if( fnOpenFileName.FileExt()!=".wld") return FALSE; try { fnOpenFileName.RemoveApplicationPath_t(); // if the file is read only if(IsFileReadOnly(fnOpenFileName)) { // warn user about it WarningMessage("'%s' is read only. You have to check it out to be able to save it.", (const char*)fnOpenFileName); // remember it m_bReadOnly = TRUE; } _pfWorldEditingProfile.Reset(); m_woWorld.Load_t( fnOpenFileName); m_woWorld.ReinitializeEntities(); _pfWorldEditingProfile.Report( theApp.m_strCSGAndShadowStatistics); theApp.m_strCSGAndShadowStatistics.SaveVar(CTString("Temp\\Profile_Open.txt")); // try to load textures for backdrops if( m_woWorld.wo_strBackdropUp != "") { SetupBackdropTextureObject( m_woWorld.wo_strBackdropUp, m_toBackdropUp); } if( m_woWorld.wo_strBackdropFt != "") { SetupBackdropTextureObject( m_woWorld.wo_strBackdropFt, m_toBackdropFt); } if( m_woWorld.wo_strBackdropRt != "") { SetupBackdropTextureObject( m_woWorld.wo_strBackdropRt, m_toBackdropRt); } POSITION pos = GetFirstViewPosition(); CWorldEditorView *pWedView = (CWorldEditorView *) GetNextView(pos); ASSERT( pWedView != NULL); CChildFrame *pWedChild = pWedView->GetChildFrame(); ASSERT( pWedChild != NULL); pWedChild->m_mvViewer.mv_plViewer = m_woWorld.wo_plFocus; pWedChild->m_mvViewer.mv_fTargetDistance = m_woWorld.wo_fTargetDistance; } catch( char *strError) { AfxMessageBox( CString(strError)); return FALSE; } // try to load object for backdrops if( m_woWorld.wo_strBackdropObject != "") { // try to try { // load 3D lightwawe object FLOATmatrix3D mStretch; mStretch.Diagonal(1.0f); m_o3dBackdropObject.LoadAny3DFormat_t( m_woWorld.wo_strBackdropObject, mStretch); } // catch and catch( char *strError) { // report errors AfxMessageBox( CString(strError)); } } if( theApp.m_Preferences.ap_bShowAllOnOpen) { OnShowAllEntities(); OnShowAllSectors(); } // flush stale caches _pShell->Execute("FreeUnusedStock();"); return TRUE; } // overridden from mfc void CWorldEditorDoc::SetModifiedFlag( BOOL bModified /*= TRUE*/ ) { CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); CDocument::SetModifiedFlag(bModified); if (!bModified) { return; } try { if (IsFileReadOnly(m_woWorld.wo_fnmFileName) && !m_bAskedToCheckOut) { // ask for check out if( ::MessageBoxA( pMainFrame->m_hWnd, "Do you want to open the world for edit?", "Warning !", MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON1 | MB_TASKMODAL | MB_TOPMOST) == IDYES) { m_bAskedToCheckOut = TRUE; OnCheckEdit(); } } } catch( char *strError) { AfxMessageBox( CString(strError)); return; } } BOOL CWorldEditorDoc::OnSaveDocument(LPCTSTR lpszPathName) { CTFileName fnSaveFileName; // save the world fnSaveFileName = CTString(CStringA(lpszPathName)); try { fnSaveFileName.RemoveApplicationPath_t(); // if the file is read only if(IsFileReadOnly(fnSaveFileName)) { // don't allow saving WarningMessage( "World file is read-only. You can't save it."); return FALSE; } POSITION pos = GetFirstViewPosition(); CWorldEditorView *pWedView = (CWorldEditorView *) GetNextView(pos); CChildFrame *pWedChild = pWedView->GetChildFrame(); m_woWorld.wo_plFocus = pWedChild->m_mvViewer.mv_plViewer; m_woWorld.wo_fTargetDistance = pWedChild->m_mvViewer.mv_fTargetDistance; m_woWorld.Save_t( fnSaveFileName); SetModifiedFlag(FALSE); } catch( char *strError) { AfxMessageBox( CString(strError)); return FALSE; } // write file's directory into application's .ini file theApp.WriteProfileString(L"World editor", L"Open directory", CString(_fnmApplicationPath+fnSaveFileName.FileDir())); // save thumbnail SaveThumbnail(); m_bWasEverSaved = TRUE; return TRUE; } static BOOL _bDontRecalculateBase = FALSE; // start creating primitive from current application's primitive, don't recreate base void CWorldEditorDoc::ApplyCurrentPrimitiveSettings(void) { ASSERT( m_pwoSecondLayer != NULL); // destroy second layer world delete m_pwoSecondLayer; m_pwoSecondLayer = NULL; INDEX iPreCSGMode = m_iPreCSGMode; // force not recreation of primitive base _bDontRecalculateBase = TRUE; StartPrimitiveCSG( theApp.m_vfpCurrent.vfp_plPrimitive, FALSE); CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); pMainFrame->m_TriangularisationCombo.SetCurSel( (int)theApp.m_vfpCurrent.vfp_ttTriangularisationType); m_iPreCSGMode = iPreCSGMode; } // start creating primitive void CWorldEditorDoc::StartPrimitiveCSG( CPlacement3D plPrimitive, BOOL bResetAngles/*=TRUE*/) { ApplyAutoColorize(); if( theApp.m_ptdActiveTexture == NULL) { AfxMessageBox( L"You have to select active texture first (double click on texture in browser)."); return; } if( !((theApp.m_vfpCurrent.vfp_ptPrimitiveType == PT_CONUS) || (theApp.m_vfpCurrent.vfp_ptPrimitiveType == PT_TORUS) || (theApp.m_vfpCurrent.vfp_ptPrimitiveType == PT_STAIRCASES) || (theApp.m_vfpCurrent.vfp_ptPrimitiveType == PT_SPHERE) || (theApp.m_vfpCurrent.vfp_ptPrimitiveType == PT_TERRAIN) ) ) { WarningMessage( "This type of primitive not yet supported. Please be patient !!!"); return; } POSITION pos = GetFirstViewPosition(); CWorldEditorView *pWedView = (CWorldEditorView *) GetNextView(pos); CChildFrame *pWedChild = pWedView->GetChildFrame(); // remember auto mip brushing flag pWedChild->m_bLastAutoMipBrushingOn = pWedChild->m_bAutoMipBrushingOn; // turn off auto mip brushing pWedChild->m_bAutoMipBrushingOn = FALSE; m_pwoSecondLayer = new CWorld; m_bPrimitiveMode = TRUE; // position the second layer m_plSecondLayer = plPrimitive; // if angle reset requested if( bResetAngles) { // reset angles so they are alligned to current grid m_plSecondLayer.pl_OrientationAngle = m_plGrid.pl_OrientationAngle; } // create the World entity CPlacement3D plPrimitiveEntity; plPrimitiveEntity.pl_PositionVector = FLOAT3D(0.0f,0.0f,0.0f); plPrimitiveEntity.pl_OrientationAngle = ANGLE3D(0,0,0); try { m_penPrimitive = m_pwoSecondLayer->CreateEntity_t( plPrimitiveEntity, CTFILENAME("Classes\\WorldBase.ecl")); } catch( char *err_str) { AfxMessageBox( CString(err_str)); // discard initialized variables needed for CSG delete m_pwoSecondLayer; m_pwoSecondLayer = NULL; m_bPrimitiveMode = FALSE; return; } // prepare the entity m_penPrimitive->Initialize(); // store current mode m_iPreCSGMode = m_iMode; // start CSG mode SetEditingMode( CSG_MODE); // create primitive for the first time //m_bPrimitiveCreatedFirstTime = TRUE; CreatePrimitive(); // if preferences say so, show info if( theApp.m_Preferences.ap_AutomaticInfo) { CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); pMainFrame->ShowInfoWindow(); } // invalidate document (i.e. all views) UpdateAllViews( NULL); } // start CSG with world template void CWorldEditorDoc::StartTemplateCSG( CPlacement3D plTemplate, const CTFileName &fnWorld, BOOL bResetAngles/*=TRUE*/) { POSITION pos = GetFirstViewPosition(); CWorldEditorView *pWedView = (CWorldEditorView *) GetNextView(pos); CChildFrame *pWedChild = pWedView->GetChildFrame(); // remember auto mip brushing flag pWedChild->m_bLastAutoMipBrushingOn = pWedChild->m_bAutoMipBrushingOn; // turn off auto mip brushing pWedChild->m_bAutoMipBrushingOn = FALSE; m_pwoSecondLayer = new CWorld; m_bPrimitiveMode = FALSE; // remember name of last template used for CSG m_fnLastDroppedTemplate = fnWorld; // position the second layer m_plSecondLayer = plTemplate; // if angle reset was requested if( bResetAngles) { // reset angles so they are alligned to current grid m_plSecondLayer.pl_OrientationAngle = m_plGrid.pl_OrientationAngle; } try { // load the World m_pwoSecondLayer->Load_t( fnWorld); } catch( char *err_str) { AfxMessageBox( CString(err_str)); delete m_pwoSecondLayer; m_pwoSecondLayer = NULL; return; } // invalidate document (i.e. all views) UpdateAllViews( NULL); // store current mode m_iPreCSGMode = m_iMode; // start CSG mode SetEditingMode( CSG_MODE); // update position property page for the first time m_chSelections.MarkChanged(); // if preferences say so, show info if( theApp.m_Preferences.ap_AutomaticInfo) { CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); pMainFrame->ShowInfoWindow(); } // don't join layers !!!! /* // if none of entities in dropped world has brush rendering type // for all of the world's entities {FOREACHINDYNAMICCONTAINER(m_pwoSecondLayer->wo_cenEntities, CEntity, iten) { CEntity::RenderType rt = iten->GetRenderType(); // if the entity is brush and it is not empty if( (rt==CEntity::RT_BRUSH || rt==CEntity::RT_FIELDBRUSH) && ( !iten->IsEmptyBrush()) && (CTString(iten->GetClass()->ec_pdecDLLClass->dec_strName) == "WorldBase") ) { // if preferences say so, show info if( theApp.m_Preferences.ap_AutomaticInfo) { CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); pMainFrame->ShowInfoWindow(); } // don't join layers return; } }} // call join layers OnJoinLayers(); */ } #define CT_PRIMITIVES_IN_HISTORY_BUFFER 1024 // apply current CSG operation void CWorldEditorDoc::ApplyCSG(enum CSGType CSGType) { if((CSGType==CSG_ADD || CSGType==CSG_ADD_REVERSE || CSGType==CSG_REMOVE || CSGType==CSG_REMOVE_REVERSE || CSGType==CSG_ADD_ENTITIES || CSGType==CSG_SPLIT_POLYGONS || CSGType==CSG_JOIN_LAYERS) && (m_pwoSecondLayer==NULL)) return; POSITION pos = GetFirstViewPosition(); CWorldEditorView *pWedView = (CWorldEditorView *) GetNextView(pos); CChildFrame *pWedChild = pWedView->GetChildFrame(); // restore auto mip brushing flag pWedChild->m_bAutoMipBrushingOn = pWedChild->m_bLastAutoMipBrushingOn; // set wait cursor CWaitCursor StartWaitCursor; if( theApp.m_bCSGReportEnabled) { _pfWorldEditingProfile.Reset(); } RememberUndo(); // remember last used CSG operation m_csgtPreLastUsedCSGOperation = m_csgtLastUsedCSGOperation; m_csgtLastUsedCSGOperation = CSGType; // set flag telling is last used CSG was primitive m_bPreLastUsedPrimitiveMode = m_bLastUsedPrimitiveMode; m_bLastUsedPrimitiveMode = m_bPrimitiveMode; // invalving entities CEntity *penThis; CEntity *penOther; BOOL bThisFound = FALSE; BOOL bOtherFound = FALSE; // calculate delta placement m_plDeltaPlacement = m_plSecondLayer; // convert it into absolute space of last used placement (delta calculated) m_plDeltaPlacement.AbsoluteToRelative( m_plLastPlacement); // remember position of last applied CSG m_plLastPlacement = m_plSecondLayer; theApp.m_vfpCurrent.vfp_plPrimitive = m_plSecondLayer; // calculate width, height, sheer,... delta to be able to use it for next clone CSG theApp.m_vfpDelta = theApp.m_vfpCurrent - theApp.m_vfpLast; // remember last values for primitive as prelast used ones theApp.m_vfpPreLast = theApp.m_vfpLast; // remember current values for primitive as last used ones theApp.m_vfpLast = theApp.m_vfpCurrent; if( m_bPrimitiveMode) { // if there are too many primitives in history buffer if( theApp.m_lhPrimitiveHistory.Count() >= CT_PRIMITIVES_IN_HISTORY_BUFFER) { // remove last used one CPrimitiveInHistoryBuffer *ppihbLast = LIST_TAIL( theApp.m_lhPrimitiveHistory, CPrimitiveInHistoryBuffer, pihb_lnNode); theApp.m_lhPrimitiveHistory.RemTail(); delete ppihbLast; } // add this primtive into history buffer CPrimitiveInHistoryBuffer *ppihbMember = new CPrimitiveInHistoryBuffer; ppihbMember->pihb_vfpPrimitive = theApp.m_vfpCurrent; ppihbMember->pihb_vfpPrimitive.vfp_csgtCSGOperation = CSGType; theApp.m_lhPrimitiveHistory.AddHead( ppihbMember->pihb_lnNode); // save primitives history buffer CTFileStream strmFile; try { strmFile.Create_t( CTString("Data\\PrimitivesHistory.pri")); INDEX ctHistory = theApp.m_lhPrimitiveHistory.Count(); strmFile << ctHistory; // write history primitives list FOREACHINLIST( CPrimitiveInHistoryBuffer, pihb_lnNode, theApp.m_lhPrimitiveHistory, itPrim) { itPrim->pihb_vfpPrimitive.Write_t( strmFile); } } catch( char *strError) { WarningMessage( strError); } // remember used values for primitive to be used as default values for next primitive // of same type switch( theApp.m_vfpCurrent.vfp_ptPrimitiveType) { case PT_CONUS:{ theApp.m_vfpConus = theApp.m_vfpCurrent; break;} case PT_TORUS:{ theApp.m_vfpTorus = theApp.m_vfpCurrent; break;} case PT_STAIRCASES:{ theApp.m_vfpStaircases = theApp.m_vfpCurrent; break;} case PT_SPHERE:{ theApp.m_vfpSphere = theApp.m_vfpCurrent; break;} case PT_TERRAIN:{ theApp.m_vfpTerrain = theApp.m_vfpCurrent; break;} default: ASSERTALWAYS( "Wrong primitive type occured"); } } CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); // join operations are not really CSG operations because we do not have second layer BOOL bJoinOperation = ( CSGType==CSG_JOIN_LAYERS || CSGType==CSG_JOIN_SECTORS || CSGType==CSG_JOIN_POLYGONS || CSGType==CSG_JOIN_POLYGONS_KEEP_TEXTURES || CSGType==CSG_JOIN_ALL_POLYGONS || CSGType==CSG_JOIN_ALL_POLYGONS_KEEP_TEXTURES); if( !bJoinOperation) { // for real CSG operations search for invalving entities penThis = pMainFrame->m_CSGDesitnationCombo.GetSelectedBrushEntity(); if( penThis != NULL) { bThisFound = TRUE; } if( !bThisFound) { WarningMessage( "Destination for CSG can't be optained, canceling CSG."); StopCSG(); return; } // find other entity {FOREACHINDYNAMICCONTAINER(m_pwoSecondLayer->wo_cenEntities, CEntity, itenOther) { if (CTString(itenOther->GetClass()->ec_pdecDLLClass->dec_strName) == "WorldBase") { penOther = &itenOther.Current(); bOtherFound = TRUE; break; } }} // if other entity can't be obtained, switch to join layers mode if( !bOtherFound) { CSGType = CSG_JOIN_LAYERS; } } // act acording requested CSG operation switch( CSGType) { case CSG_ADD: { ClearSelections(); // apply "add" m_woWorld.CSGAdd(*penThis, *m_pwoSecondLayer, *penOther, m_plSecondLayer); break; } case CSG_ADD_REVERSE: { ClearSelections(); // apply "add reverse" m_woWorld.CSGAddReverse(*penThis, *m_pwoSecondLayer, *penOther, m_plSecondLayer); break; } case CSG_REMOVE: { ClearSelections(); // apply "remove" m_woWorld.CSGRemove(*penThis, *m_pwoSecondLayer, *penOther, m_plSecondLayer); break; } case CSG_REMOVE_REVERSE: { //ClearSelections(); // apply "remove reverse" // m_woWorld.CSGRemoveReverse(*penThis, *m_pwoSecondLayer, *penOther, m_plSecondLayer); break; } case CSG_SPLIT_SECTORS: { // clear all selections except sector seletion ClearSelections( ST_SECTOR); // apply "split sectors" m_woWorld.SplitSectors(*penThis, m_selSectorSelection, *m_pwoSecondLayer, *penOther, m_plSecondLayer); break; } case CSG_JOIN_SECTORS: { // clear all selections except sector seletion ClearSelections( ST_SECTOR); // join selected sectors m_woWorld.JoinSectors( m_selSectorSelection); // store current mode m_iPreCSGMode = m_iMode; break; } case CSG_SPLIT_POLYGONS: { // clear all selections except polygon seletion ClearSelections( ST_POLYGON); // apply "split polygons" m_woWorld.SplitPolygons(*penThis, m_selPolygonSelection, *m_pwoSecondLayer, *penOther, m_plSecondLayer); break; } case CSG_JOIN_POLYGONS: case CSG_JOIN_POLYGONS_KEEP_TEXTURES: { // clear all selections except polygon seletion ClearSelections( ST_POLYGON); // join selected polygons m_woWorld.JoinPolygons(m_selPolygonSelection); // store current mode m_iPreCSGMode = m_iMode; break; } case CSG_JOIN_ALL_POLYGONS: case CSG_JOIN_ALL_POLYGONS_KEEP_TEXTURES: { // clear all selections except polygon seletion ClearSelections( ST_POLYGON); // join selected polygons m_woWorld.JoinAllPossiblePolygons( m_selPolygonSelection, CSGType==CSG_JOIN_ALL_POLYGONS_KEEP_TEXTURES, m_iTexture); // store current mode m_iPreCSGMode = m_iMode; break; } case CSG_JOIN_LAYERS: { theApp.m_vfpCurrent.vfp_csgtCSGOperation = CSG_JOIN_LAYERS; // clear entity selections m_selEntitySelection.Clear(); m_cenEntitiesSelectedByVolume.Clear(); // mark that selections have been changed m_chSelections.MarkChanged(); // make container of entities to copy CDynamicContainer cenToCopy; cenToCopy = m_pwoSecondLayer->wo_cenEntities; // remove empty brushes from it {FOREACHINDYNAMICCONTAINER(m_pwoSecondLayer->wo_cenEntities, CEntity, iten) { if( iten->IsEmptyBrush() && (iten->GetFlags()&ENF_ZONING)) { cenToCopy.Remove(iten); } }} // copy entities in container m_woWorld.CopyEntities( *m_pwoSecondLayer, cenToCopy, m_selEntitySelection, m_plSecondLayer); m_iPreCSGMode = ENTITY_MODE; break; } default: { ASSERTALWAYS( "Illegal CSG operation type requested!"); } } // increase auto colorize color's index theApp.m_iLastAutoColorizeColor=(theApp.m_iLastAutoColorizeColor+1)%32; m_chSelections.MarkChanged(); SetModifiedFlag(TRUE); m_chDocument.MarkChanged(); StopCSG(); if( theApp.m_bCSGReportEnabled) { // create CSG report _pfWorldEditingProfile.Report( theApp.m_strCSGAndShadowStatistics); theApp.m_strCSGAndShadowStatistics.SaveVar(CTString("Temp\\Profile_CSG.txt")); } m_iMirror = 0; } // clean up after doing a CSG void CWorldEditorDoc::StopCSG(void) { if( m_pwoSecondLayer == NULL) return; CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); // destroy second layer world delete m_pwoSecondLayer; m_pwoSecondLayer = NULL; m_bPrimitiveMode = FALSE; // if preferences say so, hide info if( theApp.m_Preferences.ap_AutomaticInfo) { pMainFrame->HideInfoWindow(); } // restore mode SetEditingMode( m_iPreCSGMode); // if info frame exist if( pMainFrame->m_pInfoFrame != NULL) { // force immidiate page refilling pMainFrame->m_pInfoFrame->m_pInfoSheet->OnIdle( 0); } // invalidate document (i.e. all views) UpdateAllViews( NULL); } // cancel current CSG operation void CWorldEditorDoc::CancelCSG(void) { StopCSG(); } void CWorldEditorDoc::OnIdle(void) { CValuesForPrimitive &vfp=theApp.m_vfpCurrent; if( m_pwoSecondLayer!=NULL && m_bPrimitiveMode && vfp.vfp_ptPrimitiveType==PT_TERRAIN && vfp.vfp_fnDisplacement!="" && theApp.m_Preferences.ap_bAutoUpdateDisplaceMap) { try { SLONG slFileTime=GetFileTimeStamp_t(vfp.vfp_fnDisplacement); if(slFileTime>m_slDisplaceTexTime) { CreatePrimitive(); UpdateAllViews( NULL); } } catch(char *strError) { (void) strError; } } POSITION pos = GetFirstViewPosition(); CWorldEditorView *pWedView; FOREVER { pWedView = (CWorldEditorView *) GetNextView(pos); if( pWedView == NULL) break; pWedView->OnIdle(); } if( GetEditingMode()==TERRAIN_MODE) { UpdateAllViews( NULL); } } // does "snap to grid" for given coordinate void CWorldEditorDoc::SnapFloat( FLOAT &fDest, FLOAT fStep /* SNAP_FLOAT_GRID */) { // this must use floor() to get proper snapping of negative values. FLOAT fDiv = fDest/fStep; FLOAT fRound = fDiv + 0.5f; int iSnap = int( floor(fRound)); FLOAT fRes = iSnap * fStep; fDest = fRes; } // does "snap to grid" for given angle void CWorldEditorDoc::SnapAngle( ANGLE &angDest, ANGLE angStep /* SNAP_ANGLE_GRID */) { /* Watch out for unsigned-signed mixing! All sub-expression and arguments must be unsigned for this to work correctly! Unfortunately, ANGLE is not an unsigned type by default, so we must cast it. Also, angStep must be a divisor of ANGLE_180! */ SnapFloat( angDest, angStep); /* ASSERT(ANGLE_180%angStep == 0); // don't test with ANGLE_360 ,since it is 0! angDest = ANGLE( ((UWORD(angDest)+UWORD(angStep)/2U)/UWORD(angStep))*UWORD(angStep) ); */ } // does "snap to grid" for given placement void CWorldEditorDoc::SnapToGrid( CPlacement3D &plPlacement, FLOAT fSnapValue) { FLOAT fAngleSnap = SNAP_ANGLE_GRID; if( fSnapValue < SNAP_FLOAT_CM) fSnapValue = SNAP_FLOAT_CM; if( !m_bAutoSnap) { fSnapValue = SNAP_FLOAT_CM; fAngleSnap = ANGLE_SNAP/32.0f; } // snap X coordinate SnapFloat( plPlacement.pl_PositionVector(1), fSnapValue); // snap Y coordinate SnapFloat( plPlacement.pl_PositionVector(2), fSnapValue); // snap Z coordinate SnapFloat( plPlacement.pl_PositionVector(3), fSnapValue); /* // snap H angle SnapAngle( plPlacement.pl_OrientationAngle(1)); // snap P angle SnapAngle( plPlacement.pl_OrientationAngle(2)); // snap B angle SnapAngle( plPlacement.pl_OrientationAngle(3)); */ // snap X coordinate SnapFloat( plPlacement.pl_PositionVector(1), SNAP_FLOAT_CM); // snap Y coordinate SnapFloat( plPlacement.pl_PositionVector(2), SNAP_FLOAT_CM); // snap Z coordinate SnapFloat( plPlacement.pl_PositionVector(3), SNAP_FLOAT_CM); // snap H angle SnapAngle( plPlacement.pl_OrientationAngle(1), fAngleSnap); // snap P angle SnapAngle( plPlacement.pl_OrientationAngle(2), fAngleSnap); // snap B angle SnapAngle( plPlacement.pl_OrientationAngle(3), fAngleSnap); } // static vars used for polygon creation in primitives static BOOL _bAutoCreateMipBrushes; static BOOL _bClosed; static CObjectMaterial *_pomMaterial; static CObjectSector *_poscSector; static CTextureData *_pPrimitiveTexture; static DOUBLE _fTextureWidth; static DOUBLE _fTextureHeight; void DisplaceVertex( DOUBLE3D &vVtx, CImageInfo *pII, DOUBLE fMinX, DOUBLE fMaxX, DOUBLE fMinZ, DOUBLE fMaxZ, INDEX iSlicesPerW, INDEX iSlicesPerL, FLOAT fAmplitude) { if( pII == NULL) return; FLOAT fPix = (fMaxX-fMinX)/(pII->ii_Width-1); FLOAT fDelta = (vVtx(1)-fMinX); FLOAT fMaxDelta = (fMaxX-fMinX); FLOAT fTmp = ((pII->ii_Width-1) / fMaxDelta * fDelta); PIX pixX = (PIX)((pII->ii_Width-1) /(fMaxX-fMinX) * (vVtx(1) + fPix/2.0f -fMinX) ); PIX pixY = (PIX)((pII->ii_Height-1)/(fMaxZ-fMinZ) * (vVtx(3) + fPix/2.0f -fMinZ) ); if( pixX >= pII->ii_Width) pixX = pII->ii_Width -1; if( pixY >= pII->ii_Height) pixY = pII->ii_Height-1; SLONG slPicPosition = (pII->ii_Width*pixY +pixX) * (pII->ii_BitsPerPixel/8); vVtx(2) += fAmplitude/256.0f * pII->ii_Picture[slPicPosition]; } #define HEIGHT_EPSILON 0.001 void AddPolygon(INDEX vtxCt, DOUBLE3D *avVtx, BOOL bInvert, DOUBLE3D f3dMappingTranslation = DOUBLE3D(0.0f,0.0f,0.0f), CImageInfo *pII=NULL, DOUBLE fMinX=0.0f, DOUBLE fMaxX=0.0f, DOUBLE fMinZ=0.0f, DOUBLE fMaxZ=0.0f, INDEX iSlicesPerW=0, INDEX iSlicesPerL=0, FLOAT fAmplitude=0.0f, DOUBLE fRaiseHeight=0.0f) { // copy array of vertices DOUBLE3D *avVtxCopy = new DOUBLE3D[vtxCt]; for( INDEX iCopy=0; iCopyCreatePolygon( vtxCt, avVtxCopy, *_pomMaterial, NULL, bInvert); if( pObjectPolygon != NULL) { // set shadow cluster size to 2m ((CBrushPolygonProperties&)(pObjectPolygon->opo_ubUserData)).bpp_sbShadowClusterSize=2; } break; } case TT_CENTER_VERTEX: { // calculate center vertex DOUBLE3D vCenter = DOUBLE3D( 0.0, 0.0, 0.0); INDEX iVtx=0; for( ; iVtxCreatePolygon( 3, avPolygon, *_pomMaterial, NULL, bInvert); if( pObjectPolygon != NULL) { // set shadow cluster size to 2m ((CBrushPolygonProperties&)(pObjectPolygon->opo_ubUserData)).bpp_sbShadowClusterSize=2; } } break; } default: { INDEX iStartVtx = 0; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX00) iStartVtx = 0; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX01) iStartVtx = 1; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX02) iStartVtx = 2; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX03) iStartVtx = 3; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX04) iStartVtx = 4; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX05) iStartVtx = 5; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX06) iStartVtx = 6; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX07) iStartVtx = 7; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX08) iStartVtx = 8; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX09) iStartVtx = 9; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX10) iStartVtx =10; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX11) iStartVtx =11; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX12) iStartVtx =12; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX13) iStartVtx =13; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX14) iStartVtx =14; if( theApp.m_vfpCurrent.vfp_ttTriangularisationType == TT_FROM_VTX15) iStartVtx =15; iStartVtx %= vtxCt; // create polygons DOUBLE3D avPolygon[ 3]; for( INDEX iVtx=iStartVtx; iVtxCreatePolygon( 3, avPolygon, *_pomMaterial, NULL, bInvert); if( pObjectPolygon != NULL) { // set shadow cluster size to 2m ((CBrushPolygonProperties&)(pObjectPolygon->opo_ubUserData)).bpp_sbShadowClusterSize=2; } } break; } } delete[] avVtxCopy; } void CWorldEditorDoc::ConvertObject3DToBrush(CObject3D &ob, BOOL bApplyProjectedMapping/*=FALSE*/, INDEX iMipBrush/*=0*/, FLOAT fSwitchFactor/*=1E6f*/, BOOL bApplyDefaultPolygonProperties/*=TRUE*/) { CObject3D obTmp = ob; obTmp.RecalculatePlanes(); // try to try { // turn this on to dump all primitives #ifndef NDEBUG //theApp.m_vfpCurrent.vfp_o3dPrimitive.DebugDump(); #endif //NDEBUG if( !obTmp.ArePolygonsPlanar()) { throw( "ERROR: Primitive that You want to use has non planar polygons.\n" "Make sure that stretch x and stretch y are same or use triangularisation."); } if( bApplyProjectedMapping) { DOUBLE xMin = theApp.m_vfpCurrent.vfp_fXMin; DOUBLE xMax = theApp.m_vfpCurrent.vfp_fXMax; DOUBLE zMin = theApp.m_vfpCurrent.vfp_fZMin; DOUBLE zMax = theApp.m_vfpCurrent.vfp_fZMax; // create mapping to be stretched over the top of primitive FLOATplane3D plHorizontal(FLOAT3D(0,1,0),0); CMappingDefinitionUI mduiHorizontal; mduiHorizontal.mdui_aURotation = mduiHorizontal.mdui_aVRotation = AngleDeg(90.0f); mduiHorizontal.mdui_fUStretch = FLOAT((xMax-xMin)/_fTextureWidth); mduiHorizontal.mdui_fVStretch = FLOAT((zMax-zMin)/_fTextureHeight); mduiHorizontal.mdui_fUOffset = FLOAT(xMin)/mduiHorizontal.mdui_fUStretch; mduiHorizontal.mdui_fVOffset = FLOAT(zMin)/mduiHorizontal.mdui_fVStretch; CMappingDefinition mdHorizontal; mdHorizontal.FromUI(mduiHorizontal); // for each polygon in primitive CObject3D &ob = obTmp; ob.ob_aoscSectors.Lock(); CObjectSector &osc = ob.ob_aoscSectors[0]; osc.osc_aopoPolygons.Lock(); FOREACHINDYNAMICARRAY( osc.osc_aopoPolygons, CObjectPolygon, itopo) { CObjectPolygon &opo = *itopo; // project mapping to the polygon opo.opo_amdMappings[0].ProjectMapping(plHorizontal, mdHorizontal, DOUBLEtoFLOAT(*opo.opo_Plane)); opo.opo_amdMappings[1] = opo.opo_amdMappings[0]; opo.opo_amdMappings[2] = opo.opo_amdMappings[0]; } osc.osc_aopoPolygons.Unlock(); ob.ob_aoscSectors.Unlock(); } // convert it into brush CBrush3D *pbr = m_penPrimitive->GetBrush(); if( iMipBrush == 0) { pbr->Clear(); } pbr->AddMipBrushFromObject3D_t(obTmp, fSwitchFactor); if( bApplyDefaultPolygonProperties) { // --- Apply default values for primitive polygons --- // for each mip in its brush FOREACHINLIST(CBrushMip, bm_lnInBrush, pbr->br_lhBrushMips, itbm) { // for all sectors in this mip FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) { // for all polygons in sector FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo) { itbpo->CopyPropertiesWithoutTexture( *theApp.m_pbpoPolygonWithDeafultValues); } } } } pbr->CalculateBoundingBoxes(); } // report errors catch( char *err_str) { AfxMessageBox( CString(err_str)); } } void CWorldEditorDoc::ApplyAutoColorize(void) { // if primitive auto colorization is on if( theApp.m_Preferences.ap_bAutoColorize) { theApp.m_vfpCurrent.vfp_colPolygonsColor = acol_ColorizePallete[theApp.m_iLastAutoColorizeColor]; theApp.m_vfpCurrent.vfp_colSectorsColor = theApp.m_vfpCurrent.vfp_colPolygonsColor; } } void CWorldEditorDoc::CreateConusPrimitive(void) { /* // report it _RPT0(_CRT_WARN, "\nConus\n"); */ // calculate height DOUBLE fHeight = theApp.m_vfpCurrent.vfp_fYMax-theApp.m_vfpCurrent.vfp_fYMin; if( fHeight < SNAP_FLOAT_GRID) fHeight = SNAP_FLOAT_GRID; // get count of vertices on the base INDEX vtxCt = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.Count(); // get shear values DOUBLE dx = theApp.m_vfpCurrent.vfp_fShearX; DOUBLE dz = theApp.m_vfpCurrent.vfp_fShearZ; // get stretch value for top-base vertices DOUBLE fStretchX = theApp.m_vfpCurrent.vfp_fStretchX; DOUBLE fStretchY = theApp.m_vfpCurrent.vfp_fStretchY; // base polygon DOUBLE3D *avBottomPolygon = new DOUBLE3D[ vtxCt]; for(INDEX iVtxBottom=0; iVtxBottomiSlicesIn360) iNoOfSlices=iSlicesIn360; DOUBLE3D **papvBases = new DOUBLE3D *[iNoOfSlices+1]; DOUBLE3D vCenter = DOUBLE3D( theApp.m_vfpCurrent.vfp_fRadius, 0.0f, 0.0f); BOOL bInvert = _bClosed; // rotate base vertices to calculate torus slices INDEX iSlice=0; for( ; iSlice0.0f) { angSlice(3) = -angSlice(3); } DOUBLEmatrix3D matrixRot; matrixRot ^= angSlice; papvBases[iSlice] = new DOUBLE3D[vtxCt]; // calculate vertex coordinates for each slice for(INDEX iBaseVtx=0; iBaseVtx0.0f) { angRotation1 = ANGLE3D( -AngleDeg( FLOAT((angle)*iStair+angleAdd)), 0, 0); angRotation2 = ANGLE3D( -AngleDeg( FLOAT((angle)*(iStair+1)+angleAdd)), 0, 0); } else { angRotation1 = ANGLE3D( AngleDeg( FLOAT((angle)*iStair+angleAdd)), 0, 0); angRotation2 = ANGLE3D( AngleDeg( FLOAT((angle)*(iStair+1)+angleAdd)), 0, 0); } DOUBLEmatrix3D matrixRot1; matrixRot1 ^= angRotation1; DOUBLEmatrix3D matrixRot2; matrixRot2 ^= angRotation2; papvBases[iStair] = new DOUBLE3D[4]; if( theApp.m_vfpCurrent.vfp_bLinearStaircases) { papvBases[iStair][0] = DOUBLE3D( -fWidth/2.0f, fHeight*iStair, -fLenght*iStair); papvBases[iStair][1] = DOUBLE3D( -fWidth/2.0f, fHeight*iStair, -fLenght*(iStair+1)); papvBases[iStair][2] = DOUBLE3D( fWidth/2.0f, fHeight*iStair, -fLenght*(iStair+1)); papvBases[iStair][3] = DOUBLE3D( fWidth/2.0f, fHeight*iStair, -fLenght*iStair); } else { // calculate vertex coordinates for each rotating stair base for(INDEX iBaseVtx=0; iBaseVtx<4; iBaseVtx++) { DOUBLE3D vCT1, vCT2; // create vector from center to rotating vertex if( fRadius>0.0f) { vCT1 = DOUBLE3D(-fRadius,0.0,0.0)-vCenter; vCT2 = DOUBLE3D(-fRadius+fWidth,0.0,0.0)-vCenter; } else { vCT1 = DOUBLE3D(-fRadius-fWidth,0.0,0.0)-vCenter; vCT2 = DOUBLE3D(-fRadius,0.0,0.0)-vCenter; } papvBases[iStair][3] = vCT2*matrixRot1+vCenter; papvBases[iStair][3](2) = fHeight*iStair; papvBases[iStair][2] = vCT2*matrixRot2+vCenter; papvBases[iStair][2](2) = fHeight*iStair; papvBases[iStair][1] = vCT1*matrixRot2+vCenter; papvBases[iStair][1](2) = fHeight*iStair; papvBases[iStair][0] = vCT1*matrixRot1+vCenter; papvBases[iStair][0](2) = fHeight*iStair; } } } DOUBLE3D avVtx[8]; // create polygons of stairs for(iStair=0; iStairosc_colColor = theApp.m_vfpCurrent.vfp_colSectorsColor; // create material _pomMaterial = _poscSector->osc_aomtMaterials.New(1); _pPrimitiveTexture = theApp.m_ptdActiveTexture; _fTextureWidth = METERS_MEX( _pPrimitiveTexture->GetWidth()); _fTextureHeight = METERS_MEX( _pPrimitiveTexture->GetHeight()); *_pomMaterial = CObjectMaterial( _pPrimitiveTexture->GetName()); _pomMaterial->SetColor( theApp.m_vfpCurrent.vfp_colPolygonsColor); // Pick up primitive-related variables _bClosed = theApp.m_vfpCurrent.vfp_bClosed; _bAutoCreateMipBrushes = theApp.m_vfpCurrent.vfp_bAutoCreateMipBrushes; } void GetTerrainPolygonEdges(CObjectSector &osec, INDEX iPolygon, INDEX iSlicesX, INDEX iSlicesZ, CObjectEdge *&poe0, CObjectEdge *&poe1, CObjectEdge *&poe2, CObjectEdge *&poe3, CObjectEdge *&poe4); void CWorldEditorDoc::CreateTerrainPrimitive(void) { CImageInfo iiDisplace; CImageInfo *piiDisplace = &iiDisplace; if( theApp.m_vfpCurrent.vfp_fnDisplacement != "") { try { iiDisplace.LoadAnyGfxFormat_t( theApp.m_vfpCurrent.vfp_fnDisplacement); m_slDisplaceTexTime=GetFileTimeStamp_t(theApp.m_vfpCurrent.vfp_fnDisplacement); } catch( char *strError) { (void) strError; piiDisplace = NULL; } } else { piiDisplace = NULL; } // get parameters for slices INDEX iSlicesX = theApp.m_vfpCurrent.vfp_iSlicesPerWidth; if( iSlicesX < 1) iSlicesX = 1; INDEX iSlicesZ = theApp.m_vfpCurrent.vfp_iSlicesPerHeight; if( iSlicesZ < 1) iSlicesZ = 1; INDEX iMip=0; // auto create mip brushes while( iSlicesX>=1 && iSlicesZ>=1 && ((iMip==0)||_bAutoCreateMipBrushes)) { CreateTerrainObject3D( piiDisplace, iSlicesX, iSlicesZ, iMip); ConvertObject3DToBrush(theApp.m_vfpCurrent.vfp_o3dPrimitive, TRUE, iMip, 5.0f+iMip*1.5, FALSE); iSlicesX /= 2; iSlicesZ /= 2; iMip++; } } void CWorldEditorDoc::CreateTerrainObject3D( CImageInfo *piiDisplace, INDEX iSlicesX, INDEX iSlicesZ, INDEX iMip) { // calculate width, lenght and heigth DOUBLE fWidth = (theApp.m_vfpCurrent.vfp_fXMax-theApp.m_vfpCurrent.vfp_fXMin); if( fWidth < SNAP_FLOAT_GRID) fWidth = SNAP_FLOAT_GRID; DOUBLE fHeight = (theApp.m_vfpCurrent.vfp_fYMax-theApp.m_vfpCurrent.vfp_fYMin); if( fHeight < SNAP_FLOAT_GRID) fHeight = SNAP_FLOAT_GRID; DOUBLE fLenght = (theApp.m_vfpCurrent.vfp_fZMax-theApp.m_vfpCurrent.vfp_fZMin); if( fLenght < SNAP_FLOAT_12) fLenght = SNAP_FLOAT_12; DOUBLE fMinX = theApp.m_vfpCurrent.vfp_fXMin; DOUBLE fMaxX = theApp.m_vfpCurrent.vfp_fXMax; DOUBLE fMinY = theApp.m_vfpCurrent.vfp_fYMin; DOUBLE fMaxY = theApp.m_vfpCurrent.vfp_fYMax; DOUBLE fMinZ = theApp.m_vfpCurrent.vfp_fZMin; DOUBLE fMaxZ = theApp.m_vfpCurrent.vfp_fZMax; DOUBLE fDX = fWidth/iSlicesX; DOUBLE fDZ = fLenght/iSlicesZ; FLOAT fAmplitude = theApp.m_vfpCurrent.vfp_fAmplitude; ULONG ulNonFllorPolygonFlags = BPOF_FULLBRIGHT|BPOF_DETAILPOLYGON|BPOF_PORTAL; if( !_bClosed) { // swap min and max y coordinates FLOAT fTemp = fMinY; fMinY = fMaxY; fMaxY = fTemp; ulNonFllorPolygonFlags = BPOF_FULLBRIGHT|BPOF_DETAILPOLYGON; } // initialize object 3D InitializeObject3DForPrimitive(); // get sector reference CObjectSector &osec = *_poscSector; INDEX ctVertices = (iSlicesX+1)*(iSlicesZ+1)+4; osec.osc_aovxVertices.New(ctVertices); osec.osc_aovxVertices.Lock(); // create 'floor' vertices {for( INDEX iz=0; iz<=iSlicesZ; iz++) { {for( INDEX ix=0; ix<=iSlicesX; ix++) { INDEX iVtx = iz*(iSlicesX+1)+ix; CObjectVertex &ov = osec.osc_aovxVertices[iVtx]; ov = DOUBLE3D(fMinX+fDX*ix, fMinY, fMinZ+fDZ*iz); DisplaceVertex( ov, piiDisplace, fMinX, fMaxX, fMinZ, fMaxZ, iSlicesX, iSlicesZ, fAmplitude); }} }} // create four 'ceiling' vertices #define START_OF_CEILING_VERTICES ((iSlicesX+1)*(iSlicesZ+1)) INDEX iVtx = START_OF_CEILING_VERTICES; osec.osc_aovxVertices[iVtx+0] = DOUBLE3D(fMinX, fMaxY, fMaxZ); osec.osc_aovxVertices[iVtx+1] = DOUBLE3D(fMaxX, fMaxY, fMaxZ); osec.osc_aovxVertices[iVtx+2] = DOUBLE3D(fMaxX, fMaxY, fMinZ); osec.osc_aovxVertices[iVtx+3] = DOUBLE3D(fMinX, fMaxY, fMinZ); // allocate edges INDEX ctEdges = iSlicesX*(iSlicesZ+1)+iSlicesZ*(iSlicesX+1)+iSlicesX*iSlicesZ+8; osec.osc_aoedEdges.New(ctEdges); // create edges from vertices osec.osc_aoedEdges.Lock(); // create horizontal edges {for( INDEX iz=0; izoed_Vertex1, *poe3->oed_Vertex0, *poe0->oed_Vertex0); CObjectPolygon &opo = osec.osc_aopoPolygons[iPolygon*2+0]; opo.opo_Plane = &opl; // set polygon edges opo.opo_PolygonEdges.New(3); opo.opo_PolygonEdges.Lock(); opo.opo_PolygonEdges[0].ope_Edge = poe0; opo.opo_PolygonEdges[0].ope_Backward = TRUE; opo.opo_PolygonEdges[1].ope_Edge = poe4; opo.opo_PolygonEdges[1].ope_Backward = FALSE; opo.opo_PolygonEdges[2].ope_Edge = poe3; opo.opo_PolygonEdges[2].ope_Backward = TRUE; opo.opo_PolygonEdges.Unlock(); // set other polygon properties opo.opo_Material = &omat; opo.opo_ulFlags = BPOF_FULLBRIGHT|BPOF_DETAILPOLYGON; opo.opo_colorColor = theApp.m_vfpCurrent.vfp_colPolygonsColor; } { // create lower polygon CObjectPlane &opl = osec.osc_aoplPlanes[iPolygon*2+1]; opl = DOUBLEplane3D( *poe1->oed_Vertex0, *poe1->oed_Vertex1, *poe2->oed_Vertex1); CObjectPolygon &opo = osec.osc_aopoPolygons[iPolygon*2+1]; opo.opo_Plane = &opl; // set polygon edges opo.opo_PolygonEdges.New(3); opo.opo_PolygonEdges.Lock(); opo.opo_PolygonEdges[0].ope_Edge = poe1; opo.opo_PolygonEdges[0].ope_Backward = FALSE; opo.opo_PolygonEdges[1].ope_Edge = poe2; opo.opo_PolygonEdges[1].ope_Backward = FALSE; opo.opo_PolygonEdges[2].ope_Edge = poe4; opo.opo_PolygonEdges[2].ope_Backward = TRUE; opo.opo_PolygonEdges.Unlock(); // set other polygon properties opo.opo_Material = &omat; opo.opo_ulFlags = BPOF_FULLBRIGHT|BPOF_DETAILPOLYGON; opo.opo_colorColor = theApp.m_vfpCurrent.vfp_colPolygonsColor; } } // create side polygons and their planes #define START_OF_SIDE_POLYGONS (iSlicesX*iSlicesZ*2) { // side polygon 0 CObjectPolygon &opo = osec.osc_aopoPolygons[START_OF_SIDE_POLYGONS+0]; CObjectEdge &oe0 = osec.osc_aoedEdges[ START_OF_BORDER_EDGES+0]; CObjectEdge &oe1 = osec.osc_aoedEdges[ START_OF_CEILING_EDGES+0]; CObjectPlane &opl = osec.osc_aoplPlanes[START_OF_SIDE_POLYGONS+0]; opl = DOUBLEplane3D( *oe0.oed_Vertex0, *oe0.oed_Vertex1, *oe1.oed_Vertex1); opo.opo_Plane = &opl; opo.opo_PolygonEdges.New(iSlicesX+3); opo.opo_PolygonEdges.Lock(); INDEX iEdg=0; for( ; iEdgm_pInfoFrame != NULL) && (pMainFrame->m_pInfoFrame->m_pInfoSheet->GetActivePage() == &pMainFrame->m_pInfoFrame->m_pInfoSheet->m_PgPrimitive) ) { // refresh primitive page pMainFrame->m_pInfoFrame->m_pInfoSheet->m_PgPrimitive.UpdateData( FALSE); } } /* * refresh position page */ void CWorldEditorDoc::RefreshCurrentInfoPage(void) { CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); // if info exists if( pMainFrame->m_pInfoFrame != NULL) { // if active page is position page if( pMainFrame->m_pInfoFrame->m_pInfoSheet->GetActivePage() == &pMainFrame->m_pInfoFrame->m_pInfoSheet->m_PgPosition) { // refresh position page pMainFrame->m_pInfoFrame->m_pInfoSheet->m_PgPosition.UpdateData( FALSE); } // if active page is texture page else if( pMainFrame->m_pInfoFrame->m_pInfoSheet->GetActivePage() == &pMainFrame->m_pInfoFrame->m_pInfoSheet->m_PgTexture) { // refresh polygon page pMainFrame->m_pInfoFrame->m_pInfoSheet->m_PgTexture.UpdateData( FALSE); } } } /* * snaps parameters for creating primitive to grid */ void CWorldEditorDoc::SnapPrimitiveValuesToGrid(void) { /* SnapFloat( theApp.m_vfpCurrent.vfp_fShearX); SnapFloat( theApp.m_vfpCurrent.vfp_fShearZ); SnapFloat( theApp.m_vfpCurrent.vfp_fXMin); SnapFloat( theApp.m_vfpCurrent.vfp_fXMax); SnapFloat( theApp.m_vfpCurrent.vfp_fYMin); SnapFloat( theApp.m_vfpCurrent.vfp_fYMax); SnapFloat( theApp.m_vfpCurrent.vfp_fZMin); SnapFloat( theApp.m_vfpCurrent.vfp_fZMax); */ } /* * Constructor. */ CUndo::CUndo(void) // throw char * { static INDEX iUndoFile = 0; // counter for undo files static char achUndoFileName[256]; // create a temporary file name sprintf(achUndoFileName, "Temp\\WED_Undo%d.tmp", iUndoFile); m_fnmUndoFile = CTString(achUndoFileName); // increment the counter of undo files iUndoFile++; } /* * Destructor. */ CUndo::~CUndo(void) { // delete the temporary file RemoveFile(m_fnmUndoFile); } /* * Loads state of the world from given undo/redo object */ void CWorldEditorDoc::LoadWorldFromUndoRedoList( CUndo *pUndoRedo) { // try to try { // load new world from the undo file m_woWorld.Load_t(pUndoRedo->m_fnmUndoFile); // m_woWorld.ReinitializeEntities(); // flush stale caches _pShell->Execute("FreeUnusedStock();"); // invalidate document (i.e. all views) UpdateAllViews( NULL); } // report errors catch( char *err_str) { AfxMessageBox( CString(err_str)); } } /* * Saves current state of the world as tail of give undo/redo list */ void CWorldEditorDoc::SaveWorldIntoUndoRedoList( CListHead &lhList) { // try to try { // allocate new undo/redo object CUndo *pUndoRedo = new CUndo; // save the world to the undo file m_woWorld.Save_t(pUndoRedo->m_fnmUndoFile); // add new undo as tail into undo list lhList.AddTail( pUndoRedo->m_lnListNode); } // report errors catch( char *err_str) { AfxMessageBox( CString(err_str)); } } /* * Remebers last operation into undo buffer */ void CWorldEditorDoc::RememberUndo(void) { // if undo remembering is disabled if( !theApp.m_bRememberUndo) { return; } // delete redo list FORDELETELIST(CUndo, m_lnListNode, m_lhRedo, itRedo) { delete &itRedo.Current(); } // while there are more members in undo buffer than allowed or list isn't empty while( (m_lhUndo.Count() >= theApp.m_Preferences.ap_iUndoLevels) && (!m_lhUndo.IsEmpty()) ) { // get first member in undo list CUndo *pUndo = LIST_HEAD( m_lhUndo, CUndo, m_lnListNode); // remove it pUndo->m_lnListNode.Remove(); // and delete it delete pUndo; } // if undo level is 0, don't remember any undo if( theApp.m_Preferences.ap_iUndoLevels == 0) { return; } // save current state of level into undo list SaveWorldIntoUndoRedoList( m_lhUndo); } /* * Undoes last operation */ void CWorldEditorDoc::Undo(void) { // if undo level is 0, or undo list is empty, don't do any undo if( (theApp.m_Preferences.ap_iUndoLevels == 0) || (m_lhUndo.IsEmpty()) ) { return; } // clear selections ClearSelections(); // get tail member from undo buffer CUndo *pUndo = LIST_TAIL( m_lhUndo, CUndo, m_lnListNode); // remove it from undo buffer pUndo->m_lnListNode.Remove(); // save current state of level into redo list SaveWorldIntoUndoRedoList( m_lhRedo); // restore last saved state from undo list LoadWorldFromUndoRedoList( pUndo); // delete just used undo member delete pUndo; } /* * Redoes last operation */ void CWorldEditorDoc::Redo(void) { // if redo list is empty, don't do any redo if( m_lhRedo.IsEmpty() ) { return; } // clear selections ClearSelections(); // get tail member from redo buffer CUndo *pRedo = LIST_TAIL( m_lhRedo, CUndo, m_lnListNode); // remove it from redo buffer pRedo->m_lnListNode.Remove(); // save current state of level into undo list SaveWorldIntoUndoRedoList( m_lhUndo); // restore last saved state from redo list LoadWorldFromUndoRedoList( pRedo); // delete just used redo member delete pRedo; } void CWorldEditorDoc::OnEditUndo() { if( GetEditingMode()==TERRAIN_MODE) { if( m_iCurrentTerrainUndo>=0) { ApplyTerrainUndo(&m_dcTerrainUndo[m_iCurrentTerrainUndo]); } } else { Undo(); } m_chDocument.MarkChanged(); UpdateAllViews( NULL); } void CWorldEditorDoc::OnUpdateEditUndo(CCmdUI* pCmdUI) { if( GetEditingMode()==TERRAIN_MODE) { pCmdUI->Enable( m_iCurrentTerrainUndo>=0); } else { pCmdUI->Enable( !m_lhUndo.IsEmpty()); } } void CWorldEditorDoc::OnEditRedo() { if( GetEditingMode()==TERRAIN_MODE) { INDEX ctRedos=m_dcTerrainUndo.Count()-1-m_iCurrentTerrainUndo; if( ctRedos>0) { ApplyTerrainRedo(&m_dcTerrainUndo[m_iCurrentTerrainUndo+1]); } } else { Redo(); } m_chDocument.MarkChanged(); UpdateAllViews( NULL); } void CWorldEditorDoc::OnUpdateEditRedo(CCmdUI* pCmdUI) { if( GetEditingMode()==TERRAIN_MODE) { INDEX ctRedos=m_dcTerrainUndo.Count()-1-m_iCurrentTerrainUndo; pCmdUI->Enable( ctRedos>0); } else { pCmdUI->Enable( !m_lhRedo.IsEmpty()); } } // paste given texture over polygon selection void CWorldEditorDoc::PasteTextureOverSelection_t( CTFileName fnTexName) { // for each of the selected polygons FOREACHINDYNAMICCONTAINER( m_selPolygonSelection, CBrushPolygon, itbpo) { CTextureData *pTD = (CTextureData *) itbpo->bpo_abptTextures[m_iTexture].bpt_toTexture.GetData(); if( (pTD == NULL) || (pTD->GetName() != fnTexName) ) { itbpo->bpo_abptTextures[m_iTexture].bpt_toTexture.SetData_t( fnTexName); // mark that document has been modified SetModifiedFlag( TRUE); // mark that selections have been changed m_chSelections.MarkChanged(); } } // update all views UpdateAllViews( NULL); } FLOAT _fLastTimeDeselectAllUsed = -10000.0f; // delete all selected members in current selection mode void CWorldEditorDoc::DeselectAll(void) { // if browse entities mode is on if( m_bBrowseEntitiesMode) { // cancel browse entities mode OnBrowseEntitiesMode(); } else { FLOAT fCurrentTime = _pTimer->GetRealTimeTick(); if( (fCurrentTime-_fLastTimeDeselectAllUsed)<1.0f) { ClearSelections(); _fLastTimeDeselectAllUsed = fCurrentTime; return; } _fLastTimeDeselectAllUsed = fCurrentTime; // according to current selection mode clear selected members switch( GetEditingMode()) { case POLYGON_MODE: { m_selPolygonSelection.Clear(); break; }; case SECTOR_MODE: { m_selSectorSelection.Clear(); break; }; case ENTITY_MODE: { m_selEntitySelection.Clear(); break; }; case VERTEX_MODE: { m_selVertexSelection.Clear(); break; }; case CSG_MODE: { break; }; case TERRAIN_MODE: { m_ptrSelectedTerrain=NULL; theApp.m_ctTerrainPage.MarkChanged(); break; }; default: { FatalError("Unknown editing mode."); break; }; } } // mark that selections have been changed m_chSelections.MarkChanged(); // redraw all viewes UpdateAllViews( NULL); } void CWorldEditorDoc::OnWorldSettings() { CDlgWorldSettings dlgWorldSettings; dlgWorldSettings.SetupBcgSettings( FALSE); if( dlgWorldSettings.DoModal() != IDOK) return; try { // !!!! m_woWorld.SetBackgroundTexture_t(CTString(dlgWorldSettings.m_fnBackgroundPicture)); } catch( char *strError) { AfxMessageBox( CString(strError)); } m_woWorld.SetBackgroundColor( dlgWorldSettings.m_BackgroundColor.GetColor()); m_woWorld.SetDescription( CTString(CStringA(dlgWorldSettings.m_strMissionDescription))); SetModifiedFlag( TRUE); UpdateAllViews( NULL); } /* * CSG Add */ void CWorldEditorDoc::OnCsgAdd() { BOOL bShift = (GetKeyState( VK_SHIFT)&0x8000) != 0; if( bShift) PreApplyCSG( CSG_ADD_REVERSE); // reverse priorities else PreApplyCSG( CSG_ADD); } void CWorldEditorDoc::PreApplyCSG(enum CSGType CSGType) { if( GetEditingMode() != CSG_MODE) { // search for destination entity CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); CEntity *penTarget = pMainFrame->m_CSGDesitnationCombo.GetSelectedBrushEntity(); if( (penTarget == NULL) || (penTarget->IsSelected( ENF_SELECTED)) ) { AfxMessageBox( L"Illegal CSG operands (target must not be selected) !"); return; } // create temporary world CWorld woDummyWorld; // create zero placement CPlacement3D plZeroPlacement; plZeroPlacement.pl_PositionVector = FLOAT3D(0.0f,0.0f,0.0f); plZeroPlacement.pl_OrientationAngle = ANGLE3D(0,0,0); CDynamicContainer dcenDummy; // for all still selected brush entities {FOREACHINDYNAMICCONTAINER(m_selEntitySelection, CEntity, iten) { CEntity::RenderType rt = iten->GetRenderType(); // if the entity is brush and it is not empty if( rt==CEntity::RT_BRUSH || rt==CEntity::RT_FIELDBRUSH) { // copy entity into dummy world woDummyWorld.CopyOneEntity( *iten, plZeroPlacement); } // deselect entities that are not brushes else { // deselect clicked sector dcenDummy.Add( &iten.Current()); } }} // for entities that should be deselected {FOREACHINDYNAMICCONTAINER(dcenDummy, CEntity, iten) { m_selEntitySelection.Deselect( *iten); }} dcenDummy.Clear(); // remember undo before doing CSG operations RememberUndo(); // clear all selections except entity ClearSelections( ST_ENTITY); // delete all brush entities that are still selected m_woWorld.DestroyEntities( m_selEntitySelection); // set wait cursor CWaitCursor StartWaitCursor; m_csgtLastUsedCSGOperation = CSG_ADD_ENTITIES; m_bPreLastUsedPrimitiveMode = m_bLastUsedPrimitiveMode; m_bLastUsedPrimitiveMode = FALSE; // for all of the dummy world's entities {FOREACHINDYNAMICCONTAINER(woDummyWorld.wo_cenEntities, CEntity, iten) { // create another dummy world CWorld woOneBrush; // copy entity from dummy world to world containing only one brush CEntity *penOnlyBrush = woOneBrush.CopyOneEntity( *iten, plZeroPlacement); // ----------- Do CSG beetween current entity and destination combo's entity switch( CSGType) { case CSG_ADD: { m_woWorld.CSGAdd(*penTarget, woOneBrush, *penOnlyBrush, plZeroPlacement); break; } case CSG_ADD_REVERSE: { m_woWorld.CSGAddReverse(*penTarget, woOneBrush, *penOnlyBrush, plZeroPlacement); break; } case CSG_REMOVE: { m_woWorld.CSGRemove(*penTarget, woOneBrush, *penOnlyBrush, plZeroPlacement); break; } case CSG_SPLIT_SECTORS: { m_woWorld.SplitSectors(*penTarget, m_selSectorSelection, woOneBrush, *penOnlyBrush, plZeroPlacement); break; } case CSG_SPLIT_POLYGONS: { m_woWorld.SplitPolygons(*penTarget, m_selPolygonSelection, woOneBrush, *penOnlyBrush, plZeroPlacement); break; } default: { ASSERTALWAYS("PreAplyCSG() function called with illegal CSG operation."); return; } } }} // mark that selections have been changed SetModifiedFlag(TRUE); m_chSelections.MarkChanged(); m_chDocument.MarkChanged(); } else { ApplyCSG( CSGType); } UpdateAllViews( NULL); } BOOL CWorldEditorDoc::IsEntityCSGEnabled(void) { if(m_pwoSecondLayer != NULL) return TRUE; // if we are in entity mode if( GetEditingMode() != CSG_MODE) { // for all selected entities FOREACHINDYNAMICCONTAINER(m_selEntitySelection, CEntity, iten) { CEntity::RenderType rt = iten->GetRenderType(); if( rt==CEntity::RT_BRUSH || rt==CEntity::RT_FIELDBRUSH) { return TRUE; } } } return FALSE; } void CWorldEditorDoc::OnUpdateCsgAdd(CCmdUI* pCmdUI) { pCmdUI->Enable( IsEntityCSGEnabled()); } /* * CSG Remove */ void CWorldEditorDoc::OnCsgRemove() { PreApplyCSG( CSG_REMOVE); } void CWorldEditorDoc::OnUpdateCsgRemove(CCmdUI* pCmdUI) { pCmdUI->Enable( IsEntityCSGEnabled()); } /* * CSG Split Sectors. */ void CWorldEditorDoc::OnCsgSplitSectors() { PreApplyCSG( CSG_SPLIT_SECTORS); } void CWorldEditorDoc::OnUpdateCsgSplitSectors(CCmdUI* pCmdUI) { pCmdUI->Enable( IsEntityCSGEnabled()); } /* * CSG Join Sectors. */ void CWorldEditorDoc::OnCsgJoinSectors() { ApplyCSG( CSG_JOIN_SECTORS); } void CWorldEditorDoc::OnUpdateCsgJoinSectors(CCmdUI* pCmdUI) { pCmdUI->Enable( m_woWorld.CanJoinSectors( m_selSectorSelection)); } /* * CSG Split Polygons. */ void CWorldEditorDoc::OnCsgSplitPolygons() { PreApplyCSG( CSG_SPLIT_POLYGONS); } void CWorldEditorDoc::OnUpdateCsgSplitPolygons(CCmdUI* pCmdUI) { pCmdUI->Enable( IsEntityCSGEnabled() && m_selPolygonSelection.Count() > 0); } /* * CSG Join Polygons. */ void CWorldEditorDoc::OnCsgJoinPolygons() { BOOL bCtrl = (GetKeyState( VK_CONTROL)&0x8000) != 0; if (bCtrl) { ApplyCSG(CSG_JOIN_POLYGONS_KEEP_TEXTURES); // keep textures if control pressed } else { ApplyCSG(CSG_JOIN_POLYGONS); } } void CWorldEditorDoc::OnUpdateCsgJoinPolygons(CCmdUI* pCmdUI) { // check for polygon mode and count (crashed here right after merging vertices) if( (m_iMode == POLYGON_MODE) && (m_selPolygonSelection.Count() != 0) ) { pCmdUI->Enable( m_woWorld.CanJoinPolygons(m_selPolygonSelection)); } else { pCmdUI->Enable( FALSE); } } void CWorldEditorDoc::OnCsgJoinAllPolygons() { BOOL bCtrl = (GetKeyState( VK_CONTROL)&0x8000) != 0; if (bCtrl) { ApplyCSG(CSG_JOIN_ALL_POLYGONS_KEEP_TEXTURES); // keep textures if control pressed } else { ApplyCSG(CSG_JOIN_ALL_POLYGONS); } } void CWorldEditorDoc::OnUpdateCsgJoinAllPolygons(CCmdUI* pCmdUI) { pCmdUI->Enable( m_woWorld.CanJoinAllPossiblePolygons(m_selPolygonSelection)); } /* * Cancel CSG. */ void CWorldEditorDoc::OnCsgCancel() { if( m_iMode == CSG_MODE) { CancelCSG(); } else { SetEditingMode( VERTEX_MODE); } } void CWorldEditorDoc::OnShowOrientation() { m_bOrientationIcons = !m_bOrientationIcons; theApp.WriteProfileInt(L"World editor", L"Orientation icons", m_bOrientationIcons); UpdateAllViews( NULL); } void CWorldEditorDoc::OnUpdateShowOrientation(CCmdUI* pCmdUI) { pCmdUI->SetCheck( m_bOrientationIcons); } void CWorldEditorDoc::OnAutoSnap() { m_bAutoSnap = !m_bAutoSnap; } void CWorldEditorDoc::OnUpdateAutoSnap(CCmdUI* pCmdUI) { pCmdUI->SetCheck( m_bAutoSnap); } void CWorldEditorDoc::OnCalculateShadows() { RememberUndo(); // this is before wait cursor, so that we can see if it gets blocked here // set wait cursor CWaitCursor StartWaitCursor; // reset profile _pfWorldEditingProfile.Reset(); m_woWorld.CalculateDirectionalShadows(); if( GetEditingMode()==TERRAIN_MODE) { CTerrain *ptTerrain=GetTerrain(); if(ptTerrain!=NULL) ptTerrain->UpdateShadowMap(); } // create shadows report _pfWorldEditingProfile.Report( theApp.m_strCSGAndShadowStatistics); theApp.m_strCSGAndShadowStatistics.SaveVar(CTString("Temp\\Profile_Shadows.txt")); // mark that document has changed SetModifiedFlag(TRUE); m_chDocument.MarkChanged(); // invalidate document (i.e. all views) UpdateAllViews( NULL); } void CWorldEditorDoc::OnHideSelected() { if( m_iMode == ENTITY_MODE) { OnHideSelectedEntities(); } else { OnHideSelectedSectors(); } } void CWorldEditorDoc::OnHideUnselected() { if( m_iMode == ENTITY_MODE) { OnHideUnselectedEntities(); } if( m_iMode == SECTOR_MODE) { OnHideUnselectedSectors(); } if( m_iMode == TERRAIN_MODE) { theApp.m_iTerrainBrushMode=TBM_MAXIMUM; theApp.m_ctTerrainPageCanvas.MarkChanged(); SetStatusLineModeInfoMessage(); } } void CWorldEditorDoc::OnShowAll() { if( m_iMode == ENTITY_MODE) { OnShowAllEntities(); } if( m_iMode == SECTOR_MODE) { OnShowAllSectors(); } } void CWorldEditorDoc::OnUpdateHideSelected(CCmdUI* pCmdUI) { // enable button if selection is not empty if( m_iMode == ENTITY_MODE) { pCmdUI->Enable( m_selEntitySelection.Count() != 0); } else { pCmdUI->Enable( m_selSectorSelection.Count() != 0); } } void CWorldEditorDoc::OnHideSelectedSectors() { // hide selected sectors m_woWorld.HideSelectedSectors( m_selSectorSelection); // update all views UpdateAllViews( NULL); } void CWorldEditorDoc::OnHideUnselectedSectors() { // hide unselected sectors m_woWorld.HideUnselectedSectors(); // update all views UpdateAllViews( NULL); } void CWorldEditorDoc::OnShowAllSectors() { // hide unselected sectors m_woWorld.ShowAllSectors(); // update all views UpdateAllViews( NULL); } void CWorldEditorDoc::OnHideSelectedEntities() { // hide selected entities m_woWorld.HideSelectedEntities( m_selEntitySelection); // update all views UpdateAllViews( NULL); } void CWorldEditorDoc::OnHideUnselectedEntities() { // hide unselected entities m_woWorld.HideUnselectedEntities(); // update all views UpdateAllViews( NULL); } void CWorldEditorDoc::OnShowAllEntities() { // hide unselected entities m_woWorld.ShowAllEntities(); // update all views UpdateAllViews( NULL); } // How box is created from 2 vertices: indices of first or second vertice, one that gives // current coordinate for one of box's vertices (we have two source points, T0 and T1. // Coordinate for box vertice 0 is B0(xT0, yT0, zT0), vertice 1 is B1(xT1, yT0, zT0) ...) static INDEX _aiBoxCreation[8][3] = { 0,0,0, 1,0,0, 1,0,1, 0,0,1, 0,1,0, 1,1,0, 1,1,1, 0,1,1 }; // Array of indices to vertices that need to be corrected if one of box's vertices is moved // (if vertice 0 moved, copy its x coordinate to vertices 3,7,4, copy y coordinate to // vertices 1,2,3, z to 1,5,4...) static INDEX _aiCorrectVertices[8][3*3] = { 3,7,4, 1,2,3, 1,5,4, 2,5,6, 0,2,3, 0,4,5, 1,5,6, 0,1,3, 3,6,7, 0,4,7, 0,1,2, 2,6,7, 0,3,7, 5,6,7, 0,1,5, 1,2,6, 4,6,7, 0,1,4, 1,2,5, 4,5,7, 2,3,7, 0,3,4, 4,5,6, 2,3,6 }; // Selects entity with given index inside volume void CWorldEditorDoc::SelectGivenEntity( INDEX iEntityToSelect) { // clear normal entity selection m_selEntitySelection.Clear(); // if there is any entity in volume container if( m_cenEntitiesSelectedByVolume.Count() != 0) { // clip requested entity if( iEntityToSelect >= m_cenEntitiesSelectedByVolume.Count()) { iEntityToSelect = 0; } m_iSelectedEntityInVolume = iEntityToSelect; // lock the selection m_cenEntitiesSelectedByVolume.Lock(); // get requested entity CEntity *penEntity = m_cenEntitiesSelectedByVolume.Pointer(m_iSelectedEntityInVolume); // unlock the selection m_cenEntitiesSelectedByVolume.Unlock(); // add entity into normal selection m_selEntitySelection.Select( *penEntity); // center entity POSITION pos = GetFirstViewPosition(); CWorldEditorView *pWedView = (CWorldEditorView *) GetNextView(pos); pWedView->OnCenterEntity(); } // mark that selections have been changed m_chSelections.MarkChanged(); // obtain main frame ptr CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); // and refresh property combo manualy calling on idle pMainFrame->m_PropertyComboBar.m_PropertyComboBox.OnIdle( 0); // update all views UpdateAllViews( NULL); } /* * Function puts world's entites occupied by volume box to volume box selection */ void CWorldEditorDoc::SelectEntitiesByVolumeBox(void) { m_iSelectedEntityInVolume = 0; // create volume box (for browsing entities) FLOAT3D vMinMax[2]; vMinMax[0] = m_vCreateBoxVertice0; vMinMax[1] = m_vCreateBoxVertice1; // create coordinates for box's vertices for( INDEX iBoxVertice=0;iBoxVertice<8;iBoxVertice++) { m_avVolumeBoxVertice[iBoxVertice] = FLOAT3D( vMinMax[_aiBoxCreation[iBoxVertice][0]](1), vMinMax[_aiBoxCreation[iBoxVertice][1]](2), vMinMax[_aiBoxCreation[iBoxVertice][2]](3) ); } // create bbox from requested volume FLOATaabbox3D bboxVolume( m_vCreateBoxVertice0, m_vCreateBoxVertice1); // clear entity volume container m_cenEntitiesSelectedByVolume.Clear(); // clear normal entity selection m_selEntitySelection.Clear(); // for all of the world's entities FOREACHINDYNAMICCONTAINER(m_woWorld.wo_cenEntities, CEntity, iten) { CPlacement3D plEntityPlacement = iten->GetPlacement(); // if entity handle is inside volume box if( bboxVolume.HasContactWith( FLOATaabbox3D(plEntityPlacement.pl_PositionVector)) ) { // add entity into volume container m_cenEntitiesSelectedByVolume.Add( iten); } } SetStatusLineModeInfoMessage(); } /* * Function corects coordinates of vertices that represent box because given vertice is * moved and box has invalid geometry */ void CWorldEditorDoc::CorrectBox(INDEX iMovedVtx, FLOAT3D vNewPosition) { for( INDEX iCoordinate=0;iCoordinate<3; iCoordinate++) { for( INDEX iVtxToCorrect=0;iVtxToCorrect<3; iVtxToCorrect++) { // copy coordinate m_avVolumeBoxVertice[ _aiCorrectVertices[iMovedVtx][iCoordinate*3+iVtxToCorrect]] (iCoordinate+1) = vNewPosition(iCoordinate+1); } } // copy new moved vertice's position m_avVolumeBoxVertice[ iMovedVtx] = vNewPosition; // set new values for next creation of volume box m_vCreateBoxVertice0 = m_avVolumeBoxVertice[ 0]; m_vCreateBoxVertice1 = m_avVolumeBoxVertice[ 6]; } void CWorldEditorDoc::OnBrowseEntitiesMode() { m_bBrowseEntitiesMode = !m_bBrowseEntitiesMode; // if we should start select by volume (or browse entities) mode if( m_bBrowseEntitiesMode) { SelectEntitiesByVolumeBox(); } // stop select by volume mode else { // write to ini last vertices used for volume box creation char strIni[ 128]; sprintf( strIni, "%f %f %f", m_vCreateBoxVertice0(1), m_vCreateBoxVertice0(2), m_vCreateBoxVertice0(3)); theApp.WriteProfileString( L"World editor", L"Volume box min", CString(strIni)); sprintf( strIni, "%f %f %f", m_vCreateBoxVertice1(1), m_vCreateBoxVertice1(2), m_vCreateBoxVertice1(3)); theApp.WriteProfileString( L"World editor", L"Volume box max", CString(strIni)); m_chSelections.MarkChanged(); } // update all views UpdateAllViews( NULL); } void CWorldEditorDoc::OnUpdateBrowseEntitiesMode(CCmdUI* pCmdUI) { pCmdUI->SetCheck( m_bBrowseEntitiesMode); pCmdUI->Enable( GetEditingMode() == ENTITY_MODE); } void CWorldEditorDoc::OnPreviousSelectedEntity() { CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); INDEX iEntityToSelect = (m_iSelectedEntityInVolume+ m_cenEntitiesSelectedByVolume.Count()-1)%m_cenEntitiesSelectedByVolume.Count(); // clip entity index if( iEntityToSelect >= m_cenEntitiesSelectedByVolume.Count()) { iEntityToSelect = 0; } CTString strMessage; strMessage.PrintF("Entity %d/%d", iEntityToSelect, m_cenEntitiesSelectedByVolume.Count()); pMainFrame->SetStatusBarMessage( strMessage, STATUS_LINE_PANE, 2); SelectGivenEntity( iEntityToSelect); } void CWorldEditorDoc::OnNextSelectedEntity() { CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); INDEX iEntityToSelect = (m_iSelectedEntityInVolume+1)%m_cenEntitiesSelectedByVolume.Count(); // clip entity index if( iEntityToSelect >= m_cenEntitiesSelectedByVolume.Count() ) { iEntityToSelect = 0; } CTString strMessage; strMessage.PrintF("Entity %d/%d", iEntityToSelect, m_cenEntitiesSelectedByVolume.Count()); pMainFrame->SetStatusBarMessage( strMessage, STATUS_LINE_PANE, 2); SelectGivenEntity( iEntityToSelect); } void CWorldEditorDoc::OnUpdatePreviousSelectedEntity(CCmdUI* pCmdUI) { pCmdUI->Enable( m_cenEntitiesSelectedByVolume.Count()>0); } void CWorldEditorDoc::OnUpdateNextSelectedEntity(CCmdUI* pCmdUI) { pCmdUI->Enable( m_cenEntitiesSelectedByVolume.Count()>0); } void CWorldEditorDoc::OnSelectAllInVolume( void) { // for each of the entities selected by volume FOREACHINDYNAMICCONTAINER( m_cenEntitiesSelectedByVolume, CEntity, iten) { if( !iten->IsSelected( ENF_SELECTED)) { // add entity into normal selection m_selEntitySelection.Select( *iten); } } // clear volume container //m_cenEntitiesSelectedByVolume.Clear(); // go out of browse by volume mode if( m_bBrowseEntitiesMode) OnBrowseEntitiesMode(); // mark that selections have been changed m_chSelections.MarkChanged(); // obtain main frame ptr CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); // and refresh property combo manualy calling on idle pMainFrame->m_PropertyComboBar.m_PropertyComboBox.OnIdle( 0); // update all views UpdateAllViews( NULL); } void CWorldEditorDoc::OnJoinLayers() { ApplyCSG( CSG_JOIN_LAYERS); } void CWorldEditorDoc::OnUpdateSelectByClass(CCmdUI* pCmdUI) { pCmdUI->Enable( TRUE); } void CWorldEditorDoc::OnSelectByClass() { CWorldEditorView *pWorldEditorView = theApp.GetActiveView(); CDlgBrowseByClass dlgBrowseByClass; INDEX ctEntities = m_cenEntitiesSelectedByVolume.Count(); // auto start in all entities mode if no entities are selected dlgBrowseByClass.m_bShowVolume = (ctEntities != 0); if( dlgBrowseByClass.DoModal() == IDOK) { m_chSelections.MarkChanged(); SetEditingMode( ENTITY_MODE); UpdateAllViews( NULL); if( (pWorldEditorView != NULL) && (dlgBrowseByClass.m_bCenterSelected) ) { pWorldEditorView->CenterSelected(); } } } void CWorldEditorDoc::OnSelectByClassImportant() { CWorldEditorView *pWorldEditorView = theApp.GetActiveView(); CDlgBrowseByClass dlgBrowseByClass; dlgBrowseByClass.m_bShowImportants = TRUE; if( dlgBrowseByClass.DoModal() == IDOK) { m_chSelections.MarkChanged(); SetEditingMode( ENTITY_MODE); UpdateAllViews( NULL); if( (pWorldEditorView != NULL) && (dlgBrowseByClass.m_bCenterSelected) ) { pWorldEditorView->CenterSelected(); } } } void CWorldEditorDoc::OnCrossroadForN() { if( m_iMode == VERTEX_MODE) { CDlgSnapVertex dlg; dlg.DoModal(); } else { OnSelectByClassAll(); } } void CWorldEditorDoc::OnSelectByClassAll() { CWorldEditorView *pWorldEditorView = theApp.GetActiveView(); CDlgBrowseByClass dlgBrowseByClass; dlgBrowseByClass.m_bShowVolume = FALSE; if( dlgBrowseByClass.DoModal() == IDOK) { m_chSelections.MarkChanged(); SetEditingMode( ENTITY_MODE); UpdateAllViews( NULL); if( (pWorldEditorView != NULL) && (dlgBrowseByClass.m_bCenterSelected) ) { pWorldEditorView->CenterSelected(); } } } void CWorldEditorDoc::OnTexture1() { theApp.m_bTexture1 = !theApp.m_bTexture1; UpdateAllViews( NULL); } void CWorldEditorDoc::OnUpdateTexture1(CCmdUI* pCmdUI) { pCmdUI->SetCheck( theApp.m_bTexture1); } void CWorldEditorDoc::OnTexture2() { theApp.m_bTexture2 = !theApp.m_bTexture2; UpdateAllViews( NULL); } void CWorldEditorDoc::OnUpdateTexture2(CCmdUI* pCmdUI) { pCmdUI->SetCheck( theApp.m_bTexture2); } void CWorldEditorDoc::OnTexture3() { theApp.m_bTexture3 = !theApp.m_bTexture3; UpdateAllViews( NULL); } void CWorldEditorDoc::OnUpdateTexture3(CCmdUI* pCmdUI) { pCmdUI->SetCheck( theApp.m_bTexture3); } void CWorldEditorDoc::SetActiveTextureLayer(INDEX iLayer) { if( GetEditingMode()==TERRAIN_MODE) { CTerrain *ptTerrain=GetTerrain(); if(ptTerrain==NULL) return; if(iLayer>=ptTerrain->tr_atlLayers.Count()) return; SelectLayer(iLayer); m_chSelections.MarkChanged(); theApp.m_ctTerrainPageCanvas.MarkChanged(); } else if(iLayer<3) { m_iTexture = iLayer; m_chSelections.MarkChanged(); UpdateAllViews( NULL); } } void CWorldEditorDoc::OnTextureMode1() { SetActiveTextureLayer(0); } void CWorldEditorDoc::OnTextureMode2() { SetActiveTextureLayer(1); } void CWorldEditorDoc::OnTextureMode3() { SetActiveTextureLayer(2); } void CWorldEditorDoc::OnTextureMode4() { SetActiveTextureLayer(3); } void CWorldEditorDoc::OnTextureMode5() { SetActiveTextureLayer(4); } void CWorldEditorDoc::OnTextureMode6() { SetActiveTextureLayer(5); } void CWorldEditorDoc::OnTextureMode7() { SetActiveTextureLayer(6); } void CWorldEditorDoc::OnTextureMode8() { SetActiveTextureLayer(7); } void CWorldEditorDoc::OnTextureMode9() { SetActiveTextureLayer(8); } void CWorldEditorDoc::OnTextureMode10() { SetActiveTextureLayer(9); } void CWorldEditorDoc::OnSaveThumbnail( void) { // remember current position for thumbnail saving into world CWorldEditorView *pViewForThumbnail = theApp.GetActiveView(); if( pViewForThumbnail == NULL) return; CChildFrame *pChild = pViewForThumbnail->GetChildFrame(); // set new viewer settings m_woWorld.wo_plThumbnailFocus = pChild->m_mvViewer.mv_plViewer; m_woWorld.wo_fThumbnailTargetDistance = pChild->m_mvViewer.mv_fTargetDistance; // save thumbnail SaveThumbnail(); } void CWorldEditorDoc::SaveThumbnail() { CDrawPort *pDrawPort; CImageInfo II; CTextureData TD; CAnimData AD; ULONG flags = NONE; // if document isn't saved, call save as if( GetPathName() == "") { // if failed if( !DoFileSave()) return; } // try to find perspective view POSITION pos = GetFirstViewPosition(); CWorldEditorView *pViewForThumbnail = theApp.GetActiveView(); CWorldEditorView *pWedView; FOREVER { pWedView = (CWorldEditorView *) GetNextView(pos); if( pWedView == NULL) return; if( pWedView->m_ptProjectionType == CSlaveViewer::PT_PERSPECTIVE) { pViewForThumbnail = pWedView; break; } } // if perspective view can't be found, don't do anything if( pViewForThumbnail == NULL) return; CChildFrame *pChild = pViewForThumbnail->GetChildFrame(); // create canvas to render picture _pGfx->CreateWorkCanvas( 128, 128, &pDrawPort); if( pDrawPort != NULL) { if( pDrawPort->Lock()) { // remember old viewer settings CPlacement3D plOrgPlacement = pChild->m_mvViewer.mv_plViewer; FLOAT fOldTargetDistance = pChild->m_mvViewer.mv_fTargetDistance; // set new viewer settings pChild->m_mvViewer.mv_plViewer = m_woWorld.wo_plThumbnailFocus; pChild->m_mvViewer.mv_fTargetDistance = m_woWorld.wo_fThumbnailTargetDistance; // render vew from thumbnail position pViewForThumbnail->RenderView( pDrawPort); // restore orgiginal position pChild->m_mvViewer.mv_plViewer = plOrgPlacement; pChild->m_mvViewer.mv_fTargetDistance = fOldTargetDistance; pDrawPort->Unlock(); } CTFileName fnDocument = CTString( CStringA(GetPathName())); CTFileName fnThumbnail = fnDocument.FileDir() + fnDocument.FileName() + CTString(".tbn"); pDrawPort->GrabScreen(II); // try to try { // remove application path fnThumbnail.RemoveApplicationPath_t(); // create image info from texture TD.Create_t( &II, 128, MAX_MEX_LOG2, FALSE); // save the thumbnail CTFileStream File; File.Create_t( fnThumbnail); TD.Write_t( &File); File.Close(); } // if failed catch (char *strError) { // report error AfxMessageBox(CString(strError)); } _pGfx->DestroyWorkCanvas( pDrawPort); pDrawPort = NULL; } CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); // refresh browser (open and close current virtual directory) pMainFrame->m_Browser.CloseSelectedDirectory(); pMainFrame->m_Browser.OpenSelectedDirectory(); } void CWorldEditorDoc::ResetPrimitive() { if( !m_bPrimitiveMode) return; FLOAT fDX = (theApp.m_vfpCurrent.vfp_fXMax+theApp.m_vfpCurrent.vfp_fXMin)/2.0f; FLOAT fDY = theApp.m_vfpCurrent.vfp_fYMin; FLOAT fDZ = (theApp.m_vfpCurrent.vfp_fZMax+theApp.m_vfpCurrent.vfp_fZMin)/2.0f; FLOAT fWidth = theApp.m_vfpCurrent.vfp_fXMax-theApp.m_vfpCurrent.vfp_fXMin; FLOAT fHeight = theApp.m_vfpCurrent.vfp_fYMax-theApp.m_vfpCurrent.vfp_fYMin; FLOAT fLenght = (theApp.m_vfpCurrent.vfp_fZMax-theApp.m_vfpCurrent.vfp_fZMin); FLOAT3D vDelta = FLOAT3D( fDX, fDY, fDZ); m_plSecondLayer.pl_PositionVector += vDelta; theApp.m_vfpCurrent.vfp_plPrimitive = m_plSecondLayer; theApp.m_vfpCurrent.vfp_fXMin = -fWidth/2.0f; theApp.m_vfpCurrent.vfp_fXMax = fWidth/2.0f; theApp.m_vfpCurrent.vfp_fYMin = 0.0f; theApp.m_vfpCurrent.vfp_fYMax = fHeight; theApp.m_vfpCurrent.vfp_fZMin = -fLenght/2.0f; theApp.m_vfpCurrent.vfp_fZMax = fLenght/2.0f; RefreshPrimitivePage(); m_bPrimitiveCreatedFirstTime = TRUE; _bDontRecalculateBase = FALSE; CreatePrimitive(); UpdateAllViews( NULL); } void CWorldEditorDoc::DeletePrimitiveVertex(INDEX iVtxToDelete) { // get count of vertices on the base INDEX vtxCt = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.Count(); if( vtxCt < 4) return; CStaticArray avDecreased; avDecreased.New(vtxCt-1); INDEX iVtxNew = 0; for( INDEX iVtxOld = 0; iVtxOld avIncreased; avIncreased.New(vtxCt+1); INDEX iVtxNew = 0; for( INDEX iVtxOld = 0; iVtxOldEnable( m_pwoSecondLayer != NULL && !m_bPrimitiveMode); } void CWorldEditorDoc::ApplyMirrorAndStretch(INDEX iMirror, FLOAT fStretch) { try { CWorld woDummy; if( m_pwoSecondLayer != NULL) { woDummy.MirrorAndStretch( *m_pwoSecondLayer, fStretch, (enum WorldMirrorType)iMirror); woDummy.Save_t(CTString("Temp\\MirrorAndStretch.wld")); m_pwoSecondLayer->Clear(); m_pwoSecondLayer->Load_t(CTString("Temp\\MirrorAndStretch.wld")); } else { int iRes = AfxMessageBox(L"Are you sure you want to mirror/stretch entire world?", MB_ICONEXCLAMATION|MB_YESNO|MB_DEFBUTTON2); if (iRes!=IDYES) { return; } CWaitCursor wcWait; RememberUndo(); ClearSelections(); woDummy.MirrorAndStretch( m_woWorld, fStretch, (enum WorldMirrorType)iMirror); woDummy.Save_t(CTString("Temp\\MirrorAndStretch.wld")); m_woWorld.Clear(); m_woWorld.Load_t(CTString("Temp\\MirrorAndStretch.wld")); m_woWorld.CalculateNonDirectionalShadows(); m_chDocument.MarkChanged(); SetModifiedFlag(); } } catch (char *strError) { AfxMessageBox(CString(strError)); } UpdateAllViews( NULL); } void CWorldEditorDoc::OnFilterSelection() { CDlgFilterPolygonSelection dlgFilterPolygonSelection; dlgFilterPolygonSelection.DoModal(); } BOOL CWorldEditorDoc::IsCloneUpdatingAllowed(void) { INDEX ctEntities = m_selEntitySelection.Count(); // if only one entity is selected if( ctEntities == 1) { // get only selected entity m_selEntitySelection.Lock(); CEntity *penOnlySelected = &m_selEntitySelection[0]; m_selEntitySelection.Unlock(); // if entity doesn't have parent if( penOnlySelected->GetParent() == NULL) { // allow clone updating return TRUE; } } // disable clone updating return FALSE; } void CWorldEditorDoc::OnUpdateUpdateClones(CCmdUI* pCmdUI) { pCmdUI->Enable( IsCloneUpdatingAllowed() ); } // delete selected entity with all descendents void DeleteEntityWithDescendents( CWorld &woWorld, CEntity *penParent) { FORDELETELIST( CEntity, en_lnInParent, penParent->en_lhChildren, itenChild) { DeleteEntityWithDescendents( woWorld, &*itenChild); } woWorld.DestroyOneEntity( penParent); } void CWorldEditorDoc::OnUpdateClones() { // for each case if( m_selEntitySelection.Count() == 0) { return; } // get only selected entity m_selEntitySelection.Lock(); CEntity *penOnlySelected = &m_selEntitySelection[0]; m_selEntitySelection.Unlock(); // clear selections before destroying some entities ClearSelections(); // remember placements and delete all clones (entities with same name) CTString strName = penOnlySelected->GetName(); if( strName == "World Base") { CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd()); if( ::MessageBoxA( pMainFrame->m_hWnd, "Are you sure that you want to execute update clones\n" "on entity named: \"World Base\"?", "Warning !", MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON1| MB_SYSTEMMODAL | MB_TOPMOST) != IDYES) { return; } } RememberUndo(); CDynamicContainer apenClones; {FOREACHINDYNAMICCONTAINER( m_woWorld.wo_cenEntities, CEntity, iten) { // if this is clone (by name), it is not original and it is not child of some other entity if( (strName == iten->GetName()) && ( &*iten != penOnlySelected) && (iten->GetParent() == NULL) ) { apenClones.Add( &*iten); } }} apenClones.Lock(); // remember placements of clones CStaticArray aplClones; INDEX ctEntities = apenClones.Count(); aplClones.New( ctEntities); {for( INDEX iEntity=0; iEntitym_bCutMode = TRUE; } } } void CreateCuttingWorld( FLOATplane3D &plPolygon, FLOATaabbox3D &box, CObject3D &o3d) { // ------------------- Create polygon that will be used for cutting FLOAT3D v0, v1, v2, v3; v0=v1=v2=v3= FLOAT3D( 0.0f, 0.0f, 0.0f); // find major axes of the polygon plane INDEX iMajorAxis1, iMajorAxis2; GetMajorAxesForPlane( plPolygon, iMajorAxis1, iMajorAxis2); FLOAT3D vCenter = box.Center(); FLOAT fSize = box.Size().Length(); v0(iMajorAxis1) = vCenter(iMajorAxis1)-fSize; v0(iMajorAxis2) = vCenter(iMajorAxis2)+fSize; v1(iMajorAxis1) = vCenter(iMajorAxis1)+fSize; v1(iMajorAxis2) = vCenter(iMajorAxis2)+fSize; v2(iMajorAxis1) = vCenter(iMajorAxis1)+fSize; v2(iMajorAxis2) = vCenter(iMajorAxis2)-fSize; v3(iMajorAxis1) = vCenter(iMajorAxis1)-fSize; v3(iMajorAxis2) = vCenter(iMajorAxis2)-fSize; // project coordinates back to plane FLOAT3D vp0, vp1, vp2, vp3; vp0 = plPolygon.ProjectPoint( v0); vp1 = plPolygon.ProjectPoint( v1); vp2 = plPolygon.ProjectPoint( v2); vp3 = plPolygon.ProjectPoint( v3); // add vertices CObjectSector *pos = o3d.ob_aoscSectors.New(1); pos->osc_aovxVertices.New(4); pos->osc_aovxVertices.Lock(); pos->osc_aovxVertices[0] = FLOATtoDOUBLE(vp0); pos->osc_aovxVertices[1] = FLOATtoDOUBLE(vp1); pos->osc_aovxVertices[2] = FLOATtoDOUBLE(vp2); pos->osc_aovxVertices[3] = FLOATtoDOUBLE(vp3); // add edges pos->osc_aoedEdges.New(4); pos->osc_aoedEdges.Lock(); pos->osc_aoedEdges[0].oed_Vertex0 = &pos->osc_aovxVertices[0]; pos->osc_aoedEdges[0].oed_Vertex1 = &pos->osc_aovxVertices[1]; pos->osc_aoedEdges[1].oed_Vertex0 = &pos->osc_aovxVertices[1]; pos->osc_aoedEdges[1].oed_Vertex1 = &pos->osc_aovxVertices[2]; pos->osc_aoedEdges[2].oed_Vertex0 = &pos->osc_aovxVertices[2]; pos->osc_aoedEdges[2].oed_Vertex1 = &pos->osc_aovxVertices[3]; pos->osc_aoedEdges[3].oed_Vertex0 = &pos->osc_aovxVertices[3]; pos->osc_aoedEdges[3].oed_Vertex1 = &pos->osc_aovxVertices[0]; // add plane CObjectPlane *popl = pos->osc_aoplPlanes.New(1); *popl = FLOATtoDOUBLE(plPolygon); // add material CObjectMaterial *pom = pos->osc_aomtMaterials.New(1); // set must-exist texture pom->omt_Name = "Textures\\Editor\\Default.tex"; // add polygon CObjectPolygon *pop = pos->osc_aopoPolygons.New(1); pop->opo_Plane = popl; pop->opo_Material = pom; pop->opo_PolygonEdges.New(4); pop->opo_PolygonEdges.Lock(); pop->opo_PolygonEdges[0].ope_Edge = &pos->osc_aoedEdges[0]; pop->opo_PolygonEdges[1].ope_Edge = &pos->osc_aoedEdges[1]; pop->opo_PolygonEdges[2].ope_Edge = &pos->osc_aoedEdges[2]; pop->opo_PolygonEdges[3].ope_Edge = &pos->osc_aoedEdges[3]; pop->opo_PolygonEdges.Unlock(); // unlock locked arrays pos->osc_aovxVertices.Unlock(); pos->osc_aoedEdges.Unlock(); } void CWorldEditorDoc::ApplyCut( void) { // find the view that defines cut plane POSITION posView = GetFirstViewPosition(); if( m_pCutLineView == NULL) { ASSERTALWAYS( "Cut line view wasn't set properly!"); // don't allow calling without cutting view ptr set return; } CWorldEditorView *pwedView; FOREVER { pwedView = (CWorldEditorView *) GetNextView(posView); // if we didn't find it in list of views if( pwedView == NULL) { // don't do anything return; } // if we found it if( pwedView == m_pCutLineView) { // stop looping break; } } // calculate cutting plane FLOAT3D &vcl0 = m_vCutLineStart; FLOAT3D &vcl1 = m_vCutLineEnd; // get cutting edge vector FLOAT3D vCutLine = vcl1-vcl0; // cross product with viewer direction vector will give us cutting plane normal // so calculate viewer direction vector CAnyProjection3D prProjection; // create a slave viewer CSlaveViewer svViewer( m_pCutLineView->GetChildFrame()->m_mvViewer, m_pCutLineView->m_ptProjectionType, m_plGrid, m_pCutLineView->m_pdpDrawPort); svViewer.MakeProjection(prProjection); prProjection->Prepare(); // make the ray from viewer point through the dummy point, in current projection CPlacement3D plRay; prProjection->RayThroughPoint( FLOAT3D(0.0f, 0.0f, 0.0f), plRay); // get viewer's direction vector FLOAT3D vDirection; AnglesToDirectionVector( plRay.pl_OrientationAngle, vDirection); // make cross product FLOAT3D vNormal = vCutLine * vDirection; // create plane from normal vector and one of cutting edge points FLOATplane3D plPolygon(vNormal, vcl0); // exit cut mode theApp.m_bCutModeOn = FALSE; // we need to obtain brush for CSG CBrush3D *pbrBrush = NULL; // if we are in polygon mode if( GetEditingMode() == POLYGON_MODE) { CBrushPolygon *pbpo = m_selPolygonSelection.GetFirstInSelection(); if( pbpo == NULL) { ASSERTALWAYS( "Apply cut called in polygon mode, but none polygon is selected."); return; } pbrBrush = pbpo->bpo_pbscSector->bsc_pbmBrushMip->bm_pbrBrush; } else if( GetEditingMode() == ENTITY_MODE) { // do nothing } else { // we must be in sector mode ASSERT( GetEditingMode() == SECTOR_MODE); CBrushSector *pbsc = m_selSectorSelection.GetFirstInSelection(); if( pbsc == NULL) { ASSERTALWAYS( "Apply cut called in sector mode, but none sector is selected."); return; } pbrBrush = pbsc->bsc_pbmBrushMip->bm_pbrBrush; } // brush containing polygon or sector will be our target entity CEntity *penTarget = NULL; if( pbrBrush != NULL) { penTarget=pbrBrush->br_penEntity; } RememberUndo(); // ------------------- Create cutting world // obtain child frame CChildFrame *pWedChild = pwedView->GetChildFrame(); // remember auto mip brushing flag BOOL bAutoMipBrushingOn = pWedChild->m_bAutoMipBrushingOn; // turn off auto mip brushing pWedChild->m_bAutoMipBrushingOn = FALSE; // create world cutter CWorld woCutter; // create zero-placement CPlacement3D plOrigin; plOrigin.pl_PositionVector = FLOAT3D(0.0f,0.0f,0.0f); plOrigin.pl_OrientationAngle = ANGLE3D(0,0,0); // create main brush entity CEntity *penCutter = NULL; try { penCutter = woCutter.CreateEntity_t( plOrigin, CTFILENAME("Classes\\WorldBase.ecl")); } catch( char *err_str) { AfxMessageBox( CString(err_str)); return; } // prepare the entity penCutter->Initialize(); if( GetEditingMode() == ENTITY_MODE) { } else { // ------------------- Create brush mip and sector CBrush3D *pbr = penCutter->GetBrush(); // brush must exist if( pbr == NULL) { ASSERTALWAYS( "Brush not properly initialized!"); return; } FLOATaabbox3D box; // obtain bounding box of selected polygons/sectors if( GetEditingMode() == POLYGON_MODE) { FOREACHINDYNAMICCONTAINER(m_selPolygonSelection, CBrushPolygon, itbpo) { box |= itbpo->bpo_boxBoundingBox; } } else if( GetEditingMode() == SECTOR_MODE) { FOREACHINDYNAMICCONTAINER(m_selSectorSelection, CBrushSector, itbsc) { box |= itbsc->bsc_boxBoundingBox; } } // create object3d to hold plane-cutter primitive CObject3D o3d; CreateCuttingWorld( plPolygon, box, o3d); // convert object 3d to brush try { pbr->FromObject3D_t( o3d); pbr->CalculateBoundingBoxes(); } // report errors catch( char *err_str) { AfxMessageBox( CString(err_str)); } // -------- Apply BSP cut operation if( GetEditingMode() == POLYGON_MODE) { // clear all selections except polygon seletion ClearSelections( ST_POLYGON); // apply "split polygons" m_woWorld.SplitPolygons(*penTarget, m_selPolygonSelection, woCutter, *penCutter, plOrigin); } else { // clear all selections except sector seletion ClearSelections( ST_SECTOR); // apply "split sectors" m_woWorld.SplitSectors(*penTarget, m_selSectorSelection, woCutter, *penCutter, plOrigin); } } // restore auto mip brushing pWedChild->m_bAutoMipBrushingOn = bAutoMipBrushingOn; m_chSelections.MarkChanged(); SetModifiedFlag(TRUE); m_chDocument.MarkChanged(); UpdateAllViews( NULL); } void CWorldEditorDoc::ReloadWorld(void) { // clear selections ClearSelections(); m_chDocument.MarkChanged(); // try to try { // load new world from the undo file m_woWorld.Load_t(m_woWorld.wo_fnmFileName); // flush stale caches _pShell->Execute("FreeUnusedStock();"); // invalidate document (i.e. all views) UpdateAllViews( NULL); } // report errors catch( char *err_str) { AfxMessageBox( CString(err_str)); } } void CWorldEditorDoc::OnCheckEdit(void) { CTFileName fnmFileName; ExpandFilePath(EFP_READ, m_woWorld.wo_fnmFileName, fnmFileName); CTString strCommand; strCommand.PrintF("p4 edit %s", fnmFileName); INDEX iResult = system(strCommand); if(iResult != 0) { WarningMessage( "Unable to perform open for edit!"); return; } ReloadWorld(); CTString strMessage; strMessage.PrintF("Opened for edit: %s", (const char *)m_woWorld.wo_fnmFileName); AfxMessageBox( CString(strMessage)); } void CWorldEditorDoc::OnCheckAdd() { CTFileName fnmFileName; ExpandFilePath(EFP_READ, m_woWorld.wo_fnmFileName, fnmFileName); CTString strCommand; strCommand.PrintF("p4 add %s", fnmFileName); INDEX iResult = system(strCommand); if(iResult != 0) { WarningMessage( "Unable to perform open for add!"); return; } ReloadWorld(); CTString strMessage; strMessage.PrintF( "Marked for add: %s", (const char *)m_woWorld.wo_fnmFileName); AfxMessageBox( CString(strMessage)); } void CWorldEditorDoc::OnCheckDelete() { CTFileName fnmFileName; ExpandFilePath(EFP_READ, m_woWorld.wo_fnmFileName, fnmFileName); CTString strCommand; strCommand.PrintF("p4 delete %s", fnmFileName); INDEX iResult = system(strCommand); if(iResult != 0) { WarningMessage( "Unable to perform open for delete!"); return; } ReloadWorld(); CTString strMessage; strMessage.PrintF( "Marked for delete: %s", (const char *)m_woWorld.wo_fnmFileName); AfxMessageBox( CString(strMessage)); } BOOL CWorldEditorDoc::IsReadOnly(void) { return IsFileReadOnly(m_woWorld.wo_fnmFileName); } void CWorldEditorDoc::OnUpdateCheckEdit(CCmdUI* pCmdUI) { pCmdUI->Enable(TRUE); } void CWorldEditorDoc::OnUpdateCheckAdd(CCmdUI* pCmdUI) { pCmdUI->Enable(TRUE); } void CWorldEditorDoc::OnUpdateCheckDelete(CCmdUI* pCmdUI) { pCmdUI->Enable(TRUE); } BOOL CWorldEditorDoc::IsBrushUpdatingAllowed(void) { // if only one entity is selected if( m_selEntitySelection.Count()==1) { // get only selected entity CEntity *pen = m_selEntitySelection.GetFirstInSelection(); // if it is brush entity if (pen->en_RenderType == CEntity::RT_BRUSH && pen->en_pbrBrush!=NULL) { // allow updating return TRUE; } } // disable updating return FALSE; } void CWorldEditorDoc::OnUpdateBrushes() { POSITION pos = GetFirstViewPosition(); CWorldEditorView *pWedView = (CWorldEditorView *) GetNextView(pos); ASSERT( pWedView != NULL); RememberUndo(); // get only selected entity CEntity *pen = m_selEntitySelection.GetFirstInSelection(); CTString strClone=pen->GetName(); FOREACHINDYNAMICCONTAINER(m_woWorld.wo_cenEntities, CEntity, iten) { if(iten!=pen && iten->GetName()==strClone && iten->en_RenderType==CEntity::RT_BRUSH) { iten->en_pbrBrush->Copy(*pen->en_pbrBrush, 1.0f, FALSE); pWedView->DiscardShadows( &*iten); } } UpdateAllViews( NULL); } void CWorldEditorDoc::OnInsert3dObject() { theApp.Insert3DObjects(this); } void CWorldEditorDoc::OnExport3dObject() { CTFileName fnName = _EngineGUI.FileRequester( "Export polygons as ...", "Raw 3D object\0*.raw\0" FILTER_ALL FILTER_END, "Export geometry directory", "Worlds\\"); if( fnName == "") return; try { CTFileStream strmFile; strmFile.Create_t( fnName); strmFile.PutLine_t("No name"); // for each of the selected polygons FOREACHINDYNAMICCONTAINER( m_selPolygonSelection, CBrushPolygon, itbpo) { CBrushPolygon &bpo = *itbpo; for( INDEX iVtx=0; iVtxEnable( m_selPolygonSelection.Count()!=0); } void CWorldEditorDoc::OnPopupVtxAllign() { CDlgAllignVertices dlg; dlg.DoModal(); } void CWorldEditorDoc::OnPopupVtxFilter() { CDlgFilterVertexSelection dlg; dlg.DoModal(); } void CWorldEditorDoc::OnPopupVtxNumeric() { CDlgSnapVertex dlg; dlg.DoModal(); } void CWorldEditorDoc::OnExportPlacements() { CStaticStackArray astrNeddedSmc; try { CTFileName fnWorld=m_woWorld.wo_fnmFileName; // "entity placement and names" CTFileName fnExport=fnWorld.FileDir()+fnWorld.FileName()+".epn"; // open text file CTFileStream strmFile; strmFile.Create_t( fnExport, CTStream::CM_TEXT); // for each entity in world FOREACHINDYNAMICCONTAINER(m_woWorld.wo_cenEntities, CEntity, iten) { CEntity &en=*iten; // obtain entity class ptr CDLLEntityClass *pdecDLLClass = en.GetClass()->ec_pdecDLLClass; // obtain position FLOAT3D vPos=en.GetPlacement().pl_PositionVector; FLOAT3D vRot=en.GetPlacement().pl_OrientationAngle; // dump class name and placement CTString strLine; CTString strName=en.GetName(); if(strName=="") { strName="Dummy name"; } strLine.PrintF("Class: \"%s\", Name: \"%s\", Position: (%f, %f, %f), Rotation: (%f, %f, %f)", pdecDLLClass->dec_strName, strName, vPos(1), vPos(2), vPos(3), vRot(1), vRot(2), vRot(3)); strmFile.PutLine_t(strLine); // if this is model holder 3 class, we should also dump model path if(CTString(pdecDLLClass->dec_strName)=="ModelHolder3") { CTFileName fnmFile=CTString("Unknown"); FLOAT3D vStretch=FLOAT3D(1.0f,1.0f,1.0f); // for all classes in hierarchy of this entity for(;pdecDLLClass!=NULL; pdecDLLClass = pdecDLLClass->dec_pdecBase) { // for all properties for(INDEX iProperty=0; iPropertydec_ctProperties; iProperty++) { CEntityProperty *pepProperty = &pdecDLLClass->dec_aepProperties[iProperty]; if( pepProperty->ep_eptType == CEntityProperty::EPT_FILENAME && CTString(pepProperty->ep_strName) == "Model file (.smc)") { // obtain file name fnmFile = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, CTFileName); BOOL bExistsInList=FALSE; for(INDEX iSmc=0; iSmcep_eptType == CEntityProperty::EPT_FLOAT && CTString(pepProperty->ep_strName) == "StretchAll") { FLOAT fStretchAll = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, FLOAT); vStretch(1)*=fStretchAll; vStretch(2)*=fStretchAll; vStretch(3)*=fStretchAll; } if( pepProperty->ep_eptType == CEntityProperty::EPT_ANGLE3D && CTString(pepProperty->ep_strName) == "StretchXYZ") { ANGLE3D vStretchXYZ = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, ANGLE3D); vStretch(1)*=vStretchXYZ(1); vStretch(2)*=vStretchXYZ(2); vStretch(3)*=vStretchXYZ(3); } } } CTString strLine; strLine.PrintF("Smc: \"%s\" Stretch: (%f, %f, %f)", CTString(fnmFile), vStretch(1), vStretch(2), vStretch(3)); strmFile.PutLine_t(strLine); } } // "entity placement and names" CTFileName fnSml=fnWorld.FileDir()+fnWorld.FileName()+".sml"; // open text file CTFileStream strmSmlFile; strmSmlFile.Create_t( fnSml, CTStream::CM_TEXT); // save needed smc's for(INDEX iSmc=0; iSmc=0) { return fnmFile.FileDir() + fnmFile.FileName() + "_NM.tex"; } return fnmFile; } CTString FixQuotes(const CTString &strOrg) { char achrFixed[1024]; INDEX iFixedChar = 0; for(INDEX iChar=0; iChar ang_cbpoVertices; public: void FromBrushPolygon(CBrushPolygon *pbpo); }; // Class used to represent polygon during brush to .amf exporting class CAmfPolygon { public: CStaticStackArray amfp_aangNgons; public: void FromBrushPolygon(CBrushPolygon *pbpo); }; // Creates polygon consisting of vertex loop from brush polygon void CAmfPolygon::FromBrushPolygon(CBrushPolygon *pbpo) { // make copy of the index array CStaticArray aiTriangles; aiTriangles.CopyArray( pbpo->bpo_aiTriangleElements); _nextNgon: // copy loop into n-gon CAmfNGon &aNgon = amfp_aangNgons.Push(); // find first triangle that is not handled INDEX iFirstNGonTriangle = -1; {for(INDEX iTri=0; iTribpo_apbvxTriangleVertices[aiTriangles[iFirstNGonTriangle*3+0]]); aiTriangles[iFirstNGonTriangle*3+0] = -1; aNgon.ang_cbpoVertices.Add( pbpo->bpo_apbvxTriangleVertices[aiTriangles[iFirstNGonTriangle*3+1]]); aiTriangles[iFirstNGonTriangle*3+1] = -1; aNgon.ang_cbpoVertices.Add( pbpo->bpo_apbvxTriangleVertices[aiTriangles[iFirstNGonTriangle*3+2]]); aiTriangles[iFirstNGonTriangle*3+2] = -1; // re-entry point for expanding loop _nextLoopEdge:; // for each loop's edge for(INDEX iLoopEdge=0; iLoopEdgebpo_apbvxTriangleVertices[iTriVtx0]; CBrushVertex *pbvEdg1 = pbpo->bpo_apbvxTriangleVertices[iTriVtx1]; // if this edge is the same as the loop edge if(pbvLoop0==pbvEdg1 && pbvLoop1==pbvEdg0) { // find index of vertex to insert (third vertex) INDEX iThirdVtxNo; if(iTriEdge==0) { iThirdVtxNo=2;} else if(iTriEdge==1) { iThirdVtxNo=0;} else { iThirdVtxNo=1;} INDEX iThirdVertex = aiTriangles[iTri*3+iThirdVtxNo]; // mark that triangle is integrated into the loop aiTriangles[iTri*3+0] = -1; aiTriangles[iTri*3+1] = -1; aiTriangles[iTri*3+2] = -1; CBrushVertex *pbvThird = pbpo->bpo_apbvxTriangleVertices[iThirdVertex]; aNgon.ang_cbpoVertices.Insert(pbvThird, iLoopEdge+1); goto _nextLoopEdge; } } } } // test if all triangles are cleared {for(INDEX iTri=0; iTri sf_cbpoPolygons; CAnimData *sf_padAnimData; // surface's texture UBYTE sf_ubMaterial; // surface's material public: CAmfSurface(void) { sf_padAnimData = NULL; }; // Calculates count of ngons INDEX GetNGonCount(void) { INDEX ctNgons = 0; for(INDEX iPlg=0; iPlgamfp_aangNgons.Count(); } return ctNgons; }; // Calculates count of ngon vertices INDEX GetNGonVertexCount(void) { INDEX ctVertices = 0; for(INDEX iPlg=0; iPlgamfp_aangNgons.Count(); iNgon++) { CAmfNGon &aNgon = amfp->amfp_aangNgons[iNgon]; ctVertices += aNgon.ang_cbpoVertices.Count(); } } return ctVertices; }; }; // Tests if given polygon is visible BOOL IsPolygonVisible(const CBrushPolygon &bpo) { // if is invisible if(bpo.bpo_ulFlags&BPOF_INVISIBLE) { return FALSE; } // if is portal if(bpo.bpo_ulFlags&BPOF_PORTAL) { // if is translucent portal if(bpo.bpo_ulFlags&BPOF_TRANSLUCENT) { // it is visible return TRUE; } return FALSE; } return TRUE; } // Exports one layer of given type void ExportLayer_t(CWorldEditorDoc *pDoc, CEntity &en, ExportType etExportType, CBrushMip *pbmMip, CTFileStream &strmAmf, const CString &strLayerName, INDEX iLayerNo, BOOL bFieldBrush, BOOL bCollisionOnlyBrush) { // sort brush polygons for their textures CDynamicContainer cbpoSurfaces; // assume that there will not be any portals nor occluders CStaticStackArray ciPortals; CStaticStackArray ciOccluders; CStaticStackArray ciClassifiers; // for each sector in the brush mip INDEX iPlgGlobal=0; {for(INDEX iSector=0; iSectorbm_abscSectors.Count(); iSector++) { CBrushSector &bs = pbmMip->bm_abscSectors[iSector]; // for each polygon in the sector for(INDEX iPlg=0; iPlgsf_cbpoPolygons.Add(&bpo); pSurf->sf_padAnimData = pad; pSurf->sf_ubMaterial = ubMaterial; } } else if(etExportType==ET_VISIBILITY) { BOOL bClassifier = FALSE; BOOL bOccluder = FALSE; BOOL bPortal = FALSE; // if this polygon is not involved in visibility if(bpo.bpo_ulFlags&BPOF_DETAILPOLYGON) { bClassifier = TRUE; } else if(bpo.bpo_ulFlags&BPOF_OCCLUDER) { bOccluder = TRUE; } else if(bpo.bpo_ulFlags&BPOF_PORTAL) { bPortal = TRUE; } // if surface is not yet defined if(cbpoSurfaces.Count()==0) { // add one CAmfSurface *pSurf = (CAmfSurface *) new(CAmfSurface); cbpoSurfaces.Add(pSurf); } CAmfPolygon *amfp = (CAmfPolygon *)bpo.bpo_pspoScreenPolygon; for(INDEX iNgon=0; iNgonamfp_aangNgons.Count(); iNgon++) { if(bClassifier) { ciClassifiers.Push() = iPlgGlobal; } if(bOccluder) { ciOccluders.Push() = iPlgGlobal; } if(bPortal) { ciPortals.Push() = iPlgGlobal; } iPlgGlobal++; } cbpoSurfaces[0].sf_cbpoPolygons.Add(&bpo); } } }} // count total surface polygons and vertices INDEX ctTotalPolygons = 0; INDEX ctTotalVertices = 0; {for(INDEX iSurf=0; iSurfamfp_aangNgons.Count(); iNgon++) { CAmfNGon &aNgon = amfp->amfp_aangNgons[iNgon]; for(INDEX iVtx=0; iVtxbpl_pldPreciseRelative); // calculate mapping transformation vectors CMappingDefinition &md = bpo.bpo_abptTextures[iTextureLayer].bpt_mdMapping; // if there is no texture MEX mexTexSizeU, mexTexSizeV; if(bpo.bpo_abptTextures[iTextureLayer].bpt_toTexture.GetData()==NULL) { mexTexSizeU = 1024; mexTexSizeV = 1024; } else { mexTexSizeU = bpo.bpo_abptTextures[iTextureLayer].bpt_toTexture.GetWidth(); mexTexSizeV = bpo.bpo_abptTextures[iTextureLayer].bpt_toTexture.GetHeight(); } const FLOAT fMulU = 1024.0f /mexTexSizeU; // (no need to do shift-opt, because it won't speed up much!) const FLOAT fMulV = 1024.0f /mexTexSizeV; CMappingVectors mvTransform; md.MakeMappingVectors(mvDefault, mvTransform); CAmfPolygon *amfp = (CAmfPolygon *)bpo.bpo_pspoScreenPolygon; for(INDEX iNgon=0; iNgonamfp_aangNgons.Count(); iNgon++) { CAmfNGon &aNgon = amfp->amfp_aangNgons[iNgon]; for(INDEX iVtx=0; iVtxamfp_aangNgons.Count(); iNgon++) { CAmfNGon &aNgon = amfp->amfp_aangNgons[iNgon]; INDEX ctPlgVertices = aNgon.ang_cbpoVertices.Count(); if(ctPlgVertices==0) { strmAmf.FPrintF_t(" { 3: 0, 0, 0; }\n"); } else { strmAmf.FPrintF_t(" { %d: ", ctPlgVertices); for(INDEX iVtx=0; iVtxm_woWorld.wo_astSurfaceTypes[asSurf.sf_ubMaterial].st_strName; strmAmf.FPrintF_t(" Material \"%s\";\n", strMaterial); // export first layer data CTFileName strPath; strPath = bpo.bpo_abptTextures[0].bpt_toTexture.GetName(); strPath = CorrectSlashes(strPath); strPath = RemapDetailTexturePath(strPath); if(bFieldBrush) { strmAmf.FPrintF_t(" \"base color\" Color %d;\n", C_GREEN|128); strmAmf.FPrintF_t(" \"blend type\" BlendType \"%translucent\";\n"); strmAmf.FPrintF_t(" \"double sided\" Bool \"TRUE\";\n"); } else { // setup blend type for translucent portals if( (bpo.bpo_ulFlags&BPOF_PORTAL) && (bpo.bpo_ulFlags&BPOF_TRANSLUCENT) ) { strmAmf.FPrintF_t(" \"blend type\" BlendType \"%translucent\";\n"); } //strPath.SetAbsolutePath(); //strPath.ReplaceSubstr("\\", "\\\\"); //strmAmf.FPrintF_t(" \"base texture\" Texture \"%s\";\n", strPath); //strmAmf.FPrintF_t(" \"base uvmap\" UVMap \"Texture 1\";\n"); strmAmf.FPrintF_t(" \"base color\" Color %d;\n", bpo.bpo_abptTextures[0].s.bpt_colColor); // export second layer data //strPath = bpo.bpo_abptTextures[1].bpt_toTexture.GetName(); //strPath = CorrectSlashes(strPath); //strPath = RemapDetailTexturePath(strPath); //strmAmf.FPrintF_t(" \"blend mask\" Texture \"%s\";\n", strPath); //strmAmf.FPrintF_t(" \"mask uvmap\" UVMap \"Texture 2\";\n"); // export third layer data //strPath = bpo.bpo_abptTextures[2].bpt_toTexture.GetName(); //strPath = CorrectSlashes(strPath); //strPath = RemapDetailTexturePath(strPath); //strmAmf.FPrintF_t(" \"detail normalmap\" Texture \"%s\";\n", strPath); //strmAmf.FPrintF_t(" \"detail uvmap\" UVMap \"Texture 3\";\n"); //strmAmf.FPrintF_t(" \"tangent uvmap\" UVMap \"Texture 3\";\n"); } strmAmf.PutLine_t(" }"); #else strmAmf.PutLine_t(" POLYGON_MAP_SHADER \"Bin/GameSamHD.module|Architecture\""); strmAmf.PutLine_t(" {"); // export material info CString strMaterial = pDoc->m_woWorld.wo_astSurfaceTypes[asSurf.sf_ubMaterial].st_strName; strmAmf.FPrintF_t(" Material \"%s\";\n", strMaterial); // export first layer data CTFileName strPath; strPath = bpo.bpo_abptTextures[0].bpt_toTexture.GetName(); strPath = CorrectSlashes(strPath); strPath = RemapDetailTexturePath(strPath); strmAmf.FPrintF_t(" \"diffuse 1 texture\" Texture \"%s\";\n", strPath); strmAmf.FPrintF_t(" \"diffuse 1 uvmap\" UVMap \"Texture 1\";\n"); // export second layer data strPath = bpo.bpo_abptTextures[1].bpt_toTexture.GetName(); strPath = CorrectSlashes(strPath); strPath = RemapDetailTexturePath(strPath); strmAmf.FPrintF_t(" \"shade\" Texture \"%s\";\n", strPath); strmAmf.FPrintF_t(" \"shade uvmap\" UVMap \"Texture 2\";\n"); // export third layer data strPath = bpo.bpo_abptTextures[2].bpt_toTexture.GetName(); strPath = CorrectSlashes(strPath); strPath = RemapDetailTexturePath(strPath); strmAmf.FPrintF_t(" \"normal map 1\" Texture \"%s\";\n", strPath); strmAmf.FPrintF_t(" \"normal 1 uvmap\" UVMap \"Texture 3\";\n"); strmAmf.FPrintF_t(" \"tangent uvmap\" UVMap \"Texture 3\";\n"); strmAmf.PutLine_t(" }"); #endif strmAmf.PutLine_t(" POLYGON_MAP_SMOOTHING_ANGLE 30"); INDEX ctSurfacePolygons = asSurf.GetNGonCount(); strmAmf.FPrintF_t(" POLYGONS_COUNT %d\n", ctSurfacePolygons); strmAmf.PutLine_t(" {"); for(INDEX iPlg=0; iPlgbm_abscSectors.Count(); INDEX iOccluderPolyMaps = ciOccluders.Count()>0 ? 1 : 0; INDEX iPortalPolyMaps = ciPortals.Count()>0 ? 1 : 0; INDEX iClassifierPolyMaps = ciClassifiers.Count()>0 ? 1 : 0; strmAmf.FPrintF_t(" POLYGON_MAPS %d\n", ctSectors+iOccluderPolyMaps+iPortalPolyMaps+iClassifierPolyMaps); strmAmf.PutLine_t(" {"); // dump sectors as separate polygon maps {for(INDEX iSector=0; iSectorbm_abscSectors.Count(); iSector++) { // count sector polygons INDEX ctSectorPolygons = 0; CBrushSector *pbs = &pbmMip->bm_abscSectors[iSector]; {for(INDEX iSurf=0; iSurfamfp_aangNgons.Count(); ctSectorPolygons += ctNgons; } } }} strmAmf.FPrintF_t(" POLYGON_MAP_NAME \"sector.Sector_%d\"\n", iSector); strmAmf.FPrintF_t(" POLYGONS_COUNT %d\n", ctSectorPolygons); strmAmf.PutLine_t(" {"); // dump polygon indices INDEX iPlgGlobal = 0; {for(INDEX iSurf=0; iSurfamfp_aangNgons.Count(); iNgon++) { if(bpo.bpo_pbscSector==pbs) { strmAmf.FPrintF_t(" %d;\n", iPlgGlobal); } iPlgGlobal++; } } }} strmAmf.PutLine_t(" }"); }} // dump portals if(ciPortals.Count()>0) { strmAmf.PutLine_t(" POLYGON_MAP_NAME \"portal.VisPortal\""); strmAmf.FPrintF_t(" POLYGONS_COUNT %d\n", ciPortals.Count()); strmAmf.PutLine_t(" {"); // dump polygon indices {for(INDEX iPlg=0; iPlg0) { strmAmf.PutLine_t(" POLYGON_MAP_NAME \"portal.VisOccluder\""); strmAmf.FPrintF_t(" POLYGONS_COUNT %d\n", ciOccluders.Count()); strmAmf.PutLine_t(" {"); // dump polygon indices {for(INDEX iPlg=0; iPlg0) { strmAmf.PutLine_t(" POLYGON_MAP_NAME \"portal.VisClassifier\""); strmAmf.FPrintF_t(" POLYGONS_COUNT %d\n", ciClassifiers.Count()); strmAmf.PutLine_t(" {"); // dump polygon indices {for(INDEX iPlg=0; iPlgGetFirstMip(); if(pbmMip==NULL) { return FALSE; } INDEX ctPolygons = 0; for(INDEX iSector=0; iSectorbm_abscSectors.Count(); iSector++) { CBrushSector &bs = pbmMip->bm_abscSectors[iSector]; // for each polygon in the sector for(INDEX iPlg=0; iPlg0; } // Tests if entity brush is empty BOOL IsBrushEmpty(CEntity &en) { // fetch first mip CBrushMip *pbmMip = en.en_pbrBrush->GetFirstMip(); if(pbmMip==NULL) { return FALSE; } INDEX ctPolygons = 0; for(INDEX iSector=0; iSectorbm_abscSectors.Count(); iSector++) { CBrushSector &bs = pbmMip->bm_abscSectors[iSector]; if(bs.bsc_abpoPolygons.Count()>0) { return FALSE; } } return TRUE; } // Exports given brush mip into .amf format void ExportEntityToAMF_t(CWorldEditorDoc *pDoc, CEntity &en, const CTFileName &fnAmf, BOOL bFieldBrush, BOOL bInvisibleBrush, BOOL bEmptyBrush) { // fetch first mip CBrushMip *pbmMip = en.en_pbrBrush->GetFirstMip(); // convert all of the brush polygons into ngons // for each sector in the brush mip {for(INDEX iSector=0; iSectorbm_abscSectors.Count(); iSector++) { CBrushSector &bs = pbmMip->bm_abscSectors[iSector]; // for each polygon in the sector for(INDEX iPlg=0; iPlgFromBrushPolygon(&bpo); bpo.bpo_pspoScreenPolygon = (CScreenPolygon *) pap; } }} try { // open .amf file CTFileStream strmAmf; strmAmf.Create_t( fnAmf, CTStream::CM_TEXT); strmAmf.PutLine_t("SE_MESH 1.01"); strmAmf.PutLine_t(""); // export visibility for zoning brushes INDEX ctLayers = en.en_ulFlags&ENF_ZONING ? 2 : 1; if(bEmptyBrush) { ctLayers = 0; } strmAmf.FPrintF_t("LAYERS %d\n", ctLayers); strmAmf.PutLine_t("{"); if(bInvisibleBrush) { ExportLayer_t(pDoc, en, ET_RENDERING, pbmMip, strmAmf, "Collision", 0, bFieldBrush, TRUE); } else { ExportLayer_t(pDoc, en, ET_RENDERING, pbmMip, strmAmf, "Rendering", 0, bFieldBrush, FALSE); } if(ctLayers>1) { ExportLayer_t(pDoc, en, ET_VISIBILITY, pbmMip, strmAmf, "Visibility", 1, bFieldBrush, FALSE); } strmAmf.PutLine_t("}"); } catch( char *err_str) { AfxMessageBox( CString(err_str)); } } void CWorldEditorDoc::OnExportEntities() { CStaticStackArray astrNeddedSmc; try { CTFileName fnWorld=m_woWorld.wo_fnmFileName; // "entity placement and names" CTFileName fnExport=fnWorld.FileDir()+fnWorld.FileName()+".awf"; // open text file CTFileStream strmFile; strmFile.Create_t( fnExport, CTStream::CM_TEXT); // prepare container of entities to export CDynamicContainer dcEntitiesToExport; // for each entity in world {FOREACHINDYNAMICCONTAINER(m_woWorld.wo_cenEntities, CEntity, iten) { CEntity &en=*iten; dcEntitiesToExport.Add(&en); }} // write count of entities CTString strLine; strLine.PrintF("ENTITIES %d {", dcEntitiesToExport.Count()); strmFile.PutLine_t(strLine); // for each entity in world FOREACHINDYNAMICCONTAINER(dcEntitiesToExport, CEntity, iten) { CEntity &en=*iten; // obtain entity class ptr CDLLEntityClass *pdecDLLClass = en.GetClass()->ec_pdecDLLClass; // obtain position FLOAT3D vPos=en.GetPlacement().pl_PositionVector; FLOAT3D vRot=en.GetPlacement().pl_OrientationAngle; // count entity attributes INDEX ctEntityAttributes = 0; // for all classes in hierarchy of this entity CDLLEntityClass *pdecDLLClassCount = pdecDLLClass; for(;pdecDLLClassCount!=NULL; pdecDLLClassCount = pdecDLLClassCount->dec_pdecBase) { // for all properties for(INDEX iProperty=0; iPropertydec_ctProperties; iProperty++) { CEntityProperty *pepProperty = &pdecDLLClassCount->dec_aepProperties[iProperty]; if(pepProperty->ep_strName!=CTString("")) { ctEntityAttributes++; } } } // if render type is brush if( (en.en_RenderType==CEntity::RT_BRUSH || en.en_RenderType==CEntity::RT_FIELDBRUSH) && en.en_pbrBrush!=NULL) { // add one more property because we will add one that will hint "InvisibleBrush" ctEntityAttributes++; } // write count of entity attributes (add 5 fixed ones, for class, ID, spawn flags, parent, name, pos, rot) strLine.PrintF(" ENTITY_ATTRIBUTES %d {", ctEntityAttributes+7); strmFile.PutLine_t(strLine); // entity class strLine.PrintF(" \"ENTITYCLASS\" = string(\"SS1 %s\");", pdecDLLClass->dec_strName); strmFile.PutLine_t(strLine); // entity ID strLine.PrintF(" \"ID\" = long(%d);", en.en_ulID); strmFile.PutLine_t(strLine); // entity spawn flags strLine.PrintF(" \"SS1_SPAWN_FLAGS\" = long(%d);", en.en_ulSpawnFlags); strmFile.PutLine_t(strLine); // entity name CTString strName=en.GetName(); if(strName=="") { strName=""; } SLONG idParent=-1; CEntity *penParent = en.GetParent(); if(penParent!=NULL) { idParent = penParent->en_ulID; } strLine.PrintF(" \"PARENT\" = long(%d);", idParent); strmFile.PutLine_t(strLine); strLine.PrintF(" \"NAME\" = string(\"%s\");", strName); strmFile.PutLine_t(strLine); // position strLine.PrintF(" \"POS\" = float3(%f, %f, %f);", vPos(1), vPos(2), vPos(3)); strmFile.PutLine_t(strLine); // rotation strLine.PrintF(" \"ROT\" = float3(%f, %f, %f);", vRot(1), vRot(2), vRot(3)); strmFile.PutLine_t(strLine); // for all classes in hierarchy of this entity for(;pdecDLLClass!=NULL; pdecDLLClass = pdecDLLClass->dec_pdecBase) { // for all properties for(INDEX iProperty=0; iPropertydec_ctProperties; iProperty++) { CEntityProperty *pepProperty = &pdecDLLClass->dec_aepProperties[iProperty]; if(pepProperty->ep_strName==CTString("")) { continue; } // enumerator if( pepProperty->ep_eptType == CEntityProperty::EPT_ENUM) { INDEX iEnumValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, INDEX); strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, iEnumValue); strmFile.PutLine_t(strLine); } // boolean if( pepProperty->ep_eptType == CEntityProperty::EPT_BOOL) { INDEX iBooleanValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, BOOL); strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, iBooleanValue); strmFile.PutLine_t(strLine); } // float value if( pepProperty->ep_eptType == CEntityProperty::EPT_FLOAT) { FLOAT fFloat = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, FLOAT); strLine.PrintF(" \"%s\" = float(%f);", pepProperty->ep_strName, fFloat); strmFile.PutLine_t(strLine); } // color if( pepProperty->ep_eptType == CEntityProperty::EPT_COLOR) { COLOR colValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, COLOR); strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, colValue); strmFile.PutLine_t(strLine); } // string if( pepProperty->ep_eptType == CEntityProperty::EPT_STRING) { CTString strString = FixQuotes(ENTITYPROPERTY( &en, pepProperty->ep_slOffset, CTString)); strLine.PrintF(" \"%s\" = string(\"%s\");", pepProperty->ep_strName, strString); strmFile.PutLine_t(strLine); } // range if( pepProperty->ep_eptType == CEntityProperty::EPT_RANGE) { FLOAT fFloat = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, FLOAT); strLine.PrintF(" \"%s\" = float(%f);", pepProperty->ep_strName, fFloat); strmFile.PutLine_t(strLine); } // entity ptr if( pepProperty->ep_eptType == CEntityProperty::EPT_ENTITYPTR) { // get the pointer CEntityPointer &penPointed = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, CEntityPointer); SLONG ulID = penPointed==NULL ? -1 : penPointed->en_ulID; strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, ulID); strmFile.PutLine_t(strLine); } // file name if( pepProperty->ep_eptType == CEntityProperty::EPT_FILENAME || pepProperty->ep_eptType == CEntityProperty::EPT_FILENAMENODEP) { CTFileName fnmFile = CorrectSlashes(ENTITYPROPERTY( &en, pepProperty->ep_slOffset, CTFileName)); strLine.PrintF(" \"%s\" = string(\"%s\");", pepProperty->ep_strName, fnmFile); strmFile.PutLine_t(strLine); } // index value if( pepProperty->ep_eptType == CEntityProperty::EPT_INDEX) { INDEX iValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, INDEX); strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, iValue); strmFile.PutLine_t(strLine); } // animation value if( pepProperty->ep_eptType == CEntityProperty::EPT_ANIMATION) { INDEX iValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, INDEX); strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, iValue); strmFile.PutLine_t(strLine); } // illumination type if( pepProperty->ep_eptType == CEntityProperty::EPT_ILLUMINATIONTYPE) { INDEX iValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, INDEX); strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, iValue); strmFile.PutLine_t(strLine); } // angle if( pepProperty->ep_eptType == CEntityProperty::EPT_ANGLE) { INDEX iValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, INDEX); strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, iValue); strmFile.PutLine_t(strLine); } // float 3D if( pepProperty->ep_eptType == CEntityProperty::EPT_FLOAT3D) { FLOAT3D vValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, FLOAT3D); strLine.PrintF(" \"%s\" = float3(%f, %f, %f);", pepProperty->ep_strName, vValue(1), vValue(2), vValue(3)); strmFile.PutLine_t(strLine); } // angle 3D if( pepProperty->ep_eptType == CEntityProperty::EPT_ANGLE3D) { ANGLE3D vAngle3D = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, ANGLE3D); strLine.PrintF(" \"%s\" = float3(%f, %f, %f);", pepProperty->ep_strName, vAngle3D(1), vAngle3D(2), vAngle3D(3)); strmFile.PutLine_t(strLine); } // string trans if( pepProperty->ep_eptType == CEntityProperty::EPT_STRINGTRANS) { CTString strString = FixQuotes(ENTITYPROPERTY( &en, pepProperty->ep_slOffset, CTString)); strLine.PrintF(" \"%s\" = string(\"%s\");", pepProperty->ep_strName, strString); strmFile.PutLine_t(strLine); } // flags if( pepProperty->ep_eptType == CEntityProperty::EPT_FLAGS) { ULONG ulValue = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, ULONG); strLine.PrintF(" \"%s\" = long(%d);", pepProperty->ep_strName, ulValue); strmFile.PutLine_t(strLine); } // EPT_FLOATAABBOX3D - bounding box if( pepProperty->ep_eptType == CEntityProperty::EPT_FLOATAABBOX3D) { // get value for bounding box FLOATaabbox3D bboxOld = ENTITYPROPERTY( &en, pepProperty->ep_slOffset, FLOATaabbox3D); FLOAT3D vMin = bboxOld.Min(); FLOAT3D vMax = bboxOld.Max(); strLine.PrintF(" \"%s\" = box(%f, %f, %f),(%f, %f, %f);", pepProperty->ep_strName, vMin(1), vMin(2), vMin(3), vMax(1), vMax(2), vMax(3)); strmFile.PutLine_t(strLine); } } } // if render type is brush if( (en.en_RenderType==CEntity::RT_BRUSH || en.en_RenderType==CEntity::RT_FIELDBRUSH) && en.en_pbrBrush!=NULL) { // add one "fake" property that will hint "invisible brush" status BOOL bInvisibleBrush = !IsBrushVisible(en); BOOL bEmptyBrush = IsBrushEmpty(en); strLine.PrintF(" \"Hint: Invisible brush\" = long(%d);", bInvisibleBrush); strmFile.PutLine_t(strLine); // ".amf" file name CTString strEntityID; strEntityID.PrintF("%d", en.en_ulID); CTFileName fnAmf; fnAmf.PrintF("%s_%s.amf", fnWorld.FileDir()+fnWorld.FileName(), strEntityID); BOOL bFieldBrush = en.en_RenderType==CEntity::RT_FIELDBRUSH; ExportEntityToAMF_t(this, en, fnAmf, bFieldBrush, bInvisibleBrush, bEmptyBrush); } // close entity attributes section strLine.PrintF(" }"); strmFile.PutLine_t(strLine); } // close entity section strLine.PrintF("}"); strmFile.PutLine_t(strLine); // "entity placement and names" CTFileName fnSml=fnWorld.FileDir()+fnWorld.FileName()+".sml"; // open text file CTFileStream strmSmlFile; strmSmlFile.Create_t( fnSml, CTStream::CM_TEXT); // save needed smc's for(INDEX iSmc=0; iSmc