2013-01-06 08:37:22 +01:00
|
|
|
/* Copyright (C) 2013 Wildfire Games.
|
2010-01-09 20:20:14 +01:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. 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 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include "simulation2/system/Component.h"
|
|
|
|
#include "ICmpVisual.h"
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
#include "simulation2/MessageTypes.h"
|
|
|
|
|
|
|
|
#include "ICmpFootprint.h"
|
2011-10-29 17:30:46 +02:00
|
|
|
#include "ICmpOwnership.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "ICmpPosition.h"
|
2010-09-23 14:13:13 +02:00
|
|
|
#include "ICmpRangeManager.h"
|
2013-02-03 03:08:20 +01:00
|
|
|
#include "ICmpSelectable.h"
|
|
|
|
#include "ICmpTemplateManager.h"
|
|
|
|
#include "ICmpTerrain.h"
|
2012-12-06 20:46:13 +01:00
|
|
|
#include "ICmpUnitMotion.h"
|
2010-09-23 14:13:13 +02:00
|
|
|
#include "ICmpVision.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
|
2013-08-19 00:17:57 +02:00
|
|
|
#include "graphics/Decal.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "graphics/Frustum.h"
|
|
|
|
#include "graphics/Model.h"
|
|
|
|
#include "graphics/ObjectBase.h"
|
|
|
|
#include "graphics/ObjectEntry.h"
|
|
|
|
#include "graphics/Unit.h"
|
2010-06-06 00:23:28 +02:00
|
|
|
#include "graphics/UnitAnimation.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "graphics/UnitManager.h"
|
|
|
|
#include "maths/Matrix3D.h"
|
|
|
|
#include "maths/Vector3D.h"
|
|
|
|
#include "ps/CLogger.h"
|
2013-12-30 16:52:42 +01:00
|
|
|
#include "ps/GameSetup/Config.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "renderer/Scene.h"
|
|
|
|
|
2013-03-05 21:02:16 +01:00
|
|
|
#include "tools/atlas/GameInterface/GameLoop.h"
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
class CCmpVisualActor : public ICmpVisual
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static void ClassInit(CComponentManager& componentManager)
|
|
|
|
{
|
2010-09-03 11:55:14 +02:00
|
|
|
componentManager.SubscribeToMessageType(MT_Update_Final);
|
2010-01-22 21:03:14 +01:00
|
|
|
componentManager.SubscribeToMessageType(MT_Interpolate);
|
|
|
|
componentManager.SubscribeToMessageType(MT_RenderSubmit);
|
|
|
|
componentManager.SubscribeToMessageType(MT_OwnershipChanged);
|
2011-03-13 20:22:05 +01:00
|
|
|
componentManager.SubscribeGloballyToMessageType(MT_TerrainChanged);
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DEFAULT_COMPONENT_ALLOCATOR(VisualActor)
|
|
|
|
|
2013-06-10 01:18:21 +02:00
|
|
|
private:
|
2010-06-03 03:29:43 +02:00
|
|
|
std::wstring m_ActorName;
|
2010-02-07 21:06:16 +01:00
|
|
|
CUnit* m_Unit;
|
|
|
|
|
2010-10-04 19:34:33 +02:00
|
|
|
fixed m_R, m_G, m_B; // shading colour
|
|
|
|
|
2012-12-06 20:46:13 +01:00
|
|
|
std::map<std::string, std::string> m_AnimOverride;
|
|
|
|
|
2010-09-23 14:13:13 +02:00
|
|
|
ICmpRangeManager::ELosVisibility m_Visibility; // only valid between Interpolate and RenderSubmit
|
2010-08-05 12:20:47 +02:00
|
|
|
|
2010-02-07 21:06:16 +01:00
|
|
|
// Current animation state
|
2011-10-29 17:30:46 +02:00
|
|
|
fixed m_AnimRunThreshold; // if non-zero this is the special walk/run mode
|
2010-02-07 21:06:16 +01:00
|
|
|
std::string m_AnimName;
|
|
|
|
bool m_AnimOnce;
|
2011-10-29 17:30:46 +02:00
|
|
|
fixed m_AnimSpeed;
|
2010-04-06 01:09:34 +02:00
|
|
|
std::wstring m_SoundGroup;
|
2011-10-29 17:30:46 +02:00
|
|
|
fixed m_AnimDesync;
|
|
|
|
fixed m_AnimSyncRepeatTime; // 0.0 if not synced
|
2010-02-07 21:06:16 +01:00
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
u32 m_Seed; // seed used for random variations
|
|
|
|
|
|
|
|
bool m_ConstructionPreview;
|
|
|
|
fixed m_ConstructionProgress;
|
|
|
|
|
2013-06-10 01:18:21 +02:00
|
|
|
bool m_VisibleInAtlasOnly;
|
2013-12-30 16:52:42 +01:00
|
|
|
bool m_IsActorOnly; // an in-world entity should not have this or it might not be rendered.
|
2013-06-10 01:18:21 +02:00
|
|
|
|
|
|
|
/// Whether the visual actor has been rendered at least once.
|
|
|
|
/// Necessary because the visibility update runs on simulation update,
|
|
|
|
/// which may not occur immediately if the game starts paused.
|
|
|
|
bool m_PreviouslyRendered;
|
|
|
|
|
|
|
|
public:
|
2010-04-09 21:02:39 +02:00
|
|
|
static std::string GetSchema()
|
|
|
|
{
|
|
|
|
return
|
2010-04-23 18:09:03 +02:00
|
|
|
"<a:help>Display the unit using the engine's actor system.</a:help>"
|
|
|
|
"<a:example>"
|
|
|
|
"<Actor>units/hellenes/infantry_spearman_b.xml</Actor>"
|
|
|
|
"</a:example>"
|
|
|
|
"<a:example>"
|
|
|
|
"<Actor>structures/hellenes/barracks.xml</Actor>"
|
|
|
|
"<FoundationActor>structures/fndn_4x4.xml</FoundationActor>"
|
|
|
|
"</a:example>"
|
|
|
|
"<element name='Actor' a:help='Filename of the actor to be used for this unit'>"
|
|
|
|
"<text/>"
|
|
|
|
"</element>"
|
2010-04-09 21:02:39 +02:00
|
|
|
"<optional>"
|
2010-04-23 18:09:03 +02:00
|
|
|
"<element name='FoundationActor' a:help='Filename of the actor to be used the foundation while this unit is being constructed'>"
|
|
|
|
"<text/>"
|
|
|
|
"</element>"
|
2010-04-09 21:02:39 +02:00
|
|
|
"</optional>"
|
|
|
|
"<optional>"
|
2013-02-03 03:08:20 +01:00
|
|
|
"<element name='Foundation' a:help='Used internally; if present, the unit will be rendered as a foundation'>"
|
|
|
|
"<empty/>"
|
|
|
|
"</element>"
|
|
|
|
"</optional>"
|
|
|
|
"<optional>"
|
|
|
|
"<element name='ConstructionPreview' a:help='If present, the unit should have a construction preview'>"
|
2010-04-23 18:09:03 +02:00
|
|
|
"<empty/>"
|
|
|
|
"</element>"
|
2011-03-18 17:57:54 +01:00
|
|
|
"</optional>"
|
2013-08-19 00:17:57 +02:00
|
|
|
"<optional>"
|
|
|
|
"<element name='DisableShadows' a:help='Used internally; if present, shadows will be disabled'>"
|
|
|
|
"<empty/>"
|
|
|
|
"</element>"
|
|
|
|
"</optional>"
|
2013-12-30 16:52:42 +01:00
|
|
|
"<optional>"
|
|
|
|
"<element name='ActorOnly' a:help='Used internally; if present, the unit will only be rendered if the user has high enough graphical settings.'>"
|
|
|
|
"<empty/>"
|
|
|
|
"</element>"
|
|
|
|
"</optional>"
|
2011-03-18 17:57:54 +01:00
|
|
|
"<element name='SilhouetteDisplay'>"
|
|
|
|
"<data type='boolean'/>"
|
|
|
|
"</element>"
|
|
|
|
"<element name='SilhouetteOccluder'>"
|
|
|
|
"<data type='boolean'/>"
|
2011-11-25 07:36:13 +01:00
|
|
|
"</element>"
|
|
|
|
"<optional>"
|
|
|
|
"<element name='SelectionShape'>"
|
|
|
|
"<choice>"
|
|
|
|
"<element name='Bounds' a:help='Determines the selection box based on the model bounds'>"
|
|
|
|
"<empty/>"
|
|
|
|
"</element>"
|
|
|
|
"<element name='Footprint' a:help='Determines the selection box based on the entity Footprint component'>"
|
|
|
|
"<empty/>"
|
|
|
|
"</element>"
|
|
|
|
"<element name='Box' a:help='Sets the selection shape to a box of specified dimensions'>"
|
|
|
|
"<attribute name='width'>"
|
|
|
|
"<ref name='positiveDecimal' />"
|
|
|
|
"</attribute>"
|
|
|
|
"<attribute name='height'>"
|
|
|
|
"<ref name='positiveDecimal' />"
|
|
|
|
"</attribute>"
|
|
|
|
"<attribute name='depth'>"
|
|
|
|
"<ref name='positiveDecimal' />"
|
|
|
|
"</attribute>"
|
|
|
|
"</element>"
|
|
|
|
"<element name='Cylinder' a:help='Sets the selection shape to a cylinder of specified dimensions'>"
|
|
|
|
"<attribute name='radius'>"
|
|
|
|
"<ref name='positiveDecimal' />"
|
|
|
|
"</attribute>"
|
|
|
|
"<attribute name='height'>"
|
|
|
|
"<ref name='positiveDecimal' />"
|
|
|
|
"</attribute>"
|
|
|
|
"</element>"
|
|
|
|
"</choice>"
|
|
|
|
"</element>"
|
2013-06-10 01:18:21 +02:00
|
|
|
"</optional>"
|
|
|
|
"<element name='VisibleInAtlasOnly'>"
|
|
|
|
"<data type='boolean'/>"
|
|
|
|
"</element>";
|
2010-04-09 21:02:39 +02:00
|
|
|
}
|
2010-04-15 21:59:07 +02:00
|
|
|
|
2011-01-16 15:08:38 +01:00
|
|
|
virtual void Init(const CParamNode& paramNode)
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
2012-08-06 18:46:54 +02:00
|
|
|
m_PreviouslyRendered = false;
|
2010-08-05 12:20:47 +02:00
|
|
|
m_Unit = NULL;
|
2012-07-30 23:06:54 +02:00
|
|
|
m_Visibility = ICmpRangeManager::VIS_HIDDEN;
|
2011-10-24 14:24:04 +02:00
|
|
|
m_R = m_G = m_B = fixed::FromInt(1);
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
m_ConstructionPreview = paramNode.GetChild("ConstructionPreview").IsOk();
|
|
|
|
m_ConstructionProgress = fixed::Zero();
|
|
|
|
|
|
|
|
m_Seed = GetEntityId();
|
|
|
|
|
2010-04-09 21:02:39 +02:00
|
|
|
if (paramNode.GetChild("Foundation").IsOk() && paramNode.GetChild("FoundationActor").IsOk())
|
2010-06-03 03:29:43 +02:00
|
|
|
m_ActorName = paramNode.GetChild("FoundationActor").ToString();
|
2010-03-12 23:28:51 +01:00
|
|
|
else
|
2010-06-03 03:29:43 +02:00
|
|
|
m_ActorName = paramNode.GetChild("Actor").ToString();
|
2010-03-12 23:28:51 +01:00
|
|
|
|
2013-06-10 01:18:21 +02:00
|
|
|
m_VisibleInAtlasOnly = paramNode.GetChild("VisibleInAtlasOnly").ToBool();
|
2013-12-30 16:52:42 +01:00
|
|
|
m_IsActorOnly = paramNode.GetChild("ActorOnly").IsOk();
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
InitModel(paramNode);
|
2010-02-07 21:06:16 +01:00
|
|
|
|
2013-01-06 08:37:22 +01:00
|
|
|
// We need to select animation even if graphics are disabled, as this modifies serialized state
|
2011-12-11 14:35:51 +01:00
|
|
|
SelectAnimation("idle", false, fixed::FromInt(1), L"");
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
|
2011-01-16 15:08:38 +01:00
|
|
|
virtual void Deinit()
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
|
|
|
if (m_Unit)
|
|
|
|
{
|
2011-01-16 15:08:38 +01:00
|
|
|
GetSimContext().GetUnitManager().DeleteUnit(m_Unit);
|
2010-01-09 20:20:14 +01:00
|
|
|
m_Unit = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
template<typename S>
|
|
|
|
void SerializeCommon(S& serialize)
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
2010-10-23 21:59:40 +02:00
|
|
|
serialize.NumberFixed_Unbounded("r", m_R);
|
|
|
|
serialize.NumberFixed_Unbounded("g", m_G);
|
|
|
|
serialize.NumberFixed_Unbounded("b", m_B);
|
2011-10-29 17:30:46 +02:00
|
|
|
|
|
|
|
serialize.NumberFixed_Unbounded("anim run threshold", m_AnimRunThreshold);
|
|
|
|
serialize.StringASCII("anim name", m_AnimName, 0, 256);
|
|
|
|
serialize.Bool("anim once", m_AnimOnce);
|
|
|
|
serialize.NumberFixed_Unbounded("anim speed", m_AnimSpeed);
|
|
|
|
serialize.String("sound group", m_SoundGroup, 0, 256);
|
|
|
|
serialize.NumberFixed_Unbounded("anim desync", m_AnimDesync);
|
|
|
|
serialize.NumberFixed_Unbounded("anim sync repeat time", m_AnimSyncRepeatTime);
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
serialize.NumberU32_Unbounded("seed", m_Seed);
|
|
|
|
// TODO: variation/selection strings
|
|
|
|
|
|
|
|
serialize.NumberFixed_Unbounded("constructionprogress", m_ConstructionProgress);
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
// TODO: store actor variables?
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Serialize(ISerializer& serialize)
|
|
|
|
{
|
|
|
|
// TODO: store the actor name, if !debug and it differs from the template
|
|
|
|
|
|
|
|
if (serialize.IsDebug())
|
|
|
|
{
|
|
|
|
serialize.String("actor", m_ActorName, 0, 256);
|
|
|
|
}
|
|
|
|
|
|
|
|
SerializeCommon(serialize);
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
|
2011-01-16 15:08:38 +01:00
|
|
|
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
2011-01-16 15:08:38 +01:00
|
|
|
Init(paramNode);
|
2010-10-23 21:59:40 +02:00
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
u32 oldSeed = GetActorSeed();
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
SerializeCommon(deserialize);
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
// If we serialized a different seed, reload actor
|
|
|
|
if (oldSeed != GetActorSeed())
|
|
|
|
ReloadActor();
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
fixed repeattime = m_AnimSyncRepeatTime; // save because SelectAnimation overwrites it
|
|
|
|
|
|
|
|
if (m_AnimRunThreshold.IsZero())
|
|
|
|
SelectAnimation(m_AnimName, m_AnimOnce, m_AnimSpeed, m_SoundGroup);
|
|
|
|
else
|
|
|
|
SelectMovementAnimation(m_AnimRunThreshold);
|
|
|
|
|
|
|
|
SetAnimationSyncRepeat(repeattime);
|
|
|
|
|
|
|
|
if (m_Unit)
|
|
|
|
{
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpOwnership> cmpOwnership(GetEntityHandle());
|
2012-02-08 03:46:15 +01:00
|
|
|
if (cmpOwnership)
|
2011-10-29 17:30:46 +02:00
|
|
|
m_Unit->GetModel().SetPlayerID(cmpOwnership->GetOwner());
|
|
|
|
}
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
|
2011-01-16 15:08:38 +01:00
|
|
|
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
2010-09-19 20:08:56 +02:00
|
|
|
// Quick exit for running in non-graphical mode
|
|
|
|
if (m_Unit == NULL)
|
|
|
|
return;
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
switch (msg.GetType())
|
|
|
|
{
|
2010-09-03 11:55:14 +02:00
|
|
|
case MT_Update_Final:
|
|
|
|
{
|
|
|
|
const CMessageUpdate_Final& msgData = static_cast<const CMessageUpdate_Final&> (msg);
|
|
|
|
Update(msgData.turnLength);
|
|
|
|
break;
|
|
|
|
}
|
2010-01-09 20:20:14 +01:00
|
|
|
case MT_Interpolate:
|
|
|
|
{
|
2010-02-07 21:06:16 +01:00
|
|
|
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
|
2012-06-06 21:37:03 +02:00
|
|
|
Interpolate(msgData.deltaSimTime, msgData.offset);
|
2010-01-09 20:20:14 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MT_RenderSubmit:
|
|
|
|
{
|
|
|
|
const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
|
2010-09-03 11:55:14 +02:00
|
|
|
RenderSubmit(msgData.collector, msgData.frustum, msgData.culling);
|
2010-01-09 20:20:14 +01:00
|
|
|
break;
|
|
|
|
}
|
2010-01-22 21:03:14 +01:00
|
|
|
case MT_OwnershipChanged:
|
|
|
|
{
|
|
|
|
const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg);
|
2010-09-19 20:08:56 +02:00
|
|
|
m_Unit->GetModel().SetPlayerID(msgData.to);
|
2010-01-22 21:03:14 +01:00
|
|
|
break;
|
|
|
|
}
|
2011-03-13 20:22:05 +01:00
|
|
|
case MT_TerrainChanged:
|
|
|
|
{
|
|
|
|
const CMessageTerrainChanged& msgData = static_cast<const CMessageTerrainChanged&> (msg);
|
|
|
|
m_Unit->GetModel().SetTerrainDirty(msgData.i0, msgData.j0, msgData.i1, msgData.j1);
|
|
|
|
break;
|
|
|
|
}
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
virtual CBoundingBoxAligned GetBounds()
|
|
|
|
{
|
|
|
|
if (!m_Unit)
|
|
|
|
return CBoundingBoxAligned::EMPTY;
|
|
|
|
return m_Unit->GetModel().GetWorldBounds();
|
|
|
|
}
|
|
|
|
|
2011-12-09 11:49:08 +01:00
|
|
|
virtual CUnit* GetUnit()
|
|
|
|
{
|
|
|
|
return m_Unit;
|
|
|
|
}
|
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
virtual CBoundingBoxOriented GetSelectionBox()
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
|
|
|
if (!m_Unit)
|
2011-11-25 07:36:13 +01:00
|
|
|
return CBoundingBoxOriented::EMPTY;
|
|
|
|
return m_Unit->GetModel().GetSelectionBox();
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
|
2010-03-11 21:01:16 +01:00
|
|
|
virtual CVector3D GetPosition()
|
|
|
|
{
|
|
|
|
if (!m_Unit)
|
|
|
|
return CVector3D(0, 0, 0);
|
2010-04-17 13:44:08 +02:00
|
|
|
return m_Unit->GetModel().GetTransform().GetTranslation();
|
2010-03-11 21:01:16 +01:00
|
|
|
}
|
|
|
|
|
2010-06-03 03:29:43 +02:00
|
|
|
virtual std::wstring GetActorShortName()
|
2010-04-17 13:34:40 +02:00
|
|
|
{
|
|
|
|
if (!m_Unit)
|
|
|
|
return L"";
|
2010-06-03 03:29:43 +02:00
|
|
|
return m_Unit->GetObject().m_Base->m_ShortName;
|
2010-04-17 13:34:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual std::wstring GetProjectileActor()
|
|
|
|
{
|
|
|
|
if (!m_Unit)
|
|
|
|
return L"";
|
|
|
|
return m_Unit->GetObject().m_ProjectileModelName;
|
|
|
|
}
|
|
|
|
|
2010-06-05 02:49:14 +02:00
|
|
|
virtual CVector3D GetProjectileLaunchPoint()
|
|
|
|
{
|
|
|
|
if (!m_Unit)
|
|
|
|
return CVector3D();
|
2010-09-23 15:09:35 +02:00
|
|
|
|
2011-03-13 20:22:05 +01:00
|
|
|
if (m_Unit->GetModel().ToCModel())
|
|
|
|
{
|
|
|
|
// Ensure the prop transforms are correct
|
|
|
|
m_Unit->GetModel().ValidatePosition();
|
2010-09-23 15:09:35 +02:00
|
|
|
|
2011-03-13 20:22:05 +01:00
|
|
|
CModelAbstract* ammo = m_Unit->GetModel().ToCModel()->FindFirstAmmoProp();
|
|
|
|
if (ammo)
|
|
|
|
return ammo->GetTransform().GetTranslation();
|
|
|
|
}
|
|
|
|
|
|
|
|
return CVector3D();
|
2010-06-05 02:49:14 +02:00
|
|
|
}
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
virtual void SelectAnimation(std::string name, bool once, fixed speed, std::wstring soundgroup)
|
2010-02-07 21:06:16 +01:00
|
|
|
{
|
2011-10-29 17:30:46 +02:00
|
|
|
m_AnimRunThreshold = fixed::Zero();
|
2010-02-07 21:06:16 +01:00
|
|
|
m_AnimName = name;
|
|
|
|
m_AnimOnce = once;
|
|
|
|
m_AnimSpeed = speed;
|
2010-04-06 01:09:34 +02:00
|
|
|
m_SoundGroup = soundgroup;
|
2011-10-29 17:30:46 +02:00
|
|
|
m_AnimDesync = fixed::FromInt(1)/20; // TODO: make this an argument
|
|
|
|
m_AnimSyncRepeatTime = fixed::Zero();
|
2010-04-06 01:09:34 +02:00
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
if (m_Unit)
|
|
|
|
{
|
|
|
|
m_Unit->SetEntitySelection(m_AnimName);
|
|
|
|
if (m_Unit->GetAnimation())
|
|
|
|
m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str());
|
|
|
|
}
|
2010-04-06 01:09:34 +02:00
|
|
|
}
|
|
|
|
|
2012-12-06 20:46:13 +01:00
|
|
|
virtual void ReplaceMoveAnimation(std::string name, std::string replace)
|
|
|
|
{
|
|
|
|
m_AnimOverride[name] = replace;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void ResetMoveAnimation(std::string name)
|
|
|
|
{
|
|
|
|
std::map<std::string, std::string>::const_iterator it = m_AnimOverride.find(name);
|
|
|
|
if (it != m_AnimOverride.end())
|
|
|
|
m_AnimOverride.erase(name);
|
|
|
|
}
|
|
|
|
|
2011-12-10 08:07:04 +01:00
|
|
|
virtual void SetUnitEntitySelection(const CStr& selection)
|
|
|
|
{
|
|
|
|
if (m_Unit)
|
|
|
|
{
|
|
|
|
m_Unit->SetEntitySelection(selection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
virtual void SelectMovementAnimation(fixed runThreshold)
|
2010-09-03 11:55:14 +02:00
|
|
|
{
|
|
|
|
m_AnimRunThreshold = runThreshold;
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
if (m_Unit)
|
|
|
|
{
|
|
|
|
m_Unit->SetEntitySelection("walk");
|
|
|
|
if (m_Unit->GetAnimation())
|
|
|
|
m_Unit->GetAnimation()->SetAnimationState("walk", false, 1.f, 0.f, L"");
|
|
|
|
}
|
2010-09-03 11:55:14 +02:00
|
|
|
}
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
virtual void SetAnimationSyncRepeat(fixed repeattime)
|
2010-04-06 01:09:34 +02:00
|
|
|
{
|
2010-06-03 03:29:43 +02:00
|
|
|
m_AnimSyncRepeatTime = repeattime;
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
if (m_Unit)
|
|
|
|
{
|
|
|
|
if (m_Unit->GetAnimation())
|
|
|
|
m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat());
|
|
|
|
}
|
2010-06-06 00:23:28 +02:00
|
|
|
}
|
|
|
|
|
2011-10-29 17:30:46 +02:00
|
|
|
virtual void SetAnimationSyncOffset(fixed actiontime)
|
2010-06-06 00:23:28 +02:00
|
|
|
{
|
2011-10-29 17:30:46 +02:00
|
|
|
if (m_Unit)
|
|
|
|
{
|
|
|
|
if (m_Unit->GetAnimation())
|
|
|
|
m_Unit->GetAnimation()->SetAnimationSyncOffset(actiontime.ToFloat());
|
|
|
|
}
|
2010-02-07 21:06:16 +01:00
|
|
|
}
|
|
|
|
|
2010-05-02 22:32:37 +02:00
|
|
|
virtual void SetShadingColour(fixed r, fixed g, fixed b, fixed a)
|
2010-03-17 23:56:49 +01:00
|
|
|
{
|
2010-10-04 19:34:33 +02:00
|
|
|
m_R = r;
|
|
|
|
m_G = g;
|
|
|
|
m_B = b;
|
|
|
|
UNUSED2(a); // TODO: why is this even an argument?
|
2010-03-17 23:56:49 +01:00
|
|
|
}
|
|
|
|
|
2011-04-06 02:11:40 +02:00
|
|
|
virtual void SetVariable(std::string name, float value)
|
|
|
|
{
|
2011-10-29 17:30:46 +02:00
|
|
|
if (m_Unit)
|
|
|
|
{
|
|
|
|
m_Unit->GetModel().SetEntityVariable(name, value);
|
|
|
|
}
|
2011-04-06 02:11:40 +02:00
|
|
|
}
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
virtual u32 GetActorSeed()
|
2010-06-03 03:29:43 +02:00
|
|
|
{
|
2013-02-03 03:08:20 +01:00
|
|
|
return m_Seed;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SetActorSeed(u32 seed)
|
|
|
|
{
|
|
|
|
if (seed == m_Seed)
|
2010-06-03 03:29:43 +02:00
|
|
|
return;
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
m_Seed = seed;
|
|
|
|
ReloadActor();
|
|
|
|
}
|
2010-06-03 03:29:43 +02:00
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
virtual bool HasConstructionPreview()
|
|
|
|
{
|
|
|
|
return m_ConstructionPreview;
|
|
|
|
}
|
2010-06-03 03:29:43 +02:00
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
virtual void SetConstructionProgress(fixed progress)
|
|
|
|
{
|
2013-06-25 03:09:43 +02:00
|
|
|
m_ConstructionProgress = progress;
|
2013-02-03 03:08:20 +01:00
|
|
|
}
|
2010-06-03 03:29:43 +02:00
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
virtual void Hotload(const VfsPath& name)
|
|
|
|
{
|
|
|
|
if (!m_Unit)
|
|
|
|
return;
|
2010-06-03 03:29:43 +02:00
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
if (name != m_ActorName)
|
|
|
|
return;
|
2011-03-18 17:57:54 +01:00
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
ReloadActor();
|
2010-06-03 03:29:43 +02:00
|
|
|
}
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
private:
|
2013-02-03 03:08:20 +01:00
|
|
|
/// Helper function shared by component init and actor reloading
|
|
|
|
void InitModel(const CParamNode& paramNode);
|
2011-04-02 14:51:42 +02:00
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
/// Helper method; initializes the model selection shape descriptor from XML. Factored out for readability of @ref Init.
|
2013-02-03 03:08:20 +01:00
|
|
|
void InitSelectionShapeDescriptor(const CParamNode& paramNode);
|
|
|
|
|
|
|
|
void ReloadActor();
|
2011-11-25 07:36:13 +01:00
|
|
|
|
2010-09-03 11:55:14 +02:00
|
|
|
void Update(fixed turnLength);
|
2012-07-30 23:06:54 +02:00
|
|
|
void UpdateVisibility();
|
2010-09-03 11:55:14 +02:00
|
|
|
void Interpolate(float frameTime, float frameOffset);
|
|
|
|
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
|
2010-01-09 20:20:14 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
REGISTER_COMPONENT_TYPE(VisualActor)
|
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
// ------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
void CCmpVisualActor::InitModel(const CParamNode& paramNode)
|
|
|
|
{
|
|
|
|
if (GetSimContext().HasUnitManager())
|
|
|
|
{
|
|
|
|
std::set<CStr> selections;
|
|
|
|
m_Unit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
|
|
|
|
if (m_Unit)
|
|
|
|
{
|
|
|
|
CModelAbstract& model = m_Unit->GetModel();
|
|
|
|
if (model.ToCModel())
|
|
|
|
{
|
|
|
|
u32 modelFlags = 0;
|
|
|
|
|
|
|
|
if (paramNode.GetChild("SilhouetteDisplay").ToBool())
|
|
|
|
modelFlags |= MODELFLAG_SILHOUETTE_DISPLAY;
|
|
|
|
|
|
|
|
if (paramNode.GetChild("SilhouetteOccluder").ToBool())
|
|
|
|
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
|
|
|
|
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpVision> cmpVision(GetEntityHandle());
|
2013-02-03 03:08:20 +01:00
|
|
|
if (cmpVision && cmpVision->GetAlwaysVisible())
|
|
|
|
modelFlags |= MODELFLAG_IGNORE_LOS;
|
|
|
|
|
|
|
|
model.ToCModel()->AddFlagsRec(modelFlags);
|
|
|
|
}
|
|
|
|
|
2013-08-19 00:17:57 +02:00
|
|
|
if (paramNode.GetChild("DisableShadows").IsOk())
|
|
|
|
{
|
|
|
|
if (model.ToCModel())
|
|
|
|
model.ToCModel()->RemoveShadowsRec();
|
|
|
|
else if (model.ToCModelDecal())
|
|
|
|
model.ToCModelDecal()->RemoveShadows();
|
|
|
|
}
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
// Initialize the model's selection shape descriptor. This currently relies on the component initialization order; the
|
|
|
|
// Footprint component must be initialized before this component (VisualActor) to support the ability to use the footprint
|
|
|
|
// shape for the selection box (instead of the default recursive bounding box). See TypeList.h for the order in
|
|
|
|
// which components are initialized; if for whatever reason you need to get rid of this dependency, you can always just
|
|
|
|
// initialize the selection shape descriptor on-demand.
|
|
|
|
InitSelectionShapeDescriptor(paramNode);
|
|
|
|
|
|
|
|
m_Unit->SetID(GetEntityId());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CCmpVisualActor::InitSelectionShapeDescriptor(const CParamNode& paramNode)
|
2011-11-25 07:36:13 +01:00
|
|
|
{
|
|
|
|
// by default, we don't need a custom selection shape and we can just keep the default behaviour
|
|
|
|
CModelAbstract::CustomSelectionShape* shapeDescriptor = NULL;
|
|
|
|
|
|
|
|
const CParamNode& shapeNode = paramNode.GetChild("SelectionShape");
|
|
|
|
if (shapeNode.IsOk())
|
|
|
|
{
|
|
|
|
if (shapeNode.GetChild("Bounds").IsOk())
|
|
|
|
{
|
|
|
|
// default; no need to take action
|
|
|
|
}
|
|
|
|
else if (shapeNode.GetChild("Footprint").IsOk())
|
|
|
|
{
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpFootprint> cmpFootprint(GetEntityHandle());
|
2012-02-08 03:46:15 +01:00
|
|
|
if (cmpFootprint)
|
2011-11-25 07:36:13 +01:00
|
|
|
{
|
|
|
|
ICmpFootprint::EShape fpShape; // fp stands for "footprint"
|
|
|
|
entity_pos_t fpSize0, fpSize1, fpHeight; // fp stands for "footprint"
|
|
|
|
cmpFootprint->GetShape(fpShape, fpSize0, fpSize1, fpHeight);
|
|
|
|
|
|
|
|
float size0 = fpSize0.ToFloat();
|
|
|
|
float size1 = fpSize1.ToFloat();
|
|
|
|
|
|
|
|
// TODO: we should properly distinguish between CIRCLE and SQUARE footprint shapes here, but since cylinders
|
|
|
|
// aren't implemented yet and are almost indistinguishable from boxes for small enough sizes anyway,
|
|
|
|
// we'll just use boxes for either case. However, for circular footprints the size0 and size1 values both
|
|
|
|
// represent the radius, so we do have to adjust them to match the size1 and size0's of square footprints
|
|
|
|
// (which represent the full width and depth).
|
|
|
|
if (fpShape == ICmpFootprint::CIRCLE)
|
|
|
|
{
|
|
|
|
size0 *= 2;
|
|
|
|
size1 *= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
shapeDescriptor = new CModelAbstract::CustomSelectionShape;
|
|
|
|
shapeDescriptor->m_Type = CModelAbstract::CustomSelectionShape::BOX;
|
|
|
|
shapeDescriptor->m_Size0 = size0;
|
|
|
|
shapeDescriptor->m_Size1 = size1;
|
|
|
|
shapeDescriptor->m_Height = fpHeight.ToFloat();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LOGERROR(L"[VisualActor] Cannot apply footprint-based SelectionShape; Footprint component not initialized.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (shapeNode.GetChild("Box").IsOk())
|
|
|
|
{
|
|
|
|
// TODO: we might need to support the ability to specify a different box center in the future
|
|
|
|
shapeDescriptor = new CModelAbstract::CustomSelectionShape;
|
|
|
|
shapeDescriptor->m_Type = CModelAbstract::CustomSelectionShape::BOX;
|
|
|
|
shapeDescriptor->m_Size0 = shapeNode.GetChild("Box").GetChild("@width").ToFixed().ToFloat();
|
|
|
|
shapeDescriptor->m_Size1 = shapeNode.GetChild("Box").GetChild("@depth").ToFixed().ToFloat();
|
|
|
|
shapeDescriptor->m_Height = shapeNode.GetChild("Box").GetChild("@height").ToFixed().ToFloat();
|
|
|
|
}
|
|
|
|
else if (shapeNode.GetChild("Cylinder").IsOk())
|
|
|
|
{
|
|
|
|
LOGWARNING(L"[VisualActor] TODO: Cylinder selection shapes are not yet implemented; defaulting to recursive bounding boxes");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// shouldn't happen by virtue of validation against schema
|
|
|
|
LOGERROR(L"[VisualActor] No selection shape specified");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
ENSURE(m_Unit);
|
2011-11-25 07:36:13 +01:00
|
|
|
// the model is now responsible for cleaning up the descriptor
|
2013-02-03 03:08:20 +01:00
|
|
|
m_Unit->GetModel().SetCustomSelectionShape(shapeDescriptor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CCmpVisualActor::ReloadActor()
|
|
|
|
{
|
|
|
|
if (!m_Unit)
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::set<CStr> selections;
|
|
|
|
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, GetActorSeed(), selections);
|
|
|
|
|
|
|
|
if (!newUnit)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Save some data from the old unit
|
|
|
|
CColor shading = m_Unit->GetModel().GetShadingColor();
|
|
|
|
player_id_t playerID = m_Unit->GetModel().GetPlayerID();
|
|
|
|
|
|
|
|
// Replace with the new unit
|
|
|
|
GetSimContext().GetUnitManager().DeleteUnit(m_Unit);
|
|
|
|
|
|
|
|
// HACK: selection shape needs template data, but rather than storing all that data
|
|
|
|
// in the component, we load the template here and pass it into a helper function
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpTemplateManager> cmpTemplateManager(GetSystemEntity());
|
2013-02-03 03:08:20 +01:00
|
|
|
const CParamNode* node = cmpTemplateManager->LoadLatestTemplate(GetEntityId());
|
|
|
|
ENSURE(node && node->GetChild("VisualActor").IsOk());
|
|
|
|
|
|
|
|
InitModel(node->GetChild("VisualActor"));
|
|
|
|
|
|
|
|
m_Unit->SetEntitySelection(m_AnimName);
|
|
|
|
if (m_Unit->GetAnimation())
|
|
|
|
m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str());
|
|
|
|
|
|
|
|
// We'll lose the exact synchronisation but we should at least make sure it's going at the correct rate
|
|
|
|
if (!m_AnimSyncRepeatTime.IsZero())
|
|
|
|
if (m_Unit->GetAnimation())
|
|
|
|
m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat());
|
|
|
|
|
|
|
|
m_Unit->GetModel().SetShadingColor(shading);
|
|
|
|
|
|
|
|
m_Unit->GetModel().SetPlayerID(playerID);
|
2011-11-25 07:36:13 +01:00
|
|
|
}
|
|
|
|
|
2012-12-06 20:46:13 +01:00
|
|
|
void CCmpVisualActor::Update(fixed UNUSED(turnLength))
|
2010-09-03 11:55:14 +02:00
|
|
|
{
|
|
|
|
if (m_Unit == NULL)
|
|
|
|
return;
|
|
|
|
|
2012-07-30 23:06:54 +02:00
|
|
|
UpdateVisibility();
|
|
|
|
|
2010-09-03 11:55:14 +02:00
|
|
|
// If we're in the special movement mode, select an appropriate animation
|
2011-10-29 17:30:46 +02:00
|
|
|
if (!m_AnimRunThreshold.IsZero())
|
2010-09-03 11:55:14 +02:00
|
|
|
{
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
2012-02-08 03:46:15 +01:00
|
|
|
if (!cmpPosition || !cmpPosition->IsInWorld())
|
2010-09-03 11:55:14 +02:00
|
|
|
return;
|
|
|
|
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetEntityHandle());
|
2012-12-06 20:46:13 +01:00
|
|
|
if (!cmpUnitMotion)
|
|
|
|
return;
|
|
|
|
|
|
|
|
float speed = cmpUnitMotion->GetCurrentSpeed().ToFloat();
|
2010-09-03 11:55:14 +02:00
|
|
|
|
2012-12-06 20:46:13 +01:00
|
|
|
std::string name;
|
|
|
|
if (speed == 0.0f)
|
|
|
|
name = "idle";
|
|
|
|
else
|
|
|
|
name = (speed < m_AnimRunThreshold.ToFloat()) ? "walk" : "run";
|
|
|
|
|
|
|
|
std::map<std::string, std::string>::const_iterator it = m_AnimOverride.find(name);
|
|
|
|
if (it != m_AnimOverride.end())
|
|
|
|
name = it->second;
|
|
|
|
|
|
|
|
m_Unit->SetEntitySelection(name);
|
2010-09-03 11:55:14 +02:00
|
|
|
if (speed == 0.0f)
|
2011-03-13 20:22:05 +01:00
|
|
|
{
|
2012-12-06 20:46:13 +01:00
|
|
|
m_Unit->SetEntitySelection(name);
|
2011-03-13 20:22:05 +01:00
|
|
|
if (m_Unit->GetAnimation())
|
2012-12-06 20:46:13 +01:00
|
|
|
m_Unit->GetAnimation()->SetAnimationState(name, false, 1.f, 0.f, L"");
|
2011-03-13 20:22:05 +01:00
|
|
|
}
|
2010-09-03 11:55:14 +02:00
|
|
|
else
|
2011-03-13 20:22:05 +01:00
|
|
|
{
|
2012-12-06 20:46:13 +01:00
|
|
|
m_Unit->SetEntitySelection(name);
|
2011-03-13 20:22:05 +01:00
|
|
|
if (m_Unit->GetAnimation())
|
2012-12-06 20:46:13 +01:00
|
|
|
m_Unit->GetAnimation()->SetAnimationState(name, false, speed, 0.f, L"");
|
2011-03-13 20:22:05 +01:00
|
|
|
}
|
2010-09-03 11:55:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-30 23:06:54 +02:00
|
|
|
void CCmpVisualActor::UpdateVisibility()
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
2012-07-30 23:06:54 +02:00
|
|
|
ICmpRangeManager::ELosVisibility oldVisibility = m_Visibility;
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
2012-07-30 23:06:54 +02:00
|
|
|
if (cmpPosition && cmpPosition->IsInWorld())
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
2012-07-30 23:06:54 +02:00
|
|
|
// The 'always visible' flag means we should always render the unit
|
|
|
|
// (regardless of whether the LOS system thinks it's visible)
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpVision> cmpVision(GetEntityHandle());
|
2012-07-30 23:06:54 +02:00
|
|
|
if (cmpVision && cmpVision->GetAlwaysVisible())
|
|
|
|
m_Visibility = ICmpRangeManager::VIS_VISIBLE;
|
|
|
|
else
|
|
|
|
{
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity());
|
2012-08-07 00:38:42 +02:00
|
|
|
// Uncomment the following lines to prevent the models from popping into existence
|
|
|
|
// near the LOS boundary. Is rather resource intensive.
|
|
|
|
//if (cmpVision->GetRetainInFog())
|
|
|
|
// m_Visibility = ICmpRangeManager::VIS_VISIBLE;
|
|
|
|
//else
|
2013-09-11 22:41:53 +02:00
|
|
|
m_Visibility = cmpRangeManager->GetLosVisibility(GetEntityHandle(),
|
2012-08-07 00:38:42 +02:00
|
|
|
GetSimContext().GetCurrentDisplayedPlayer());
|
2012-07-30 23:06:54 +02:00
|
|
|
}
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
2012-07-30 23:06:54 +02:00
|
|
|
else
|
|
|
|
m_Visibility = ICmpRangeManager::VIS_HIDDEN;
|
2010-01-09 20:20:14 +01:00
|
|
|
|
2012-07-30 23:06:54 +02:00
|
|
|
if (m_Visibility != oldVisibility)
|
2010-10-04 19:34:33 +02:00
|
|
|
{
|
2012-07-30 23:06:54 +02:00
|
|
|
// Change the visibility of the visual actor's selectable if it has one.
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpSelectable> cmpSelectable(GetEntityHandle());
|
2012-07-30 23:06:54 +02:00
|
|
|
if (cmpSelectable)
|
|
|
|
cmpSelectable->SetVisibility(m_Visibility == ICmpRangeManager::VIS_HIDDEN ? false : true);
|
2010-10-04 19:34:33 +02:00
|
|
|
}
|
2012-07-30 23:06:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
|
|
|
|
{
|
|
|
|
if (m_Unit == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Disable rendering of the unit if it has no position
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
2012-07-30 23:06:54 +02:00
|
|
|
if (!cmpPosition || !cmpPosition->IsInWorld())
|
|
|
|
return;
|
2012-08-06 18:46:54 +02:00
|
|
|
else if (!m_PreviouslyRendered)
|
2010-10-04 19:34:33 +02:00
|
|
|
{
|
2012-07-30 23:06:54 +02:00
|
|
|
UpdateVisibility();
|
2012-08-06 18:46:54 +02:00
|
|
|
m_PreviouslyRendered = true;
|
2010-10-04 19:34:33 +02:00
|
|
|
}
|
2013-03-23 18:59:54 +01:00
|
|
|
|
2010-09-23 14:13:13 +02:00
|
|
|
// Even if HIDDEN due to LOS, we need to set up the transforms
|
|
|
|
// so that projectiles will be launched from the right place
|
2010-08-05 12:20:47 +02:00
|
|
|
|
2010-08-09 03:28:13 +02:00
|
|
|
bool floating = m_Unit->GetObject().m_Base->m_Properties.m_FloatOnWater;
|
|
|
|
|
|
|
|
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset, floating));
|
2010-01-09 20:20:14 +01:00
|
|
|
|
2013-02-03 03:08:20 +01:00
|
|
|
if (!m_ConstructionProgress.IsZero())
|
|
|
|
{
|
|
|
|
// We use selection boxes to calculate the model size, since the model could be offset
|
|
|
|
// TODO: this annoyingly shows decals, would be nice to hide them
|
|
|
|
CBoundingBoxOriented bounds = GetSelectionBox();
|
|
|
|
if (!bounds.IsEmpty())
|
|
|
|
{
|
|
|
|
float dy = 2.0f * bounds.m_HalfSizes.Y;
|
|
|
|
|
|
|
|
// If this is a floating unit, we want it to start all the way under the terrain,
|
|
|
|
// so find the difference between its current position and the terrain
|
|
|
|
|
2013-09-11 22:41:53 +02:00
|
|
|
CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
|
2013-02-03 03:08:20 +01:00
|
|
|
if (floating && cmpTerrain)
|
|
|
|
{
|
|
|
|
CVector3D pos = transform.GetTranslation();
|
|
|
|
float ground = cmpTerrain->GetExactGroundLevel(pos.X, pos.Z);
|
|
|
|
dy += std::max(0.f, pos.Y - ground);
|
|
|
|
}
|
|
|
|
|
|
|
|
transform.Translate(0.0f, (m_ConstructionProgress.ToFloat() - 1.0f) * dy, 0.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-13 20:22:05 +01:00
|
|
|
CModelAbstract& model = m_Unit->GetModel();
|
2010-10-04 19:34:33 +02:00
|
|
|
|
|
|
|
model.SetTransform(transform);
|
2010-02-07 21:06:16 +01:00
|
|
|
m_Unit->UpdateModel(frameTime);
|
2010-10-04 19:34:33 +02:00
|
|
|
|
|
|
|
// If not hidden, then we need to set up some extra state for rendering
|
|
|
|
if (m_Visibility != ICmpRangeManager::VIS_HIDDEN)
|
|
|
|
{
|
|
|
|
model.ValidatePosition();
|
2011-04-02 14:04:19 +02:00
|
|
|
model.SetShadingColor(CColor(m_R.ToFloat(), m_G.ToFloat(), m_B.ToFloat(), 1.0f));
|
2010-10-04 19:34:33 +02:00
|
|
|
}
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
|
2010-09-03 11:55:14 +02:00
|
|
|
void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
|
2010-01-09 20:20:14 +01:00
|
|
|
{
|
2010-01-22 21:03:14 +01:00
|
|
|
if (m_Unit == NULL)
|
|
|
|
return;
|
|
|
|
|
2010-09-23 14:13:13 +02:00
|
|
|
if (m_Visibility == ICmpRangeManager::VIS_HIDDEN)
|
2010-08-05 12:20:47 +02:00
|
|
|
return;
|
|
|
|
|
2011-03-13 20:22:05 +01:00
|
|
|
CModelAbstract& model = m_Unit->GetModel();
|
2013-12-30 16:52:42 +01:00
|
|
|
|
|
|
|
if (!g_AtlasGameLoop->running && !g_RenderActors && m_IsActorOnly)
|
|
|
|
return;
|
2010-01-09 20:20:14 +01:00
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetWorldBoundsRec()))
|
2010-01-09 20:20:14 +01:00
|
|
|
return;
|
|
|
|
|
2013-06-10 01:18:21 +02:00
|
|
|
if (!g_AtlasGameLoop->running && m_VisibleInAtlasOnly)
|
|
|
|
return;
|
|
|
|
|
2010-04-17 13:44:08 +02:00
|
|
|
collector.SubmitRecursive(&model);
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|