forked from 0ad/0ad
# Add fog-of-war.
Move Vision component to C++, for more efficient FoW processing. Disable FoW on demo maps. This was SVN commit r8160.
This commit is contained in:
parent
8886841860
commit
924d1219a7
@ -77,7 +77,7 @@
|
||||
/>
|
||||
|
||||
<!-- Dev/cheat commands -->
|
||||
<object name="devCommands" size="100%-155 50 100%-16 162" z="200" type="image" sprite="devCommandsBackground"
|
||||
<object name="devCommands" size="100%-155 50 100%-16 178" z="200" type="image" sprite="devCommandsBackground"
|
||||
hidden="true" hotkey="session.devcommands.toggle">
|
||||
<action on="Press">
|
||||
this.hidden = !this.hidden;
|
||||
@ -113,6 +113,11 @@
|
||||
<object size="100%-16 96 100% 112" type="checkbox" style="wheatCrossBox" checked="true">
|
||||
<action on="Press">gameView.constrainCamera = this.checked;</action>
|
||||
</object>
|
||||
|
||||
<object size="0 112 100%-18 128" type="text" style="devCommandsText">Reveal map</object>
|
||||
<object size="100%-16 112 100% 128" type="checkbox" style="wheatCrossBox">
|
||||
<action on="Press">Engine.SetRevealMap(this.checked);</action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
<!-- ================================ ================================ -->
|
||||
|
BIN
binaries/data/mods/public/maps/scenarios/Combat_demo.xml
(Stored with Git LFS)
BIN
binaries/data/mods/public/maps/scenarios/Combat_demo.xml
(Stored with Git LFS)
Binary file not shown.
BIN
binaries/data/mods/public/maps/scenarios/Combat_demo_(huge).xml
(Stored with Git LFS)
BIN
binaries/data/mods/public/maps/scenarios/Combat_demo_(huge).xml
(Stored with Git LFS)
Binary file not shown.
BIN
binaries/data/mods/public/maps/scenarios/Pathfinding_demo.xml
(Stored with Git LFS)
BIN
binaries/data/mods/public/maps/scenarios/Pathfinding_demo.xml
(Stored with Git LFS)
Binary file not shown.
BIN
binaries/data/mods/public/maps/scenarios/Pathfinding_terrain_demo.xml
(Stored with Git LFS)
BIN
binaries/data/mods/public/maps/scenarios/Pathfinding_terrain_demo.xml
(Stored with Git LFS)
Binary file not shown.
BIN
binaries/data/mods/public/maps/scenarios/Units_demo.xml
(Stored with Git LFS)
BIN
binaries/data/mods/public/maps/scenarios/Units_demo.xml
(Stored with Git LFS)
Binary file not shown.
@ -1,22 +0,0 @@
|
||||
function Vision() {}
|
||||
|
||||
Vision.prototype.Schema =
|
||||
"<optional>" +
|
||||
"<element name='Range'>" +
|
||||
"<data type='positiveInteger'/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<element name='RetainInFog'>" +
|
||||
"<data type='boolean'/>" +
|
||||
"</element>";
|
||||
|
||||
/*
|
||||
* TODO: this all needs to be designed and implemented
|
||||
*/
|
||||
|
||||
Vision.prototype.GetRange = function()
|
||||
{
|
||||
return +this.template.Range;
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_Vision, "Vision", Vision);
|
@ -1 +0,0 @@
|
||||
Engine.RegisterInterface("Vision");
|
@ -12,6 +12,13 @@ function LoadMapSettings(settings)
|
||||
cmpUnitAI.SetStance(settings.DefaultStance);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.RevealMap)
|
||||
{
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
if (cmpRangeManager)
|
||||
cmpRangeManager.SetLosRevealAll(true);
|
||||
}
|
||||
}
|
||||
|
||||
Engine.RegisterGlobal("LoadMapSettings", LoadMapSettings);
|
||||
|
@ -5,6 +5,7 @@
|
||||
<GenericName>Gaia</GenericName>
|
||||
</Identity>
|
||||
<Vision>
|
||||
<Range>0</Range>
|
||||
<RetainInFog>true</RetainInFog>
|
||||
</Vision>
|
||||
</Entity>
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpMinimap.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
|
||||
bool g_GameRestarted = false;
|
||||
|
||||
@ -246,9 +247,7 @@ void CMiniMap::Draw()
|
||||
if(m_TerrainDirty)
|
||||
RebuildTerrainTexture();
|
||||
|
||||
// CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
|
||||
// if(losMgr->m_LOSSetting != LOS_SETTING_ALL_VISIBLE)
|
||||
// RebuildLOSTexture();
|
||||
RebuildLOSTexture();
|
||||
}
|
||||
|
||||
const float texCoordMax = ((float)m_MapSize - 1) / ((float)m_TextureSize);
|
||||
@ -366,16 +365,24 @@ void CMiniMap::Draw()
|
||||
std::vector<MinimapUnitVertex> vertexArray;
|
||||
vertexArray.reserve(ents.size());
|
||||
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY);
|
||||
debug_assert(!cmpRangeManager.null());
|
||||
|
||||
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
|
||||
{
|
||||
MinimapUnitVertex v;
|
||||
ICmpMinimap* cmpMinimap = static_cast<ICmpMinimap*>(it->second);
|
||||
if (cmpMinimap->GetRenderData(v.r, v.g, v.b, v.x, v.y))
|
||||
entity_pos_t posX, posZ;
|
||||
if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ))
|
||||
{
|
||||
v.a = 255;
|
||||
v.x = x + v.x*sx;
|
||||
v.y = y - v.y*sy;
|
||||
vertexArray.push_back(v);
|
||||
ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetPlayerID());
|
||||
if (vis != ICmpRangeManager::VIS_HIDDEN)
|
||||
{
|
||||
v.a = 255;
|
||||
v.x = x + posX.ToFloat()*sx;
|
||||
v.y = y - posZ.ToFloat()*sy;
|
||||
vertexArray.push_back(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,7 +428,7 @@ void CMiniMap::CreateTextures()
|
||||
glGenTextures(1, &m_LOSTexture);
|
||||
g_Renderer.BindTexture(0, m_LOSTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
|
||||
m_LOSData = new u8[(m_MapSize - 1) * (m_MapSize - 1)];
|
||||
m_LOSData = new u8[m_MapSize * m_MapSize];
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
|
||||
@ -493,41 +500,39 @@ void CMiniMap::RebuildTerrainTexture()
|
||||
|
||||
void CMiniMap::RebuildLOSTexture()
|
||||
{
|
||||
PROFILE_START("rebuild minimap: los");
|
||||
PROFILE("rebuild minimap: los");
|
||||
|
||||
ssize_t x = 0;
|
||||
ssize_t y = 0;
|
||||
ssize_t w = m_MapSize - 1;
|
||||
ssize_t h = m_MapSize - 1;
|
||||
ssize_t w = m_MapSize;
|
||||
ssize_t h = m_MapSize;
|
||||
|
||||
memset(m_LOSData, 0, w*h);
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
if (cmpRangeManager.null() || cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID()))
|
||||
{
|
||||
memset(m_LOSData, 0, w*h);
|
||||
}
|
||||
else
|
||||
{
|
||||
ICmpRangeManager::CLosQuerier los = cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID());
|
||||
|
||||
// TODO: ought to do something like:
|
||||
/*
|
||||
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
|
||||
CPlayer* player = g_Game->GetLocalPlayer();
|
||||
u8 *dataPtr = m_LOSData;
|
||||
|
||||
for(ssize_t j = 0; j < h; j++)
|
||||
for (ssize_t j = 0; j < h; j++)
|
||||
{
|
||||
u8 *dataPtr = m_LOSData + ((y + j) * (m_MapSize - 1)) + x;
|
||||
for(ssize_t i = 0; i < w; i++)
|
||||
for (ssize_t i = 0; i < w; i++)
|
||||
{
|
||||
ELOSStatus status = losMgr->GetStatus(i, j, player);
|
||||
if(status == LOS_UNEXPLORED)
|
||||
*dataPtr++ = 0xff;
|
||||
else if(status == LOS_EXPLORED)
|
||||
*dataPtr++ = (u8) (0xff * 0.3f);
|
||||
else
|
||||
if (los.IsVisible(i, j))
|
||||
*dataPtr++ = 0;
|
||||
else if (los.IsExplored(i, j))
|
||||
*dataPtr++ = 76;
|
||||
else
|
||||
*dataPtr++ = 255;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Upload the texture
|
||||
g_Renderer.BindTexture(0, m_LOSTexture);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_ALPHA, GL_UNSIGNED_BYTE, m_LOSData);
|
||||
|
||||
PROFILE_END("rebuild minimap: los");
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize, m_MapSize, GL_ALPHA, GL_UNSIGNED_BYTE, m_LOSData);
|
||||
}
|
||||
|
||||
void CMiniMap::Destroy()
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpCommandQueue.h"
|
||||
#include "simulation2/components/ICmpGuiInterface.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
#include "simulation2/components/ICmpTemplateManager.h"
|
||||
#include "simulation2/helpers/Selection.h"
|
||||
|
||||
@ -119,7 +120,7 @@ void PostNetworkCommand(void* cbdata, CScriptVal cmd)
|
||||
|
||||
std::vector<entity_id_t> PickEntitiesAtPoint(void* UNUSED(cbdata), int x, int y)
|
||||
{
|
||||
return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y);
|
||||
return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetPlayerID());
|
||||
}
|
||||
|
||||
std::vector<entity_id_t> PickFriendlyEntitiesInRect(void* UNUSED(cbdata), int x0, int y0, int x1, int y1, int player)
|
||||
@ -304,6 +305,18 @@ CScriptVal LoadMapData(void* cbdata, std::wstring name)
|
||||
return reader.GetScriptData(guiManager->GetScriptInterface()).get();
|
||||
}
|
||||
|
||||
void SetRevealMap(void* UNUSED(cbdata), bool enabled)
|
||||
{
|
||||
if (!g_Game)
|
||||
return;
|
||||
|
||||
CSimulation2* sim = g_Game->GetSimulation2();
|
||||
debug_assert(sim);
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY);
|
||||
if (!cmpRangeManager.null())
|
||||
cmpRangeManager->SetLosRevealAll(enabled);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
@ -343,4 +356,5 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
|
||||
scriptInterface.RegisterFunction<void, &RestartInAtlas>("RestartInAtlas");
|
||||
scriptInterface.RegisterFunction<bool, &AtlasIsAvailable>("AtlasIsAvailable");
|
||||
scriptInterface.RegisterFunction<CScriptVal, std::wstring, &LoadMapData>("LoadMapData");
|
||||
scriptInterface.RegisterFunction<void, bool, &SetRevealMap>("SetRevealMap");
|
||||
}
|
||||
|
@ -176,6 +176,11 @@ public:
|
||||
return value >> fract_bits;
|
||||
}
|
||||
|
||||
int ToInt_RoundToNearest() const // (ties to infinity)
|
||||
{
|
||||
return (value + fract_pow2/2) >> fract_bits;
|
||||
}
|
||||
|
||||
/// Returns the shortest string such that FromString will parse to the correct value.
|
||||
CStr8 ToString() const;
|
||||
|
||||
@ -280,6 +285,14 @@ public:
|
||||
return CFixed((T)t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiply the value by itself. Might overflow.
|
||||
*/
|
||||
CFixed Square() const
|
||||
{
|
||||
return (*this).Multiply(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute this*m/d. Must not have d == 0. Won't overflow if the result can be represented as a CFixed.
|
||||
*/
|
||||
|
@ -189,6 +189,22 @@ public:
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(-2.99f).ToInt_RoundToNegInfinity(), -3);
|
||||
}
|
||||
|
||||
void test_RoundToNearest()
|
||||
{
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(10.f).ToInt_RoundToNearest(), 10);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(10.1f).ToInt_RoundToNearest(), 10);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(10.5f).ToInt_RoundToNearest(), 11);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(10.99f).ToInt_RoundToNearest(), 11);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(0.1f).ToInt_RoundToNearest(), 0);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(0.0f).ToInt_RoundToNearest(), 0);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(-0.1f).ToInt_RoundToNearest(), 0);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(-0.99f).ToInt_RoundToNearest(), -1);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(-1.0f).ToInt_RoundToNearest(), -1);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(-2.0f).ToInt_RoundToNearest(), -2);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(-2.5f).ToInt_RoundToNearest(), -2);
|
||||
TS_ASSERT_EQUALS(fixed::FromFloat(-2.99f).ToInt_RoundToNearest(), -3);
|
||||
}
|
||||
|
||||
// TODO: test all the arithmetic operators
|
||||
|
||||
void test_Mod()
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "graphics/Patch.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
|
||||
const ssize_t BlendOffsets[8][2] = {
|
||||
{ 0, -1 },
|
||||
@ -431,47 +432,11 @@ void CPatchRData::Update()
|
||||
ssize_t pz=m_Patch->m_Z;
|
||||
|
||||
CTerrain* terrain=m_Patch->m_Parent;
|
||||
ssize_t mapSize=terrain->GetVerticesPerSide();
|
||||
ssize_t vsize=PATCH_SIZE+1;
|
||||
SColor4ub baseColour = terrain->GetBaseColour();
|
||||
|
||||
/*
|
||||
if (g_Game) // XXX: need to implement this for new sim system
|
||||
{
|
||||
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
|
||||
|
||||
// this is very similar to BuildVertices(), but just for color
|
||||
for (ssize_t j=0;j<vsize;j++) {
|
||||
for (ssize_t i=0;i<vsize;i++) {
|
||||
ssize_t ix=px*PATCH_SIZE+i;
|
||||
ssize_t iz=pz*PATCH_SIZE+j;
|
||||
ssize_t v=(j*vsize)+i;
|
||||
|
||||
const ssize_t DX[] = {1,1,0,0};
|
||||
const ssize_t DZ[] = {0,1,1,0};
|
||||
SColor4ub losMod = baseColour;
|
||||
|
||||
for(size_t k=0; k<4; k++)
|
||||
{
|
||||
ssize_t tx = ix - DX[k];
|
||||
ssize_t tz = iz - DZ[k];
|
||||
|
||||
if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2)
|
||||
{
|
||||
ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer());
|
||||
if(s==LOS_EXPLORED && losMod.R > 178)
|
||||
losMod = SColor4ub(178, 178, 178, 255);
|
||||
else if(s==LOS_UNEXPLORED && losMod.R > 0)
|
||||
losMod = SColor4ub(0, 0, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
m_Vertices[v].m_LOSColor = losMod;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
*/
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
if (cmpRangeManager.null() || cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID()))
|
||||
{
|
||||
for (ssize_t j = 0; j < vsize; ++j)
|
||||
{
|
||||
@ -481,7 +446,31 @@ void CPatchRData::Update()
|
||||
m_Vertices[v].m_LOSColor = baseColour;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ICmpRangeManager::CLosQuerier los = cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID());
|
||||
|
||||
// this is very similar to BuildVertices(), but just for color
|
||||
for (ssize_t j = 0; j < vsize; j++)
|
||||
{
|
||||
for (ssize_t i = 0; i < vsize; i++)
|
||||
{
|
||||
ssize_t ix = px * PATCH_SIZE + i;
|
||||
ssize_t iz = pz * PATCH_SIZE + j;
|
||||
ssize_t v = (j * vsize) + i;
|
||||
|
||||
SColor4ub losMod;
|
||||
if (los.IsVisible(ix, iz))
|
||||
losMod = baseColour;
|
||||
else if (los.IsExplored(ix, iz))
|
||||
losMod = SColor4ub(178, 178, 178, 255);
|
||||
else
|
||||
losMod = SColor4ub(0, 0, 0, 255);
|
||||
|
||||
m_Vertices[v].m_LOSColor = losMod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// upload base vertices into their vertex buffer
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* 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
|
||||
@ -38,6 +38,7 @@
|
||||
#include "ps/World.h"
|
||||
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
|
||||
#include "renderer/PatchRData.h"
|
||||
#include "renderer/Renderer.h"
|
||||
@ -434,9 +435,11 @@ void TerrainRenderer::RenderWater()
|
||||
}
|
||||
}
|
||||
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
||||
// int mapSize = terrain->GetVerticesPerSide();
|
||||
|
||||
// CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
debug_assert(!cmpRangeManager.null());
|
||||
ICmpRangeManager::CLosQuerier los = cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID());
|
||||
bool losRevealAll = cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID());
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
@ -566,50 +569,35 @@ void TerrainRenderer::RenderWater()
|
||||
|
||||
// is any corner of the tile below the water height? if not, no point rendering it
|
||||
bool shouldRender = false;
|
||||
for(int j=0; j<4; j++)
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
float terrainHeight = terrain->GetVertexGroundLevel(x + DX[j], z + DZ[j]);
|
||||
if( terrainHeight < WaterMgr->m_WaterHeight )
|
||||
if (terrainHeight < WaterMgr->m_WaterHeight)
|
||||
{
|
||||
shouldRender = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!shouldRender)
|
||||
{
|
||||
if (!shouldRender)
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j=0; j<4; j++)
|
||||
{
|
||||
int ix = x + DX[j];
|
||||
int iz = z + DZ[j];
|
||||
ssize_t ix = x + DX[j];
|
||||
ssize_t iz = z + DZ[j];
|
||||
|
||||
float vertX = ix * CELL_SIZE;
|
||||
float vertZ = iz * CELL_SIZE;
|
||||
|
||||
float terrainHeight = terrain->GetVertexGroundLevel(ix, iz);
|
||||
float losMod = 1.0f;
|
||||
|
||||
/*
|
||||
if (false) // XXX: need to implement this for new sim system
|
||||
{
|
||||
for(size_t k=0; k<4; k++)
|
||||
{
|
||||
ssize_t tx = ix - DX[k];
|
||||
ssize_t tz = iz - DZ[k];
|
||||
|
||||
if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2)
|
||||
{
|
||||
ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer());
|
||||
if(s == LOS_EXPLORED && losMod > 0.7f)
|
||||
losMod = 0.7f;
|
||||
else if(s==LOS_UNEXPLORED && losMod > 0.0f)
|
||||
losMod = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
float losMod;
|
||||
if (losRevealAll || los.IsVisible(ix, iz))
|
||||
losMod = 1.0f;
|
||||
else if (los.IsExplored(ix, iz))
|
||||
losMod = 0.7f;
|
||||
else
|
||||
losMod = 0.0f;
|
||||
|
||||
if (fancy)
|
||||
{
|
||||
@ -672,5 +660,3 @@ void TerrainRenderer::RenderWater()
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
|
||||
|
@ -115,6 +115,9 @@ public:
|
||||
fixed turnLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare for rendering a new frame (set up model positions etc).
|
||||
*/
|
||||
class CMessageInterpolate : public CMessage
|
||||
{
|
||||
public:
|
||||
@ -129,6 +132,10 @@ public:
|
||||
float offset; // range [0, 1] (inclusive); fractional time of current frame between previous/next simulation turns
|
||||
};
|
||||
|
||||
/**
|
||||
* Add renderable objects to the scene collector.
|
||||
* Called after CMessageInterpolate.
|
||||
*/
|
||||
class CMessageRenderSubmit : public CMessage
|
||||
{
|
||||
public:
|
||||
|
@ -118,6 +118,9 @@ COMPONENT(Terrain)
|
||||
INTERFACE(UnitMotion)
|
||||
COMPONENT(UnitMotion) // must be after Obstruction
|
||||
|
||||
INTERFACE(Vision)
|
||||
COMPONENT(Vision)
|
||||
|
||||
INTERFACE(Visual)
|
||||
COMPONENT(VisualActor)
|
||||
|
||||
|
@ -160,7 +160,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool GetRenderData(u8& r, u8& g, u8& b, float& x, float& z)
|
||||
virtual bool GetRenderData(u8& r, u8& g, u8& b, entity_pos_t& x, entity_pos_t& z)
|
||||
{
|
||||
if (!m_Active)
|
||||
return false;
|
||||
@ -168,8 +168,8 @@ public:
|
||||
r = m_R;
|
||||
g = m_G;
|
||||
b = m_B;
|
||||
x = m_X.ToFloat();
|
||||
z = m_Z.ToFloat();
|
||||
x = m_X;
|
||||
z = m_Z;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "ICmpRangeManager.h"
|
||||
|
||||
#include "ICmpPosition.h"
|
||||
#include "ICmpVision.h"
|
||||
#include "simulation2/MessageTypes.h"
|
||||
#include "simulation2/helpers/Render.h"
|
||||
#include "simulation2/helpers/Spatial.h"
|
||||
@ -49,11 +50,11 @@ struct Query
|
||||
|
||||
/**
|
||||
* Convert an owner ID (-1 = unowned, 0 = gaia, 1..30 = players)
|
||||
* into a 31-bit mask for quick set-membership tests.
|
||||
* into a 32-bit mask for quick set-membership tests.
|
||||
*/
|
||||
static u32 CalcOwnerMask(i32 owner)
|
||||
{
|
||||
if (owner >= -1 && owner < 30)
|
||||
if (owner >= -1 && owner < 31)
|
||||
return 1 << (1+owner);
|
||||
else
|
||||
return 0; // owner was invalid
|
||||
@ -64,13 +65,15 @@ static u32 CalcOwnerMask(i32 owner)
|
||||
*/
|
||||
struct EntityData
|
||||
{
|
||||
EntityData() : ownerMask(CalcOwnerMask(-1)), inWorld(0) { }
|
||||
EntityData() : retainInFog(0), owner(-1), inWorld(0) { }
|
||||
entity_pos_t x, z;
|
||||
u32 ownerMask : 31;
|
||||
u32 inWorld : 1;
|
||||
entity_pos_t visionRange;
|
||||
u8 retainInFog; // boolean
|
||||
i8 owner;
|
||||
u8 inWorld; // boolean
|
||||
};
|
||||
|
||||
cassert(sizeof(EntityData) == 12);
|
||||
cassert(sizeof(EntityData) == 16);
|
||||
|
||||
/**
|
||||
* Functor for sorting entities by distance from a source point.
|
||||
@ -101,12 +104,13 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic range manager implementation.
|
||||
* Range manager implementation.
|
||||
* Maintains a list of all entities (and their positions and owners), which is used for
|
||||
* queries.
|
||||
*
|
||||
* TODO: Ideally this would use a quadtree or something for more efficient spatial queries,
|
||||
* since it's about O(n^2) in the total number of entities on the map.
|
||||
* LOS implementation is based on the model described in GPG2.
|
||||
* (TODO: would be nice to make it cleverer, so e.g. mountains and walls
|
||||
* can block vision)
|
||||
*/
|
||||
class CCmpRangeManager : public ICmpRangeManager
|
||||
{
|
||||
@ -131,10 +135,27 @@ public:
|
||||
|
||||
SpatialSubdivision<entity_id_t> m_Subdivision;
|
||||
|
||||
// Range query state:
|
||||
tag_t m_QueryNext; // next allocated id
|
||||
std::map<tag_t, Query> m_Queries;
|
||||
std::map<entity_id_t, EntityData> m_EntityData;
|
||||
|
||||
// LOS state:
|
||||
|
||||
bool m_LosRevealAll;
|
||||
ssize_t m_TerrainVerticesPerSide;
|
||||
|
||||
// Counts of units seeing vertex, per vertex, per player (starting with player 0).
|
||||
// Use u16 to avoid overflows when we have very large (but not infeasibly large) numbers
|
||||
// of units in a very small area.
|
||||
// (Note we use vertexes, not tiles, to better match the renderer.)
|
||||
// Lazily constructed when it's needed, to save memory in smaller games.
|
||||
std::vector<std::vector<u16> > m_LosPlayerCounts;
|
||||
|
||||
// 2-bit ELosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive)
|
||||
std::vector<u32> m_LosState;
|
||||
static const int MAX_LOS_PLAYER_ID = 16;
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
return "<a:component type='system'/><empty/>";
|
||||
@ -150,6 +171,9 @@ public:
|
||||
// Initialise with bogus values (these will get replaced when
|
||||
// SetBounds is called)
|
||||
ResetSubdivisions(entity_pos_t::FromInt(1), entity_pos_t::FromInt(1));
|
||||
|
||||
m_LosRevealAll = false;
|
||||
m_TerrainVerticesPerSide = 0;
|
||||
}
|
||||
|
||||
virtual void Deinit(const CSimContext& UNUSED(context))
|
||||
@ -189,6 +213,14 @@ public:
|
||||
// use the default-constructed EntityData here
|
||||
EntityData entdata;
|
||||
|
||||
// Store the LOS data, if any
|
||||
CmpPtr<ICmpVision> cmpVision(GetSimContext(), ent);
|
||||
if (!cmpVision.null())
|
||||
{
|
||||
entdata.visionRange = cmpVision->GetRange();
|
||||
entdata.retainInFog = (cmpVision->GetRetainInFog() ? 1 : 0);
|
||||
}
|
||||
|
||||
// Remember this entity
|
||||
m_EntityData.insert(std::make_pair(ent, entdata));
|
||||
|
||||
@ -208,9 +240,18 @@ public:
|
||||
if (msgData.inWorld)
|
||||
{
|
||||
if (it->second.inWorld)
|
||||
m_Subdivision.Move(ent, CFixedVector2D(it->second.x, it->second.z), CFixedVector2D(msgData.x, msgData.z));
|
||||
{
|
||||
CFixedVector2D from(it->second.x, it->second.z);
|
||||
CFixedVector2D to(msgData.x, msgData.z);
|
||||
m_Subdivision.Move(ent, from, to);
|
||||
LosMove(it->second.owner, it->second.visionRange, from, to);
|
||||
}
|
||||
else
|
||||
m_Subdivision.Add(ent, CFixedVector2D(msgData.x, msgData.z));
|
||||
{
|
||||
CFixedVector2D to(msgData.x, msgData.z);
|
||||
m_Subdivision.Add(ent, to);
|
||||
LosAdd(it->second.owner, it->second.visionRange, to);
|
||||
}
|
||||
|
||||
it->second.inWorld = 1;
|
||||
it->second.x = msgData.x;
|
||||
@ -219,7 +260,11 @@ public:
|
||||
else
|
||||
{
|
||||
if (it->second.inWorld)
|
||||
m_Subdivision.Remove(ent, CFixedVector2D(it->second.x, it->second.z));
|
||||
{
|
||||
CFixedVector2D from(it->second.x, it->second.z);
|
||||
m_Subdivision.Remove(ent, from);
|
||||
LosRemove(it->second.owner, it->second.visionRange, from);
|
||||
}
|
||||
|
||||
it->second.inWorld = 0;
|
||||
it->second.x = entity_pos_t::Zero();
|
||||
@ -239,7 +284,14 @@ public:
|
||||
if (it == m_EntityData.end())
|
||||
break;
|
||||
|
||||
it->second.ownerMask = CalcOwnerMask(msgData.to);
|
||||
if (it->second.inWorld)
|
||||
{
|
||||
CFixedVector2D pos(it->second.x, it->second.z);
|
||||
LosRemove(it->second.owner, it->second.visionRange, pos);
|
||||
LosAdd(msgData.to, it->second.visionRange, pos);
|
||||
}
|
||||
|
||||
it->second.owner = msgData.to;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -276,10 +328,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1)
|
||||
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices)
|
||||
{
|
||||
debug_assert(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet
|
||||
ResetSubdivisions(x1, z1);
|
||||
|
||||
m_TerrainVerticesPerSide = vertices;
|
||||
m_LosPlayerCounts.clear();
|
||||
m_LosPlayerCounts.resize(MAX_LOS_PLAYER_ID+1);
|
||||
m_LosState.clear();
|
||||
m_LosState.resize(vertices*vertices);
|
||||
|
||||
for (std::map<u32, EntityData>::iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it)
|
||||
LosAdd(it->second.owner, it->second.visionRange, CFixedVector2D(it->second.x, it->second.z));
|
||||
}
|
||||
|
||||
void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1)
|
||||
@ -482,7 +543,7 @@ private:
|
||||
debug_assert(it != m_EntityData.end());
|
||||
|
||||
// Quick filter to ignore entities with the wrong owner
|
||||
if (!(it->second.ownerMask & q.ownersMask))
|
||||
if (!(CalcOwnerMask(it->second.owner) & q.ownersMask))
|
||||
continue;
|
||||
|
||||
// Restrict based on precise location
|
||||
@ -573,6 +634,158 @@ private:
|
||||
for (size_t i = 0; i < m_DebugOverlayLines.size(); ++i)
|
||||
collector.Submit(&m_DebugOverlayLines[i]);
|
||||
}
|
||||
|
||||
|
||||
// LOS implementation:
|
||||
|
||||
virtual CLosQuerier GetLosQuerier(int player)
|
||||
{
|
||||
return CLosQuerier(player, m_LosState, m_TerrainVerticesPerSide);
|
||||
}
|
||||
|
||||
virtual ELosVisibility GetLosVisibility(entity_id_t ent, int player)
|
||||
{
|
||||
// (We can't use m_EntityData since this needs to handle LOCAL entities too)
|
||||
|
||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
|
||||
if (cmpPosition.null() || !cmpPosition->IsInWorld())
|
||||
return VIS_HIDDEN;
|
||||
|
||||
if (m_LosRevealAll)
|
||||
return VIS_VISIBLE;
|
||||
|
||||
CFixedVector2D pos = cmpPosition->GetPosition2D();
|
||||
|
||||
CLosQuerier los(player, m_LosState, m_TerrainVerticesPerSide);
|
||||
|
||||
int i = (pos.X / (int)CELL_SIZE).ToInt_RoundToNearest();
|
||||
int j = (pos.Y / (int)CELL_SIZE).ToInt_RoundToNearest();
|
||||
|
||||
if (los.IsVisible(i, j))
|
||||
return VIS_VISIBLE;
|
||||
|
||||
if (los.IsExplored(i, j))
|
||||
{
|
||||
CmpPtr<ICmpVision> cmpVision(GetSimContext(), ent);
|
||||
if (!cmpVision.null() && cmpVision->GetRetainInFog())
|
||||
return VIS_FOGGED;
|
||||
}
|
||||
|
||||
return VIS_HIDDEN;
|
||||
}
|
||||
|
||||
virtual void SetLosRevealAll(bool enabled)
|
||||
{
|
||||
// Eventually we might want this to be a per-player flag (which is why
|
||||
// GetLosRevealAll takes a player argument), but currently it's just a
|
||||
// global setting since I can't quite work out where per-player would be useful
|
||||
|
||||
m_LosRevealAll = enabled;
|
||||
}
|
||||
|
||||
virtual bool GetLosRevealAll(int UNUSED(player))
|
||||
{
|
||||
return m_LosRevealAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive).
|
||||
* amount is +1 or -1.
|
||||
*/
|
||||
inline void LosUpdateStripHelper(u8 owner, ssize_t i0, ssize_t i1, ssize_t j, int amount, std::vector<u16>& counts)
|
||||
{
|
||||
for (ssize_t i = i0; i <= i1; ++i)
|
||||
{
|
||||
ssize_t idx = j*m_TerrainVerticesPerSide + i;
|
||||
|
||||
// Increasing from zero to non-zero - move from unexplored/explored to visible+explored
|
||||
if (counts[idx] == 0 && amount > 0)
|
||||
{
|
||||
m_LosState[idx] |= ((LOS_VISIBLE | LOS_EXPLORED) << (2*(owner-1)));
|
||||
}
|
||||
|
||||
counts[idx] += amount;
|
||||
|
||||
// Decreasing from non-zero to zero - move from visible+explored to explored
|
||||
if (counts[idx] == 0 && amount < 0)
|
||||
{
|
||||
m_LosState[idx] &= ~(LOS_VISIBLE << (2*(owner-1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the LOS state of tiles within a given circular range.
|
||||
* Assumes owner is in the valid range.
|
||||
*/
|
||||
inline void LosUpdateHelper(u8 owner, entity_pos_t visionRange, CFixedVector2D pos, int amount)
|
||||
{
|
||||
if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet
|
||||
return;
|
||||
|
||||
PROFILE("LosUpdateHelper");
|
||||
|
||||
std::vector<u16>& counts = m_LosPlayerCounts.at(owner);
|
||||
|
||||
// Lazy initialisation of counts:
|
||||
if (counts.empty())
|
||||
counts.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide);
|
||||
|
||||
// Compute the circular region as a series of strips.
|
||||
// Rather than quantise pos to vertexes, we do more precise sub-tile computations
|
||||
// to get smoother behaviour as a unit moves rather than jumping a whole tile
|
||||
// at once.
|
||||
|
||||
// Compute top/bottom coordinates, and clamp to exclude the 1-tile border around the map
|
||||
// (so that we never render the sharp edge of the map)
|
||||
ssize_t j0 = ((pos.Y - visionRange)/(int)CELL_SIZE).ToInt_RoundToInfinity();
|
||||
ssize_t j1 = ((pos.Y + visionRange)/(int)CELL_SIZE).ToInt_RoundToNegInfinity();
|
||||
ssize_t j0clamp = std::max(j0, (ssize_t)1);
|
||||
ssize_t j1clamp = std::min(j1, m_TerrainVerticesPerSide-2);
|
||||
|
||||
entity_pos_t xscale = pos.X / (int)CELL_SIZE;
|
||||
entity_pos_t yscale = pos.Y / (int)CELL_SIZE;
|
||||
entity_pos_t rsquared = (visionRange / (int)CELL_SIZE).Square();
|
||||
|
||||
for (ssize_t j = j0clamp; j <= j1clamp; ++j)
|
||||
{
|
||||
// Compute values such that (i - x)^2 + (j - y)^2 <= r^2
|
||||
// (TODO: is this sqrt slow? can we optimise it?)
|
||||
entity_pos_t di = (rsquared - (entity_pos_t::FromInt(j) - yscale).Square()).Sqrt();
|
||||
ssize_t i0 = (xscale - di).ToInt_RoundToInfinity();
|
||||
ssize_t i1 = (xscale + di).ToInt_RoundToNegInfinity();
|
||||
|
||||
ssize_t i0clamp = std::max(i0, (ssize_t)1);
|
||||
ssize_t i1clamp = std::min(i1, m_TerrainVerticesPerSide-2);
|
||||
LosUpdateStripHelper(owner, i0clamp, i1clamp, j, amount, counts);
|
||||
}
|
||||
}
|
||||
|
||||
void LosAdd(i8 owner, entity_pos_t visionRange, CFixedVector2D pos)
|
||||
{
|
||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID)
|
||||
return;
|
||||
|
||||
LosUpdateHelper(owner, visionRange, pos, 1);
|
||||
}
|
||||
|
||||
void LosRemove(i8 owner, entity_pos_t visionRange, CFixedVector2D pos)
|
||||
{
|
||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID)
|
||||
return;
|
||||
|
||||
LosUpdateHelper(owner, visionRange, pos, -1);
|
||||
}
|
||||
|
||||
void LosMove(i8 owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to)
|
||||
{
|
||||
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID)
|
||||
return;
|
||||
|
||||
// TODO: we could optimise this by only modifying tiles that changed
|
||||
LosRemove(owner, visionRange, from);
|
||||
LosAdd(owner, visionRange, to);
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(RangeManager)
|
||||
|
@ -433,9 +433,15 @@ void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& i
|
||||
if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Obstruction><Inactive/></Obstruction></Entity>");
|
||||
|
||||
// Corpses should include decay components and un-inactivate them
|
||||
if (!corpse)
|
||||
{
|
||||
// Previews should always be visible in fog-of-war
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range><RetainInFog>true</RetainInFog></Vision></Entity>");
|
||||
}
|
||||
|
||||
if (corpse)
|
||||
{
|
||||
// Corpses should include decay components and un-inactivate them
|
||||
if (out.GetChild("Entity").GetChild("Decay").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Decay><Inactive disable=''/></Decay></Entity>");
|
||||
}
|
||||
@ -459,6 +465,7 @@ void CCmpTemplateManager::CopyFoundationSubset(CParamNode& out, const CParamNode
|
||||
permittedComponentTypes.insert("Decay");
|
||||
permittedComponentTypes.insert("Cost");
|
||||
permittedComponentTypes.insert("Sound");
|
||||
permittedComponentTypes.insert("Vision");
|
||||
|
||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||
@ -478,4 +485,9 @@ void CCmpTemplateManager::CopyFoundationSubset(CParamNode& out, const CParamNode
|
||||
// Don't provide population bonuses yet (but still do take up population cost)
|
||||
if (out.GetChild("Entity").GetChild("Cost").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Cost><PopulationBonus>0</PopulationBonus></Cost></Entity>");
|
||||
|
||||
// Foundations should be visible themselves in fog-of-war if their base template is,
|
||||
// but shouldn't have any vision range
|
||||
if (out.GetChild("Entity").GetChild("Vision").IsOk())
|
||||
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
|
||||
}
|
||||
|
@ -94,7 +94,8 @@ public:
|
||||
{
|
||||
cmpRangeManager->SetBounds(entity_pos_t::Zero(), entity_pos_t::Zero(),
|
||||
entity_pos_t::FromInt(m_Terrain->GetTilesPerSide()*CELL_SIZE),
|
||||
entity_pos_t::FromInt(m_Terrain->GetTilesPerSide()*CELL_SIZE));
|
||||
entity_pos_t::FromInt(m_Terrain->GetTilesPerSide()*CELL_SIZE),
|
||||
m_Terrain->GetVerticesPerSide());
|
||||
}
|
||||
}
|
||||
|
||||
|
76
source/simulation2/components/CCmpVision.cpp
Normal file
76
source/simulation2/components/CCmpVision.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/* 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 "ICmpVision.h"
|
||||
|
||||
class CCmpVision: public ICmpVision
|
||||
{
|
||||
public:
|
||||
static void ClassInit(CComponentManager& componentManager)
|
||||
{
|
||||
}
|
||||
|
||||
DEFAULT_COMPONENT_ALLOCATOR(Vision)
|
||||
|
||||
entity_pos_t m_Range;
|
||||
bool m_RetainInFog;
|
||||
|
||||
static std::string GetSchema()
|
||||
{
|
||||
return
|
||||
"<element name='Range'>"
|
||||
"<data type='nonNegativeInteger'/>"
|
||||
"</element>"
|
||||
"<element name='RetainInFog'>"
|
||||
"<data type='boolean'/>"
|
||||
"</element>";
|
||||
}
|
||||
|
||||
virtual void Init(const CSimContext& UNUSED(context), const CParamNode& paramNode)
|
||||
{
|
||||
m_Range = paramNode.GetChild("Range").ToFixed();
|
||||
m_RetainInFog = paramNode.GetChild("RetainInFog").ToBool();
|
||||
}
|
||||
|
||||
virtual void Deinit(const CSimContext& UNUSED(context))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Serialize(ISerializer& UNUSED(serialize))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
|
||||
{
|
||||
Init(context, paramNode);
|
||||
}
|
||||
|
||||
virtual entity_pos_t GetRange()
|
||||
{
|
||||
return m_Range;
|
||||
}
|
||||
|
||||
virtual bool GetRetainInFog()
|
||||
{
|
||||
return m_RetainInFog;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(Vision)
|
@ -21,6 +21,8 @@
|
||||
#include "ICmpVisual.h"
|
||||
|
||||
#include "ICmpPosition.h"
|
||||
#include "ICmpRangeManager.h"
|
||||
#include "ICmpVision.h"
|
||||
#include "simulation2/MessageTypes.h"
|
||||
|
||||
#include "graphics/Frustum.h"
|
||||
@ -52,7 +54,7 @@ public:
|
||||
std::wstring m_ActorName;
|
||||
CUnit* m_Unit;
|
||||
|
||||
bool m_Hidden; // only valid between Interpolate and RenderSubmit
|
||||
ICmpRangeManager::ELosVisibility m_Visibility; // only valid between Interpolate and RenderSubmit
|
||||
|
||||
// Current animation state
|
||||
float m_AnimRunThreshold; // if non-zero this is the special walk/run mode
|
||||
@ -358,11 +360,15 @@ void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
|
||||
// Disable rendering of the unit if it has no position
|
||||
if (!cmpPosition->IsInWorld())
|
||||
{
|
||||
m_Hidden = true;
|
||||
m_Visibility = ICmpRangeManager::VIS_HIDDEN;
|
||||
return;
|
||||
}
|
||||
|
||||
m_Hidden = false;
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(GetSimContext(), SYSTEM_ENTITY);
|
||||
m_Visibility = cmpRangeManager->GetLosVisibility(GetEntityId(), GetSimContext().GetCurrentDisplayedPlayer());
|
||||
|
||||
// Even if HIDDEN due to LOS, we need to set up the transforms
|
||||
// so that projectiles will be launched from the right place
|
||||
|
||||
bool floating = m_Unit->GetObject().m_Base->m_Properties.m_FloatOnWater;
|
||||
|
||||
@ -377,11 +383,9 @@ void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& fr
|
||||
if (m_Unit == NULL)
|
||||
return;
|
||||
|
||||
if (m_Hidden)
|
||||
if (m_Visibility == ICmpRangeManager::VIS_HIDDEN)
|
||||
return;
|
||||
|
||||
// TODO: need to think about things like LOS here
|
||||
|
||||
CModel& model = m_Unit->GetModel();
|
||||
|
||||
model.ValidatePosition();
|
||||
@ -389,7 +393,10 @@ void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& fr
|
||||
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetBounds()))
|
||||
return;
|
||||
|
||||
// TODO: model->SetShadingColor(CColor(1.0f, 1.0f, 1.0f, 1.0f) if visible, CColor(0.7f, 0.7f, 0.7f, 1.0f) if hidden in FOW)
|
||||
if (m_Visibility == ICmpRangeManager::VIS_FOGGED)
|
||||
model.SetShadingColor(CColor(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
else
|
||||
model.SetShadingColor(CColor(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
collector.SubmitRecursive(&model);
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
#include "simulation2/helpers/Position.h"
|
||||
|
||||
/**
|
||||
* Per-unit minimap data.
|
||||
*/
|
||||
@ -31,7 +33,7 @@ public:
|
||||
* If it should not be drawn, returns false; otherwise the arguments are set
|
||||
* to the colour and world position.
|
||||
*/
|
||||
virtual bool GetRenderData(u8& r, u8& g, u8& b, float& x, float& z) = 0;
|
||||
virtual bool GetRenderData(u8& r, u8& g, u8& b, entity_pos_t& x, entity_pos_t& z) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(Minimap)
|
||||
};
|
||||
|
@ -29,4 +29,5 @@ DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableAct
|
||||
DEFINE_INTERFACE_METHOD_1("DisableActiveQuery", void, ICmpRangeManager, DisableActiveQuery, ICmpRangeManager::tag_t)
|
||||
DEFINE_INTERFACE_METHOD_1("ResetActiveQuery", std::vector<entity_id_t>, ICmpRangeManager, ResetActiveQuery, ICmpRangeManager::tag_t)
|
||||
DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpRangeManager, SetDebugOverlay, bool)
|
||||
DEFINE_INTERFACE_METHOD_1("SetLosRevealAll", void, ICmpRangeManager, SetLosRevealAll, bool)
|
||||
END_INTERFACE_WRAPPER(RangeManager)
|
||||
|
@ -22,8 +22,14 @@
|
||||
|
||||
#include "simulation2/helpers/Position.h"
|
||||
|
||||
#include "graphics/Terrain.h" // for CELL_SIZE
|
||||
|
||||
/**
|
||||
* Provides efficient range-based queries of the game world.
|
||||
* Provides efficient range-based queries of the game world,
|
||||
* and also LOS-based effects (fog of war).
|
||||
*
|
||||
* (These are somewhat distinct concepts but they share a lot of the implementation,
|
||||
* so for efficiency they're combined into this class.)
|
||||
*
|
||||
* Possible use cases:
|
||||
* - combat units need to detect targetable enemies entering LOS, so they can choose
|
||||
@ -67,8 +73,9 @@ public:
|
||||
* Set the bounds of the world.
|
||||
* Entities should not be outside the bounds (else efficiency will suffer).
|
||||
* @param x0,z0,x1,z1 Coordinates of the corners of the world
|
||||
* @param vertices Number of terrain vertices per side
|
||||
*/
|
||||
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) = 0;
|
||||
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices) = 0;
|
||||
|
||||
/**
|
||||
* Execute a passive query.
|
||||
@ -123,6 +130,98 @@ public:
|
||||
*/
|
||||
virtual void SetDebugOverlay(bool enabled) = 0;
|
||||
|
||||
// LOS interface:
|
||||
|
||||
enum ELosState
|
||||
{
|
||||
LOS_UNEXPLORED = 0,
|
||||
LOS_EXPLORED = 1,
|
||||
LOS_VISIBLE = 2,
|
||||
LOS_MASK = 3
|
||||
};
|
||||
|
||||
enum ELosVisibility
|
||||
{
|
||||
VIS_HIDDEN,
|
||||
VIS_FOGGED,
|
||||
VIS_VISIBLE
|
||||
};
|
||||
|
||||
/**
|
||||
* Object providing efficient abstracted access to the LOS state.
|
||||
* This depends on some implementation details of CCmpRangeManager.
|
||||
*
|
||||
* This *ignores* the GetLosRevealAll flag - callers should check that explicitly.
|
||||
*/
|
||||
class CLosQuerier
|
||||
{
|
||||
private:
|
||||
friend class CCmpRangeManager;
|
||||
|
||||
CLosQuerier(int player, const std::vector<u32>& data, ssize_t verticesPerSide) :
|
||||
m_Data(data), m_VerticesPerSide(verticesPerSide)
|
||||
{
|
||||
if (player > 0 && player <= 16)
|
||||
m_PlayerMask = LOS_MASK << (2*(player-1));
|
||||
else
|
||||
m_PlayerMask = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns whether the given vertex is visible (i.e. is within a unit's LOS).
|
||||
* i and j must be in the range [0, verticesPerSide).
|
||||
*/
|
||||
inline bool IsVisible(ssize_t i, ssize_t j)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
debug_assert(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide);
|
||||
#endif
|
||||
// Check high bit of each bit-pair
|
||||
return (m_Data.at(j*m_VerticesPerSide + i) & m_PlayerMask) & 0xAAAAAAAAu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given vertex is explored (i.e. was (or still is) within a unit's LOS).
|
||||
* i and j must be in the range [0, verticesPerSide).
|
||||
*/
|
||||
inline bool IsExplored(ssize_t i, ssize_t j)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
debug_assert(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide);
|
||||
#endif
|
||||
// Check low bit of each bit-pair
|
||||
return (m_Data.at(j*m_VerticesPerSide + i) & m_PlayerMask) & 0x55555555u;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 m_PlayerMask;
|
||||
const std::vector<u32>& m_Data;
|
||||
ssize_t m_VerticesPerSide;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a CLosQuerier for checking whether vertex positions are visible to the given player.
|
||||
*/
|
||||
virtual CLosQuerier GetLosQuerier(int player) = 0;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
virtual ELosVisibility GetLosVisibility(entity_id_t ent, int player) = 0;
|
||||
|
||||
/**
|
||||
* Set globally whether the whole map should be made visible.
|
||||
*/
|
||||
virtual void SetLosRevealAll(bool enabled) = 0;
|
||||
|
||||
/**
|
||||
* Returns whether the whole map has been made visible to the given player.
|
||||
*/
|
||||
virtual bool GetLosRevealAll(int player) = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(RangeManager)
|
||||
};
|
||||
|
||||
|
27
source/simulation2/components/ICmpVision.cpp
Normal file
27
source/simulation2/components/ICmpVision.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/* 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 "ICmpVision.h"
|
||||
|
||||
#include "simulation2/system/InterfaceScripted.h"
|
||||
|
||||
BEGIN_INTERFACE_WRAPPER(Vision)
|
||||
DEFINE_INTERFACE_METHOD_0("GetRange", entity_pos_t, ICmpVision, GetRange)
|
||||
DEFINE_INTERFACE_METHOD_0("GetRetainInFog", bool, ICmpVision, GetRetainInFog)
|
||||
END_INTERFACE_WRAPPER(Vision)
|
38
source/simulation2/components/ICmpVision.h
Normal file
38
source/simulation2/components/ICmpVision.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_ICMPVISION
|
||||
#define INCLUDED_ICMPVISION
|
||||
|
||||
#include "simulation2/system/Interface.h"
|
||||
|
||||
#include "simulation2/helpers/Position.h"
|
||||
|
||||
/**
|
||||
* Vision (LOS etc) interface (typically implemented by a scripted component).
|
||||
*/
|
||||
class ICmpVision : public IComponent
|
||||
{
|
||||
public:
|
||||
virtual entity_pos_t GetRange() = 0;
|
||||
|
||||
virtual bool GetRetainInFog() = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(Vision)
|
||||
};
|
||||
|
||||
#endif // INCLUDED_ICMPVISION
|
@ -22,14 +22,18 @@
|
||||
#include "graphics/Camera.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpOwnership.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
#include "simulation2/components/ICmpSelectable.h"
|
||||
#include "simulation2/components/ICmpVisual.h"
|
||||
|
||||
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY)
|
||||
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player)
|
||||
{
|
||||
CVector3D origin, dir;
|
||||
camera.BuildCameraRay(screenX, screenY, origin, dir);
|
||||
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
|
||||
debug_assert(!cmpRangeManager.null());
|
||||
|
||||
std::vector<std::pair<float, entity_id_t> > hits; // (dist^2, entity) pairs
|
||||
|
||||
const CSimulation2::InterfaceList& ents = simulation.GetEntitiesWithInterface(IID_Selectable);
|
||||
@ -37,7 +41,9 @@ std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simu
|
||||
{
|
||||
entity_id_t ent = it->first;
|
||||
|
||||
// TODO: ought to filter out units hidden by current player's LOS
|
||||
// Ignore entities hidden by LOS
|
||||
if (cmpRangeManager->GetLosVisibility(ent, player) == ICmpRangeManager::VIS_HIDDEN)
|
||||
continue;
|
||||
|
||||
CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
|
||||
if (cmpVisual.null())
|
||||
@ -81,6 +87,9 @@ std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simul
|
||||
|
||||
std::vector<entity_id_t> hitEnts;
|
||||
|
||||
// (We don't bother doing LOS checks for these units, since we assume 'owner'
|
||||
// will be the current local player and all their own units will always be visible)
|
||||
|
||||
const CSimulation2::InterfaceList& ents = simulation.GetEntitiesWithInterface(IID_Selectable);
|
||||
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
|
||||
{
|
||||
|
@ -36,8 +36,9 @@ namespace EntitySelection
|
||||
/**
|
||||
* Finds all selectable entities under the given screen coordinates.
|
||||
* Returns list ordered by closeness of picking, closest first.
|
||||
* Restricted to entities in the LOS of @p player, but with any owner.
|
||||
*/
|
||||
std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY);
|
||||
std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player);
|
||||
|
||||
/**
|
||||
* Finds all selectable entities within the given screen coordinate rectangle,
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
#include "ComponentManager.h"
|
||||
|
||||
#include "ps/Game.h"
|
||||
|
||||
CSimContext::CSimContext() :
|
||||
m_ComponentManager(NULL), m_UnitManager(NULL), m_Terrain(NULL)
|
||||
{
|
||||
@ -63,3 +65,10 @@ ScriptInterface& CSimContext::GetScriptInterface() const
|
||||
{
|
||||
return GetComponentManager().GetScriptInterface();
|
||||
}
|
||||
|
||||
int CSimContext::GetCurrentDisplayedPlayer() const
|
||||
{
|
||||
if (!g_Game)
|
||||
return -1;
|
||||
return g_Game->GetPlayerID();
|
||||
}
|
||||
|
@ -43,6 +43,12 @@ public:
|
||||
|
||||
ScriptInterface& GetScriptInterface() const;
|
||||
|
||||
/**
|
||||
* Returns the player ID that the current display is being rendered for.
|
||||
* Currently relies on g_Game being initialised (evil globals...)
|
||||
*/
|
||||
int GetCurrentDisplayedPlayer() const;
|
||||
|
||||
private:
|
||||
CComponentManager* m_ComponentManager;
|
||||
CUnitManager* m_UnitManager;
|
||||
|
@ -81,15 +81,26 @@ public:
|
||||
|
||||
const CParamNode* preview = tempMan->LoadTemplate(ent2, "preview|unit", -1);
|
||||
TS_ASSERT(preview != NULL);
|
||||
TS_ASSERT_WSTR_EQUALS(preview->ToXML(), L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example</Actor></VisualActor>");
|
||||
TS_ASSERT_WSTR_EQUALS(preview->ToXML(),
|
||||
L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position>"
|
||||
L"<Vision><Range>0</Range><RetainInFog>true</RetainInFog></Vision>"
|
||||
L"<VisualActor><Actor>example</Actor></VisualActor>");
|
||||
|
||||
const CParamNode* previewobstruct = tempMan->LoadTemplate(ent2, "preview|unitobstruct", -1);
|
||||
TS_ASSERT(previewobstruct != NULL);
|
||||
TS_ASSERT_WSTR_EQUALS(previewobstruct->ToXML(), L"<Footprint><Circle radius=\"4\"></Circle><Height>1.0</Height></Footprint><Obstruction><Inactive></Inactive><Unit radius=\"4\"></Unit></Obstruction><Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example</Actor></VisualActor>");
|
||||
TS_ASSERT_WSTR_EQUALS(previewobstruct->ToXML(),
|
||||
L"<Footprint><Circle radius=\"4\"></Circle><Height>1.0</Height></Footprint>"
|
||||
L"<Obstruction><Inactive></Inactive><Unit radius=\"4\"></Unit></Obstruction>"
|
||||
L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position>"
|
||||
L"<Vision><Range>0</Range><RetainInFog>true</RetainInFog></Vision>"
|
||||
L"<VisualActor><Actor>example</Actor></VisualActor>");
|
||||
|
||||
const CParamNode* previewactor = tempMan->LoadTemplate(ent2, "preview|actor|example2", -1);
|
||||
TS_ASSERT(previewactor != NULL);
|
||||
TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(), L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example2</Actor></VisualActor>");
|
||||
TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(),
|
||||
L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position>"
|
||||
L"<Vision><Range>0</Range><RetainInFog>true</RetainInFog></Vision>"
|
||||
L"<VisualActor><Actor>example2</Actor></VisualActor>");
|
||||
}
|
||||
|
||||
void test_LoadTemplate_scriptcache()
|
||||
|
@ -40,6 +40,8 @@
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/components/ICmpPosition.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
#include "simulation2/components/ICmpTerrain.h"
|
||||
#include "simulation2/components/ICmpUnitMotion.h"
|
||||
#include "simulation2/components/ICmpVisual.h"
|
||||
|
||||
@ -116,6 +118,15 @@ ActorViewer::ActorViewer()
|
||||
// Start the simulation
|
||||
m.Simulation2.LoadDefaultScripts();
|
||||
m.Simulation2.ResetState();
|
||||
|
||||
// Tell the simulation we've already loaded the terrain
|
||||
CmpPtr<ICmpTerrain> cmpTerrain(m.Simulation2, SYSTEM_ENTITY);
|
||||
if (!cmpTerrain.null())
|
||||
cmpTerrain->ReloadTerrain();
|
||||
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(m.Simulation2, SYSTEM_ENTITY);
|
||||
if (!cmpRangeManager.null())
|
||||
cmpRangeManager->SetLosRevealAll(true);
|
||||
}
|
||||
|
||||
ActorViewer::~ActorViewer()
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "simulation2/components/ICmpPlayer.h"
|
||||
#include "simulation2/components/ICmpPlayerManager.h"
|
||||
#include "simulation2/components/ICmpPosition.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -66,6 +67,11 @@ namespace
|
||||
LDR_NonprogressiveLoad();
|
||||
PSRETURN ret = g_Game->ReallyStartGame();
|
||||
debug_assert(ret == PSRETURN_OK);
|
||||
|
||||
// Disable fog-of-war
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||
if (!cmpRangeManager.null())
|
||||
cmpRangeManager->SetLosRevealAll(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user