From 34ea855031e3f696af5b05e0275d6cb537ed75fd Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 2 Feb 2026 22:59:10 -0600 Subject: [PATCH 01/16] add a (re)Generate font cache button to the profile tab also add a zeroth %sidebar-->button --- .../game/tools/guiEditor/gui/guiEditor.ed.gui | 19 +++++++++++++++++++ .../guiEditor/scripts/guiEditor.ed.tscript | 10 +++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Templates/BaseGame/game/tools/guiEditor/gui/guiEditor.ed.gui b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditor.ed.gui index 301d1e689..175cdb29b 100644 --- a/Templates/BaseGame/game/tools/guiEditor/gui/guiEditor.ed.gui +++ b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditor.ed.gui @@ -1411,6 +1411,25 @@ $guiContent = new GuiControl(GuiEditorGui, EditorGuiGroup) { }; }; + new GuiBitmapButtonCtrl() { + bitmapAsset = "ToolsModule:reset_icon_n_image"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "138 12"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "button0"; + canSaveDynamicFields = "0"; + }; new GuiBitmapButtonCtrl() { bitmapAsset = "ToolsModule:reset_icon_n_image"; groupNum = "-1"; diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditor.ed.tscript b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditor.ed.tscript index b3e89a1c9..d23d61a5f 100644 --- a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditor.ed.tscript +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditor.ed.tscript @@ -907,7 +907,7 @@ function GuiEditorTabBook::onTabSelected( %this, %text, %index ) switch$( %name ) { case "guiPage": - + %sidebar-->button0.setVisible( false ); %sidebar-->button1.setVisible( false ); %sidebar-->button2.setVisible( false ); %sidebar-->button3.setVisible( true ); @@ -922,7 +922,7 @@ function GuiEditorTabBook::onTabSelected( %this, %text, %index ) %sidebar-->button3.tooltip = "Hide Selected Control(s)"; case "profilesPage": - + %sidebar-->button0.setVisible( true ); %sidebar-->button1.setVisible( true ); %sidebar-->button2.setVisible( true ); %sidebar-->button3.setVisible( true ); @@ -942,7 +942,11 @@ function GuiEditorTabBook::onTabSelected( %this, %text, %index ) %sidebar-->button1.setBitmap( "ToolsModule:reset_icon_n_image" ); %sidebar-->button1.command = "GuiEditor.revertProfile( GuiEditorProfilesTree.getSelectedProfile() );"; - %sidebar-->button1.tooltip = "Revert Changes to the Selected Profile"; + %sidebar-->button1.tooltip = "Revert Changes to the Selected Profile"; + + %sidebar-->button0.setBitmap( "ToolsModule:iconSave_image" ); + %sidebar-->button0.command = "populateAllFonts();"; + %sidebar-->button0.tooltip = "(Re)Generate font cache"; case "toolboxPage": From 6f0c31468f6c71fb28bbd92b8b3042dfdabb2fbf Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Tue, 17 Feb 2026 00:13:10 +0000 Subject: [PATCH 02/16] various fixes added fix for font cache (stb required size to be read switch back to libpng knocks the stream off) Added extra control over the rendering of guibtimaps If guibitmaps are a child of buttons the profile colors now affect how to the button is rendered. --- Engine/source/gfx/gFont.cpp | 4 +- .../source/gui/buttons/guiButtonBaseCtrl.cpp | 9 +- Engine/source/gui/buttons/guiButtonBaseCtrl.h | 1 + Engine/source/gui/controls/guiBitmapCtrl.cpp | 148 ++++++++++++++---- Engine/source/gui/controls/guiBitmapCtrl.h | 14 ++ 5 files changed, 143 insertions(+), 33 deletions(-) diff --git a/Engine/source/gfx/gFont.cpp b/Engine/source/gfx/gFont.cpp index 05a3f55ac..a4d349af1 100644 --- a/Engine/source/gfx/gFont.cpp +++ b/Engine/source/gfx/gFont.cpp @@ -705,10 +705,8 @@ bool GFont::read(Stream& io_rStream) delete bmp; return false; }*/ - U32 len; - io_rStream.read(&len); - if (!bmp->readBitmapStream("png", io_rStream, len)) + if (!bmp->readBitmapStream("png", io_rStream, U32_MAX)) { delete bmp; return false; diff --git a/Engine/source/gui/buttons/guiButtonBaseCtrl.cpp b/Engine/source/gui/buttons/guiButtonBaseCtrl.cpp index 2a545ffe1..20e42484c 100644 --- a/Engine/source/gui/buttons/guiButtonBaseCtrl.cpp +++ b/Engine/source/gui/buttons/guiButtonBaseCtrl.cpp @@ -303,7 +303,10 @@ void GuiButtonBaseCtrl::onMouseEnter(const GuiEvent& event) SFX->playOnce(mProfile->getSoundButtonOverProfile()); mHighlighted = true; - messageSiblings(mRadioGroup); + + if (mButtonType != ButtonTypeRadio) + messageSiblings(mRadioGroup); + onHighlighted_callback(mHighlighted); } } @@ -320,7 +323,9 @@ void GuiButtonBaseCtrl::onMouseLeave(const GuiEvent&) mDepressed = false; mHighlighted = false; onHighlighted_callback(mHighlighted); - messageSiblings(mRadioGroup); + + if (mButtonType != ButtonTypeRadio) + messageSiblings(mRadioGroup); } //----------------------------------------------------------------------------- diff --git a/Engine/source/gui/buttons/guiButtonBaseCtrl.h b/Engine/source/gui/buttons/guiButtonBaseCtrl.h index cf9566f0c..8ef4331ba 100644 --- a/Engine/source/gui/buttons/guiButtonBaseCtrl.h +++ b/Engine/source/gui/buttons/guiButtonBaseCtrl.h @@ -96,6 +96,7 @@ public: bool getStateOn() const { return mStateOn; } void setDepressed(bool depressed) { mDepressed = depressed; } + bool isDepressed() { return mDepressed; } void resetState() { mDepressed = false; mHighlighted = false; } void setHighlighted(bool highlighted); diff --git a/Engine/source/gui/controls/guiBitmapCtrl.cpp b/Engine/source/gui/controls/guiBitmapCtrl.cpp index fe7aa7862..98a9232f2 100644 --- a/Engine/source/gui/controls/guiBitmapCtrl.cpp +++ b/Engine/source/gui/controls/guiBitmapCtrl.cpp @@ -28,6 +28,8 @@ #include "console/engineAPI.h" #include "gfx/gfxDevice.h" #include "gfx/gfxDrawUtil.h" +#include "gfx/gfxAPI.h" +#include "gui/buttons/guiButtonBaseCtrl.h" #include "materials/matTextureTarget.h" @@ -55,11 +57,26 @@ ConsoleDocClass(GuiBitmapCtrl, "@ingroup GuiControls" ); +ImplementEnumType(BitmapDrawMode, + "Draw Mode of the bitmap control.\n\n" + "@ingroup GuiControls") +{ + GuiBitmapCtrl::BitmapMode_Stretch, "Stretch", "Stretches the bitmap to fill the control extents. Aspect ratio is not preserved." +}, +{ GuiBitmapCtrl::BitmapMode_Tile, "Tile", "Repeats the bitmap to fill the control extents without scaling." }, +{ GuiBitmapCtrl::BitmapMode_Fit, "Fit", "Scales the bitmap to fit entirely within the control while preserving aspect ratio. May result in letterboxing." }, +{ GuiBitmapCtrl::BitmapMode_Fill, "Fill", "Scales the bitmap to completely fill the control while preserving aspect ratio. Portions of the bitmap may be cropped." }, +{ GuiBitmapCtrl::BitmapMode_Center, "Center", "Draws the bitmap at its original size centered within the control. No scaling is applied." }, + +EndImplementEnumType; + GuiBitmapCtrl::GuiBitmapCtrl(void) : mStartPoint(0, 0), mColor(ColorI::WHITE), mAngle(0), mWrap(false), + mDrawMode(BitmapMode_Stretch), + mFilterType(GFXTextureFilterLinear), mBitmap(NULL), mBitmapName(StringTable->EmptyString()) { @@ -72,10 +89,12 @@ void GuiBitmapCtrl::initPersistFields() INITPERSISTFIELD_IMAGEASSET(Bitmap, GuiBitmapCtrl, "The bitmap to render in this BitmapCtrl.") - addField("color", TypeColorI, Offset(mColor, GuiBitmapCtrl), "color mul"); + addField("color", TypeColorI, Offset(mColor, GuiBitmapCtrl), "color mul"); addField("wrap", TypeBool, Offset(mWrap, GuiBitmapCtrl), "If true, the bitmap is tiled inside the control rather than stretched to fit."); + addField("drawMode", TYPEID(), Offset(mDrawMode, GuiBitmapCtrl), "Sets the mode to draw this bitmap in this control (wrap forces Tile mode)."); + addField("filterType", TYPEID(), Offset(mFilterType, GuiBitmapCtrl), "Sets the bitmap texture filter mode."); addFieldV("angle", TypeRangedF32, Offset(mAngle, GuiBitmapCtrl), &CommonValidators::DegreeRange, "rotation"); - endGroup( "Bitmap" ); + endGroup("Bitmap"); Parent::initPersistFields(); } @@ -91,6 +110,7 @@ bool GuiBitmapCtrl::onWake() void GuiBitmapCtrl::onSleep() { + mBitmap = NULL; Parent::onSleep(); } @@ -171,36 +191,108 @@ void GuiBitmapCtrl::onRender(Point2I offset, const RectI& updateRect) { GFX->getDrawUtil()->clearBitmapModulation(); GFX->getDrawUtil()->setBitmapModulation(mColor); - if (mWrap) - { - // We manually draw each repeat because non power of two textures will - // not tile correctly when rendered with GFX->drawBitmapTile(). The non POT - // bitmap will be padded by the hardware, and we'll see lots of slack - // in the texture. So... lets do what we must: draw each repeat by itself: - GFXTextureObject* texture = mBitmap; - RectI srcRegion; - RectI dstRegion; - F32 xdone = ((F32)getExtent().x / (F32)texture->mBitmapSize.x) + 1; - F32 ydone = ((F32)getExtent().y / (F32)texture->mBitmapSize.y) + 1; - S32 xshift = mStartPoint.x % texture->mBitmapSize.x; - S32 yshift = mStartPoint.y % texture->mBitmapSize.y; - for (S32 y = 0; y < ydone; ++y) - for (S32 x = 0; x < xdone; ++x) - { - srcRegion.set(0, 0, texture->mBitmapSize.x, texture->mBitmapSize.y); - dstRegion.set(((texture->mBitmapSize.x * x) + offset.x) - xshift, - ((texture->mBitmapSize.y * y) + offset.y) - yshift, - texture->mBitmapSize.x, - texture->mBitmapSize.y); - GFX->getDrawUtil()->drawBitmapStretchSR(texture, dstRegion, srcRegion, GFXBitmapFlip_None, GFXTextureFilterLinear, mAngle); - } + if (dynamic_cast(getParent())) + { + GuiButtonBaseCtrl* parent = static_cast(getParent()); + + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFillColor); + + if (parent->isHighlighted()) + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFillColorHL); + if (parent->isDepressed() || parent->getStateOn()) + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFillColorSEL); + if (!parent->isActive()) + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFillColorNA); } - else + + if (mWrap && mDrawMode != BitmapMode_Tile) + mDrawMode = BitmapMode_Tile; + + RectI ctrlRect(offset, getExtent()); + GFXTextureObject* texture = mBitmap; + + F32 texW = (F32)texture->getWidth(); + F32 texH = (F32)texture->getHeight(); + F32 ctrlW = (F32)getExtent().x; + F32 ctrlH = (F32)getExtent().y; + + switch (mDrawMode) { - RectI rect(offset, getExtent()); - GFX->getDrawUtil()->drawBitmapStretch(mBitmap, rect, GFXBitmapFlip_None, GFXTextureFilterLinear, false, mAngle); + case BitmapMode_Tile: + { + // How many repetitions needed (+1 ensures full coverage) + const S32 xCount = (ctrlW / texW) + 1; + const S32 yCount = (ctrlH / texH) + 1; + + // Scroll offset (wrap-safe) + const S32 xShift = mStartPoint.x % (S32)texW; + const S32 yShift = mStartPoint.y % (S32)texH; + + RectI srcRegion(0, 0, (S32)texW, (S32)texH); + RectI dstRegion; + + for (S32 y = 0; y < yCount; ++y) + { + for (S32 x = 0; x < xCount; ++x) + { + dstRegion.set( + offset.x + (x * texW) - xShift, + offset.y + (y * texH) - yShift, + texW, + texH + ); + + GFX->getDrawUtil()->drawBitmapStretchSR(texture, dstRegion, srcRegion, GFXBitmapFlip_None, mFilterType, true, mAngle); + } + } + + break; + } + case GuiBitmapCtrl::BitmapMode_Fit: + { + F32 scale = getMin(ctrlW / texW, ctrlH / texH); + + S32 drawW = (S32)(texW * scale); + S32 drawH = (S32)(texH * scale); + + S32 x = offset.x + (ctrlW - drawW) * 0.5f; + S32 y = offset.y + (ctrlH - drawH) * 0.5f; + + RectI dst(x, y, drawW, drawH); + GFX->getDrawUtil()->drawBitmapStretch(texture, dst, GFXBitmapFlip_None, mFilterType, false, mAngle); + break; + } + case GuiBitmapCtrl::BitmapMode_Fill: + { + F32 scale = getMax(ctrlW / texW, ctrlH / texH); + + S32 drawW = (S32)(texW * scale); + S32 drawH = (S32)(texH * scale); + + S32 x = offset.x + (ctrlW - drawW) * 0.5f; + S32 y = offset.y + (ctrlH - drawH) * 0.5f; + + RectI dst(x, y, drawW, drawH); + GFX->getDrawUtil()->drawBitmapStretch(texture, dst, GFXBitmapFlip_None, mFilterType, false, mAngle); + break; + } + case GuiBitmapCtrl::BitmapMode_Center: + { + S32 x = offset.x + (ctrlW - texW) * 0.5f; + S32 y = offset.y + (ctrlH - texH) * 0.5f; + + RectI dst(x, y, texW, texH); + + GFX->getDrawUtil()->drawBitmapStretch(texture, dst, GFXBitmapFlip_None, mFilterType, false, mAngle); + break; + } + default: + { + GFX->getDrawUtil()->drawBitmapStretch(texture, ctrlRect, GFXBitmapFlip_None, mFilterType, false, mAngle); + break; + } } } diff --git a/Engine/source/gui/controls/guiBitmapCtrl.h b/Engine/source/gui/controls/guiBitmapCtrl.h index adf6f5322..ab44fdec2 100644 --- a/Engine/source/gui/controls/guiBitmapCtrl.h +++ b/Engine/source/gui/controls/guiBitmapCtrl.h @@ -35,6 +35,15 @@ public: typedef GuiControl Parent; + enum BitmapMode + { + BitmapMode_Stretch, + BitmapMode_Tile, + BitmapMode_Fit, + BitmapMode_Fill, + BitmapMode_Center + }; + protected: /// Name of the bitmap file. If this is 'texhandle' the bitmap is not loaded @@ -47,6 +56,8 @@ protected: /// If true, bitmap tiles inside control. Otherwise stretches. bool mWrap; + BitmapMode mDrawMode; + GFXTextureFilterType mFilterType; public: GFXTexHandle mBitmap; @@ -74,4 +85,7 @@ public: "The bitmap can either be tiled or stretched inside the control."); }; +typedef GuiBitmapCtrl::BitmapMode BitmapDrawMode; +DefineEnumType(BitmapDrawMode); + #endif From 562756a306f400ab406c04500d64713aef474c95 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Tue, 17 Feb 2026 00:23:53 +0000 Subject: [PATCH 03/16] Update guiBitmapCtrl.cpp --- Engine/source/gui/controls/guiBitmapCtrl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/gui/controls/guiBitmapCtrl.cpp b/Engine/source/gui/controls/guiBitmapCtrl.cpp index 98a9232f2..e572233f0 100644 --- a/Engine/source/gui/controls/guiBitmapCtrl.cpp +++ b/Engine/source/gui/controls/guiBitmapCtrl.cpp @@ -89,7 +89,7 @@ void GuiBitmapCtrl::initPersistFields() INITPERSISTFIELD_IMAGEASSET(Bitmap, GuiBitmapCtrl, "The bitmap to render in this BitmapCtrl.") - addField("color", TypeColorI, Offset(mColor, GuiBitmapCtrl), "color mul"); + addField("color", TypeColorI, Offset(mColor, GuiBitmapCtrl), "color mul"); addField("wrap", TypeBool, Offset(mWrap, GuiBitmapCtrl), "If true, the bitmap is tiled inside the control rather than stretched to fit."); addField("drawMode", TYPEID(), Offset(mDrawMode, GuiBitmapCtrl), "Sets the mode to draw this bitmap in this control (wrap forces Tile mode)."); addField("filterType", TYPEID(), Offset(mFilterType, GuiBitmapCtrl), "Sets the bitmap texture filter mode."); From 01b10cfb8904492d4641064a962fd45514b27817 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 19 Feb 2026 14:46:19 -0600 Subject: [PATCH 04/16] make reflectors optional don't waste memory per object-instance on non reflective objects --- Engine/source/T3D/shapeBase.cpp | 39 +++++++++++++++++++++++---------- Engine/source/T3D/shapeBase.h | 2 +- Engine/source/T3D/tsStatic.cpp | 36 +++++++++++++++++++++--------- Engine/source/T3D/tsStatic.h | 2 +- 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 729271fae..96fadaf6e 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -975,6 +975,7 @@ ShapeBase::ShapeBase() mMoveMotion( false ), mShapeBaseMount( NULL ), mShapeInstance( NULL ), + mCubeReflector(NULL), mConvexList( new Convex ), mEnergy( 0.0f ), mRechargeRate( 0.0f ), @@ -1053,6 +1054,12 @@ ShapeBase::~ShapeBase() mShapeInstance = NULL; } + if (mCubeReflector) + { + mCubeReflector->unregisterReflector(); + SAFE_DELETE(mCubeReflector); + } + CollisionTimeout* ptr = mTimeoutList; while (ptr) { CollisionTimeout* cur = ptr; @@ -1173,7 +1180,11 @@ void ShapeBase::onRemove() if ( isClientObject() ) { - mCubeReflector.unregisterReflector(); + if (mCubeReflector) + { + mCubeReflector->unregisterReflector(); + SAFE_DELETE(mCubeReflector); + } } } @@ -1349,11 +1360,17 @@ bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload ) mLightPlugin->reset(); if ( isClientObject() ) - { - mCubeReflector.unregisterReflector(); - - if ( mDataBlock->reflectorDesc ) - mCubeReflector.registerReflector( this, mDataBlock->reflectorDesc ); + { + if (mCubeReflector) + { + mCubeReflector->unregisterReflector(); + SAFE_DELETE(mCubeReflector); + } + if (mDataBlock->reflectorDesc) + { + mCubeReflector = new CubeReflector(); + mCubeReflector->registerReflector(this, mDataBlock->reflectorDesc); + } } return true; @@ -2681,7 +2698,7 @@ void ShapeBase::_prepRenderImage( SceneRenderState *state, // If we're currently rendering our own reflection we // don't want to render ourselves into it. - if ( mCubeReflector.isRendering() ) + if (mCubeReflector && mCubeReflector->isRendering()) return; // We force all the shapes to use the highest detail @@ -2785,8 +2802,8 @@ void ShapeBase::prepBatchRender(SceneRenderState* state, S32 mountedImageIndex ) // Set up our TS render state. TSRenderState rdata; rdata.setSceneState( state ); - if ( mCubeReflector.isEnabled() ) - rdata.setCubemap( mCubeReflector.getCubemap() ); + if (mCubeReflector && mCubeReflector->isEnabled()) + rdata.setCubemap( mCubeReflector->getCubemap() ); rdata.setFadeOverride( (1.0f - mCloakLevel) * mFadeVal ); // We might have some forward lit materials @@ -2810,14 +2827,14 @@ void ShapeBase::prepBatchRender(SceneRenderState* state, S32 mountedImageIndex ) mat.scale( mObjScale ); GFX->setWorldMatrix( mat ); - if ( state->isDiffusePass() && mCubeReflector.isEnabled() && mCubeReflector.getOcclusionQuery() ) + if ( state->isDiffusePass() && mCubeReflector && mCubeReflector->isEnabled() && mCubeReflector->getOcclusionQuery() ) { RenderPassManager *pass = state->getRenderPass(); OccluderRenderInst *ri = pass->allocInst(); ri->type = RenderPassManager::RIT_Occluder; - ri->query = mCubeReflector.getOcclusionQuery(); + ri->query = mCubeReflector->getOcclusionQuery(); mObjToWorld.mulP( mObjBox.getCenter(), &ri->position ); ri->scale.set( mObjBox.getExtents() ); ri->orientation = pass->allocUniqueXform( mObjToWorld ); diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index a8807a026..71967bbe1 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -1207,7 +1207,7 @@ public: static F32 sFullCorrectionDistance; static F32 sCloakSpeed; // Time to cloak, in seconds - CubeReflector mCubeReflector; + CubeReflector* mCubeReflector; /// @name Initialization /// @{ diff --git a/Engine/source/T3D/tsStatic.cpp b/Engine/source/T3D/tsStatic.cpp index 329485b4f..c244d25c3 100644 --- a/Engine/source/T3D/tsStatic.cpp +++ b/Engine/source/T3D/tsStatic.cpp @@ -109,7 +109,8 @@ F32 TSStatic::smStaticObjectUnfadeableSize = 75; TSStatic::TSStatic() : cubeDescId(0), - reflectorDesc(NULL) + reflectorDesc(NULL), + mCubeReflector(NULL) { mNetFlags.set(Ghostable | ScopeAlways); @@ -159,6 +160,11 @@ TSStatic::~TSStatic() delete mConvexList; mConvexList = NULL; mShapeAsset.unregisterRefreshNotify(); + if (mCubeReflector) + { + mCubeReflector->unregisterReflector(); + SAFE_DELETE(mCubeReflector); + } } ImplementEnumType(TSMeshType, @@ -361,10 +367,17 @@ bool TSStatic::onAdd() if (isClientObject()) { - mCubeReflector.unregisterReflector(); + if (mCubeReflector) + { + mCubeReflector->unregisterReflector(); + SAFE_DELETE(mCubeReflector); + } if (reflectorDesc) - mCubeReflector.registerReflector(this, reflectorDesc); + { + mCubeReflector = new CubeReflector(); + mCubeReflector->registerReflector(this, reflectorDesc); + } } _updateShouldTick(); @@ -594,8 +607,11 @@ void TSStatic::onRemove() mShapeInstance = NULL; mAmbientThread = NULL; - if (isClientObject()) - mCubeReflector.unregisterReflector(); + if (isClientObject() && mCubeReflector) + { + mCubeReflector->unregisterReflector(); + SAFE_DELETE(mCubeReflector); + } Parent::onRemove(); } @@ -780,7 +796,7 @@ void TSStatic::prepRenderImage(SceneRenderState* state) // If we're currently rendering our own reflection we // don't want to render ourselves into it. - if (mCubeReflector.isRendering()) + if (mCubeReflector && mCubeReflector->isRendering()) return; @@ -800,8 +816,8 @@ void TSStatic::prepRenderImage(SceneRenderState* state) rdata.setFadeOverride(1.0f); rdata.setOriginSort(mUseOriginSort); - if (mCubeReflector.isEnabled()) - rdata.setCubemap(mCubeReflector.getCubemap()); + if (mCubeReflector && mCubeReflector->isEnabled()) + rdata.setCubemap(mCubeReflector->getCubemap()); // Acculumation rdata.setAccuTex(mAccuTex); @@ -830,13 +846,13 @@ void TSStatic::prepRenderImage(SceneRenderState* state) mat.scale(mObjScale); GFX->setWorldMatrix(mat); - if (state->isDiffusePass() && mCubeReflector.isEnabled() && mCubeReflector.getOcclusionQuery()) + if (state->isDiffusePass() && mCubeReflector && mCubeReflector->isEnabled() && mCubeReflector->getOcclusionQuery()) { RenderPassManager* pass = state->getRenderPass(); OccluderRenderInst* ri = pass->allocInst(); ri->type = RenderPassManager::RIT_Occluder; - ri->query = mCubeReflector.getOcclusionQuery(); + ri->query = mCubeReflector->getOcclusionQuery(); mObjToWorld.mulP(mObjBox.getCenter(), &ri->position); ri->scale.set(mObjBox.getExtents()); ri->orientation = pass->allocUniqueXform(mObjToWorld); diff --git a/Engine/source/T3D/tsStatic.h b/Engine/source/T3D/tsStatic.h index 150aa59cf..7ab21ba0a 100644 --- a/Engine/source/T3D/tsStatic.h +++ b/Engine/source/T3D/tsStatic.h @@ -184,7 +184,7 @@ protected: String cubeDescName; U32 cubeDescId; ReflectorDesc* reflectorDesc; - CubeReflector mCubeReflector; + CubeReflector* mCubeReflector; void onAssetRefreshed(AssetPtrBase* pAssetPtrBase) override { From 57890a432764fe8df457c66c1d49054c48450559 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 21 Feb 2026 09:29:33 -0600 Subject: [PATCH 05/16] don't spool up new helper proxies if the goals are unchanged --- Engine/source/T3D/AI/AIAimTarget.cpp | 4 ++- Engine/source/T3D/AI/AIController.cpp | 47 ++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.cpp b/Engine/source/T3D/AI/AIAimTarget.cpp index 35c7d927e..86ea7145b 100644 --- a/Engine/source/T3D/AI/AIAimTarget.cpp +++ b/Engine/source/T3D/AI/AIAimTarget.cpp @@ -194,7 +194,9 @@ DefineEngineMethod(AIController, getAimObject, S32, (), , "@see setAimObject()\n") { - SceneObject* obj = dynamic_cast(object->getAim()->mObj.getPointer()); + SceneObject* obj = NULL; + if (object->getAim()) + obj = dynamic_cast(object->getAim()->mObj.getPointer()); return obj ? obj->getId() : -1; } diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 8ace880f1..d38eb4bfd 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -70,7 +70,12 @@ bool AIController::setControllerDataProperty(void* obj, const char* index, const void AIController::setGoal(AIInfo* targ) { - if (mGoal) { delete(mGoal); mGoal = NULL; } + if (mGoal) + { + if (mGoal->mObj.isValid() && targ->mObj.isValid() && mGoal->mObj == targ->mObj) + return; + delete(mGoal); mGoal = NULL; + } if (targ->mObj.isValid()) { @@ -86,26 +91,58 @@ void AIController::setGoal(AIInfo* targ) void AIController::setGoal(Point3F loc, F32 rad) { - if (mGoal) delete(mGoal); + if (mGoal) + { + if (mGoal->mPosSet && mGoal->getPosition() == loc) + { + mGoal->mRadius = rad; + return; + } + delete(mGoal); + } mGoal = new AIGoal(this, loc, rad); } void AIController::setGoal(SimObjectPtr objIn, F32 rad) { - if (mGoal) delete(mGoal); + if (mGoal) + { + if (mGoal->mObj.isValid() && objIn.isValid() && mGoal->mObj == objIn) + { + mGoal->mRadius = rad; + return; + } + delete(mGoal); + } mGoal = new AIGoal(this, objIn, rad); } void AIController::setAim(Point3F loc, F32 rad, Point3F offset) { - if (mAimTarget) delete(mAimTarget); + if (mAimTarget) + { + if (mAimTarget->mPosSet && mAimTarget->getPosition() == loc) + { + mAimTarget->mAimOffset = offset; + return; + } + delete(mAimTarget); + } mAimTarget = new AIAimTarget(this, loc, rad); mAimTarget->mAimOffset = offset; } void AIController::setAim(SimObjectPtr objIn, F32 rad, Point3F offset) { - if (mAimTarget) delete(mAimTarget); + if (mAimTarget) + { + if (mAimTarget->mObj.isValid() && objIn.isValid() && mAimTarget->mObj == objIn) + { + mAimTarget->mAimOffset = offset; + return; + } + delete(mAimTarget); + } mAimTarget = new AIAimTarget(this, objIn, rad); mAimTarget->mAimOffset = offset; } From 8e7af0bf55c7604943e39f216b402eeccb451508 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 21 Feb 2026 09:59:15 -0600 Subject: [PATCH 06/16] don't spool up new cover proxies if those are unchanged either --- Engine/source/T3D/AI/AIController.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index dcc46f661..361e4f518 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -70,8 +70,24 @@ public: private: AICover* mCover; public: - void setCover(Point3F loc, F32 rad = 0.0f) { delete(mCover); mCover = new AICover(this, loc, rad); } - void setCover(SimObjectPtr objIn, F32 rad = 0.0f) { delete(mCover); mCover = new AICover(this, objIn, rad); } + void setCover(Point3F loc, F32 rad = 0.0f) + { + if (mCover && mCover->mPosSet && mCover->getPosition() == loc) + { + mCover->mRadius == rad; + return; + } + delete(mCover); mCover = new AICover(this, loc, rad); + } + void setCover(SimObjectPtr objIn, F32 rad = 0.0f) + { + if (mCover && mCover->mObj == objIn) + { + mCover->mRadius == rad; + return; + } + delete(mCover); mCover = new AICover(this, objIn, rad); + } AICover* getCover() { return mCover; } bool findCover(const Point3F& from, F32 radius); void clearCover(); From 171211c4e2c8db036d5d33cf8d8f190a1748debd Mon Sep 17 00:00:00 2001 From: JeffR Date: Sat, 21 Feb 2026 11:03:07 -0600 Subject: [PATCH 07/16] Caches the processed line in CodeBlock for better assert reporting Adds additional context on eval and evaluate calls to better isolate script executions that are throwing errors, such as via command fields or eval() calls. --- Engine/source/T3D/trigger.cpp | 16 +++++++++++++--- Engine/source/console/consoleFunctions.cpp | 3 +-- Engine/source/console/torquescript/astNodes.cpp | 3 ++- Engine/source/console/torquescript/codeBlock.cpp | 2 ++ Engine/source/console/torquescript/codeBlock.h | 1 + Engine/source/console/torquescript/compiler.cpp | 9 ++++++--- .../source/gui/controls/guiGameListMenuCtrl.cpp | 4 +++- .../source/gui/controls/guiGameSettingsCtrl.cpp | 4 +++- Engine/source/gui/controls/guiListBoxCtrl.cpp | 4 +++- Engine/source/gui/controls/guiMLTextEditCtrl.cpp | 4 +++- Engine/source/gui/core/guiControl.cpp | 4 +++- Engine/source/sim/actionMap.cpp | 8 ++++++-- 12 files changed, 46 insertions(+), 16 deletions(-) diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp index 9df293ba5..5df83f681 100644 --- a/Engine/source/T3D/trigger.cpp +++ b/Engine/source/T3D/trigger.cpp @@ -742,7 +742,10 @@ void Trigger::potentialEnterObject(GameBase* enter) { String command = String("%obj = ") + enter->getIdString() + ";"; command = command + String("%this = ") + getIdString() + ";" + mEnterCommand; - Con::evaluate(command.c_str()); + + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getIdString(); + String context = String::ToString("%s, %s", getGroup(), objectName); + Con::evaluate(command.c_str(), false, context); } if( mDataBlock && testTrippable() && testCondition()) @@ -791,7 +794,10 @@ void Trigger::processTick(const Move* move) { String command = String("%obj = ") + remove->getIdString() + ";"; command = command + String("%this = ") + getIdString() + ";" + mLeaveCommand; - Con::evaluate(command.c_str()); + + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getIdString(); + String context = String::ToString("%s, %s", getGroup(), objectName); + Con::evaluate(command.c_str(), false, context); } if (testTrippable() && testCondition()) mDataBlock->onLeaveTrigger_callback( this, remove ); @@ -800,7 +806,11 @@ void Trigger::processTick(const Move* move) } if (evalCmD(&mTickCommand)) - Con::evaluate(mTickCommand.c_str()); + { + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getIdString(); + String context = String::ToString("%s, %s", getGroup(), objectName); + Con::evaluate(mTickCommand.c_str(), false, context); + } if (mObjects.size() != 0 && testTrippable() && testCondition()) mDataBlock->onTickTrigger_callback( this ); diff --git a/Engine/source/console/consoleFunctions.cpp b/Engine/source/console/consoleFunctions.cpp index 77f1e78c2..5da712e5d 100644 --- a/Engine/source/console/consoleFunctions.cpp +++ b/Engine/source/console/consoleFunctions.cpp @@ -2367,8 +2367,7 @@ DefineEngineFunction( exec, bool, ( const char* fileName, bool noCalls, bool jou DefineEngineFunction( eval, const char*, ( const char* consoleString, bool echo ), (false), "eval(consoleString)") { - Con::EvalResult returnValue = Con::evaluate(consoleString, echo, NULL); - + Con::EvalResult returnValue = Con::evaluate(consoleString, echo, Platform::makeRelativePathName(Con::getCurrentScriptModulePath(), NULL)); return Con::getReturnBuffer(returnValue.value.getString()); } diff --git a/Engine/source/console/torquescript/astNodes.cpp b/Engine/source/console/torquescript/astNodes.cpp index 6fd28bc58..e1f3f6224 100644 --- a/Engine/source/console/torquescript/astNodes.cpp +++ b/Engine/source/console/torquescript/astNodes.cpp @@ -59,7 +59,8 @@ inline FuncVars* getFuncVars(S32 lineNumber) { if (gFuncVars == &gGlobalScopeFuncVars) { - const char* str = avar("Attemping to use local variable in global scope. File: %s Line: %d", CodeBlock::smCurrentParser->getCurrentFile(), lineNumber); + const char* lineTxt = CodeBlock::smCurrentLineText; + const char* str = avar("Attemping to use local variable in global scope. File: %s Line Num: %d \nLine: %s", CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineTxt); scriptErrorHandler(str); } return gFuncVars; diff --git a/Engine/source/console/torquescript/codeBlock.cpp b/Engine/source/console/torquescript/codeBlock.cpp index 5a4bdb30a..cd324767d 100644 --- a/Engine/source/console/torquescript/codeBlock.cpp +++ b/Engine/source/console/torquescript/codeBlock.cpp @@ -35,6 +35,7 @@ using namespace Compiler; bool CodeBlock::smInFunction = false; CodeBlock * CodeBlock::smCodeBlockList = NULL; +StringTableEntry CodeBlock::smCurrentLineText = StringTable->EmptyString(); TorqueScriptParser *CodeBlock::smCurrentParser = NULL; extern FuncVars gEvalFuncVars; @@ -578,6 +579,7 @@ Con::EvalResult CodeBlock::compileExec(StringTableEntry fileName, const char *in consoleAllocReset(); name = fileName; + smCurrentLineText = inString; if (fileName) { diff --git a/Engine/source/console/torquescript/codeBlock.h b/Engine/source/console/torquescript/codeBlock.h index b9d7dd207..cef6d2683 100644 --- a/Engine/source/console/torquescript/codeBlock.h +++ b/Engine/source/console/torquescript/codeBlock.h @@ -61,6 +61,7 @@ private: public: static bool smInFunction; static TorqueScriptParser * smCurrentParser; + static StringTableEntry smCurrentLineText; static CodeBlock *getCodeBlockList() { diff --git a/Engine/source/console/torquescript/compiler.cpp b/Engine/source/console/torquescript/compiler.cpp index 51bf7c0c5..24e9135cc 100644 --- a/Engine/source/console/torquescript/compiler.cpp +++ b/Engine/source/console/torquescript/compiler.cpp @@ -160,7 +160,8 @@ S32 FuncVars::assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, if (found->second.isConstant) { - const char* str = avar("Script Warning: Reassigning variable %s when it is a constant. File: %s Line : %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber); + const char* lineTxt = CodeBlock::smCurrentLineText; + const char* str = avar("Script Warning: Reassigning variable %s when it is a constant. File: %s Line Num: %d \nLine: %s", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineTxt); scriptErrorHandler(str); } return found->second.reg; @@ -179,7 +180,8 @@ S32 FuncVars::lookup(StringTableEntry var, S32 lineNumber) if (found == vars.end()) { - const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber); + const char* lineTxt = CodeBlock::smCurrentLineText; + const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s Line Num: %d \nLine: %s", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineTxt); scriptErrorHandler(str); return assign(var, TypeReqString, lineNumber, false); @@ -194,7 +196,8 @@ TypeReq FuncVars::lookupType(StringTableEntry var, S32 lineNumber) if (found == vars.end()) { - const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s Line: %d", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber); + const char* lineText = CodeBlock::smCurrentLineText; + const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s Line Num: %d \nLine: %s", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineText); scriptErrorHandler(str); assign(var, TypeReqString, lineNumber, false); diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp index fb69fbc71..a81551d0e 100644 --- a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp +++ b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp @@ -974,7 +974,9 @@ void GuiGameListMenuCtrl::doScriptCommand(StringTableEntry command) if (command && command[0]) { setThisControl(); - Con::evaluate(command, false, __FILE__); + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); + String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + Con::evaluate(command, false, context); } } diff --git a/Engine/source/gui/controls/guiGameSettingsCtrl.cpp b/Engine/source/gui/controls/guiGameSettingsCtrl.cpp index 0b1e7ebea..ea3fd2ece 100644 --- a/Engine/source/gui/controls/guiGameSettingsCtrl.cpp +++ b/Engine/source/gui/controls/guiGameSettingsCtrl.cpp @@ -494,7 +494,9 @@ void GuiGameSettingsCtrl::doScriptCommand(StringTableEntry command) if (command && command[0]) { setThisControl(); - Con::evaluate(command, false, __FILE__); + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); + String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + Con::evaluate(command, false, context); } } diff --git a/Engine/source/gui/controls/guiListBoxCtrl.cpp b/Engine/source/gui/controls/guiListBoxCtrl.cpp index 66328b2b3..5a5a81e6a 100644 --- a/Engine/source/gui/controls/guiListBoxCtrl.cpp +++ b/Engine/source/gui/controls/guiListBoxCtrl.cpp @@ -1539,7 +1539,9 @@ StringTableEntry GuiListBoxCtrl::_makeMirrorItemName( SimObject *inObj ) Con::setIntVariable( "$ThisControl", getId() ); Con::setIntVariable( "$ThisObject", inObj->getId() ); - outName = StringTable->insert( Con::evaluate( mMakeNameCallback ).value, true ); + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); + String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + outName = StringTable->insert( Con::evaluate( mMakeNameCallback, false, context).value, true ); } else if ( inObj->getName() ) outName = StringTable->insert( inObj->getName() ); diff --git a/Engine/source/gui/controls/guiMLTextEditCtrl.cpp b/Engine/source/gui/controls/guiMLTextEditCtrl.cpp index 75cebfd41..d22300f37 100644 --- a/Engine/source/gui/controls/guiMLTextEditCtrl.cpp +++ b/Engine/source/gui/controls/guiMLTextEditCtrl.cpp @@ -211,7 +211,9 @@ bool GuiMLTextEditCtrl::onKeyDown(const GuiEvent& event) case KEY_ESCAPE: if ( mEscapeCommand[0] ) { - Con::evaluate( mEscapeCommand ); + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); + String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + Con::evaluate( mEscapeCommand, false, context ); return( true ); } return( Parent::onKeyDown( event ) ); diff --git a/Engine/source/gui/core/guiControl.cpp b/Engine/source/gui/core/guiControl.cpp index bc175e9fb..406a803d4 100644 --- a/Engine/source/gui/core/guiControl.cpp +++ b/Engine/source/gui/core/guiControl.cpp @@ -2495,7 +2495,9 @@ void GuiControl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent const char* GuiControl::evaluate( const char* str ) { smThisControl = this; - const char* result = Con::evaluate(str, false).value; + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); + String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + const char* result = Con::evaluate(str, false, context).value; smThisControl = NULL; return result; diff --git a/Engine/source/sim/actionMap.cpp b/Engine/source/sim/actionMap.cpp index 2e9c3b0e7..d392ab3d2 100644 --- a/Engine/source/sim/actionMap.cpp +++ b/Engine/source/sim/actionMap.cpp @@ -1482,8 +1482,12 @@ bool ActionMap::processAction(const InputEventInfo* pEvent) if(pNode->flags & Node::BindCmd) { // it's a bind command - if(pNode->makeConsoleCommand) - Con::evaluate(pNode->makeConsoleCommand); + if (pNode->makeConsoleCommand) + { + StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); + String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + Con::evaluate(pNode->makeConsoleCommand, false, context); + } } else if (pNode->flags & Node::Held) { From 9084e81bc1fd80a55c6a3483037f52dfb5612b5a Mon Sep 17 00:00:00 2001 From: JeffR Date: Sun, 22 Feb 2026 18:18:42 -0600 Subject: [PATCH 08/16] Shifted CodeBlock::smCurrentLineText to be a const char* so it's use is clearer Ensured CodeBlock::smCurrentLineText is cleared at the end of execution Tweaked and cleaned up the context lines passed along for further debugging data in the event of a script assert --- Engine/source/T3D/SubScene.cpp | 11 ++++- Engine/source/T3D/missionMarker.cpp | 12 ++++- Engine/source/T3D/trigger.cpp | 45 ++++++++++++++++--- .../source/console/torquescript/astNodes.cpp | 2 +- .../source/console/torquescript/codeBlock.cpp | 8 +++- .../source/console/torquescript/codeBlock.h | 2 +- .../source/console/torquescript/compiler.cpp | 34 +++++++++++--- .../gui/controls/guiGameListMenuCtrl.cpp | 2 +- .../gui/controls/guiGameSettingsCtrl.cpp | 2 +- Engine/source/gui/core/guiControl.cpp | 10 ++++- Engine/source/sim/actionMap.cpp | 2 +- 11 files changed, 107 insertions(+), 23 deletions(-) diff --git a/Engine/source/T3D/SubScene.cpp b/Engine/source/T3D/SubScene.cpp index effda500f..4dca81595 100644 --- a/Engine/source/T3D/SubScene.cpp +++ b/Engine/source/T3D/SubScene.cpp @@ -225,7 +225,16 @@ bool SubScene::evaluateCondition() Con::setBoolVariable(resVar.c_str(), false); String command = resVar + "=" + mLoadIf + ";"; - Con::evaluatef(command.c_str()); + StringTableEntry objectName = getName(); + if (objectName != NULL) + objectName = getIdString(); + + StringTableEntry groupName = getGroup()->getName(); + if (groupName != NULL) + groupName = getGroup()->getIdString(); + + String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); + Con::evaluate(command.c_str(), false, context); return Con::getBoolVariable(resVar.c_str()); } return true; diff --git a/Engine/source/T3D/missionMarker.cpp b/Engine/source/T3D/missionMarker.cpp index 69ae9ab81..9acf4c61c 100644 --- a/Engine/source/T3D/missionMarker.cpp +++ b/Engine/source/T3D/missionMarker.cpp @@ -367,7 +367,17 @@ bool SpawnSphere::testCondition() String resVar = getIdString() + String(".result"); Con::setBoolVariable(resVar.c_str(), false); String command = resVar + "=" + mSpawnIf + ";"; - Con::evaluatef(command.c_str()); + + StringTableEntry objectName = getName(); + if (objectName != NULL) + objectName = getIdString(); + + StringTableEntry groupName = getGroup()->getName(); + if (groupName != NULL) + groupName = getGroup()->getIdString(); + + String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); + Con::evaluate(command.c_str(), false, context); if (Con::getBoolVariable(resVar.c_str()) == 1) { return true; diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp index 5df83f681..95a331851 100644 --- a/Engine/source/T3D/trigger.cpp +++ b/Engine/source/T3D/trigger.cpp @@ -704,7 +704,17 @@ bool Trigger::testCondition() String resVar = getIdString() + String(".result"); Con::setBoolVariable(resVar.c_str(), false); String command = resVar + "=" + mTripIf + ";"; - Con::evaluatef(command.c_str()); + + StringTableEntry objectName = getName(); + if (objectName != NULL) + objectName = getIdString(); + + StringTableEntry groupName = getGroup()->getName(); + if (groupName != NULL) + groupName = getGroup()->getIdString(); + + String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); + Con::evaluate(command.c_str(), false, context); if (Con::getBoolVariable(resVar.c_str()) == 1) { return true; @@ -743,8 +753,15 @@ void Trigger::potentialEnterObject(GameBase* enter) String command = String("%obj = ") + enter->getIdString() + ";"; command = command + String("%this = ") + getIdString() + ";" + mEnterCommand; - StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getIdString(); - String context = String::ToString("%s, %s", getGroup(), objectName); + StringTableEntry objectName = getName(); + if (objectName != NULL) + objectName = getIdString(); + + StringTableEntry groupName = getGroup()->getName(); + if (groupName != NULL) + groupName = getGroup()->getIdString(); + + String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); Con::evaluate(command.c_str(), false, context); } @@ -795,8 +812,15 @@ void Trigger::processTick(const Move* move) String command = String("%obj = ") + remove->getIdString() + ";"; command = command + String("%this = ") + getIdString() + ";" + mLeaveCommand; - StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getIdString(); - String context = String::ToString("%s, %s", getGroup(), objectName); + StringTableEntry objectName = getName(); + if (objectName != NULL) + objectName = getIdString(); + + StringTableEntry groupName = getGroup()->getName(); + if (groupName != NULL) + groupName = getGroup()->getIdString(); + + String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); Con::evaluate(command.c_str(), false, context); } if (testTrippable() && testCondition()) @@ -807,8 +831,15 @@ void Trigger::processTick(const Move* move) if (evalCmD(&mTickCommand)) { - StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getIdString(); - String context = String::ToString("%s, %s", getGroup(), objectName); + StringTableEntry objectName = getName(); + if (objectName != NULL) + objectName = getIdString(); + + StringTableEntry groupName = getGroup()->getName(); + if (groupName != NULL) + groupName = getGroup()->getIdString(); + + String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); Con::evaluate(mTickCommand.c_str(), false, context); } diff --git a/Engine/source/console/torquescript/astNodes.cpp b/Engine/source/console/torquescript/astNodes.cpp index e1f3f6224..c20a0b975 100644 --- a/Engine/source/console/torquescript/astNodes.cpp +++ b/Engine/source/console/torquescript/astNodes.cpp @@ -60,7 +60,7 @@ inline FuncVars* getFuncVars(S32 lineNumber) if (gFuncVars == &gGlobalScopeFuncVars) { const char* lineTxt = CodeBlock::smCurrentLineText; - const char* str = avar("Attemping to use local variable in global scope. File: %s Line Num: %d \nLine: %s", CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineTxt); + const char* str = avar("Attemping to use local variable in global scope. File: %s\nLine Num: %d\nLine: \"%s\"", CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineTxt); scriptErrorHandler(str); } return gFuncVars; diff --git a/Engine/source/console/torquescript/codeBlock.cpp b/Engine/source/console/torquescript/codeBlock.cpp index cd324767d..d3bf6b3f5 100644 --- a/Engine/source/console/torquescript/codeBlock.cpp +++ b/Engine/source/console/torquescript/codeBlock.cpp @@ -35,7 +35,7 @@ using namespace Compiler; bool CodeBlock::smInFunction = false; CodeBlock * CodeBlock::smCodeBlockList = NULL; -StringTableEntry CodeBlock::smCurrentLineText = StringTable->EmptyString(); +const char* CodeBlock::smCurrentLineText = "\0"; TorqueScriptParser *CodeBlock::smCurrentParser = NULL; extern FuncVars gEvalFuncVars; @@ -625,6 +625,7 @@ Con::EvalResult CodeBlock::compileExec(StringTableEntry fileName, const char *in if (!Script::gStatementList) { + smCurrentLineText = "\0"; delete this; return Con::EvalResult(Con::getVariable("$ScriptError")); } @@ -670,7 +671,10 @@ Con::EvalResult CodeBlock::compileExec(StringTableEntry fileName, const char *in Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp); // repurpose argc as local register counter for global state - return (exec(0, fileName, NULL, localRegisterCount, 0, noCalls, NULL, setFrame)); + Con::EvalResult execResult = (exec(0, fileName, NULL, localRegisterCount, 0, noCalls, NULL, setFrame)); + + smCurrentLineText = "\0"; + return execResult; } //------------------------------------------------------------------------- diff --git a/Engine/source/console/torquescript/codeBlock.h b/Engine/source/console/torquescript/codeBlock.h index cef6d2683..33404d9a6 100644 --- a/Engine/source/console/torquescript/codeBlock.h +++ b/Engine/source/console/torquescript/codeBlock.h @@ -61,7 +61,7 @@ private: public: static bool smInFunction; static TorqueScriptParser * smCurrentParser; - static StringTableEntry smCurrentLineText; + static const char* smCurrentLineText; static CodeBlock *getCodeBlockList() { diff --git a/Engine/source/console/torquescript/compiler.cpp b/Engine/source/console/torquescript/compiler.cpp index 24e9135cc..206f7d679 100644 --- a/Engine/source/console/torquescript/compiler.cpp +++ b/Engine/source/console/torquescript/compiler.cpp @@ -160,8 +160,16 @@ S32 FuncVars::assign(StringTableEntry var, TypeReq currentType, S32 lineNumber, if (found->second.isConstant) { - const char* lineTxt = CodeBlock::smCurrentLineText; - const char* str = avar("Script Warning: Reassigning variable %s when it is a constant. File: %s Line Num: %d \nLine: %s", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineTxt); + const char* lineText = CodeBlock::smCurrentLineText; + + String codeString = CodeBlock::smCurrentLineText; + Vector splitLines; + codeString.split("\n", splitLines); + + if (lineNumber > 0 && splitLines.size() > lineNumber) + lineText = splitLines[lineNumber - 1].c_str(); + + const char* str = avar("Script Warning: Reassigning variable %s when it is a constant. File: %s\nLine Num: %d\nLine: \"%s\"", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineText); scriptErrorHandler(str); } return found->second.reg; @@ -180,8 +188,16 @@ S32 FuncVars::lookup(StringTableEntry var, S32 lineNumber) if (found == vars.end()) { - const char* lineTxt = CodeBlock::smCurrentLineText; - const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s Line Num: %d \nLine: %s", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineTxt); + const char* lineText = CodeBlock::smCurrentLineText; + + String codeString = CodeBlock::smCurrentLineText; + Vector splitLines; + codeString.split("\n", splitLines); + + if (lineNumber > 0 && splitLines.size() > lineNumber) + lineText = splitLines[lineNumber - 1].c_str(); + + const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s\nLine Num: %d\nLine: \"%s\"", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineText); scriptErrorHandler(str); return assign(var, TypeReqString, lineNumber, false); @@ -197,7 +213,15 @@ TypeReq FuncVars::lookupType(StringTableEntry var, S32 lineNumber) if (found == vars.end()) { const char* lineText = CodeBlock::smCurrentLineText; - const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s Line Num: %d \nLine: %s", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineText); + + String codeString = CodeBlock::smCurrentLineText; + Vector splitLines; + codeString.split("\n", splitLines); + + if (lineNumber > 0 && splitLines.size() > lineNumber) + lineText = splitLines[lineNumber-1].c_str(); + + const char* str = avar("Script Warning: Variable %s referenced before used when compiling script. File: %s\nLine Num: %d\nLine: \"%s\"", var, CodeBlock::smCurrentParser->getCurrentFile(), lineNumber, lineText); scriptErrorHandler(str); assign(var, TypeReqString, lineNumber, false); diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp index a81551d0e..2509c8cbd 100644 --- a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp +++ b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp @@ -975,7 +975,7 @@ void GuiGameListMenuCtrl::doScriptCommand(StringTableEntry command) { setThisControl(); StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); - String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + String context = String::ToString("%s\nObject: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); Con::evaluate(command, false, context); } } diff --git a/Engine/source/gui/controls/guiGameSettingsCtrl.cpp b/Engine/source/gui/controls/guiGameSettingsCtrl.cpp index ea3fd2ece..778c3c7b7 100644 --- a/Engine/source/gui/controls/guiGameSettingsCtrl.cpp +++ b/Engine/source/gui/controls/guiGameSettingsCtrl.cpp @@ -495,7 +495,7 @@ void GuiGameSettingsCtrl::doScriptCommand(StringTableEntry command) { setThisControl(); StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); - String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + String context = String::ToString("%s\nObject: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); Con::evaluate(command, false, context); } } diff --git a/Engine/source/gui/core/guiControl.cpp b/Engine/source/gui/core/guiControl.cpp index 406a803d4..b7b272612 100644 --- a/Engine/source/gui/core/guiControl.cpp +++ b/Engine/source/gui/core/guiControl.cpp @@ -2495,8 +2495,14 @@ void GuiControl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent const char* GuiControl::evaluate( const char* str ) { smThisControl = this; - StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); - String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + StringTableEntry objectName = getName(); + if (getName() == NULL) + objectName = getInternalName(); + StringTableEntry fileName = getFilename(); + if (fileName != NULL) + fileName = Platform::makeRelativePathName(fileName, NULL); + + String context = String::ToString("%s\nObject: %s", fileName, objectName); const char* result = Con::evaluate(str, false, context).value; smThisControl = NULL; diff --git a/Engine/source/sim/actionMap.cpp b/Engine/source/sim/actionMap.cpp index d392ab3d2..e492752f5 100644 --- a/Engine/source/sim/actionMap.cpp +++ b/Engine/source/sim/actionMap.cpp @@ -1485,7 +1485,7 @@ bool ActionMap::processAction(const InputEventInfo* pEvent) if (pNode->makeConsoleCommand) { StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); - String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); + String context = String::ToString("%s\nObject: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); Con::evaluate(pNode->makeConsoleCommand, false, context); } } From 627b9bc076304ee447afff382ce65ac6dd3796b4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 24 Feb 2026 15:04:38 -0600 Subject: [PATCH 09/16] minor cleanups for https://github.com/TorqueGameEngines/Torque3D/pull/1672 for context Strings, use c_str(), for triggers, use Con::getCurrentScriptModulePath() as otherwise, the "%this =" and "%obj=" injections will be treated as within the global scope. which is not allowed. --- Engine/source/T3D/trigger.cpp | 34 +++++-------------- .../gui/controls/guiGameSettingsCtrl.cpp | 2 +- .../source/gui/controls/guiMLTextEditCtrl.cpp | 2 +- Engine/source/sim/actionMap.cpp | 2 +- 4 files changed, 11 insertions(+), 29 deletions(-) diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp index 95a331851..33075e70d 100644 --- a/Engine/source/T3D/trigger.cpp +++ b/Engine/source/T3D/trigger.cpp @@ -28,6 +28,7 @@ #include "console/engineAPI.h" #include "collision/boxConvex.h" #include "console/script.h" +#include "console/consoleInternal.h" #include "core/stream/bitStream.h" #include "math/mathIO.h" @@ -172,8 +173,9 @@ Trigger::Trigger() mPhysicsRep = NULL; mTripOnce = false; + mTripped = false; mTrippedBy = 0xFFFFFFFF; - mTripIf = ""; + mTripIf.clear(); //Default up a basic square Point3F vecs[3] = { Point3F(1.0, 0.0, 0.0), @@ -748,21 +750,11 @@ void Trigger::potentialEnterObject(GameBase* enter) mObjects.push_back(enter); deleteNotify(enter); - if(evalCmD(&mEnterCommand)) + if(isServerObject() && evalCmD(&mEnterCommand)) { String command = String("%obj = ") + enter->getIdString() + ";"; command = command + String("%this = ") + getIdString() + ";" + mEnterCommand; - - StringTableEntry objectName = getName(); - if (objectName != NULL) - objectName = getIdString(); - - StringTableEntry groupName = getGroup()->getName(); - if (groupName != NULL) - groupName = getGroup()->getIdString(); - - String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); - Con::evaluate(command.c_str(), false, context); + Con::evaluate(command.c_str(), false, Con::getCurrentScriptModulePath()); } if( mDataBlock && testTrippable() && testCondition()) @@ -807,21 +799,11 @@ void Trigger::processTick(const Move* move) mObjects.erase(i); clearNotify(remove); - if (evalCmD(&mLeaveCommand)) + if (isServerObject() && evalCmD(&mLeaveCommand)) { String command = String("%obj = ") + remove->getIdString() + ";"; command = command + String("%this = ") + getIdString() + ";" + mLeaveCommand; - - StringTableEntry objectName = getName(); - if (objectName != NULL) - objectName = getIdString(); - - StringTableEntry groupName = getGroup()->getName(); - if (groupName != NULL) - groupName = getGroup()->getIdString(); - - String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); - Con::evaluate(command.c_str(), false, context); + Con::evaluate(command.c_str(), false, Con::getCurrentScriptModulePath()); } if (testTrippable() && testCondition()) mDataBlock->onLeaveTrigger_callback( this, remove ); @@ -829,7 +811,7 @@ void Trigger::processTick(const Move* move) } } - if (evalCmD(&mTickCommand)) + if (isServerObject() && evalCmD(&mTickCommand)) { StringTableEntry objectName = getName(); if (objectName != NULL) diff --git a/Engine/source/gui/controls/guiGameSettingsCtrl.cpp b/Engine/source/gui/controls/guiGameSettingsCtrl.cpp index 778c3c7b7..a562782a3 100644 --- a/Engine/source/gui/controls/guiGameSettingsCtrl.cpp +++ b/Engine/source/gui/controls/guiGameSettingsCtrl.cpp @@ -496,7 +496,7 @@ void GuiGameSettingsCtrl::doScriptCommand(StringTableEntry command) setThisControl(); StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); String context = String::ToString("%s\nObject: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); - Con::evaluate(command, false, context); + Con::evaluate(command, false, context.c_str()); } } diff --git a/Engine/source/gui/controls/guiMLTextEditCtrl.cpp b/Engine/source/gui/controls/guiMLTextEditCtrl.cpp index d22300f37..04a176f9e 100644 --- a/Engine/source/gui/controls/guiMLTextEditCtrl.cpp +++ b/Engine/source/gui/controls/guiMLTextEditCtrl.cpp @@ -213,7 +213,7 @@ bool GuiMLTextEditCtrl::onKeyDown(const GuiEvent& event) { StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); - Con::evaluate( mEscapeCommand, false, context ); + Con::evaluate( mEscapeCommand, false, context.c_str()); return( true ); } return( Parent::onKeyDown( event ) ); diff --git a/Engine/source/sim/actionMap.cpp b/Engine/source/sim/actionMap.cpp index e492752f5..16d107eb3 100644 --- a/Engine/source/sim/actionMap.cpp +++ b/Engine/source/sim/actionMap.cpp @@ -1486,7 +1486,7 @@ bool ActionMap::processAction(const InputEventInfo* pEvent) { StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); String context = String::ToString("%s\nObject: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); - Con::evaluate(pNode->makeConsoleCommand, false, context); + Con::evaluate(pNode->makeConsoleCommand, false, context.c_str()); } } else if (pNode->flags & Node::Held) From 2b6b56d0d98296991f1d072fbcb122ffd862c0a0 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 24 Feb 2026 17:03:12 -0600 Subject: [PATCH 10/16] don't flip invertroughness on by default --- Engine/source/T3D/assets/assetImporter.cpp | 2 +- Engine/source/ts/assimp/assimpAppMaterial.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Engine/source/T3D/assets/assetImporter.cpp b/Engine/source/T3D/assets/assetImporter.cpp index a807bbc0f..ec0fd1f89 100644 --- a/Engine/source/T3D/assets/assetImporter.cpp +++ b/Engine/source/T3D/assets/assetImporter.cpp @@ -3003,7 +3003,7 @@ Torque::Path AssetImporter::importMaterialAsset(AssetImportObject* assetItem) if (hasRoughness) { - newMat->mInvertRoughness[0] = true; + newMat->mInvertRoughness[0] = false; } newAsset->addObject(newMat); diff --git a/Engine/source/ts/assimp/assimpAppMaterial.cpp b/Engine/source/ts/assimp/assimpAppMaterial.cpp index fce979dfc..1d560da30 100644 --- a/Engine/source/ts/assimp/assimpAppMaterial.cpp +++ b/Engine/source/ts/assimp/assimpAppMaterial.cpp @@ -206,15 +206,15 @@ void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) co if (rmName.isNotEmpty()) { mat->_setRoughMap(cleanTextureName(rmName, cleanFile, path, false), 0); // Roughness - mat->mRoughnessChan[0] = 1.0f; - mat->mInvertRoughness[0] = (floatVal == 1.0f); + mat->mRoughnessChan[0] = 1; + mat->mInvertRoughness[0] = false; mat->_setMetalMap(cleanTextureName(rmName, cleanFile, path, false), 0); // Metallic - mat->mMetalChan[0] = 2.0f; + mat->mMetalChan[0] = 2; } if (aoName.isNotEmpty()) { mat->_setAOMap(cleanTextureName(aoName, cleanFile, path, false), 0); // occlusion - mat->mAOChan[0] = 0.0f; + mat->mAOChan[0] = 0; } else { From 75af5a2bf6ee1e774c933d130cc658423b31abfa Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 28 Feb 2026 19:19:08 -0600 Subject: [PATCH 11/16] bind grid snapping bool to one variable --- .../tools/convexEditor/convexEditorGui.tscript | 18 +++++++++--------- .../game/tools/convexEditor/main.tscript | 6 +++--- .../worldEditor/scripts/EditorGui.ed.tscript | 16 ++++++++-------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.tscript b/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.tscript index 72b3b13a8..ff10d5dcc 100644 --- a/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.tscript +++ b/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.tscript @@ -60,10 +60,10 @@ function ConvexEditorGui::onWake( %this ) %this.releaseSidePanel(); } - EWorldEditor.UseGridSnap = EditorSettings.value("WorldEditor/Tools/UseGridSnap"); - CESnapOptions-->objectGridSnapBtn.setStateOn( EWorldEditor.UseGridSnap ); - %this.setGridSnap( EWorldEditor.UseGridSnap ); - EWorldEditor.setGridSnap( EWorldEditor.UseGridSnap ); + EWorldEditor.gridSnap = EditorSettings.value("WorldEditor/Tools/gridSnap"); + CESnapOptions-->objectGridSnapBtn.setStateOn( EWorldEditor.gridSnap ); + %this.setGridSnap( EWorldEditor.gridSnap ); + EWorldEditor.setGridSnap( EWorldEditor.gridSnap ); } function ConvexEditorGui::onSleep( %this ) @@ -217,11 +217,11 @@ function ConvexEditorMaterialResetBtn::onClick(%this) function ConvexEditorGui::toggleGridSnap(%this) { - EWorldEditor.UseGridSnap = !EWorldEditor.UseGridSnap; - EditorSettings.setValue("WorldEditor/Tools/UseGridSnap", EWorldEditor.UseGridSnap ); - CESnapOptions-->objectGridSnapBtn.setStateOn( EWorldEditor.UseGridSnap ); - %this.setGridSnap( EWorldEditor.UseGridSnap ); - EWorldEditor.setGridSnap( EWorldEditor.UseGridSnap ); + EWorldEditor.gridSnap = !EWorldEditor.gridSnap; + EditorSettings.setValue("WorldEditor/Tools/gridSnap", EWorldEditor.gridSnap ); + CESnapOptions-->objectGridSnapBtn.setStateOn( EWorldEditor.gridSnap ); + %this.setGridSnap( EWorldEditor.gridSnap ); + EWorldEditor.setGridSnap( EWorldEditor.gridSnap ); } diff --git a/Templates/BaseGame/game/tools/convexEditor/main.tscript b/Templates/BaseGame/game/tools/convexEditor/main.tscript index f81ef26cc..92e4770da 100644 --- a/Templates/BaseGame/game/tools/convexEditor/main.tscript +++ b/Templates/BaseGame/game/tools/convexEditor/main.tscript @@ -130,9 +130,9 @@ function ConvexEditorPlugin::onActivated( %this ) ConvexEditorScaleModeBtn.performClick(); } - EWorldEditor.UseGridSnap = EditorSettings.value("WorldEditor/Tools/UseGridSnap"); - CESnapOptions-->objectGridSnapBtn.setStateOn( EWorldEditor.UseGridSnap ); - %this.setGridSnap( EWorldEditor.UseGridSnap ); + EWorldEditor.gridSnap = EditorSettings.value("WorldEditor/Tools/gridSnap"); + CESnapOptions-->objectGridSnapBtn.setStateOn( EWorldEditor.gridSnap ); + %this.setGridSnap( EWorldEditor.gridSnap ); Parent::onActivated( %this ); EditorGui.SetStandardPalletBar(); diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript index 8b6562f77..9dcd083da 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.tscript @@ -1051,9 +1051,9 @@ function WorldEditorPlugin::onActivated( %this ) ETransformSelection.setVisible(true); } - EWorldEditor.UseGridSnap = EditorSettings.value("WorldEditor/Tools/UseGridSnap"); - ESnapOptions-->GridSnapButton.setStateOn( EWorldEditor.UseGridSnap ); - SnapToBar-->objectGridSnapBtn.setStateOn( EWorldEditor.UseGridSnap ); + EWorldEditor.gridSnap = EditorSettings.value("WorldEditor/Tools/gridSnap"); + ESnapOptions-->GridSnapButton.setStateOn( EWorldEditor.gridSnap ); + SnapToBar-->objectGridSnapBtn.setStateOn( EWorldEditor.gridSnap ); Parent::onActivated(%this); @@ -2615,8 +2615,8 @@ function EWorldEditor::syncGui( %this ) ESnapOptions-->SnapSize.setText( EWorldEditor.getSoftSnapSize() ); ESnapOptions-->GridSize.setText( EWorldEditor.getGridSize() ); - %this.UseGridSnap = EditorSettings.value("WorldEditor/Tools/UseGridSnap"); - ESnapOptions-->GridSnapButton.setStateOn( %this.UseGridSnap ); + %this.gridSnap = EditorSettings.value("WorldEditor/Tools/gridSnap"); + ESnapOptions-->GridSnapButton.setStateOn( %this.gridSnap ); %this.UseGroupCenter = EditorSettings.value("WorldEditor/Tools/UseGroupCenter"); @@ -2961,9 +2961,9 @@ function toggleSnappingOptions( %var ) } else if( %var $= "grid" ) { - EWorldEditor.UseGridSnap = !EWorldEditor.UseGridSnap; - EditorSettings.setValue("WorldEditor/Tools/UseGridSnap", EWorldEditor.UseGridSnap ); - EWorldEditor.setGridSnap( EWorldEditor.UseGridSnap ); + EWorldEditor.gridSnap = !EWorldEditor.gridSnap; + EditorSettings.setValue("WorldEditor/Tools/gridSnap", EWorldEditor.gridSnap ); + EWorldEditor.setGridSnap( EWorldEditor.gridSnap ); } else if( %var $= "byGroup" ) { From 6449d22d7ffc3a12e61b82c4891ab8a5f7311599 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sun, 1 Mar 2026 12:18:30 +0000 Subject: [PATCH 12/16] opengl debug committing to test on windows with proper debug output --- Engine/source/gfx/gl/gfxGLDevice.cpp | 122 ++++++++++++++----- Engine/source/gfx/gl/gfxGLTextureManager.cpp | 99 +++++++++++---- Engine/source/gfx/gl/gfxGLTextureObject.cpp | 93 ++++++++++---- Engine/source/gfx/gl/gfxGLUtils.h | 26 ++++ Engine/source/gfx/gl/sdl/gfxGLDevice.sdl.cpp | 10 +- Engine/source/platformSDL/sdlPlatformGL.cpp | 4 +- 6 files changed, 270 insertions(+), 84 deletions(-) diff --git a/Engine/source/gfx/gl/gfxGLDevice.cpp b/Engine/source/gfx/gl/gfxGLDevice.cpp index 8019f21bf..8ec644b8a 100644 --- a/Engine/source/gfx/gl/gfxGLDevice.cpp +++ b/Engine/source/gfx/gl/gfxGLDevice.cpp @@ -91,19 +91,74 @@ void loadGLExtensions(void *context) GL::gglPerformExtensionBinds(context); } -void STDCALL glDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, - const GLchar *message, const void *userParam) +void APIENTRY glDebugCallback( + GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) { - // JTH [11/24/2016]: This is a temporary fix so that we do not get spammed for redundant fbo changes. - // This only happens on Intel cards. This should be looked into sometime in the near future. - if (dStrStartsWith(message, "API_ID_REDUNDANT_FBO")) + // Ignore non-significant notifications (optional) + if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) return; + + const char* srcStr = "UNKNOWN"; + const char* typeStr = "UNKNOWN"; + const char* sevStr = "UNKNOWN"; + + switch (source) + { + case GL_DEBUG_SOURCE_API: srcStr = "API"; break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: srcStr = "WINDOW"; break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: srcStr = "SHADER"; break; + case GL_DEBUG_SOURCE_THIRD_PARTY: srcStr = "THIRD_PARTY"; break; + case GL_DEBUG_SOURCE_APPLICATION: srcStr = "APP"; break; + case GL_DEBUG_SOURCE_OTHER: srcStr = "OTHER"; break; + } + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: typeStr = "ERROR"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: typeStr = "DEPRECATED"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: typeStr = "UNDEFINED"; break; + case GL_DEBUG_TYPE_PORTABILITY: typeStr = "PORTABILITY"; break; + case GL_DEBUG_TYPE_PERFORMANCE: typeStr = "PERFORMANCE"; break; + case GL_DEBUG_TYPE_MARKER: typeStr = "MARKER"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: typeStr = "PUSH"; break; + case GL_DEBUG_TYPE_POP_GROUP: typeStr = "POP"; break; + case GL_DEBUG_TYPE_OTHER: typeStr = "OTHER"; break; + } + + switch (severity) + { + case GL_DEBUG_SEVERITY_HIGH: sevStr = "HIGH"; break; + case GL_DEBUG_SEVERITY_MEDIUM: sevStr = "MEDIUM"; break; + case GL_DEBUG_SEVERITY_LOW: sevStr = "LOW"; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: sevStr = "NOTIFY"; break; + } + + // Filter known noisy IDs here if needed + // Example: + // if (id == 131185) return; + if (severity == GL_DEBUG_SEVERITY_HIGH) - Con::errorf("OPENGL: %s", message); + { + Con::errorf("OPENGL [%s][%s][%s][%u]: %s", + sevStr, srcStr, typeStr, id, message); + AssertFatal(false, "OpenGL HIGH severity error."); + } else if (severity == GL_DEBUG_SEVERITY_MEDIUM) - Con::warnf("OPENGL: %s", message); - else if (severity == GL_DEBUG_SEVERITY_LOW) - Con::printf("OPENGL: %s", message); + { + Con::warnf("OPENGL [%s][%s][%s][%u]: %s", + sevStr, srcStr, typeStr, id, message); + } + else + { + Con::printf("OPENGL [%s][%s][%s][%u]: %s", + sevStr, srcStr, typeStr, id, message); + } } void STDCALL glAmdDebugCallback(GLuint id, GLenum category, GLenum severity, GLsizei length, @@ -157,27 +212,34 @@ void GFXGLDevice::initGLState() #endif #if TORQUE_DEBUG - if( gglHasExtension(ARB_debug_output) ) - { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallbackARB(glDebugCallback, NULL); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - GLuint unusedIds = 0; - glDebugMessageControlARB(GL_DONT_CARE, - GL_DONT_CARE, - GL_DONT_CARE, - 0, - &unusedIds, - GL_TRUE); - } - else if(gglHasExtension(AMD_debug_output)) - { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallbackAMD(glAmdDebugCallback, NULL); - //glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - GLuint unusedIds = 0; - glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0,&unusedIds, GL_TRUE); - } + +bool debugInitialized = false; +int flags; +glGetIntegerv(GL_CONTEXT_FLAGS, &flags); +if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) +{ + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + glDebugMessageCallback(glDebugCallback, nullptr); + + glDebugMessageControl( + GL_DONT_CARE, + GL_DONT_CARE, + GL_DONT_CARE, + 0, + nullptr, + GL_TRUE); + + Con::printf("OpenGL debug output enabled."); + debugInitialized = true; +} + +if (!debugInitialized) +{ + Con::warnf("OpenGL debug output NOT available."); +} + #endif PlatformGL::setVSync(smEnableVSync); diff --git a/Engine/source/gfx/gl/gfxGLTextureManager.cpp b/Engine/source/gfx/gl/gfxGLTextureManager.cpp index ca0a958f3..c6c73fbee 100644 --- a/Engine/source/gfx/gl/gfxGLTextureManager.cpp +++ b/Engine/source/gfx/gl/gfxGLTextureManager.cpp @@ -175,7 +175,7 @@ void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex, //calculate num mipmaps if(retTex->mMipLevels == 0) retTex->mMipLevels = getMaxMipmaps(width, height, 1); - + glTexParameteri(binding, GL_TEXTURE_MAX_LEVEL, retTex->mMipLevels-1 ); bool hasTexStorage = false; @@ -364,34 +364,83 @@ void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex, // loadTexture - GBitmap //----------------------------------------------------------------------------- -static void _textureUpload(const S32 width, const S32 height,const S32 bytesPerPixel,const GFXGLTextureObject* texture, const GFXFormat fmt, const U8* data,const S32 mip=0, const U32 face = 0, Swizzle *pSwizzle = NULL) +static void _textureUpload( + const S32 width, + const S32 height, + const S32 bytesPerPixel, + const GFXGLTextureObject* texture, + const GFXFormat fmt, + const U8* data, + const S32 mip = 0, + const U32 face = 0, + Swizzle* pSwizzle = NULL) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->getBuffer()); - U32 bufSize = width * height * bytesPerPixel; - glBufferData(GL_PIXEL_UNPACK_BUFFER, bufSize, NULL, GL_STREAM_DRAW); + const GLenum target = texture->getBinding(); - if(pSwizzle) - { - PROFILE_SCOPE(Swizzle32_Upload); - U8* pboMemory = (U8*)dMalloc(bufSize); - pSwizzle->ToBuffer(pboMemory, data, bufSize); - glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufSize, pboMemory); - dFree(pboMemory); - } - else - { - PROFILE_SCOPE(SwizzleNull_Upload); - glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, bufSize, data); - } + // Save pixel store state + GLint prevUnpackAlign; + glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlign); - if(texture->getBinding() == GL_TEXTURE_CUBE_MAP) - glTexSubImage2D(GFXGLFaceType[face], mip, 0, 0, width, height, GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL); - else if (texture->getBinding() == GL_TEXTURE_2D) - glTexSubImage2D(texture->getBinding(), mip, 0, 0, width, height, GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL); - else - glTexSubImage1D(texture->getBinding(), mip, 0, (width > 1 ? width : height), GFXGLTextureFormat[fmt], GFXGLTextureType[fmt], NULL); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + const U32 bufSize = width * height * bytesPerPixel; + + const U8* uploadPtr = data; + U8* tempBuffer = nullptr; + + if (pSwizzle) + { + tempBuffer = (U8*)dMalloc(bufSize); + pSwizzle->ToBuffer(tempBuffer, data, bufSize); + uploadPtr = tempBuffer; + } + + if (target == GL_TEXTURE_CUBE_MAP) + { + glTexSubImage2D( + GFXGLFaceType[face], + mip, + 0, 0, + width, height, + GFXGLTextureFormat[fmt], + GFXGLTextureType[fmt], + uploadPtr + ); + } + else if (target == GL_TEXTURE_2D) + { + glTexSubImage2D( + GL_TEXTURE_2D, + mip, + 0, 0, + width, height, + GFXGLTextureFormat[fmt], + GFXGLTextureType[fmt], + uploadPtr + ); + } + else if (target == GL_TEXTURE_1D) + { + glTexSubImage1D( + GL_TEXTURE_1D, + mip, + 0, + width, + GFXGLTextureFormat[fmt], + GFXGLTextureType[fmt], + uploadPtr + ); + } + + if (tempBuffer) + dFree(tempBuffer); + + // Restore state + glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlign); + +#ifdef TORQUE_DEBUG + glCheckErrors(); +#endif } bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL) diff --git a/Engine/source/gfx/gl/gfxGLTextureObject.cpp b/Engine/source/gfx/gl/gfxGLTextureObject.cpp index 651059d34..81ca6fa9d 100644 --- a/Engine/source/gfx/gl/gfxGLTextureObject.cpp +++ b/Engine/source/gfx/gl/gfxGLTextureObject.cpp @@ -102,36 +102,79 @@ GFXLockedRect* GFXGLTextureObject::lock(U32 mipLevel /*= 0*/, RectI* inRect /*= void GFXGLTextureObject::unlock(U32 mipLevel /*= 0*/, U32 faceIndex /*= 0*/) { - if(!mLockedRect.bits) - return; + if (!mLockedRect.bits) + return; - // I know this is in unlock, but in GL we actually do our submission in unlock. - PROFILE_SCOPE(GFXGLTextureObject_lockRT); + PROFILE_SCOPE(GFXGLTextureObject_unlock); - PRESERVE_TEXTURE(mBinding); - glBindTexture(mBinding, mHandle); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mBuffer); - glBufferData(GL_PIXEL_UNPACK_BUFFER, (mLockedRectRect.extent.x + 1) * (mLockedRectRect.extent.y + 1) * mBytesPerTexel, mFrameAllocatorPtr, GL_STREAM_DRAW); - S32 z = getDepth(); - if (mBinding == GL_TEXTURE_3D) - glTexSubImage3D(mBinding, mipLevel, mLockedRectRect.point.x, mLockedRectRect.point.y, z, - mLockedRectRect.extent.x, mLockedRectRect.extent.y, z, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], NULL); - else if(mBinding == GL_TEXTURE_2D) - glTexSubImage2D(mBinding, mipLevel, mLockedRectRect.point.x, mLockedRectRect.point.y, - mLockedRectRect.extent.x, mLockedRectRect.extent.y, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], NULL); - else if(mBinding == GL_TEXTURE_1D) - glTexSubImage1D(mBinding, mipLevel, (mLockedRectRect.point.x > 1 ? mLockedRectRect.point.x : mLockedRectRect.point.y), - (mLockedRectRect.extent.x > 1 ? mLockedRectRect.extent.x : mLockedRectRect.extent.y), GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], NULL); + PRESERVE_TEXTURE(mBinding); + glBindTexture(mBinding, mHandle); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + // --- Save pixel store state --- + GLint prevUnpackAlign; + glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlign); - mLockedRect.bits = NULL; -#if TORQUE_DEBUG - AssertFatal(mFrameAllocatorMarkGuard == FrameAllocator::getWaterMark(), ""); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + const U32 width = mLockedRectRect.extent.x; + const U32 height = mLockedRectRect.extent.y; + const U32 depth = getDepth(); + + if (mBinding == GL_TEXTURE_3D) + { + glTexSubImage3D( + mBinding, + mipLevel, + mLockedRectRect.point.x, + mLockedRectRect.point.y, + 0, + width, + height, + depth, + GFXGLTextureFormat[mFormat], + GFXGLTextureType[mFormat], + mLockedRect.bits + ); + } + else if (mBinding == GL_TEXTURE_2D) + { + glTexSubImage2D( + mBinding, + mipLevel, + mLockedRectRect.point.x, + mLockedRectRect.point.y, + width, + height, + GFXGLTextureFormat[mFormat], + GFXGLTextureType[mFormat], + mLockedRect.bits + ); + } + else if (mBinding == GL_TEXTURE_1D) + { + glTexSubImage1D( + mBinding, + mipLevel, + mLockedRectRect.point.x, + width, + GFXGLTextureFormat[mFormat], + GFXGLTextureType[mFormat], + mLockedRect.bits + ); + } + + // --- Restore state --- + glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlign); + + mLockedRect.bits = NULL; + + FrameAllocator::setWaterMark(mFrameAllocatorMark); + mFrameAllocatorMark = 0; + mFrameAllocatorPtr = NULL; + +#ifdef TORQUE_DEBUG + glCheckErrors(); #endif - FrameAllocator::setWaterMark(mFrameAllocatorMark); - mFrameAllocatorMark = 0; - mFrameAllocatorPtr = NULL; } void GFXGLTextureObject::release() diff --git a/Engine/source/gfx/gl/gfxGLUtils.h b/Engine/source/gfx/gl/gfxGLUtils.h index 2218836b0..928cf98e3 100644 --- a/Engine/source/gfx/gl/gfxGLUtils.h +++ b/Engine/source/gfx/gl/gfxGLUtils.h @@ -28,6 +28,32 @@ #include "gfx/gl/gfxGLStateCache.h" #include "gfx/bitmap/imageUtils.h" +inline const char* glGetErrorString(GLenum error) +{ + switch (error) + { + case GL_NO_ERROR: return "No Error"; + case GL_INVALID_ENUM: return "Invalid Enum"; + case GL_INVALID_VALUE: return "Invalid Value"; + case GL_INVALID_OPERATION: return "Invalid Operation"; + case GL_INVALID_FRAMEBUFFER_OPERATION: return "Invalid Framebuffer Operation"; + case GL_OUT_OF_MEMORY: return "Out of Memory"; + case GL_STACK_UNDERFLOW: return "Stack Underflow"; + case GL_STACK_OVERFLOW: return "Stack Overflow"; + case GL_CONTEXT_LOST: return "Context Lost"; + default: return "Unknown Error"; + } +} + +inline void _glCheckErrors(const char *filename, int line) +{ + GLenum err; + while ((err = glGetError()) != GL_NO_ERROR) + Con::printf("OpenGL Error: %s (%d) [%u] %s\n", filename, line, err, glGetErrorString(err)); +} + +#define glCheckErrors() _glCheckErrors(__FILE__, __LINE__) + inline U32 getMaxMipmaps(U32 width, U32 height, U32 depth) { return getMax( getBinLog2(depth), getMax(getBinLog2(width), getBinLog2(height))) + 1; diff --git a/Engine/source/gfx/gl/sdl/gfxGLDevice.sdl.cpp b/Engine/source/gfx/gl/sdl/gfxGLDevice.sdl.cpp index 1bb34d4f2..a81c0334c 100644 --- a/Engine/source/gfx/gl/sdl/gfxGLDevice.sdl.cpp +++ b/Engine/source/gfx/gl/sdl/gfxGLDevice.sdl.cpp @@ -103,9 +103,17 @@ void GFXGLDevice::enumerateAdapters( Vector &adapterList ) } SDL_ClearError(); + U32 debugFlag = 0; +#ifdef TORQUE_DEBUG + debugFlag |= SDL_GL_CONTEXT_DEBUG_FLAG; +#endif SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, debugFlag); SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 1); - + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); +#ifdef TORQUE_GL_SOFTWARE + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 0); +#endif SDL_GLContext tempContext = SDL_GL_CreateContext( tempWindow ); if( !tempContext ) { diff --git a/Engine/source/platformSDL/sdlPlatformGL.cpp b/Engine/source/platformSDL/sdlPlatformGL.cpp index fd55727b8..22e719f58 100644 --- a/Engine/source/platformSDL/sdlPlatformGL.cpp +++ b/Engine/source/platformSDL/sdlPlatformGL.cpp @@ -19,12 +19,10 @@ namespace PlatformGL #ifdef TORQUE_DEBUG debugFlag |= SDL_GL_CONTEXT_DEBUG_FLAG; #endif - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, majorOGL); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minorOGL); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, debugFlag); SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 1); + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); #ifdef TORQUE_GL_SOFTWARE SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 0); #endif From cf294ad12181309f30e63f7e17b4745e3babb4cb Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 2 Mar 2026 08:52:47 -0600 Subject: [PATCH 13/16] selfshadowing corrections best to stick to the order of the XY tags use PI, not 1/PI to avoid "normal flipping" --- .../game/core/rendering/shaders/gl/lighting.glsl | 10 +++++----- .../game/core/rendering/shaders/lighting.hlsl | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl index 0ebcd3794..a1df93c70 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl @@ -178,9 +178,9 @@ SurfaceToLight createSurfaceToLight(in Surface surface, in vec3 L) surfaceToLight.Lu = L; surfaceToLight.L = normalize(L); surfaceToLight.H = normalize(surface.V + surfaceToLight.L); - surfaceToLight.NdotL = saturate(dot(surfaceToLight.L, surface.N)); - surfaceToLight.HdotV = saturate(dot(surfaceToLight.H, surface.V)); - surfaceToLight.NdotH = saturate(dot(surfaceToLight.H, surface.N)); + surfaceToLight.NdotL = saturate(dot(surface.N,surfaceToLight.L)); + surfaceToLight.HdotV = saturate(dot(surfaceToLight.H,surface.V)); + surfaceToLight.NdotH = saturate(dot(surface.N,surfaceToLight.H)); return surfaceToLight; } @@ -243,12 +243,12 @@ vec3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) float denominator = 4.0 * max(surface.NdotV, 0.0) * max(surfaceToLight.NdotL, 0.0) + 0.0001; vec3 specularBRDF = numerator / denominator; - vec3 diffuseBRDF = surface.baseColor.rgb * M_1OVER_PI_F * surface.ao; + vec3 diffuseBRDF = surface.baseColor.rgb * surface.ao * M_PI_F; // Final output combining all terms vec3 kS = F; // Specular reflectance vec3 kD = (1.0 - kS) * (1.0 - surface.metalness); // Diffuse reflectance - vec3 returnBRDF = kD * (diffuseBRDF) + specularBRDF; + vec3 returnBRDF = kD * diffuseBRDF + specularBRDF; if(isCapturing == 1) return lerp(returnBRDF ,surface.albedo.rgb,surface.metalness); diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index a2e8d2f4e..015c925bf 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -177,9 +177,9 @@ inline SurfaceToLight createSurfaceToLight(in Surface surface, in float3 L) surfaceToLight.Lu = L; surfaceToLight.L = normalize(L); surfaceToLight.H = normalize(surface.V + surfaceToLight.L); - surfaceToLight.NdotL = saturate(dot(surfaceToLight.L, surface.N)); + surfaceToLight.NdotL = saturate(dot(surface.N, surfaceToLight.L)); surfaceToLight.HdotV = saturate(dot(surfaceToLight.H, surface.V)); - surfaceToLight.NdotH = saturate(dot(surfaceToLight.H, surface.N)); + surfaceToLight.NdotH = saturate(dot(surface.N, surfaceToLight.H)); return surfaceToLight; } @@ -243,12 +243,12 @@ float3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) float denominator = 4.0 * max(surface.NdotV, 0.0) * max(surfaceToLight.NdotL, 0.0) + 0.0001; float3 specularBRDF = numerator / denominator; - float3 diffuseBRDF = surface.baseColor.rgb * M_1OVER_PI_F * surface.ao; + float3 diffuseBRDF = surface.baseColor.rgb * surface.ao* M_PI_F; // Final output combining all terms float3 kS = F; // Specular reflectance float3 kD = (1.0 - kS) * (1.0 - surface.metalness); // Diffuse reflectance - float3 returnBRDF = kD * (diffuseBRDF) + specularBRDF; + float3 returnBRDF = kD*diffuseBRDF + specularBRDF; if(isCapturing == 1) return lerp(returnBRDF ,surface.albedo.rgb,surface.metalness); @@ -277,8 +277,8 @@ float3 getPunctualLight(Surface surface, SurfaceToLight surfaceToLight, float3 l if(isCapturing != 1) lightfloor = 0.0; - float attenuation = getDistanceAtt(surfaceToLight.Lu, radius); - + float attenuation = getDistanceAtt(surfaceToLight.Lu, radius); + // Calculate both specular and diffuse lighting in one BRDF evaluation float3 directLighting = evaluateStandardBRDF(surface, surfaceToLight); @@ -297,7 +297,7 @@ float3 getSpotlight(Surface surface, SurfaceToLight surfaceToLight, float3 light float attenuation = 1.0f; attenuation *= getDistanceAtt(surfaceToLight.Lu, radius); attenuation *= getSpotAngleAtt(-surfaceToLight.L, lightDir, lightSpotParams.xy); - + // Calculate both specular and diffuse lighting in one BRDF evaluation float3 directLighting = evaluateStandardBRDF(surface, surfaceToLight); From 80f62573fea7fd64daf2b8eb5693d44582165107 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 2 Mar 2026 11:38:20 -0600 Subject: [PATCH 14/16] context safties and from Marauder, hardened filename entry for con::evaluate/compileexec. if the string forwarded along is not a real filename, consider it an eval block --- Engine/source/T3D/SubScene.cpp | 4 ++-- Engine/source/T3D/missionMarker.cpp | 4 ++-- Engine/source/T3D/trigger.cpp | 6 +++--- Engine/source/console/torquescript/codeBlock.cpp | 2 +- Engine/source/console/torquescript/runtime.cpp | 4 +++- .../source/gui/controls/guiGameListMenuCtrl.cpp | 2 +- Engine/source/gui/controls/guiListBoxCtrl.cpp | 2 +- Engine/source/gui/core/guiControl.cpp | 15 ++++++++------- 8 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Engine/source/T3D/SubScene.cpp b/Engine/source/T3D/SubScene.cpp index 4dca81595..798d42ace 100644 --- a/Engine/source/T3D/SubScene.cpp +++ b/Engine/source/T3D/SubScene.cpp @@ -229,12 +229,12 @@ bool SubScene::evaluateCondition() if (objectName != NULL) objectName = getIdString(); - StringTableEntry groupName = getGroup()->getName(); + StringTableEntry groupName = getGroup() ? getGroup()->getName() : NULL; if (groupName != NULL) groupName = getGroup()->getIdString(); String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); - Con::evaluate(command.c_str(), false, context); + Con::evaluate(command.c_str(), false, context.c_str()); return Con::getBoolVariable(resVar.c_str()); } return true; diff --git a/Engine/source/T3D/missionMarker.cpp b/Engine/source/T3D/missionMarker.cpp index 9acf4c61c..a3e380951 100644 --- a/Engine/source/T3D/missionMarker.cpp +++ b/Engine/source/T3D/missionMarker.cpp @@ -372,12 +372,12 @@ bool SpawnSphere::testCondition() if (objectName != NULL) objectName = getIdString(); - StringTableEntry groupName = getGroup()->getName(); + StringTableEntry groupName = getGroup() ? getGroup()->getName() : NULL; if (groupName != NULL) groupName = getGroup()->getIdString(); String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); - Con::evaluate(command.c_str(), false, context); + Con::evaluate(command.c_str(), false, context.c_str()); if (Con::getBoolVariable(resVar.c_str()) == 1) { return true; diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp index 33075e70d..2558c45d5 100644 --- a/Engine/source/T3D/trigger.cpp +++ b/Engine/source/T3D/trigger.cpp @@ -711,12 +711,12 @@ bool Trigger::testCondition() if (objectName != NULL) objectName = getIdString(); - StringTableEntry groupName = getGroup()->getName(); + StringTableEntry groupName = getGroup() ? getGroup()->getName() : NULL; if (groupName != NULL) groupName = getGroup()->getIdString(); String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); - Con::evaluate(command.c_str(), false, context); + Con::evaluate(command.c_str(), false, context.c_str()); if (Con::getBoolVariable(resVar.c_str()) == 1) { return true; @@ -817,7 +817,7 @@ void Trigger::processTick(const Move* move) if (objectName != NULL) objectName = getIdString(); - StringTableEntry groupName = getGroup()->getName(); + StringTableEntry groupName = getGroup() ? getGroup()->getName() : NULL; if (groupName != NULL) groupName = getGroup()->getIdString(); diff --git a/Engine/source/console/torquescript/codeBlock.cpp b/Engine/source/console/torquescript/codeBlock.cpp index d3bf6b3f5..2696ca054 100644 --- a/Engine/source/console/torquescript/codeBlock.cpp +++ b/Engine/source/console/torquescript/codeBlock.cpp @@ -611,7 +611,7 @@ Con::EvalResult CodeBlock::compileExec(StringTableEntry fileName, const char *in Script::gStatementList = NULL; // we are an eval compile if we don't have a file name associated (no exec) - gIsEvalCompile = fileName == NULL; + gIsEvalCompile = fileName == NULL || setFrame == 0; gFuncVars = gIsEvalCompile ? &gEvalFuncVars : &gGlobalScopeFuncVars; // Set up the parser. diff --git a/Engine/source/console/torquescript/runtime.cpp b/Engine/source/console/torquescript/runtime.cpp index 45c3249ee..1f0db44d3 100644 --- a/Engine/source/console/torquescript/runtime.cpp +++ b/Engine/source/console/torquescript/runtime.cpp @@ -38,8 +38,10 @@ namespace TorqueScript if (fileName) fileName = StringTable->insert(fileName); + bool fileExec = Torque::FS::IsFile(fileName); + CodeBlock* newCodeBlock = new CodeBlock(); - return (newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0)); + return (newCodeBlock->compileExec(fileName, string, false, fileExec ? -1 : 0)); } Con::EvalResult TorqueScriptRuntime::evaluate(const char* script, S32 frame, bool echo, const char* fileName) diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp index 2509c8cbd..6ceb1cbcd 100644 --- a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp +++ b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp @@ -976,7 +976,7 @@ void GuiGameListMenuCtrl::doScriptCommand(StringTableEntry command) setThisControl(); StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); String context = String::ToString("%s\nObject: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); - Con::evaluate(command, false, context); + Con::evaluate(command, false, context.c_str()); } } diff --git a/Engine/source/gui/controls/guiListBoxCtrl.cpp b/Engine/source/gui/controls/guiListBoxCtrl.cpp index 5a5a81e6a..58441dd46 100644 --- a/Engine/source/gui/controls/guiListBoxCtrl.cpp +++ b/Engine/source/gui/controls/guiListBoxCtrl.cpp @@ -1541,7 +1541,7 @@ StringTableEntry GuiListBoxCtrl::_makeMirrorItemName( SimObject *inObj ) StringTableEntry objectName = getName() != StringTable->EmptyString() ? getName() : getInternalName(); String context = String::ToString("%s, Object: %s", Platform::makeRelativePathName(getFilename(), NULL), objectName); - outName = StringTable->insert( Con::evaluate( mMakeNameCallback, false, context).value, true ); + outName = StringTable->insert( Con::evaluate( mMakeNameCallback, false, context.c_str()).value, true ); } else if ( inObj->getName() ) outName = StringTable->insert( inObj->getName() ); diff --git a/Engine/source/gui/core/guiControl.cpp b/Engine/source/gui/core/guiControl.cpp index b7b272612..bf9eec103 100644 --- a/Engine/source/gui/core/guiControl.cpp +++ b/Engine/source/gui/core/guiControl.cpp @@ -2496,14 +2496,15 @@ const char* GuiControl::evaluate( const char* str ) { smThisControl = this; StringTableEntry objectName = getName(); - if (getName() == NULL) - objectName = getInternalName(); - StringTableEntry fileName = getFilename(); - if (fileName != NULL) - fileName = Platform::makeRelativePathName(fileName, NULL); + if (objectName != NULL) + objectName = getIdString(); - String context = String::ToString("%s\nObject: %s", fileName, objectName); - const char* result = Con::evaluate(str, false, context).value; + StringTableEntry groupName = getGroup() ? getGroup()->getName() : NULL; + if (groupName != NULL) + groupName = getGroup()->getIdString(); + + String context = String::ToString("%s\nGroup: %s, Object: %s", getFilename(), groupName, objectName); + const char* result = Con::evaluate(str, false, context.c_str()).value; smThisControl = NULL; return result; From 001468cb5e894def09334475697292719a2ab5d9 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 2 Mar 2026 17:08:12 -0600 Subject: [PATCH 15/16] tone down overcorrection --- Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl | 2 +- Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl index a1df93c70..addc16541 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl +++ b/Templates/BaseGame/game/core/rendering/shaders/gl/lighting.glsl @@ -243,7 +243,7 @@ vec3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) float denominator = 4.0 * max(surface.NdotV, 0.0) * max(surfaceToLight.NdotL, 0.0) + 0.0001; vec3 specularBRDF = numerator / denominator; - vec3 diffuseBRDF = surface.baseColor.rgb * surface.ao * M_PI_F; + vec3 diffuseBRDF = surface.baseColor.rgb * surface.ao * M_HALFPI_F; // Final output combining all terms vec3 kS = F; // Specular reflectance diff --git a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl index 015c925bf..8b544ca9c 100644 --- a/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl +++ b/Templates/BaseGame/game/core/rendering/shaders/lighting.hlsl @@ -243,7 +243,7 @@ float3 evaluateStandardBRDF(Surface surface, SurfaceToLight surfaceToLight) float denominator = 4.0 * max(surface.NdotV, 0.0) * max(surfaceToLight.NdotL, 0.0) + 0.0001; float3 specularBRDF = numerator / denominator; - float3 diffuseBRDF = surface.baseColor.rgb * surface.ao* M_PI_F; + float3 diffuseBRDF = surface.baseColor.rgb * surface.ao* M_HALFPI_F; // Final output combining all terms float3 kS = F; // Specular reflectance From 0418aaa9dbb4ac5d2f1f7ef4a8841be4a3a701dd Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 4 Mar 2026 10:15:46 -0600 Subject: [PATCH 16/16] from marauder: cover corrections --- Engine/source/T3D/AI/AIController.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 361e4f518..fdd9eb9df 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -74,7 +74,7 @@ public: { if (mCover && mCover->mPosSet && mCover->getPosition() == loc) { - mCover->mRadius == rad; + mCover->mRadius = rad; return; } delete(mCover); mCover = new AICover(this, loc, rad); @@ -83,7 +83,7 @@ public: { if (mCover && mCover->mObj == objIn) { - mCover->mRadius == rad; + mCover->mRadius = rad; return; } delete(mCover); mCover = new AICover(this, objIn, rad);