| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: Thing.cpp ////////////////////////////////////////////////////////////
- // Created: Colin Day, May 2001
- //
- // Desc: Things are the base class for objects and drawables, objects
- // are logic side representations while drawables are client
- // side. Common data will be held in the Thing defined here
- // and systems that need to work with both of them will work with
- // "Things"
- //
- //-----------------------------------------------------------------------------
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/PerfTimer.h"
- #include "Common/Thing.h"
- #include "Common/ThingTemplate.h"
- #include "Common/ThingFactory.h"
- #include "Common/GlobalData.h"
- #include "Common/NameKeyGenerator.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/Team.h"
- #include "Lib/Trig.h"
- #include "GameLogic/TerrainLogic.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //=============================================================================
- /** Constructor */
- //=============================================================================
- Thing::Thing( const ThingTemplate *thingTemplate )
- {
- // sanity
- if( thingTemplate == NULL )
- {
-
- // cannot create thing without template
- DEBUG_CRASH(( "no template" ));
- return;
- } // end if
-
- m_template = thingTemplate;
- #if defined(_DEBUG) || defined(_INTERNAL)
- m_templateName = thingTemplate->getName();
- #endif
- m_transform.Make_Identity();
- m_cachedPos.zero();
- m_cachedAngle = 0.0f;
- m_cachedDirVector.zero();
- m_cachedAltitudeAboveTerrain = 0;
- m_cachedAltitudeAboveTerrainOrWater = 0;
- m_cacheFlags = 0;
- }
- //=============================================================================
- /** Destructor */
- //=============================================================================
- Thing::~Thing()
- {
- }
- //DECLARE_PERF_TIMER(ThingMatrixStuff)
- //=============================================================================
- const ThingTemplate *Thing::getTemplate() const
- {
- return m_template;
- }
- //=============================================================================
- const Coord3D* Thing::getUnitDirectionVector2D() const
- {
- //USE_PERF_TIMER(ThingMatrixStuff)
- if (!(m_cacheFlags & VALID_DIRVECTOR))
- {
- Real angle = getOrientation();
- m_cachedDirVector.x = Cos( angle );
- m_cachedDirVector.y = Sin( angle );
- m_cachedDirVector.z = 0;
- m_cacheFlags |= VALID_DIRVECTOR;
- }
- return &m_cachedDirVector;
- }
- //=============================================================================
- void Thing::getUnitDirectionVector2D(Coord3D& dir) const
- {
- dir = *getUnitDirectionVector2D();
- }
- //=============================================================================
- void Thing::getUnitDirectionVector3D(Coord3D& dir) const
- {
- Vector3 vdir = m_transform.Get_X_Vector();
- vdir.Normalize();
- dir.x = vdir.X;
- dir.y = vdir.Y;
- dir.z = vdir.Z;
- }
- //=============================================================================
- // the nice thing about this is that we don't have to recalc out cached terrain stuff.
- void Thing::setPositionZ( Real z )
- {
- //USE_PERF_TIMER(ThingMatrixStuff)
- if( !m_template->isKindOf( KINDOF_STICK_TO_TERRAIN_SLOPE) )
- {
- Real oldAngle = m_cachedAngle;
- Coord3D oldPos = m_cachedPos;
- Matrix3D oldMtx = m_transform;
- m_transform.Set_Z_Translation( z );
- m_cachedPos.z = z;
- if (m_cacheFlags & VALID_ALTITUDE_TERRAIN)
- {
- m_cachedAltitudeAboveTerrain += (z - oldPos.z);
- }
- if (m_cacheFlags & VALID_ALTITUDE_SEALEVEL)
- {
- m_cachedAltitudeAboveTerrainOrWater += (z - oldPos.z);
- }
- reactToTransformChange(&oldMtx, &oldPos, oldAngle);
- }
- else
- {
- Matrix3D mtx;
- const Bool stickToGround = true; // yes, set the "z" pos
- Coord3D pos = m_cachedPos;
- pos.z = z;
- TheTerrainLogic->alignOnTerrain(getOrientation(), pos, stickToGround, mtx );
- setTransformMatrix(&mtx);
- }
- DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
- }
- //=============================================================================
- void Thing::setPosition( const Coord3D *pos )
- {
- //USE_PERF_TIMER(ThingMatrixStuff)
- if( !m_template->isKindOf( KINDOF_STICK_TO_TERRAIN_SLOPE) )
- {
- Real oldAngle = m_cachedAngle;
- Coord3D oldPos = m_cachedPos;
- Matrix3D oldMtx = m_transform;
- //DEBUG_ASSERTCRASH(!(_isnan(pos->x) || _isnan(pos->y) || _isnan(pos->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
- m_transform.Set_X_Translation( pos->x );
- m_transform.Set_Y_Translation( pos->y );
- m_transform.Set_Z_Translation( pos->z );
- m_cachedPos = *pos;
- m_cacheFlags &= ~(VALID_ALTITUDE_TERRAIN | VALID_ALTITUDE_SEALEVEL); // but don't clear the dir flags.
- reactToTransformChange(&oldMtx, &oldPos, oldAngle);
- }
- else
- {
- Matrix3D mtx;
- const Bool stickToGround = true; // yes, set the "z" pos
- TheTerrainLogic->alignOnTerrain(getOrientation(), *pos, stickToGround, mtx );
- setTransformMatrix(&mtx);
- }
- DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
- }
- //=============================================================================
- void Thing::setOrientation( Real angle )
- {
- //USE_PERF_TIMER(ThingMatrixStuff)
- Coord3D u, x, y, z, pos;
- // setOrientation always forces us straight up in the Z axis,
- // or aligned with the terrain if we have the magic flag set.
- // don't want this? call setTransformMatrix instead.
- Real oldAngle = m_cachedAngle;
- Coord3D oldPos = m_cachedPos;
- Matrix3D oldMtx = m_transform;
- pos.x = m_transform.Get_X_Translation();
- pos.y = m_transform.Get_Y_Translation();
- pos.z = m_transform.Get_Z_Translation();
- if( m_template->isKindOf( KINDOF_STICK_TO_TERRAIN_SLOPE) )
- {
- Matrix3D mtx;
- const Bool stickToGround = true; // yes, set the "z" pos
- TheTerrainLogic->alignOnTerrain(angle, pos, stickToGround, m_transform );
- }
- else
- {
- z.x = 0.0f;
- z.y = 0.0f;
- z.z = 1.0f;
- u.x = Cos(angle);
- u.y = Sin(angle);
- u.z = 0.0f;
- y.crossProduct( &z, &u, &y );
- x.crossProduct( &y, &z, &x );
- m_transform.Set( x.x, y.x, z.x, pos.x,
- x.y, y.y, z.y, pos.y,
- x.z, y.z, z.z, pos.z );
- }
- //DEBUG_ASSERTCRASH(-PI <= angle && angle <= PI, ("Please pass only normalized (-PI..PI) angles to setOrientation (%f).\n", angle));
- m_cachedAngle = normalizeAngle(angle);
- m_cachedPos = pos;
- m_cacheFlags &= ~VALID_DIRVECTOR; // but don't clear the altitude flags.
- reactToTransformChange(&oldMtx, &oldPos, oldAngle);
- DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
- }
- //=============================================================================
- /** Set the world transformation matrix */
- //=============================================================================
- void Thing::setTransformMatrix( const Matrix3D *mx )
- {
- //USE_PERF_TIMER(ThingMatrixStuff)
- Real oldAngle = m_cachedAngle;
- Coord3D oldPos = m_cachedPos;
- Matrix3D oldMtx = m_transform;
- m_transform = *mx;
- m_cachedPos.x = m_transform.Get_X_Translation();
- m_cachedPos.y = m_transform.Get_Y_Translation();
- m_cachedPos.z = m_transform.Get_Z_Translation();
- m_cachedAngle = m_transform.Get_Z_Rotation();
- m_cacheFlags = 0;
- reactToTransformChange(&oldMtx, &oldPos, oldAngle);
- DEBUG_ASSERTCRASH(!(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)), ("Drawable/Object position NAN! '%s'\n", m_template->getName().str() ));
- }
- //-------------------------------------------------------------------------------------------------
- Bool Thing::isKindOf(KindOfType t) const
- {
- return getTemplate()->isKindOf(t);
- }
- //-------------------------------------------------------------------------------------------------
- Bool Thing::isKindOfMulti(const KindOfMaskType& mustBeSet, const KindOfMaskType& mustBeClear) const
- {
- return getTemplate()->isKindOfMulti(mustBeSet, mustBeClear);
- }
- // ------------------------------------------------------------------------------------------------
- Bool Thing::isAnyKindOf( const KindOfMaskType& anyKindOf ) const
- {
- return getTemplate()->isAnyKindOf( anyKindOf );
- }
- // ------------------------------------------------------------------------------------------------
- Real Thing::calculateHeightAboveTerrain() const
- {
- //USE_PERF_TIMER(ThingMatrixStuff)
- const Coord3D* pos = getPosition();
- Real terrainZ = TheTerrainLogic->getGroundHeight( pos->x, pos->y );
- Real myZ = pos->z;
- return myZ - terrainZ;
- }
- //-------------------------------------------------------------------------------------------------
- Real Thing::getHeightAboveTerrain() const
- {
- if (!(m_cacheFlags & VALID_ALTITUDE_TERRAIN))
- {
- m_cachedAltitudeAboveTerrain = calculateHeightAboveTerrain();
- m_cacheFlags |= VALID_ALTITUDE_TERRAIN;
- }
- return m_cachedAltitudeAboveTerrain;
- }
- //-------------------------------------------------------------------------------------------------
- Real Thing::getHeightAboveTerrainOrWater() const
- {
- //USE_PERF_TIMER(ThingMatrixStuff)
- if (!(m_cacheFlags & VALID_ALTITUDE_SEALEVEL))
- {
- const Coord3D* pos = getPosition();
- Real waterZ;
- if (TheTerrainLogic->isUnderwater(pos->x, pos->y, &waterZ))
- {
- m_cachedAltitudeAboveTerrainOrWater = pos->z - waterZ;
- }
- else
- {
- m_cachedAltitudeAboveTerrainOrWater = getHeightAboveTerrain();
- }
- m_cacheFlags |= VALID_ALTITUDE_SEALEVEL;
- }
- return m_cachedAltitudeAboveTerrainOrWater;
- }
- //=============================================================================
- /** If we treat this as airborne, then they slide down slopes. This checks whether
- they are high enough that we should let them act like they're flying. jba. */
- //=============================================================================
- Bool Thing::isSignificantlyAboveTerrain() const
- {
- // If it's high enough that it will take more than 3 frames to return to the ground,
- // then it's significantly airborne. jba
- return (getHeightAboveTerrain() > -(3*3)*TheGlobalData->m_gravity);
- }
- //-------------------------------------------------------------------------------------------------
- void Thing::convertBonePosToWorldPos(const Coord3D* bonePos, const Matrix3D* boneTransform, Coord3D* worldPos, Matrix3D* worldTransform) const
- {
- if (worldTransform)
- {
- #ifdef ALLOW_TEMPORARIES
- *worldTransform = m_transform * (*boneTransform);
- #else
- worldTransform->mul(m_transform, *boneTransform);
- #endif
- }
- if (worldPos)
- {
- Vector3 vector;
- vector.X = bonePos->x;
- vector.Y = bonePos->y;
- vector.Z = bonePos->z;
- m_transform.Transform_Vector(m_transform, vector, &vector);
- worldPos->x = vector.X;
- worldPos->y = vector.Y;
- worldPos->z = vector.Z;
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Push the 'in' parameter through our transformation matrix and store in 'out' */
- // ------------------------------------------------------------------------------------------------
- void Thing::transformPoint( const Coord3D *in, Coord3D *out )
- {
- // santiy
- if( in == NULL || out == NULL )
- return;
- // for conversion
- Vector3 vectorIn;
- Vector3 vectorOut;
- ///@ todo this is dumb and we should not have to convert types
- // convert to Vector3 datatypes
- vectorIn.X = in->x;
- vectorIn.Y = in->y;
- vectorIn.Z = in->z;
- // do the transform
- m_transform.Transform_Vector( m_transform, vectorIn, &vectorOut );
- // store converted vector in 'out'
- out->x = vectorOut.X;
- out->y = vectorOut.Y;
- out->z = vectorOut.Z;
- } // end transformPoint
|