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"
);
RectI MissionArea::smMissionArea(Point2I(768, 768), Point2I(512, 512));
RectI MissionArea::smMissionArea(Point2I(-100, -100), Point2I(100, 100));
MissionArea * MissionArea::smServerObject = NULL;
@ -59,7 +59,7 @@ MissionArea * MissionArea::smServerObject = NULL;
MissionArea::MissionArea()
{
mArea.set(Point2I(768, 768), Point2I(512, 512));
mArea.set(Point2I(-100, -100), Point2I(100, 100));
mNetFlags.set(Ghostable | ScopeAlways);
mFlightCeiling = 2000;

View file

@ -29,6 +29,13 @@
#include "gui/3d/guiTSControl.h"
#include "T3D/gameFunctions.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 {
F32 round_local(F32 val)
@ -65,7 +72,8 @@ GuiMissionAreaCtrl::GuiMissionAreaCtrl()
mSquareBitmap = true;
mMissionArea = 0;
mTerrainBlock = 0;
mLevelTexture = NULL;
mLevelBounds = Box3F::Zero;
mMissionBoundsColor.set(255,0,0);
mCameraColor.set(255,0,0);
@ -75,6 +83,8 @@ GuiMissionAreaCtrl::GuiMissionAreaCtrl()
mLastHitMode = Handle_None;
mSavedDrag = false;
mBitmap.set(256, 256, GFXFormatR8G8B8, &GFXRenderTargetProfile, "MissionAreaRenderTarget");
}
GuiMissionAreaCtrl::~GuiMissionAreaCtrl()
@ -131,18 +141,7 @@ bool GuiMissionAreaCtrl::onWake()
if(!Parent::onWake())
return(false);
//mMissionArea = const_cast<MissionArea*>(MissionArea::getServerObject());
//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();
updateLevelBitmap();
// make sure mission area is clamped
setArea(getArea());
@ -157,7 +156,6 @@ void GuiMissionAreaCtrl::onSleep()
{
mBitmap = NULL;
mMissionArea = 0;
mTerrainBlock = 0;
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 )
{
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()
{
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)));
}
void GuiMissionAreaCtrl::setupScreenTransform(const Point2I & offset)
void GuiMissionAreaCtrl::setupScreenTransform(const Point2I& offset)
{
const MatrixF & terrMat = mTerrainBlock->getTransform();
Point3F terrPos;
terrMat.getColumn(3, &terrPos);
terrPos.z = 0;
// Compute 2D size of the bounding box
Point2F boxSize(mLevelBounds.len_x(), mLevelBounds.len_y());
Point2F boxMin(mLevelBounds.minExtents.x, mLevelBounds.minExtents.y);
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( );
Point2F extent( static_cast<F32>( extenti.x ), static_cast<F32>( extenti.y ) );
if(mSquareBitmap)
// Maintain square aspect ratio if requested
if (mSquareBitmap)
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
// positive y increase up the screen.
mScale.set(extent.x / terrDim, -extent.y / terrDim, 0);
// Compute scale (how many pixels per world unit)
mScale.set(extent.x / boxSize.x, -extent.y / boxSize.y, 0); // Y flipped
Point3F terrOffset = -terrPos;
terrOffset.convolve(mScale);
// Instead of offsetting the center to (0,0), we want to place the box center at GUI center
// 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
// rather than the top left.
mCenterPos.set(terrOffset.x + F32(offset.x), terrOffset.y + F32(offset.y) + extent.y);
Point2F worldCenterOffset = boxCenter * Point2F(mScale.x, mScale.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)
@ -575,7 +594,7 @@ void GuiMissionAreaCtrl::onRender(Point2I offset, const RectI & updateRect)
setUpdate();
// draw an x
if(!bool(mMissionArea) || !bool(mTerrainBlock))
if(!bool(mMissionArea))
{
GFX->setStateBlock(mSolidStateBlock);
PrimBuild::color3i( 0, 0, 0 );
@ -683,12 +702,13 @@ DefineEngineMethod( GuiMissionAreaCtrl, setMissionArea, void, ( MissionArea* are
object->setMissionArea( area );
}
DefineEngineMethod( GuiMissionAreaCtrl, updateTerrain, void, ( ),,
"@brief Update the terrain bitmap.\n\n")
DefineEngineMethod(GuiMissionAreaCtrl, updateLevelBitmap, void, (), ,
"@brief Update the level bitmap and bounds.\n\n")
{
object->updateTerrain();
object->updateLevelBitmap();
}
//------------------------------------------------------------------------------
void GuiMissionAreaUndoAction::undo()

View file

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

View file

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

View file

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

View file

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