Serious-Engine/Sources/WorldEditor/WorldEditorDoc.cpp

5668 lines
179 KiB
C++
Raw Normal View History

2016-03-12 01:20:51 +01:00
/* 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. */
2016-03-11 14:57:17 +01:00
// WorldEditorDoc.cpp : implementation of the CWorldEditorDoc class
//
#include "stdafx.h"
#include "WorldEditor.h"
#include "WorldEditorDoc.h"
#include <Engine/Base/Profiling.h>
#include <Engine/Build.h>
#include <direct.h>
#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<CEntity> 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; iCopy<vtxCt; iCopy++)
{
avVtxCopy[iCopy] = avVtx[iCopy];
}
// displace all vertices
if( pII != NULL)
{
for( INDEX iVtx=0; iVtx<vtxCt; iVtx++)
{
if( Abs(avVtxCopy[iVtx](2)-fRaiseHeight)<HEIGHT_EPSILON)
{
DisplaceVertex( avVtxCopy[iVtx], pII, fMinX, fMaxX, fMinZ, fMaxZ,
iSlicesPerW, iSlicesPerL, fAmplitude);
}
}
}
/*
// report it
_RPT1(_CRT_WARN, "\n%d:", vtxCt);
for(INDEX ivx=0; ivx<vtxCt; ivx++) {
// report it
_RPT3(_CRT_WARN, " (%f, %f, %f)",
avVtx[ivx](1),
avVtx[ivx](2),
avVtx[ivx](3));
}
*/
switch( theApp.m_vfpCurrent.vfp_ttTriangularisationType)
{
case TT_NONE:
{
// create polygon
CObjectPolygon *pObjectPolygon = _poscSector->CreatePolygon(
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( ; iVtx<vtxCt; iVtx++)
{
vCenter+=avVtxCopy[iVtx];
}
vCenter /= vtxCt;
// create polygons
DOUBLE3D avPolygon[ 3];
for( iVtx=0; iVtx<vtxCt; iVtx++)
{
INDEX iNextVtx = (iVtx+1)%vtxCt;
avPolygon[ 0] = avVtxCopy[iVtx];
avPolygon[ 1] = avVtxCopy[iNextVtx];
avPolygon[ 2] = vCenter;
CObjectPolygon *pObjectPolygon = _poscSector->CreatePolygon(
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; iVtx<iStartVtx+vtxCt-2; iVtx++)
{
INDEX iNextVtx = (iVtx+1)%vtxCt;
INDEX iNextNextVtx = (iNextVtx+1)%vtxCt;
avPolygon[ 0] = avVtxCopy[iStartVtx];
avPolygon[ 1] = avVtxCopy[iNextVtx];
avPolygon[ 2] = avVtxCopy[iNextNextVtx];
CObjectPolygon *pObjectPolygon = _poscSector->CreatePolygon(
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; iVtxBottom<vtxCt; iVtxBottom++)
{
avBottomPolygon[ iVtxBottom] = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[ iVtxBottom];
avBottomPolygon[ iVtxBottom](2) = theApp.m_vfpCurrent.vfp_fYMin;
}
AddPolygon( vtxCt, avBottomPolygon, _bClosed);
// top polygon
DOUBLE3D *avTopPolygon = new DOUBLE3D[ vtxCt];
for(INDEX iVtxTop=0; iVtxTop<vtxCt; iVtxTop++)
{
avTopPolygon[ iVtxTop] = DOUBLE3D(
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[ iVtxTop](1) * fStretchX + dx,
theApp.m_vfpCurrent.vfp_fYMax,
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[ iVtxTop](3) * fStretchY + dz);
}
if( (fStretchX != 0.0f) && (fStretchY != 0.0f) )
{
AddPolygon( vtxCt, avTopPolygon, !_bClosed);
}
// side polygons
DOUBLE3D *avSidePolygon = new DOUBLE3D[ 4];
for( INDEX iBaseVtx=0; iBaseVtx<vtxCt; iBaseVtx++)
{
INDEX iNextVtx = (iBaseVtx+1) % vtxCt;
avSidePolygon[ 0] = avBottomPolygon[ iBaseVtx];
avSidePolygon[ 0](2) = theApp.m_vfpCurrent.vfp_fYMin;
avSidePolygon[ 1] = avTopPolygon[ iBaseVtx];
avSidePolygon[ 1](2) = theApp.m_vfpCurrent.vfp_fYMax;
avSidePolygon[ 2] = avTopPolygon[ iNextVtx];
avSidePolygon[ 2](2) = theApp.m_vfpCurrent.vfp_fYMax;
avSidePolygon[ 3] = avBottomPolygon[ iNextVtx];
avSidePolygon[ 3](2) = theApp.m_vfpCurrent.vfp_fYMin;
// get lenght of base edge
DOUBLE fEdgeLenght =
(theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[ 1] -
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[ 0]).Length();
// calculate how many times we wrapped (tiled) one texture
INDEX iWrappedTimes = INDEX( (iBaseVtx * fEdgeLenght) / _fTextureWidth);
// prepare vector of mapping translation (allign to left)
DOUBLE3D f3dMappingTranslation = avSidePolygon[ 0];
// add primitive-texture height difference to start from top
f3dMappingTranslation -= DOUBLE3D( 0.0f, fHeight-_fTextureHeight, 0.0f);
// calculate last edge's mapping remainings for "continous" mapping
DOUBLE fMappingRemaining = iBaseVtx*fEdgeLenght - iWrappedTimes*_fTextureWidth;
// calculate edge vector going from end toward start vertice of base edge
DOUBLE3D f3dEdge = avSidePolygon[ 3] - avSidePolygon[ 0];
// normalize it
f3dEdge.Normalize();
// multiply it with mapping remaining value
f3dEdge *= fMappingRemaining;
// add its influence into mapping translation vector
f3dMappingTranslation += f3dEdge;
// create polygons on side of conus
AddPolygon( 4, avSidePolygon, _bClosed, f3dMappingTranslation);
}
delete avBottomPolygon;
delete avTopPolygon;
delete avSidePolygon;
theApp.m_vfpCurrent.vfp_o3dPrimitive.Optimize();
ConvertObject3DToBrush(theApp.m_vfpCurrent.vfp_o3dPrimitive);
}
void CWorldEditorDoc::CreateTorusPrimitive(void)
{
/*
// report it
_RPT0(_CRT_WARN, "\nTorus\n");
*/
// get count of vertices that will be used for creating base polygon
INDEX vtxCt = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.Count();
// get torus parameters
INDEX iSlicesIn360 = theApp.m_vfpCurrent.vfp_iSlicesIn360;
INDEX iNoOfSlices = theApp.m_vfpCurrent.vfp_iNoOfSlices;
FLOAT fHeight = theApp.m_vfpCurrent.vfp_fYMax-theApp.m_vfpCurrent.vfp_fYMin;
// clamp no of slices
if(iNoOfSlices<1) iNoOfSlices=1;
//if(iNoOfSlices>iSlicesIn360) 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( ; iSlice<iNoOfSlices+1; iSlice++)
{
// create rotation matrix
ANGLE3D angSlice = ANGLE3D( 0, 0, AngleDeg( (360.0f/iSlicesIn360)*iSlice));
if( theApp.m_vfpCurrent.vfp_fRadius>0.0f)
{
angSlice(3) = -angSlice(3);
}
DOUBLEmatrix3D matrixRot;
matrixRot ^= angSlice;
papvBases[iSlice] = new DOUBLE3D[vtxCt];
// calculate vertex coordinates for each slice
for(INDEX iBaseVtx=0; iBaseVtx<vtxCt; iBaseVtx++)
{
// create vector from center to rotating vertex
DOUBLE3D vCT = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[ iBaseVtx]-vCenter;
papvBases[iSlice][iBaseVtx] = vCT*matrixRot+vCenter;
papvBases[iSlice][iBaseVtx](3) += iSlice*fHeight;
}
}
if(iNoOfSlices!=iSlicesIn360 || fHeight!=0)
{
// create torus starting polygon
AddPolygon( vtxCt, papvBases[0], bInvert);
// create torus ending polygon
AddPolygon( vtxCt, papvBases[iNoOfSlices], !bInvert);
}
DOUBLE3D avPolygonVertices[4];
// create polygons on sides
for(iSlice=0; iSlice<iNoOfSlices; iSlice++)
{
INDEX iNextSlice = (iSlice+1)%(iNoOfSlices+1);
for(INDEX iVtx=0; iVtx<vtxCt; iVtx++)
{
INDEX iNextVtx = (iVtx+1)%vtxCt;
avPolygonVertices[0] = papvBases[iSlice][iVtx];
avPolygonVertices[1] = papvBases[iSlice][iNextVtx];
avPolygonVertices[2] = papvBases[iNextSlice][iNextVtx];
avPolygonVertices[3] = papvBases[iNextSlice][iVtx];
AddPolygon( 4, avPolygonVertices, !bInvert);
}
}
// free allocated arrays
for( INDEX iFree=0; iFree<iNoOfSlices+1; iFree++)
{
delete papvBases[iFree];
}
delete papvBases;
theApp.m_vfpCurrent.vfp_o3dPrimitive.Optimize();
ConvertObject3DToBrush(theApp.m_vfpCurrent.vfp_o3dPrimitive);
}
void CWorldEditorDoc::CreateStaircasesPrimitive(void)
{
/*
// report it
_RPT0(_CRT_WARN, "\nStaircases\n");
*/
// calculate height
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;
// get parameters for staircases
INDEX iStairsIn360 = theApp.m_vfpCurrent.vfp_iSlicesIn360;
INDEX iNoOfStairs = theApp.m_vfpCurrent.vfp_iNoOfSlices;
DOUBLE fRadius = theApp.m_vfpCurrent.vfp_fRadius;
DOUBLE angle = 360.0/iStairsIn360;
DOUBLE angleAdd = 0.0;
// if base is created inside circle
if( theApp.m_vfpCurrent.vfp_bOuter && !theApp.m_vfpCurrent.vfp_bLinearStaircases)
{
fRadius = fRadius/Cos(FLOAT(angle/2));
angleAdd = -angle/2.0;
fWidth = fWidth/Cos(FLOAT(angle/2));
}
BOOL bTopSlope = theApp.m_vfpCurrent.vfp_iTopShape == 1;
BOOL bTopCeiling = theApp.m_vfpCurrent.vfp_iTopShape == 2;
BOOL bBottomSlope = theApp.m_vfpCurrent.vfp_iBottomShape == 1;
BOOL bBottomFloor = theApp.m_vfpCurrent.vfp_iBottomShape == 2;
DOUBLE3D **papvBases = new DOUBLE3D *[iNoOfStairs];
DOUBLE3D vCenter = DOUBLE3D( 0.0f, 0.0f, 0.0f);
BOOL bInvert = _bClosed;
// rotate base vertices to calculate rotating staircases
INDEX iStair=0;
for( ; iStair<iNoOfStairs; iStair++)
{
// create rotation matrix
ANGLE3D angRotation1;
ANGLE3D angRotation2;
if( fRadius>0.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; iStair<iNoOfStairs; iStair++)
{
// add only first front polygon if rest are eaten by stairs material
BOOL bAddFrontVerticalPolygon = TRUE;
if( (bTopSlope || bTopCeiling) && (iStair != 0) )
bAddFrontVerticalPolygon = FALSE;
// add only last back polygon if rest are eaten by stairs material
BOOL bAddBackVerticalPolygon = TRUE;
if( (bBottomSlope || bBottomFloor) && (iStair != (iNoOfStairs-1)) )
bAddBackVerticalPolygon = FALSE;
avVtx[0] = papvBases[iStair][0];
avVtx[1] = papvBases[iStair][1];
avVtx[2] = papvBases[iStair][2];
avVtx[3] = papvBases[iStair][3];
avVtx[4] = papvBases[iStair][0];
avVtx[4](2) += fHeight;
avVtx[5] = papvBases[iStair][1];
avVtx[5](2) += fHeight;
avVtx[6] = papvBases[iStair][2];
avVtx[6](2) += fHeight;
avVtx[7] = papvBases[iStair][3];
avVtx[7](2) += fHeight;
// if bottom shape is slope
if( bBottomSlope)
{
avVtx[1](2) += fHeight;
avVtx[2](2) += fHeight;
}
// if top shape is slope
if( bTopSlope)
{
avVtx[5](2) += fHeight;
avVtx[6](2) += fHeight;
}
DOUBLE3D avPolygon[4];
avPolygon[0] = avVtx[0];
avPolygon[1] = avVtx[1];
avPolygon[2] = avVtx[5];
avPolygon[3] = avVtx[4];
// if bottom shape is floor
if( bBottomFloor)
{
avPolygon[0](2) = 0.0;
avPolygon[1](2) = 0.0;
}
// if top shape is ceiling
if( bTopCeiling)
{
avPolygon[2](2) = fHeight*iNoOfStairs;
avPolygon[3](2) = fHeight*iNoOfStairs;
}
AddPolygon( 4, avPolygon, !bInvert);
avPolygon[0] = avVtx[3];
avPolygon[1] = avVtx[2];
avPolygon[2] = avVtx[6];
avPolygon[3] = avVtx[7];
// if bottom shape is floor
if( bBottomFloor)
{
avPolygon[0](2) = 0.0;
avPolygon[1](2) = 0.0;
}
// if top shape is ceiling
if( bTopCeiling)
{
avPolygon[2](2) = fHeight*iNoOfStairs;
avPolygon[3](2) = fHeight*iNoOfStairs;
}
AddPolygon( 4, avPolygon, bInvert);
if( bAddFrontVerticalPolygon)
{
avPolygon[0] = avVtx[0];
avPolygon[1] = avVtx[4];
avPolygon[2] = avVtx[7];
avPolygon[3] = avVtx[3];
// if top shape is ceiling
if( bTopCeiling)
{
avPolygon[1](2) = fHeight*iNoOfStairs;
avPolygon[2](2) = fHeight*iNoOfStairs;
}
AddPolygon( 4, avPolygon, !bInvert);
}
// if bottom shape is slope, top is stairs, don't create polygon because its area is 0
if( !(bBottomSlope && !bTopSlope) && bAddBackVerticalPolygon)
{
avPolygon[0] = avVtx[1];
avPolygon[1] = avVtx[5];
avPolygon[2] = avVtx[6];
avPolygon[3] = avVtx[2];
if( bBottomFloor)
{
avPolygon[0](2) = 0.0;
avPolygon[3](2) = 0.0;
}
AddPolygon( 4, avPolygon, bInvert);
}
// if top shape is slope
if( bTopSlope == 1)
{
avPolygon[0] = avVtx[4];
avPolygon[1] = avVtx[6];
avPolygon[2] = avVtx[7];
AddPolygon( 3, avPolygon, !bInvert);
avPolygon[0] = avVtx[4];
avPolygon[1] = avVtx[5];
avPolygon[2] = avVtx[6];
AddPolygon( 3, avPolygon, !bInvert);
}
else
{
avPolygon[0] = avVtx[4];
avPolygon[1] = avVtx[5];
avPolygon[2] = avVtx[6];
avPolygon[3] = avVtx[7];
// if top shape is ceiling
if( bTopCeiling)
{
avPolygon[0](2) = fHeight*iNoOfStairs;
avPolygon[1](2) = fHeight*iNoOfStairs;
avPolygon[2](2) = fHeight*iNoOfStairs;
avPolygon[3](2) = fHeight*iNoOfStairs;
}
AddPolygon( 4, avPolygon, !bInvert);
}
// if bottom shape is slope
if( bBottomSlope == 1)
{
avPolygon[0] = avVtx[0];
avPolygon[1] = avVtx[2];
avPolygon[2] = avVtx[3];
AddPolygon( 3, avPolygon, bInvert);
avPolygon[0] = avVtx[0];
avPolygon[1] = avVtx[1];
avPolygon[2] = avVtx[2];
AddPolygon( 3, avPolygon, bInvert);
}
else
{
avPolygon[0] = avVtx[0];
avPolygon[1] = avVtx[1];
avPolygon[2] = avVtx[2];
avPolygon[3] = avVtx[3];
// if bottom shape is floor
if( bBottomFloor)
{
avPolygon[0](2) = 0.0;
avPolygon[1](2) = 0.0;
avPolygon[2](2) = 0.0;
avPolygon[3](2) = 0.0;
}
AddPolygon( 4, avPolygon, bInvert);
}
}
// free allocated arrays
for( INDEX iFree=0; iFree<iNoOfStairs; iFree++)
{
delete papvBases[iFree];
}
delete papvBases;
theApp.m_vfpCurrent.vfp_o3dPrimitive.Optimize();
ConvertObject3DToBrush(theApp.m_vfpCurrent.vfp_o3dPrimitive);
}
void CWorldEditorDoc::CreateSpherePrimitive(void)
{
// calculate width, lenght and height but as radiuses !!! (divided by 2)
DOUBLE fWidth = (theApp.m_vfpCurrent.vfp_fXMax-theApp.m_vfpCurrent.vfp_fXMin)/2.0;
if( fWidth < SNAP_FLOAT_GRID) fWidth = SNAP_FLOAT_GRID;
DOUBLE fHeight = (theApp.m_vfpCurrent.vfp_fYMax-theApp.m_vfpCurrent.vfp_fYMin)/2.0;
if( fHeight < SNAP_FLOAT_GRID) fHeight = SNAP_FLOAT_GRID;
DOUBLE fLenght = (theApp.m_vfpCurrent.vfp_fZMax-theApp.m_vfpCurrent.vfp_fZMin)/2.0;
if( fLenght < SNAP_FLOAT_12) fLenght = SNAP_FLOAT_12;
// get parameters for staircases
INDEX iMeridians = theApp.m_vfpCurrent.vfp_iMeridians;
INDEX iParalels = theApp.m_vfpCurrent.vfp_iParalels;
BOOL bInvert = _bClosed;
// calculate bases
DOUBLE3D **papvSlices = new DOUBLE3D *[iParalels+1];
ANGLE angleSliceDelta = AngleDeg(180.0f)/iParalels;
ANGLE angleSlice = -90.0f;
// calculate all slices on sphere
INDEX iSlice=0;
for( ; iSlice<iParalels+1; iSlice++)
{
DOUBLE fSliceHeight, dA, dB;
// if equal slices
if( theApp.m_vfpCurrent.vfp_bLinearStaircases)
{
fSliceHeight = -fHeight + (fHeight*2/iParalels*iSlice);
}
else
{
fSliceHeight = Sin( angleSlice) * fHeight;
}
// snap X coordinate (1 cm)
//Snap(fSliceHeight, SNAP_DOUBLE_CM);
// calculate width and lenght of elipse for current slice
dA = sqrt((fWidth*fWidth*fHeight*fHeight-
fWidth*fWidth*fSliceHeight*fSliceHeight)/(fHeight*fHeight));
dB = sqrt((fLenght*fLenght*fHeight*fHeight-
fLenght*fLenght*fSliceHeight*fSliceHeight)/(fHeight*fHeight));
// array for vertices of this slice
papvSlices[iSlice] = new DOUBLE3D[iMeridians];
ANGLE angle = AngleDeg(360.0f)/iMeridians;
ANGLE angleCt = 0;
for( INDEX iVtx=0; iVtx<iMeridians; iVtx++)
{
DOUBLE x = Cos( angleCt) * dA + (theApp.m_vfpCurrent.vfp_fXMin+theApp.m_vfpCurrent.vfp_fXMax)/2.0f;
DOUBLE z = Sin( angleCt) * dB + (theApp.m_vfpCurrent.vfp_fZMin+theApp.m_vfpCurrent.vfp_fZMax)/2.0f;
// snap X coordinate (1 cm)
//Snap(x, SNAP_DOUBLE_CM);
// snap Y coordinate (1 cm)
//Snap(z, SNAP_DOUBLE_CM);
papvSlices[iSlice][iVtx] = DOUBLE3D( x, fSliceHeight, z);
angleCt += angle;
}
angleSlice += angleSliceDelta;
}
DOUBLE3D avPolygon[4];
// create polygons
for( iSlice=0; iSlice<iParalels; iSlice++)
{
INDEX iNextSlice = iSlice+1;
for( INDEX iVtx=0; iVtx<iMeridians; iVtx++)
{
INDEX iNextVtx = (iVtx+1)%iMeridians;
if (iSlice == 0) {
avPolygon[0] = papvSlices[iNextSlice][iVtx];
avPolygon[1] = papvSlices[iNextSlice][iNextVtx];
avPolygon[2] = papvSlices[iSlice][0];
AddPolygon( 3, avPolygon, bInvert);
} else if (iSlice == iParalels-1) {
avPolygon[0] = papvSlices[iNextSlice][0];
avPolygon[1] = papvSlices[iSlice][iNextVtx];
avPolygon[2] = papvSlices[iSlice][iVtx];
AddPolygon( 3, avPolygon, bInvert);
} else {
avPolygon[0] = papvSlices[iNextSlice][iVtx];
avPolygon[1] = papvSlices[iNextSlice][iNextVtx];
avPolygon[2] = papvSlices[iSlice][iNextVtx];
avPolygon[3] = papvSlices[iSlice][iVtx];
AddPolygon( 4, avPolygon, bInvert);
}
}
}
// free allocated arrays
for( INDEX iFree=0; iFree<iParalels+1; iFree++)
{
delete papvSlices[iFree];
}
delete papvSlices;
theApp.m_vfpCurrent.vfp_o3dPrimitive.Optimize();
ConvertObject3DToBrush(theApp.m_vfpCurrent.vfp_o3dPrimitive);
}
void InitializeObject3DForPrimitive(void)
{
// clear Object3D that will be used for creating primitive
theApp.m_vfpCurrent.vfp_o3dPrimitive.Clear();
// create sector
_poscSector = theApp.m_vfpCurrent.vfp_o3dPrimitive.ob_aoscSectors.New(1);
_poscSector->osc_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; iz<iSlicesZ+1; iz++)
{
{for( INDEX ix=0; ix<iSlicesX; ix++)
{
INDEX iVtx1 = iz*(iSlicesX+1)+ix;
INDEX iEdg = iz*iSlicesX+ix;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
oedg.oed_Vertex0 = &osec.osc_aovxVertices[iVtx1];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[iVtx1+1];
}
}
}}
// create vertical edges
#define START_OF_VERTICAL_EDGES (iSlicesX*(iSlicesZ+1))
{for( INDEX iz=0; iz<iSlicesZ; iz++)
{
{for( INDEX ix=0; ix<iSlicesX+1; ix++)
{
INDEX iVtx1 = iz*(iSlicesX+1)+ix;
INDEX iEdg = START_OF_VERTICAL_EDGES + iz*(iSlicesX+1)+ix;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
oedg.oed_Vertex0 = &osec.osc_aovxVertices[iVtx1];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[iVtx1+(iSlicesX+1)];
}
}
}}
// create slope edges
#define START_OF_SLOPE_EDGES (iSlicesX*(iSlicesZ+1)+(iSlicesX+1)*iSlicesZ)
{for( INDEX iz=0; iz<iSlicesZ; iz++)
{
{for( INDEX ix=0; ix<iSlicesX; ix++)
{
INDEX iVtx1 = iz*(iSlicesX+1)+ix;
INDEX iEdg = START_OF_SLOPE_EDGES + iz*iSlicesX+ix;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
oedg.oed_Vertex0 = &osec.osc_aovxVertices[iVtx1];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[iVtx1+iSlicesX+1+1];
}
}
}}
// create border edges
#define START_OF_BORDER_EDGES (iSlicesX*(iSlicesZ+1)+(iSlicesX+1)*iSlicesZ+iSlicesX*iSlicesZ)
{
// 0
INDEX iEdg = START_OF_BORDER_EDGES + 0;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
INDEX iVtx0 = (iSlicesX+1)*iSlicesZ;
oedg.oed_Vertex0 = &osec.osc_aovxVertices[iVtx0];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+0];
}
{
// 1
INDEX iEdg = START_OF_BORDER_EDGES + 1;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
INDEX iVtx0 = (iSlicesX+1)*(iSlicesZ+1)-1;
oedg.oed_Vertex0 = &osec.osc_aovxVertices[iVtx0];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+1];
}
{
// 2
INDEX iEdg = START_OF_BORDER_EDGES + 2;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
INDEX iVtx0 = iSlicesX;
oedg.oed_Vertex0 = &osec.osc_aovxVertices[iVtx0];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+2];
}
{
// 3
INDEX iEdg = START_OF_BORDER_EDGES + 3;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
INDEX iVtx0 = 0;
oedg.oed_Vertex0 = &osec.osc_aovxVertices[iVtx0];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+3];
}
// create ceiling edges
#define START_OF_CEILING_EDGES (START_OF_BORDER_EDGES + 4)
{
// 0
INDEX iEdg = START_OF_CEILING_EDGES + 0;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
oedg.oed_Vertex0 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+1];
}
{
// 1
INDEX iEdg = START_OF_CEILING_EDGES + 1;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
oedg.oed_Vertex0 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+1];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+2];
}
{
// 2
INDEX iEdg = START_OF_CEILING_EDGES + 2;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
oedg.oed_Vertex0 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+2];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+3];
}
{
// 3
INDEX iEdg = START_OF_CEILING_EDGES + 3;
CObjectEdge &oedg = osec.osc_aoedEdges[iEdg];
oedg.oed_Vertex0 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+3];
oedg.oed_Vertex1 = &osec.osc_aovxVertices[START_OF_CEILING_VERTICES+0];
}
// get material
CObjectMaterial &omat = *_pomMaterial;
// allocate polygons and their planes
INDEX ctPolygons = iSlicesX*iSlicesZ*2+4+1;
osec.osc_aopoPolygons.New(ctPolygons);
osec.osc_aoplPlanes.New(ctPolygons);
osec.osc_aopoPolygons.Lock();
osec.osc_aoplPlanes.Lock();
// create floor polygons and their planes
for( INDEX iPolygon=0; iPolygon<iSlicesX*iSlicesZ; iPolygon++)
{
// obtain edges of one broken checked polygon
CObjectEdge *poe0, *poe1, *poe2, *poe3, *poe4;
GetTerrainPolygonEdges(osec, iPolygon, iSlicesX, iSlicesZ, poe0, poe1, poe2, poe3, poe4);
{
// create upper polygon
CObjectPlane &opl = osec.osc_aoplPlanes[iPolygon*2+0];
opl = DOUBLEplane3D( *poe3->oed_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( ; iEdg<iSlicesX; iEdg++)
{
INDEX iEdgeIdx = iSlicesX*iSlicesZ+iEdg;
opo.opo_PolygonEdges[iEdg].ope_Edge = &osec.osc_aoedEdges[ iEdgeIdx];
opo.opo_PolygonEdges[iEdg].ope_Backward = TRUE;
}
opo.opo_PolygonEdges[iEdg+0].ope_Edge = &osec.osc_aoedEdges[ START_OF_BORDER_EDGES+0];
opo.opo_PolygonEdges[iEdg+0].ope_Backward = FALSE;
opo.opo_PolygonEdges[iEdg+1].ope_Edge = &osec.osc_aoedEdges[ START_OF_CEILING_EDGES+0];
opo.opo_PolygonEdges[iEdg+1].ope_Backward = FALSE;
opo.opo_PolygonEdges[iEdg+2].ope_Edge = &osec.osc_aoedEdges[ START_OF_BORDER_EDGES+1];
opo.opo_PolygonEdges[iEdg+2].ope_Backward = TRUE;
opo.opo_Material = &omat;
opo.opo_ulFlags = ulNonFllorPolygonFlags;
opo.opo_colorColor = theApp.m_vfpCurrent.vfp_colPolygonsColor;
opo.opo_PolygonEdges.Unlock();
}
{
// side polygon 1
CObjectPolygon &opo = osec.osc_aopoPolygons[START_OF_SIDE_POLYGONS+1];
CObjectEdge &oe0 = osec.osc_aoedEdges[ START_OF_BORDER_EDGES+1];
CObjectEdge &oe1 = osec.osc_aoedEdges[ START_OF_CEILING_EDGES+1];
CObjectPlane &opl = osec.osc_aoplPlanes[START_OF_SIDE_POLYGONS+1];
opl = DOUBLEplane3D( *oe0.oed_Vertex0, *oe0.oed_Vertex1, *oe1.oed_Vertex1);
opo.opo_Plane = &opl;
opo.opo_PolygonEdges.New(iSlicesZ+3);
opo.opo_PolygonEdges.Lock();
INDEX iEdg=0;
for( ; iEdg<iSlicesZ; iEdg++)
{
opo.opo_PolygonEdges[iEdg].ope_Edge = &osec.osc_aoedEdges[ START_OF_VERTICAL_EDGES+iSlicesX+iEdg*(iSlicesX+1)];
opo.opo_PolygonEdges[iEdg].ope_Backward = FALSE;
}
opo.opo_PolygonEdges[iEdg+0].ope_Edge = &osec.osc_aoedEdges[ START_OF_BORDER_EDGES+1];
opo.opo_PolygonEdges[iEdg+0].ope_Backward = FALSE;
opo.opo_PolygonEdges[iEdg+1].ope_Edge = &osec.osc_aoedEdges[ START_OF_CEILING_EDGES+1];
opo.opo_PolygonEdges[iEdg+1].ope_Backward = FALSE;
opo.opo_PolygonEdges[iEdg+2].ope_Edge = &osec.osc_aoedEdges[ START_OF_BORDER_EDGES+2];
opo.opo_PolygonEdges[iEdg+2].ope_Backward = TRUE;
opo.opo_Material = &omat;
opo.opo_ulFlags = ulNonFllorPolygonFlags;
opo.opo_colorColor = theApp.m_vfpCurrent.vfp_colPolygonsColor;
opo.opo_PolygonEdges.Unlock();
}
{
// side polygon 2
CObjectPolygon &opo = osec.osc_aopoPolygons[START_OF_SIDE_POLYGONS+2];
CObjectEdge &oe0 = osec.osc_aoedEdges[ START_OF_BORDER_EDGES+2];
CObjectEdge &oe1 = osec.osc_aoedEdges[ START_OF_CEILING_EDGES+2];
CObjectPlane &opl = osec.osc_aoplPlanes[START_OF_SIDE_POLYGONS+2];
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( ; iEdg<iSlicesX; iEdg++)
{
opo.opo_PolygonEdges[iEdg].ope_Edge = &osec.osc_aoedEdges[ iEdg];
opo.opo_PolygonEdges[iEdg].ope_Backward = FALSE;
}
opo.opo_PolygonEdges[iEdg+0].ope_Edge = &osec.osc_aoedEdges[ START_OF_BORDER_EDGES+2];
opo.opo_PolygonEdges[iEdg+0].ope_Backward = FALSE;
opo.opo_PolygonEdges[iEdg+1].ope_Edge = &osec.osc_aoedEdges[ START_OF_CEILING_EDGES+2];
opo.opo_PolygonEdges[iEdg+1].ope_Backward = FALSE;
opo.opo_PolygonEdges[iEdg+2].ope_Edge = &osec.osc_aoedEdges[ START_OF_BORDER_EDGES+3];
opo.opo_PolygonEdges[iEdg+2].ope_Backward = TRUE;
opo.opo_Material = &omat;
opo.opo_ulFlags = ulNonFllorPolygonFlags;
opo.opo_colorColor = theApp.m_vfpCurrent.vfp_colPolygonsColor;
opo.opo_PolygonEdges.Unlock();
}
{
// side polygon 3
CObjectPolygon &opo = osec.osc_aopoPolygons[START_OF_SIDE_POLYGONS+3];
CObjectEdge &oe0 = osec.osc_aoedEdges[ START_OF_BORDER_EDGES+3];
CObjectEdge &oe1 = osec.osc_aoedEdges[ START_OF_CEILING_EDGES+3];
CObjectPlane &opl = osec.osc_aoplPlanes[START_OF_SIDE_POLYGONS+3];
opl = DOUBLEplane3D( *oe0.oed_Vertex0, *oe0.oed_Vertex1, *oe1.oed_Vertex1);
opo.opo_Plane = &opl;
opo.opo_PolygonEdges.New(iSlicesZ+3);
opo.opo_PolygonEdges.Lock();
INDEX iEdg=0;
for( ; iEdg<iSlicesZ; iEdg++)
{
opo.opo_PolygonEdges[iEdg].ope_Edge = &osec.osc_aoedEdges[ START_OF_VERTICAL_EDGES+iEdg*(iSlicesX+1)];
opo.opo_PolygonEdges[iEdg].ope_Backward = TRUE;
}
opo.opo_PolygonEdges[iEdg+0].ope_Edge = &osec.osc_aoedEdges[ START_OF_BORDER_EDGES+3];
opo.opo_PolygonEdges[iEdg+0].ope_Backward = FALSE;
opo.opo_PolygonEdges[iEdg+1].ope_Edge = &osec.osc_aoedEdges[ START_OF_CEILING_EDGES+3];
opo.opo_PolygonEdges[iEdg+1].ope_Backward = FALSE;
opo.opo_PolygonEdges[iEdg+2].ope_Edge = &osec.osc_aoedEdges[ START_OF_BORDER_EDGES+0];
opo.opo_PolygonEdges[iEdg+2].ope_Backward = TRUE;
opo.opo_Material = &omat;
opo.opo_ulFlags = ulNonFllorPolygonFlags;
opo.opo_colorColor = theApp.m_vfpCurrent.vfp_colPolygonsColor;
opo.opo_PolygonEdges.Unlock();
}
// create ceiling polygon and its plane
#define CEILING_POLYGON (iSlicesX*iSlicesZ*2+4)
{
// ceiling polygon
CObjectPolygon &opo = osec.osc_aopoPolygons[CEILING_POLYGON];
CObjectEdge &oe0 = osec.osc_aoedEdges[ START_OF_CEILING_EDGES+0];
CObjectEdge &oe1 = osec.osc_aoedEdges[ START_OF_CEILING_EDGES+1];
CObjectPlane &opl = osec.osc_aoplPlanes[CEILING_POLYGON];
opl = DOUBLEplane3D( *oe1.oed_Vertex1, *oe1.oed_Vertex0, *oe0.oed_Vertex0);
opo.opo_Plane = &opl;
opo.opo_PolygonEdges.New(4);
opo.opo_PolygonEdges.Lock();
opo.opo_PolygonEdges[0].ope_Edge = &osec.osc_aoedEdges[ START_OF_CEILING_EDGES+0];
opo.opo_PolygonEdges[0].ope_Backward = TRUE;
opo.opo_PolygonEdges[1].ope_Edge = &osec.osc_aoedEdges[ START_OF_CEILING_EDGES+1];
opo.opo_PolygonEdges[1].ope_Backward = TRUE;
opo.opo_PolygonEdges[2].ope_Edge = &osec.osc_aoedEdges[ START_OF_CEILING_EDGES+2];
opo.opo_PolygonEdges[2].ope_Backward = TRUE;
opo.opo_PolygonEdges[3].ope_Edge = &osec.osc_aoedEdges[ START_OF_CEILING_EDGES+3];
opo.opo_PolygonEdges[3].ope_Backward = TRUE;
opo.opo_Material = &omat;
opo.opo_ulFlags = ulNonFllorPolygonFlags;
opo.opo_colorColor = theApp.m_vfpCurrent.vfp_colPolygonsColor;
opo.opo_PolygonEdges.Unlock();
}
osec.osc_aovxVertices.Unlock();
osec.osc_aoedEdges.Unlock();
osec.osc_aoplPlanes.Unlock();
osec.osc_aopoPolygons.Unlock();
theApp.m_vfpCurrent.vfp_o3dPrimitive.Optimize();
}
void GetTerrainPolygonEdges(CObjectSector &osec, INDEX iPolygon, INDEX iSlicesX, INDEX iSlicesZ,
CObjectEdge *&poe0, CObjectEdge *&poe1, CObjectEdge *&poe2,
CObjectEdge *&poe3, CObjectEdge *&poe4)
{
INDEX iPolX = iPolygon%iSlicesX;
INDEX iPolZ = iPolygon/iSlicesX;
INDEX iEdge0 = iPolZ*iSlicesX+iPolX;
poe0 = &osec.osc_aoedEdges[ iEdge0];
INDEX iEdge1 = START_OF_VERTICAL_EDGES+iPolZ*(iSlicesX+1)+iPolX;
poe1 = &osec.osc_aoedEdges[ iEdge1];
INDEX iEdge2 = iEdge0+iSlicesX;
poe2 = &osec.osc_aoedEdges[ iEdge2];
INDEX iEdge3 = iEdge1+1;
poe3 = &osec.osc_aoedEdges[ iEdge3];
INDEX iEdge4 = START_OF_SLOPE_EDGES+iPolZ*iSlicesX+iPolX;
poe4 = &osec.osc_aoedEdges[ iEdge4];
}
void CWorldEditorDoc::CreatePrimitive(void)
{
// this is patch because deleting of primitive property page can call this
if( m_iMode != CSG_MODE) return;
// activ texture must exist
ASSERT( theApp.m_ptdActiveTexture != NULL);
InitializeObject3DForPrimitive();
switch( theApp.m_vfpCurrent.vfp_ptPrimitiveType)
{
case PT_CONUS:
case PT_TORUS:
{
// calculate width, height and lenght
DOUBLE fWidth = theApp.m_vfpCurrent.vfp_fXMax-theApp.m_vfpCurrent.vfp_fXMin;
DOUBLE fHeight = theApp.m_vfpCurrent.vfp_fYMax-theApp.m_vfpCurrent.vfp_fYMin;
DOUBLE fLenght = theApp.m_vfpCurrent.vfp_fZMax-theApp.m_vfpCurrent.vfp_fZMin;
// some values must be valid, so if they are not, coorect them
if( fWidth < SNAP_FLOAT_GRID) fWidth = SNAP_FLOAT_GRID;
if( fHeight < SNAP_FLOAT_GRID) fHeight = SNAP_FLOAT_GRID;
if( fLenght < SNAP_FLOAT_GRID) fLenght = SNAP_FLOAT_GRID;
// divide width and lenght by two because these values are used as radiuses
fWidth /= 2.0f;
fLenght/= 2.0f;
// get count of vertices that will be used for creating base polygon
INDEX vtxCt = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.Count();
// if currently used number of vertices is not same as one used for last create primitive,
// or if change occure in width or lenght
if( (m_bPrimitiveCreatedFirstTime) ||
(m_ctLastPrimitiveVertices != vtxCt) ||
(m_fLastPrimitiveWidth != fWidth) ||
(m_fLastPrimitiveLenght != fLenght) ||
(m_bLastIfOuter != theApp.m_vfpCurrent.vfp_bOuter) ||
(m_ttLastTriangularisationType != theApp.m_vfpCurrent.vfp_ttTriangularisationType) )
{
// recreate base vertices (discard vertex dragging)
if( !_bDontRecalculateBase)
{
theApp.m_vfpCurrent.CalculatePrimitiveBase();
}
_bDontRecalculateBase = FALSE;
// and remember new values for base, wait with recreating before any
// change occure or until CSG operaton is applyed
m_bPrimitiveCreatedFirstTime = FALSE;
m_ctLastPrimitiveVertices = vtxCt;
m_fLastPrimitiveWidth = fWidth;
m_fLastPrimitiveLenght = fLenght;
m_bLastIfOuter = theApp.m_vfpCurrent.vfp_bOuter;
m_ttLastTriangularisationType = theApp.m_vfpCurrent.vfp_ttTriangularisationType;
}
// create primitive for the first time
if( theApp.m_vfpCurrent.vfp_ptPrimitiveType == PT_CONUS)
{
CreateConusPrimitive();
}
else
{
CreateTorusPrimitive();
}
break;
}
case PT_STAIRCASES:
{
CreateStaircasesPrimitive();
break;
}
case PT_SPHERE:
{
CreateSpherePrimitive();
break;
}
case PT_TERRAIN:
{
CreateTerrainPrimitive();
break;
}
default:
{
ASSERTALWAYS( "Invalid primitive type found!");
}
}
// update position property page
m_chSelections.MarkChanged();
}
/*
* refresh primitive page
*/
void CWorldEditorDoc::RefreshPrimitivePage(void)
{
CMainFrame* pMainFrame = STATIC_DOWNCAST(CMainFrame, AfxGetMainWnd());
// if info exists and active page is primitive page
if( (pMainFrame->m_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<CEntity> 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<DOUBLE3D> avDecreased;
avDecreased.New(vtxCt-1);
INDEX iVtxNew = 0;
for( INDEX iVtxOld = 0; iVtxOld<vtxCt; iVtxOld++)
{
if( iVtxOld != iVtxToDelete)
{
avDecreased[iVtxNew] = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[iVtxOld];
iVtxNew++;
}
}
// copy new array back to primitive
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.Clear();
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.New(vtxCt-1);
for( INDEX iVtx=0; iVtx<vtxCt-1; iVtx++)
{
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[iVtx] = avDecreased[iVtx];
}
m_ctLastPrimitiveVertices = vtxCt-1;
RefreshPrimitivePage();
CreatePrimitive();
UpdateAllViews( NULL);
}
void CWorldEditorDoc::InsertPrimitiveVertex(INDEX iEdge, FLOAT3D vVertexToInsert)
{
// get count of vertices on the base
INDEX vtxCt = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.Count();
CStaticArray<DOUBLE3D> avIncreased;
avIncreased.New(vtxCt+1);
INDEX iVtxNew = 0;
for( INDEX iVtxOld = 0; iVtxOld<vtxCt; iVtxOld++)
{
avIncreased[iVtxNew] = theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[iVtxOld];
iVtxNew++;
if( iVtxOld == iEdge)
{
avIncreased[iVtxNew] = FLOATtoDOUBLE(vVertexToInsert);
avIncreased[iVtxNew](2) = 0.0f;
iVtxNew++;
}
}
// copy new array back to primitive
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.Clear();
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive.New(vtxCt+1);
for( INDEX iVtx=0; iVtx<vtxCt+1; iVtx++)
{
theApp.m_vfpCurrent.vfp_avVerticesOnBaseOfPrimitive[iVtx] = avIncreased[iVtx];
}
m_ctLastPrimitiveVertices = vtxCt+1;
RefreshPrimitivePage();
CreatePrimitive();
UpdateAllViews( NULL);
}
void CWorldEditorDoc::OnUpdateLinks()
{
CWaitCursor wc;
m_woWorld.RebuildLinks();
}
void CWorldEditorDoc::OnSnapshot()
{
RememberUndo();
}
void CWorldEditorDoc::OnMirrorAndStretch()
{
if (!m_bPrimitiveMode) {
CDlgMirrorAndStretch dlg;
// set dialog name
if (m_pwoSecondLayer != NULL) {
dlg.m_strName = "Mirror and stretch second layer";
} else {
dlg.m_strName = "Mirror and stretch entire world";
}
if( dlg.DoModal() == IDOK)
{
ApplyMirrorAndStretch( dlg.m_iMirror, dlg.m_fStretch);
}
} else {
ASSERT(FALSE);
}
}
void CWorldEditorDoc::OnFlipLayer()
{
if (!m_bPrimitiveMode) {
ApplyMirrorAndStretch( m_iMirror, 1.0f);
m_iMirror = (m_iMirror+1)%4;
ApplyMirrorAndStretch( m_iMirror, 1.0f);
}
}
void CWorldEditorDoc::OnUpdateFlipLayer(CCmdUI* pCmdUI)
{
// enable only for second layer
pCmdUI->Enable( 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<CEntity> 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<CPlacement3D> aplClones;
INDEX ctEntities = apenClones.Count();
aplClones.New( ctEntities);
{for( INDEX iEntity=0; iEntity<ctEntities; iEntity++)
{
aplClones[ iEntity] = apenClones[ iEntity].GetPlacement();
}}
// delete clones with their descendents
{for( INDEX iEntity=0; iEntity<ctEntities; iEntity++)
{
DeleteEntityWithDescendents( m_woWorld, &apenClones[ iEntity]);
}}
// clone entity(ies) for each remembered placement
{for( INDEX iEntity=0; iEntity<ctEntities; iEntity++)
{
m_woWorld.CopyEntityInWorld( *penOnlySelected, aplClones[ iEntity]);
}}
apenClones.Unlock();
m_chSelections.MarkChanged();
m_chDocument.MarkChanged();
SetModifiedFlag();
UpdateAllViews( NULL);
}
void CWorldEditorDoc::SetCutMode( CWorldEditorView *pwedView)
{
POSITION pos = GetFirstViewPosition();
CWorldEditorView *pwedTemp;
FOREVER
{
pwedTemp = (CWorldEditorView *) GetNextView(pos);
if( pwedTemp == NULL) return;
if( pwedTemp == pwedView)
{
pwedTemp->m_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; iVtx<bpo.bpo_aiTriangleElements.Count(); iVtx+=3)
{
CBrushVertex &vtx1=*bpo.bpo_apbvxTriangleVertices[bpo.bpo_aiTriangleElements[iVtx+0]];
CBrushVertex &vtx2=*bpo.bpo_apbvxTriangleVertices[bpo.bpo_aiTriangleElements[iVtx+1]];
CBrushVertex &vtx3=*bpo.bpo_apbvxTriangleVertices[bpo.bpo_aiTriangleElements[iVtx+2]];
CTString strTemp;
strTemp.PrintF("%g %g %g %g %g %g %g %g %g",
vtx1.bvx_vAbsolute(1), vtx1.bvx_vAbsolute(2), vtx1.bvx_vAbsolute(3),
vtx2.bvx_vAbsolute(1), vtx2.bvx_vAbsolute(2), vtx2.bvx_vAbsolute(3),
vtx3.bvx_vAbsolute(1), vtx3.bvx_vAbsolute(2), vtx3.bvx_vAbsolute(3));
strmFile.PutLine_t( strTemp);
}
}
strmFile.Close();
}
catch( char *err_str)
{
AfxMessageBox( CString(err_str));
}
}
void CWorldEditorDoc::OnUpdateExport3dObject(CCmdUI* pCmdUI)
{
pCmdUI->Enable( 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<CTString> 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; iProperty<pdecDLLClass->dec_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; iSmc<astrNeddedSmc.Count(); iSmc++)
{
if(astrNeddedSmc[iSmc]==CTString(fnmFile))
{
bExistsInList=TRUE;
break;
}
}
if(!bExistsInList)
{
CTString &strNew=astrNeddedSmc.Push();
strNew=CTString(fnmFile);
}
}
if( pepProperty->ep_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<astrNeddedSmc.Count(); iSmc++)
{
strmSmlFile.PutLine_t(astrNeddedSmc[iSmc]);
}
AfxMessageBox(L"Placements exported!", MB_OK|MB_ICONINFORMATION);
}
catch( char *err_str)
{
AfxMessageBox( CString(err_str));
}
}
CTFileName CorrectSlashes(const CTFileName &fnmFile)
{
char afnmSlash[1024];
for(INDEX iChar=0; iChar<fnmFile.Length(); iChar++) {
afnmSlash[iChar] = fnmFile[iChar];
if(afnmSlash[iChar]=='\\') {
afnmSlash[iChar] = '/';
}
}
// end the string
afnmSlash[fnmFile.Length()] = 0;
return CTString(afnmSlash);
}
// Detects detail texture and replaces it with normal map texture
CTFileName RemapDetailTexturePath(CTFileName &fnmFile)
{
if(fnmFile.FindSubstr("/Detail/") >=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<strOrg.Length(); iChar++) {
// if we found a quote
if(strOrg[iChar]=='\"') {
// replace it with \"
achrFixed[iFixedChar++] = '\\';
achrFixed[iFixedChar++] = '\"';
} else {
achrFixed[iFixedChar++] = strOrg[iChar];
}
}
// end the string
achrFixed[iFixedChar] = 0;
return CTString(achrFixed);
}
// Class used to represent polygon during brush to .amf exporting
class CAmfNGon {
public:
CDynamicContainer<CBrushVertex> ang_cbpoVertices;
public:
void FromBrushPolygon(CBrushPolygon *pbpo);
};
// Class used to represent polygon during brush to .amf exporting
class CAmfPolygon {
public:
CStaticStackArray<CAmfNGon> 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<INDEX> 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; iTri<aiTriangles.Count()/3; iTri++) {
if(aiTriangles[iTri*3] != -1) {
iFirstNGonTriangle=iTri;
break;
}
}}
// triangle must be found
if(iFirstNGonTriangle==-1) {
return;
}
// and add it to the loop and mark them as handled
aNgon.ang_cbpoVertices.Add( pbpo->bpo_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; iLoopEdge<aNgon.ang_cbpoVertices.Count(); iLoopEdge++) {
// get edge vertices
CBrushVertex *pbvLoop0 = &aNgon.ang_cbpoVertices[iLoopEdge];
CBrushVertex *pbvLoop1 = &aNgon.ang_cbpoVertices[(iLoopEdge+1)%aNgon.ang_cbpoVertices.Count()];
// find triangle that shares edge
for(INDEX iTri=0; iTri<aiTriangles.Count()/3; iTri++) {
// for each edge in triangle
for(INDEX iTriEdge=0; iTriEdge<3; iTriEdge++) {
// fetch edge vertex indices
INDEX iTriVtx0 = aiTriangles[iTri*3+iTriEdge];
INDEX iTriVtx1 = aiTriangles[iTri*3+(iTriEdge+1)%3];
// if triangle is already handled
if(iTriVtx0==-1 || iTriVtx1==-1) {
break;
}
CBrushVertex *pbvEdg0 = pbpo->bpo_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<aiTriangles.Count()/3; iTri++) {
// if not all are cleared
if(aiTriangles[iTri*3] != -1) {
// add another ngon
goto _nextNgon;
}
}}
}
// Types of exported types
enum ExportType {
ET_RENDERING,
ET_VISIBILITY,
};
// Class used to collect surface data for brush to .amf exporting
class CAmfSurface {
public:
CDynamicContainer<CBrushPolygon> 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; iPlg<sf_cbpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = sf_cbpoPolygons[iPlg];
CAmfPolygon *amfp = (CAmfPolygon *)bpo.bpo_pspoScreenPolygon;
ctNgons += amfp->amfp_aangNgons.Count();
}
return ctNgons;
};
// Calculates count of ngon vertices
INDEX GetNGonVertexCount(void) {
INDEX ctVertices = 0;
for(INDEX iPlg=0; iPlg<sf_cbpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = sf_cbpoPolygons[iPlg];
CAmfPolygon *amfp = (CAmfPolygon *)bpo.bpo_pspoScreenPolygon;
for(INDEX iNgon=0; iNgon<amfp->amfp_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<CAmfSurface> cbpoSurfaces;
// assume that there will not be any portals nor occluders
CStaticStackArray<INDEX> ciPortals;
CStaticStackArray<INDEX> ciOccluders;
CStaticStackArray<INDEX> ciClassifiers;
// for each sector in the brush mip
INDEX iPlgGlobal=0;
{for(INDEX iSector=0; iSector<pbmMip->bm_abscSectors.Count(); iSector++) {
CBrushSector &bs = pbmMip->bm_abscSectors[iSector];
// for each polygon in the sector
for(INDEX iPlg=0; iPlg<bs.bsc_abpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = bs.bsc_abpoPolygons[iPlg];
// if we are exporting collision (e.g. for empty brushes)
if(etExportType==ET_RENDERING) {
if(!bFieldBrush && (!IsPolygonVisible(bpo) && !bCollisionOnlyBrush) ) {
continue;
}
CAnimData *pad = bpo.bpo_abptTextures[0].bpt_toTexture.GetData();
UBYTE ubMaterial = bpo.bpo_bppProperties.bpp_ubSurfaceType;
BOOL bFound = FALSE;
for(INDEX iSurf=0; iSurf<cbpoSurfaces.Count(); iSurf++) {
CAmfSurface &asSurf = cbpoSurfaces[iSurf];
// if this surface for the texture-surface pair is already defined
if( (asSurf.sf_padAnimData==pad) && (asSurf.sf_ubMaterial==ubMaterial) ) {
// add polygon to existing surface
asSurf.sf_cbpoPolygons.Add(&bpo);
bFound = TRUE;
break;
}
}
// if surface with current texture and material is not yet defined
if(!bFound) {
CAmfSurface *pSurf = (CAmfSurface *) new(CAmfSurface);
cbpoSurfaces.Add(pSurf);
pSurf->sf_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; iNgon<amfp->amfp_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; iSurf<cbpoSurfaces.Count(); iSurf++) {
CAmfSurface &amfs = cbpoSurfaces[iSurf];
ctTotalPolygons+=amfs.GetNGonCount();
ctTotalVertices+=amfs.GetNGonVertexCount();
}}
// if there is no polygons to export
if(ctTotalPolygons==0) {
return;
}
strmAmf.FPrintF_t(" LAYER_NAME \"%s\"\n", strLayerName);
strmAmf.FPrintF_t(" LAYER_INDEX %d\n", iLayerNo);
strmAmf.PutLine_t(" {");
INDEX ctVertexMaps = etExportType==ET_RENDERING ? 4 : 1;
strmAmf.FPrintF_t(" VERTEX_MAPS %d\n", ctVertexMaps);
strmAmf.PutLine_t(" {");
strmAmf.PutLine_t(" VERTEX_MAP \"morph.position\"");
strmAmf.PutLine_t(" {");
strmAmf.FPrintF_t(" ELEMENTS %d\n", ctTotalVertices);
strmAmf.PutLine_t(" {");
{for(INDEX iSurf=0; iSurf<cbpoSurfaces.Count(); iSurf++) {
CAmfSurface &asSurf = cbpoSurfaces[iSurf];
for(INDEX iPlg=0; iPlg<asSurf.sf_cbpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = asSurf.sf_cbpoPolygons[iPlg];
CAmfPolygon *amfp = (CAmfPolygon *)bpo.bpo_pspoScreenPolygon;
for(INDEX iNgon=0; iNgon<amfp->amfp_aangNgons.Count(); iNgon++) {
CAmfNGon &aNgon = amfp->amfp_aangNgons[iNgon];
for(INDEX iVtx=0; iVtx<aNgon.ang_cbpoVertices.Count(); iVtx++) {
CBrushVertex &bVtx = aNgon.ang_cbpoVertices[iVtx];
strmAmf.FPrintF_t(" { %f, %f, %f; }\n", bVtx.bvx_vdPreciseRelative(1), bVtx.bvx_vdPreciseRelative(2), bVtx.bvx_vdPreciseRelative(3));
}
}
}
}}
strmAmf.PutLine_t(" }");
strmAmf.PutLine_t(" }");
if(etExportType==ET_RENDERING) {
for(INDEX iTextureLayer=0; iTextureLayer<3; iTextureLayer++) {
strmAmf.FPrintF_t(" VERTEX_MAP \"texcoord.Texture %d\"", iTextureLayer+1);
strmAmf.PutLine_t(" {");
strmAmf.FPrintF_t(" ELEMENTS %d\n", ctTotalVertices);
strmAmf.PutLine_t(" {");
{for(INDEX iSurf=0; iSurf<cbpoSurfaces.Count(); iSurf++) {
CAmfSurface &asSurf = cbpoSurfaces[iSurf];
for(INDEX iPlg=0; iPlg<asSurf.sf_cbpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = asSurf.sf_cbpoPolygons[iPlg];
// fetch mapping parameters
CMappingVectors mvDefault;
mvDefault.FromPlane_DOUBLE(bpo.bpo_pbplPlane->bpl_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; iNgon<amfp->amfp_aangNgons.Count(); iNgon++) {
CAmfNGon &aNgon = amfp->amfp_aangNgons[iNgon];
for(INDEX iVtx=0; iVtx<aNgon.ang_cbpoVertices.Count(); iVtx++) {
CBrushVertex &bVtx = aNgon.ang_cbpoVertices[iVtx];
// calculate mapping coordinates
FLOAT3D vUV = bVtx.bvx_vRelative-mvTransform.mv_vO;
FLOAT fU = vUV % mvTransform.mv_vU;
FLOAT fV = vUV % mvTransform.mv_vV;
fU *= fMulU;
fV *= fMulV;
// fix qnans
if(!_finite(fU)) { fU=0;}
if(!_finite(fV)) { fV=0;}
strmAmf.FPrintF_t(" { %f, %f; }\n", fU, fV);
}
}
}
}}
strmAmf.PutLine_t(" }");
strmAmf.PutLine_t(" }");
}
}
strmAmf.PutLine_t(" }");
strmAmf.FPrintF_t(" VERTICES %d\n", ctTotalVertices);
strmAmf.PutLine_t(" {");
for(INDEX iVtx=0; iVtx<ctTotalVertices; iVtx++) {
if(etExportType==ET_RENDERING) {
strmAmf.FPrintF_t(" { 4: 0[%d], 1[%d], 2[%d], 3[%d];}\n", iVtx, iVtx, iVtx, iVtx);
} else {
strmAmf.FPrintF_t(" { 1: 0[%d]; }\n", iVtx);
}
}
strmAmf.PutLine_t(" }");
strmAmf.FPrintF_t(" POLYGONS %d\n", ctTotalPolygons);
strmAmf.PutLine_t(" {");
INDEX iPlgVtx = 0;
{for(INDEX iSurf=0; iSurf<cbpoSurfaces.Count(); iSurf++) {
CAmfSurface &asSurf = cbpoSurfaces[iSurf];
for(INDEX iPlg=0; iPlg<asSurf.sf_cbpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = asSurf.sf_cbpoPolygons[iPlg];
CAmfPolygon *amfp = (CAmfPolygon *)bpo.bpo_pspoScreenPolygon;
for(INDEX iNgon=0; iNgon<amfp->amfp_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; iVtx<ctPlgVertices; iVtx++) {
if(iVtx==ctPlgVertices-1) {
strmAmf.FPrintF_t("%d; }\n", iPlgVtx++);
} else {
strmAmf.FPrintF_t("%d, ", iPlgVtx++);
}
}
}
}
}
}}
strmAmf.PutLine_t(" }");
// for rendering
if(etExportType==ET_RENDERING) {
strmAmf.FPrintF_t(" POLYGON_MAPS %d\n", cbpoSurfaces.Count());
strmAmf.PutLine_t(" {");
// dump surfaces
INDEX iPlgGlobal=0;
{for(INDEX iSurf=0; iSurf<cbpoSurfaces.Count(); iSurf++) {
CAmfSurface &asSurf = cbpoSurfaces[iSurf];
CBrushPolygon &bpo = asSurf.sf_cbpoPolygons[0];
strmAmf.FPrintF_t(" POLYGON_MAP_NAME \"surface.Default_%d_%d\"\n", en.en_ulID, iSurf);
#if 1
// dump surface data
if(bCollisionOnlyBrush) {
strmAmf.PutLine_t(" POLYGON_MAP_SHADER \"\"");
} else {
strmAmf.PutLine_t(" POLYGON_MAP_SHADER \"Bin/Shaders.module|Standard\"");
}
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);
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; iPlg<ctSurfacePolygons; iPlg++) {
strmAmf.FPrintF_t(" %d;\n", iPlgGlobal++);
}
strmAmf.PutLine_t(" }");
}}
strmAmf.PutLine_t(" }");
} else if(etExportType==ET_VISIBILITY) {
// for visibility
INDEX ctSectors = pbmMip->bm_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; iSector<pbmMip->bm_abscSectors.Count(); iSector++) {
// count sector polygons
INDEX ctSectorPolygons = 0;
CBrushSector *pbs = &pbmMip->bm_abscSectors[iSector];
{for(INDEX iSurf=0; iSurf<cbpoSurfaces.Count(); iSurf++) {
CAmfSurface &asSurf = cbpoSurfaces[iSurf];
for(INDEX iPlg=0; iPlg<asSurf.sf_cbpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = asSurf.sf_cbpoPolygons[iPlg];
if(bpo.bpo_pbscSector==pbs) {
CAmfPolygon *amfp = (CAmfPolygon *)bpo.bpo_pspoScreenPolygon;
INDEX ctNgons = amfp->amfp_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; iSurf<cbpoSurfaces.Count(); iSurf++) {
CAmfSurface &asSurf = cbpoSurfaces[iSurf];
for(INDEX iPlg=0; iPlg<asSurf.sf_cbpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = asSurf.sf_cbpoPolygons[iPlg];
CAmfPolygon *amfp = (CAmfPolygon *)bpo.bpo_pspoScreenPolygon;
for(INDEX iNgon=0; iNgon<amfp->amfp_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; iPlg<ciPortals.Count(); iPlg++) {
strmAmf.FPrintF_t(" %d;\n", ciPortals[iPlg]);
}}
strmAmf.PutLine_t(" }");
}
// dump occluders
if(ciOccluders.Count()>0) {
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; iPlg<ciOccluders.Count(); iPlg++) {
strmAmf.FPrintF_t(" %d;\n", ciOccluders[iPlg]);
}}
strmAmf.PutLine_t(" }");
}
// dump classifiers
if(ciClassifiers.Count()>0) {
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; iPlg<ciClassifiers.Count(); iPlg++) {
strmAmf.FPrintF_t(" %d;\n", ciClassifiers[iPlg]);
}}
strmAmf.PutLine_t(" }");
}
strmAmf.PutLine_t(" }");
}
strmAmf.PutLine_t(" }");
}
// Tests if entity brushes are valid
BOOL IsBrushVisible(CEntity &en)
{
// fetch first mip
CBrushMip *pbmMip = en.en_pbrBrush->GetFirstMip();
if(pbmMip==NULL) {
return FALSE;
}
INDEX ctPolygons = 0;
for(INDEX iSector=0; iSector<pbmMip->bm_abscSectors.Count(); iSector++) {
CBrushSector &bs = pbmMip->bm_abscSectors[iSector];
// for each polygon in the sector
for(INDEX iPlg=0; iPlg<bs.bsc_abpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = bs.bsc_abpoPolygons[iPlg];
if(!IsPolygonVisible(bpo)) {
continue;
}
ctPolygons++;
}
}
// if there was no polygons to export
return ctPolygons>0;
}
// 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; iSector<pbmMip->bm_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; iSector<pbmMip->bm_abscSectors.Count(); iSector++) {
CBrushSector &bs = pbmMip->bm_abscSectors[iSector];
// for each polygon in the sector
for(INDEX iPlg=0; iPlg<bs.bsc_abpoPolygons.Count(); iPlg++) {
CBrushPolygon &bpo = bs.bsc_abpoPolygons[iPlg];
// convert it into ngons
CAmfPolygon *pap = new(CAmfPolygon);
pap->FromBrushPolygon(&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<CTString> 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<CEntity> 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; iProperty<pdecDLLClassCount->dec_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="<unnamed>";
}
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; iProperty<pdecDLLClass->dec_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<astrNeddedSmc.Count(); iSmc++)
{
strmSmlFile.PutLine_t(astrNeddedSmc[iSmc]);
}
AfxMessageBox(L"Entities exported!", MB_OK|MB_ICONINFORMATION);
}
catch( char *err_str)
{
AfxMessageBox( CString(err_str));
}
}