From 0a1e3f74ed33b24be88a1661324a7cbb9154d4ad Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sun, 15 Jun 2025 12:41:47 +0100 Subject: [PATCH] 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 --- Engine/source/T3D/missionArea.cpp | 4 +- .../source/gui/worldEditor/guiMissionArea.cpp | 222 ++++++++++-------- .../source/gui/worldEditor/guiMissionArea.h | 9 +- .../game/tools/missionAreaEditor/main.tscript | 4 +- .../missionAreaEditorGui.ed.gui | 2 +- .../missionAreaEditorGui.ed.tscript | 8 +- 6 files changed, 133 insertions(+), 116 deletions(-) diff --git a/Engine/source/T3D/missionArea.cpp b/Engine/source/T3D/missionArea.cpp index 5a764fb15..c363e4e91 100644 --- a/Engine/source/T3D/missionArea.cpp +++ b/Engine/source/T3D/missionArea.cpp @@ -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; diff --git a/Engine/source/gui/worldEditor/guiMissionArea.cpp b/Engine/source/gui/worldEditor/guiMissionArea.cpp index 8963911da..a5869fc5c 100644 --- a/Engine/source/gui/worldEditor/guiMissionArea.cpp +++ b/Engine/source/gui/worldEditor/guiMissionArea.cpp @@ -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::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(*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(*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( extenti.x ), static_cast( 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() diff --git a/Engine/source/gui/worldEditor/guiMissionArea.h b/Engine/source/gui/worldEditor/guiMissionArea.h index 302bda930..d8ad9e74f 100644 --- a/Engine/source/gui/worldEditor/guiMissionArea.h +++ b/Engine/source/gui/worldEditor/guiMissionArea.h @@ -58,10 +58,11 @@ protected: }; SimObjectPtr mMissionArea; - SimObjectPtr 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); diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/main.tscript b/Templates/BaseGame/game/tools/missionAreaEditor/main.tscript index 1877f425c..3b3e5fcfc 100644 --- a/Templates/BaseGame/game/tools/missionAreaEditor/main.tscript +++ b/Templates/BaseGame/game/tools/missionAreaEditor/main.tscript @@ -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(); } diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.gui b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.gui index 276c1de42..795a193e9 100644 --- a/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.gui +++ b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.gui @@ -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"; diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.tscript b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.tscript index 126e8dae3..d3076aeeb 100644 --- a/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.tscript +++ b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.tscript @@ -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(); }