2010-03-17 16:01:12 -07:00
/* Copyright (C) 2010 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 "ICmpObstructionManager.h"
# include "simulation2/MessageTypes.h"
2010-04-29 16:36:05 -07:00
# include "simulation2/helpers/Geometry.h"
2010-03-20 12:18:01 -07:00
# include "simulation2/helpers/Render.h"
2010-03-17 16:01:12 -07:00
2010-03-20 12:18:01 -07:00
# include "graphics/Overlay.h"
2010-03-17 16:01:12 -07:00
# include "graphics/Terrain.h"
# include "maths/MathUtil.h"
# include "ps/Overlay.h"
# include "ps/Profile.h"
2010-03-20 12:18:01 -07:00
# include "renderer/Scene.h"
2010-03-17 16:01:12 -07:00
// Externally, tags are opaque non-zero positive integers.
// Internally, they are tagged (by shape) indexes into shape lists.
// idx must be non-zero.
2010-07-04 10:19:38 -07:00
# define TAG_IS_VALID(tag) ((tag).valid())
# define TAG_IS_UNIT(tag) (((tag).n & 1) == 0)
# define TAG_IS_STATIC(tag) (((tag).n & 1) == 1)
# define UNIT_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 0)
# define STATIC_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 1)
# define TAG_TO_INDEX(tag) ((tag).n >> 1)
2010-03-17 16:01:12 -07:00
/**
2010-04-29 16:36:05 -07:00
* Internal representation of axis - aligned sometimes - square sometimes - circle shapes for moving units
2010-03-17 16:01:12 -07:00
*/
2010-04-29 16:36:05 -07:00
struct UnitShape
2010-03-17 16:01:12 -07:00
{
2010-04-29 16:36:05 -07:00
entity_pos_t x , z ;
entity_pos_t r ; // radius of circle, or half width of square
bool moving ; // whether it's currently mobile (and should be generally ignored when pathing)
2010-09-03 02:55:14 -07:00
entity_id_t group ; // control group (typically the owner entity, or a formation controller entity) (units ignore collisions with others in the same group)
2010-03-17 16:01:12 -07:00
} ;
/**
2010-04-29 16:36:05 -07:00
* Internal representation of arbitrary - rotation static square shapes for buildings
2010-03-17 16:01:12 -07:00
*/
2010-04-29 16:36:05 -07:00
struct StaticShape
2010-03-17 16:01:12 -07:00
{
2010-04-29 16:36:05 -07:00
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
2010-03-17 16:01:12 -07:00
} ;
class CCmpObstructionManager : public ICmpObstructionManager
{
public :
2010-03-20 12:18:01 -07:00
static void ClassInit ( CComponentManager & componentManager )
2010-03-17 16:01:12 -07:00
{
2010-03-20 12:18:01 -07:00
componentManager . SubscribeToMessageType ( MT_RenderSubmit ) ; // for debug overlays
2010-03-17 16:01:12 -07:00
}
DEFAULT_COMPONENT_ALLOCATOR ( ObstructionManager )
2010-03-20 12:18:01 -07:00
bool m_DebugOverlayEnabled ;
bool m_DebugOverlayDirty ;
std : : vector < SOverlayLine > m_DebugOverlayLines ;
2010-03-17 16:01:12 -07:00
// TODO: using std::map is a bit inefficient; is there a better way to store these?
2010-04-29 16:36:05 -07:00
std : : map < u32 , UnitShape > m_UnitShapes ;
std : : map < u32 , StaticShape > m_StaticShapes ;
u32 m_UnitShapeNext ; // next allocated id
u32 m_StaticShapeNext ;
2010-03-17 16:01:12 -07:00
2010-07-25 07:11:21 -07:00
entity_pos_t m_WorldX0 ;
entity_pos_t m_WorldZ0 ;
entity_pos_t m_WorldX1 ;
entity_pos_t m_WorldZ1 ;
2010-04-23 09:09:03 -07:00
static std : : string GetSchema ( )
{
return " <a:component type='system'/><empty/> " ;
}
2010-03-17 16:01:12 -07:00
virtual void Init ( const CSimContext & context , const CParamNode & UNUSED ( paramNode ) )
{
2010-03-20 12:18:01 -07:00
m_DebugOverlayEnabled = false ;
m_DebugOverlayDirty = true ;
2010-04-29 16:36:05 -07:00
m_UnitShapeNext = 1 ;
m_StaticShapeNext = 1 ;
2010-03-17 16:01:12 -07:00
m_DirtyID = 1 ; // init to 1 so default-initialised grids are considered dirty
2010-07-25 07:11:21 -07:00
m_WorldX0 = m_WorldZ0 = m_WorldX1 = m_WorldZ1 = entity_pos_t : : Zero ( ) ;
2010-03-17 16:01:12 -07:00
}
virtual void Deinit ( const CSimContext & UNUSED ( context ) )
{
}
virtual void Serialize ( ISerializer & serialize )
{
// TODO: do something here
// (Do we need to serialise the obstruction state, or is it fine to regenerate it from
// the original entities after deserialisation?)
}
virtual void Deserialize ( const CSimContext & context , const CParamNode & paramNode , IDeserializer & deserialize )
{
Init ( context , paramNode ) ;
// TODO
}
2010-03-20 12:18:01 -07:00
virtual void HandleMessage ( const CSimContext & context , const CMessage & msg , bool UNUSED ( global ) )
{
switch ( msg . GetType ( ) )
{
case MT_RenderSubmit :
{
const CMessageRenderSubmit & msgData = static_cast < const CMessageRenderSubmit & > ( msg ) ;
RenderSubmit ( context , msgData . collector ) ;
break ;
}
}
}
2010-07-25 07:11:21 -07:00
virtual void SetBounds ( entity_pos_t x0 , entity_pos_t z0 , entity_pos_t x1 , entity_pos_t z1 )
{
m_WorldX0 = x0 ;
m_WorldZ0 = z0 ;
m_WorldX1 = x1 ;
m_WorldZ1 = z1 ;
MakeDirty ( ) ;
}
2010-09-03 02:55:14 -07:00
virtual tag_t AddUnitShape ( entity_pos_t x , entity_pos_t z , entity_pos_t r , bool moving , entity_id_t group )
2010-03-17 16:01:12 -07:00
{
2010-09-03 02:55:14 -07:00
UnitShape shape = { x , z , r , moving , group } ;
2010-04-29 16:36:05 -07:00
size_t id = m_UnitShapeNext + + ;
m_UnitShapes [ id ] = shape ;
2010-07-25 07:11:21 -07:00
MakeDirtyUnits ( ) ;
2010-04-29 16:36:05 -07:00
return UNIT_INDEX_TO_TAG ( id ) ;
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
virtual tag_t AddStaticShape ( entity_pos_t x , entity_pos_t z , entity_angle_t a , entity_pos_t w , entity_pos_t h )
2010-03-17 16:01:12 -07:00
{
2010-05-02 13:32:37 -07:00
fixed s , c ;
2010-04-29 16:36:05 -07:00
sincos_approx ( a , s , c ) ;
CFixedVector2D u ( c , - s ) ;
CFixedVector2D v ( s , c ) ;
StaticShape shape = { x , z , u , v , w / 2 , h / 2 } ;
size_t id = m_StaticShapeNext + + ;
m_StaticShapes [ id ] = shape ;
2010-03-17 16:01:12 -07:00
MakeDirty ( ) ;
2010-04-29 16:36:05 -07:00
return STATIC_INDEX_TO_TAG ( id ) ;
2010-03-17 16:01:12 -07:00
}
virtual void MoveShape ( tag_t tag , entity_pos_t x , entity_pos_t z , entity_angle_t a )
{
2010-07-04 10:19:38 -07:00
debug_assert ( TAG_IS_VALID ( tag ) ) ;
2010-03-17 16:01:12 -07:00
2010-04-29 16:36:05 -07:00
if ( TAG_IS_UNIT ( tag ) )
2010-03-17 16:01:12 -07:00
{
2010-04-29 16:36:05 -07:00
UnitShape & shape = m_UnitShapes [ TAG_TO_INDEX ( tag ) ] ;
shape . x = x ;
shape . z = z ;
2010-07-25 07:11:21 -07:00
MakeDirtyUnits ( ) ;
2010-03-17 16:01:12 -07:00
}
else
{
2010-05-02 13:32:37 -07:00
fixed s , c ;
2010-04-29 16:36:05 -07:00
sincos_approx ( a , s , c ) ;
CFixedVector2D u ( c , - s ) ;
CFixedVector2D v ( s , c ) ;
StaticShape & shape = m_StaticShapes [ TAG_TO_INDEX ( tag ) ] ;
shape . x = x ;
shape . z = z ;
shape . u = u ;
shape . v = v ;
2010-03-17 16:01:12 -07:00
2010-07-25 07:11:21 -07:00
MakeDirty ( ) ;
}
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
virtual void SetUnitMovingFlag ( tag_t tag , bool moving )
{
2010-07-04 10:19:38 -07:00
debug_assert ( TAG_IS_VALID ( tag ) & & TAG_IS_UNIT ( tag ) ) ;
2010-04-29 16:36:05 -07:00
if ( TAG_IS_UNIT ( tag ) )
{
UnitShape & shape = m_UnitShapes [ TAG_TO_INDEX ( tag ) ] ;
shape . moving = moving ;
2010-07-25 07:11:21 -07:00
MakeDirtyUnits ( ) ;
2010-04-29 16:36:05 -07:00
}
}
2010-09-03 02:55:14 -07:00
virtual void SetUnitControlGroup ( tag_t tag , entity_id_t group )
{
debug_assert ( TAG_IS_VALID ( tag ) & & TAG_IS_UNIT ( tag ) ) ;
if ( TAG_IS_UNIT ( tag ) )
{
UnitShape & shape = m_UnitShapes [ TAG_TO_INDEX ( tag ) ] ;
shape . group = group ;
MakeDirtyUnits ( ) ;
}
}
2010-03-17 16:01:12 -07:00
virtual void RemoveShape ( tag_t tag )
{
2010-07-04 10:19:38 -07:00
debug_assert ( TAG_IS_VALID ( tag ) ) ;
2010-03-17 16:01:12 -07:00
2010-04-29 16:36:05 -07:00
if ( TAG_IS_UNIT ( tag ) )
2010-07-25 07:11:21 -07:00
{
2010-04-29 16:36:05 -07:00
m_UnitShapes . erase ( TAG_TO_INDEX ( tag ) ) ;
2010-07-25 07:11:21 -07:00
MakeDirtyUnits ( ) ;
}
2010-03-17 16:01:12 -07:00
else
2010-07-25 07:11:21 -07:00
{
2010-04-29 16:36:05 -07:00
m_StaticShapes . erase ( TAG_TO_INDEX ( tag ) ) ;
2010-07-25 07:11:21 -07:00
MakeDirty ( ) ;
}
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
virtual ObstructionSquare GetObstruction ( tag_t tag )
{
2010-07-04 10:19:38 -07:00
debug_assert ( TAG_IS_VALID ( tag ) ) ;
2010-04-29 16:36:05 -07:00
if ( TAG_IS_UNIT ( tag ) )
{
UnitShape & shape = m_UnitShapes [ TAG_TO_INDEX ( tag ) ] ;
CFixedVector2D u ( entity_pos_t : : FromInt ( 1 ) , entity_pos_t : : Zero ( ) ) ;
CFixedVector2D v ( entity_pos_t : : Zero ( ) , entity_pos_t : : FromInt ( 1 ) ) ;
ObstructionSquare o = { shape . x , shape . z , u , v , shape . r , shape . r } ;
return o ;
}
else
{
StaticShape & shape = m_StaticShapes [ TAG_TO_INDEX ( tag ) ] ;
ObstructionSquare o = { shape . x , shape . z , shape . u , shape . v , shape . hw , shape . hh } ;
return o ;
}
}
2010-03-17 16:01:12 -07:00
virtual 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 ) ;
2010-04-29 16:36:05 -07:00
virtual 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 ) ;
virtual bool TestUnitShape ( const IObstructionTestFilter & filter , entity_pos_t x , entity_pos_t z , entity_pos_t r ) ;
2010-03-17 16:01:12 -07:00
virtual bool Rasterise ( Grid < u8 > & grid ) ;
2010-04-29 16:36:05 -07:00
virtual 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 ) ;
2010-07-31 14:22:39 -07:00
virtual bool FindMostImportantObstruction ( const IObstructionTestFilter & filter , entity_pos_t x , entity_pos_t z , entity_pos_t r , ObstructionSquare & square ) ;
2010-03-17 16:01:12 -07:00
2010-03-20 12:18:01 -07:00
virtual void SetDebugOverlay ( bool enabled )
{
m_DebugOverlayEnabled = enabled ;
m_DebugOverlayDirty = true ;
if ( ! enabled )
m_DebugOverlayLines . clear ( ) ;
}
void RenderSubmit ( const CSimContext & context , SceneCollector & collector ) ;
2010-03-17 16:01:12 -07:00
private :
// To support lazy updates of grid rasterisations of obstruction data,
// we maintain a DirtyID here and increment it whenever obstructions change;
// if a grid has a lower DirtyID then it needs to be updated.
size_t m_DirtyID ;
/**
2010-07-25 07:11:21 -07:00
* Mark all previous Rasterise ( ) d grids as dirty , and the debug display .
* Call this when any static shapes or world bounds have changed .
2010-03-17 16:01:12 -07:00
*/
void MakeDirty ( )
{
+ + m_DirtyID ;
2010-03-20 12:18:01 -07:00
m_DebugOverlayDirty = true ;
2010-03-17 16:01:12 -07:00
}
2010-07-25 07:11:21 -07:00
/**
* Mark the debug display as dirty .
* Call this when any unit shapes ( which don ' t affect Rasterise ) have changed .
*/
void MakeDirtyUnits ( )
{
m_DebugOverlayDirty = true ;
}
2010-03-17 16:01:12 -07:00
/**
* Test whether a Rasterise ( ) d grid is dirty and needs updating
*/
template < typename T >
bool IsDirty ( const Grid < T > & grid )
{
return grid . m_DirtyID < m_DirtyID ;
}
2010-07-25 07:11:21 -07:00
/**
* Return whether the given point is within the world bounds by at least r
*/
bool IsInWorld ( entity_pos_t x , entity_pos_t z , entity_pos_t r )
{
return ( m_WorldX0 + r < = x & & x < = m_WorldX1 - r & & m_WorldZ0 + r < = z & & z < = m_WorldZ1 - r ) ;
}
/**
* Return whether the given point is within the world bounds
*/
bool IsInWorld ( CFixedVector2D p )
{
return ( m_WorldX0 < = p . X & & p . X < = m_WorldX1 & & m_WorldZ0 < = p . Y & & p . Y < = m_WorldZ1 ) ;
}
2010-03-17 16:01:12 -07:00
} ;
REGISTER_COMPONENT_TYPE ( ObstructionManager )
bool CCmpObstructionManager : : TestLine ( const IObstructionTestFilter & filter , entity_pos_t x0 , entity_pos_t z0 , entity_pos_t x1 , entity_pos_t z1 , entity_pos_t r )
{
PROFILE ( " TestLine " ) ;
// TODO: this is all very inefficient, it should use some kind of spatial data structures
2010-07-25 07:11:21 -07:00
// Check that both end points are within the world (which means the whole line must be)
if ( ! IsInWorld ( x0 , z0 , r ) | | ! IsInWorld ( x1 , z1 , r ) )
return true ;
2010-04-29 16:36:05 -07:00
for ( std : : map < u32 , UnitShape > : : iterator it = m_UnitShapes . begin ( ) ; it ! = m_UnitShapes . end ( ) ; + + it )
2010-03-17 16:01:12 -07:00
{
2010-09-03 02:55:14 -07:00
if ( ! filter . Allowed ( UNIT_INDEX_TO_TAG ( it - > first ) , it - > second . moving , it - > second . group ) )
2010-03-17 16:01:12 -07:00
continue ;
2010-04-29 16:36:05 -07:00
CFixedVector2D center ( it - > second . x , it - > second . z ) ;
CFixedVector2D halfSize ( it - > second . r + r , it - > second . r + r ) ;
CFixedVector2D u ( entity_pos_t : : FromInt ( 1 ) , entity_pos_t : : Zero ( ) ) ;
CFixedVector2D v ( entity_pos_t : : Zero ( ) , entity_pos_t : : FromInt ( 1 ) ) ;
if ( Geometry : : TestRaySquare ( CFixedVector2D ( x0 , z0 ) - center , CFixedVector2D ( x1 , z1 ) - center , u , v , halfSize ) )
return true ;
// If this is slow we could use a specialised TestRayAlignedSquare for axis-aligned squares
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
for ( std : : map < u32 , StaticShape > : : iterator it = m_StaticShapes . begin ( ) ; it ! = m_StaticShapes . end ( ) ; + + it )
2010-03-17 16:01:12 -07:00
{
2010-09-03 02:55:14 -07:00
if ( ! filter . Allowed ( STATIC_INDEX_TO_TAG ( it - > first ) , false , INVALID_ENTITY ) )
2010-03-17 16:01:12 -07:00
continue ;
2010-04-29 16:36:05 -07:00
CFixedVector2D center ( it - > second . x , it - > second . z ) ;
CFixedVector2D halfSize ( it - > second . hw + r , it - > second . hh + r ) ;
if ( Geometry : : TestRaySquare ( CFixedVector2D ( x0 , z0 ) - center , CFixedVector2D ( x1 , z1 ) - center , it - > second . u , it - > second . v , halfSize ) )
return true ;
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
return false ;
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
bool CCmpObstructionManager : : TestStaticShape ( const IObstructionTestFilter & filter , entity_pos_t x , entity_pos_t z , entity_pos_t a , entity_pos_t w , entity_pos_t h )
2010-03-17 16:01:12 -07:00
{
2010-04-29 16:36:05 -07:00
PROFILE ( " TestStaticShape " ) ;
2010-05-02 13:32:37 -07:00
fixed s , c ;
2010-04-29 16:36:05 -07:00
sincos_approx ( a , s , c ) ;
CFixedVector2D u ( c , - s ) ;
CFixedVector2D v ( s , c ) ;
CFixedVector2D center ( x , z ) ;
CFixedVector2D halfSize ( w / 2 , h / 2 ) ;
2010-03-17 16:01:12 -07:00
2010-07-25 07:11:21 -07:00
// Check that all corners are within the world (which means the whole shape must be)
if ( ! IsInWorld ( center + u . Multiply ( halfSize . X ) + v . Multiply ( halfSize . Y ) ) | |
! IsInWorld ( center + u . Multiply ( halfSize . X ) - v . Multiply ( halfSize . Y ) ) | |
! IsInWorld ( center - u . Multiply ( halfSize . X ) + v . Multiply ( halfSize . Y ) ) | |
! IsInWorld ( center - u . Multiply ( halfSize . X ) - v . Multiply ( halfSize . Y ) ) )
return true ;
2010-04-29 16:36:05 -07:00
for ( std : : map < u32 , UnitShape > : : iterator it = m_UnitShapes . begin ( ) ; it ! = m_UnitShapes . end ( ) ; + + it )
2010-03-17 16:01:12 -07:00
{
2010-09-03 02:55:14 -07:00
if ( ! filter . Allowed ( UNIT_INDEX_TO_TAG ( it - > first ) , it - > second . moving , it - > second . group ) )
2010-03-17 16:01:12 -07:00
continue ;
2010-04-29 16:36:05 -07:00
CFixedVector2D center1 ( it - > second . x , it - > second . z ) ;
if ( Geometry : : PointIsInSquare ( center1 - center , u , v , CFixedVector2D ( halfSize . X + it - > second . r , halfSize . Y + it - > second . r ) ) )
return true ;
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
for ( std : : map < u32 , StaticShape > : : iterator it = m_StaticShapes . begin ( ) ; it ! = m_StaticShapes . end ( ) ; + + it )
2010-03-17 16:01:12 -07:00
{
2010-09-03 02:55:14 -07:00
if ( ! filter . Allowed ( STATIC_INDEX_TO_TAG ( it - > first ) , false , INVALID_ENTITY ) )
2010-03-17 16:01:12 -07:00
continue ;
2010-04-29 16:36:05 -07:00
CFixedVector2D center1 ( it - > second . x , it - > second . z ) ;
CFixedVector2D halfSize1 ( it - > second . hw , it - > second . hh ) ;
if ( Geometry : : TestSquareSquare ( center , u , v , halfSize , center1 , it - > second . u , it - > second . v , halfSize1 ) )
return true ;
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
return false ;
2010-03-17 16:01:12 -07:00
}
2010-04-29 16:36:05 -07:00
bool CCmpObstructionManager : : TestUnitShape ( const IObstructionTestFilter & filter , entity_pos_t x , entity_pos_t z , entity_pos_t r )
2010-03-17 16:01:12 -07:00
{
2010-04-29 16:36:05 -07:00
PROFILE ( " TestUnitShape " ) ;
2010-07-25 07:11:21 -07:00
// Check that the shape is within the world
if ( ! IsInWorld ( x , z , r ) )
return true ;
2010-04-29 16:36:05 -07:00
CFixedVector2D center ( x , z ) ;
for ( std : : map < u32 , UnitShape > : : iterator it = m_UnitShapes . begin ( ) ; it ! = m_UnitShapes . end ( ) ; + + it )
{
2010-09-03 02:55:14 -07:00
if ( ! filter . Allowed ( UNIT_INDEX_TO_TAG ( it - > first ) , it - > second . moving , it - > second . group ) )
2010-04-29 16:36:05 -07:00
continue ;
entity_pos_t r1 = it - > second . r ;
if ( ! ( it - > second . x + r1 < x - r | | it - > second . x - r1 > x + r | | it - > second . z + r1 < z - r | | it - > second . z - r1 > z + r ) )
return true ;
}
for ( std : : map < u32 , StaticShape > : : iterator it = m_StaticShapes . begin ( ) ; it ! = m_StaticShapes . end ( ) ; + + it )
{
2010-09-03 02:55:14 -07:00
if ( ! filter . Allowed ( STATIC_INDEX_TO_TAG ( it - > first ) , false , INVALID_ENTITY ) )
2010-04-29 16:36:05 -07:00
continue ;
CFixedVector2D center1 ( it - > second . x , it - > second . z ) ;
if ( Geometry : : PointIsInSquare ( center1 - center , it - > second . u , it - > second . v , CFixedVector2D ( it - > second . hw + r , it - > second . hh + r ) ) )
return true ;
}
return false ;
2010-03-17 16:01:12 -07:00
}
/**
* Compute the tile indexes on the grid nearest to a given point
*/
static void NearestTile ( entity_pos_t x , entity_pos_t z , u16 & i , u16 & j , u16 w , u16 h )
{
2010-04-29 16:36:05 -07:00
i = clamp ( ( x / ( int ) CELL_SIZE ) . ToInt_RoundToZero ( ) , 0 , w - 1 ) ;
j = clamp ( ( z / ( int ) CELL_SIZE ) . ToInt_RoundToZero ( ) , 0 , h - 1 ) ;
}
/**
* Returns the position of the center of the given tile
*/
static void TileCenter ( u16 i , u16 j , entity_pos_t & x , entity_pos_t & z )
{
x = entity_pos_t : : FromInt ( i * ( int ) CELL_SIZE + CELL_SIZE / 2 ) ;
z = entity_pos_t : : FromInt ( j * ( int ) CELL_SIZE + CELL_SIZE / 2 ) ;
2010-03-17 16:01:12 -07:00
}
bool CCmpObstructionManager : : Rasterise ( Grid < u8 > & grid )
{
if ( ! IsDirty ( grid ) )
return false ;
2010-07-25 07:11:21 -07:00
PROFILE ( " Rasterise " ) ;
2010-03-17 16:01:12 -07:00
grid . m_DirtyID = m_DirtyID ;
// TODO: this is all hopelessly inefficient
// What we should perhaps do is have some kind of quadtree storing Shapes so it's
// quick to invalidate and update small numbers of tiles
grid . reset ( ) ;
2010-04-29 16:36:05 -07:00
for ( std : : map < u32 , StaticShape > : : iterator it = m_StaticShapes . begin ( ) ; it ! = m_StaticShapes . end ( ) ; + + it )
2010-03-17 16:01:12 -07:00
{
2010-04-29 16:36:05 -07:00
CFixedVector2D center ( it - > second . x , it - > second . z ) ;
// Since we only count tiles whose centers are inside the square,
// we maybe want to expand the square a bit so we're less likely to think there's
// free space between buildings when there isn't. But this is just a random guess
// and needs to be tweaked until everything works nicely.
2010-08-14 15:11:32 -07:00
//entity_pos_t expand = entity_pos_t::FromInt(CELL_SIZE / 2);
// Actually that's bad because units get stuck when the A* pathfinder thinks they're
// blocked on all sides, so it's better to underestimate
entity_pos_t expand = entity_pos_t : : FromInt ( 0 ) ;
2010-04-29 16:36:05 -07:00
CFixedVector2D halfSize ( it - > second . hw + expand , it - > second . hh + expand ) ;
CFixedVector2D halfBound = Geometry : : GetHalfBoundingBox ( it - > second . u , it - > second . v , halfSize ) ;
2010-03-17 16:01:12 -07:00
u16 i0 , j0 , i1 , j1 ;
2010-04-29 16:36:05 -07:00
NearestTile ( center . X - halfBound . X , center . Y - halfBound . Y , i0 , j0 , grid . m_W , grid . m_H ) ;
NearestTile ( center . X + halfBound . X , center . Y + halfBound . Y , i1 , j1 , grid . m_W , grid . m_H ) ;
2010-03-17 16:01:12 -07:00
for ( u16 j = j0 ; j < = j1 ; + + j )
2010-04-29 16:36:05 -07:00
{
2010-03-17 16:01:12 -07:00
for ( u16 i = i0 ; i < = i1 ; + + i )
2010-04-29 16:36:05 -07:00
{
entity_pos_t x , z ;
TileCenter ( i , j , x , z ) ;
if ( Geometry : : PointIsInSquare ( CFixedVector2D ( x , z ) - center , it - > second . u , it - > second . v , halfSize ) )
2010-07-25 07:11:21 -07:00
grid . set ( i , j , TILE_OBSTRUCTED ) ;
2010-04-29 16:36:05 -07:00
}
}
2010-03-17 16:01:12 -07:00
}
2010-07-25 07:11:21 -07:00
// Any tiles outside or very near the edge of the map are impassable
{
u16 i0 , j0 , i1 , j1 ;
NearestTile ( m_WorldX0 , m_WorldZ0 , i0 , j0 , grid . m_W , grid . m_H ) ;
NearestTile ( m_WorldX1 , m_WorldZ1 , i1 , j1 , grid . m_W , grid . m_H ) ;
for ( u16 j = 0 ; j < grid . m_H ; + + j )
for ( u16 i = 0 ; i < = i0 ; + + i )
grid . set ( i , j , TILE_OBSTRUCTED | TILE_OUTOFBOUNDS ) ;
for ( u16 j = 0 ; j < grid . m_H ; + + j )
for ( u16 i = i1 ; i < grid . m_W ; + + i )
grid . set ( i , j , TILE_OBSTRUCTED | TILE_OUTOFBOUNDS ) ;
for ( u16 j = 0 ; j < = j0 ; + + j )
for ( u16 i = i0 ; i < = i1 ; + + i )
grid . set ( i , j , TILE_OBSTRUCTED | TILE_OUTOFBOUNDS ) ;
for ( u16 j = j1 ; j < grid . m_H ; + + j )
for ( u16 i = i0 ; i < = i1 ; + + i )
grid . set ( i , j , TILE_OBSTRUCTED | TILE_OUTOFBOUNDS ) ;
}
2010-03-17 16:01:12 -07:00
return true ;
}
2010-03-20 12:18:01 -07:00
2010-04-29 16:36:05 -07:00
void CCmpObstructionManager : : GetObstructionsInRange ( const IObstructionTestFilter & filter , entity_pos_t x0 , entity_pos_t z0 , entity_pos_t x1 , entity_pos_t z1 , std : : vector < ObstructionSquare > & squares )
{
// TODO: this should be made faster with quadtrees or whatever
PROFILE ( " GetObstructionsInRange " ) ;
for ( std : : map < u32 , UnitShape > : : iterator it = m_UnitShapes . begin ( ) ; it ! = m_UnitShapes . end ( ) ; + + it )
{
2010-09-03 02:55:14 -07:00
if ( ! filter . Allowed ( UNIT_INDEX_TO_TAG ( it - > first ) , it - > second . moving , it - > second . group ) )
2010-04-29 16:36:05 -07:00
continue ;
entity_pos_t r = it - > second . r ;
// Skip this object if it's completely outside the requested range
if ( it - > second . x + r < x0 | | it - > second . x - r > x1 | | it - > second . z + r < z0 | | it - > second . z - r > z1 )
continue ;
CFixedVector2D u ( entity_pos_t : : FromInt ( 1 ) , entity_pos_t : : Zero ( ) ) ;
CFixedVector2D v ( entity_pos_t : : Zero ( ) , entity_pos_t : : FromInt ( 1 ) ) ;
ObstructionSquare s = { it - > second . x , it - > second . z , u , v , r , r } ;
squares . push_back ( s ) ;
}
for ( std : : map < u32 , StaticShape > : : iterator it = m_StaticShapes . begin ( ) ; it ! = m_StaticShapes . end ( ) ; + + it )
{
2010-09-03 02:55:14 -07:00
if ( ! filter . Allowed ( STATIC_INDEX_TO_TAG ( it - > first ) , false , INVALID_ENTITY ) )
2010-04-29 16:36:05 -07:00
continue ;
entity_pos_t r = it - > second . hw + it - > second . hh ; // overestimate the max dist of an edge from the center
// Skip this object if its overestimated bounding box is completely outside the requested range
if ( it - > second . x + r < x0 | | it - > second . x - r > x1 | | it - > second . z + r < z0 | | it - > second . z - r > z1 )
continue ;
// TODO: maybe we should use Geometry::GetHalfBoundingBox to be more precise?
ObstructionSquare s = { it - > second . x , it - > second . z , it - > second . u , it - > second . v , it - > second . hw , it - > second . hh } ;
squares . push_back ( s ) ;
}
}
2010-07-31 14:22:39 -07:00
bool CCmpObstructionManager : : FindMostImportantObstruction ( const IObstructionTestFilter & filter , entity_pos_t x , entity_pos_t z , entity_pos_t r , ObstructionSquare & square )
2010-04-29 16:36:05 -07:00
{
std : : vector < ObstructionSquare > squares ;
CFixedVector2D center ( x , z ) ;
// First look for obstructions that are covering the exact target point
GetObstructionsInRange ( filter , x , z , x , z , squares ) ;
// Building squares are more important but returned last, so check backwards
for ( std : : vector < ObstructionSquare > : : reverse_iterator it = squares . rbegin ( ) ; it ! = squares . rend ( ) ; + + it )
{
CFixedVector2D halfSize ( it - > hw , it - > hh ) ;
if ( Geometry : : PointIsInSquare ( CFixedVector2D ( it - > x , it - > z ) - center , it - > u , it - > v , halfSize ) )
{
square = * it ;
return true ;
}
}
// Then look for obstructions that cover the target point when expanded by r
// (i.e. if the target is not inside an object but closer than we can get to it)
// TODO: actually do that
// (This might matter when you tell a unit to walk too close to the edge of a building)
return false ;
}
2010-03-20 12:18:01 -07:00
void CCmpObstructionManager : : RenderSubmit ( const CSimContext & context , SceneCollector & collector )
{
if ( ! m_DebugOverlayEnabled )
return ;
CColor defaultColour ( 0 , 0 , 1 , 1 ) ;
2010-04-29 16:36:05 -07:00
CColor movingColour ( 1 , 0 , 1 , 1 ) ;
2010-07-25 07:11:21 -07:00
CColor boundsColour ( 1 , 1 , 0 , 1 ) ;
2010-03-20 12:18:01 -07:00
// If the shapes have changed, then regenerate all the overlays
if ( m_DebugOverlayDirty )
{
m_DebugOverlayLines . clear ( ) ;
2010-07-25 07:11:21 -07:00
m_DebugOverlayLines . push_back ( SOverlayLine ( ) ) ;
m_DebugOverlayLines . back ( ) . m_Color = boundsColour ;
SimRender : : ConstructSquareOnGround ( context ,
( m_WorldX0 + m_WorldX1 ) . ToFloat ( ) / 2.f , ( m_WorldZ0 + m_WorldZ1 ) . ToFloat ( ) / 2.f ,
( m_WorldX1 - m_WorldX0 ) . ToFloat ( ) , ( m_WorldZ1 - m_WorldZ0 ) . ToFloat ( ) ,
0 , m_DebugOverlayLines . back ( ) , true ) ;
2010-04-29 16:36:05 -07:00
for ( std : : map < u32 , UnitShape > : : iterator it = m_UnitShapes . begin ( ) ; it ! = m_UnitShapes . end ( ) ; + + it )
2010-03-20 12:18:01 -07:00
{
m_DebugOverlayLines . push_back ( SOverlayLine ( ) ) ;
2010-04-29 16:36:05 -07:00
m_DebugOverlayLines . back ( ) . m_Color = ( it - > second . moving ? movingColour : defaultColour ) ;
2010-05-27 16:23:53 -07:00
SimRender : : ConstructSquareOnGround ( context , it - > second . x . ToFloat ( ) , it - > second . z . ToFloat ( ) , it - > second . r . ToFloat ( ) * 2 , it - > second . r . ToFloat ( ) * 2 , 0 , m_DebugOverlayLines . back ( ) , true ) ;
2010-03-20 12:18:01 -07:00
}
2010-04-29 16:36:05 -07:00
for ( std : : map < u32 , StaticShape > : : iterator it = m_StaticShapes . begin ( ) ; it ! = m_StaticShapes . end ( ) ; + + it )
2010-03-20 12:18:01 -07:00
{
m_DebugOverlayLines . push_back ( SOverlayLine ( ) ) ;
m_DebugOverlayLines . back ( ) . m_Color = defaultColour ;
2010-04-29 16:36:05 -07:00
float a = atan2 ( it - > second . v . X . ToFloat ( ) , it - > second . v . Y . ToFloat ( ) ) ;
2010-05-27 16:23:53 -07:00
SimRender : : ConstructSquareOnGround ( context , it - > second . x . ToFloat ( ) , it - > second . z . ToFloat ( ) , it - > second . hw . ToFloat ( ) * 2 , it - > second . hh . ToFloat ( ) * 2 , a , m_DebugOverlayLines . back ( ) , true ) ;
2010-03-20 12:18:01 -07:00
}
m_DebugOverlayDirty = false ;
}
for ( size_t i = 0 ; i < m_DebugOverlayLines . size ( ) ; + + i )
collector . Submit ( & m_DebugOverlayLines [ i ] ) ;
}