Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpObstructionManager.cpp
Show All 11 Lines | |||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * 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/>. | * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include "precompiled.h" | #include "precompiled.h" | ||||
#include "CCmpObstructionManager.h" | |||||
#include "simulation2/system/Component.h" | #include "simulation2/system/Component.h" | ||||
#include "ICmpObstructionManager.h" | |||||
#include "ICmpPosition.h" | #include "ICmpPosition.h" | ||||
#include "ICmpRangeManager.h" | #include "ICmpRangeManager.h" | ||||
#include "simulation2/MessageTypes.h" | #include "simulation2/MessageTypes.h" | ||||
#include "simulation2/helpers/Geometry.h" | #include "simulation2/helpers/Geometry.h" | ||||
#include "simulation2/helpers/Grid.h" | |||||
#include "simulation2/helpers/Rasterize.h" | #include "simulation2/helpers/Rasterize.h" | ||||
#include "simulation2/helpers/Render.h" | #include "simulation2/helpers/Render.h" | ||||
#include "simulation2/helpers/Spatial.h" | |||||
#include "simulation2/serialization/SerializedTypes.h" | #include "simulation2/serialization/SerializedTypes.h" | ||||
#include "graphics/Overlay.h" | |||||
#include "maths/MathUtil.h" | #include "maths/MathUtil.h" | ||||
#include "ps/Profile.h" | #include "ps/Profile.h" | ||||
#include "renderer/Scene.h" | |||||
#include "ps/CLogger.h" | #include "ps/CLogger.h" | ||||
// Externally, tags are opaque non-zero positive integers. | // Externally, tags are opaque non-zero positive integers. | ||||
// Internally, they are tagged (by shape) indexes into shape lists. | // Internally, they are tagged (by shape) indexes into shape lists. | ||||
// idx must be non-zero. | // idx must be non-zero. | ||||
#define TAG_IS_VALID(tag) ((tag).valid()) | #define TAG_IS_VALID(tag) ((tag).valid()) | ||||
#define TAG_IS_UNIT(tag) (((tag).n & 1) == 0) | #define TAG_IS_UNIT(tag) (((tag).n & 1) == 0) | ||||
#define TAG_IS_STATIC(tag) (((tag).n & 1) == 1) | #define TAG_IS_STATIC(tag) (((tag).n & 1) == 1) | ||||
#define UNIT_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 0) | #define UNIT_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 0) | ||||
#define STATIC_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 1) | #define STATIC_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 1) | ||||
#define TAG_TO_INDEX(tag) ((tag).n >> 1) | #define TAG_TO_INDEX(tag) ((tag).n >> 1) | ||||
namespace | |||||
{ | |||||
/** | /** | ||||
* Size of each obstruction subdivision square. | * Size of each obstruction subdivision square. | ||||
* TODO: find the optimal number instead of blindly guessing. | * TODO: find the optimal number instead of blindly guessing. | ||||
*/ | */ | ||||
constexpr entity_pos_t OBSTRUCTION_SUBDIVISION_SIZE = entity_pos_t::FromInt(32); | constexpr entity_pos_t OBSTRUCTION_SUBDIVISION_SIZE = entity_pos_t::FromInt(32); | ||||
/** | /** | ||||
* Internal representation of axis-aligned circular shapes for moving units | |||||
*/ | |||||
struct UnitShape | |||||
{ | |||||
entity_id_t entity; | |||||
entity_pos_t x, z; | |||||
entity_pos_t clearance; | |||||
ICmpObstructionManager::flags_t flags; | |||||
entity_id_t group; // control group (typically the owner entity, or a formation controller entity) (units ignore collisions with others in the same group) | |||||
}; | |||||
/** | |||||
* Internal representation of arbitrary-rotation static square shapes for buildings | |||||
*/ | |||||
struct StaticShape | |||||
{ | |||||
entity_id_t entity; | |||||
entity_pos_t x, z; // world-space coordinates | |||||
CFixedVector2D u, v; // orthogonal unit vectors - axes of local coordinate space | |||||
entity_pos_t hw, hh; // half width/height in local coordinate space | |||||
ICmpObstructionManager::flags_t flags; | |||||
entity_id_t group; | |||||
entity_id_t group2; | |||||
}; | |||||
} // anonymous namespace | |||||
/** | |||||
* Serialization helper template for UnitShape | * Serialization helper template for UnitShape | ||||
*/ | */ | ||||
template<> | template<> | ||||
struct SerializeHelper<UnitShape> | struct SerializeHelper<CCmpObstructionManager::UnitShape> | ||||
{ | { | ||||
template<typename S> | template<typename S> | ||||
void operator()(S& serialize, const char* UNUSED(name), Serialize::qualify<S, UnitShape> value) const | void operator()(S& serialize, const char* UNUSED(name), | ||||
Serialize::qualify<S, CCmpObstructionManager::UnitShape> value) const | |||||
{ | { | ||||
serialize.NumberU32_Unbounded("entity", value.entity); | serialize.NumberU32_Unbounded("entity", value.entity); | ||||
serialize.NumberFixed_Unbounded("x", value.x); | serialize.NumberFixed_Unbounded("x", value.x); | ||||
serialize.NumberFixed_Unbounded("z", value.z); | serialize.NumberFixed_Unbounded("z", value.z); | ||||
serialize.NumberFixed_Unbounded("clearance", value.clearance); | serialize.NumberFixed_Unbounded("clearance", value.clearance); | ||||
serialize.NumberU8_Unbounded("flags", value.flags); | serialize.NumberU8_Unbounded("flags", value.flags); | ||||
serialize.NumberU32_Unbounded("group", value.group); | serialize.NumberU32_Unbounded("group", value.group); | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Serialization helper template for StaticShape | * Serialization helper template for StaticShape | ||||
*/ | */ | ||||
template<> | template<> | ||||
struct SerializeHelper<StaticShape> | struct SerializeHelper<CCmpObstructionManager::StaticShape> | ||||
{ | { | ||||
template<typename S> | template<typename S> | ||||
void operator()(S& serialize, const char* UNUSED(name), Serialize::qualify<S, StaticShape> value) const | void operator()(S& serialize, const char* UNUSED(name), | ||||
Serialize::qualify<S, CCmpObstructionManager::StaticShape> value) const | |||||
{ | { | ||||
serialize.NumberU32_Unbounded("entity", value.entity); | serialize.NumberU32_Unbounded("entity", value.entity); | ||||
serialize.NumberFixed_Unbounded("x", value.x); | serialize.NumberFixed_Unbounded("x", value.x); | ||||
serialize.NumberFixed_Unbounded("z", value.z); | serialize.NumberFixed_Unbounded("z", value.z); | ||||
serialize.NumberFixed_Unbounded("u.x", value.u.X); | serialize.NumberFixed_Unbounded("u.x", value.u.X); | ||||
serialize.NumberFixed_Unbounded("u.y", value.u.Y); | serialize.NumberFixed_Unbounded("u.y", value.u.Y); | ||||
serialize.NumberFixed_Unbounded("v.x", value.v.X); | serialize.NumberFixed_Unbounded("v.x", value.v.X); | ||||
serialize.NumberFixed_Unbounded("v.y", value.v.Y); | serialize.NumberFixed_Unbounded("v.y", value.v.Y); | ||||
serialize.NumberFixed_Unbounded("hw", value.hw); | serialize.NumberFixed_Unbounded("hw", value.hw); | ||||
serialize.NumberFixed_Unbounded("hh", value.hh); | serialize.NumberFixed_Unbounded("hh", value.hh); | ||||
serialize.NumberU8_Unbounded("flags", value.flags); | serialize.NumberU8_Unbounded("flags", value.flags); | ||||
serialize.NumberU32_Unbounded("group", value.group); | serialize.NumberU32_Unbounded("group", value.group); | ||||
serialize.NumberU32_Unbounded("group2", value.group2); | serialize.NumberU32_Unbounded("group2", value.group2); | ||||
} | } | ||||
}; | }; | ||||
class CCmpObstructionManager final : public ICmpObstructionManager | void CCmpObstructionManager::ClassInit(CComponentManager& componentManager) | ||||
{ | |||||
public: | |||||
static void ClassInit(CComponentManager& componentManager) | |||||
{ | { | ||||
componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays | componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays | ||||
} | } | ||||
DEFAULT_COMPONENT_ALLOCATOR(ObstructionManager) | IComponent* CCmpObstructionManager::Allocate(const ScriptInterface&, JS::HandleValue) | ||||
{ | |||||
bool m_DebugOverlayEnabled; | return nullptr; | ||||
bool m_DebugOverlayDirty; | } | ||||
std::vector<SOverlayLine> m_DebugOverlayLines; | |||||
SpatialSubdivision m_UnitSubdivision; | |||||
SpatialSubdivision m_StaticSubdivision; | |||||
// TODO: using std::map is a bit inefficient; is there a better way to store these? | |||||
std::map<u32, UnitShape> m_UnitShapes; | |||||
std::map<u32, StaticShape> m_StaticShapes; | |||||
u32 m_UnitShapeNext; // next allocated id | |||||
u32 m_StaticShapeNext; | |||||
entity_pos_t m_MaxClearance; | |||||
bool m_PassabilityCircular; | void CCmpObstructionManager::Deallocate(IComponent*) | ||||
{} | |||||
entity_pos_t m_WorldX0; | int CCmpObstructionManager::GetComponentTypeId() const | ||||
entity_pos_t m_WorldZ0; | { | ||||
entity_pos_t m_WorldX1; | return CID_ObstructionManager; | ||||
entity_pos_t m_WorldZ1; | } | ||||
static std::string GetSchema() | std::string CCmpObstructionManager::GetSchema() | ||||
{ | { | ||||
return "<a:component type='system'/><empty/>"; | return "<a:component type='system'/><empty/>"; | ||||
} | } | ||||
void Init(const CParamNode& UNUSED(paramNode)) override | void CCmpObstructionManager::Init(const CParamNode& UNUSED(paramNode)) | ||||
{ | { | ||||
m_DebugOverlayEnabled = false; | m_DebugOverlayEnabled = false; | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
m_UnitShapeNext = 1; | m_UnitShapeNext = 1; | ||||
m_StaticShapeNext = 1; | m_StaticShapeNext = 1; | ||||
m_UpdateInformations.dirty = true; | m_UpdateInformations.dirty = true; | ||||
m_UpdateInformations.globallyDirty = true; | m_UpdateInformations.globallyDirty = true; | ||||
m_PassabilityCircular = false; | m_PassabilityCircular = false; | ||||
m_WorldX0 = m_WorldZ0 = m_WorldX1 = m_WorldZ1 = entity_pos_t::Zero(); | m_WorldX0 = m_WorldZ0 = m_WorldX1 = m_WorldZ1 = entity_pos_t::Zero(); | ||||
// Initialise with bogus values (these will get replaced when | // Initialise with bogus values (these will get replaced when | ||||
// SetBounds is called) | // SetBounds is called) | ||||
ResetSubdivisions(entity_pos_t::FromInt(1024), entity_pos_t::FromInt(1024)); | ResetSubdivisions(entity_pos_t::FromInt(1024), entity_pos_t::FromInt(1024)); | ||||
} | } | ||||
void Deinit() override | void CCmpObstructionManager::Deinit() | ||||
{ | {} | ||||
} | |||||
template<typename S> | void CCmpObstructionManager::Serialize(ISerializer& serialize) | ||||
void SerializeCommon(S& serialize) | |||||
{ | |||||
Serializer(serialize, "unit subdiv", m_UnitSubdivision); | |||||
Serializer(serialize, "static subdiv", m_StaticSubdivision); | |||||
serialize.NumberFixed_Unbounded("max clearance", m_MaxClearance); | |||||
Serializer(serialize, "unit shapes", m_UnitShapes); | |||||
Serializer(serialize, "static shapes", m_StaticShapes); | |||||
serialize.NumberU32_Unbounded("unit shape next", m_UnitShapeNext); | |||||
serialize.NumberU32_Unbounded("static shape next", m_StaticShapeNext); | |||||
serialize.Bool("circular", m_PassabilityCircular); | |||||
serialize.NumberFixed_Unbounded("world x0", m_WorldX0); | |||||
serialize.NumberFixed_Unbounded("world z0", m_WorldZ0); | |||||
serialize.NumberFixed_Unbounded("world x1", m_WorldX1); | |||||
serialize.NumberFixed_Unbounded("world z1", m_WorldZ1); | |||||
} | |||||
void Serialize(ISerializer& serialize) override | |||||
{ | { | ||||
// TODO: this could perhaps be optimised by not storing all the obstructions, | // TODO: this could perhaps be optimised by not storing all the obstructions, | ||||
// and instead regenerating them from the other entities on Deserialize | // and instead regenerating them from the other entities on Deserialize | ||||
SerializeCommon(serialize); | SerializeCommon(serialize); | ||||
} | } | ||||
void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override | void CCmpObstructionManager::Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) | ||||
{ | { | ||||
Init(paramNode); | Init(paramNode); | ||||
SerializeCommon(deserialize); | SerializeCommon(deserialize); | ||||
i32 size = ((m_WorldX1-m_WorldX0)/Pathfinding::NAVCELL_SIZE_INT).ToInt_RoundToInfinity(); | i32 size = ((m_WorldX1-m_WorldX0)/Pathfinding::NAVCELL_SIZE_INT).ToInt_RoundToInfinity(); | ||||
m_UpdateInformations.dirtinessGrid = Grid<u8>(size, size); | m_UpdateInformations.dirtinessGrid = Grid<u8>(size, size); | ||||
} | } | ||||
void HandleMessage(const CMessage& msg, bool UNUSED(global)) override | void CCmpObstructionManager::HandleMessage(const CMessage& msg, bool UNUSED(global)) | ||||
{ | { | ||||
switch (msg.GetType()) | switch (msg.GetType()) | ||||
{ | { | ||||
case MT_RenderSubmit: | case MT_RenderSubmit: | ||||
{ | { | ||||
const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg); | const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg); | ||||
RenderSubmit(msgData.collector); | RenderSubmit(msgData.collector); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// NB: on deserialization, this function is not called after the component is reset. | // NB: on deserialization, this function is not called after the component is reset. | ||||
// So anything that happens here should be safely serialized. | // So anything that happens here should be safely serialized. | ||||
void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) override | void CCmpObstructionManager::SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) | ||||
{ | { | ||||
m_WorldX0 = x0; | m_WorldX0 = x0; | ||||
m_WorldZ0 = z0; | m_WorldZ0 = z0; | ||||
m_WorldX1 = x1; | m_WorldX1 = x1; | ||||
m_WorldZ1 = z1; | m_WorldZ1 = z1; | ||||
MakeDirtyAll(); | MakeDirtyAll(); | ||||
// Subdivision system bounds: | // Subdivision system bounds: | ||||
ENSURE(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet | ENSURE(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet | ||||
ResetSubdivisions(x1, z1); | ResetSubdivisions(x1, z1); | ||||
i32 size = ((m_WorldX1-m_WorldX0)/Pathfinding::NAVCELL_SIZE_INT).ToInt_RoundToInfinity(); | i32 size = ((m_WorldX1-m_WorldX0)/Pathfinding::NAVCELL_SIZE_INT).ToInt_RoundToInfinity(); | ||||
m_UpdateInformations.dirtinessGrid = Grid<u8>(size, size); | m_UpdateInformations.dirtinessGrid = Grid<u8>(size, size); | ||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | ||||
if (cmpPathfinder) | if (cmpPathfinder) | ||||
m_MaxClearance = cmpPathfinder->GetMaximumClearance(); | m_MaxClearance = cmpPathfinder->GetMaximumClearance(); | ||||
} | } | ||||
void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1) | void CCmpObstructionManager::ResetSubdivisions(entity_pos_t x1, entity_pos_t z1) | ||||
{ | { | ||||
m_UnitSubdivision.Reset(x1, z1, OBSTRUCTION_SUBDIVISION_SIZE); | m_UnitSubdivision.Reset(x1, z1, OBSTRUCTION_SUBDIVISION_SIZE); | ||||
m_StaticSubdivision.Reset(x1, z1, OBSTRUCTION_SUBDIVISION_SIZE); | m_StaticSubdivision.Reset(x1, z1, OBSTRUCTION_SUBDIVISION_SIZE); | ||||
for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it) | for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it) | ||||
{ | { | ||||
CFixedVector2D center(it->second.x, it->second.z); | CFixedVector2D center(it->second.x, it->second.z); | ||||
CFixedVector2D halfSize(it->second.clearance, it->second.clearance); | CFixedVector2D halfSize(it->second.clearance, it->second.clearance); | ||||
m_UnitSubdivision.Add(it->first, center - halfSize, center + halfSize); | m_UnitSubdivision.Add(it->first, center - halfSize, center + halfSize); | ||||
} | } | ||||
for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it) | for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it) | ||||
{ | { | ||||
CFixedVector2D center(it->second.x, it->second.z); | CFixedVector2D center(it->second.x, it->second.z); | ||||
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(it->second.u, it->second.v, CFixedVector2D(it->second.hw, it->second.hh)); | CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(it->second.u, it->second.v, CFixedVector2D(it->second.hw, it->second.hh)); | ||||
m_StaticSubdivision.Add(it->first, center - bbHalfSize, center + bbHalfSize); | m_StaticSubdivision.Add(it->first, center - bbHalfSize, center + bbHalfSize); | ||||
} | } | ||||
} | } | ||||
tag_t AddUnitShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_pos_t clearance, flags_t flags, entity_id_t group) override | auto CCmpObstructionManager::AddUnitShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, | ||||
entity_pos_t clearance, flags_t flags, entity_id_t group) -> tag_t | |||||
{ | { | ||||
UnitShape shape = { ent, x, z, clearance, flags, group }; | UnitShape shape = { ent, x, z, clearance, flags, group }; | ||||
u32 id = m_UnitShapeNext++; | u32 id = m_UnitShapeNext++; | ||||
m_UnitShapes[id] = shape; | m_UnitShapes[id] = shape; | ||||
m_UnitSubdivision.Add(id, CFixedVector2D(x - clearance, z - clearance), CFixedVector2D(x + clearance, z + clearance)); | m_UnitSubdivision.Add(id, CFixedVector2D(x - clearance, z - clearance), CFixedVector2D(x + clearance, z + clearance)); | ||||
MakeDirtyUnit(flags, id, shape); | MakeDirtyUnit(flags, id, shape); | ||||
return UNIT_INDEX_TO_TAG(id); | return UNIT_INDEX_TO_TAG(id); | ||||
} | } | ||||
tag_t AddStaticShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h, flags_t flags, entity_id_t group, entity_id_t group2 /* = INVALID_ENTITY */) override | auto CCmpObstructionManager::AddStaticShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, | ||||
entity_angle_t a, entity_pos_t w, entity_pos_t h, flags_t flags, entity_id_t group, | |||||
entity_id_t group2 /* = INVALID_ENTITY */) -> tag_t | |||||
{ | { | ||||
fixed s, c; | fixed s, c; | ||||
sincos_approx(a, s, c); | sincos_approx(a, s, c); | ||||
CFixedVector2D u(c, -s); | CFixedVector2D u(c, -s); | ||||
CFixedVector2D v(s, c); | CFixedVector2D v(s, c); | ||||
StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags, group, group2 }; | StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags, group, group2 }; | ||||
u32 id = m_StaticShapeNext++; | u32 id = m_StaticShapeNext++; | ||||
m_StaticShapes[id] = shape; | m_StaticShapes[id] = shape; | ||||
CFixedVector2D center(x, z); | CFixedVector2D center(x, z); | ||||
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2)); | CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2)); | ||||
m_StaticSubdivision.Add(id, center - bbHalfSize, center + bbHalfSize); | m_StaticSubdivision.Add(id, center - bbHalfSize, center + bbHalfSize); | ||||
MakeDirtyStatic(flags, id, shape); | MakeDirtyStatic(flags, id, shape); | ||||
return STATIC_INDEX_TO_TAG(id); | return STATIC_INDEX_TO_TAG(id); | ||||
} | } | ||||
ObstructionSquare GetUnitShapeObstruction(entity_pos_t x, entity_pos_t z, entity_pos_t clearance) const override | auto CCmpObstructionManager::GetUnitShapeObstruction(entity_pos_t x, entity_pos_t z, | ||||
entity_pos_t clearance) const -> ObstructionSquare | |||||
{ | { | ||||
CFixedVector2D u(entity_pos_t::FromInt(1), entity_pos_t::Zero()); | CFixedVector2D u(entity_pos_t::FromInt(1), entity_pos_t::Zero()); | ||||
CFixedVector2D v(entity_pos_t::Zero(), entity_pos_t::FromInt(1)); | CFixedVector2D v(entity_pos_t::Zero(), entity_pos_t::FromInt(1)); | ||||
ObstructionSquare o = { x, z, u, v, clearance, clearance }; | ObstructionSquare o = { x, z, u, v, clearance, clearance }; | ||||
return o; | return o; | ||||
} | } | ||||
ObstructionSquare GetStaticShapeObstruction(entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h) const override | auto CCmpObstructionManager::GetStaticShapeObstruction(entity_pos_t x, entity_pos_t z, | ||||
entity_angle_t a, entity_pos_t w, entity_pos_t h) const -> ObstructionSquare | |||||
{ | { | ||||
fixed s, c; | fixed s, c; | ||||
sincos_approx(a, s, c); | sincos_approx(a, s, c); | ||||
CFixedVector2D u(c, -s); | CFixedVector2D u(c, -s); | ||||
CFixedVector2D v(s, c); | CFixedVector2D v(s, c); | ||||
ObstructionSquare o = { x, z, u, v, w/2, h/2 }; | ObstructionSquare o = { x, z, u, v, w/2, h/2 }; | ||||
return o; | return o; | ||||
} | } | ||||
void MoveShape(tag_t tag, entity_pos_t x, entity_pos_t z, entity_angle_t a) override | void CCmpObstructionManager::MoveShape(tag_t tag, entity_pos_t x, entity_pos_t z, entity_angle_t a) | ||||
{ | { | ||||
ENSURE(TAG_IS_VALID(tag)); | ENSURE(TAG_IS_VALID(tag)); | ||||
if (TAG_IS_UNIT(tag)) | if (TAG_IS_UNIT(tag)) | ||||
{ | { | ||||
UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)]; | UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)]; | ||||
MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region | MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region | ||||
m_UnitSubdivision.Move(TAG_TO_INDEX(tag), | m_UnitSubdivision.Move(TAG_TO_INDEX(tag), | ||||
CFixedVector2D(shape.x - shape.clearance, shape.z - shape.clearance), | CFixedVector2D(shape.x - shape.clearance, shape.z - shape.clearance), | ||||
CFixedVector2D(shape.x + shape.clearance, shape.z + shape.clearance), | CFixedVector2D(shape.x + shape.clearance, shape.z + shape.clearance), | ||||
CFixedVector2D(x - shape.clearance, z - shape.clearance), | CFixedVector2D(x - shape.clearance, z - shape.clearance), | ||||
CFixedVector2D(x + shape.clearance, z + shape.clearance)); | CFixedVector2D(x + shape.clearance, z + shape.clearance)); | ||||
shape.x = x; | shape.x = x; | ||||
shape.z = z; | shape.z = z; | ||||
MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region | MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
fixed s, c; | fixed s, c; | ||||
sincos_approx(a, s, c); | sincos_approx(a, s, c); | ||||
CFixedVector2D u(c, -s); | CFixedVector2D u(c, -s); | ||||
CFixedVector2D v(s, c); | CFixedVector2D v(s, c); | ||||
StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)]; | StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)]; | ||||
MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region | MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the old shape region | ||||
CFixedVector2D fromBbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh)); | CFixedVector2D fromBbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh)); | ||||
CFixedVector2D toBbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(shape.hw, shape.hh)); | CFixedVector2D toBbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(shape.hw, shape.hh)); | ||||
m_StaticSubdivision.Move(TAG_TO_INDEX(tag), | m_StaticSubdivision.Move(TAG_TO_INDEX(tag), | ||||
CFixedVector2D(shape.x, shape.z) - fromBbHalfSize, | CFixedVector2D(shape.x, shape.z) - fromBbHalfSize, | ||||
CFixedVector2D(shape.x, shape.z) + fromBbHalfSize, | CFixedVector2D(shape.x, shape.z) + fromBbHalfSize, | ||||
CFixedVector2D(x, z) - toBbHalfSize, | CFixedVector2D(x, z) - toBbHalfSize, | ||||
CFixedVector2D(x, z) + toBbHalfSize); | CFixedVector2D(x, z) + toBbHalfSize); | ||||
shape.x = x; | shape.x = x; | ||||
shape.z = z; | shape.z = z; | ||||
shape.u = u; | shape.u = u; | ||||
shape.v = v; | shape.v = v; | ||||
MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region | MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); // dirty the new shape region | ||||
} | } | ||||
} | } | ||||
void SetUnitMovingFlag(tag_t tag, bool moving) override | void CCmpObstructionManager::SetUnitMovingFlag(tag_t tag, bool moving) | ||||
{ | { | ||||
ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag)); | ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag)); | ||||
if (TAG_IS_UNIT(tag)) | if (TAG_IS_UNIT(tag)) | ||||
{ | { | ||||
UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)]; | UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)]; | ||||
if (moving) | if (moving) | ||||
shape.flags |= FLAG_MOVING; | shape.flags |= FLAG_MOVING; | ||||
else | else | ||||
shape.flags &= (flags_t)~FLAG_MOVING; | shape.flags &= (flags_t)~FLAG_MOVING; | ||||
MakeDirtyDebug(); | MakeDirtyDebug(); | ||||
} | } | ||||
} | } | ||||
void SetUnitControlGroup(tag_t tag, entity_id_t group) override | void CCmpObstructionManager::SetUnitControlGroup(tag_t tag, entity_id_t group) | ||||
{ | { | ||||
ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag)); | ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag)); | ||||
if (TAG_IS_UNIT(tag)) | if (TAG_IS_UNIT(tag)) | ||||
{ | { | ||||
UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)]; | UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)]; | ||||
shape.group = group; | shape.group = group; | ||||
} | } | ||||
} | } | ||||
void SetStaticControlGroup(tag_t tag, entity_id_t group, entity_id_t group2) override | void CCmpObstructionManager::SetStaticControlGroup(tag_t tag, entity_id_t group, entity_id_t group2) | ||||
{ | { | ||||
ENSURE(TAG_IS_VALID(tag) && TAG_IS_STATIC(tag)); | ENSURE(TAG_IS_VALID(tag) && TAG_IS_STATIC(tag)); | ||||
if (TAG_IS_STATIC(tag)) | if (TAG_IS_STATIC(tag)) | ||||
{ | { | ||||
StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)]; | StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)]; | ||||
shape.group = group; | shape.group = group; | ||||
shape.group2 = group2; | shape.group2 = group2; | ||||
} | } | ||||
} | } | ||||
void RemoveShape(tag_t tag) override | void CCmpObstructionManager::RemoveShape(tag_t tag) | ||||
{ | { | ||||
ENSURE(TAG_IS_VALID(tag)); | ENSURE(TAG_IS_VALID(tag)); | ||||
if (TAG_IS_UNIT(tag)) | if (TAG_IS_UNIT(tag)) | ||||
{ | { | ||||
UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)]; | UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)]; | ||||
m_UnitSubdivision.Remove(TAG_TO_INDEX(tag), | m_UnitSubdivision.Remove(TAG_TO_INDEX(tag), | ||||
CFixedVector2D(shape.x - shape.clearance, shape.z - shape.clearance), | CFixedVector2D(shape.x - shape.clearance, shape.z - shape.clearance), | ||||
CFixedVector2D(shape.x + shape.clearance, shape.z + shape.clearance)); | CFixedVector2D(shape.x + shape.clearance, shape.z + shape.clearance)); | ||||
MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); | MakeDirtyUnit(shape.flags, TAG_TO_INDEX(tag), shape); | ||||
m_UnitShapes.erase(TAG_TO_INDEX(tag)); | m_UnitShapes.erase(TAG_TO_INDEX(tag)); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)]; | StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)]; | ||||
CFixedVector2D center(shape.x, shape.z); | CFixedVector2D center(shape.x, shape.z); | ||||
CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh)); | CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh)); | ||||
m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize); | m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize); | ||||
MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); | MakeDirtyStatic(shape.flags, TAG_TO_INDEX(tag), shape); | ||||
m_StaticShapes.erase(TAG_TO_INDEX(tag)); | m_StaticShapes.erase(TAG_TO_INDEX(tag)); | ||||
} | } | ||||
} | } | ||||
ObstructionSquare GetObstruction(tag_t tag) const override | auto CCmpObstructionManager::GetObstruction(tag_t tag) const -> ObstructionSquare | ||||
{ | { | ||||
ENSURE(TAG_IS_VALID(tag)); | ENSURE(TAG_IS_VALID(tag)); | ||||
if (TAG_IS_UNIT(tag)) | if (TAG_IS_UNIT(tag)) | ||||
{ | { | ||||
const UnitShape& shape = m_UnitShapes.at(TAG_TO_INDEX(tag)); | const UnitShape& shape = m_UnitShapes.at(TAG_TO_INDEX(tag)); | ||||
CFixedVector2D u(entity_pos_t::FromInt(1), entity_pos_t::Zero()); | CFixedVector2D u(entity_pos_t::FromInt(1), entity_pos_t::Zero()); | ||||
CFixedVector2D v(entity_pos_t::Zero(), entity_pos_t::FromInt(1)); | CFixedVector2D v(entity_pos_t::Zero(), entity_pos_t::FromInt(1)); | ||||
ObstructionSquare o = { shape.x, shape.z, u, v, shape.clearance, shape.clearance }; | ObstructionSquare o = { shape.x, shape.z, u, v, shape.clearance, shape.clearance }; | ||||
return o; | return o; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
const StaticShape& shape = m_StaticShapes.at(TAG_TO_INDEX(tag)); | const StaticShape& shape = m_StaticShapes.at(TAG_TO_INDEX(tag)); | ||||
ObstructionSquare o = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh }; | ObstructionSquare o = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh }; | ||||
return o; | return o; | ||||
} | } | ||||
} | } | ||||
fixed DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const override; | void CCmpObstructionManager::SetPassabilityCircular(bool enabled) | ||||
fixed MaxDistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const override; | |||||
fixed DistanceToTarget(entity_id_t ent, entity_id_t target) const override; | |||||
fixed MaxDistanceToTarget(entity_id_t ent, entity_id_t target) const override; | |||||
fixed DistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const override; | |||||
fixed MaxDistanceBetweenShapes(const ObstructionSquare& source, const ObstructionSquare& target) const override; | |||||
bool IsInPointRange(entity_id_t ent, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override; | |||||
bool IsInTargetRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override; | |||||
bool IsInTargetParabolicRange(entity_id_t ent, entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange, entity_pos_t yOrigin, bool opposite) const override; | |||||
bool IsPointInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t px, entity_pos_t pz, entity_pos_t minRange, entity_pos_t maxRange) const override; | |||||
bool AreShapesInRange(const ObstructionSquare& source, const ObstructionSquare& target, entity_pos_t minRange, entity_pos_t maxRange, bool opposite) const override; | |||||
bool TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r, bool relaxClearanceForUnits = false) const override; | |||||
bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector<entity_id_t>* out) const override; | |||||
bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector<entity_id_t>* out) const override; | |||||
void Rasterize(Grid<NavcellData>& grid, const std::vector<PathfinderPassability>& passClasses, bool fullUpdate) override; | |||||
void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares) const override; | |||||
void GetUnitObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares) const override; | |||||
void GetStaticObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares) const override; | |||||
void GetUnitsOnObstruction(const ObstructionSquare& square, std::vector<entity_id_t>& out, const IObstructionTestFilter& filter, bool strict = false) const override; | |||||
void GetStaticObstructionsOnObstruction(const ObstructionSquare& square, std::vector<entity_id_t>& out, const IObstructionTestFilter& filter) const override; | |||||
void SetPassabilityCircular(bool enabled) override | |||||
{ | { | ||||
m_PassabilityCircular = enabled; | m_PassabilityCircular = enabled; | ||||
MakeDirtyAll(); | MakeDirtyAll(); | ||||
CMessageObstructionMapShapeChanged msg; | CMessageObstructionMapShapeChanged msg; | ||||
GetSimContext().GetComponentManager().BroadcastMessage(msg); | GetSimContext().GetComponentManager().BroadcastMessage(msg); | ||||
} | } | ||||
bool GetPassabilityCircular() const override | bool CCmpObstructionManager::GetPassabilityCircular() const | ||||
{ | { | ||||
return m_PassabilityCircular; | return m_PassabilityCircular; | ||||
} | } | ||||
void SetDebugOverlay(bool enabled) override | void CCmpObstructionManager::SetDebugOverlay(bool enabled) | ||||
{ | { | ||||
m_DebugOverlayEnabled = enabled; | m_DebugOverlayEnabled = enabled; | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
if (!enabled) | if (!enabled) | ||||
m_DebugOverlayLines.clear(); | m_DebugOverlayLines.clear(); | ||||
} | } | ||||
void RenderSubmit(SceneCollector& collector); | void CCmpObstructionManager::UpdateInformations(GridUpdateInformation& informations) | ||||
void UpdateInformations(GridUpdateInformation& informations) override | |||||
{ | { | ||||
if (!m_UpdateInformations.dirtinessGrid.blank()) | if (!m_UpdateInformations.dirtinessGrid.blank()) | ||||
informations.MergeAndClear(m_UpdateInformations); | informations.MergeAndClear(m_UpdateInformations); | ||||
} | } | ||||
private: | void CCmpObstructionManager::MakeDirtyAll() | ||||
// Dynamic updates for the long-range pathfinder | |||||
GridUpdateInformation m_UpdateInformations; | |||||
// These vectors might contain shapes that were deleted | |||||
std::vector<u32> m_DirtyStaticShapes; | |||||
std::vector<u32> m_DirtyUnitShapes; | |||||
/** | |||||
* Mark all previous Rasterize()d grids as dirty, and the debug display. | |||||
* Call this when the world bounds have changed. | |||||
*/ | |||||
void MakeDirtyAll() | |||||
{ | { | ||||
m_UpdateInformations.dirty = true; | m_UpdateInformations.dirty = true; | ||||
m_UpdateInformations.globallyDirty = true; | m_UpdateInformations.globallyDirty = true; | ||||
m_UpdateInformations.dirtinessGrid.reset(); | m_UpdateInformations.dirtinessGrid.reset(); | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
} | } | ||||
/** | void CCmpObstructionManager::MakeDirtyDebug() | ||||
* Mark the debug display as dirty. | |||||
* Call this when nothing has changed except a unit's 'moving' flag. | |||||
*/ | |||||
void MakeDirtyDebug() | |||||
{ | { | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
} | } | ||||
inline void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const entity_pos_t& r) | void CCmpObstructionManager::MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, | ||||
const entity_pos_t& r) | |||||
{ | { | ||||
MarkDirtinessGrid(x, z, CFixedVector2D(r, r)); | MarkDirtinessGrid(x, z, CFixedVector2D(r, r)); | ||||
} | } | ||||
inline void MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, const CFixedVector2D& hbox) | void CCmpObstructionManager::MarkDirtinessGrid(const entity_pos_t& x, const entity_pos_t& z, | ||||
const CFixedVector2D& hbox) | |||||
{ | { | ||||
if (m_UpdateInformations.dirtinessGrid.m_W == 0) | if (m_UpdateInformations.dirtinessGrid.m_W == 0) | ||||
return; | return; | ||||
u16 j0, j1, i0, i1; | u16 j0, j1, i0, i1; | ||||
Pathfinding::NearestNavcell(x - hbox.X, z - hbox.Y, i0, j0, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H); | Pathfinding::NearestNavcell(x - hbox.X, z - hbox.Y, i0, j0, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H); | ||||
Pathfinding::NearestNavcell(x + hbox.X, z + hbox.Y, i1, j1, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H); | Pathfinding::NearestNavcell(x + hbox.X, z + hbox.Y, i1, j1, m_UpdateInformations.dirtinessGrid.m_W, m_UpdateInformations.dirtinessGrid.m_H); | ||||
for (int j = j0; j < j1; ++j) | for (int j = j0; j < j1; ++j) | ||||
for (int i = i0; i < i1; ++i) | for (int i = i0; i < i1; ++i) | ||||
m_UpdateInformations.dirtinessGrid.set(i, j, 1); | m_UpdateInformations.dirtinessGrid.set(i, j, 1); | ||||
} | } | ||||
/** | void CCmpObstructionManager::MakeDirtyStatic(flags_t flags, u32 index, const StaticShape& shape) | ||||
* Mark all previous Rasterize()d grids as dirty, if they depend on this shape. | |||||
* Call this when a static shape has changed. | |||||
*/ | |||||
void MakeDirtyStatic(flags_t flags, u32 index, const StaticShape& shape) | |||||
{ | { | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION)) | if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION)) | ||||
{ | { | ||||
m_UpdateInformations.dirty = true; | m_UpdateInformations.dirty = true; | ||||
if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), index) == m_DirtyStaticShapes.end()) | if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), index) == m_DirtyStaticShapes.end()) | ||||
m_DirtyStaticShapes.push_back(index); | m_DirtyStaticShapes.push_back(index); | ||||
// All shapes overlapping the updated part of the grid should be dirtied too. | // All shapes overlapping the updated part of the grid should be dirtied too. | ||||
// We are going to invalidate the region of the grid corresponding to the modified shape plus its clearance, | // We are going to invalidate the region of the grid corresponding to the modified shape plus its clearance, | ||||
// and we need to get the shapes whose clearance can overlap this area. So we need to extend the search area | // and we need to get the shapes whose clearance can overlap this area. So we need to extend the search area | ||||
// by two times the maximum clearance. | // by two times the maximum clearance. | ||||
CFixedVector2D center(shape.x, shape.z); | CFixedVector2D center(shape.x, shape.z); | ||||
CFixedVector2D hbox = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh)); | CFixedVector2D hbox = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh)); | ||||
CFixedVector2D expand(m_MaxClearance, m_MaxClearance); | CFixedVector2D expand(m_MaxClearance, m_MaxClearance); | ||||
std::vector<u32> staticsNear; | std::vector<u32> staticsNear; | ||||
m_StaticSubdivision.GetInRange(staticsNear, center - hbox - expand*2, center + hbox + expand*2); | m_StaticSubdivision.GetInRange(staticsNear, center - hbox - expand*2, center + hbox + expand*2); | ||||
for (u32& staticId : staticsNear) | for (u32& staticId : staticsNear) | ||||
if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end()) | if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end()) | ||||
m_DirtyStaticShapes.push_back(staticId); | m_DirtyStaticShapes.push_back(staticId); | ||||
std::vector<u32> unitsNear; | std::vector<u32> unitsNear; | ||||
m_UnitSubdivision.GetInRange(unitsNear, center - hbox - expand*2, center + hbox + expand*2); | m_UnitSubdivision.GetInRange(unitsNear, center - hbox - expand*2, center + hbox + expand*2); | ||||
for (u32& unitId : unitsNear) | for (u32& unitId : unitsNear) | ||||
if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end()) | if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end()) | ||||
m_DirtyUnitShapes.push_back(unitId); | m_DirtyUnitShapes.push_back(unitId); | ||||
MarkDirtinessGrid(shape.x, shape.z, hbox + expand); | MarkDirtinessGrid(shape.x, shape.z, hbox + expand); | ||||
} | } | ||||
} | } | ||||
/** | void CCmpObstructionManager::MakeDirtyUnit(flags_t flags, u32 index, const UnitShape& shape) | ||||
* Mark all previous Rasterize()d grids as dirty, if they depend on this shape. | |||||
* Call this when a unit shape has changed. | |||||
*/ | |||||
void MakeDirtyUnit(flags_t flags, u32 index, const UnitShape& shape) | |||||
{ | { | ||||
m_DebugOverlayDirty = true; | m_DebugOverlayDirty = true; | ||||
if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION)) | if (flags & (FLAG_BLOCK_PATHFINDING | FLAG_BLOCK_FOUNDATION)) | ||||
{ | { | ||||
m_UpdateInformations.dirty = true; | m_UpdateInformations.dirty = true; | ||||
if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), index) == m_DirtyUnitShapes.end()) | if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), index) == m_DirtyUnitShapes.end()) | ||||
m_DirtyUnitShapes.push_back(index); | m_DirtyUnitShapes.push_back(index); | ||||
// All shapes overlapping the updated part of the grid should be dirtied too. | // All shapes overlapping the updated part of the grid should be dirtied too. | ||||
// We are going to invalidate the region of the grid corresponding to the modified shape plus its clearance, | // We are going to invalidate the region of the grid corresponding to the modified shape plus its clearance, | ||||
// and we need to get the shapes whose clearance can overlap this area. So we need to extend the search area | // and we need to get the shapes whose clearance can overlap this area. So we need to extend the search area | ||||
// by two times the maximum clearance. | // by two times the maximum clearance. | ||||
CFixedVector2D center(shape.x, shape.z); | CFixedVector2D center(shape.x, shape.z); | ||||
std::vector<u32> staticsNear; | std::vector<u32> staticsNear; | ||||
m_StaticSubdivision.GetNear(staticsNear, center, shape.clearance + m_MaxClearance*2); | m_StaticSubdivision.GetNear(staticsNear, center, shape.clearance + m_MaxClearance*2); | ||||
for (u32& staticId : staticsNear) | for (u32& staticId : staticsNear) | ||||
if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end()) | if (std::find(m_DirtyStaticShapes.begin(), m_DirtyStaticShapes.end(), staticId) == m_DirtyStaticShapes.end()) | ||||
m_DirtyStaticShapes.push_back(staticId); | m_DirtyStaticShapes.push_back(staticId); | ||||
std::vector<u32> unitsNear; | std::vector<u32> unitsNear; | ||||
m_UnitSubdivision.GetNear(unitsNear, center, shape.clearance + m_MaxClearance*2); | m_UnitSubdivision.GetNear(unitsNear, center, shape.clearance + m_MaxClearance*2); | ||||
for (u32& unitId : unitsNear) | for (u32& unitId : unitsNear) | ||||
if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end()) | if (std::find(m_DirtyUnitShapes.begin(), m_DirtyUnitShapes.end(), unitId) == m_DirtyUnitShapes.end()) | ||||
m_DirtyUnitShapes.push_back(unitId); | m_DirtyUnitShapes.push_back(unitId); | ||||
MarkDirtinessGrid(shape.x, shape.z, shape.clearance + m_MaxClearance); | MarkDirtinessGrid(shape.x, shape.z, shape.clearance + m_MaxClearance); | ||||
} | } | ||||
} | } | ||||
/** | bool CCmpObstructionManager::IsInWorld(entity_pos_t x, entity_pos_t z, entity_pos_t r) const | ||||
* Return whether the given point is within the world bounds by at least r | |||||
*/ | |||||
inline bool IsInWorld(entity_pos_t x, entity_pos_t z, entity_pos_t r) const | |||||
{ | { | ||||
return (m_WorldX0+r <= x && x <= m_WorldX1-r && m_WorldZ0+r <= z && z <= m_WorldZ1-r); | return (m_WorldX0+r <= x && x <= m_WorldX1-r && m_WorldZ0+r <= z && z <= m_WorldZ1-r); | ||||
} | } | ||||
/** | bool CCmpObstructionManager::IsInWorld(const CFixedVector2D& p) const | ||||
* Return whether the given point is within the world bounds | |||||
*/ | |||||
inline bool IsInWorld(const CFixedVector2D& p) const | |||||
{ | { | ||||
return (m_WorldX0 <= p.X && p.X <= m_WorldX1 && m_WorldZ0 <= p.Y && p.Y <= m_WorldZ1); | return (m_WorldX0 <= p.X && p.X <= m_WorldX1 && m_WorldZ0 <= p.Y && p.Y <= m_WorldZ1); | ||||
} | } | ||||
void RasterizeHelper(Grid<NavcellData>& grid, ICmpObstructionManager::flags_t requireMask, bool fullUpdate, pass_class_t appliedMask, entity_pos_t clearance = fixed::Zero()) const; | |||||
}; | |||||
REGISTER_COMPONENT_TYPE(ObstructionManager) | REGISTER_COMPONENT_TYPE(ObstructionManager) | ||||
/** | /** | ||||
* DistanceTo function family, all end up in calculating a vector length, DistanceBetweenShapes or | * DistanceTo function family, all end up in calculating a vector length, DistanceBetweenShapes or | ||||
* MaxDistanceBetweenShapes. The MaxFoo family calculates the opposite edge opposite edge distance. | * MaxDistanceBetweenShapes. The MaxFoo family calculates the opposite edge opposite edge distance. | ||||
* When the distance is undefined we return -1. | * When the distance is undefined we return -1. | ||||
*/ | */ | ||||
fixed CCmpObstructionManager::DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const | fixed CCmpObstructionManager::DistanceToPoint(entity_id_t ent, entity_pos_t px, entity_pos_t pz) const | ||||
▲ Show 20 Lines • Show All 491 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator