LCOV - code coverage report
Current view: top level - source/graphics - Model.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 243 0.4 %
Date: 2023-01-19 00:18:29 Functions: 2 29 6.9 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "Model.h"
      21             : 
      22             : #include "graphics/Decal.h"
      23             : #include "graphics/MeshManager.h"
      24             : #include "graphics/ModelDef.h"
      25             : #include "graphics/ObjectEntry.h"
      26             : #include "graphics/SkeletonAnim.h"
      27             : #include "graphics/SkeletonAnimDef.h"
      28             : #include "maths/BoundingBoxAligned.h"
      29             : #include "maths/Quaternion.h"
      30             : #include "lib/sysdep/rtl.h"
      31             : #include "ps/CLogger.h"
      32             : #include "ps/CStrInternStatic.h"
      33             : #include "ps/Profile.h"
      34             : #include "renderer/RenderingOptions.h"
      35             : #include "simulation2/components/ICmpTerrain.h"
      36             : #include "simulation2/components/ICmpWaterManager.h"
      37             : #include "simulation2/Simulation2.h"
      38             : 
      39             : 
      40             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
      41             : // Constructor
      42           0 : CModel::CModel(CSimulation2& simulation)
      43             :     : m_Flags(0), m_Anim(NULL), m_AnimTime(0), m_Simulation(simulation),
      44           0 :     m_BoneMatrices(NULL), m_AmmoPropPoint(NULL), m_AmmoLoadedProp(0)
      45             : {
      46           0 : }
      47             : 
      48             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
      49             : // Destructor
      50           0 : CModel::~CModel()
      51             : {
      52           0 :     ReleaseData();
      53           0 : }
      54             : 
      55             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
      56             : // ReleaseData: delete anything allocated by the model
      57           0 : void CModel::ReleaseData()
      58             : {
      59           0 :     rtl_FreeAligned(m_BoneMatrices);
      60             : 
      61           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
      62           0 :         delete m_Props[i].m_Model;
      63           0 :     m_Props.clear();
      64             : 
      65           0 :     m_pModelDef = CModelDefPtr();
      66           0 : }
      67             : 
      68             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
      69             : // InitModel: setup model from given geometry
      70           0 : bool CModel::InitModel(const CModelDefPtr& modeldef)
      71             : {
      72             :     // clean up any existing data first
      73           0 :     ReleaseData();
      74             : 
      75           0 :     m_pModelDef = modeldef;
      76             : 
      77           0 :     size_t numBones = modeldef->GetNumBones();
      78           0 :     if (numBones != 0)
      79             :     {
      80           0 :         size_t numBlends = modeldef->GetNumBlends();
      81             : 
      82             :         // allocate matrices for bone transformations
      83             :         // (one extra matrix is used for the special case of bind-shape relative weighting)
      84           0 :         m_BoneMatrices = (CMatrix3D*)rtl_AllocateAligned(sizeof(CMatrix3D) * (numBones + 1 + numBlends), 16);
      85           0 :         for (size_t i = 0; i < numBones + 1 + numBlends; ++i)
      86             :         {
      87           0 :             m_BoneMatrices[i].SetIdentity();
      88             :         }
      89             :     }
      90             : 
      91           0 :     m_PositionValid = true;
      92             : 
      93           0 :     return true;
      94             : }
      95             : 
      96             : 
      97             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
      98             : // CalcBound: calculate the world space bounds of this model
      99           0 : void CModel::CalcBounds()
     100             : {
     101             :     // Need to calculate the object bounds first, if that hasn't already been done
     102           0 :     if (! (m_Anim && m_Anim->m_AnimDef))
     103             :     {
     104           0 :         if (m_ObjectBounds.IsEmpty())
     105           0 :             CalcStaticObjectBounds();
     106             :     }
     107             :     else
     108             :     {
     109           0 :         if (m_Anim->m_ObjectBounds.IsEmpty())
     110           0 :             CalcAnimatedObjectBounds(m_Anim->m_AnimDef, m_Anim->m_ObjectBounds);
     111           0 :         ENSURE(! m_Anim->m_ObjectBounds.IsEmpty()); // (if this happens, it'll be recalculating the bounds every time)
     112           0 :         m_ObjectBounds = m_Anim->m_ObjectBounds;
     113             :     }
     114             : 
     115             :     // Ensure the transform is set correctly before we use it
     116           0 :     ValidatePosition();
     117             : 
     118             :     // Now transform the object-space bounds to world-space bounds
     119           0 :     m_ObjectBounds.Transform(GetTransform(), m_WorldBounds);
     120           0 : }
     121             : 
     122             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     123             : // CalcObjectBounds: calculate object space bounds of this model, based solely on vertex positions
     124           0 : void CModel::CalcStaticObjectBounds()
     125             : {
     126           0 :     PROFILE2("CalcStaticObjectBounds");
     127           0 :     m_pModelDef->GetMaxBounds(nullptr, !(m_Flags & MODELFLAG_NOLOOPANIMATION), m_ObjectBounds);
     128           0 : }
     129             : 
     130             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     131             : // CalcAnimatedObjectBound: calculate bounds encompassing all vertex positions for given animation
     132           0 : void CModel::CalcAnimatedObjectBounds(CSkeletonAnimDef* anim, CBoundingBoxAligned& result)
     133             : {
     134           0 :     PROFILE2("CalcAnimatedObjectBounds");
     135           0 :     m_pModelDef->GetMaxBounds(anim, !(m_Flags & MODELFLAG_NOLOOPANIMATION), result);
     136           0 : }
     137             : 
     138             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     139           0 : const CBoundingBoxAligned CModel::GetWorldBoundsRec()
     140             : {
     141           0 :     CBoundingBoxAligned bounds = GetWorldBounds();
     142           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
     143           0 :         bounds += m_Props[i].m_Model->GetWorldBoundsRec();
     144           0 :     return bounds;
     145             : }
     146             : 
     147           0 : const CBoundingBoxAligned CModel::GetObjectSelectionBoundsRec()
     148             : {
     149           0 :     CBoundingBoxAligned objBounds = GetObjectBounds();      // updates the (children-not-included) object-space bounds if necessary
     150             : 
     151             :     // now extend these bounds to include the props' selection bounds (if any)
     152           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
     153             :     {
     154           0 :         const Prop& prop = m_Props[i];
     155           0 :         if (prop.m_Hidden || !prop.m_Selectable)
     156           0 :             continue; // prop is hidden from rendering, so it also shouldn't be used for selection
     157             : 
     158           0 :         CBoundingBoxAligned propSelectionBounds = prop.m_Model->GetObjectSelectionBoundsRec();
     159           0 :         if (propSelectionBounds.IsEmpty())
     160           0 :             continue;   // submodel does not wish to participate in selection box, exclude it
     161             : 
     162             :         // We have the prop's bounds in its own object-space; now we need to transform them so they can be properly added
     163             :         // to the bounds in our object-space. For that, we need the transform of the prop attachment point.
     164             :         //
     165             :         // We have the prop point information; however, it's not trivial to compute its exact location in our object-space
     166             :         // since it may or may not be attached to a bone (see SPropPoint), which in turn may or may not be in the middle of
     167             :         // an animation. The bone matrices might be of interest, but they're really only meant to be used for the animation
     168             :         // system and are quite opaque to use from the outside (see @ref ValidatePosition).
     169             :         //
     170             :         // However, a nice side effect of ValidatePosition is that it also computes the absolute world-space transform of
     171             :         // our props and sets it on their respective models. In particular, @ref ValidatePosition will compute the prop's
     172             :         // world-space transform as either
     173             :         //
     174             :         // T' = T x B x O
     175             :         // or
     176             :         // T' = T x O
     177             :         //
     178             :         // where T' is the prop's world-space transform, T is our world-space transform, O is the prop's local
     179             :         // offset/rotation matrix, and B is an optional transformation matrix of the bone the prop is attached to
     180             :         // (taking into account animation and everything).
     181             :         //
     182             :         // From this, it is clear that either O or B x O is the object-space transformation matrix of the prop. So,
     183             :         // all we need to do is apply our own inverse world-transform T^(-1) to T' to get our desired result. Luckily,
     184             :         // this is precomputed upon setting the transform matrix (see @ref SetTransform), so it is free to fetch.
     185             : 
     186           0 :         CMatrix3D propObjectTransform = prop.m_Model->GetTransform(); // T'
     187           0 :         propObjectTransform.Concatenate(GetInvTransform()); // T^(-1) x T'
     188             : 
     189             :         // Transform the prop's bounds into our object coordinate space
     190           0 :         CBoundingBoxAligned transformedPropSelectionBounds;
     191           0 :         propSelectionBounds.Transform(propObjectTransform, transformedPropSelectionBounds);
     192             : 
     193           0 :         objBounds += transformedPropSelectionBounds;
     194             :     }
     195             : 
     196           0 :     return objBounds;
     197             : }
     198             : 
     199             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     200             : // Update: update this model to the given time, in msec
     201           0 : void CModel::UpdateTo(float time)
     202             : {
     203             :     // update animation time, but don't calculate bone matrices - do that (lazily) when
     204             :     // something requests them; that saves some calculation work for offscreen models,
     205             :     // and also assures the world space, inverted bone matrices (required for normal
     206             :     // skinning) are up to date with respect to m_Transform
     207           0 :     m_AnimTime = time;
     208             : 
     209             :     // mark vertices as dirty
     210           0 :     SetDirty(RENDERDATA_UPDATE_VERTICES);
     211             : 
     212             :     // mark matrices as dirty
     213           0 :     InvalidatePosition();
     214           0 : }
     215             : 
     216             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     217             : // InvalidatePosition
     218           0 : void CModel::InvalidatePosition()
     219             : {
     220           0 :     m_PositionValid = false;
     221             : 
     222           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
     223           0 :         m_Props[i].m_Model->InvalidatePosition();
     224           0 : }
     225             : 
     226             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     227             : // ValidatePosition: ensure that current transform and bone matrices are both uptodate
     228           0 : void CModel::ValidatePosition()
     229             : {
     230           0 :     if (m_PositionValid)
     231             :     {
     232           0 :         ENSURE(!m_Parent || m_Parent->m_PositionValid);
     233           0 :         return;
     234             :     }
     235             : 
     236           0 :     if (m_Parent && !m_Parent->m_PositionValid)
     237             :     {
     238             :         // Make sure we don't base our calculations on
     239             :         // a parent animation state that is out of date.
     240           0 :         m_Parent->ValidatePosition();
     241             : 
     242             :         // Parent will recursively call our validation.
     243           0 :         ENSURE(m_PositionValid);
     244           0 :         return;
     245             :     }
     246             : 
     247           0 :     if (m_Anim && m_BoneMatrices)
     248             :     {
     249             : //      PROFILE( "generating bone matrices" );
     250             : 
     251           0 :         ENSURE(m_pModelDef->GetNumBones() == m_Anim->m_AnimDef->GetNumKeys());
     252             : 
     253           0 :         m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime, m_BoneMatrices, !(m_Flags & MODELFLAG_NOLOOPANIMATION));
     254             :     }
     255           0 :     else if (m_BoneMatrices)
     256             :     {
     257             :         // Bones but no animation - probably a buggy actor forgot to set up the animation,
     258             :         // so just render it in its bind pose
     259             : 
     260           0 :         for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
     261             :         {
     262           0 :             m_BoneMatrices[i].SetIdentity();
     263           0 :             m_BoneMatrices[i].Rotate(m_pModelDef->GetBones()[i].m_Rotation);
     264           0 :             m_BoneMatrices[i].Translate(m_pModelDef->GetBones()[i].m_Translation);
     265             :         }
     266             :     }
     267             : 
     268             :     // For CPU skinning, we precompute as much as possible so that the only
     269             :     // per-vertex work is a single matrix*vec multiplication.
     270             :     // For GPU skinning, we try to minimise CPU work by doing most computation
     271             :     // in the vertex shader instead.
     272             :     // Using g_RenderingOptions to detect CPU vs GPU is a bit hacky,
     273             :     // and this doesn't allow the setting to change at runtime, but there isn't
     274             :     // an obvious cleaner way to determine what data needs to be computed,
     275             :     // and GPU skinning is a rarely-used experimental feature anyway.
     276           0 :     bool worldSpaceBoneMatrices = !g_RenderingOptions.GetGPUSkinning();
     277           0 :     bool computeBlendMatrices = !g_RenderingOptions.GetGPUSkinning();
     278             : 
     279           0 :     if (m_BoneMatrices && worldSpaceBoneMatrices)
     280             :     {
     281             :         // add world-space transformation to m_BoneMatrices
     282           0 :         const CMatrix3D transform = GetTransform();
     283           0 :         for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
     284           0 :             m_BoneMatrices[i].Concatenate(transform);
     285             :     }
     286             : 
     287             :     // our own position is now valid; now we can safely update our props' positions without fearing
     288             :     // that doing so will cause a revalidation of this model (see recursion above).
     289           0 :     m_PositionValid = true;
     290             : 
     291           0 :     CMatrix3D translate;
     292           0 :     CVector3D objTranslation = m_Transform.GetTranslation();
     293           0 :     float objectHeight = 0.0f;
     294             : 
     295           0 :     CmpPtr<ICmpTerrain> cmpTerrain(m_Simulation, SYSTEM_ENTITY);
     296           0 :     if (cmpTerrain)
     297           0 :         objectHeight = cmpTerrain->GetExactGroundLevel(objTranslation.X, objTranslation.Z);
     298             : 
     299             :     // Object height is incorrect for floating objects. We use water height instead.
     300           0 :     CmpPtr<ICmpWaterManager> cmpWaterManager(m_Simulation, SYSTEM_ENTITY);
     301           0 :     if (cmpWaterManager)
     302             :     {
     303           0 :         float waterHeight = cmpWaterManager->GetExactWaterLevel(objTranslation.X, objTranslation.Z);
     304           0 :         if (waterHeight >= objectHeight && m_Flags & MODELFLAG_FLOATONWATER)
     305           0 :             objectHeight = waterHeight;
     306             :     }
     307             : 
     308             :     // re-position and validate all props
     309           0 :     for (const Prop& prop : m_Props)
     310             :     {
     311           0 :         CMatrix3D proptransform = prop.m_Point->m_Transform;
     312           0 :         if (prop.m_Point->m_BoneIndex != 0xff)
     313             :         {
     314           0 :             CMatrix3D boneMatrix = m_BoneMatrices[prop.m_Point->m_BoneIndex];
     315           0 :             if (!worldSpaceBoneMatrices)
     316           0 :                 boneMatrix.Concatenate(GetTransform());
     317           0 :             proptransform.Concatenate(boneMatrix);
     318             :         }
     319             :         else
     320             :         {
     321             :             // not relative to any bone; just apply world-space transformation (i.e. relative to object-space origin)
     322           0 :             proptransform.Concatenate(m_Transform);
     323             :         }
     324             : 
     325             :         // Adjust prop height to terrain level when needed
     326           0 :         if (cmpTerrain && (prop.m_MaxHeight != 0.f || prop.m_MinHeight != 0.f))
     327             :         {
     328           0 :             const CVector3D& propTranslation = proptransform.GetTranslation();
     329           0 :             const float propTerrain = cmpTerrain->GetExactGroundLevel(propTranslation.X, propTranslation.Z);
     330           0 :             const float translateHeight = std::min(prop.m_MaxHeight, std::max(prop.m_MinHeight, propTerrain - objectHeight));
     331           0 :             translate.SetTranslation(0.f, translateHeight, 0.f);
     332           0 :             proptransform.Concatenate(translate);
     333             :         }
     334             : 
     335           0 :         prop.m_Model->SetTransform(proptransform);
     336           0 :         prop.m_Model->ValidatePosition();
     337             :     }
     338             : 
     339           0 :     if (m_BoneMatrices)
     340             :     {
     341           0 :         for (size_t i = 0; i < m_pModelDef->GetNumBones(); i++)
     342             :         {
     343           0 :             m_BoneMatrices[i] = m_BoneMatrices[i] * m_pModelDef->GetInverseBindBoneMatrices()[i];
     344             :         }
     345             : 
     346             :         // Note: there is a special case of joint influence, in which the vertex
     347             :         //  is influenced by the bind-shape transform instead of a particular bone,
     348             :         //  which we indicate with the blending bone ID set to the total number
     349             :         //  of bones. But since we're skinning in world space, we use the model's
     350             :         //  world space transform and store that matrix in this special index.
     351             :         //  (see http://trac.wildfiregames.com/ticket/1012)
     352           0 :         m_BoneMatrices[m_pModelDef->GetNumBones()] = m_Transform;
     353             : 
     354           0 :         if (computeBlendMatrices)
     355           0 :             m_pModelDef->BlendBoneMatrices(m_BoneMatrices);
     356             :     }
     357             : }
     358             : 
     359             : 
     360             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     361             : // SetAnimation: set the given animation as the current animation on this model;
     362             : // return false on error, else true
     363           0 : bool CModel::SetAnimation(CSkeletonAnim* anim, bool once)
     364             : {
     365           0 :     m_Anim = nullptr; // in case something fails
     366             : 
     367           0 :     if (anim)
     368             :     {
     369           0 :         m_Flags &= ~MODELFLAG_NOLOOPANIMATION;
     370             : 
     371           0 :         if (once)
     372           0 :             m_Flags |= MODELFLAG_NOLOOPANIMATION;
     373             : 
     374             :         // Not rigged or animation is not valid.
     375           0 :         if (!m_BoneMatrices || !anim->m_AnimDef)
     376           0 :             return false;
     377             : 
     378           0 :         if (anim->m_AnimDef->GetNumKeys() != m_pModelDef->GetNumBones())
     379             :         {
     380           0 :             LOGERROR("Mismatch between model's skeleton and animation's skeleton (%s.dae has %lu model bones while the animation %s has %lu animation keys.)",
     381             :                 m_pModelDef->GetName().string8().c_str() ,
     382             :                 static_cast<unsigned long>(m_pModelDef->GetNumBones()), 
     383             :                 anim->m_Name.c_str(),
     384             :                 static_cast<unsigned long>(anim->m_AnimDef->GetNumKeys()));
     385           0 :             return false;
     386             :         }
     387             : 
     388             :         // Reset the cached bounds when the animation is changed.
     389           0 :         m_ObjectBounds.SetEmpty();
     390           0 :         InvalidateBounds();
     391             : 
     392             :         // Start anim from beginning.
     393           0 :         m_AnimTime = 0;
     394             :     }
     395             : 
     396           0 :     m_Anim = anim;
     397             : 
     398           0 :     return true;
     399             : }
     400             : 
     401             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     402             : // CopyAnimation
     403           0 : void CModel::CopyAnimationFrom(CModel* source)
     404             : {
     405           0 :     m_Anim = source->m_Anim;
     406           0 :     m_AnimTime = source->m_AnimTime;
     407             : 
     408           0 :     m_ObjectBounds.SetEmpty();
     409           0 :     InvalidateBounds();
     410           0 : }
     411             : 
     412             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     413             : // AddProp: add a prop to the model on the given point
     414           0 : void CModel::AddProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry, float minHeight, float maxHeight, bool selectable)
     415             : {
     416             :     // position model according to prop point position
     417             : 
     418             :     // this next call will invalidate the bounds of "model", which will in turn also invalidate the selection box
     419           0 :     model->SetTransform(point->m_Transform);
     420           0 :     model->m_Parent = this;
     421             : 
     422           0 :     Prop prop;
     423           0 :     prop.m_Point = point;
     424           0 :     prop.m_Model = model;
     425           0 :     prop.m_ObjectEntry = objectentry;
     426           0 :     prop.m_MinHeight = minHeight;
     427           0 :     prop.m_MaxHeight = maxHeight;
     428           0 :     prop.m_Selectable = selectable;
     429           0 :     m_Props.push_back(prop);
     430           0 : }
     431             : 
     432           0 : void CModel::AddAmmoProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry)
     433             : {
     434           0 :     AddProp(point, model, objectentry);
     435           0 :     m_AmmoPropPoint = point;
     436           0 :     m_AmmoLoadedProp = m_Props.size() - 1;
     437           0 :     m_Props[m_AmmoLoadedProp].m_Hidden = true;
     438             : 
     439             :     // we only need to invalidate the selection box here if it is based on props and their visibilities
     440           0 :     if (!m_CustomSelectionShape)
     441           0 :         m_SelectionBoxValid = false;
     442           0 : }
     443             : 
     444           0 : void CModel::ShowAmmoProp()
     445             : {
     446           0 :     if (m_AmmoPropPoint == NULL)
     447           0 :         return;
     448             : 
     449             :     // Show the ammo prop, hide all others on the same prop point
     450           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
     451           0 :         if (m_Props[i].m_Point == m_AmmoPropPoint)
     452           0 :             m_Props[i].m_Hidden = (i != m_AmmoLoadedProp);
     453             : 
     454             :     //  we only need to invalidate the selection box here if it is based on props and their visibilities
     455           0 :     if (!m_CustomSelectionShape)
     456           0 :         m_SelectionBoxValid = false;
     457             : }
     458             : 
     459           0 : void CModel::HideAmmoProp()
     460             : {
     461           0 :     if (m_AmmoPropPoint == NULL)
     462           0 :         return;
     463             : 
     464             :     // Hide the ammo prop, show all others on the same prop point
     465           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
     466           0 :         if (m_Props[i].m_Point == m_AmmoPropPoint)
     467           0 :             m_Props[i].m_Hidden = (i == m_AmmoLoadedProp);
     468             : 
     469             :     //  we only need to invalidate here if the selection box is based on props and their visibilities
     470           0 :     if (!m_CustomSelectionShape)
     471           0 :         m_SelectionBoxValid = false;
     472             : }
     473             : 
     474           0 : CModelAbstract* CModel::FindFirstAmmoProp()
     475             : {
     476           0 :     if (m_AmmoPropPoint)
     477           0 :         return m_Props[m_AmmoLoadedProp].m_Model;
     478             : 
     479           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
     480             :     {
     481           0 :         CModel* propModel = m_Props[i].m_Model->ToCModel();
     482           0 :         if (propModel)
     483             :         {
     484           0 :             CModelAbstract* model = propModel->FindFirstAmmoProp();
     485           0 :             if (model)
     486           0 :                 return model;
     487             :         }
     488             :     }
     489             : 
     490           0 :     return NULL;
     491             : }
     492             : 
     493             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     494             : // Clone: return a clone of this model
     495           0 : CModelAbstract* CModel::Clone() const
     496             : {
     497           0 :     CModel* clone = new CModel(m_Simulation);
     498           0 :     clone->m_ObjectBounds = m_ObjectBounds;
     499           0 :     clone->InitModel(m_pModelDef);
     500           0 :     clone->SetMaterial(m_Material);
     501           0 :     clone->SetAnimation(m_Anim);
     502           0 :     clone->SetFlags(m_Flags);
     503             : 
     504           0 :     for (size_t i = 0; i < m_Props.size(); i++)
     505             :     {
     506             :         // eek!  TODO, RC - need to investigate shallow clone here
     507           0 :         if (m_AmmoPropPoint && i == m_AmmoLoadedProp)
     508           0 :             clone->AddAmmoProp(m_Props[i].m_Point, m_Props[i].m_Model->Clone(), m_Props[i].m_ObjectEntry);
     509             :         else
     510           0 :             clone->AddProp(m_Props[i].m_Point, m_Props[i].m_Model->Clone(), m_Props[i].m_ObjectEntry, m_Props[i].m_MinHeight, m_Props[i].m_MaxHeight, m_Props[i].m_Selectable);
     511             :     }
     512             : 
     513           0 :     return clone;
     514             : }
     515             : 
     516             : 
     517             : /////////////////////////////////////////////////////////////////////////////////////////////////////////////
     518             : // SetTransform: set the transform on this object, and reorientate props accordingly
     519           0 : void CModel::SetTransform(const CMatrix3D& transform)
     520             : {
     521             :     // call base class to set transform on this object
     522           0 :     CRenderableObject::SetTransform(transform);
     523           0 :     InvalidatePosition();
     524           0 : }
     525             : 
     526             : //////////////////////////////////////////////////////////////////////////
     527             : 
     528           0 : void CModel::AddFlagsRec(int flags)
     529             : {
     530           0 :     m_Flags |= flags;
     531             : 
     532           0 :     if (flags & MODELFLAG_IGNORE_LOS)
     533           0 :         m_Material.AddShaderDefine(str_IGNORE_LOS, str_1);
     534             : 
     535           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
     536           0 :         if (m_Props[i].m_Model->ToCModel())
     537           0 :             m_Props[i].m_Model->ToCModel()->AddFlagsRec(flags);
     538           0 : }
     539             : 
     540           0 : void CModel::RemoveShadowsRec()
     541             : {
     542           0 :     m_Flags &= ~MODELFLAG_CASTSHADOWS;
     543             : 
     544           0 :     m_Material.AddShaderDefine(str_DISABLE_RECEIVE_SHADOWS, str_1);
     545             : 
     546           0 :     for (size_t i = 0; i < m_Props.size(); ++i)
     547             :     {
     548           0 :         if (m_Props[i].m_Model->ToCModel())
     549           0 :             m_Props[i].m_Model->ToCModel()->RemoveShadowsRec();
     550           0 :         else if (m_Props[i].m_Model->ToCModelDecal())
     551           0 :             m_Props[i].m_Model->ToCModelDecal()->RemoveShadows();
     552             :     }
     553           0 : }
     554             : 
     555           0 : void CModel::SetMaterial(const CMaterial &material)
     556             : {
     557           0 :     m_Material = material;
     558           0 : }
     559             : 
     560           0 : void CModel::SetPlayerID(player_id_t id)
     561             : {
     562           0 :     CModelAbstract::SetPlayerID(id);
     563             : 
     564           0 :     for (std::vector<Prop>::iterator it = m_Props.begin(); it != m_Props.end(); ++it)
     565           0 :         it->m_Model->SetPlayerID(id);
     566           0 : }
     567             : 
     568           0 : void CModel::SetShadingColor(const CColor& color)
     569             : {
     570           0 :     CModelAbstract::SetShadingColor(color);
     571             : 
     572           0 :     for (std::vector<Prop>::iterator it = m_Props.begin(); it != m_Props.end(); ++it)
     573           0 :         it->m_Model->SetShadingColor(color);
     574           3 : }

Generated by: LCOV version 1.13