1
0
forked from 0ad/0ad

Move back the computation of most of the visibilities from JS to engine, to improve performance.

Also fix the remaining TODO left by #958, as a use case of the scripted
Visibility component.

Refs #2913, #958.

This was SVN commit r16248.
This commit is contained in:
Nicolas Auvray 2015-01-30 15:28:06 +00:00
parent 7d282f6bb1
commit 1564a10120
20 changed files with 131 additions and 92 deletions

View File

@ -95,14 +95,12 @@ BuildRestrictions.prototype.CheckPlacement = function()
if (!cmpPlayer.IsAI())
{
// Check whether it's in a visible or fogged region
// tell GetLosVisibility to force RetainInFog because preview entities set this to false,
// which would show them as hidden instead of fogged
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (!cmpRangeManager || !cmpOwnership)
return result; // Fail
var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner(), true) != "hidden");
var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner()) != "hidden");
if (!explored)
{
result.message = markForTranslation("%(name)s cannot be built in unexplored area");

View File

@ -340,7 +340,7 @@ BuildingAI.prototype.CheckTargetVisible = function(target)
if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner()))
return true;
if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner(), false) == "hidden")
if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) == "hidden")
return false;
// Either visible directly, or visible in fog

View File

@ -160,7 +160,7 @@ Fogging.prototype.OnDestroy = function(msg)
for (var player = 0; player < this.mirages.length; ++player)
{
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (cmpRangeManager.GetLosVisibility(this.mirages[player], player, false) == "hidden")
if (cmpRangeManager.GetLosVisibility(this.mirages[player], player) == "hidden")
{
Engine.DestroyEntity(this.mirages[player]);
continue;

View File

@ -381,7 +381,7 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
}
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
ret.visibility = cmpRangeManager.GetLosVisibility(ent, player, false);
ret.visibility = cmpRangeManager.GetLosVisibility(ent, player);
return ret;
};
@ -1433,10 +1433,8 @@ GuiInterface.prototype.SetWallPlacementPreview = function(player, cmd)
cmpOwnership.SetOwner(player);
// Check whether it's in a visible or fogged region
// tell GetLosVisibility to force RetainInFog because preview entities set this to false,
// which would show them as hidden instead of fogged
// TODO: should definitely reuse SetBuildingPlacementPreview, this is just straight up copy/pasta
var visible = (cmpRangeManager.GetLosVisibility(ent, player, true) != "hidden");
var visible = (cmpRangeManager.GetLosVisibility(ent, player) != "hidden");
if (visible)
{
var cmpBuildRestrictions = Engine.QueryInterface(ent, IID_BuildRestrictions);

View File

@ -4397,7 +4397,7 @@ UnitAI.prototype.CheckTargetVisible = function(target)
if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner()))
return true;
if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner(), false) == "hidden")
if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) == "hidden")
return false;
// Either visible directly, or visible in fog

View File

