forked from 0ad/0ad
New unit renderer.
Instead of each CCmpVisualActor rendering itself individually, collect all the units in a single CCmpUnitRenderer. This avoids the overhead of doing Interpolate/RenderSubmit calls every frame for every object in the world. It also allows more efficient culling. CCmpUnitRenderer knows the positions of each object at the start and end of each turn, and computes the bounding sphere of the object along that path. That allows quick culling without recomputing the precise interpolated transform every frame. (In the future it could be improved much more.) Clarify and clean up the sending of PositionChanged messages, and add new InterpolatedPositionChanged. Remove the forceFloating parameter from GetInterpolatedTransform, since it doesn't fit the new design. Replace it with a (non-synchronised) flag in CCmpPosition. Move construction progress from CCmpVisualActor to CCmpPosition, so that it consistently affects all position/transform computation. Refs #2337. This was SVN commit r15265.
This commit is contained in:
parent
d117d96d22
commit
1882f28504
@ -41,9 +41,9 @@ Foundation.prototype.InitialiseConstruction = function(owner, template)
|
||||
Foundation.prototype.OnHealthChanged = function(msg)
|
||||
{
|
||||
// Gradually reveal the final building preview
|
||||
var cmpPreviewVisual = Engine.QueryInterface(this.previewEntity, IID_Visual);
|
||||
if (cmpPreviewVisual)
|
||||
cmpPreviewVisual.SetConstructionProgress(this.GetBuildProgress());
|
||||
var cmpPosition = Engine.QueryInterface(this.previewEntity, IID_Position);
|
||||
if (cmpPosition)
|
||||
cmpPosition.SetConstructionProgress(this.GetBuildProgress());
|
||||
|
||||
Engine.PostMessage(this.entity, MT_FoundationProgressChanged, { "to": this.GetBuildPercentage() });
|
||||
};
|
||||
@ -218,10 +218,12 @@ Foundation.prototype.Build = function(builderEnt, work)
|
||||
cmpPreviewOwnership.SetOwner(cmpFoundationOwnership.GetOwner());
|
||||
|
||||
// Initially hide the preview underground
|
||||
var cmpPreviewPosition = Engine.QueryInterface(this.previewEntity, IID_Position);
|
||||
cmpPreviewPosition.SetConstructionProgress(0.0);
|
||||
|
||||
var cmpPreviewVisual = Engine.QueryInterface(this.previewEntity, IID_Visual);
|
||||
if (cmpPreviewVisual)
|
||||
{
|
||||
cmpPreviewVisual.SetConstructionProgress(0.0);
|
||||
cmpPreviewVisual.SetActorSeed(cmpFoundationVisual.GetActorSeed());
|
||||
cmpPreviewVisual.SelectAnimation("scaffold", false, 1.0, "");
|
||||
}
|
||||
@ -229,7 +231,6 @@ Foundation.prototype.Build = function(builderEnt, work)
|
||||
var cmpFoundationPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
var pos = cmpFoundationPosition.GetPosition();
|
||||
var rot = cmpFoundationPosition.GetRotation();
|
||||
var cmpPreviewPosition = Engine.QueryInterface(this.previewEntity, IID_Position);
|
||||
cmpPreviewPosition.SetYRotation(rot.y);
|
||||
cmpPreviewPosition.SetXZRotation(rot.x, rot.z);
|
||||
cmpPreviewPosition.JumpTo(pos.x, pos.z);
|
||||
|
@ -775,7 +775,7 @@ void CGameView::Update(const float deltaRealTime)
|
||||
{
|
||||
// Get the most recent interpolated position
|
||||
float frameOffset = m->Game->GetSimulation2()->GetLastFrameOffset();
|
||||
CMatrix3D transform = cmpPosition->GetInterpolatedTransform(frameOffset, false);
|
||||
CMatrix3D transform = cmpPosition->GetInterpolatedTransform(frameOffset);
|
||||
CVector3D pos = transform.GetTranslation();
|
||||
|
||||
if (m->FollowFirstPerson)
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#include "simulation2/components/ICmpPathfinder.h"
|
||||
|
||||
#include "maths/Vector3D.h"
|
||||
|
||||
#define DEFAULT_MESSAGE_IMPL(name) \
|
||||
virtual int GetType() const { return MT_##name; } \
|
||||
virtual const char* GetScriptHandlerName() const { return "On" #name; } \
|
||||
@ -255,7 +257,8 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Sent during TurnStart.
|
||||
* Sent by CCmpPosition whenever anything has changed that will affect the
|
||||
* return value of GetPosition2D() or GetRotation().Y
|
||||
*
|
||||
* If @c inWorld is false, then the other fields are invalid and meaningless.
|
||||
* Otherwise they represent the current position.
|
||||
@ -276,13 +279,33 @@ public:
|
||||
entity_angle_t a;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sent by CCmpPosition whenever anything has changed that will affect the
|
||||
* return value of GetInterpolatedTransform()
|
||||
*/
|
||||
class CMessageInterpolatedPositionChanged : public CMessage
|
||||
{
|
||||
public:
|
||||
DEFAULT_MESSAGE_IMPL(InterpolatedPositionChanged)
|
||||
|
||||
CMessageInterpolatedPositionChanged(entity_id_t entity, bool inWorld, const CVector3D& pos0, const CVector3D& pos1) :
|
||||
entity(entity), inWorld(inWorld), pos0(pos0), pos1(pos1)
|
||||
{
|
||||
}
|
||||
|
||||
entity_id_t entity;
|
||||
bool inWorld;
|
||||
CVector3D pos0;
|
||||
CVector3D pos1;
|
||||
};
|
||||
|
||||
/*Sent whenever the territory type (neutral,own,enemy) differs from the former type*/
|
||||
class CMessageTerritoryPositionChanged : public CMessage
|
||||
{
|
||||
public:
|
||||
DEFAULT_MESSAGE_IMPL(TerritoryPositionChanged)
|
||||
|
||||
CMessageTerritoryPositionChanged(entity_id_t entity, player_id_t newTerritory) :
|
||||
CMessageTerritoryPositionChanged(entity_id_t entity, player_id_t newTerritory) :
|
||||
entity(entity), newTerritory(newTerritory)
|
||||
{
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ MESSAGE(Create)
|
||||
MESSAGE(Destroy)
|
||||
MESSAGE(OwnershipChanged)
|
||||
MESSAGE(PositionChanged)
|
||||
MESSAGE(InterpolatedPositionChanged)
|
||||
MESSAGE(TerritoryPositionChanged)
|
||||
MESSAGE(MotionChanged)
|
||||
MESSAGE(RangeUpdate)
|
||||
@ -162,6 +163,9 @@ INTERFACE(UnitMotion)
|
||||
COMPONENT(UnitMotion) // must be after Obstruction
|
||||
COMPONENT(UnitMotionScripted)
|
||||
|
||||
INTERFACE(UnitRenderer)
|
||||
COMPONENT(UnitRenderer)
|
||||
|
||||
INTERFACE(Vision)
|
||||
COMPONENT(Vision)
|
||||
|
||||
|
@ -131,7 +131,7 @@ public:
|
||||
}
|
||||
|
||||
// Find the precise position of the unit
|
||||
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset, false));
|
||||
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset));
|
||||
CVector3D position(transform.GetTranslation());
|
||||
|
||||
// Move all the sprites to the desired offset relative to the unit
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -23,6 +23,8 @@
|
||||
#include "simulation2/MessageTypes.h"
|
||||
|
||||
#include "ICmpTerrain.h"
|
||||
#include "ICmpTerritoryManager.h"
|
||||
#include "ICmpVisual.h"
|
||||
#include "ICmpWaterManager.h"
|
||||
|
||||
#include "graphics/Terrain.h"
|
||||
@ -32,7 +34,6 @@
|
||||
#include "maths/Vector3D.h"
|
||||
#include "maths/Vector2D.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ICmpTerritoryManager.h"
|
||||
|
||||
/**
|
||||
* Basic ICmpPosition implementation.
|
||||
@ -44,6 +45,9 @@ public:
|
||||
{
|
||||
componentManager.SubscribeToMessageType(MT_TurnStart);
|
||||
componentManager.SubscribeToMessageType(MT_Interpolate);
|
||||
componentManager.SubscribeToMessageType(MT_TerrainChanged);
|
||||
componentManager.SubscribeToMessageType(MT_WaterChanged);
|
||||
componentManager.SubscribeToMessageType(MT_Deserialized);
|
||||
|
||||
// TODO: if this component turns out to be a performance issue, it should
|
||||
// be optimised by creating a new PositionStatic component that doesn't subscribe
|
||||
@ -60,7 +64,7 @@ public:
|
||||
UPRIGHT = 0,
|
||||
PITCH = 1,
|
||||
PITCH_ROLL = 2,
|
||||
ROLL=3,
|
||||
ROLL = 3,
|
||||
} m_AnchorType;
|
||||
|
||||
bool m_Floating;
|
||||
@ -76,6 +80,8 @@ public:
|
||||
entity_pos_t m_Y, m_LastYDifference; // either the relative or the absolute Y coordinate
|
||||
bool m_RelativeToGround; // whether m_Y is relative to terrain/water plane, or an absolute height
|
||||
|
||||
fixed m_ConstructionProgress;
|
||||
|
||||
// when the entity is a turret, only m_RotY is used, and this is the rotation
|
||||
// relative to the parent entity
|
||||
entity_angle_t m_RotX, m_RotY, m_RotZ;
|
||||
@ -86,9 +92,10 @@ public:
|
||||
CFixedVector3D m_TurretPosition;
|
||||
std::set<entity_id_t> m_Turrets;
|
||||
|
||||
// not serialized:
|
||||
// Not serialized:
|
||||
float m_InterpolatedRotX, m_InterpolatedRotY, m_InterpolatedRotZ;
|
||||
float m_LastInterpolatedRotX, m_LastInterpolatedRotZ; // not serialized
|
||||
float m_LastInterpolatedRotX, m_LastInterpolatedRotZ;
|
||||
bool m_ActorFloating;
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
@ -147,6 +154,8 @@ public:
|
||||
|
||||
m_TurretParent = INVALID_ENTITY;
|
||||
m_TurretPosition = CFixedVector3D();
|
||||
|
||||
m_ActorFloating = false;
|
||||
}
|
||||
|
||||
virtual void Deinit()
|
||||
@ -172,6 +181,7 @@ public:
|
||||
serialize.NumberFixed_Unbounded("altitude", m_Y);
|
||||
serialize.Bool("relative", m_RelativeToGround);
|
||||
serialize.Bool("floating", m_Floating);
|
||||
serialize.NumberFixed_Unbounded("constructionprogress", m_ConstructionProgress);
|
||||
|
||||
if (serialize.IsDebug())
|
||||
{
|
||||
@ -227,6 +237,7 @@ public:
|
||||
deserialize.NumberFixed_Unbounded("altitude", m_Y);
|
||||
deserialize.Bool("relative", m_RelativeToGround);
|
||||
deserialize.Bool("floating", m_Floating);
|
||||
deserialize.NumberFixed_Unbounded("constructionprogress", m_ConstructionProgress);
|
||||
// TODO: should there be range checks on all these values?
|
||||
|
||||
m_InterpolatedRotY = m_RotY.ToFloat();
|
||||
@ -302,6 +313,11 @@ public:
|
||||
return m_TurretParent;
|
||||
}
|
||||
|
||||
void Deserialized()
|
||||
{
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual bool IsInWorld()
|
||||
{
|
||||
return m_InWorld;
|
||||
@ -312,6 +328,7 @@ public:
|
||||
m_InWorld = false;
|
||||
|
||||
AdvertisePositionChanges();
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual void MoveTo(entity_pos_t x, entity_pos_t z)
|
||||
@ -328,6 +345,7 @@ public:
|
||||
}
|
||||
|
||||
AdvertisePositionChanges();
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual void MoveAndTurnTo(entity_pos_t x, entity_pos_t z, entity_angle_t ry)
|
||||
@ -359,6 +377,8 @@ public:
|
||||
m_LastInterpolatedRotZ = m_InterpolatedRotZ;
|
||||
|
||||
AdvertisePositionChanges();
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual void SetHeightOffset(entity_pos_t dy)
|
||||
@ -366,7 +386,7 @@ public:
|
||||
// subtract the offset and replace with a new offset
|
||||
m_LastYDifference = dy - GetHeightOffset();
|
||||
m_Y += m_LastYDifference;
|
||||
AdvertisePositionChanges();
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual entity_pos_t GetHeightOffset()
|
||||
@ -393,7 +413,7 @@ public:
|
||||
// subtract the absolute height and replace it with a new absolute height
|
||||
m_LastYDifference = y - GetHeightFixed();
|
||||
m_Y += m_LastYDifference;
|
||||
AdvertisePositionChanges();
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual entity_pos_t GetHeightFixed()
|
||||
@ -426,6 +446,7 @@ public:
|
||||
m_Y = relative ? GetHeightOffset() : GetHeightFixed();
|
||||
m_RelativeToGround = relative;
|
||||
m_LastYDifference = entity_pos_t::Zero();
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual bool IsFloating()
|
||||
@ -436,6 +457,19 @@ public:
|
||||
virtual void SetFloating(bool flag)
|
||||
{
|
||||
m_Floating = flag;
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual void SetActorFloating(bool flag)
|
||||
{
|
||||
m_ActorFloating = flag;
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual void SetConstructionProgress(fixed progress)
|
||||
{
|
||||
m_ConstructionProgress = progress;
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
}
|
||||
|
||||
virtual CFixedVector3D GetPosition()
|
||||
@ -529,8 +563,6 @@ public:
|
||||
m_LastInterpolatedRotX = m_InterpolatedRotX;
|
||||
m_LastInterpolatedRotZ = m_InterpolatedRotZ;
|
||||
}
|
||||
|
||||
AdvertisePositionChanges();
|
||||
}
|
||||
|
||||
virtual CFixedVector3D GetRotation()
|
||||
@ -556,6 +588,36 @@ public:
|
||||
return CFixedVector2D(m_X - m_LastX, m_Z - m_LastZ).Length();
|
||||
}
|
||||
|
||||
float GetConstructionProgressOffset(const CVector3D& pos)
|
||||
{
|
||||
if (m_ConstructionProgress.IsZero())
|
||||
return 0.0f;
|
||||
|
||||
CmpPtr<ICmpVisual> cmpVisual(GetEntityHandle());
|
||||
if (!cmpVisual)
|
||||
return 0.0f;
|
||||
|
||||
// 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 = cmpVisual->GetSelectionBox();
|
||||
if (bounds.IsEmpty())
|
||||
return 0.0f;
|
||||
|
||||
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
|
||||
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
|
||||
if (cmpTerrain && (m_Floating || m_ActorFloating))
|
||||
{
|
||||
float ground = cmpTerrain->GetExactGroundLevel(pos.X, pos.Z);
|
||||
dy += std::max(0.f, pos.Y - ground);
|
||||
}
|
||||
|
||||
return (m_ConstructionProgress.ToFloat() - 1.0f) * dy;
|
||||
}
|
||||
|
||||
virtual void GetInterpolatedPosition2D(float frameOffset, float& x, float& z, float& rotY)
|
||||
{
|
||||
if (!m_InWorld)
|
||||
@ -570,7 +632,7 @@ public:
|
||||
rotY = m_InterpolatedRotY;
|
||||
}
|
||||
|
||||
virtual CMatrix3D GetInterpolatedTransform(float frameOffset, bool forceFloating)
|
||||
virtual CMatrix3D GetInterpolatedTransform(float frameOffset)
|
||||
{
|
||||
if (m_TurretParent != INVALID_ENTITY)
|
||||
{
|
||||
@ -591,7 +653,7 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
CMatrix3D parentTransformMatrix = cmpPosition->GetInterpolatedTransform(frameOffset, forceFloating);
|
||||
CMatrix3D parentTransformMatrix = cmpPosition->GetInterpolatedTransform(frameOffset);
|
||||
CMatrix3D ownTransformation = CMatrix3D();
|
||||
ownTransformation.SetYRotation(m_InterpolatedRotY);
|
||||
ownTransformation.Translate(-m_TurretPosition.X.ToFloat(), m_TurretPosition.Y.ToFloat(), -m_TurretPosition.Z.ToFloat());
|
||||
@ -617,7 +679,7 @@ public:
|
||||
if (cmpTerrain)
|
||||
baseY = cmpTerrain->GetExactGroundLevel(x, z);
|
||||
|
||||
if (m_Floating || forceFloating)
|
||||
if (m_Floating || m_ActorFloating)
|
||||
{
|
||||
CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity());
|
||||
if (cmpWaterManager)
|
||||
@ -634,12 +696,54 @@ public:
|
||||
m.SetXRotation(Interpolate(m_LastInterpolatedRotX, m_InterpolatedRotX, frameOffset));
|
||||
m.RotateZ(Interpolate(m_LastInterpolatedRotZ, m_InterpolatedRotZ, frameOffset));
|
||||
|
||||
CVector3D pos(x, y, z);
|
||||
|
||||
pos.Y += GetConstructionProgressOffset(pos);
|
||||
|
||||
m.RotateY(rotY + (float)M_PI);
|
||||
m.Translate(CVector3D(x, y, z));
|
||||
m.Translate(pos);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void GetInterpolatedPositions(CVector3D& pos0, CVector3D& pos1)
|
||||
{
|
||||
float baseY0 = 0;
|
||||
float baseY1 = 0;
|
||||
float x0 = m_LastX.ToFloat();
|
||||
float z0 = m_LastZ.ToFloat();
|
||||
float x1 = m_X.ToFloat();
|
||||
float z1 = m_Z.ToFloat();
|
||||
if (m_RelativeToGround)
|
||||
{
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (cmpTerrain)
|
||||
{
|
||||
baseY0 = cmpTerrain->GetExactGroundLevel(x0, z0);
|
||||
baseY1 = cmpTerrain->GetExactGroundLevel(x1, z1);
|
||||
}
|
||||
|
||||
if (m_Floating || m_ActorFloating)
|
||||
{
|
||||
CmpPtr<ICmpWaterManager> cmpWaterManager(GetSimContext(), SYSTEM_ENTITY);
|
||||
if (cmpWaterManager)
|
||||
{
|
||||
baseY0 = std::max(baseY0, cmpWaterManager->GetExactWaterLevel(x0, z0));
|
||||
baseY1 = std::max(baseY1, cmpWaterManager->GetExactWaterLevel(x1, z1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float y0 = baseY0 + m_Y.ToFloat() + m_LastYDifference.ToFloat();
|
||||
float y1 = baseY1 + m_Y.ToFloat();
|
||||
|
||||
pos0 = CVector3D(x0, y0, z0);
|
||||
pos1 = CVector3D(x1, y1, z1);
|
||||
|
||||
pos0.Y += GetConstructionProgressOffset(pos0);
|
||||
pos1.Y += GetConstructionProgressOffset(pos1);
|
||||
}
|
||||
|
||||
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
|
||||
{
|
||||
switch (msg.GetType())
|
||||
@ -716,10 +820,29 @@ public:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_TerrainChanged:
|
||||
case MT_WaterChanged:
|
||||
{
|
||||
AdvertiseInterpolatedPositionChanges();
|
||||
break;
|
||||
}
|
||||
case MT_Deserialized:
|
||||
{
|
||||
Deserialized();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* This must be called after changing anything that will affect the
|
||||
* return value of GetPosition2D() or GetRotation().Y:
|
||||
* - m_InWorld
|
||||
* - m_X, m_Z
|
||||
* - m_RotY
|
||||
*/
|
||||
void AdvertisePositionChanges()
|
||||
{
|
||||
for (std::set<entity_id_t>::const_iterator it = m_Turrets.begin(); it != m_Turrets.end(); ++it)
|
||||
@ -740,6 +863,33 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This must be called after changing anything that will affect the
|
||||
* return value of GetInterpolatedPositions():
|
||||
* - m_InWorld
|
||||
* - m_X, m_Z
|
||||
* - m_LastX, m_LastZ
|
||||
* - m_Y, m_LastYDifference, m_RelativeToGround
|
||||
* - If m_RelativeToGround, then the ground under this unit
|
||||
* - If m_RelativeToGround && m_Float, then the water level
|
||||
*/
|
||||
void AdvertiseInterpolatedPositionChanges()
|
||||
{
|
||||
if (m_InWorld)
|
||||
{
|
||||
CVector3D pos0, pos1;
|
||||
GetInterpolatedPositions(pos0, pos1);
|
||||
|
||||
CMessageInterpolatedPositionChanged msg(GetEntityId(), true, pos0, pos1);
|
||||
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
CMessageInterpolatedPositionChanged msg(GetEntityId(), false, CVector3D(), CVector3D());
|
||||
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateXZRotation()
|
||||
{
|
||||
if (!m_InWorld)
|
||||
|
400
source/simulation2/components/CCmpUnitRenderer.cpp
Normal file
400
source/simulation2/components/CCmpUnitRenderer.cpp
Normal file
@ -0,0 +1,400 @@
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* 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 "ICmpUnitRenderer.h"
|
||||
|
||||
#include "simulation2/MessageTypes.h"
|
||||
|
||||
#include "ICmpPosition.h"
|
||||
#include "ICmpRangeManager.h"
|
||||
#include "ICmpSelectable.h"
|
||||
#include "ICmpVision.h"
|
||||
|
||||
#include "graphics/Frustum.h"
|
||||
#include "graphics/ModelAbstract.h"
|
||||
#include "graphics/ObjectEntry.h"
|
||||
#include "graphics/Overlay.h"
|
||||
#include "graphics/Unit.h"
|
||||
#include "maths/BoundingSphere.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
#include "renderer/Scene.h"
|
||||
|
||||
#include "tools/atlas/GameInterface/GameLoop.h"
|
||||
|
||||
/**
|
||||
* Efficiently(ish) renders all the units in the world.
|
||||
*
|
||||
* The class maintains a list of all units that currently exist, and the data
|
||||
* needed for frustum-culling them. To minimise the amount of work done per
|
||||
* frame (despite a unit's interpolated position changing every frame), the
|
||||
* culling data is only updated once per turn: we store the position at the
|
||||
* start of the turn, and the position at the end of the turn, and assume the
|
||||
* unit might be anywhere between those two points (linearly).
|
||||
*
|
||||
* (Note this is a slightly invalid assumption: units don't always move linearly,
|
||||
* since their interpolated position depends on terrain and water. But over a
|
||||
* single turn it's probably going to be a good enough approximation, and will
|
||||
* only break for units that both start and end the turn off-screen.)
|
||||
*
|
||||
* We want to ignore rotation entirely, since it's a complex function of
|
||||
* interpolated position and terrain. So we store a bounding sphere, which
|
||||
* is rotation-independent, instead of a bounding box.
|
||||
*/
|
||||
class CCmpUnitRenderer : public ICmpUnitRenderer
|
||||
{
|
||||
public:
|
||||
struct SUnit
|
||||
{
|
||||
CEntityHandle entity;
|
||||
|
||||
CUnit* actor;
|
||||
|
||||
int flags;
|
||||
|
||||
/**
|
||||
* Worst-case bounding shape, relative to position. Needs to account
|
||||
* for all possible animations, orientations, etc.
|
||||
*/
|
||||
CBoundingSphere boundsApprox;
|
||||
|
||||
/**
|
||||
* Cached LOS visibility status.
|
||||
*/
|
||||
ICmpRangeManager::ELosVisibility visibility;
|
||||
bool visibilityDirty;
|
||||
|
||||
/**
|
||||
* Whether the unit has a valid position. If false, pos0 and pos1
|
||||
* are meaningless.
|
||||
*/
|
||||
bool inWorld;
|
||||
|
||||
/**
|
||||
* World-space positions to interpolate between.
|
||||
*/
|
||||
CVector3D pos0;
|
||||
CVector3D pos1;
|
||||
|
||||
/**
|
||||
* Bounds encompassing the unit's bounds when it is anywhere between
|
||||
* pos0 and pos1.
|
||||
*/
|
||||
CBoundingSphere sweptBounds;
|
||||
|
||||
/**
|
||||
* For debug overlay.
|
||||
*/
|
||||
bool culled;
|
||||
};
|
||||
|
||||
std::vector<SUnit> m_Units;
|
||||
std::vector<tag_t> m_UnitTagsFree;
|
||||
|
||||
float m_FrameOffset;
|
||||
|
||||
bool m_EnableDebugOverlays;
|
||||
std::vector<SOverlaySphere> m_DebugSpheres;
|
||||
|
||||
static void ClassInit(CComponentManager& componentManager)
|
||||
{
|
||||
componentManager.SubscribeToMessageType(MT_TurnStart);
|
||||
componentManager.SubscribeToMessageType(MT_Interpolate);
|
||||
componentManager.SubscribeToMessageType(MT_RenderSubmit);
|
||||
}
|
||||
|
||||
DEFAULT_COMPONENT_ALLOCATOR(UnitRenderer)
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
return "<a:component type='system'/><empty/>";
|
||||
}
|
||||
|
||||
virtual void Init(const CParamNode& UNUSED(paramNode))
|
||||
{
|
||||
m_FrameOffset = 0.0f;
|
||||
m_EnableDebugOverlays = false;
|
||||
}
|
||||
|
||||
virtual void Deinit()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Serialize(ISerializer& UNUSED(serialize))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
|
||||
{
|
||||
Init(paramNode);
|
||||
}
|
||||
|
||||
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
|
||||
{
|
||||
switch (msg.GetType())
|
||||
{
|
||||
case MT_TurnStart:
|
||||
{
|
||||
TurnStart();
|
||||
break;
|
||||
}
|
||||
case MT_Interpolate:
|
||||
{
|
||||
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
|
||||
Interpolate(msgData.deltaSimTime, msgData.offset);
|
||||
break;
|
||||
}
|
||||
case MT_RenderSubmit:
|
||||
{
|
||||
const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
|
||||
RenderSubmit(msgData.collector, msgData.frustum, msgData.culling);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SUnit* LookupUnit(tag_t tag)
|
||||
{
|
||||
if (tag.n < 1 || tag.n - 1 >= m_Units.size())
|
||||
return NULL;
|
||||
return &m_Units[tag.n - 1];
|
||||
}
|
||||
|
||||
virtual tag_t AddUnit(CEntityHandle entity, CUnit* actor, const CBoundingSphere& boundsApprox, int flags)
|
||||
{
|
||||
ENSURE(actor != NULL);
|
||||
|
||||
tag_t tag;
|
||||
if (!m_UnitTagsFree.empty())
|
||||
{
|
||||
tag = m_UnitTagsFree.back();
|
||||
m_UnitTagsFree.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Units.push_back(SUnit());
|
||||
tag.n = m_Units.size();
|
||||
}
|
||||
|
||||
SUnit* unit = LookupUnit(tag);
|
||||
unit->entity = entity;
|
||||
unit->actor = actor;
|
||||
unit->flags = flags;
|
||||
unit->boundsApprox = boundsApprox;
|
||||
unit->inWorld = false;
|
||||
unit->visibilityDirty = true;
|
||||
unit->pos0 = unit->pos1 = CVector3D();
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
virtual void RemoveUnit(tag_t tag)
|
||||
{
|
||||
SUnit* unit = LookupUnit(tag);
|
||||
unit->actor = NULL;
|
||||
unit->inWorld = false;
|
||||
m_UnitTagsFree.push_back(tag);
|
||||
}
|
||||
|
||||
void RecomputeSweptBounds(SUnit* unit)
|
||||
{
|
||||
// Compute the bounding sphere of the capsule formed by
|
||||
// sweeping boundsApprox from pos0 to pos1
|
||||
CVector3D mid = (unit->pos0 + unit->pos1) * 0.5f + unit->boundsApprox.GetCenter();
|
||||
float radius = (unit->pos1 - unit->pos0).Length() * 0.5f + unit->boundsApprox.GetRadius();
|
||||
unit->sweptBounds = CBoundingSphere(mid, radius);
|
||||
}
|
||||
|
||||
virtual void UpdateUnit(tag_t tag, CUnit* actor, const CBoundingSphere& boundsApprox)
|
||||
{
|
||||
SUnit* unit = LookupUnit(tag);
|
||||
unit->actor = actor;
|
||||
unit->boundsApprox = boundsApprox;
|
||||
RecomputeSweptBounds(unit);
|
||||
}
|
||||
|
||||
virtual void UpdateUnitPos(tag_t tag, bool inWorld, const CVector3D& pos0, const CVector3D& pos1)
|
||||
{
|
||||
SUnit* unit = LookupUnit(tag);
|
||||
unit->inWorld = inWorld;
|
||||
unit->pos0 = pos0;
|
||||
unit->pos1 = pos1;
|
||||
unit->visibilityDirty = true;
|
||||
RecomputeSweptBounds(unit);
|
||||
}
|
||||
|
||||
void TurnStart();
|
||||
void Interpolate(float frameTime, float frameOffset);
|
||||
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
|
||||
|
||||
void UpdateVisibility(SUnit& unit);
|
||||
|
||||
virtual float GetFrameOffset()
|
||||
{
|
||||
return m_FrameOffset;
|
||||
}
|
||||
|
||||
virtual void SetDebugOverlay(bool enabled)
|
||||
{
|
||||
m_EnableDebugOverlays = enabled;
|
||||
}
|
||||
};
|
||||
|
||||
void CCmpUnitRenderer::TurnStart()
|
||||
{
|
||||
PROFILE3("UnitRenderer::TurnStart");
|
||||
|
||||
// Assume units have stopped moving after the previous turn. If that assumption is not
|
||||
// correct, we will get a UpdateUnitPos to tell us about its movement in the new turn.
|
||||
for (size_t i = 0; i < m_Units.size(); i++)
|
||||
{
|
||||
SUnit& unit = m_Units[i];
|
||||
unit.pos0 = unit.pos1;
|
||||
unit.sweptBounds = CBoundingSphere(unit.pos1, unit.boundsApprox.GetRadius());
|
||||
|
||||
// Visibility must be recomputed on the first frame during this turn
|
||||
unit.visibilityDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpUnitRenderer::Interpolate(float frameTime, float frameOffset)
|
||||
{
|
||||
PROFILE3("UnitRenderer::Interpolate");
|
||||
|
||||
m_FrameOffset = frameOffset;
|
||||
|
||||
// TODO: we shouldn't update all the animations etc for units that are off-screen
|
||||
// (but need to be careful about e.g. sounds triggered by animations of off-screen
|
||||
// units)
|
||||
for (size_t i = 0; i < m_Units.size(); i++)
|
||||
{
|
||||
SUnit& unit = m_Units[i];
|
||||
if (unit.actor)
|
||||
unit.actor->UpdateModel(frameTime);
|
||||
}
|
||||
|
||||
m_DebugSpheres.clear();
|
||||
if (m_EnableDebugOverlays)
|
||||
{
|
||||
for (size_t i = 0; i < m_Units.size(); i++)
|
||||
{
|
||||
SUnit& unit = m_Units[i];
|
||||
if (!(unit.actor && unit.inWorld))
|
||||
continue;
|
||||
|
||||
SOverlaySphere sphere;
|
||||
sphere.m_Center = unit.sweptBounds.GetCenter();
|
||||
sphere.m_Radius = unit.sweptBounds.GetRadius();
|
||||
if (unit.culled)
|
||||
sphere.m_Color = CColor(1.0f, 0.5f, 0.5f, 0.5f);
|
||||
else
|
||||
sphere.m_Color = CColor(0.5f, 0.5f, 1.0f, 0.5f);
|
||||
|
||||
m_DebugSpheres.push_back(sphere);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpUnitRenderer::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
|
||||
{
|
||||
// TODO: need a coarse culling pass based on some kind of spatial data
|
||||
// structure - that's the main point of this design. Once we've got a
|
||||
// rough list of possibly-visible units, then we can do the more precise
|
||||
// culling. (And once it's cheap enough, we can do multiple culling passes
|
||||
// per frame - one for shadow generation, one for water reflections, etc.)
|
||||
|
||||
PROFILE3("UnitRenderer::RenderSubmit");
|
||||
|
||||
for (size_t i = 0; i < m_Units.size(); ++i)
|
||||
{
|
||||
SUnit& unit = m_Units[i];
|
||||
|
||||
unit.culled = true;
|
||||
|
||||
if (!(unit.actor && unit.inWorld))
|
||||
continue;
|
||||
|
||||
if (!g_AtlasGameLoop->running && !g_RenderActors && (unit.flags & ACTOR_ONLY))
|
||||
continue;
|
||||
|
||||
if (!g_AtlasGameLoop->running && (unit.flags & VISIBLE_IN_ATLAS_ONLY))
|
||||
continue;
|
||||
|
||||
if (culling && !frustum.IsSphereVisible(unit.sweptBounds.GetCenter(), unit.sweptBounds.GetRadius()))
|
||||
continue;
|
||||
|
||||
if (unit.visibilityDirty)
|
||||
UpdateVisibility(unit);
|
||||
if (unit.visibility == ICmpRangeManager::VIS_HIDDEN)
|
||||
continue;
|
||||
|
||||
unit.culled = false;
|
||||
|
||||
CmpPtr<ICmpPosition> cmpPosition(unit.entity);
|
||||
if (!cmpPosition)
|
||||
continue;
|
||||
|
||||
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(m_FrameOffset));
|
||||
|
||||
CModelAbstract& unitModel = unit.actor->GetModel();
|
||||
unitModel.SetTransform(transform);
|
||||
|
||||
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), unitModel.GetWorldBoundsRec()))
|
||||
continue;
|
||||
|
||||
collector.SubmitRecursive(&unitModel);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_DebugSpheres.size(); ++i)
|
||||
collector.Submit(&m_DebugSpheres[i]);
|
||||
}
|
||||
|
||||
void CCmpUnitRenderer::UpdateVisibility(SUnit& unit)
|
||||
{
|
||||
if (unit.inWorld)
|
||||
{
|
||||
// The 'always visible' flag means we should always render the unit
|
||||
// (regardless of whether the LOS system thinks it's visible)
|
||||
CmpPtr<ICmpVision> cmpVision(unit.entity);
|
||||
if (cmpVision && cmpVision->GetAlwaysVisible())
|
||||
unit.visibility = ICmpRangeManager::VIS_VISIBLE;
|
||||
else
|
||||
{
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity());
|
||||
// Uncomment the following lines to prevent the models from popping into existence
|
||||
// near the LOS boundary. Is rather resource intensive.
|
||||
//if (cmpVision->GetRetainInFog())
|
||||
// unit.visibility = ICmpRangeManager::VIS_VISIBLE;
|
||||
//else
|
||||
unit.visibility = cmpRangeManager->GetLosVisibility(unit.entity,
|
||||
GetSimContext().GetCurrentDisplayedPlayer());
|
||||
}
|
||||
}
|
||||
else
|
||||
unit.visibility = ICmpRangeManager::VIS_HIDDEN;
|
||||
|
||||
// Change the visibility of the visual actor's selectable if it has one.
|
||||
CmpPtr<ICmpSelectable> cmpSelectable(unit.entity);
|
||||
if (cmpSelectable)
|
||||
cmpSelectable->SetVisibility(unit.visibility != ICmpRangeManager::VIS_HIDDEN);
|
||||
|
||||
unit.visibilityDirty = false;
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT_TYPE(UnitRenderer)
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2013 Wildfire Games.
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -23,10 +23,9 @@
|
||||
#include "simulation2/MessageTypes.h"
|
||||
|
||||
#include "ICmpFootprint.h"
|
||||
#include "ICmpUnitRenderer.h"
|
||||
#include "ICmpOwnership.h"
|
||||
#include "ICmpPosition.h"
|
||||
#include "ICmpRangeManager.h"
|
||||
#include "ICmpSelectable.h"
|
||||
#include "ICmpTemplateManager.h"
|
||||
#include "ICmpTerrain.h"
|
||||
#include "ICmpUnitMotion.h"
|
||||
@ -41,25 +40,24 @@
|
||||
#include "graphics/Unit.h"
|
||||
#include "graphics/UnitAnimation.h"
|
||||
#include "graphics/UnitManager.h"
|
||||
#include "maths/BoundingSphere.h"
|
||||
#include "maths/Matrix3D.h"
|
||||
#include "maths/Vector3D.h"
|
||||
#include "ps/CLogger.h"
|
||||
#include "ps/GameSetup/Config.h"
|
||||
#include "renderer/Scene.h"
|
||||
|
||||
#include "tools/atlas/GameInterface/GameLoop.h"
|
||||
|
||||
class CCmpVisualActor : public ICmpVisual
|
||||
{
|
||||
public:
|
||||
static void ClassInit(CComponentManager& componentManager)
|
||||
{
|
||||
componentManager.SubscribeToMessageType(MT_Update_Final);
|
||||
componentManager.SubscribeToMessageType(MT_Interpolate);
|
||||
componentManager.SubscribeToMessageType(MT_RenderSubmit);
|
||||
componentManager.SubscribeToMessageType(MT_InterpolatedPositionChanged);
|
||||
componentManager.SubscribeToMessageType(MT_OwnershipChanged);
|
||||
componentManager.SubscribeToMessageType(MT_ValueModification);
|
||||
componentManager.SubscribeGloballyToMessageType(MT_TerrainChanged);
|
||||
componentManager.SubscribeToMessageType(MT_TerrainChanged);
|
||||
componentManager.SubscribeToMessageType(MT_Destroy);
|
||||
}
|
||||
|
||||
DEFAULT_COMPONENT_ALLOCATOR(VisualActor)
|
||||
@ -73,8 +71,6 @@ private:
|
||||
|
||||
std::map<std::string, std::string> m_AnimOverride;
|
||||
|
||||
ICmpRangeManager::ELosVisibility m_Visibility; // only valid between Interpolate and RenderSubmit
|
||||
|
||||
// Current animation state
|
||||
fixed m_AnimRunThreshold; // if non-zero this is the special walk/run mode
|
||||
std::string m_AnimName;
|
||||
@ -87,15 +83,11 @@ private:
|
||||
u32 m_Seed; // seed used for random variations
|
||||
|
||||
bool m_ConstructionPreview;
|
||||
fixed m_ConstructionProgress;
|
||||
|
||||
bool m_VisibleInAtlasOnly;
|
||||
bool m_IsActorOnly; // an in-world entity should not have this or it might not be rendered.
|
||||
|
||||
/// 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;
|
||||
ICmpUnitRenderer::tag_t m_ModelTag;
|
||||
|
||||
public:
|
||||
static std::string GetSchema()
|
||||
@ -181,13 +173,10 @@ public:
|
||||
|
||||
virtual void Init(const CParamNode& paramNode)
|
||||
{
|
||||
m_PreviouslyRendered = false;
|
||||
m_Unit = NULL;
|
||||
m_Visibility = ICmpRangeManager::VIS_HIDDEN;
|
||||
m_R = m_G = m_B = fixed::FromInt(1);
|
||||
|
||||
m_ConstructionPreview = paramNode.GetChild("ConstructionPreview").IsOk();
|
||||
m_ConstructionProgress = fixed::Zero();
|
||||
|
||||
m_Seed = GetEntityId();
|
||||
|
||||
@ -234,8 +223,6 @@ public:
|
||||
// TODO: variation/selection strings
|
||||
serialize.String("actor", m_ActorName, 0, 256);
|
||||
|
||||
serialize.NumberFixed_Unbounded("constructionprogress", m_ConstructionProgress);
|
||||
|
||||
// TODO: store actor variables?
|
||||
}
|
||||
|
||||
@ -294,18 +281,6 @@ public:
|
||||
Update(msgData.turnLength);
|
||||
break;
|
||||
}
|
||||
case MT_Interpolate:
|
||||
{
|
||||
const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
|
||||
Interpolate(msgData.deltaSimTime, msgData.offset);
|
||||
break;
|
||||
}
|
||||
case MT_RenderSubmit:
|
||||
{
|
||||
const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
|
||||
RenderSubmit(msgData.collector, msgData.frustum, msgData.culling);
|
||||
break;
|
||||
}
|
||||
case MT_OwnershipChanged:
|
||||
{
|
||||
const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg);
|
||||
@ -336,6 +311,26 @@ public:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_InterpolatedPositionChanged:
|
||||
{
|
||||
const CMessageInterpolatedPositionChanged& msgData = static_cast<const CMessageInterpolatedPositionChanged&> (msg);
|
||||
if (m_ModelTag.valid())
|
||||
{
|
||||
CmpPtr<ICmpUnitRenderer> cmpModelRenderer(GetSystemEntity());
|
||||
cmpModelRenderer->UpdateUnitPos(m_ModelTag, msgData.inWorld, msgData.pos0, msgData.pos1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MT_Destroy:
|
||||
{
|
||||
if (m_ModelTag.valid())
|
||||
{
|
||||
CmpPtr<ICmpUnitRenderer> cmpModelRenderer(GetSystemEntity());
|
||||
cmpModelRenderer->RemoveUnit(m_ModelTag);
|
||||
m_ModelTag = ICmpUnitRenderer::tag_t();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,7 +382,15 @@ public:
|
||||
if (m_Unit->GetModel().ToCModel())
|
||||
{
|
||||
// Ensure the prop transforms are correct
|
||||
m_Unit->GetModel().ValidatePosition();
|
||||
CmpPtr<ICmpUnitRenderer> cmpUnitRenderer(GetSystemEntity());
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
||||
if (cmpUnitRenderer && cmpPosition)
|
||||
{
|
||||
float frameOffset = cmpUnitRenderer->GetFrameOffset();
|
||||
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset));
|
||||
m_Unit->GetModel().SetTransform(transform);
|
||||
m_Unit->GetModel().ValidatePosition();
|
||||
}
|
||||
|
||||
CModelAbstract* ammo = m_Unit->GetModel().ToCModel()->FindFirstAmmoProp();
|
||||
if (ammo)
|
||||
@ -473,6 +476,12 @@ public:
|
||||
m_G = g;
|
||||
m_B = b;
|
||||
UNUSED2(a); // TODO: why is this even an argument?
|
||||
|
||||
if (m_Unit)
|
||||
{
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
model.SetShadingColor(CColor(m_R.ToFloat(), m_G.ToFloat(), m_B.ToFloat(), 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetVariable(std::string name, float value)
|
||||
@ -502,11 +511,6 @@ public:
|
||||
return m_ConstructionPreview;
|
||||
}
|
||||
|
||||
virtual void SetConstructionProgress(fixed progress)
|
||||
{
|
||||
m_ConstructionProgress = progress;
|
||||
}
|
||||
|
||||
virtual void Hotload(const VfsPath& name)
|
||||
{
|
||||
if (!m_Unit)
|
||||
@ -529,8 +533,6 @@ private:
|
||||
|
||||
void Update(fixed turnLength);
|
||||
void UpdateVisibility();
|
||||
void Interpolate(float frameTime, float frameOffset);
|
||||
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(VisualActor)
|
||||
@ -539,49 +541,74 @@ REGISTER_COMPONENT_TYPE(VisualActor)
|
||||
|
||||
void CCmpVisualActor::InitModel(const CParamNode& paramNode)
|
||||
{
|
||||
if (GetSimContext().HasUnitManager())
|
||||
if (!GetSimContext().HasUnitManager())
|
||||
return;
|
||||
|
||||
std::set<CStr> selections;
|
||||
std::wstring actorName = m_ActorName;
|
||||
if (actorName.find(L".xml") == std::wstring::npos)
|
||||
actorName += L".xml";
|
||||
m_Unit = GetSimContext().GetUnitManager().CreateUnit(actorName, GetActorSeed(), selections);
|
||||
if (!m_Unit)
|
||||
return;
|
||||
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
if (model.ToCModel())
|
||||
{
|
||||
std::set<CStr> selections;
|
||||
std::wstring actorName = m_ActorName;
|
||||
if (actorName.find(L".xml") == std::wstring::npos)
|
||||
actorName += L".xml";
|
||||
m_Unit = GetSimContext().GetUnitManager().CreateUnit(actorName, GetActorSeed(), selections);
|
||||
if (m_Unit)
|
||||
u32 modelFlags = 0;
|
||||
|
||||
if (paramNode.GetChild("SilhouetteDisplay").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_DISPLAY;
|
||||
|
||||
if (paramNode.GetChild("SilhouetteOccluder").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
|
||||
|
||||
CmpPtr<ICmpVision> cmpVision(GetEntityHandle());
|
||||
if (cmpVision && cmpVision->GetAlwaysVisible())
|
||||
modelFlags |= MODELFLAG_IGNORE_LOS;
|
||||
|
||||
model.ToCModel()->AddFlagsRec(modelFlags);
|
||||
}
|
||||
|
||||
if (paramNode.GetChild("DisableShadows").IsOk())
|
||||
{
|
||||
if (model.ToCModel())
|
||||
model.ToCModel()->RemoveShadowsRec();
|
||||
else if (model.ToCModelDecal())
|
||||
model.ToCModelDecal()->RemoveShadows();
|
||||
}
|
||||
|
||||
// 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());
|
||||
|
||||
bool floating = m_Unit->GetObject().m_Base->m_Properties.m_FloatOnWater;
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
||||
if (cmpPosition)
|
||||
cmpPosition->SetActorFloating(floating);
|
||||
|
||||
if (!m_ModelTag.valid())
|
||||
{
|
||||
CmpPtr<ICmpUnitRenderer> cmpModelRenderer(GetSystemEntity());
|
||||
if (cmpModelRenderer)
|
||||
{
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
if (model.ToCModel())
|
||||
{
|
||||
u32 modelFlags = 0;
|
||||
// TODO: this should account for all possible props, animations, etc,
|
||||
// else we might accidentally cull the unit when it should be visible
|
||||
CBoundingBoxAligned bounds = m_Unit->GetModel().GetWorldBoundsRec();
|
||||
CBoundingSphere boundSphere = CBoundingSphere::FromSweptBox(bounds);
|
||||
|
||||
if (paramNode.GetChild("SilhouetteDisplay").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_DISPLAY;
|
||||
int flags = 0;
|
||||
if (m_IsActorOnly)
|
||||
flags |= ICmpUnitRenderer::ACTOR_ONLY;
|
||||
if (m_VisibleInAtlasOnly)
|
||||
flags |= ICmpUnitRenderer::VISIBLE_IN_ATLAS_ONLY;
|
||||
|
||||
if (paramNode.GetChild("SilhouetteOccluder").ToBool())
|
||||
modelFlags |= MODELFLAG_SILHOUETTE_OCCLUDER;
|
||||
|
||||
CmpPtr<ICmpVision> cmpVision(GetEntityHandle());
|
||||
if (cmpVision && cmpVision->GetAlwaysVisible())
|
||||
modelFlags |= MODELFLAG_IGNORE_LOS;
|
||||
|
||||
model.ToCModel()->AddFlagsRec(modelFlags);
|
||||
}
|
||||
|
||||
if (paramNode.GetChild("DisableShadows").IsOk())
|
||||
{
|
||||
if (model.ToCModel())
|
||||
model.ToCModel()->RemoveShadowsRec();
|
||||
else if (model.ToCModelDecal())
|
||||
model.ToCModelDecal()->RemoveShadows();
|
||||
}
|
||||
|
||||
// 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());
|
||||
m_ModelTag = cmpModelRenderer->AddUnit(GetEntityHandle(), m_Unit, boundSphere, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -662,15 +689,6 @@ void CCmpVisualActor::ReloadActor()
|
||||
if (!m_Unit)
|
||||
return;
|
||||
|
||||
std::set<CStr> selections;
|
||||
std::wstring actorName = m_ActorName;
|
||||
if (actorName.find(L".xml") == std::wstring::npos)
|
||||
actorName += L".xml";
|
||||
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(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();
|
||||
@ -698,6 +716,14 @@ void CCmpVisualActor::ReloadActor()
|
||||
m_Unit->GetModel().SetShadingColor(shading);
|
||||
|
||||
m_Unit->GetModel().SetPlayerID(playerID);
|
||||
|
||||
if (m_ModelTag.valid())
|
||||
{
|
||||
CmpPtr<ICmpUnitRenderer> cmpModelRenderer(GetSystemEntity());
|
||||
CBoundingBoxAligned bounds = m_Unit->GetModel().GetWorldBoundsRec();
|
||||
CBoundingSphere boundSphere = CBoundingSphere::FromSweptBox(bounds);
|
||||
cmpModelRenderer->UpdateUnit(m_ModelTag, m_Unit, boundSphere);
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpVisualActor::Update(fixed UNUSED(turnLength))
|
||||
@ -705,8 +731,6 @@ void CCmpVisualActor::Update(fixed UNUSED(turnLength))
|
||||
if (m_Unit == NULL)
|
||||
return;
|
||||
|
||||
UpdateVisibility();
|
||||
|
||||
// If we're in the special movement mode, select an appropriate animation
|
||||
if (!m_AnimRunThreshold.IsZero())
|
||||
{
|
||||
@ -745,119 +769,3 @@ void CCmpVisualActor::Update(fixed UNUSED(turnLength))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpVisualActor::UpdateVisibility()
|
||||
{
|
||||
ICmpRangeManager::ELosVisibility oldVisibility = m_Visibility;
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
||||
if (cmpPosition && cmpPosition->IsInWorld())
|
||||
{
|
||||
// The 'always visible' flag means we should always render the unit
|
||||
// (regardless of whether the LOS system thinks it's visible)
|
||||
CmpPtr<ICmpVision> cmpVision(GetEntityHandle());
|
||||
if (cmpVision && cmpVision->GetAlwaysVisible())
|
||||
m_Visibility = ICmpRangeManager::VIS_VISIBLE;
|
||||
else
|
||||
{
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity());
|
||||
// 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
|
||||
m_Visibility = cmpRangeManager->GetLosVisibility(GetEntityHandle(),
|
||||
GetSimContext().GetCurrentDisplayedPlayer());
|
||||
}
|
||||
}
|
||||
else
|
||||
m_Visibility = ICmpRangeManager::VIS_HIDDEN;
|
||||
|
||||
if (m_Visibility != oldVisibility)
|
||||
{
|
||||
// Change the visibility of the visual actor's selectable if it has one.
|
||||
CmpPtr<ICmpSelectable> cmpSelectable(GetEntityHandle());
|
||||
if (cmpSelectable)
|
||||
cmpSelectable->SetVisibility(m_Visibility == ICmpRangeManager::VIS_HIDDEN ? false : true);
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
|
||||
{
|
||||
if (m_Unit == NULL)
|
||||
return;
|
||||
|
||||
// Disable rendering of the unit if it has no position
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
|
||||
if (!cmpPosition || !cmpPosition->IsInWorld())
|
||||
return;
|
||||
else if (!m_PreviouslyRendered)
|
||||
{
|
||||
UpdateVisibility();
|
||||
m_PreviouslyRendered = true;
|
||||
}
|
||||
|
||||
// Even if HIDDEN due to LOS, we need to set up the transforms
|
||||
// so that projectiles will be launched from the right place
|
||||
|
||||
bool floating = m_Unit->GetObject().m_Base->m_Properties.m_FloatOnWater;
|
||||
|
||||
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset, floating));
|
||||
|
||||
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
|
||||
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
|
||||
model.SetTransform(transform);
|
||||
m_Unit->UpdateModel(frameTime);
|
||||
|
||||
// If not hidden, then we need to set up some extra state for rendering
|
||||
if (m_Visibility != ICmpRangeManager::VIS_HIDDEN)
|
||||
{
|
||||
model.ValidatePosition();
|
||||
model.SetShadingColor(CColor(m_R.ToFloat(), m_G.ToFloat(), m_B.ToFloat(), 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
|
||||
{
|
||||
if (m_Unit == NULL)
|
||||
return;
|
||||
|
||||
if (m_Visibility == ICmpRangeManager::VIS_HIDDEN)
|
||||
return;
|
||||
|
||||
CModelAbstract& model = m_Unit->GetModel();
|
||||
|
||||
if (!g_AtlasGameLoop->running && !g_RenderActors && m_IsActorOnly)
|
||||
return;
|
||||
|
||||
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetWorldBoundsRec()))
|
||||
return;
|
||||
|
||||
if (!g_AtlasGameLoop->running && m_VisibleInAtlasOnly)
|
||||
return;
|
||||
|
||||
collector.SubmitRecursive(&model);
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ DEFINE_INTERFACE_METHOD_0("IsHeightRelative", bool, ICmpPosition, IsHeightRelati
|
||||
DEFINE_INTERFACE_METHOD_1("SetHeightRelative", void, ICmpPosition, SetHeightRelative, bool)
|
||||
DEFINE_INTERFACE_METHOD_0("IsFloating", bool, ICmpPosition, IsFloating)
|
||||
DEFINE_INTERFACE_METHOD_1("SetFloating", void, ICmpPosition, SetFloating, bool)
|
||||
DEFINE_INTERFACE_METHOD_1("SetConstructionProgress", void, ICmpPosition, SetConstructionProgress, fixed)
|
||||
DEFINE_INTERFACE_METHOD_0("GetPosition", CFixedVector3D, ICmpPosition, GetPosition)
|
||||
DEFINE_INTERFACE_METHOD_0("GetPosition2D", CFixedVector2D, ICmpPosition, GetPosition2D)
|
||||
DEFINE_INTERFACE_METHOD_0("GetPreviousPosition", CFixedVector3D, ICmpPosition, GetPreviousPosition)
|
||||
|
@ -144,6 +144,18 @@ public:
|
||||
*/
|
||||
virtual void SetFloating(bool flag) = 0;
|
||||
|
||||
/**
|
||||
* Set the entity to float on water, in a non-network-synchronised visual-only way.
|
||||
* (This is to support the 'floating' flag in actor XMLs.)
|
||||
*/
|
||||
virtual void SetActorFloating(bool flag) = 0;
|
||||
|
||||
/**
|
||||
* Set construction progress of the model, this affects the rendered position of the model.
|
||||
* 0.0 will be fully underground, 1.0 will be fully visible, 0.5 will be half underground.
|
||||
*/
|
||||
virtual void SetConstructionProgress(fixed progress) = 0;
|
||||
|
||||
/**
|
||||
* Returns the current x,y,z position (no interpolation).
|
||||
* Depends on the current terrain heightmap.
|
||||
@ -216,7 +228,7 @@ public:
|
||||
* Get the current interpolated transform matrix, for rendering.
|
||||
* Must not be called unless IsInWorld is true.
|
||||
*/
|
||||
virtual CMatrix3D GetInterpolatedTransform(float frameOffset, bool forceFloating) = 0;
|
||||
virtual CMatrix3D GetInterpolatedTransform(float frameOffset) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(Position)
|
||||
};
|
||||
|
26
source/simulation2/components/ICmpUnitRenderer.cpp
Normal file
26
source/simulation2/components/ICmpUnitRenderer.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* 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 "ICmpUnitRenderer.h"
|
||||
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(UnitRenderer)
|
||||
DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpUnitRenderer, SetDebugOverlay, bool)
|
||||
END_INTERFACE_WRAPPER(UnitRenderer)
|
70
source/simulation2/components/ICmpUnitRenderer.h
Normal file
70
source/simulation2/components/ICmpUnitRenderer.h
Normal file
@ -0,0 +1,70 @@
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_ICMPMODELRENDERER
|
||||
#define INCLUDED_ICMPMODELRENDERER
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
class CUnit;
|
||||
class CBoundingSphere;
|
||||
class CVector3D;
|
||||
|
||||
class ICmpUnitRenderer : public IComponent
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* External identifiers for models.
|
||||
* (This is a struct rather than a raw u32 for type-safety.)
|
||||
*/
|
||||
struct tag_t
|
||||
{
|
||||
tag_t() : n(0) {}
|
||||
explicit tag_t(u32 n) : n(n) {}
|
||||
bool valid() { return n != 0; }
|
||||
|
||||
u32 n;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ACTOR_ONLY = 1 << 0,
|
||||
VISIBLE_IN_ATLAS_ONLY = 1 << 1,
|
||||
};
|
||||
|
||||
virtual tag_t AddUnit(CEntityHandle entity, CUnit* unit, const CBoundingSphere& boundsApprox, int flags) = 0;
|
||||
|
||||
virtual void RemoveUnit(tag_t tag) = 0;
|
||||
|
||||
virtual void UpdateUnit(tag_t tag, CUnit* unit, const CBoundingSphere& boundsApprox) = 0;
|
||||
|
||||
virtual void UpdateUnitPos(tag_t tag, bool inWorld, const CVector3D& pos0, const CVector3D& pos1) = 0;
|
||||
|
||||
/**
|
||||
* Returns the frame offset from the last Interpolate message.
|
||||
*/
|
||||
virtual float GetFrameOffset() = 0;
|
||||
|
||||
/**
|
||||
* Toggle the rendering of debug info.
|
||||
*/
|
||||
virtual void SetDebugOverlay(bool enabled) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(UnitRenderer)
|
||||
};
|
||||
|
||||
#endif // INCLUDED_ICMPMODELRENDERER
|
@ -33,5 +33,4 @@ DEFINE_INTERFACE_METHOD_2("SetVariable", void, ICmpVisual, SetVariable, std::str
|
||||
DEFINE_INTERFACE_METHOD_0("GetActorSeed", u32, ICmpVisual, GetActorSeed)
|
||||
DEFINE_INTERFACE_METHOD_1("SetActorSeed", void, ICmpVisual, SetActorSeed, u32)
|
||||
DEFINE_INTERFACE_METHOD_0("HasConstructionPreview", bool, ICmpVisual, HasConstructionPreview)
|
||||
DEFINE_INTERFACE_METHOD_1("SetConstructionProgress", void, ICmpVisual, SetConstructionProgress, fixed)
|
||||
END_INTERFACE_WRAPPER(Visual)
|
||||
|
@ -161,12 +161,6 @@ public:
|
||||
*/
|
||||
virtual bool HasConstructionPreview() = 0;
|
||||
|
||||
/**
|
||||
* Set construction progress of the model, this affects the rendered position of the model.
|
||||
* 0.0 will be fully underground, 1.0 will be fully visible, 0.5 will be half underground.
|
||||
*/
|
||||
virtual void SetConstructionProgress(fixed progress) = 0;
|
||||
|
||||
/**
|
||||
* Called when an actor file has been modified and reloaded dynamically.
|
||||
* If this component uses the named actor file, it should regenerate its actor
|
||||
|
@ -72,9 +72,9 @@ public:
|
||||
// Position computed from move plus terrain
|
||||
TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(100, 60, 200));
|
||||
// Interpolated position should be constant
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f, false).GetTranslation(), CVector3D(100, 60, 200));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f, false).GetTranslation(), CVector3D(100, 60, 200));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f, false).GetTranslation(), CVector3D(100, 60, 200));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(100, 60, 200));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(100, 60, 200));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(100, 60, 200));
|
||||
|
||||
// No TurnStart message, so this move doesn't affect the interpolation
|
||||
cmp->MoveTo(entity_pos_t::FromInt(0), entity_pos_t::FromInt(0));
|
||||
@ -83,9 +83,9 @@ public:
|
||||
// Position computed from move plus terrain
|
||||
TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(200, 60, 0));
|
||||
// Interpolated position should vary, from original move into world to new move
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f, false).GetTranslation(), CVector3D(100, 60, 200));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f, false).GetTranslation(), CVector3D(150, 60, 100));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f, false).GetTranslation(), CVector3D(200, 60, 0));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(100, 60, 200));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(150, 60, 100));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(200, 60, 0));
|
||||
|
||||
// Latch new position for interpolation
|
||||
CMessageTurnStart msg;
|
||||
@ -93,18 +93,18 @@ public:
|
||||
|
||||
// Move smoothly to new position
|
||||
cmp->MoveTo(entity_pos_t::FromInt(400), entity_pos_t::FromInt(300));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f, false).GetTranslation(), CVector3D(200, 60, 0));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f, false).GetTranslation(), CVector3D(300, 60, 150));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f, false).GetTranslation(), CVector3D(400, 60, 300));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(200, 60, 0));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(300, 60, 150));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(400, 60, 300));
|
||||
|
||||
// Jump to new position
|
||||
cmp->JumpTo(entity_pos_t::FromInt(300), entity_pos_t::FromInt(100));
|
||||
// Position computed from jump plus terrain
|
||||
TS_ASSERT_EQUALS(cmp->GetPosition(), fixedvec(300, 60, 100));
|
||||
// Interpolated position should be constant after jump
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f, false).GetTranslation(), CVector3D(300, 60, 100));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f, false).GetTranslation(), CVector3D(300, 60, 100));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f, false).GetTranslation(), CVector3D(300, 60, 100));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.0f).GetTranslation(), CVector3D(300, 60, 100));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(0.5f).GetTranslation(), CVector3D(300, 60, 100));
|
||||
TS_ASSERT_EQUALS(cmp->GetInterpolatedTransform(1.0f).GetTranslation(), CVector3D(300, 60, 100));
|
||||
|
||||
// TODO: Test the rotation methods
|
||||
}
|
||||
|
@ -57,6 +57,8 @@ public:
|
||||
virtual void SetHeightRelative(bool UNUSED(relative)) { }
|
||||
virtual bool IsFloating() { return false; }
|
||||
virtual void SetFloating(bool UNUSED(flag)) { }
|
||||
virtual void SetActorFloating(bool UNUSED(flag)) { }
|
||||
virtual void SetConstructionProgress(fixed UNUSED(progress)) { }
|
||||
virtual CFixedVector3D GetPosition() { return CFixedVector3D(); }
|
||||
virtual CFixedVector2D GetPosition2D() { return CFixedVector2D(); }
|
||||
virtual CFixedVector3D GetPreviousPosition() { return CFixedVector3D(); }
|
||||
@ -67,7 +69,7 @@ public:
|
||||
virtual CFixedVector3D GetRotation() { return CFixedVector3D(); }
|
||||
virtual fixed GetDistanceTravelled() { return fixed::Zero(); }
|
||||
virtual void GetInterpolatedPosition2D(float UNUSED(frameOffset), float& x, float& z, float& rotY) { x = z = rotY = 0; }
|
||||
virtual CMatrix3D GetInterpolatedTransform(float UNUSED(frameOffset), bool UNUSED(forceFloating)) { return CMatrix3D(); }
|
||||
virtual CMatrix3D GetInterpolatedTransform(float UNUSED(frameOffset)) { return CMatrix3D(); }
|
||||
};
|
||||
|
||||
class TestCmpRangeManager : public CxxTest::TestSuite
|
||||
|
@ -238,6 +238,20 @@ CMessage* CMessagePositionChanged::FromJSVal(ScriptInterface& scriptInterface, j
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
jsval CMessageInterpolatedPositionChanged::ToJSVal(ScriptInterface& UNUSED(scriptInterface)) const
|
||||
{
|
||||
LOGWARNING(L"CMessageInterpolatedPositionChanged::ToJSVal not implemented");
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
||||
CMessage* CMessageInterpolatedPositionChanged::FromJSVal(ScriptInterface& UNUSED(scriptInterface), jsval UNUSED(val))
|
||||
{
|
||||
LOGWARNING(L"CMessageInterpolatedPositionChanged::FromJSVal not implemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
jsval CMessageTerritoryPositionChanged::ToJSVal(ScriptInterface& scriptInterface) const
|
||||
{
|
||||
TOJSVAL_SETUP();
|
||||
|
@ -646,6 +646,7 @@ void CComponentManager::AddSystemComponents(bool skipScriptedComponents, bool sk
|
||||
AddComponent(m_SystemEntity, CID_SoundManager, noParam);
|
||||
AddComponent(m_SystemEntity, CID_Terrain, noParam);
|
||||
AddComponent(m_SystemEntity, CID_TerritoryManager, noParam);
|
||||
AddComponent(m_SystemEntity, CID_UnitRenderer, noParam);
|
||||
AddComponent(m_SystemEntity, CID_WaterManager, noParam);
|
||||
|
||||
// Add scripted system components:
|
||||
|
@ -605,7 +605,7 @@ BEGIN_COMMAND(RotateObject)
|
||||
m_AngleOld = cmpPosition->GetRotation().Y.ToFloat();
|
||||
if (msg->usetarget)
|
||||
{
|
||||
CMatrix3D transform = cmpPosition->GetInterpolatedTransform(0.f, false);
|
||||
CMatrix3D transform = cmpPosition->GetInterpolatedTransform(0.f);
|
||||
CVector3D pos = transform.GetTranslation();
|
||||
CVector3D target = msg->target->GetWorldSpace(pos.Y);
|
||||
m_AngleNew = atan2(target.X-pos.X, target.Z-pos.Z);
|
||||
|
Loading…
Reference in New Issue
Block a user