remove terrain dependency from mission area

Mission Area now captures the entire level bounds based on objects in the scene
Terrain is no longer required for mission area to be set
This commit is contained in:
marauder2k7 2025-06-15 12:41:47 +01:00
parent 6ffa5fe265
commit 0a1e3f74ed
6 changed files with 133 additions and 116 deletions

View file

@ -51,7 +51,7 @@ ConsoleDocClass( MissionArea,
"@ingroup enviroMisc\n" "@ingroup enviroMisc\n"
); );
RectI MissionArea::smMissionArea(Point2I(768, 768), Point2I(512, 512)); RectI MissionArea::smMissionArea(Point2I(-100, -100), Point2I(100, 100));
MissionArea * MissionArea::smServerObject = NULL; MissionArea * MissionArea::smServerObject = NULL;
@ -59,7 +59,7 @@ MissionArea * MissionArea::smServerObject = NULL;
MissionArea::MissionArea() MissionArea::MissionArea()
{ {
mArea.set(Point2I(768, 768), Point2I(512, 512)); mArea.set(Point2I(-100, -100), Point2I(100, 100));
mNetFlags.set(Ghostable | ScopeAlways); mNetFlags.set(Ghostable | ScopeAlways);
mFlightCeiling = 2000; mFlightCeiling = 2000;

View file

@ -29,6 +29,13 @@
#include "gui/3d/guiTSControl.h" #include "gui/3d/guiTSControl.h"
#include "T3D/gameFunctions.h" #include "T3D/gameFunctions.h"
#include "terrain/terrData.h" #include "terrain/terrData.h"
#include "gfx/gfxTransformSaver.h"
#include "scene/sceneManager.h"
#include "scene/sceneRenderState.h"
#include "lighting/lightManager.h"
#include "lighting/shadowMap/lightShadowMap.h"
#include "math/mathUtils.h"
#include "math/util/frustum.h"
namespace { namespace {
F32 round_local(F32 val) F32 round_local(F32 val)
@ -65,7 +72,8 @@ GuiMissionAreaCtrl::GuiMissionAreaCtrl()
mSquareBitmap = true; mSquareBitmap = true;
mMissionArea = 0; mMissionArea = 0;
mTerrainBlock = 0; mLevelTexture = NULL;
mLevelBounds = Box3F::Zero;
mMissionBoundsColor.set(255,0,0); mMissionBoundsColor.set(255,0,0);
mCameraColor.set(255,0,0); mCameraColor.set(255,0,0);
@ -75,6 +83,8 @@ GuiMissionAreaCtrl::GuiMissionAreaCtrl()
mLastHitMode = Handle_None; mLastHitMode = Handle_None;
mSavedDrag = false; mSavedDrag = false;
mBitmap.set(256, 256, GFXFormatR8G8B8, &GFXRenderTargetProfile, "MissionAreaRenderTarget");
} }
GuiMissionAreaCtrl::~GuiMissionAreaCtrl() GuiMissionAreaCtrl::~GuiMissionAreaCtrl()
@ -131,18 +141,7 @@ bool GuiMissionAreaCtrl::onWake()
if(!Parent::onWake()) if(!Parent::onWake())
return(false); return(false);
//mMissionArea = const_cast<MissionArea*>(MissionArea::getServerObject()); updateLevelBitmap();
//if(!bool(mMissionArea))
// Con::warnf(ConsoleLogEntry::General, "GuiMissionAreaCtrl::onWake: no MissionArea object.");
//mTerrainBlock = getTerrainObj();
//if(!bool(mTerrainBlock))
// Con::warnf(ConsoleLogEntry::General, "GuiMissionAreaCtrl::onWake: no TerrainBlock object.");
//if ( !bool(mMissionArea) || !bool(mTerrainBlock) )
// return true;
updateTerrainBitmap();
// make sure mission area is clamped // make sure mission area is clamped
setArea(getArea()); setArea(getArea());
@ -157,7 +156,6 @@ void GuiMissionAreaCtrl::onSleep()
{ {
mBitmap = NULL; mBitmap = NULL;
mMissionArea = 0; mMissionArea = 0;
mTerrainBlock = 0;
Parent::onSleep(); Parent::onSleep();
} }
@ -319,71 +317,6 @@ void GuiMissionAreaCtrl::submitUndo( const UTF8 *name )
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void GuiMissionAreaCtrl::updateTerrain()
{
mTerrainBlock = getTerrainObj();
updateTerrainBitmap();
}
TerrainBlock * GuiMissionAreaCtrl::getTerrainObj()
{
SimSet * scopeAlwaysSet = Sim::getGhostAlwaysSet();
for(SimSet::iterator itr = scopeAlwaysSet->begin(); itr != scopeAlwaysSet->end(); itr++)
{
TerrainBlock * terrain = dynamic_cast<TerrainBlock*>(*itr);
if(terrain)
return(terrain);
}
return(0);
}
void GuiMissionAreaCtrl::updateTerrainBitmap()
{
GBitmap * bitmap = createTerrainBitmap();
if( bitmap )
setBitmapHandle( GFXTexHandle( bitmap, &GFXDefaultGUIProfile, true, String("Terrain Bitmap Update") ) );
else
setBitmap( "" );
}
GBitmap * GuiMissionAreaCtrl::createTerrainBitmap()
{
if(!mTerrainBlock)
return NULL;
GBitmap * bitmap = new GBitmap(mTerrainBlock->getBlockSize(), mTerrainBlock->getBlockSize(), false, GFXFormatR8G8B8 );
// get the min/max
F32 min, max;
mTerrainBlock->getMinMaxHeight(&min, &max);
F32 diff = max - min;
F32 colRange = 255.0f / diff;
// This method allocates it's bitmap above, and does all assignment
// in the following loop. It is not subject to 24-bit -> 32-bit conversion
// problems, because the texture handle creation is where the conversion would
// occur, if it occurs. Since the data in the texture is never read back, and
// the bitmap is deleted after texture-upload, this is not a problem.
for(S32 y = 0; y < mTerrainBlock->getBlockSize() ; y++)
{
for(S32 x = 0; x < mTerrainBlock->getBlockSize(); x++)
{
F32 height;
height = mTerrainBlock->getHeight(Point2I(x, y));
U8 col = U8((height - min) * colRange);
ColorI color(col, col, col);
bitmap->setColor(x, y, color);
}
}
return(bitmap);
}
//------------------------------------------------------------------------------
void GuiMissionAreaCtrl::setMissionArea( MissionArea* area ) void GuiMissionAreaCtrl::setMissionArea( MissionArea* area )
{ {
mMissionArea = area; mMissionArea = area;
@ -393,6 +326,90 @@ void GuiMissionAreaCtrl::setMissionArea( MissionArea* area )
} }
} }
void GuiMissionAreaCtrl::updateLevelBitmap()
{
if (mLevelTexture.isNull())
mLevelTexture = GFX->allocRenderToTextureTarget();
mLevelTexture->attachTexture(GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil);
mLevelTexture->attachTexture(GFXTextureTarget::Color0, mBitmap);
mLevelBounds = Box3F::Zero;
for (SimSetIterator iter(Sim::getRootGroup()); *iter; ++iter)
{
SceneObject* obj = dynamic_cast<SceneObject*>(*iter);
if (!obj)
continue;
// Skip if bounds are too large (e.g., GroundPlane or visual-only objects)
const Box3F& box = obj->getWorldBox();
const F32 maxSizeThreshold = 2048.0f; // or tweak for your game scale
VectorF extents = box.getExtents();
if (extents.x > maxSizeThreshold || extents.y > maxSizeThreshold)
continue;
// Merge bounds
mLevelBounds.intersect(box);
}
GFXTransformSaver saver;
// Calculate orthographic dimensions
Point3F minPt = mLevelBounds.minExtents;
Point3F maxPt = mLevelBounds.maxExtents;
F32 orthoWidth = maxPt.x - minPt.x;
F32 orthoHeight = maxPt.y - minPt.y;
F32 nearPlane = 0.01f;
F32 farPlane = 1000.0f;
// Set orthographic projection centered around level bounds
GFX->setOrtho(
-orthoWidth * 0.5f, orthoWidth * 0.5f, // left/right
-orthoHeight * 0.5f, orthoHeight * 0.5f, // bottom/top
nearPlane, farPlane,
true // flip y
);
GFX->pushActiveRenderTarget();
// create camera matrix
MatrixF lightMatrix(true);
VectorF eye = mLevelBounds.getCenter();
eye.z += 500.0f;
lightMatrix.LookAt(eye, VectorF(0.0f, 0.0f, -1.0f), VectorF(0.0f, -1.0f, 0.0f));
lightMatrix.inverse();
GFX->setWorldMatrix(lightMatrix);
GFX->clearTextureStateImmediate(0);
GFX->setActiveRenderTarget(mLevelTexture);
GFX->clear(GFXClearStencil | GFXClearTarget | GFXClearZBuffer, ColorI::BLACK, 1.0f, 0);
SceneRenderState reflectRenderState
(
gClientSceneGraph,
SPT_Reflect,
SceneCameraState::fromGFX()
);
// We don't use a special clipping projection, but still need to initialize
// this for objects like SkyBox which will use it during a reflect pass.
gClientSceneGraph->setNonClipProjection(GFX->getProjectionMatrix());
// render scene
LIGHTMGR->registerGlobalLights(&reflectRenderState.getCullingFrustum(), false);
gClientSceneGraph->renderSceneNoLights(&reflectRenderState);
LIGHTMGR->unregisterAllLights();
// Clean up.
mLevelTexture->resolve();
GFX->popActiveRenderTarget();
}
const RectI & GuiMissionAreaCtrl::getArea() const RectI & GuiMissionAreaCtrl::getArea()
{ {
if( !bool(mMissionArea) ) if( !bool(mMissionArea) )
@ -503,31 +520,33 @@ Point2I GuiMissionAreaCtrl::screenDeltaToWorldDelta(const Point2I &screenPoint)
return(Point2I(S32(screenPoint.x / mScale.x), S32(screenPoint.y / mScale.y))); return(Point2I(S32(screenPoint.x / mScale.x), S32(screenPoint.y / mScale.y)));
} }
void GuiMissionAreaCtrl::setupScreenTransform(const Point2I & offset) void GuiMissionAreaCtrl::setupScreenTransform(const Point2I& offset)
{ {
const MatrixF & terrMat = mTerrainBlock->getTransform(); // Compute 2D size of the bounding box
Point3F terrPos; Point2F boxSize(mLevelBounds.len_x(), mLevelBounds.len_y());
terrMat.getColumn(3, &terrPos); Point2F boxMin(mLevelBounds.minExtents.x, mLevelBounds.minExtents.y);
terrPos.z = 0; Point2F boxCenter = Point2F(mLevelBounds.getCenter().x, mLevelBounds.getCenter().y);
F32 terrDim = mTerrainBlock->getWorldBlockSize(); // GUI control size
const Point2I& extenti = getExtent();
Point2F extent((F32)extenti.x, (F32)extenti.y);
const Point2I& extenti = getExtent( ); // Maintain square aspect ratio if requested
Point2F extent( static_cast<F32>( extenti.x ), static_cast<F32>( extenti.y ) ); if (mSquareBitmap)
if(mSquareBitmap)
extent.x > extent.y ? extent.x = extent.y : extent.y = extent.x; extent.x > extent.y ? extent.x = extent.y : extent.y = extent.x;
// We need to negate the y-axis so we are correctly oriented with // Compute scale (how many pixels per world unit)
// positive y increase up the screen. mScale.set(extent.x / boxSize.x, -extent.y / boxSize.y, 0); // Y flipped
mScale.set(extent.x / terrDim, -extent.y / terrDim, 0);
Point3F terrOffset = -terrPos; // Instead of offsetting the center to (0,0), we want to place the box center at GUI center
terrOffset.convolve(mScale); // So we compute the world-to-screen offset for center
Point2F screenCenter((F32)offset.x + extent.x * 0.5f, (F32)offset.y + extent.y * 0.5f);
// We need to add the y extent so we start from the bottom left of the control Point2F worldCenterOffset = boxCenter * Point2F(mScale.x, mScale.y);
// rather than the top left.
mCenterPos.set(terrOffset.x + F32(offset.x), terrOffset.y + F32(offset.y) + extent.y); // Compute the final offset that maps world center to screen center
mCenterPos.set(screenCenter.x - worldCenterOffset.x,
screenCenter.y - worldCenterOffset.y);
} }
void GuiMissionAreaCtrl::getScreenMissionArea(RectI & rect) void GuiMissionAreaCtrl::getScreenMissionArea(RectI & rect)
@ -575,7 +594,7 @@ void GuiMissionAreaCtrl::onRender(Point2I offset, const RectI & updateRect)
setUpdate(); setUpdate();
// draw an x // draw an x
if(!bool(mMissionArea) || !bool(mTerrainBlock)) if(!bool(mMissionArea))
{ {
GFX->setStateBlock(mSolidStateBlock); GFX->setStateBlock(mSolidStateBlock);
PrimBuild::color3i( 0, 0, 0 ); PrimBuild::color3i( 0, 0, 0 );
@ -683,12 +702,13 @@ DefineEngineMethod( GuiMissionAreaCtrl, setMissionArea, void, ( MissionArea* are
object->setMissionArea( area ); object->setMissionArea( area );
} }
DefineEngineMethod( GuiMissionAreaCtrl, updateTerrain, void, ( ),, DefineEngineMethod(GuiMissionAreaCtrl, updateLevelBitmap, void, (), ,
"@brief Update the terrain bitmap.\n\n") "@brief Update the level bitmap and bounds.\n\n")
{ {
object->updateTerrain(); object->updateLevelBitmap();
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void GuiMissionAreaUndoAction::undo() void GuiMissionAreaUndoAction::undo()

View file

@ -58,10 +58,11 @@ protected:
}; };
SimObjectPtr<MissionArea> mMissionArea; SimObjectPtr<MissionArea> mMissionArea;
SimObjectPtr<TerrainBlock> mTerrainBlock;
GFXStateBlockRef mBlendStateBlock; GFXStateBlockRef mBlendStateBlock;
GFXStateBlockRef mSolidStateBlock; GFXStateBlockRef mSolidStateBlock;
GFXTextureTargetRef mLevelTexture;
Box3F mLevelBounds;
DECLARE_IMAGEASSET(GuiMissionAreaCtrl, HandleBitmap, GFXDefaultGUIProfile) DECLARE_IMAGEASSET(GuiMissionAreaCtrl, HandleBitmap, GFXDefaultGUIProfile)
@ -82,10 +83,6 @@ protected:
void submitUndo( const UTF8 *name = "Action" ); void submitUndo( const UTF8 *name = "Action" );
TerrainBlock * getTerrainObj();
GBitmap * createTerrainBitmap();
void updateTerrainBitmap();
//void onUpdate(); //void onUpdate();
void setupScreenTransform(const Point2I & offset); void setupScreenTransform(const Point2I & offset);
@ -132,7 +129,7 @@ public:
void onMouseLeave(const GuiEvent & event) override; void onMouseLeave(const GuiEvent & event) override;
void setMissionArea( MissionArea* area ); void setMissionArea( MissionArea* area );
void updateTerrain(); void updateLevelBitmap();
const RectI & getArea(); const RectI & getArea();
void setArea(const RectI & area); void setArea(const RectI & area);

View file

@ -137,7 +137,7 @@ function MissionAreaEditorPlugin::readSettings( %this )
{ {
EditorSettings.beginGroup( "MissionAreaEditor", true ); EditorSettings.beginGroup( "MissionAreaEditor", true );
MissionAreaEditorTerrainEditor.missionBoundsColor = EditorSettings.value("MissionBoundsColor"); MissionAreaEditorLevelEditor.missionBoundsColor = EditorSettings.value("MissionBoundsColor");
EditorSettings.endGroup(); EditorSettings.endGroup();
} }
@ -146,7 +146,7 @@ function MissionAreaEditorPlugin::writeSettings( %this )
{ {
EditorSettings.beginGroup( "MissionAreaEditor", true ); EditorSettings.beginGroup( "MissionAreaEditor", true );
EditorSettings.setValue( "MissionBoundsColor", MissionAreaEditorTerrainEditor.missionBoundsColor ); EditorSettings.setValue( "MissionBoundsColor", MissionAreaEditorLevelEditor.missionBoundsColor );
EditorSettings.endGroup(); EditorSettings.endGroup();
} }

View file

@ -79,7 +79,7 @@ $guiContent = new GuiMissionAreaEditorCtrl(MissionAreaEditorGui, EditorGuiGroup)
VertSizing = "height"; VertSizing = "height";
isContainer = "1"; isContainer = "1";
new GuiMissionAreaCtrl(MissionAreaEditorTerrainEditor) { new GuiMissionAreaCtrl(MissionAreaEditorLevelEditor) {
canSaveDynamicFields = "0"; canSaveDynamicFields = "0";
isContainer = "0"; isContainer = "0";
Profile = "EditorDefaultProfile"; Profile = "EditorDefaultProfile";

View file

@ -290,7 +290,7 @@ function MissionAreaEditorGui::onEditorActivated( %this )
{ {
EWorldEditor.selectObject( %ma ); EWorldEditor.selectObject( %ma );
EWorldEditor.syncGui(); EWorldEditor.syncGui();
MissionAreaEditorTerrainEditor.updateTerrain(); MissionAreaEditorLevelEditor.updateLevelBitmap();
%this.setSelectedMissionArea( %ma ); %this.setSelectedMissionArea( %ma );
%this.onMissionAreaSelected( %this.getSelectedMissionArea() ); %this.onMissionAreaSelected( %this.getSelectedMissionArea() );
} }
@ -303,18 +303,18 @@ function MissionAreaEditorGui::onEditorDeactivated( %this )
function MissionAreaEditorGui::onMissionAreaSelected( %this, %missionArea ) function MissionAreaEditorGui::onMissionAreaSelected( %this, %missionArea )
{ {
%this.missionArea = %missionArea; %this.missionArea = %missionArea;
MissionAreaEditorTerrainEditor.setMissionArea( %missionArea ); MissionAreaEditorLevelEditor.setMissionArea( %missionArea );
MissionAreaInspector.inspect( %missionArea ); MissionAreaInspector.inspect( %missionArea );
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
function MissionAreaEditorTerrainEditor::onMissionAreaModified( %this ) function MissionAreaEditorLevelEditor::onMissionAreaModified( %this )
{ {
MissionAreaInspector.refresh(); MissionAreaInspector.refresh();
} }
function MissionAreaEditorTerrainEditor::onUndo( %this ) function MissionAreaEditorLevelEditor::onUndo( %this )
{ {
MissionAreaInspector.refresh(); MissionAreaInspector.refresh();
} }