@ -10,70 +10,65 @@ Visibility.prototype.Schema =
"</element>" +
"<element name='AlwaysVisible'>" +
"<data type='boolean'/>" +
"</element>" +
"<element name='Preview'>" +
"<data type='boolean'/>" +
"</element>";
Visibility.prototype.Init = function()
{
this.retainInFog = this.template.RetainInFog == "true";
this.alwaysVisible = this.template.AlwaysVisible == "true";
this.preview = this.template.Preview == "true";
this.activated = false;
if (this.preview)
this.activated = true;
};
/**
* This function is called for entities in explored territory.
* isOutsideFog: true if we're in the vision range of a unit, false otherwise
* forceRetainInFog: useful for previewed entities, see the RangeManager system component documentation
* If this function returns true, the range manager will call the GetVisibility function
* instead of computing the visibility.
*/
Visibility.prototype.GetLosVisibility = function(player, isOutsideFog, forceRetainInFog)
Visibility.prototype.IsActivated = function()
{
if (isOutsideFog)
return this.activated;
};
/**
* This function is called if IsActivated returns true.
* If so, the return value supersedes the visibility computed by the range manager.
* isVisible: true if the entity is in the vision range of a unit, false otherwise
* isExplored: true if the entity is in explored territory, false otherwise
*/
Visibility.prototype.GetVisibility = function(player, isVisible, isExplored)
{
if (!this.activated)
warn("The Visibility component was asked to provide a superseding visibility while not activated, this should not happen");
if (this.preview)
{
var cmpMirage = Engine.QueryInterface(this.entity, IID_Mirage);
if (cmpMirage)
return VIS_HIDDEN;
// For the owner only, mock the "RetainInFog" behaviour
return VIS_VISIBLE;
}
// Fogged if the 'retain in fog' flag is set, and in a non-visible explored region
if (!forceRetainInFog && !this.GetRetainInFog())
return VIS_HIDDEN;
var cmpMirage = Engine.QueryInterface(this.entity, IID_Mirage);
if (cmpMirage && cmpMirage.GetPlayer() == player)
return VIS_FOGGED;
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (!cmpOwnership)
return VIS_FOGGED;
if (cmpOwnership.GetOwner() == player)
{
var cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
if (!cmpFogging)
return VIS_FOGGED;
// Fogged entities must not disappear while the mirage is not ready
if (!cmpFogging.IsMiraged(player))
return VIS_FOGGED;
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (cmpOwnership && cmpOwnership.GetOwner() == player && isExplored)
return isVisible ? VIS_VISIBLE : VIS_FOGGED;
return VIS_HIDDEN;
}
// Fogged entities must not disappear while the mirage is not ready
var cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
if (cmpFogging && cmpFogging.WasSeen(player) && !cmpFogging.IsMiraged(player))
return VIS_FOGGED;
return VIS_HIDDEN;
return VIS_VISIBLE;
};
Visibility.prototype.GetRetainInFog = function()
{
return this.template.RetainInFog == "true";
return this.retainInFog;
};
Visibility.prototype.GetAlwaysVisible = function()
{
return this.template.AlwaysVisible == "true";
return this.alwaysVisible;
};
Engine.RegisterComponentType(IID_Visibility, "Visibility", Visibility);

View File

@ -9,6 +9,7 @@
<Visibility>
<RetainInFog>true</RetainInFog>
<AlwaysVisible>false</AlwaysVisible>
<Preview>false</Preview>
</Visibility>
<Vision>
<Range>0</Range>

View File

@ -23,6 +23,7 @@
<Visibility>
<RetainInFog>true</RetainInFog>
<AlwaysVisible>false</AlwaysVisible>
<Preview>false</Preview>
</Visibility>
<Vision>
<Range>0</Range>

View File

@ -17,6 +17,7 @@
<Visibility>
<RetainInFog>true</RetainInFog>
<AlwaysVisible>false</AlwaysVisible>
<Preview>false</Preview>
</Visibility>
<Vision>
<Range>0</Range>

View File

@ -105,6 +105,7 @@
<Visibility>
<RetainInFog>true</RetainInFog>
<AlwaysVisible>false</AlwaysVisible>
<Preview>false</Preview>
</Visibility>
<Vision>
<Range>40</Range>

View File

@ -29,6 +29,7 @@
<Visibility>
<RetainInFog>true</RetainInFog>
<AlwaysVisible>false</AlwaysVisible>
<Preview>false</Preview>
</Visibility>
<Vision>
<Range>0</Range>

View File

@ -118,6 +118,7 @@
<Visibility>
<RetainInFog>false</RetainInFog>
<AlwaysVisible>false</AlwaysVisible>
<Preview>false</Preview>
</Visibility>
<Vision>
<Range>10</Range>

View File

