diff --git a/Core/GameEngine/Include/Common/GameDefines.h b/Core/GameEngine/Include/Common/GameDefines.h index 667ea8aa9ff..fbbf5c92392 100644 --- a/Core/GameEngine/Include/Common/GameDefines.h +++ b/Core/GameEngine/Include/Common/GameDefines.h @@ -50,6 +50,11 @@ #define RETAIL_COMPATIBLE_AIGROUP (1) // AIGroup logic is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04 #endif +// IamInnocent - DRAWUPDATE is Retail CRC Compatible, but might need more checking. Remove this comment once this check is done. +#ifndef RETAIL_COMPATIBLE_DRAWUPDATE +#define RETAIL_COMPATIBLE_DRAWUPDATE (1) // Drawable Update logic is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04 +#endif + #ifndef ENABLE_GAMETEXT_SUBSTITUTES #define ENABLE_GAMETEXT_SUBSTITUTES (1) // The code can provide substitute texts when labels and strings are missing in the STR or CSF translation file #endif diff --git a/Core/GameEngine/Include/GameClient/View.h b/Core/GameEngine/Include/GameClient/View.h index 9d26976debb..7fefca3cd0f 100644 --- a/Core/GameEngine/Include/GameClient/View.h +++ b/Core/GameEngine/Include/GameClient/View.h @@ -234,6 +234,10 @@ class View : public Snapshot virtual void forceCameraConstraintRecalc(void) {} virtual void setGuardBandBias( const Coord2D *gb ) = 0; +#if !RETAIL_COMPATIBLE_DRAWUPDATE + virtual void setUpdateEfficient(void) {} +#endif + protected: friend class Display; diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h index df1dc768794..39a2c8ff5e2 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h @@ -230,6 +230,10 @@ class W3DView : public View, public SubsystemInterface virtual void setGuardBandBias( const Coord2D *gb ) { m_guardBandBias.x = gb->x; m_guardBandBias.y = gb->y; } +#if !RETAIL_COMPATIBLE_DRAWUPDATE + virtual void setUpdateEfficient(void) { m_updateEfficient = TRUE; } +#endif + private: @@ -296,6 +300,11 @@ class W3DView : public View, public SubsystemInterface Bool m_useRealZoomCam; AsciiString m_cameraSlaveObjectName; AsciiString m_cameraSlaveObjectBoneName; + +#if !RETAIL_COMPATIBLE_DRAWUPDATE + // Efficient Draw Update + Bool m_updateEfficient; +#endif }; // EXTERNALS ////////////////////////////////////////////////////////////////////////////////////// diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp index 704f000e259..4710f8eeb6b 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp @@ -75,6 +75,10 @@ #include "GameLogic/TerrainLogic.h" ///< @todo This should be TerrainVisual (client side) #include "Common/AudioEventInfo.h" +#if !RETAIL_COMPATIBLE_DRAWUPDATE +#include "GameLogic/PartitionManager.h" +#endif + #include "W3DDevice/Common/W3DConvert.h" #include "W3DDevice/GameClient/HeightMap.h" #include "W3DDevice/GameClient/W3DAssetManager.h" @@ -171,6 +175,9 @@ W3DView::W3DView() m_shakerAngles.X =0.0f; // Proper camera shake generator & sources m_shakerAngles.Y =0.0f; m_shakerAngles.Z =0.0f; +#if !RETAIL_COMPATIBLE_DRAWUPDATE + m_updateEfficient = false; +#endif } @@ -624,6 +631,12 @@ void W3DView::setCameraTransform( void ) it = NULL; } } + +#if !RETAIL_COMPATIBLE_DRAWUPDATE + // Redraw everything + TheGameClient->clearEfficientDrawablesList(); + m_updateEfficient = TRUE; +#endif } //------------------------------------------------------------------------------------------------- @@ -1401,12 +1414,25 @@ void W3DView::update(void) TheTerrainVisual->updateSeismicSimulations(); #endif + // render all of the visible Drawables + /// @todo this needs to use a real region partition or something + //// TheSuperHackers @performance IamInnocent 01/01/26 - Implemented an Efficient Drawable Iteration Function for Drawables Under Screen Region +#if !RETAIL_COMPATIBLE_DRAWUPDATE + if(WW3D::Get_Sync_Frame_Time() || m_updateEfficient) + { + Region3D axisAlignedRegion; + getAxisAlignedViewRegion(axisAlignedRegion); + + TheGameClient->setEfficientDrawableRegion(&axisAlignedRegion); + TheGameClient->iterateDrawablesInRegion( &axisAlignedRegion, drawDrawable, NULL ); + m_updateEfficient = FALSE; + } +#else Region3D axisAlignedRegion; getAxisAlignedViewRegion(axisAlignedRegion); - // render all of the visible Drawables - /// @todo this needs to use a real region partition or something TheGameClient->iterateDrawablesInRegion( &axisAlignedRegion, drawDrawable, NULL ); +#endif } //------------------------------------------------------------------------------------------------- @@ -2126,10 +2152,11 @@ Int W3DView::iterateDrawablesInRegion( IRegion2D *screenRegion, regionIsPoint = TRUE; } - normalizedRegion.lo.x = ((Real)(screenRegion->lo.x - m_originX) / (Real)getWidth()) * 2.0f - 1.0f; - normalizedRegion.lo.y = -(((Real)(screenRegion->hi.y - m_originY) / (Real)getHeight()) * 2.0f - 1.0f); - normalizedRegion.hi.x = ((Real)(screenRegion->hi.x - m_originX) / (Real)getWidth()) * 2.0f - 1.0f; - normalizedRegion.hi.y = -(((Real)(screenRegion->lo.y - m_originY) / (Real)getHeight()) * 2.0f - 1.0f); + // Changed to use fast int->real type casts + normalizedRegion.lo.x = (INT_TO_REAL(screenRegion->lo.x - m_originX) / INT_TO_REAL(getWidth())) * 2.0f - 1.0f; + normalizedRegion.lo.y = -((INT_TO_REAL(screenRegion->hi.y - m_originY) / INT_TO_REAL(getHeight())) * 2.0f - 1.0f); + normalizedRegion.hi.x = (INT_TO_REAL(screenRegion->hi.x - m_originX) / INT_TO_REAL(getWidth())) * 2.0f - 1.0f; + normalizedRegion.hi.y = -((INT_TO_REAL(screenRegion->lo.y - m_originY) / INT_TO_REAL(getHeight())) * 2.0f - 1.0f); } @@ -2144,6 +2171,56 @@ Int W3DView::iterateDrawablesInRegion( IRegion2D *screenRegion, } } +#if !RETAIL_COMPATIBLE_DRAWUPDATE + // TheSuperHackers @performance IamInnocent 01/01/2026 - Uses Partition Manager to find Drawables that fits onto the region. + /// This function is mainly used During Selection. + if(screenRegion != NULL && !onlyDrawableToTest && !ThePartitionManager->hasNoOffset()) + { + std::list< Drawable* > drawables = ThePartitionManager->getDrawablesInRegion( screenRegion ); + + for( std::list< Drawable* >::iterator it = drawables.begin(); it != drawables.end(); ++it ) + { + // not inside + inside = FALSE; + + // project the center of the drawable to the screen + /// @todo use a real 3D position in the drawable + pos = *(*it)->getPosition(); + world.X = pos.x; + world.Y = pos.y; + world.Z = pos.z; + + // project the world point to the screen + if( m_3DCamera->Project( screen, world ) == CameraClass::INSIDE_FRUSTUM && + screen.X >= normalizedRegion.lo.x && + screen.X <= normalizedRegion.hi.x && + screen.Y >= normalizedRegion.lo.y && + screen.Y <= normalizedRegion.hi.y ) + { + + inside = TRUE; + + } // end if + + // if inside do the callback and count up + if( inside ) + { + + if( callback( (*it), userData ) ) + ++count; + + } // end if + + // If onlyDrawableToTest, then we should bail out now. + //if (onlyDrawableToTest != NULL) + // break; + + } // end for draw + + return count; + } +#endif + for( draw = TheGameClient->firstDrawable(); draw; draw = draw->getNextDrawable() ) diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h index 3a0d32e59f6..148a656e198 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h @@ -155,6 +155,13 @@ class GameClient : public SubsystemInterface, void incrementRenderedObjectCount() { m_renderedObjectCount++; } virtual void notifyTerrainObjectMoved(Object *obj) = 0; +#if !RETAIL_COMPATIBLE_DRAWUPDATE + void informClientNewDrawable(Drawable *draw); + void addDrawableToEfficientList(Drawable *draw); + void clearEfficientDrawablesList() { m_drawablesIterateListMarkedForClear = TRUE; } + void setEfficientDrawableRegion(Region3D *region) { m_axisAlignedRegion.lo = region->lo; m_axisAlignedRegion.hi = region->hi; } + Region3D *getEfficientDrawableRegion() { return &m_axisAlignedRegion; } +#endif protected: @@ -213,6 +220,13 @@ class GameClient : public SubsystemInterface, typedef std::list< Drawable* > TextBearingDrawableList; typedef TextBearingDrawableList::iterator TextBearingDrawableListIterator; TextBearingDrawableList m_textBearingDrawableList; ///< the drawables that have registered here during drawablepostdraw + +#if !RETAIL_COMPATIBLE_DRAWUPDATE + std::list< Drawable* > m_drawablesIterateList; + Bool m_drawablesIterateListMarkedForClear; + + Region3D m_axisAlignedRegion; +#endif }; //Kris: Try not to use this if possible. In every case I found in the code base, the status was always Drawable::SELECTED. diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/PartitionManager.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/PartitionManager.h index 627f5494d51..3a44d0068b9 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/PartitionManager.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/PartitionManager.h @@ -92,6 +92,7 @@ class Team; class ThingTemplate; class GhostObject; class CommandButton; +class Drawable; enum CommandSourceType CPP_11(: Int); @@ -1528,6 +1529,9 @@ class PartitionManager : public SubsystemInterface, public Snapshot // If saveToFog is false, then we are writing STORE_PERMENANT_REVEAL void storeFoggedCells(ShroudStatusStoreRestore &outPartitionStore, Bool storeToFog) const; void restoreFoggedCells(const ShroudStatusStoreRestore &inPartitionStore, Bool restoreToFog); + + std::list getDrawablesInRegion( IRegion2D *region2D ); + Bool hasNoOffset() const { return m_radiusVec.empty(); } }; // ----------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/Thing.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/Thing.cpp index 6fc61f7be96..cf14c59f685 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/Thing.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/Thing.cpp @@ -46,6 +46,9 @@ #include "Lib/trig.h" #include "GameLogic/TerrainLogic.h" +#if !RETAIL_COMPATIBLE_DRAWUPDATE +#include "GameClient/GameClient.h" +#endif static constexpr const Real InitialThingPosX = 0.0f; static constexpr const Real InitialThingPosY = 0.0f; @@ -169,6 +172,15 @@ void Thing::setPositionZ( Real z ) setTransformMatrix(&mtx); } DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'", m_template->getName().str() )); +#if !RETAIL_COMPATIBLE_DRAWUPDATE + if(TheGameClient) + { + if(AsObject(this) && AsObject(this)->getDrawable()) + TheGameClient->informClientNewDrawable(AsObject(this)->getDrawable()); + else if(AsDrawable(this)) + TheGameClient->informClientNewDrawable(AsDrawable(this)); + } +#endif } //============================================================================= @@ -198,6 +210,15 @@ void Thing::setPosition( const Coord3D *pos ) setTransformMatrix(&mtx); } DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'", m_template->getName().str() )); +#if !RETAIL_COMPATIBLE_DRAWUPDATE + if(TheGameClient) + { + if(AsObject(this) && AsObject(this)->getDrawable()) + TheGameClient->informClientNewDrawable(AsObject(this)->getDrawable()); + else if(AsDrawable(this)) + TheGameClient->informClientNewDrawable(AsDrawable(this)); + } +#endif } //============================================================================= diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp index 9f1f5f7eafe..1815fac09cf 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp @@ -2646,7 +2646,11 @@ void Drawable::draw() applyPhysicsXform(&transformMtx); } +#if RETAIL_COMPATIBLE_DRAWUPDATE for (DrawModule** dm = getDrawModules(); *dm; ++dm) +#else + for (DrawModule** dm = getDrawModules(); dm != nullptr && *dm; ++dm) +#endif { (*dm)->doDrawModule(&transformMtx); } @@ -4070,7 +4074,11 @@ void Drawable::replaceModelConditionStateInDrawable() const TerrainDecalType terrainDecalType = getTerrainDecalType(); setTerrainDecal(TERRAIN_DECAL_NONE); +#if RETAIL_COMPATIBLE_DRAWUPDATE for (DrawModule** dm = getDrawModules(); *dm; ++dm) +#else + for (DrawModule** dm = getDrawModules(); dm != nullptr && *dm; ++dm) +#endif { ObjectDrawInterface* di = (*dm)->getObjectDrawInterface(); if (di) @@ -4650,6 +4658,9 @@ void Drawable::updateHiddenStatus() di->setHidden(hidden != 0); } +#if !RETAIL_COMPATIBLE_DRAWUPDATE + TheGameClient->informClientNewDrawable(this); +#endif } //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp index 4b8c9f00e9b..3de0cbea9fb 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp @@ -109,6 +109,18 @@ GameClient::GameClient() m_nextDrawableID = (DrawableID)1; TheDrawGroupInfo = new DrawGroupInfo; + +#if !RETAIL_COMPATIBLE_DRAWUPDATE + m_drawablesIterateList.clear(); + m_drawablesIterateListMarkedForClear = FALSE; + + m_axisAlignedRegion.lo.x = 0.0f; + m_axisAlignedRegion.lo.y = 0.0f; + m_axisAlignedRegion.lo.z = 0.0f; + m_axisAlignedRegion.hi.x = 0.0f; + m_axisAlignedRegion.hi.y = 0.0f; + m_axisAlignedRegion.hi.z = 0.0f; +#endif } //std::vector preloadTextureNamesGlobalHack; @@ -128,6 +140,18 @@ GameClient::~GameClient() // clear any drawable TOC we might have m_drawableTOC.clear(); +#if !RETAIL_COMPATIBLE_DRAWUPDATE + m_drawablesIterateList.clear(); + m_drawablesIterateListMarkedForClear = FALSE; + + m_axisAlignedRegion.lo.x = 0.0f; + m_axisAlignedRegion.lo.y = 0.0f; + m_axisAlignedRegion.lo.z = 0.0f; + m_axisAlignedRegion.hi.x = 0.0f; + m_axisAlignedRegion.hi.y = 0.0f; + m_axisAlignedRegion.hi.z = 0.0f; +#endif + //DEBUG_LOG(("Preloaded texture files ------------------------------------------")); //for (Int oog=0; oog::iterator it = m_drawablesIterateList.begin(); it != m_drawablesIterateList.end();) + { + Coord3D pos = *(*it)->getPosition(); + if( pos.x >= region->lo.x && pos.x <= region->hi.x && + pos.y >= region->lo.y && pos.y <= region->hi.y && + pos.z >= region->lo.z && pos.z <= region->hi.z ) + { + (*userFunc)( (*it), userData ); + } + else + { + it = m_drawablesIterateList.erase(it); + continue; + } + ++it; + } + + // We stop here + return; + } +#endif + for( draw = m_drawableList; draw; draw=nextDrawable ) { nextDrawable = draw->getNextDrawable(); @@ -809,11 +878,54 @@ void GameClient::iterateDrawablesInRegion( Region3D *region, GameClientFuncPtr u pos.y >= region->lo.y && pos.y <= region->hi.y && pos.z >= region->lo.z && pos.z <= region->hi.z) ) { +#if !RETAIL_COMPATIBLE_DRAWUPDATE + addDrawableToEfficientList(draw); +#endif (*userFunc)( draw, userData ); } } } +#if !RETAIL_COMPATIBLE_DRAWUPDATE +/** ----------------------------------------------------------------------------------------------- + * Inform the Client to add this Unit to the Efficient Drawable List + */ +void GameClient::informClientNewDrawable(Drawable *draw) +{ + // sanity + if( draw == NULL ) + return; + + // Only inform New Drawable if the drawables are iterated at least once throughout the whole map. + if(m_drawablesIterateList.empty()) + return; + + Coord3D currPos = *draw->getPosition(); + Region3D *region = getEfficientDrawableRegion(); + if( currPos.x <= region->lo.x || currPos.x >= region->hi.x || + currPos.y <= region->lo.y || currPos.y >= region->hi.y || + currPos.z <= region->lo.z || currPos.z >= region->hi.z ) + return; + + TheTacticalView->setUpdateEfficient(); + + addDrawableToEfficientList(draw); +} + +/** ----------------------------------------------------------------------------------------------- + * Add drawable to the Efficient Drawable List + */ +void GameClient::addDrawableToEfficientList(Drawable *draw) +{ + // Only add the Drawable if it is not within the Iterate List + std::list< Drawable* >::iterator it = std::find(m_drawablesIterateList.begin(), m_drawablesIterateList.end(), draw); + if (it == m_drawablesIterateList.end()) + { + m_drawablesIterateList.push_back( draw ); + } +} +#endif + /**Helper function to update fake GLA structures to become visible to certain players. We should only call this during critical moments, such as changing teams, changing to observer, etc.*/ diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 7052147358b..e789fc0971b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -4810,6 +4810,10 @@ void Object::handlePartitionCellMaintenance() handleShroud(); handleValueMap(); handleThreatMap(); + +#if !RETAIL_COMPATIBLE_DRAWUPDATE + TheGameClient->informClientNewDrawable(getDrawable()); +#endif } //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp index e5f48c12136..ba7eb9d48e1 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp @@ -4939,6 +4939,161 @@ void PartitionManager::restoreFoggedCells(const ShroudStatusStoreRestore &inPart } } +//----------------------------------------------------------------------------- +std::list PartitionManager::getDrawablesInRegion( IRegion2D *region2D ) +{ + //IamInnocent 6/10/2025 - Uses PartitionManager to Find Drawables within a Region + std::list drawables; + + ICoord2D loRegion, hiRegion; + Coord3D loWorld, hiWorld; + + if(region2D != NULL) + { + loRegion.x = region2D->lo.x; + loRegion.y = region2D->lo.y; + hiRegion.x = region2D->hi.x; + hiRegion.y = region2D->hi.y; + } + else + { + // This function is used to find the Whole Screen Region, currently it is not needed. + /*IRegion2D region; + ICoord2D origin; + ICoord2D size; + + TheTacticalView->getOrigin( &origin.x, &origin.y ); + size.x = TheTacticalView->getWidth(); + size.y = TheTacticalView->getHeight(); + + TheInGameUI->buildRegion( &origin, &size, ®ion ); + loRegion.x = region.lo.x; + loRegion.y = region.lo.y; + hiRegion.x = region.hi.x; + hiRegion.y = region.hi.y; + */ + return drawables; + } + TheTacticalView->screenToTerrain( &loRegion, &loWorld ); + TheTacticalView->screenToTerrain( &hiRegion, &hiWorld ); + + Int cellCenterX, cellCenterY; + Real centerX = loWorld.x + (hiWorld.x - loWorld.x) / 2; + Real centerY = loWorld.y + (hiWorld.y - loWorld.y) / 2; + worldToCell(centerX, centerY, &cellCenterX, &cellCenterY); + + /* + m_radiusVec[curRadius] contains a list of the cells (foo) that could + contain objects that are <= (curRadius * cellSize) distance away from cell (0,0). + */ +#ifdef FASTER_GCO + + Int maxRadius = m_maxGcoRadius; + Real maxDist = hiWorld.x - loWorld.x > loWorld.y - hiWorld.y ? hiWorld.x - loWorld.x : loWorld.y - hiWorld.y; + if (maxDist < HUGE_DIST) + { + // don't go outwards any farther than necessary. + /// Need to increase the minimum value to be able to select air units under a small region + maxRadius = minInt(m_maxGcoRadius, worldToCellDist(max(200.0f,maxDist))); + } +#if defined(INTENSE_DEBUG) + /* + Note, if you ever enable this code, be forewarned that it can give + you "false positives" for objects that are located just off the map... (srj) + */ + Int maxRadiusLimit = maxRadius + 3; + if (maxRadiusLimit > m_maxGcoRadius) maxRadiusLimit = m_maxGcoRadius; +#else + Int maxRadiusLimit = maxRadius; +#endif + + static Int theIterFlag = 1; // nonzero, thanks + ++theIterFlag; + + /* + m_radiusVec[curRadius] contains a list of the cells (foo) that could + contain objects that are <= (curRadius * cellSize) distance away from cell (0,0). + */ + for (Int curRadius = 0; curRadius <= maxRadiusLimit; ++curRadius) + { + const OffsetVec& offsets = m_radiusVec[curRadius]; + if (offsets.empty()) + continue; + for (OffsetVec::const_iterator it = offsets.begin(); it != offsets.end(); ++it) + { + PartitionCell* thisCell = getCellAt(cellCenterX + it->x, cellCenterY + it->y); + if (thisCell == NULL) + continue; + + for (CellAndObjectIntersection *thisCoi = thisCell->getFirstCoiInCell(); thisCoi; thisCoi = thisCoi->getNextCoi()) + { + PartitionData *thisMod = thisCoi->getModule(); + Object *thisObj = thisMod->getObject(); + + if (thisObj == NULL) + continue; + + // since an object can exist in multiple COIs, we use this to avoid processing + // the same one more than once. + if (thisMod->friend_getDoneFlag() == theIterFlag) + continue; + thisMod->friend_setDoneFlag(theIterFlag); + + if (!thisObj->getDrawable()) + continue; + + drawables.push_back( thisObj->getDrawable() ); + + } // next coi + } // next cell in this radius + } // next radius + +#else // not FASTER_GCO + + CellOutwardIterator iter(this, cellCenterX, cellCenterY); + if (maxDist < HUGE_DIST) + { + // don't go outwards any farther than necessary. + Int max = worldToCellDist(maxDist) + 1; + // default value for "max" is largest possible, based on map size, so we should + // never make it any larger than that + if (max < iter.getMaxRadius()) + iter.setMaxRadius(max); + } + + static Int theIterFlag = 1; // nonzero, thanks + ++theIterFlag; + + PartitionCell *thisCell; + while ((thisCell = iter.nextNonEmpty()) != NULL) + { + CellAndObjectIntersection *nextCoi; + for (CellAndObjectIntersection *thisCoi = thisCell->getFirstCoiInCell(); thisCoi; thisCoi = nextCoi) + { + nextCoi = thisCoi->getNextCoi(); + + PartitionData *thisMod = thisCoi->getModule(); + + Object *thisObj = thisMod->getObject(); + + if (thisObj == NULL) + continue; + + if (thisMod->friend_getDoneFlag() == theIterFlag) + continue; + + thisMod->friend_setDoneFlag(theIterFlag); + + if (!thisObj->getDrawable()) + continue; + + drawables.push_back( thisObj->getDrawable() ); + } + } + +#endif // not FASTER_GCO + return drawables; +} //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp index ed6fc31c190..02bab10f649 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp @@ -46,6 +46,10 @@ #include "GameLogic/Weapon.h" #include "GameLogic/LogicRandomValue.h" +#if !RETAIL_COMPATIBLE_DRAWUPDATE +#include "GameClient/GameClient.h" +#endif + const Real DEFAULT_MASS = 1.0f; const Real DEFAULT_SHOCK_YAW = 0.05f; @@ -908,6 +912,12 @@ UpdateSleepTime PhysicsBehavior::update() } } +#if !RETAIL_COMPATIBLE_DRAWUPDATE + // Always inform the Drawable for Physics, since the Object may end up within the Screen Region and it will need to be Registered. + if(obj->getDrawable()) + TheGameClient->informClientNewDrawable(obj->getDrawable()); +#endif + setFlag(UPDATE_EVER_RUN, true); setFlag(WAS_AIRBORNE_LAST_FRAME, airborneAtEnd); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp index a390bc943f5..210e3fc33b8 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp @@ -784,6 +784,14 @@ UpdateSleepTime StealthUpdate::update( void ) } self->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DETECTED ) ); + +#if !RETAIL_COMPATIBLE_DRAWUPDATE + if(draw && ThePlayerList->getLocalPlayer()->getRelationship(self->getTeam()) == ENEMIES) + { + // Redraw everything as Stealth Detection bugs out how existing Drawables work + TheGameClient->clearEfficientDrawablesList(); + } +#endif } else { @@ -1109,6 +1117,14 @@ void StealthUpdate::changeVisualDisguise() self->clearModelConditionState( MODELCONDITION_DISGUISED ); } +#if !RETAIL_COMPATIBLE_DRAWUPDATE + if(ThePlayerList->getLocalPlayer()->getRelationship(self->getTeam()) == ENEMIES) + { + // Redraw everything as Stealth Detection bugs out how existing Drawables work + TheGameClient->clearEfficientDrawablesList(); + } +#endif + //Reset the radar (determines color on add) TheRadar->removeObject( self ); TheRadar->addObject( self ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp index c729fb81cde..1d59ffd6bd2 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp @@ -4234,6 +4234,10 @@ void GameLogic::bindObjectAndDrawable(Object* obj, Drawable* draw) { draw->friend_bindToObject( obj ); obj->friend_bindToDrawable( draw ); + +#if !RETAIL_COMPATIBLE_DRAWUPDATE + TheGameClient->informClientNewDrawable(draw); +#endif } // ------------------------------------------------------------------------------------------------