2017-01-19 18:25:19 -08:00
/* Copyright (C) 2017 Wildfire Games.
2010-01-09 11:20:14 -08:00
* This file is part of 0 A . D .
*
* 0 A . D . is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* 0 A . D . is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "precompiled.h"
# include "ICmpSelectable.h"
# include "graphics/Overlay.h"
2012-04-21 21:04:02 -07:00
# include "graphics/Terrain.h"
# include "graphics/TextureManager.h"
# include "maths/Ease.h"
2010-01-09 11:20:14 -08:00
# include "maths/MathUtil.h"
# include "maths/Matrix3D.h"
# include "maths/Vector3D.h"
2012-04-21 21:04:02 -07:00
# include "maths/Vector2D.h"
2016-06-21 03:33:11 -07:00
# include "ps/Profile.h"
2010-01-09 11:20:14 -08:00
# include "renderer/Scene.h"
2012-04-21 21:04:02 -07:00
# include "renderer/Renderer.h"
# include "simulation2/MessageTypes.h"
# include "simulation2/components/ICmpPosition.h"
# include "simulation2/components/ICmpFootprint.h"
# include "simulation2/components/ICmpVisual.h"
# include "simulation2/components/ICmpTerrain.h"
# include "simulation2/components/ICmpOwnership.h"
# include "simulation2/components/ICmpPlayer.h"
# include "simulation2/components/ICmpPlayerManager.h"
# include "simulation2/components/ICmpWaterManager.h"
# include "simulation2/helpers/Render.h"
2012-06-06 12:37:03 -07:00
# include "simulation2/system/Component.h"
2010-01-09 11:20:14 -08:00
2012-07-18 21:45:01 -07:00
// Minimum alpha value for always visible overlays [0 fully transparent, 1 fully opaque]
static const float MIN_ALPHA_ALWAYS_VISIBLE = 0.65f ;
// Minimum alpha value for other overlays
static const float MIN_ALPHA_UNSELECTED = 0.0f ;
// Desaturation value for unselected, always visible overlays (0.33 = 33% desaturated or 66% of original saturation)
static const float RGB_DESATURATION = 0.333333f ;
2010-01-09 11:20:14 -08:00
class CCmpSelectable : public ICmpSelectable
{
public :
static void ClassInit ( CComponentManager & componentManager )
{
2012-04-21 21:04:02 -07:00
componentManager . SubscribeToMessageType ( MT_OwnershipChanged ) ;
2017-08-09 04:19:14 -07:00
componentManager . SubscribeToMessageType ( MT_PlayerColorChanged ) ;
2012-04-21 21:04:02 -07:00
componentManager . SubscribeToMessageType ( MT_PositionChanged ) ;
2014-05-26 06:45:49 -07:00
componentManager . SubscribeToMessageType ( MT_TerrainChanged ) ;
componentManager . SubscribeToMessageType ( MT_WaterChanged ) ;
2010-01-09 11:20:14 -08:00
}
DEFAULT_COMPONENT_ALLOCATOR ( Selectable )
CCmpSelectable ( )
2016-11-23 05:02:58 -08:00
: m_DebugBoundingBoxOverlay ( NULL ) , m_DebugSelectionBoxOverlay ( NULL ) ,
2017-05-05 17:47:21 -07:00
m_BuildingOverlay ( NULL ) , m_UnitOverlay ( NULL ) , m_RangeOverlayData ( ) ,
2012-07-18 21:45:01 -07:00
m_FadeBaselineAlpha ( 0.f ) , m_FadeDeltaAlpha ( 0.f ) , m_FadeProgress ( 0.f ) ,
2012-07-30 14:06:54 -07:00
m_Selected ( false ) , m_Cached ( false ) , m_Visible ( false )
2010-01-09 11:20:14 -08:00
{
2012-04-21 21:04:02 -07:00
m_Color = CColor ( 0 , 0 , 0 , m_FadeBaselineAlpha ) ;
2010-01-09 11:20:14 -08:00
}
2012-04-21 21:04:02 -07:00
~ CCmpSelectable ( )
{
2011-11-24 22:36:13 -08:00
delete m_DebugBoundingBoxOverlay ;
delete m_DebugSelectionBoxOverlay ;
2012-04-21 21:04:02 -07:00
delete m_BuildingOverlay ;
delete m_UnitOverlay ;
2017-05-05 17:47:21 -07:00
for ( RangeOverlayData & rangeOverlay : m_RangeOverlayData )
delete rangeOverlay . second ;
2011-11-24 22:36:13 -08:00
}
2010-04-23 09:09:03 -07:00
static std : : string GetSchema ( )
{
return
" <a:help>Allows this entity to be selected by the player.</a:help> "
" <a:example/> "
2012-02-26 21:32:35 -08:00
" <optional> "
" <element name='EditorOnly' a:help='If this element is present, the entity is only selectable in Atlas'> "
" <empty/> "
" </element> "
2012-04-21 21:04:02 -07:00
" </optional> "
" <element name='Overlay' a:help='Specifies the type of overlay to be displayed when this entity is selected'> "
2012-07-18 21:45:01 -07:00
" <optional> "
" <element name='AlwaysVisible' a:help='If this element is present, the selection overlay will always be visible (with transparency and desaturation)'> "
" <empty/> "
" </element> "
" </optional> "
2012-04-21 21:04:02 -07:00
" <choice> "
" <element name='Texture' a:help='Displays a texture underneath the entity.'> "
" <element name='MainTexture' a:help='Texture to display underneath the entity. Filepath relative to art/textures/selection/.'><text/></element> "
" <element name='MainTextureMask' a:help='Mask texture that controls where to apply player color. Filepath relative to art/textures/selection/.'><text/></element> "
" </element> "
" <element name='Outline' a:help='Traces the outline of the entity with a line texture.'> "
" <element name='LineTexture' a:help='Texture to apply to the line. Filepath relative to art/textures/selection/.'><text/></element> "
" <element name='LineTextureMask' a:help='Texture that controls where to apply player color. Filepath relative to art/textures/selection/.'><text/></element> "
" <element name='LineThickness' a:help='Thickness of the line, in world units.'><ref name='positiveDecimal'/></element> "
" </element> "
" </choice> "
" </element> " ;
2010-04-23 09:09:03 -07:00
}
2012-02-26 21:32:35 -08:00
virtual void Init ( const CParamNode & paramNode )
2010-01-09 11:20:14 -08:00
{
2012-02-26 21:32:35 -08:00
m_EditorOnly = paramNode . GetChild ( " EditorOnly " ) . IsOk ( ) ;
2010-01-09 11:20:14 -08:00
2012-07-18 21:45:01 -07:00
// Certain special units always have their selection overlay shown
m_AlwaysVisible = paramNode . GetChild ( " Overlay " ) . GetChild ( " AlwaysVisible " ) . IsOk ( ) ;
if ( m_AlwaysVisible )
{
m_AlphaMin = MIN_ALPHA_ALWAYS_VISIBLE ;
m_Color . a = m_AlphaMin ;
}
else
m_AlphaMin = MIN_ALPHA_UNSELECTED ;
2012-04-21 21:04:02 -07:00
const CParamNode & textureNode = paramNode . GetChild ( " Overlay " ) . GetChild ( " Texture " ) ;
const CParamNode & outlineNode = paramNode . GetChild ( " Overlay " ) . GetChild ( " Outline " ) ;
// Save some memory by using interned file paths in these descriptors (almost all actors and
// entities have this component, and many use the same textures).
if ( textureNode . IsOk ( ) )
{
// textured quad mode (dynamic, for units)
2017-12-09 11:49:56 -08:00
m_OverlayDescriptor . m_Type = DYNAMIC_QUAD ;
2017-05-05 17:47:21 -07:00
m_OverlayDescriptor . m_QuadTexture = CStrIntern ( TEXTUREBASEPATH + textureNode . GetChild ( " MainTexture " ) . ToUTF8 ( ) ) ;
m_OverlayDescriptor . m_QuadTextureMask = CStrIntern ( TEXTUREBASEPATH + textureNode . GetChild ( " MainTextureMask " ) . ToUTF8 ( ) ) ;
2012-04-21 21:04:02 -07:00
}
else if ( outlineNode . IsOk ( ) )
{
// textured outline mode (static, for buildings)
2017-12-09 11:49:56 -08:00
m_OverlayDescriptor . m_Type = STATIC_OUTLINE ;
2017-05-05 17:47:21 -07:00
m_OverlayDescriptor . m_LineTexture = CStrIntern ( TEXTUREBASEPATH + outlineNode . GetChild ( " LineTexture " ) . ToUTF8 ( ) ) ;
m_OverlayDescriptor . m_LineTextureMask = CStrIntern ( TEXTUREBASEPATH + outlineNode . GetChild ( " LineTextureMask " ) . ToUTF8 ( ) ) ;
2012-04-21 21:04:02 -07:00
m_OverlayDescriptor . m_LineThickness = outlineNode . GetChild ( " LineThickness " ) . ToFloat ( ) ;
}
2014-06-19 16:20:12 -07:00
m_EnabledInterpolate = false ;
m_EnabledRenderSubmit = false ;
UpdateMessageSubscriptions ( ) ;
2010-01-09 11:20:14 -08:00
}
2012-04-21 21:04:02 -07:00
virtual void Deinit ( ) { }
2010-01-09 11:20:14 -08:00
virtual void Serialize ( ISerializer & UNUSED ( serialize ) )
{
// Nothing to do here (the overlay object is not worth saving, it'll get
// reconstructed by the GUI soon enough, I think)
}
2012-02-28 14:12:30 -08:00
virtual void Deserialize ( const CParamNode & paramNode , IDeserializer & UNUSED ( deserialize ) )
2010-01-09 11:20:14 -08:00
{
2012-02-28 14:12:30 -08:00
// Need to call Init to reload the template properties
Init ( paramNode ) ;
2010-01-09 11:20:14 -08:00
}
2012-04-21 21:04:02 -07:00
virtual void HandleMessage ( const CMessage & msg , bool UNUSED ( global ) ) ;
2016-01-23 07:17:56 -08:00
virtual void SetSelectionHighlight ( const CColor & color , bool selected )
2010-01-09 11:20:14 -08:00
{
2012-07-18 21:45:01 -07:00
m_Selected = selected ;
2012-04-21 21:04:02 -07:00
m_Color . r = color . r ;
m_Color . g = color . g ;
m_Color . b = color . b ;
2012-07-18 21:45:01 -07:00
// Always-visible overlays will be desaturated if their parent unit is deselected.
if ( m_AlwaysVisible & & ! selected )
{
float max ;
// Reduce saturation by one-third, the quick-and-dirty way.
if ( m_Color . r > m_Color . b )
max = ( m_Color . r > m_Color . g ) ? m_Color . r : m_Color . g ;
else
max = ( m_Color . b > m_Color . g ) ? m_Color . b : m_Color . g ;
m_Color . r + = ( max - m_Color . r ) * RGB_DESATURATION ;
m_Color . g + = ( max - m_Color . g ) * RGB_DESATURATION ;
m_Color . b + = ( max - m_Color . b ) * RGB_DESATURATION ;
}
2012-04-22 10:49:43 -07:00
SetSelectionHighlightAlpha ( color . a ) ;
}
2012-04-21 21:04:02 -07:00
2017-05-05 17:47:21 -07:00
virtual void AddRangeOverlay ( float radius , const std : : string & texture , const std : : string & textureMask , float thickness )
{
if ( ! CRenderer : : IsInitialised ( ) )
return ;
SOverlayDescriptor rangeOverlayDescriptor ;
SOverlayTexturedLine * rangeOverlay = nullptr ;
rangeOverlayDescriptor . m_Radius = radius ;
rangeOverlayDescriptor . m_LineTexture = CStrIntern ( TEXTUREBASEPATH + texture ) ;
rangeOverlayDescriptor . m_LineTextureMask = CStrIntern ( TEXTUREBASEPATH + textureMask ) ;
rangeOverlayDescriptor . m_LineThickness = thickness ;
m_RangeOverlayData . push_back ( { rangeOverlayDescriptor , rangeOverlay } ) ;
}
2012-04-22 10:49:43 -07:00
virtual void SetSelectionHighlightAlpha ( float alpha )
{
2012-07-18 21:45:01 -07:00
alpha = std : : max ( m_AlphaMin , alpha ) ;
2012-04-21 21:04:02 -07:00
// set up fading from the current value (as the baseline) to the target value
m_FadeBaselineAlpha = m_Color . a ;
2012-04-22 10:49:43 -07:00
m_FadeDeltaAlpha = alpha - m_FadeBaselineAlpha ;
2012-04-21 21:04:02 -07:00
m_FadeProgress = 0.f ;
2014-06-19 16:20:12 -07:00
UpdateMessageSubscriptions ( ) ;
2012-04-21 21:04:02 -07:00
}
2012-07-30 14:06:54 -07:00
virtual void SetVisibility ( bool visible )
{
m_Visible = visible ;
2014-06-19 16:20:12 -07:00
UpdateMessageSubscriptions ( ) ;
2012-07-30 14:06:54 -07:00
}
2017-01-19 18:25:19 -08:00
virtual bool IsEditorOnly ( ) const
2012-04-21 21:04:02 -07:00
{
return m_EditorOnly ;
}
void RenderSubmit ( SceneCollector & collector ) ;
/**
2017-05-05 17:47:21 -07:00
* Draw a textured line overlay . The selection overlays for structures are based solely on footprint shape .
2012-04-21 21:04:02 -07:00
*/
2017-05-05 17:47:21 -07:00
void UpdateTexturedLineOverlay ( const SOverlayDescriptor * overlayDescriptor , SOverlayTexturedLine & overlay , float frameOffset , bool buildingOverlay ) ;
2012-04-21 21:04:02 -07:00
/**
* Called from the interpolation handler ; responsible for ensuring the dynamic overlay ( provided we ' re
* using one ) is up - to - date and ready to be submitted to the next rendering run .
*/
void UpdateDynamicOverlay ( float frameOffset ) ;
/// Explicitly invalidates the static overlay.
void InvalidateStaticOverlay ( ) ;
2014-06-19 16:20:12 -07:00
/**
* Subscribe / unsubscribe to MT_Interpolate , MT_RenderSubmit , depending on
* whether we will do any actual work when receiving them . ( This is to avoid
* the performance cost of receiving messages in the typical case when the
* entity is not selected . )
*
* Must be called after changing m_Visible , m_FadeDeltaAlpha , m_Color . a
*/
void UpdateMessageSubscriptions ( ) ;
2017-05-05 17:47:21 -07:00
/**
* Delete all range overlays .
*/
void ResetRangeOverlays ( ) ;
2017-08-09 04:19:14 -07:00
/**
* Set the color of the current owner .
*/
void UpdatePlayerColor ( ) ;
2012-04-21 21:04:02 -07:00
private :
SOverlayDescriptor m_OverlayDescriptor ;
SOverlayTexturedLine * m_BuildingOverlay ;
SOverlayQuad * m_UnitOverlay ;
2017-05-05 17:47:21 -07:00
// Holds the data for all range overlays
typedef std : : pair < SOverlayDescriptor , SOverlayTexturedLine * > RangeOverlayData ;
std : : vector < RangeOverlayData > m_RangeOverlayData ;
2012-04-21 21:04:02 -07:00
SOverlayLine * m_DebugBoundingBoxOverlay ;
SOverlayLine * m_DebugSelectionBoxOverlay ;
2014-06-19 16:20:12 -07:00
bool m_EnabledInterpolate ;
bool m_EnabledRenderSubmit ;
2012-07-30 14:06:54 -07:00
// Whether the selectable will be rendered.
bool m_Visible ;
2012-07-18 21:45:01 -07:00
// Whether the entity is only selectable in Atlas editor
2012-04-21 21:04:02 -07:00
bool m_EditorOnly ;
2012-07-18 21:45:01 -07:00
// Whether the selection overlay is always visible
bool m_AlwaysVisible ;
/// Whether the parent entity is selected (caches GUI's selection state).
bool m_Selected ;
2012-04-21 21:04:02 -07:00
/// Current selection overlay color. Alpha component is subject to fading.
CColor m_Color ;
2015-03-15 16:59:48 -07:00
/// Whether the selectable's player color has been cached for rendering.
2012-07-18 21:45:01 -07:00
bool m_Cached ;
/// Minimum value for current selection overlay alpha.
float m_AlphaMin ;
2012-04-21 21:04:02 -07:00
/// Baseline alpha value to start fading from. Constant during a single fade.
float m_FadeBaselineAlpha ;
/// Delta between target and baseline alpha. Constant during a single fade. Can be positive or negative.
float m_FadeDeltaAlpha ;
/// Linear time progress of the fade, between 0 and m_FadeDuration.
float m_FadeProgress ;
2012-06-06 12:37:03 -07:00
2012-04-21 21:04:02 -07:00
/// Total duration of a single fade, in seconds. Assumed constant for now; feel free to change this into
/// a member variable if you need to adjust it per component.
static const double FADE_DURATION ;
2017-05-05 17:47:21 -07:00
static const char * TEXTUREBASEPATH ;
2012-04-21 21:04:02 -07:00
} ;
const double CCmpSelectable : : FADE_DURATION = 0.3 ;
2017-05-05 17:47:21 -07:00
const char * CCmpSelectable : : TEXTUREBASEPATH = " art/textures/selection/ " ;
2012-04-21 21:04:02 -07:00
void CCmpSelectable : : HandleMessage ( const CMessage & msg , bool UNUSED ( global ) )
{
switch ( msg . GetType ( ) )
{
case MT_Interpolate :
2015-02-07 07:38:22 -08:00
{
2016-06-25 06:12:35 -07:00
PROFILE ( " Selectable::Interpolate " ) ;
2014-06-19 16:20:12 -07:00
2015-02-07 07:38:22 -08:00
const CMessageInterpolate & msgData = static_cast < const CMessageInterpolate & > ( msg ) ;
2012-04-21 21:04:02 -07:00
2015-02-07 07:38:22 -08:00
if ( m_FadeDeltaAlpha ! = 0.f )
{
m_FadeProgress + = msgData . deltaRealTime ;
if ( m_FadeProgress > = FADE_DURATION )
2010-01-09 11:20:14 -08:00
{
2015-02-07 07:38:22 -08:00
const float targetAlpha = m_FadeBaselineAlpha + m_FadeDeltaAlpha ;
// stop the fade
m_Color . a = targetAlpha ;
m_FadeBaselineAlpha = targetAlpha ;
m_FadeDeltaAlpha = 0.f ;
m_FadeProgress = FADE_DURATION ; // will need to be reset to start the next fade again
2010-01-09 11:20:14 -08:00
}
2015-02-07 07:38:22 -08:00
else
{
m_Color . a = Ease : : QuartOut ( m_FadeProgress , m_FadeBaselineAlpha , m_FadeDeltaAlpha , FADE_DURATION ) ;
}
}
2012-04-21 21:04:02 -07:00
2015-02-07 07:38:22 -08:00
// update dynamic overlay only when visible
if ( m_Color . a > 0 )
2017-05-05 17:47:21 -07:00
{
2015-02-07 07:38:22 -08:00
UpdateDynamicOverlay ( msgData . offset ) ;
2012-04-21 21:04:02 -07:00
2017-05-05 17:47:21 -07:00
for ( RangeOverlayData & rangeOverlay : m_RangeOverlayData )
{
delete rangeOverlay . second ;
rangeOverlay . second = new SOverlayTexturedLine ;
UpdateTexturedLineOverlay ( & rangeOverlay . first , * rangeOverlay . second , msgData . offset , false ) ;
}
}
2015-02-07 07:38:22 -08:00
UpdateMessageSubscriptions ( ) ;
2014-06-19 16:20:12 -07:00
2015-02-07 07:38:22 -08:00
break ;
}
2016-11-23 05:02:58 -08:00
case MT_OwnershipChanged :
2015-02-07 07:38:22 -08:00
{
const CMessageOwnershipChanged & msgData = static_cast < const CMessageOwnershipChanged & > ( msg ) ;
2012-04-21 21:04:02 -07:00
2017-08-11 13:05:03 -07:00
// Ignore newly constructed entities, as they receive their color upon first selection
// Ignore deleted entities because they won't be rendered
if ( msgData . from = = INVALID_PLAYER | | msgData . to = = INVALID_PLAYER )
2015-02-07 07:38:22 -08:00
break ;
2012-04-21 21:04:02 -07:00
2017-08-09 04:19:14 -07:00
UpdatePlayerColor ( ) ;
InvalidateStaticOverlay ( ) ;
break ;
}
case MT_PlayerColorChanged :
{
const CMessagePlayerColorChanged & msgData = static_cast < const CMessagePlayerColorChanged & > ( msg ) ;
2012-04-21 21:04:02 -07:00
2017-08-09 04:19:14 -07:00
CmpPtr < ICmpOwnership > cmpOwnership ( GetEntityHandle ( ) ) ;
if ( ! cmpOwnership | | msgData . player ! = cmpOwnership - > GetOwner ( ) )
2015-02-07 07:38:22 -08:00
break ;
2012-04-21 21:04:02 -07:00
2017-08-09 04:19:14 -07:00
UpdatePlayerColor ( ) ;
2015-02-07 07:38:22 -08:00
break ;
}
2012-04-21 21:04:02 -07:00
case MT_PositionChanged :
2015-02-07 07:38:22 -08:00
{
2017-06-15 10:45:16 -07:00
if ( m_AlwaysVisible )
{
const CMessagePositionChanged & msgData = static_cast < const CMessagePositionChanged & > ( msg ) ;
if ( ! msgData . inWorld )
m_Color . a = m_AlphaMin = MIN_ALPHA_UNSELECTED ;
else if ( ! m_Selected )
m_Color . a = m_AlphaMin = MIN_ALPHA_ALWAYS_VISIBLE ;
}
2015-02-07 07:38:22 -08:00
InvalidateStaticOverlay ( ) ;
break ;
}
2014-05-26 06:45:49 -07:00
case MT_TerrainChanged :
case MT_WaterChanged :
2015-02-07 07:38:22 -08:00
InvalidateStaticOverlay ( ) ;
break ;
2012-04-21 21:04:02 -07:00
case MT_RenderSubmit :
2015-02-07 07:38:22 -08:00
{
2016-06-25 06:12:35 -07:00
PROFILE ( " Selectable::RenderSubmit " ) ;
2014-06-19 16:20:12 -07:00
2015-02-07 07:38:22 -08:00
const CMessageRenderSubmit & msgData = static_cast < const CMessageRenderSubmit & > ( msg ) ;
RenderSubmit ( msgData . collector ) ;
2012-04-21 21:04:02 -07:00
2015-02-07 07:38:22 -08:00
break ;
}
2010-01-09 11:20:14 -08:00
}
2012-04-21 21:04:02 -07:00
}
2010-01-09 11:20:14 -08:00
2017-08-09 04:19:14 -07:00
void CCmpSelectable : : UpdatePlayerColor ( )
{
CmpPtr < ICmpOwnership > cmpOwnership ( GetEntityHandle ( ) ) ;
CmpPtr < ICmpPlayerManager > cmpPlayerManager ( GetSystemEntity ( ) ) ;
if ( ! cmpPlayerManager )
return ;
// Default to white if there's no owner (e.g. decorative, editor-only actors)
CColor color ( 1.0 , 1.0 , 1.0 , 1.0 ) ;
if ( cmpOwnership )
{
CmpPtr < ICmpPlayer > cmpPlayer ( GetSimContext ( ) , cmpPlayerManager - > GetPlayerByID ( cmpOwnership - > GetOwner ( ) ) ) ;
if ( cmpPlayer )
color = cmpPlayer - > GetColor ( ) ;
}
// Update the highlight color, while keeping the current alpha target value intact
// (i.e. baseline + delta), so that any ongoing fades simply continue with the new color.
color . a = m_FadeBaselineAlpha + m_FadeDeltaAlpha ;
SetSelectionHighlight ( color , m_Selected ) ;
}
2017-05-05 17:47:21 -07:00
void CCmpSelectable : : ResetRangeOverlays ( )
{
for ( RangeOverlayData & rangeOverlay : m_RangeOverlayData )
delete rangeOverlay . second ;
m_RangeOverlayData . clear ( ) ;
UpdateMessageSubscriptions ( ) ;
}
2014-06-19 16:20:12 -07:00
void CCmpSelectable : : UpdateMessageSubscriptions ( )
{
bool needInterpolate = false ;
bool needRenderSubmit = false ;
if ( m_FadeDeltaAlpha ! = 0.f | | m_Color . a > 0 )
needInterpolate = true ;
if ( m_Visible & & m_Color . a > 0 )
needRenderSubmit = true ;
if ( needInterpolate ! = m_EnabledInterpolate )
{
GetSimContext ( ) . GetComponentManager ( ) . DynamicSubscriptionNonsync ( MT_Interpolate , this , needInterpolate ) ;
m_EnabledInterpolate = needInterpolate ;
}
if ( needRenderSubmit ! = m_EnabledRenderSubmit )
{
GetSimContext ( ) . GetComponentManager ( ) . DynamicSubscriptionNonsync ( MT_RenderSubmit , this , needRenderSubmit ) ;
m_EnabledRenderSubmit = needRenderSubmit ;
}
}
2012-04-21 21:04:02 -07:00
void CCmpSelectable : : InvalidateStaticOverlay ( )
{
SAFE_DELETE ( m_BuildingOverlay ) ;
}
2012-02-26 21:32:35 -08:00
2017-05-05 17:47:21 -07:00
void CCmpSelectable : : UpdateTexturedLineOverlay ( const SOverlayDescriptor * overlayDescriptor , SOverlayTexturedLine & overlay , float frameOffset , bool buildingOverlay )
2012-04-21 21:04:02 -07:00
{
if ( ! CRenderer : : IsInitialised ( ) )
return ;
2013-09-11 13:41:53 -07:00
CmpPtr < ICmpPosition > cmpPosition ( GetEntityHandle ( ) ) ;
CmpPtr < ICmpFootprint > cmpFootprint ( GetEntityHandle ( ) ) ;
2012-04-21 21:04:02 -07:00
if ( ! cmpFootprint | | ! cmpPosition | | ! cmpPosition - > IsInWorld ( ) )
return ;
ICmpFootprint : : EShape fpShape ;
entity_pos_t fpSize0_fixed , fpSize1_fixed , fpHeight_fixed ;
cmpFootprint - > GetShape ( fpShape , fpSize0_fixed , fpSize1_fixed , fpHeight_fixed ) ;
2017-05-05 17:47:21 -07:00
float rotY ;
CVector2D origin ;
cmpPosition - > GetInterpolatedPosition2D ( frameOffset , origin . X , origin . Y , rotY ) ;
CFixedVector3D rotation = cmpPosition - > GetRotation ( ) ;
CTextureProperties texturePropsBase ( overlayDescriptor - > m_LineTexture . c_str ( ) ) ;
2016-11-23 05:02:58 -08:00
texturePropsBase . SetWrap ( GL_CLAMP_TO_BORDER , GL_CLAMP_TO_EDGE ) ;
2012-04-21 21:04:02 -07:00
texturePropsBase . SetMaxAnisotropy ( 4.f ) ;
2017-05-05 17:47:21 -07:00
CTextureProperties texturePropsMask ( overlayDescriptor - > m_LineTextureMask . c_str ( ) ) ;
2016-11-23 05:02:58 -08:00
texturePropsMask . SetWrap ( GL_CLAMP_TO_BORDER , GL_CLAMP_TO_EDGE ) ;
2012-04-21 21:04:02 -07:00
texturePropsMask . SetMaxAnisotropy ( 4.f ) ;
2017-05-05 17:47:21 -07:00
overlay . m_AlwaysVisible = false ;
overlay . m_Closed = true ;
overlay . m_SimContext = & GetSimContext ( ) ;
overlay . m_Thickness = overlayDescriptor - > m_LineThickness ;
overlay . m_TextureBase = g_Renderer . GetTextureManager ( ) . CreateTexture ( texturePropsBase ) ;
overlay . m_TextureMask = g_Renderer . GetTextureManager ( ) . CreateTexture ( texturePropsMask ) ;
overlay . m_Color = m_Color ;
2012-04-21 21:04:02 -07:00
2017-05-05 17:47:21 -07:00
if ( buildingOverlay & & fpShape = = ICmpFootprint : : SQUARE )
2010-01-09 11:20:14 -08:00
{
2017-05-05 17:47:21 -07:00
float s = sinf ( - rotation . Y . ToFloat ( ) ) ;
float c = cosf ( - rotation . Y . ToFloat ( ) ) ;
CVector2D unitX ( c , s ) ;
CVector2D unitZ ( - s , c ) ;
// Add half the line thickness to the radius so that we get an 'outside' stroke of the footprint shape
const float halfSizeX = fpSize0_fixed . ToFloat ( ) / 2.f + overlay . m_Thickness / 2.f ;
const float halfSizeZ = fpSize1_fixed . ToFloat ( ) / 2.f + overlay . m_Thickness / 2.f ;
std : : vector < CVector2D > points ;
points . push_back ( CVector2D ( origin + unitX * halfSizeX + unitZ * ( - halfSizeZ ) ) ) ;
points . push_back ( CVector2D ( origin + unitX * ( - halfSizeX ) + unitZ * ( - halfSizeZ ) ) ) ;
points . push_back ( CVector2D ( origin + unitX * ( - halfSizeX ) + unitZ * halfSizeZ ) ) ;
points . push_back ( CVector2D ( origin + unitX * halfSizeX + unitZ * halfSizeZ ) ) ;
SimRender : : SubdividePoints ( points , TERRAIN_TILE_SIZE / 3.f , overlay . m_Closed ) ;
overlay . PushCoords ( points ) ;
}
else
{
const float radius = ( buildingOverlay ? fpSize0_fixed . ToFloat ( ) : overlayDescriptor - > m_Radius ) + overlay . m_Thickness / 3.f ;
2012-04-21 21:04:02 -07:00
2017-08-08 10:48:44 -07:00
u32 numSteps = ceilf ( float ( 2 * M_PI ) * radius / ( TERRAIN_TILE_SIZE / 3.f ) ) ;
for ( u32 i = 0 ; i < numSteps ; + + i )
2017-05-05 17:47:21 -07:00
{
2017-08-08 10:48:44 -07:00
float angle = i * float ( 2 * M_PI ) / numSteps ;
2017-05-05 17:47:21 -07:00
float px = origin . X + radius * sinf ( angle ) ;
float pz = origin . Y + radius * cosf ( angle ) ;
2012-04-21 21:04:02 -07:00
2017-05-05 17:47:21 -07:00
overlay . PushCoords ( px , pz ) ;
2010-01-09 11:20:14 -08:00
}
2012-04-21 21:04:02 -07:00
}
2010-01-09 11:20:14 -08:00
2017-05-05 17:47:21 -07:00
ENSURE ( overlay . m_TextureBase ) ;
2012-04-21 21:04:02 -07:00
}
void CCmpSelectable : : UpdateDynamicOverlay ( float frameOffset )
{
// Dynamic overlay lines are allocated once and never deleted. Since they are expected to change frequently,
// they are assumed dirty on every call to this function, and we should therefore use this function more
// thoughtfully than calling it right before every frame render.
2016-11-23 06:09:58 -08:00
2012-04-21 21:04:02 -07:00
if ( m_OverlayDescriptor . m_Type ! = DYNAMIC_QUAD )
return ;
if ( ! CRenderer : : IsInitialised ( ) )
return ;
2013-09-11 13:41:53 -07:00
CmpPtr < ICmpPosition > cmpPosition ( GetEntityHandle ( ) ) ;
CmpPtr < ICmpFootprint > cmpFootprint ( GetEntityHandle ( ) ) ;
2012-04-21 21:04:02 -07:00
if ( ! cmpFootprint | | ! cmpPosition | | ! cmpPosition - > IsInWorld ( ) )
return ;
float rotY ;
CVector2D position ;
cmpPosition - > GetInterpolatedPosition2D ( frameOffset , position . X , position . Y , rotY ) ;
2013-09-11 13:41:53 -07:00
CmpPtr < ICmpWaterManager > cmpWaterManager ( GetSystemEntity ( ) ) ;
CmpPtr < ICmpTerrain > cmpTerrain ( GetSystemEntity ( ) ) ;
2012-04-21 21:04:02 -07:00
ENSURE ( cmpWaterManager & & cmpTerrain ) ;
CTerrain * terrain = cmpTerrain - > GetCTerrain ( ) ;
ENSURE ( terrain ) ;
ICmpFootprint : : EShape fpShape ;
entity_pos_t fpSize0_fixed , fpSize1_fixed , fpHeight_fixed ;
cmpFootprint - > GetShape ( fpShape , fpSize0_fixed , fpSize1_fixed , fpHeight_fixed ) ;
// ---------------------------------------------------------------------------------
if ( ! m_UnitOverlay )
{
m_UnitOverlay = new SOverlayQuad ;
// Assuming we don't need the capability of swapping textures on-demand.
2016-11-23 05:02:58 -08:00
CTextureProperties texturePropsBase ( m_OverlayDescriptor . m_QuadTexture . c_str ( ) ) ;
texturePropsBase . SetWrap ( GL_CLAMP_TO_BORDER , GL_CLAMP_TO_EDGE ) ;
2012-04-21 21:04:02 -07:00
texturePropsBase . SetMaxAnisotropy ( 4.f ) ;
CTextureProperties texturePropsMask ( m_OverlayDescriptor . m_QuadTextureMask . c_str ( ) ) ;
2016-11-23 05:02:58 -08:00
texturePropsMask . SetWrap ( GL_CLAMP_TO_BORDER , GL_CLAMP_TO_EDGE ) ;
2012-04-21 21:04:02 -07:00
texturePropsMask . SetMaxAnisotropy ( 4.f ) ;
m_UnitOverlay - > m_Texture = g_Renderer . GetTextureManager ( ) . CreateTexture ( texturePropsBase ) ;
m_UnitOverlay - > m_TextureMask = g_Renderer . GetTextureManager ( ) . CreateTexture ( texturePropsMask ) ;
2010-01-09 11:20:14 -08:00
}
2012-04-21 21:04:02 -07:00
m_UnitOverlay - > m_Color = m_Color ;
2016-11-23 05:02:58 -08:00
// TODO: some code duplication here :< would be nice to factor out getting the corner points of an
2012-04-21 21:04:02 -07:00
// entity based on its footprint sizes (and regardless of whether it's a circle or a square)
float s = sinf ( - rotY ) ;
float c = cosf ( - rotY ) ;
CVector2D unitX ( c , s ) ;
CVector2D unitZ ( - s , c ) ;
float halfSizeX = fpSize0_fixed . ToFloat ( ) ;
float halfSizeZ = fpSize1_fixed . ToFloat ( ) ;
if ( fpShape = = ICmpFootprint : : SQUARE )
2010-01-09 11:20:14 -08:00
{
2012-04-21 21:04:02 -07:00
halfSizeX / = 2.0f ;
halfSizeZ / = 2.0f ;
}
2010-01-09 11:20:14 -08:00
2012-04-21 21:04:02 -07:00
std : : vector < CVector2D > points ;
points . push_back ( CVector2D ( position + unitX * ( - halfSizeX ) + unitZ * halfSizeZ ) ) ; // top left
points . push_back ( CVector2D ( position + unitX * ( - halfSizeX ) + unitZ * ( - halfSizeZ ) ) ) ; // bottom left
points . push_back ( CVector2D ( position + unitX * halfSizeX + unitZ * ( - halfSizeZ ) ) ) ; // bottom right
points . push_back ( CVector2D ( position + unitX * halfSizeX + unitZ * halfSizeZ ) ) ; // top right
2010-01-09 11:20:14 -08:00
2012-04-21 21:04:02 -07:00
for ( int i = 0 ; i < 4 ; i + + )
{
float quadY = std : : max (
terrain - > GetExactGroundLevel ( points [ i ] . X , points [ i ] . Y ) ,
cmpWaterManager - > GetExactWaterLevel ( points [ i ] . X , points [ i ] . Y )
) ;
2010-01-09 11:20:14 -08:00
2012-04-21 21:04:02 -07:00
m_UnitOverlay - > m_Corners [ i ] = CVector3D ( points [ i ] . X , quadY , points [ i ] . Y ) ;
}
}
void CCmpSelectable : : RenderSubmit ( SceneCollector & collector )
{
// don't render selection overlay if it's not gonna be visible
2016-01-03 04:41:04 -08:00
if ( ! ICmpSelectable : : m_OverrideVisible )
return ;
2012-07-30 14:06:54 -07:00
if ( m_Visible & & m_Color . a > 0 )
2012-04-21 21:04:02 -07:00
{
2012-07-18 21:45:01 -07:00
if ( ! m_Cached )
{
2017-08-09 04:19:14 -07:00
UpdatePlayerColor ( ) ;
2012-07-18 21:45:01 -07:00
m_Cached = true ;
}
2012-04-21 21:04:02 -07:00
switch ( m_OverlayDescriptor . m_Type )
2010-04-23 09:57:18 -07:00
{
2012-04-21 21:04:02 -07:00
case STATIC_OUTLINE :
{
2017-05-05 17:47:21 -07:00
if ( ! m_BuildingOverlay )
{
// Static overlays are allocated once and not updated until they are explicitly deleted again
// (see InvalidateStaticOverlay). Since they are expected to change rarely (if ever) during
// normal gameplay, this saves us doing all the work below on each frame.
m_BuildingOverlay = new SOverlayTexturedLine ;
UpdateTexturedLineOverlay ( & m_OverlayDescriptor , * m_BuildingOverlay , 0 , true ) ;
}
2012-04-21 21:04:02 -07:00
m_BuildingOverlay - > m_Color = m_Color ; // done separately so alpha changes don't require a full update call
collector . Submit ( m_BuildingOverlay ) ;
}
break ;
case DYNAMIC_QUAD :
{
if ( m_UnitOverlay )
collector . Submit ( m_UnitOverlay ) ;
}
break ;
default :
break ;
2010-04-23 09:57:18 -07:00
}
2017-05-05 17:47:21 -07:00
for ( const RangeOverlayData & rangeOverlay : m_RangeOverlayData )
if ( rangeOverlay . second )
collector . Submit ( rangeOverlay . second ) ;
2010-01-09 11:20:14 -08:00
}
2012-04-21 21:04:02 -07:00
// Render bounding box debug overlays if we have a positive target alpha value. This ensures
// that the debug overlays respond immediately to deselection without delay from fading out.
if ( m_FadeBaselineAlpha + m_FadeDeltaAlpha > 0 )
2010-01-09 11:20:14 -08:00
{
2011-11-24 22:36:13 -08:00
if ( ICmpSelectable : : ms_EnableDebugOverlays )
{
// allocate debug overlays on-demand
if ( ! m_DebugBoundingBoxOverlay ) m_DebugBoundingBoxOverlay = new SOverlayLine ;
if ( ! m_DebugSelectionBoxOverlay ) m_DebugSelectionBoxOverlay = new SOverlayLine ;
2013-09-11 13:41:53 -07:00
CmpPtr < ICmpVisual > cmpVisual ( GetEntityHandle ( ) ) ;
2016-11-23 05:02:58 -08:00
if ( cmpVisual )
2011-11-24 22:36:13 -08:00
{
SimRender : : ConstructBoxOutline ( cmpVisual - > GetBounds ( ) , * m_DebugBoundingBoxOverlay ) ;
2016-11-23 05:02:58 -08:00
m_DebugBoundingBoxOverlay - > m_Thickness = 2 ;
2011-11-24 22:36:13 -08:00
m_DebugBoundingBoxOverlay - > m_Color = CColor ( 1.f , 0.f , 0.f , 1.f ) ;
SimRender : : ConstructBoxOutline ( cmpVisual - > GetSelectionBox ( ) , * m_DebugSelectionBoxOverlay ) ;
m_DebugSelectionBoxOverlay - > m_Thickness = 2 ;
m_DebugSelectionBoxOverlay - > m_Color = CColor ( 0.f , 1.f , 0.f , 1.f ) ;
collector . Submit ( m_DebugBoundingBoxOverlay ) ;
collector . Submit ( m_DebugSelectionBoxOverlay ) ;
}
}
else
{
// reclaim debug overlay line memory when no longer debugging (and make sure to set to zero after deletion)
if ( m_DebugBoundingBoxOverlay ) SAFE_DELETE ( m_DebugBoundingBoxOverlay ) ;
if ( m_DebugSelectionBoxOverlay ) SAFE_DELETE ( m_DebugSelectionBoxOverlay ) ;
}
2010-01-09 11:20:14 -08:00
}
2012-04-21 21:04:02 -07:00
}
2010-01-09 11:20:14 -08:00
REGISTER_COMPONENT_TYPE ( Selectable )