@ -41,6 +41,7 @@
<Visibility>
<RetainInFog>true</RetainInFog>
<AlwaysVisible>false</AlwaysVisible>
<Preview>false</Preview>
</Visibility>
<Vision>
<Range>0</Range>

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -717,7 +717,7 @@ void CGameView::Update(const float deltaRealTime)
CmpPtr<ICmpPosition> cmpPosition(*(m->Game->GetSimulation2()), m->FollowEntity);
CmpPtr<ICmpRangeManager> cmpRangeManager(*(m->Game->GetSimulation2()), SYSTEM_ENTITY);
if (cmpPosition && cmpPosition->IsInWorld() &&
cmpRangeManager && cmpRangeManager->GetLosVisibility(m->FollowEntity, m->Game->GetPlayerID(), false) == ICmpRangeManager::VIS_VISIBLE)
cmpRangeManager && cmpRangeManager->GetLosVisibility(m->FollowEntity, m->Game->GetPlayerID()) == ICmpRangeManager::VIS_VISIBLE)
{
// Get the most recent interpolated position
float frameOffset = m->Game->GetSimulation2()->GetLastFrameOffset();

View File

@ -407,7 +407,7 @@ void CTemplateLoader::CopyPreviewSubset(CParamNode& out, const CParamNode& in, b
CParamNode::LoadXMLString(out, "<Entity><VisualActor><DisableShadows/></VisualActor></Entity>");
// Previews should always be visible in fog-of-war/etc
CParamNode::LoadXMLString(out, "<Entity><Visibility><RetainInFog>false</RetainInFog><AlwaysVisible>true</AlwaysVisible></Visibility></Entity>");
CParamNode::LoadXMLString(out, "<Entity><Visibility><RetainInFog>false</RetainInFog><AlwaysVisible>true</AlwaysVisible><Preview>true</Preview></Visibility></Entity>");
}
if (corpse)
@ -421,7 +421,7 @@ void CTemplateLoader::CopyPreviewSubset(CParamNode& out, const CParamNode& in, b
CParamNode::LoadXMLString(out, "<Entity><VisualActor><SilhouetteDisplay>false</SilhouetteDisplay></VisualActor></Entity>");
// Corpses should remain visible in fog-of-war
CParamNode::LoadXMLString(out, "<Entity><Visibility><RetainInFog>true</RetainInFog><AlwaysVisible>false</AlwaysVisible></Visibility></Entity>");
CParamNode::LoadXMLString(out, "<Entity><Visibility><RetainInFog>true</RetainInFog><AlwaysVisible>false</AlwaysVisible><Preview>false</Preview></Visibility></Entity>");
}
}
@ -456,7 +456,7 @@ void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in)
// Set the entity as mirage entity
CParamNode::LoadXMLString(out, "<Entity><Mirage/></Entity>");
CParamNode::LoadXMLString(out, "<Entity><Visibility><RetainInFog>true</RetainInFog><AlwaysVisible>false</AlwaysVisible></Visibility></Entity>");
CParamNode::LoadXMLString(out, "<Entity><Visibility><RetainInFog>true</RetainInFog><AlwaysVisible>false</AlwaysVisible><Preview>false</Preview></Visibility></Entity>");
}
void CTemplateLoader::CopyFoundationSubset(CParamNode& out, const CParamNode& in)

View File

@ -1386,7 +1386,7 @@ public:
return CLosQuerier(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide);
}
ELosVisibility ComputeLosVisibility(CEntityHandle ent, player_id_t player, bool forceRetainInFog)
ELosVisibility ComputeLosVisibility(CEntityHandle ent, player_id_t player)
{
// Entities not with positions in the world are never visible
if (ent.GetId() == INVALID_ENTITY)
@ -1413,34 +1413,61 @@ public:
return VIS_VISIBLE;
}
// Visible if within a visible region
// Get visible regions
CLosQuerier los(GetSharedLosMask(player), m_LosState, m_TerrainVerticesPerSide);
// Ask the Visibility component about special situations
CmpPtr<ICmpVisibility> cmpVisibility(ent);
if (cmpVisibility && cmpVisibility->IsActivated())
return cmpVisibility->GetVisibility(player, los.IsVisible(i, j), los.IsExplored(i, j));
// Else, default behavior
if (los.IsVisible(i, j))
return VIS_VISIBLE;
if (!los.IsExplored(i, j))
return VIS_HIDDEN;
// Try to ask the Visibility component of the entity, if any
CmpPtr<ICmpVisibility> cmpVisibility(ent);
if (cmpVisibility)
return cmpVisibility->GetLosVisibility(player, los.IsVisible(i, j), forceRetainInFog);
// Invisible if the 'retain in fog' flag is not set, and in a non-visible explored region
if (!(cmpVisibility && cmpVisibility->GetRetainInFog()))
return VIS_HIDDEN;
// Default behaviour
if (los.IsVisible(i, j))
if (cmpMirage && cmpMirage->GetPlayer() == player)
return VIS_FOGGED;
CmpPtr<ICmpOwnership> cmpOwnership(ent);
if (!cmpOwnership)
return VIS_VISIBLE;
if (forceRetainInFog)
if (cmpOwnership->GetOwner() == player)
{
CmpPtr<ICmpFogging> cmpFogging(ent);
if (!cmpFogging)
return VIS_VISIBLE;
// Fogged entities must not disappear while the mirage is not ready
if (!cmpFogging->IsMiraged(player))
return VIS_FOGGED;
return VIS_HIDDEN;
}
// Fogged entities must not disappear while the mirage is not ready
CmpPtr<ICmpFogging> cmpFogging(ent);
if (cmpFogging && cmpFogging->WasSeen(player) && !cmpFogging->IsMiraged(player))
return VIS_FOGGED;
return VIS_HIDDEN;
}
ELosVisibility ComputeLosVisibility(entity_id_t ent, player_id_t player, bool forceRetainInFog)
ELosVisibility ComputeLosVisibility(entity_id_t ent, player_id_t player)
{
CEntityHandle handle = GetSimContext().GetComponentManager().LookupEntityHandle(ent);
return ComputeLosVisibility(handle, player, forceRetainInFog);
return ComputeLosVisibility(handle, player);
}
virtual ELosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player, bool forceRetainInFog)
virtual ELosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player)
{
entity_id_t entId = ent.GetId();
@ -1455,22 +1482,22 @@ public:
i32 n = PosToLosTilesHelper(pos.X, pos.Y);
if (m_DirtyVisibility[n])
return ComputeLosVisibility(ent, player, forceRetainInFog);
return ComputeLosVisibility(ent, player);
if (std::find(m_ModifiedEntities.begin(), m_ModifiedEntities.end(), entId) != m_ModifiedEntities.end())
return ComputeLosVisibility(ent, player, forceRetainInFog);
return ComputeLosVisibility(ent, player);
EntityMap<EntityData>::iterator it = m_EntityData.find(entId);
if (it == m_EntityData.end())
return ComputeLosVisibility(ent, player, forceRetainInFog);
return ComputeLosVisibility(ent, player);
return static_cast<ELosVisibility>(GetPlayerVisibility(it->second.visibilities, player));
}
virtual ELosVisibility GetLosVisibility(entity_id_t ent, player_id_t player, bool forceRetainInFog)
virtual ELosVisibility GetLosVisibility(entity_id_t ent, player_id_t player)
{
CEntityHandle handle = GetSimContext().GetComponentManager().LookupEntityHandle(ent);
return GetLosVisibility(handle, player, forceRetainInFog);
return GetLosVisibility(handle, player);
}
i32 PosToLosTilesHelper(entity_pos_t x, entity_pos_t z)
@ -1549,7 +1576,7 @@ public:
for (player_id_t player = 1; player < MAX_LOS_PLAYER_ID+1; ++player)
{
u8 oldVis = GetPlayerVisibility(itEnts->second.visibilities, player);
u8 newVis = ComputeLosVisibility(itEnts->first, player, false);
u8 newVis = ComputeLosVisibility(itEnts->first, player);
oldVisibilities.push_back(oldVis);
newVisibilities.push_back(newVis);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -21,9 +21,9 @@
#include "simulation2/system/InterfaceScripted.h"
std::string ICmpRangeManager::GetLosVisibility_wrapper(entity_id_t ent, int player, bool forceRetainInFog)
std::string ICmpRangeManager::GetLosVisibility_wrapper(entity_id_t ent, int player)
{
ELosVisibility visibility = GetLosVisibility(ent, player, forceRetainInFog);
ELosVisibility visibility = GetLosVisibility(ent, player);
switch (visibility)
{
case VIS_HIDDEN: return "hidden";
@ -51,7 +51,7 @@ DEFINE_INTERFACE_METHOD_0("ExploreTerritories", void, ICmpRangeManager, ExploreT
DEFINE_INTERFACE_METHOD_2("SetLosRevealAll", void, ICmpRangeManager, SetLosRevealAll, player_id_t, bool)
DEFINE_INTERFACE_METHOD_1("GetLosRevealAll", bool, ICmpRangeManager, GetLosRevealAll, player_id_t)
DEFINE_INTERFACE_METHOD_5("GetElevationAdaptedRange", entity_pos_t, ICmpRangeManager, GetElevationAdaptedRange, CFixedVector3D, CFixedVector3D, entity_pos_t, entity_pos_t, entity_pos_t)
DEFINE_INTERFACE_METHOD_3("GetLosVisibility", std::string, ICmpRangeManager, GetLosVisibility_wrapper, entity_id_t, player_id_t, bool)
DEFINE_INTERFACE_METHOD_2("GetLosVisibility", std::string, ICmpRangeManager, GetLosVisibility_wrapper, entity_id_t, player_id_t)
DEFINE_INTERFACE_METHOD_1("SetLosCircular", void, ICmpRangeManager, SetLosCircular, bool)
DEFINE_INTERFACE_METHOD_0("GetLosCircular", bool, ICmpRangeManager, GetLosCircular)
DEFINE_INTERFACE_METHOD_2("SetSharedLos", void, ICmpRangeManager, SetSharedLos, player_id_t, std::vector<player_id_t>)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -319,19 +319,16 @@ public:
* Returns the visibility status of the given entity, with respect to the given player.
* Returns VIS_HIDDEN if the entity doesn't exist or is not in the world.
* This respects the GetLosRevealAll flag.
* If forceRetainInFog is true, the visibility acts as if CCmpVision's RetainInFog flag were set.
* TODO: This is a hack to allow preview entities in FoW to return fogged instead of hidden,
* see http://trac.wildfiregames.com/ticket/958
*/
virtual ELosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player, bool forceRetainInFog = false) = 0;
virtual ELosVisibility GetLosVisibility(entity_id_t ent, player_id_t player, bool forceRetainInFog = false) = 0;
virtual ELosVisibility GetLosVisibility(CEntityHandle ent, player_id_t player) = 0;
virtual ELosVisibility GetLosVisibility(entity_id_t ent, player_id_t player) = 0;
/**
* GetLosVisibility wrapped for script calls.
* Returns "hidden", "fogged" or "visible".
*/
std::string GetLosVisibility_wrapper(entity_id_t ent, player_id_t player, bool forceRetainInFog);
std::string GetLosVisibility_wrapper(entity_id_t ent, player_id_t player);
/**
* Explore all tiles (but leave them in the FoW) for player p

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -30,9 +30,14 @@ class CCmpVisibilityScripted : public ICmpVisibility
public:
DEFAULT_SCRIPT_WRAPPER(VisibilityScripted)
virtual ICmpRangeManager::ELosVisibility GetLosVisibility(player_id_t player, bool isOutsideFog, bool forceRetainInFog)
virtual bool IsActivated()
{
int visibility = m_Script.Call<int, player_id_t, bool, bool>("GetLosVisibility", player, isOutsideFog, forceRetainInFog);
return m_Script.Call<bool>("IsActivated");
}
virtual ICmpRangeManager::ELosVisibility GetVisibility(player_id_t player, bool isVisible, bool isExplored)
{
int visibility = m_Script.Call<int, player_id_t, bool, bool>("GetVisibility", player, isVisible, isExplored);
switch (visibility)
{

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -22,10 +22,22 @@
#include "simulation2/components/ICmpRangeManager.h"
/**
* The Visibility component is a scripted component that allows any part of the simulation to
* influence the visibility of an entity.
*
* This component:
* - Holds the template values RetainInFog and AlwaysVisible, used by the range manager to compute
* the visibility of the entity;
* - Can supersede the range manager if it is "activated". This is to avoid useless calls to the scripts.
*/
class ICmpVisibility : public IComponent
{
public:
virtual ICmpRangeManager::ELosVisibility GetLosVisibility(player_id_t player, bool isOutsideFog, bool forceRetainInFog) = 0;
virtual bool IsActivated() = 0;
virtual ICmpRangeManager::ELosVisibility GetVisibility(player_id_t player, bool isVisible, bool isExplored) = 0;
virtual bool GetRetainInFog() = 0;