Index: ps/trunk/binaries/data/mods/public/simulation/components/Vision.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Vision.js (revision 8159)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Vision.js (nonexistent)
@@ -1,22 +0,0 @@
-function Vision() {}
-
-Vision.prototype.Schema =
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "";
-
-/*
- * TODO: this all needs to be designed and implemented
- */
-
-Vision.prototype.GetRange = function()
-{
- return +this.template.Range;
-};
-
-Engine.RegisterComponentType(IID_Vision, "Vision", Vision);
Index: ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Vision.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Vision.js (revision 8159)
+++ ps/trunk/binaries/data/mods/public/simulation/components/interfaces/Vision.js (nonexistent)
@@ -1 +0,0 @@
-Engine.RegisterInterface("Vision");
Index: ps/trunk/source/maths/tests/test_Fixed.h
===================================================================
--- ps/trunk/source/maths/tests/test_Fixed.h (revision 8159)
+++ ps/trunk/source/maths/tests/test_Fixed.h (revision 8160)
@@ -1,306 +1,322 @@
/* 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 .
*/
#include "lib/self_test.h"
#include "maths/Fixed.h"
#include "maths/MathUtil.h"
class TestFixed : public CxxTest::TestSuite
{
public:
void test_basic()
{
fixed a = fixed::FromInt(12345);
TS_ASSERT_EQUALS((a + a).ToDouble(), 24690);
TS_ASSERT_EQUALS((a - a).ToDouble(), 0);
TS_ASSERT_EQUALS((-a).ToDouble(), -12345);
TS_ASSERT_EQUALS((a / a).ToDouble(), 1);
TS_ASSERT_EQUALS((a / 823).ToDouble(), 15);
TS_ASSERT_EQUALS((a * 2).ToDouble(), 24690);
}
void test_FromInt()
{
fixed a = fixed::FromInt(123);
TS_ASSERT_EQUALS(a.ToFloat(), 123.0f);
TS_ASSERT_EQUALS(a.ToDouble(), 123.0);
TS_ASSERT_EQUALS(a.ToInt_RoundToZero(), 123);
}
void test_FromFloat()
{
fixed a = fixed::FromFloat(123.125f);
TS_ASSERT_EQUALS(a.ToFloat(), 123.125f);
TS_ASSERT_EQUALS(a.ToDouble(), 123.125);
fixed b = fixed::FromFloat(-123.125f);
TS_ASSERT_EQUALS(b.ToFloat(), -123.125f);
TS_ASSERT_EQUALS(b.ToDouble(), -123.125);
fixed c = fixed::FromFloat(INFINITY);
TS_ASSERT_EQUALS(c.GetInternalValue(), (i32)0);
fixed d = fixed::FromFloat(-INFINITY);
TS_ASSERT_EQUALS(d.GetInternalValue(), (i32)0);
fixed e = fixed::FromFloat(NAN);
TS_ASSERT_EQUALS(e.GetInternalValue(), (i32)0);
}
void test_FromDouble()
{
fixed a = fixed::FromDouble(123.125);
TS_ASSERT_EQUALS(a.ToFloat(), 123.125f);
TS_ASSERT_EQUALS(a.ToDouble(), 123.125);
fixed b = fixed::FromDouble(-123.125);
TS_ASSERT_EQUALS(b.ToFloat(), -123.125f);
TS_ASSERT_EQUALS(b.ToDouble(), -123.125);
fixed c = fixed::FromDouble(INFINITY);
TS_ASSERT_EQUALS(c.GetInternalValue(), (i32)0);
fixed d = fixed::FromDouble(-INFINITY);
TS_ASSERT_EQUALS(d.GetInternalValue(), (i32)0);
fixed e = fixed::FromDouble(NAN);
TS_ASSERT_EQUALS(e.GetInternalValue(), (i32)0);
}
void test_FromFloat_Rounding()
{
double eps = pow(2.0, -16.0);
TS_ASSERT_EQUALS(fixed::FromFloat(eps).ToDouble(), eps);
TS_ASSERT_EQUALS(fixed::FromFloat(eps * 0.5625).ToDouble(), eps);
TS_ASSERT_EQUALS(fixed::FromFloat(eps * 0.5).ToDouble(), eps);
TS_ASSERT_EQUALS(fixed::FromFloat(eps * 0.4375).ToDouble(), 0.0);
TS_ASSERT_EQUALS(fixed::FromFloat(-eps).ToDouble(), -eps);
TS_ASSERT_EQUALS(fixed::FromFloat(-eps * 0.5625).ToDouble(), -eps);
TS_ASSERT_EQUALS(fixed::FromFloat(-eps * 0.5).ToDouble(), -eps);
TS_ASSERT_EQUALS(fixed::FromFloat(-eps * 0.4375).ToDouble(), 0.0);
}
void test_FromString()
{
TS_ASSERT_EQUALS(fixed::FromString("").ToDouble(), 0.0);
TS_ASSERT_EQUALS(fixed::FromString("123").ToDouble(), 123.0);
TS_ASSERT_EQUALS(fixed::FromString("+123").ToDouble(), 123.0);
TS_ASSERT_EQUALS(fixed::FromString("-123").ToDouble(), -123.0);
TS_ASSERT_EQUALS(fixed::FromString("123.5").ToDouble(), 123.5);
TS_ASSERT_EQUALS(fixed::FromString("-123.5").ToDouble(), -123.5);
TS_ASSERT_EQUALS(fixed::FromString(".5").ToDouble(), 0.5);
TS_ASSERT_EQUALS(fixed::FromString("5.").ToDouble(), 5.0);
TS_ASSERT_EQUALS(fixed::FromString("254.391").GetInternalValue(), 16671768);
TS_ASSERT_EQUALS(fixed::FromString("123x456").ToDouble(), 123.0);
TS_ASSERT_EQUALS(fixed::FromString("1.00002").ToDouble(), 1.0 + 1.0/65536.0);
TS_ASSERT_EQUALS(fixed::FromString("1.0000200000000000000000").ToDouble(), 1.0 + 1.0/65536.0);
TS_ASSERT_EQUALS(fixed::FromString("1.000009").ToDouble(), 1.0);
TS_ASSERT_EQUALS(fixed::FromString("0.9999999999999999999999").ToDouble(), 1.0 - 1.0/65536.0);
// TODO: could test more large/small numbers, errors, etc
}
void test_ToString()
{
#define T(n, str) { fixed f = fixed::FromDouble(n); TS_ASSERT_STR_EQUALS(f.ToString(), str); TS_ASSERT_EQUALS(fixed::FromString(f.ToString()), f); }
T(1.0, "1");
T(-1.0, "-1");
T(10000.0, "10000");
T(1.25, "1.25");
T(-1.25, "-1.25");
T(0.5, "0.5");
T(1.0/65536.0, "0.00002");
T(2.0/65536.0, "0.00004");
T(250367.0/65536.0, "3.8203");
T(32768.0 - 1.0/65536.0, "32767.99999");
T(-32768.0 + 1.0/65536.0, "-32767.99999");
#undef T
for (int i = 0; i < 65536; ++i)
{
fixed f = fixed::FromDouble(i / 65536.0);
TS_ASSERT_EQUALS(fixed::FromString(f.ToString()), f);
}
}
void test_RoundToZero()
{
TS_ASSERT_EQUALS(fixed::FromFloat(10.f).ToInt_RoundToZero(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.1f).ToInt_RoundToZero(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.5f).ToInt_RoundToZero(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.99f).ToInt_RoundToZero(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(0.1f).ToInt_RoundToZero(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(0.0f).ToInt_RoundToZero(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-0.1f).ToInt_RoundToZero(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-0.99f).ToInt_RoundToZero(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-1.0f).ToInt_RoundToZero(), -1);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.0f).ToInt_RoundToZero(), -2);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.5f).ToInt_RoundToZero(), -2);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.99f).ToInt_RoundToZero(), -2);
}
void test_RoundToInfinity()
{
TS_ASSERT_EQUALS(fixed::FromFloat(10.f).ToInt_RoundToInfinity(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.1f).ToInt_RoundToInfinity(), 11);
TS_ASSERT_EQUALS(fixed::FromFloat(10.5f).ToInt_RoundToInfinity(), 11);
TS_ASSERT_EQUALS(fixed::FromFloat(10.99f).ToInt_RoundToInfinity(), 11);
TS_ASSERT_EQUALS(fixed::FromFloat(0.1f).ToInt_RoundToInfinity(), 1);
TS_ASSERT_EQUALS(fixed::FromFloat(0.0f).ToInt_RoundToInfinity(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-0.1f).ToInt_RoundToInfinity(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-0.99f).ToInt_RoundToInfinity(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-1.0f).ToInt_RoundToInfinity(), -1);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.0f).ToInt_RoundToInfinity(), -2);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.5f).ToInt_RoundToInfinity(), -2);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.99f).ToInt_RoundToInfinity(), -2);
}
void test_RoundToNegInfinity()
{
TS_ASSERT_EQUALS(fixed::FromFloat(10.f).ToInt_RoundToNegInfinity(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.1f).ToInt_RoundToNegInfinity(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.5f).ToInt_RoundToNegInfinity(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.99f).ToInt_RoundToNegInfinity(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(0.1f).ToInt_RoundToNegInfinity(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(0.0f).ToInt_RoundToNegInfinity(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-0.1f).ToInt_RoundToNegInfinity(), -1);
TS_ASSERT_EQUALS(fixed::FromFloat(-0.99f).ToInt_RoundToNegInfinity(), -1);
TS_ASSERT_EQUALS(fixed::FromFloat(-1.0f).ToInt_RoundToNegInfinity(), -1);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.0f).ToInt_RoundToNegInfinity(), -2);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.5f).ToInt_RoundToNegInfinity(), -3);
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()
{
TS_ASSERT_EQUALS(fixed::FromInt(0) % fixed::FromInt(4), fixed::FromInt(0));
TS_ASSERT_EQUALS(fixed::FromInt(0) % fixed::FromInt(-4), fixed::FromInt(0));
TS_ASSERT_EQUALS(fixed::FromInt(5) % fixed::FromInt(4), fixed::FromInt(1));
TS_ASSERT_EQUALS(fixed::FromInt(5) % fixed::FromInt(-4), fixed::FromInt(-3));
TS_ASSERT_EQUALS(fixed::FromInt(-5) % fixed::FromInt(4), fixed::FromInt(3));
TS_ASSERT_EQUALS(fixed::FromInt(-5) % fixed::FromInt(-4), fixed::FromInt(-1));
TS_ASSERT_EQUALS((fixed::FromDouble(5.5) % fixed::FromInt(4)).ToDouble(), 1.5);
TS_ASSERT_EQUALS((fixed::FromDouble(1.75) % fixed::FromDouble(0.5)).ToDouble(), 0.25);
}
void test_Sqrt()
{
TS_ASSERT_EQUALS(fixed::FromDouble(1.0).Sqrt().ToDouble(), 1.0);
TS_ASSERT_DELTA(fixed::FromDouble(2.0).Sqrt().ToDouble(), sqrt(2.0), 1.0/65536.0);
TS_ASSERT_EQUALS(fixed::FromDouble(32400.0).Sqrt().ToDouble(), 180.0);
TS_ASSERT_EQUALS(fixed::FromDouble(0.0625).Sqrt().ToDouble(), 0.25);
TS_ASSERT_EQUALS(fixed::FromDouble(-1.0).Sqrt().ToDouble(), 0.0);
TS_ASSERT_EQUALS(fixed::FromDouble(0.0).Sqrt().ToDouble(), 0.0);
}
void test_Atan2()
{
// Special cases from atan2 man page:
TS_ASSERT_DELTA(atan2_approx(fixed::FromInt(0), fixed::FromInt(-1)).ToDouble(), M_PI, 0.01);
TS_ASSERT_EQUALS(atan2_approx(fixed::FromInt(0), fixed::FromInt(+1)).ToDouble(), 0);
TS_ASSERT_DELTA(atan2_approx(fixed::FromInt(-1), fixed::FromInt(0)).ToDouble(), -M_PI_2, 0.01);
TS_ASSERT_DELTA(atan2_approx(fixed::FromInt(+1), fixed::FromInt(0)).ToDouble(), +M_PI_2, 0.01);
TS_ASSERT_EQUALS(atan2_approx(fixed::FromInt(0), fixed::FromInt(0)).ToDouble(), 0);
// Test that it approximately matches libc's atan2
#define T(y, x) TS_ASSERT_DELTA(atan2_approx(fixed::FromDouble(y), fixed::FromDouble(x)).ToDouble(), atan2((double)(y), (double)(x)), 0.072)
// Quadrants
T(1, 1);
T(-1, 1);
T(1, -1);
T(-1, -1);
// Scale
T(10, 10);
T(100, 100);
T(1000, 1000);
T(10000, 10000);
// Some arbitrary cases
T(2, 1);
T(3, 1);
T(4, 1);
T(10, 1);
T(1000, 1);
T(1, 2);
T(1, 3);
T(1, 4);
T(1, 10);
T(1, 1000);
// The highest-error region
for (double x = 2.0; x < 4.0; x += 0.01)
{
T(1, x);
}
#undef T
}
void test_SinCos()
{
fixed s, c;
sincos_approx(fixed::FromInt(0), s, c);
TS_ASSERT_EQUALS(s, fixed::FromInt(0));
TS_ASSERT_EQUALS(c, fixed::FromInt(1));
sincos_approx(fixed::Pi(), s, c);
TS_ASSERT_DELTA(s.ToDouble(), 0.0, 0.00005);
TS_ASSERT_EQUALS(c, fixed::FromInt(-1));
sincos_approx(fixed::FromDouble(M_PI*2.0), s, c);
TS_ASSERT_DELTA(s.ToDouble(), 0.0, 0.0001);
TS_ASSERT_DELTA(c.ToDouble(), 1.0, 0.0001);
sincos_approx(fixed::FromDouble(M_PI*100.0), s, c);
TS_ASSERT_DELTA(s.ToDouble(), 0.0, 0.004);
TS_ASSERT_DELTA(c.ToDouble(), 1.0, 0.004);
sincos_approx(fixed::FromDouble(M_PI*-100.0), s, c);
TS_ASSERT_DELTA(s.ToDouble(), 0.0, 0.004);
TS_ASSERT_DELTA(c.ToDouble(), 1.0, 0.004);
/*
for (double a = 0.0; a < 6.28; a += 0.1)
{
sincos_approx(fixed::FromDouble(a), s, c);
printf("%f: sin = %f / %f; cos = %f / %f\n", a, s.ToDouble(), sin(a), c.ToDouble(), cos(a));
}
*/
double err = 0.0;
for (double a = -6.28; a < 6.28; a += 0.001)
{
sincos_approx(fixed::FromDouble(a), s, c);
err = std::max(err, fabs(sin(a) - s.ToDouble()));
err = std::max(err, fabs(cos(a) - c.ToDouble()));
}
// printf("### Max error %f = %f/2^16\n", err, err*65536.0);
TS_ASSERT_LESS_THAN(err, 0.00046);
}
};
Index: ps/trunk/binaries/data/mods/public/maps/scenarios/Pathfinding_demo.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scenarios/Pathfinding_demo.xml (revision 8159)
+++ ps/trunk/binaries/data/mods/public/maps/scenarios/Pathfinding_demo.xml (revision 8160)
@@ -1,634 +1,635 @@
defaultdefault515080.450
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
units/hele_cavalry_swordsman_e
1
units/hele_cavalry_swordsman_e
1
units/hele_cavalry_swordsman_e
1
units/hele_cavalry_swordsman_e
1
units/hele_cavalry_swordsman_e
1
units/hele_cavalry_swordsman_e
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
structures/celt_corral
1
units/celt_infantry_javelinist_e
2
units/celt_infantry_javelinist_e
1
units/hele_infantry_archer_e
1
units/hele_infantry_archer_e
1
structures/celt_temple
2
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
structures/celt_temple
2
structures/celt_temple
2
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
gaia/geology_metal_mediterranean
1
gaia/geology_stone_mediterranean
1
gaia/flora_tree_oak_large
1
gaia/fauna_zebra
1
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/public/maps/scenarios/Units_demo.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scenarios/Units_demo.xml (revision 8159)
+++ ps/trunk/binaries/data/mods/public/maps/scenarios/Units_demo.xml (revision 8160)
@@ -1,93 +1,94 @@
defaultdefault515080.450
Index: ps/trunk/binaries/data/mods/public/gui/session_new/session.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session_new/session.xml (revision 8159)
+++ ps/trunk/binaries/data/mods/public/gui/session_new/session.xml (revision 8160)
@@ -1,562 +1,567 @@
Return to Main MenuleaveGame()
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml (revision 8159)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_gaia.xml (revision 8160)
@@ -1,10 +1,11 @@
gaiaGaia
+ 0true
Index: ps/trunk/source/gui/scripting/ScriptFunctions.cpp
===================================================================
--- ps/trunk/source/gui/scripting/ScriptFunctions.cpp (revision 8159)
+++ ps/trunk/source/gui/scripting/ScriptFunctions.cpp (revision 8160)
@@ -1,346 +1,360 @@
/* 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 .
*/
#include "precompiled.h"
#include "scriptinterface/ScriptInterface.h"
#include "graphics/Camera.h"
#include "graphics/GameView.h"
#include "graphics/MapReader.h"
#include "gui/GUIManager.h"
#include "lib/sysdep/sysdep.h"
#include "maths/FixedVector3D.h"
#include "network/NetClient.h"
#include "network/NetServer.h"
#include "network/NetTurnManager.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/Overlay.h"
#include "ps/GameSetup/Atlas.h"
#include "ps/GameSetup/Config.h"
#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"
#include "js/jsapi.h"
/*
* This file defines a set of functions that are available to GUI scripts, to allow
* interaction with the rest of the engine.
* Functions are exposed to scripts within the global object 'Engine', so
* scripts should call "Engine.FunctionName(...)" etc.
*/
extern void restart_mainloop_in_atlas(); // from main.cpp
namespace {
CScriptVal GetActiveGui(void* UNUSED(cbdata))
{
return OBJECT_TO_JSVAL(g_GUI->GetScriptObject());
}
void PushGuiPage(void* UNUSED(cbdata), std::wstring name, CScriptVal initData)
{
g_GUI->PushPage(name, initData);
}
void SwitchGuiPage(void* UNUSED(cbdata), std::wstring name, CScriptVal initData)
{
g_GUI->SwitchPage(name, initData);
}
void PopGuiPage(void* UNUSED(cbdata))
{
g_GUI->PopPage();
}
bool IsNewSimulation(void* UNUSED(cbdata))
{
return true; // XXX: delete this function
}
CScriptVal GuiInterfaceCall(void* cbdata, std::wstring name, CScriptVal data)
{
CGUIManager* guiManager = static_cast (cbdata);
if (!g_Game)
return JSVAL_VOID;
CSimulation2* sim = g_Game->GetSimulation2();
debug_assert(sim);
CmpPtr gui(*sim, SYSTEM_ENTITY);
if (gui.null())
return JSVAL_VOID;
int player = -1;
if (g_Game)
player = g_Game->GetPlayerID();
CScriptValRooted arg (sim->GetScriptInterface().GetContext(), sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), data.get()));
CScriptVal ret (gui->ScriptCall(player, name, arg.get()));
return guiManager->GetScriptInterface().CloneValueFromOtherContext(sim->GetScriptInterface(), ret.get());
}
void PostNetworkCommand(void* cbdata, CScriptVal cmd)
{
CGUIManager* guiManager = static_cast (cbdata);
if (!g_Game)
return;
CSimulation2* sim = g_Game->GetSimulation2();
debug_assert(sim);
CmpPtr queue(*sim, SYSTEM_ENTITY);
if (queue.null())
return;
jsval cmd2 = sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), cmd.get());
queue->PostNetworkCommand(cmd2);
}
std::vector 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 PickFriendlyEntitiesInRect(void* UNUSED(cbdata), int x0, int y0, int x1, int y1, int player)
{
return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, player);
}
CFixedVector3D GetTerrainAtPoint(void* UNUSED(cbdata), int x, int y)
{
CVector3D pos = g_Game->GetView()->GetCamera()->GetWorldCoordinates(x, y, true);
return CFixedVector3D(fixed::FromFloat(pos.X), fixed::FromFloat(pos.Y), fixed::FromFloat(pos.Z));
}
std::wstring SetCursor(void* UNUSED(cbdata), std::wstring name)
{
std::wstring old = g_CursorName;
g_CursorName = name;
return old;
}
int GetPlayerID(void* UNUSED(cbdata))
{
if (g_Game)
return g_Game->GetPlayerID();
return -1;
}
std::wstring GetDefaultPlayerName(void* UNUSED(cbdata))
{
// TODO: this should come from a config file or something
std::wstring name = sys_get_user_name();
if (name.empty())
name = L"anonymous";
return name;
}
void StartNetworkGame(void* UNUSED(cbdata))
{
debug_assert(g_NetServer);
g_NetServer->StartGame();
}
void StartGame(void* cbdata, CScriptVal attribs, int playerID)
{
CGUIManager* guiManager = static_cast (cbdata);
debug_assert(!g_NetServer);
debug_assert(!g_NetClient);
debug_assert(!g_Game);
g_Game = new CGame();
// Convert from GUI script context to sim script context
CSimulation2* sim = g_Game->GetSimulation2();
CScriptValRooted gameAttribs (sim->GetScriptInterface().GetContext(),
sim->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), attribs.get()));
g_Game->SetPlayerID(playerID);
g_Game->StartGame(gameAttribs);
}
void SetNetworkGameAttributes(void* cbdata, CScriptVal attribs)
{
CGUIManager* guiManager = static_cast (cbdata);
debug_assert(g_NetServer);
// Convert from GUI script context to net server script context
CScriptValRooted gameAttribs (g_NetServer->GetScriptInterface().GetContext(),
g_NetServer->GetScriptInterface().CloneValueFromOtherContext(guiManager->GetScriptInterface(), attribs.get()));
g_NetServer->UpdateGameAttributes(gameAttribs);
}
void StartNetworkHost(void* cbdata, std::wstring playerName)
{
CGUIManager* guiManager = static_cast (cbdata);
debug_assert(!g_NetClient);
debug_assert(!g_NetServer);
debug_assert(!g_Game);
g_NetServer = new CNetServer();
if (!g_NetServer->SetupConnection())
{
guiManager->GetScriptInterface().ReportError("Failed to start server");
SAFE_DELETE(g_NetServer);
return;
}
g_Game = new CGame();
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
if (!g_NetClient->SetupConnection("127.0.0.1"))
{
guiManager->GetScriptInterface().ReportError("Failed to connect to server");
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
}
}
void StartNetworkJoin(void* cbdata, std::wstring playerName, std::string serverAddress)
{
CGUIManager* guiManager = static_cast (cbdata);
debug_assert(!g_NetClient);
debug_assert(!g_NetServer);
debug_assert(!g_Game);
g_Game = new CGame();
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
if (!g_NetClient->SetupConnection(serverAddress))
{
guiManager->GetScriptInterface().ReportError("Failed to connect to server");
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
}
}
void DisconnectNetworkGame(void* UNUSED(cbdata))
{
// TODO: we ought to do async reliable disconnections
SAFE_DELETE(g_NetServer);
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
}
CScriptVal PollNetworkClient(void* cbdata)
{
CGUIManager* guiManager = static_cast (cbdata);
if (!g_NetClient)
return CScriptVal();
CScriptValRooted poll = g_NetClient->GuiPoll();
// Convert from net client context to GUI script context
return guiManager->GetScriptInterface().CloneValueFromOtherContext(g_NetClient->GetScriptInterface(), poll.get());
}
void AssignNetworkPlayer(void* UNUSED(cbdata), int playerID, std::string guid)
{
debug_assert(g_NetServer);
g_NetServer->AssignPlayer(playerID, guid);
}
void SendNetworkChat(void* UNUSED(cbdata), std::wstring message)
{
debug_assert(g_NetClient);
g_NetClient->SendChatMessage(message);
}
void OpenURL(void* UNUSED(cbdata), std::string url)
{
sys_open_url(url);
}
void RestartInAtlas(void* UNUSED(cbdata))
{
restart_mainloop_in_atlas();
}
bool AtlasIsAvailable(void* UNUSED(cbdata))
{
return ATLAS_IsAvailable();
}
CScriptVal LoadMapData(void* cbdata, std::wstring name)
{
CGUIManager* guiManager = static_cast (cbdata);
CMapSummaryReader reader;
PSRETURN err = reader.LoadMap(VfsPath(L"maps/scenarios/") / (name + L".xml"));
if (err)
return CScriptVal();
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 cmpRangeManager(*sim, SYSTEM_ENTITY);
+ if (!cmpRangeManager.null())
+ cmpRangeManager->SetLosRevealAll(enabled);
+}
+
} // namespace
void GuiScriptingInit(ScriptInterface& scriptInterface)
{
// GUI manager functions:
scriptInterface.RegisterFunction("GetActiveGui");
scriptInterface.RegisterFunction("PushGuiPage");
scriptInterface.RegisterFunction("SwitchGuiPage");
scriptInterface.RegisterFunction("PopGuiPage");
// Simulation<->GUI interface functions:
scriptInterface.RegisterFunction("IsNewSimulation");
scriptInterface.RegisterFunction("GuiInterfaceCall");
scriptInterface.RegisterFunction("PostNetworkCommand");
// Entity picking
scriptInterface.RegisterFunction, int, int, &PickEntitiesAtPoint>("PickEntitiesAtPoint");
scriptInterface.RegisterFunction, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect");
scriptInterface.RegisterFunction("GetTerrainAtPoint");
// Network / game setup functions
scriptInterface.RegisterFunction("StartNetworkGame");
scriptInterface.RegisterFunction("StartGame");
scriptInterface.RegisterFunction("StartNetworkHost");
scriptInterface.RegisterFunction("StartNetworkJoin");
scriptInterface.RegisterFunction("DisconnectNetworkGame");
scriptInterface.RegisterFunction("PollNetworkClient");
scriptInterface.RegisterFunction("SetNetworkGameAttributes");
scriptInterface.RegisterFunction("AssignNetworkPlayer");
scriptInterface.RegisterFunction("SendNetworkChat");
// Misc functions
scriptInterface.RegisterFunction("SetCursor");
scriptInterface.RegisterFunction("GetPlayerID");
scriptInterface.RegisterFunction("GetDefaultPlayerName");
scriptInterface.RegisterFunction("OpenURL");
scriptInterface.RegisterFunction("RestartInAtlas");
scriptInterface.RegisterFunction("AtlasIsAvailable");
scriptInterface.RegisterFunction("LoadMapData");
+ scriptInterface.RegisterFunction("SetRevealMap");
}
Index: ps/trunk/source/gui/MiniMap.cpp
===================================================================
--- ps/trunk/source/gui/MiniMap.cpp (revision 8159)
+++ ps/trunk/source/gui/MiniMap.cpp (revision 8160)
@@ -1,543 +1,548 @@
/* 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 .
*/
#include "precompiled.h"
#include
#include "MiniMap.h"
#include "graphics/GameView.h"
#include "graphics/MiniPatch.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
#include "graphics/TerrainTextureManager.h"
#include "lib/ogl.h"
#include "lib/external_libraries/sdl.h"
#include "lib/bits.h"
#include "lib/timer.h"
#include "lib/sysdep/cpu.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpMinimap.h"
+#include "simulation2/components/ICmpRangeManager.h"
bool g_GameRestarted = false;
static unsigned int ScaleColor(unsigned int color, float x)
{
unsigned int r = unsigned(float(color & 0xff) * x);
unsigned int g = unsigned(float((color>>8) & 0xff) * x);
unsigned int b = unsigned(float((color>>16) & 0xff) * x);
return (0xff000000 | r | g<<8 | b<<16);
}
CMiniMap::CMiniMap()
: m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0),
m_LOSTexture(0), m_LOSData(0), m_TerrainDirty(true)
{
AddSetting(GUIST_CColor, "fov_wedge_color");
AddSetting(GUIST_CStr, "tooltip");
AddSetting(GUIST_CStr, "tooltip_style");
m_Clicking = false;
}
CMiniMap::~CMiniMap()
{
Destroy();
}
void CMiniMap::HandleMessage(const SGUIMessage &Message)
{
switch(Message.type)
{
case GUIM_MOUSE_PRESS_LEFT:
{
SetCameraPos();
m_Clicking = true;
break;
}
case GUIM_MOUSE_RELEASE_LEFT:
{
if(m_Clicking)
SetCameraPos();
m_Clicking = false;
break;
}
case GUIM_MOUSE_DBLCLICK_LEFT:
{
if(m_Clicking)
SetCameraPos();
m_Clicking = false;
break;
}
case GUIM_MOUSE_ENTER:
{
// g_Selection.m_mouseOverMM = true;
break;
}
case GUIM_MOUSE_LEAVE:
{
// g_Selection.m_mouseOverMM = false;
m_Clicking = false;
break;
}
case GUIM_MOUSE_RELEASE_RIGHT:
{
CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 1);
break;
}
case GUIM_MOUSE_DBLCLICK_RIGHT:
{
CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 2);
break;
}
case GUIM_MOUSE_MOTION:
{
if (m_Clicking)
SetCameraPos();
break;
}
default:
break;
} // switch
}
void CMiniMap::SetCameraPos()
{
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
CVector3D target;
CPos mousePos = GetMousePos();
target.X = CELL_SIZE * m_MapSize * ((mousePos.x - m_CachedActualSize.left) / m_CachedActualSize.GetWidth());
target.Z = CELL_SIZE * m_MapSize * ((m_CachedActualSize.bottom - mousePos.y) / m_CachedActualSize.GetHeight());
target.Y = terrain->GetExactGroundLevel(target.X, target.Z);
g_Game->GetView()->MoveCameraTarget(target);
}
void CMiniMap::FireWorldClickEvent(int button, int clicks)
{
// Determine X and Z according to proportion of mouse position and minimap
CPos MousePos = GetMousePos();
float x = CELL_SIZE * m_MapSize *
((MousePos.x - m_CachedActualSize.left) / m_CachedActualSize.GetWidth());
float z = CELL_SIZE * m_MapSize *
((m_CachedActualSize.bottom - MousePos.y) / m_CachedActualSize.GetHeight());
CScriptValRooted coords;
g_ScriptingHost.GetScriptInterface().Eval("({})", coords);
g_ScriptingHost.GetScriptInterface().SetProperty(coords.get(), "x", x, false);
g_ScriptingHost.GetScriptInterface().SetProperty(coords.get(), "z", z, false);
ScriptEvent("worldclick", coords);
UNUSED2(button);
UNUSED2(clicks);
}
// render view rect : John M. Mena
// This sets up and draws the rectangle on the mini-map
// which represents the view of the camera in the world.
void CMiniMap::DrawViewRect()
{
// Compute the camera frustum intersected with a fixed-height plane.
// TODO: Currently we hard-code the height, so this'll be dodgy when maps aren't the
// expected height - how can we make it better without the view rect wobbling in
// size while the player scrolls?
float h = 16384.f * HEIGHT_SCALE;
CVector3D hitPt[4];
hitPt[0]=m_Camera->GetWorldCoordinates(0, g_Renderer.GetHeight(), h);
hitPt[1]=m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), g_Renderer.GetHeight(), h);
hitPt[2]=m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), 0, h);
hitPt[3]=m_Camera->GetWorldCoordinates(0, 0, h);
float ViewRect[4][2];
for (int i=0;i<4;i++) {
// convert to minimap space
float px=hitPt[i].X;
float pz=hitPt[i].Z;
ViewRect[i][0]=(m_CachedActualSize.GetWidth()*px/float(CELL_SIZE*m_MapSize));
ViewRect[i][1]=(m_CachedActualSize.GetHeight()*pz/float(CELL_SIZE*m_MapSize));
}
// Enable Scissoring as to restrict the rectangle
// to only the mini-map below by retrieving the mini-maps
// screen coords.
const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom;
glScissor((int)x, g_Renderer.GetHeight()-(int)y, (int)m_CachedActualSize.GetWidth(), (int)m_CachedActualSize.GetHeight());
glEnable(GL_SCISSOR_TEST);
glEnable(GL_LINE_SMOOTH);
glLineWidth(2.0f);
glColor3f(1.0f, 0.3f, 0.3f);
// Draw the viewing rectangle with the ScEd's conversion algorithm
glBegin(GL_LINE_LOOP);
glVertex2f(x+ViewRect[0][0], y-ViewRect[0][1]);
glVertex2f(x+ViewRect[1][0], y-ViewRect[1][1]);
glVertex2f(x+ViewRect[2][0], y-ViewRect[2][1]);
glVertex2f(x+ViewRect[3][0], y-ViewRect[3][1]);
glEnd();
// restore state
glDisable(GL_SCISSOR_TEST);
glDisable(GL_LINE_SMOOTH);
glLineWidth(1.0f);
}
struct MinimapUnitVertex
{
u8 r, g, b, a;
float x, y;
};
void CMiniMap::Draw()
{
PROFILE("minimap");
// The terrain isn't actually initialized until the map is loaded, which
// happens when the game is started, so abort until then.
if(!(GetGUI() && g_Game && g_Game->IsGameStarted()))
return;
glDisable(GL_DEPTH_TEST);
// Set our globals in case they hadn't been set before
m_Camera = g_Game->GetView()->GetCamera();
m_Terrain = g_Game->GetWorld()->GetTerrain();
m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left);
m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top);
m_MapSize = m_Terrain->GetVerticesPerSide();
m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize);
if(!m_TerrainTexture || g_GameRestarted)
CreateTextures();
// only update 10x / second
// (note: since units only move a few pixels per second on the minimap,
// we can get away with infrequent updates; this is slow, ~20ms)
// TODO: we don't need to do it this frequently, it only needs to be once
// per simulation turn at most
static double last_time;
const double cur_time = timer_Time();
if(cur_time - last_time > 100e-3) // 10 updates/sec
{
last_time = cur_time;
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);
const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom;
const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top;
const float z = GetBufferedZ();
// Draw the main textured quad
g_Renderer.BindTexture(0, m_TerrainTexture);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(x, y, z);
glTexCoord2f(texCoordMax, 0.0f);
glVertex3f(x2, y, z);
glTexCoord2f(texCoordMax, texCoordMax);
glVertex3f(x2, y2, z);
glTexCoord2f(0.0f, texCoordMax);
glVertex3f(x, y2, z);
glEnd();
/* // TODO: reimplement with new sim system
// Shade territories by player
CTerritoryManager* territoryMgr = g_Game->GetWorld()->GetTerritoryManager();
std::vector& territories = territoryMgr->GetTerritories();
PROFILE_START("minimap territory shade");
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for( size_t i=0; iowner->GetPlayerID() == 0 )
continue;
std::vector& boundary = territories[i]->boundary;
SPlayerColour col = territories[i]->owner->GetColour();
glColor4f(col.r, col.g, col.b, 0.25f);
glBegin(GL_POLYGON);
for( size_t j=0; jGetTilesPerSide() * CELL_SIZE);
float fy = boundary[j].y / (m_Terrain->GetTilesPerSide() * CELL_SIZE);
glVertex3f( x*(1-fx) + x2*fx, y*(1-fy) + y2*fy, z );
}
glEnd();
}
glDisable(GL_BLEND);
PROFILE_END("minimap territory shade");
// Draw territory boundaries
glEnable(GL_LINE_SMOOTH);
glLineWidth(1.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0.8f, 0.8f, 0.8f, 0.8f);
for( size_t i=0; i& boundary = territories[i]->boundary;
glBegin(GL_LINE_LOOP);
for( size_t j=0; jGetTilesPerSide() * CELL_SIZE);
float fy = boundary[j].y / (m_Terrain->GetTilesPerSide() * CELL_SIZE);
glVertex3f( x*(1-fx) + x2*fx, y*(1-fy) + y2*fy, z );
}
glEnd();
}
glLineWidth(1.0f);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
*/
// Draw the LOS quad in black, using alpha values from the LOS texture
g_Renderer.BindTexture(0, m_LOSTexture);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBegin(GL_QUADS);
glColor3f(0.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(x, y, z);
glTexCoord2f(texCoordMax, 0.0f);
glVertex3f(x2, y, z);
glTexCoord2f(texCoordMax, texCoordMax);
glVertex3f(x2, y2, z);
glTexCoord2f(0.0f, texCoordMax);
glVertex3f(x, y2, z);
glEnd();
glDisable(GL_BLEND);
PROFILE_START("minimap units");
// Don't enable GL_POINT_SMOOTH because it's far too slow
// (~70msec/frame on a GF4 rendering a thousand points)
glPointSize(3.f);
float sx = (float)m_Width / ((m_MapSize - 1) * CELL_SIZE);
float sy = (float)m_Height / ((m_MapSize - 1) * CELL_SIZE);
CSimulation2* sim = g_Game->GetSimulation2();
const CSimulation2::InterfaceList& ents = sim->GetEntitiesWithInterface(IID_Minimap);
std::vector vertexArray;
vertexArray.reserve(ents.size());
+ CmpPtr 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(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);
+ }
}
}
if (!vertexArray.empty())
{
glPushMatrix();
glTranslatef(0, 0, z);
glInterleavedArrays(GL_C4UB_V2F, sizeof(MinimapUnitVertex), &vertexArray[0]);
glDrawArrays(GL_POINTS, 0, (GLsizei)vertexArray.size());
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
}
PROFILE_END("minimap units");
DrawViewRect();
// Reset everything back to normal
glPointSize(1.0f);
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
}
void CMiniMap::CreateTextures()
{
Destroy();
// Create terrain texture
glGenTextures(1, &m_TerrainTexture);
g_Renderer.BindTexture(0, m_TerrainTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_TextureSize, m_TextureSize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0);
m_TerrainData = new u32[(m_MapSize - 1) * (m_MapSize - 1)];
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);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
// Create LOS texture
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);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
// Rebuild and upload both of them
RebuildTerrainTexture();
RebuildLOSTexture();
}
void CMiniMap::RebuildTerrainTexture()
{
u32 x = 0;
u32 y = 0;
u32 w = m_MapSize - 1;
u32 h = m_MapSize - 1;
float waterHeight = g_Renderer.GetWaterManager()->m_WaterHeight;
m_TerrainDirty = false;
for(u32 j = 0; j < h; j++)
{
u32 *dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x;
for(u32 i = 0; i < w; i++)
{
float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j)
+ m_Terrain->GetVertexGroundLevel((int)i+1, (int)j)
+ m_Terrain->GetVertexGroundLevel((int)i, (int)j+1)
+ m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1)
) / 4.0f;
if(avgHeight < waterHeight)
{
*dataPtr++ = 0xff304080; // TODO: perhaps use the renderer's water color?
}
else
{
int hmap = ((int)m_Terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8;
int val = (hmap / 3) + 170;
u32 color = 0xFFFFFFFF;
CMiniPatch *mp = m_Terrain->GetTile(x + i, y + j);
if(mp)
{
CTerrainTextureEntry *tex = mp->GetTextureEntry();
if(tex)
{
// If the texture can't be loaded yet, set the dirty flags
// so we'll try regenerating the terrain texture again soon
if(!tex->GetTexture()->TryLoad())
m_TerrainDirty = true;
color = tex->GetBaseColor();
}
}
*dataPtr++ = ScaleColor(color, float(val) / 255.0f);
}
}
}
// Upload the texture
g_Renderer.BindTexture(0, m_TerrainTexture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_BGRA_EXT, GL_UNSIGNED_BYTE, m_TerrainData);
}
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 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()
{
if(m_TerrainTexture)
glDeleteTextures(1, &m_TerrainTexture);
if(m_LOSTexture)
glDeleteTextures(1, &m_LOSTexture);
delete[] m_TerrainData; m_TerrainData = 0;
delete[] m_LOSData; m_LOSData = 0;
}
Index: ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp
===================================================================
--- ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp (revision 8159)
+++ ps/trunk/source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp (revision 8160)
@@ -1,103 +1,109 @@
/* 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 .
*/
#include "precompiled.h"
#include "MessageHandler.h"
#include "../GameLoop.h"
#include "graphics/GameView.h"
#include "graphics/MapWriter.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
#include "graphics/TerrainTextureManager.h"
#include "ps/Game.h"
#include "ps/Loader.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation/LOSManager.h"
#include "simulation/Simulation.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPlayer.h"
#include "simulation2/components/ICmpPlayerManager.h"
#include "simulation2/components/ICmpPosition.h"
+#include "simulation2/components/ICmpRangeManager.h"
namespace
{
void InitGame()
{
if (g_Game)
{
delete g_Game;
g_Game = NULL;
}
g_Game = new CGame();
// Default to player 1 for playtesting
g_Game->SetPlayerID(1);
}
void StartGame(const CStrW& map)
{
CStrW mapBase = map.BeforeLast(L".pmp"); // strip the file extension, if any
CScriptValRooted attrs;
g_Game->GetSimulation2()->GetScriptInterface().Eval("({})", attrs);
g_Game->GetSimulation2()->GetScriptInterface().SetProperty(attrs.get(), "map", std::wstring(mapBase), false);
g_Game->StartGame(attrs);
LDR_NonprogressiveLoad();
PSRETURN ret = g_Game->ReallyStartGame();
debug_assert(ret == PSRETURN_OK);
+
+ // Disable fog-of-war
+ CmpPtr cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
+ if (!cmpRangeManager.null())
+ cmpRangeManager->SetLosRevealAll(true);
}
}
namespace AtlasMessage {
MESSAGEHANDLER(GenerateMap)
{
InitGame();
// Load the empty default map
StartGame(L"_default");
// TODO: use msg->size somehow
// (e.g. load the map then resize the terrain to match it)
UNUSED2(msg);
}
MESSAGEHANDLER(LoadMap)
{
InitGame();
StartGame(*msg->filename);
}
MESSAGEHANDLER(SaveMap)
{
CMapWriter writer;
const VfsPath pathname = VfsPath(L"maps/scenarios/") / *msg->filename;
writer.SaveMap(pathname,
g_Game->GetWorld()->GetTerrain(),
g_Renderer.GetWaterManager(), g_Renderer.GetSkyManager(),
&g_LightEnv, g_Game->GetView()->GetCamera(), g_Game->GetView()->GetCinema(),
g_Game->GetSimulation2());
}
}
Index: ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp
===================================================================
--- ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp (revision 8159)
+++ ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp (revision 8160)
@@ -1,357 +1,368 @@
/* 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 .
*/
#include "precompiled.h"
#include "ActorViewer.h"
#include "View.h"
#include "graphics/ColladaManager.h"
#include "graphics/Model.h"
#include "graphics/ObjectManager.h"
#include "graphics/Patch.h"
#include "graphics/SkeletonAnimManager.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
#include "graphics/TerrainTextureManager.h"
#include "graphics/UnitManager.h"
#include "maths/MathUtil.h"
#include "ps/Font.h"
#include "ps/GameSetup/Config.h"
#include "ps/ProfileViewer.h"
#include "renderer/Renderer.h"
#include "renderer/Scene.h"
#include "renderer/SkyManager.h"
#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"
struct ActorViewerImpl : public Scene
{
NONCOPYABLE(ActorViewerImpl);
public:
ActorViewerImpl()
: Entity(INVALID_ENTITY), Terrain(), ColladaManager(), MeshManager(ColladaManager), SkeletonAnimManager(ColladaManager),
UnitManager(), Simulation2(&UnitManager, &Terrain), ObjectManager(MeshManager, SkeletonAnimManager, Simulation2)
{
UnitManager.SetObjectManager(ObjectManager);
}
entity_id_t Entity;
CStrW CurrentUnitID;
CStrW CurrentUnitAnim;
float CurrentSpeed;
bool WalkEnabled;
bool GroundEnabled;
bool ShadowsEnabled;
SColor4ub Background;
CTerrain Terrain;
CColladaManager ColladaManager;
CMeshManager MeshManager;
CSkeletonAnimManager SkeletonAnimManager;
CObjectManager ObjectManager;
CUnitManager UnitManager;
CSimulation2 Simulation2;
// Simplistic implementation of the Scene interface
void EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
{
if (GroundEnabled)
c->Submit(Terrain.GetPatch(0, 0));
Simulation2.RenderSubmit(*c, frustum, false);
}
};
ActorViewer::ActorViewer()
: m(*new ActorViewerImpl())
{
m.WalkEnabled = false;
m.GroundEnabled = true;
m.ShadowsEnabled = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
m.Background = SColor4ub(255, 255, 255, 255);
// Create a tiny empty piece of terrain, just so we can put shadows
// on it without having to think too hard
m.Terrain.Initialize(1, NULL);
CTerrainTextureEntry* tex = g_TexMan.FindTexture("whiteness");
if (tex)
{
CPatch* patch = m.Terrain.GetPatch(0, 0);
for (ssize_t i = 0; i < PATCH_SIZE; ++i)
{
for (ssize_t j = 0; j < PATCH_SIZE; ++j)
{
CMiniPatch& mp = patch->m_MiniPatches[i][j];
mp.Tex = tex;
mp.Priority = 0;
}
}
}
else
{
debug_warn(L"Failed to load whiteness texture");
}
// Start the simulation
m.Simulation2.LoadDefaultScripts();
m.Simulation2.ResetState();
+
+ // Tell the simulation we've already loaded the terrain
+ CmpPtr cmpTerrain(m.Simulation2, SYSTEM_ENTITY);
+ if (!cmpTerrain.null())
+ cmpTerrain->ReloadTerrain();
+
+ CmpPtr cmpRangeManager(m.Simulation2, SYSTEM_ENTITY);
+ if (!cmpRangeManager.null())
+ cmpRangeManager->SetLosRevealAll(true);
}
ActorViewer::~ActorViewer()
{
delete &m;
}
CSimulation2* ActorViewer::GetSimulation2()
{
return &m.Simulation2;
}
entity_id_t ActorViewer::GetEntity()
{
return m.Entity;
}
void ActorViewer::UnloadObjects()
{
m.ObjectManager.UnloadObjects();
}
void ActorViewer::SetActor(const CStrW& name, const CStrW& animation)
{
bool needsAnimReload = false;
CStrW id = name;
// Recreate the entity, if we don't have one or if the new one is different
if (m.Entity == INVALID_ENTITY || id != m.CurrentUnitID)
{
// Delete the old entity (if any)
if (m.Entity != INVALID_ENTITY)
{
m.Simulation2.DestroyEntity(m.Entity);
m.Simulation2.FlushDestroyedEntities();
m.Entity = INVALID_ENTITY;
}
// If there's no actor to display, return with nothing loaded
if (id.empty())
return;
m.Entity = m.Simulation2.AddEntity(L"preview|" + id);
if (m.Entity == INVALID_ENTITY)
return;
CmpPtr cmpPosition(m.Simulation2, m.Entity);
if (!cmpPosition.null())
{
cmpPosition->JumpTo(entity_pos_t::FromInt(CELL_SIZE*PATCH_SIZE/2), entity_pos_t::FromInt(CELL_SIZE*PATCH_SIZE/2));
cmpPosition->SetYRotation(entity_angle_t::FromFloat((float)M_PI));
}
needsAnimReload = true;
}
if (animation != m.CurrentUnitAnim)
needsAnimReload = true;
if (needsAnimReload)
{
CStr anim = CStr(animation).LowerCase();
// Emulate the typical simulation animation behaviour
float speed;
float repeattime = 0.f;
if (anim == "walk")
{
CmpPtr cmpUnitMotion(m.Simulation2, m.Entity);
if (!cmpUnitMotion.null())
speed = cmpUnitMotion->GetWalkSpeed().ToFloat();
else
speed = 7.f; // typical unit speed
m.CurrentSpeed = speed;
}
else if (anim == "run")
{
CmpPtr cmpUnitMotion(m.Simulation2, m.Entity);
if (!cmpUnitMotion.null())
speed = cmpUnitMotion->GetRunSpeed().ToFloat();
else
speed = 12.f; // typical unit speed
m.CurrentSpeed = speed;
}
else if (anim == "melee")
{
speed = 1.f; // speed will be ignored if we have a repeattime
m.CurrentSpeed = 0.f;
CStr code = "var cmp = Engine.QueryInterface("+CStr(m.Entity)+", IID_Attack); if (cmp) cmp.GetTimers(cmp.GetBestAttack()).repeat; else 0;";
m.Simulation2.GetScriptInterface().Eval(code.c_str(), repeattime);
}
else
{
// Play the animation at normal speed, but movement speed is zero
speed = 1.f;
m.CurrentSpeed = 0.f;
}
CStr sound;
if (anim == "melee")
sound = "attack";
else if (anim == "build")
sound = "build";
else if (anim.Find("gather_") == 0)
sound = anim;
std::wstring soundgroup;
if (!sound.empty())
{
CStr code = "var cmp = Engine.QueryInterface("+CStr(m.Entity)+", IID_Sound); if (cmp) cmp.GetSoundGroup('"+sound+"'); else '';";
m.Simulation2.GetScriptInterface().Eval(code.c_str(), soundgroup);
}
CmpPtr cmpVisual(m.Simulation2, m.Entity);
if (!cmpVisual.null())
{
// TODO: SetEntitySelection(anim)
cmpVisual->SelectAnimation(anim, false, speed, soundgroup);
if (repeattime)
cmpVisual->SetAnimationSyncRepeat(repeattime);
}
}
m.CurrentUnitID = id;
m.CurrentUnitAnim = animation;
}
void ActorViewer::SetBackgroundColour(const SColor4ub& colour)
{
m.Background = colour;
m.Terrain.SetBaseColour(colour);
}
void ActorViewer::SetWalkEnabled(bool enabled) { m.WalkEnabled = enabled; }
void ActorViewer::SetGroundEnabled(bool enabled) { m.GroundEnabled = enabled; }
void ActorViewer::SetShadowsEnabled(bool enabled) { m.ShadowsEnabled = enabled; }
void ActorViewer::SetStatsEnabled(bool enabled)
{
if (enabled)
g_ProfileViewer.ShowTable("renderer");
else
g_ProfileViewer.ShowTable("");
}
void ActorViewer::Render()
{
m.Terrain.MakeDirty(RENDERDATA_UPDATE_COLOR);
g_Renderer.SetClearColor(m.Background);
// Disable shadows locally (avoid clobbering global state)
bool oldShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, m.ShadowsEnabled);
bool oldSky = g_Renderer.GetSkyManager()->m_RenderSky;
g_Renderer.GetSkyManager()->m_RenderSky = false;
g_Renderer.BeginFrame();
// Find the centre of the interesting region, in the middle of the patch
// and half way up the model (assuming there is one)
CVector3D centre;
CmpPtr cmpVisual(m.Simulation2, m.Entity);
if (!cmpVisual.null())
cmpVisual->GetBounds().GetCentre(centre);
else
centre.Y = 0.f;
centre.X = centre.Z = CELL_SIZE * PATCH_SIZE/2;
CCamera camera = View::GetView_Actor()->GetCamera();
camera.m_Orientation.Translate(centre.X, centre.Y, centre.Z);
camera.UpdateFrustum();
g_Renderer.SetSceneCamera(camera, camera);
g_Renderer.RenderScene(&m);
// ....
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glEnable(GL_TEXTURE_2D);
g_ProfileViewer.RenderProfile();
glPopAttrib();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
g_Renderer.EndFrame();
// Restore the old renderer state
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, oldShadows);
g_Renderer.GetSkyManager()->m_RenderSky = oldSky;
ogl_WarnIfError();
}
void ActorViewer::Update(float dt)
{
m.Simulation2.Update((int)(dt*1000));
m.Simulation2.Interpolate(dt, 0);
if (m.WalkEnabled && m.CurrentSpeed)
{
CmpPtr cmpPosition(m.Simulation2, m.Entity);
if (!cmpPosition.null())
{
// Move the model by speed*dt forwards
float z = cmpPosition->GetPosition().Z.ToFloat();
z -= m.CurrentSpeed*dt;
// Wrap at the edges, so it doesn't run off into the horizon
if (z < CELL_SIZE*PATCH_SIZE * 0.4f)
z = CELL_SIZE*PATCH_SIZE * 0.6f;
cmpPosition->JumpTo(cmpPosition->GetPosition().X, entity_pos_t::FromFloat(z));
}
}
}
Index: ps/trunk/source/simulation2/MessageTypes.h
===================================================================
--- ps/trunk/source/simulation2/MessageTypes.h (revision 8159)
+++ ps/trunk/source/simulation2/MessageTypes.h (revision 8160)
@@ -1,313 +1,320 @@
/* 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 .
*/
#ifndef INCLUDED_MESSAGETYPES
#define INCLUDED_MESSAGETYPES
#include "simulation2/system/Components.h"
#include "simulation2/system/Entity.h"
#include "simulation2/system/Message.h"
#include "simulation2/helpers/Position.h"
#include "simulation2/components/ICmpPathfinder.h"
#define DEFAULT_MESSAGE_IMPL(name) \
virtual int GetType() const { return MT_##name; } \
virtual const char* GetScriptHandlerName() const { return "On" #name; } \
virtual const char* GetScriptGlobalHandlerName() const { return "OnGlobal" #name; } \
virtual jsval ToJSVal(ScriptInterface& scriptInterface) const; \
static CMessage* FromJSVal(ScriptInterface&, jsval val);
class SceneCollector;
class CFrustum;
class CMessageTurnStart : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(TurnStart)
CMessageTurnStart()
{
}
};
// The update process is split into a number of phases, in an attempt
// to cope with dependencies between components. Each phase is implemented
// as a separate message. Simulation2.cpp sends them in sequence.
/**
* Generic per-turn update message, for things that don't care much about ordering.
*/
class CMessageUpdate : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Update)
CMessageUpdate(fixed turnLength) :
turnLength(turnLength)
{
}
fixed turnLength;
};
/**
* Update phase for formation controller movement (must happen before individual
* units move to follow their formation).
*/
class CMessageUpdate_MotionFormation : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Update_MotionFormation)
CMessageUpdate_MotionFormation(fixed turnLength) :
turnLength(turnLength)
{
}
fixed turnLength;
};
/**
* Update phase for non-formation-controller unit movement.
*/
class CMessageUpdate_MotionUnit : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Update_MotionUnit)
CMessageUpdate_MotionUnit(fixed turnLength) :
turnLength(turnLength)
{
}
fixed turnLength;
};
/**
* Final update phase, after all other updates.
*/
class CMessageUpdate_Final : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Update_Final)
CMessageUpdate_Final(fixed turnLength) :
turnLength(turnLength)
{
}
fixed turnLength;
};
+/**
+ * Prepare for rendering a new frame (set up model positions etc).
+ */
class CMessageInterpolate : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Interpolate)
CMessageInterpolate(float frameTime, float offset) :
frameTime(frameTime), offset(offset)
{
}
float frameTime; // time in seconds since previous interpolate
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:
DEFAULT_MESSAGE_IMPL(RenderSubmit)
CMessageRenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling) :
collector(collector), frustum(frustum), culling(culling)
{
}
SceneCollector& collector;
const CFrustum& frustum;
bool culling;
};
/**
* This is sent immediately after a new entity's components have all been created
* and initialised.
*/
class CMessageCreate : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Create)
CMessageCreate(entity_id_t entity) :
entity(entity)
{
}
entity_id_t entity;
};
/**
* This is sent immediately before a destroyed entity is flushed and really destroyed.
* (That is, after CComponentManager::DestroyComponentsSoon and inside FlushDestroyedComponents).
* The entity will still exist at the time this message is sent.
* It's possible for this message to be sent multiple times for one entity, but all its components
* will have been deleted after the first time.
*/
class CMessageDestroy : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(Destroy)
CMessageDestroy(entity_id_t entity) :
entity(entity)
{
}
entity_id_t entity;
};
class CMessageOwnershipChanged : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(OwnershipChanged)
CMessageOwnershipChanged(entity_id_t entity, int32_t from, int32_t to) :
entity(entity), from(from), to(to)
{
}
entity_id_t entity;
int32_t from;
int32_t to;
};
/**
* Sent during TurnStart.
*
* If @c inWorld is false, then the other fields are invalid and meaningless.
* Otherwise they represent the current position.
*/
class CMessagePositionChanged : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(PositionChanged)
CMessagePositionChanged(entity_id_t entity, bool inWorld, entity_pos_t x, entity_pos_t z, entity_angle_t a) :
entity(entity), inWorld(inWorld), x(x), z(z), a(a)
{
}
entity_id_t entity;
bool inWorld;
entity_pos_t x, z;
entity_angle_t a;
};
/**
* Sent by CCmpUnitMotion during Update, whenever the motion status has changed
* since the previous update.
*/
class CMessageMotionChanged : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(MotionChanged)
CMessageMotionChanged(bool starting, bool error) :
starting(starting), error(error)
{
}
bool starting; // whether this is a start or end of movement
bool error; // whether we failed to start moving (couldn't find any path)
};
/**
* Sent when terrain (texture or elevation) has been changed.
*/
class CMessageTerrainChanged : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(TerrainChanged)
CMessageTerrainChanged(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1) :
i0(i0), j0(j0), i1(i1), j1(j1)
{
}
ssize_t i0, j0, i1, j1; // inclusive lower bound, exclusive upper bound, in tiles
};
/**
* Sent by CCmpRangeManager at most once per turn, when an active range query
* has had matching units enter/leave the range since the last RangeUpdate.
*/
class CMessageRangeUpdate : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(RangeUpdate)
CMessageRangeUpdate(u32 tag, const std::vector& added, const std::vector& removed) :
tag(tag), added(added), removed(removed)
{
}
u32 tag;
std::vector added;
std::vector removed;
// CCmpRangeManager wants to store a vector of messages and wants to
// swap vectors instead of copying (to save on memory allocations),
// so add some constructors for it:
CMessageRangeUpdate(u32 tag) :
tag(tag)
{
}
CMessageRangeUpdate(const CMessageRangeUpdate& other) :
CMessage(), tag(other.tag), added(other.added), removed(other.removed)
{
}
CMessageRangeUpdate& operator=(const CMessageRangeUpdate& other)
{
tag = other.tag;
added = other.added;
removed = other.removed;
return *this;
}
};
/**
* Sent by CCmpPathfinder after async path requests.
*/
class CMessagePathResult : public CMessage
{
public:
DEFAULT_MESSAGE_IMPL(PathResult)
CMessagePathResult(u32 ticket, const ICmpPathfinder::Path& path) :
ticket(ticket), path(path)
{
}
u32 ticket;
ICmpPathfinder::Path path;
};
#endif // INCLUDED_MESSAGETYPES
Index: ps/trunk/source/simulation2/helpers/Selection.cpp
===================================================================
--- ps/trunk/source/simulation2/helpers/Selection.cpp (revision 8159)
+++ ps/trunk/source/simulation2/helpers/Selection.cpp (revision 8160)
@@ -1,119 +1,128 @@
/* 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 .
*/
#include "precompiled.h"
#include "Selection.h"
#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 EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY)
+std::vector EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player)
{
CVector3D origin, dir;
camera.BuildCameraRay(screenX, screenY, origin, dir);
+ CmpPtr cmpRangeManager(simulation, SYSTEM_ENTITY);
+ debug_assert(!cmpRangeManager.null());
+
std::vector > hits; // (dist^2, entity) pairs
const CSimulation2::InterfaceList& ents = simulation.GetEntitiesWithInterface(IID_Selectable);
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
{
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 cmpVisual(simulation.GetSimContext(), ent);
if (cmpVisual.null())
continue;
CBound bounds = cmpVisual->GetBounds();
float tmin, tmax;
if (!bounds.RayIntersect(origin, dir, tmin, tmax))
continue;
// Find the perpendicular distance from the object's centre to the picker ray
CVector3D centre;
bounds.GetCentre(centre);
CVector3D closest = origin + dir * (centre - origin).Dot(dir);
float dist2 = (closest - centre).LengthSquared();
hits.push_back(std::make_pair(dist2, ent));
}
// Sort hits by distance
std::sort(hits.begin(), hits.end()); // lexicographic comparison
// Extract the entity IDs
std::vector hitEnts;
hitEnts.reserve(hits.size());
for (size_t i = 0; i < hits.size(); ++i)
hitEnts.push_back(hits[i].second);
return hitEnts;
}
std::vector EntitySelection::PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner)
{
// Make sure sx0 <= sx1, and sy0 <= sy1
if (sx0 > sx1)
std::swap(sx0, sx1);
if (sy0 > sy1)
std::swap(sy0, sy1);
std::vector 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)
{
entity_id_t ent = it->first;
// Ignore entities not owned by 'owner'
CmpPtr cmpOwnership(simulation.GetSimContext(), ent);
if (cmpOwnership.null() || cmpOwnership->GetOwner() != owner)
continue;
// Find the current interpolated model position.
// (We just use the centre position and not the whole bounding box, because maybe
// that's better for users trying to select objects in busy areas)
CmpPtr cmpVisual(simulation.GetSimContext(), ent);
if (cmpVisual.null())
continue;
CVector3D position = cmpVisual->GetPosition();
// Reject if it's not on-screen (e.g. it's behind the camera)
if (!camera.GetFrustum().IsPointVisible(position))
continue;
// Compare screen-space coordinates
float x, y;
camera.GetScreenCoordinates(position, x, y);
int ix = (int)x;
int iy = (int)y;
if (sx0 <= ix && ix <= sx1 && sy0 <= iy && iy <= sy1)
hitEnts.push_back(ent);
}
return hitEnts;
}
Index: ps/trunk/source/simulation2/helpers/Selection.h
===================================================================
--- ps/trunk/source/simulation2/helpers/Selection.h (revision 8159)
+++ ps/trunk/source/simulation2/helpers/Selection.h (revision 8160)
@@ -1,51 +1,52 @@
/* 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 .
*/
#ifndef INCLUDED_SELECTION
#define INCLUDED_SELECTION
/**
* @file
* Helper functions related to entity selection
*/
#include "simulation2/system/Entity.h"
#include
class CSimulation2;
class CCamera;
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 PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY);
+std::vector PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player);
/**
* Finds all selectable entities within the given screen coordinate rectangle,
* that belong to player @p owner.
* Returns unordered list.
*/
std::vector PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner);
} // namespace
#endif // INCLUDED_SELECTION
Index: ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h
===================================================================
--- ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h (revision 8159)
+++ ps/trunk/source/simulation2/tests/test_CmpTemplateManager.h (revision 8160)
@@ -1,240 +1,251 @@
/* 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 .
*/
#include "lib/self_test.h"
#include "simulation2/system/ComponentManager.h"
#include "simulation2/components/ICmpTemplateManager.h"
#include "simulation2/components/ICmpTest.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/system/ParamNode.h"
#include "simulation2/system/SimContext.h"
#include "simulation2/Simulation2.h"
#include "graphics/Terrain.h"
#include "ps/Filesystem.h"
#include "ps/CLogger.h"
#include "ps/XML/Xeromyces.h"
class TestCmpTemplateManager : public CxxTest::TestSuite
{
public:
void setUp()
{
g_VFS = CreateVfs(20 * MiB);
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/L"mods/_test.sim", VFS_MOUNT_MUST_EXIST));
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/L"_testcache"));
CXeromyces::Startup();
}
void tearDown()
{
CXeromyces::Terminate();
g_VFS.reset();
DeleteDirectory(DataDir()/L"_testcache");
}
void test_LoadTemplate()
{
CSimContext context;
CComponentManager man(context);
man.LoadComponentTypes();
entity_id_t ent1 = 1, ent2 = 2;
CParamNode noParam;
TS_ASSERT(man.AddComponent(ent1, CID_TemplateManager, noParam));
ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager));
TS_ASSERT(tempMan != NULL);
const CParamNode* basic = tempMan->LoadTemplate(ent2, "basic", -1);
TS_ASSERT(basic != NULL);
TS_ASSERT_WSTR_EQUALS(basic->ToXML(), L"12345");
const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, "inherit2", -1);
TS_ASSERT(inherit2 != NULL);
TS_ASSERT_WSTR_EQUALS(inherit2->ToXML(), L"d2e1f1g2");
const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, "inherit1", -1);
TS_ASSERT(inherit1 != NULL);
TS_ASSERT_WSTR_EQUALS(inherit1->ToXML(), L"d1e1f1");
const CParamNode* actor = tempMan->LoadTemplate(ent2, "actor|example1", -1);
TS_ASSERT(actor != NULL);
TS_ASSERT_WSTR_EQUALS(actor->ToXML(), L"0uprightfalseexample1");
const CParamNode* preview = tempMan->LoadTemplate(ent2, "preview|unit", -1);
TS_ASSERT(preview != NULL);
- TS_ASSERT_WSTR_EQUALS(preview->ToXML(), L"0uprightfalseexample");
+ TS_ASSERT_WSTR_EQUALS(preview->ToXML(),
+ L"0uprightfalse"
+ L"0true"
+ L"example");
const CParamNode* previewobstruct = tempMan->LoadTemplate(ent2, "preview|unitobstruct", -1);
TS_ASSERT(previewobstruct != NULL);
- TS_ASSERT_WSTR_EQUALS(previewobstruct->ToXML(), L"1.00uprightfalseexample");
+ TS_ASSERT_WSTR_EQUALS(previewobstruct->ToXML(),
+ L"1.0"
+ L""
+ L"0uprightfalse"
+ L"0true"
+ L"example");
const CParamNode* previewactor = tempMan->LoadTemplate(ent2, "preview|actor|example2", -1);
TS_ASSERT(previewactor != NULL);
- TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(), L"0uprightfalseexample2");
+ TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(),
+ L"0uprightfalse"
+ L"0true"
+ L"example2");
}
void test_LoadTemplate_scriptcache()
{
CSimContext context;
CComponentManager man(context);
man.LoadComponentTypes();
entity_id_t ent1 = 1, ent2 = 2;
CParamNode noParam;
TS_ASSERT(man.AddComponent(ent1, CID_TemplateManager, noParam));
ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager));
TS_ASSERT(tempMan != NULL);
tempMan->DisableValidation();
CScriptValRooted val;
JSContext* cx = man.GetScriptInterface().GetContext();
// This is testing some bugs in the template JS object caching
const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, "inherit1", -1);
val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, inherit1));
TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Test1A:{'@a':\"a1\", '@b':\"b1\", '@c':\"c1\", d:\"d1\", e:\"e1\", f:\"f1\"}})");
const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, "inherit2", -1);
val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, inherit2));
TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({'@parent':\"inherit1\", Test1A:{'@a':\"a2\", '@b':\"b1\", '@c':\"c1\", d:\"d2\", e:\"e1\", f:\"f1\", g:\"g2\"}})");
const CParamNode* actor = tempMan->LoadTemplate(ent2, "actor|example1", -1);
val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, &actor->GetChild("VisualActor")));
TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Actor:\"example1\"})");
const CParamNode* foundation = tempMan->LoadTemplate(ent2, "foundation|actor|example1", -1);
val = CScriptValRooted(cx, ScriptInterface::ToJSVal(cx, &foundation->GetChild("VisualActor")));
TS_ASSERT_WSTR_EQUALS(man.GetScriptInterface().ToString(val.get()), L"({Actor:\"example1\", Foundation:(void 0)})");
}
void test_LoadTemplate_errors()
{
CSimContext context;
CComponentManager man(context);
man.LoadComponentTypes();
entity_id_t ent1 = 1, ent2 = 2;
CParamNode noParam;
TS_ASSERT(man.AddComponent(ent1, CID_TemplateManager, noParam));
ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager));
TS_ASSERT(tempMan != NULL);
TestLogger logger;
TS_ASSERT(tempMan->LoadTemplate(ent2, "illformed", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "invalid", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-special", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "preview|nonexistent", -1) == NULL);
}
void test_LoadTemplate_multiple()
{
CSimContext context;
CComponentManager man(context);
man.LoadComponentTypes();
entity_id_t ent1 = 1, ent2 = 2;
CParamNode noParam;
TS_ASSERT(man.AddComponent(ent1, CID_TemplateManager, noParam));
ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager));
TS_ASSERT(tempMan != NULL);
const CParamNode* basicA = tempMan->LoadTemplate(ent2, "basic", -1);
TS_ASSERT(basicA != NULL);
const CParamNode* basicB = tempMan->LoadTemplate(ent2, "basic", -1);
TS_ASSERT(basicA == basicB);
const CParamNode* inherit2A = tempMan->LoadTemplate(ent2, "inherit2", -1);
TS_ASSERT(inherit2A != NULL);
const CParamNode* inherit2B = tempMan->LoadTemplate(ent2, "inherit2", -1);
TS_ASSERT(inherit2A == inherit2B);
TestLogger logger;
TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL);
}
};
class TestCmpTemplateManager_2 : public CxxTest::TestSuite
{
public:
void setUp()
{
g_VFS = CreateVfs(20 * MiB);
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/L"mods/public", VFS_MOUNT_MUST_EXIST));
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/L"_testcache"));
CXeromyces::Startup();
}
void tearDown()
{
CXeromyces::Terminate();
g_VFS.reset();
DeleteDirectory(DataDir()/L"_testcache");
}
// This just attempts loading every public entity, to check there's no validation errors
void test_load_all_DISABLED() // disabled since it's a bit slow and noisy
{
CTerrain dummy;
CSimulation2 sim(NULL, &dummy);
sim.LoadDefaultScripts();
sim.ResetState();
CmpPtr cmpTempMan(sim, SYSTEM_ENTITY);
TS_ASSERT(!cmpTempMan.null());
std::vector templates = cmpTempMan->FindAllTemplates();
for (size_t i = 0; i < templates.size(); ++i)
{
std::wstring name = templates[i];
printf("# %ls\n", name.c_str());
const CParamNode* p = cmpTempMan->GetTemplate(CStr8(name));
TS_ASSERT(p != NULL);
}
}
};
Index: ps/trunk/source/simulation2/TypeList.h
===================================================================
--- ps/trunk/source/simulation2/TypeList.h (revision 8159)
+++ ps/trunk/source/simulation2/TypeList.h (revision 8160)
@@ -1,125 +1,128 @@
/* 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 .
*/
// MESSAGE: message types
// INTERFACE: component interface types
// COMPONENT: component types
// Components intended only for use in test cases:
// (The tests rely on the enum IDs, so don't change the order of these)
INTERFACE(Test1)
COMPONENT(Test1A)
COMPONENT(Test1B)
COMPONENT(Test1Scripted)
INTERFACE(Test2)
COMPONENT(Test2A)
COMPONENT(Test2Scripted)
// Message types:
MESSAGE(TurnStart)
MESSAGE(Update)
MESSAGE(Update_MotionFormation)
MESSAGE(Update_MotionUnit)
MESSAGE(Update_Final)
MESSAGE(Interpolate) // non-deterministic (use with caution)
MESSAGE(RenderSubmit) // non-deterministic (use with caution)
MESSAGE(Create)
MESSAGE(Destroy)
MESSAGE(OwnershipChanged)
MESSAGE(PositionChanged)
MESSAGE(MotionChanged)
MESSAGE(RangeUpdate)
MESSAGE(TerrainChanged)
MESSAGE(PathResult)
// TemplateManager must come before all other (non-test) components,
// so that it is the first to be (de)serialized
INTERFACE(TemplateManager)
COMPONENT(TemplateManager)
// Special component for script component types with no native interface
INTERFACE(UnknownScript)
COMPONENT(UnknownScript)
// In alphabetical order:
INTERFACE(CommandQueue)
COMPONENT(CommandQueue)
INTERFACE(Decay)
COMPONENT(Decay)
INTERFACE(Footprint)
COMPONENT(Footprint)
INTERFACE(GuiInterface)
COMPONENT(GuiInterfaceScripted)
INTERFACE(Minimap)
COMPONENT(Minimap)
INTERFACE(Motion)
COMPONENT(MotionBall)
COMPONENT(MotionScripted)
INTERFACE(Obstruction)
COMPONENT(Obstruction)
INTERFACE(ObstructionManager)
COMPONENT(ObstructionManager)
INTERFACE(Ownership)
COMPONENT(Ownership)
INTERFACE(Pathfinder)
COMPONENT(Pathfinder)
INTERFACE(Player)
COMPONENT(PlayerScripted)
INTERFACE(PlayerManager)
COMPONENT(PlayerManagerScripted)
INTERFACE(Position)
COMPONENT(Position) // must be before VisualActor
INTERFACE(ProjectileManager)
COMPONENT(ProjectileManager)
INTERFACE(RangeManager)
COMPONENT(RangeManager)
INTERFACE(Selectable)
COMPONENT(Selectable)
INTERFACE(SoundManager)
COMPONENT(SoundManager)
INTERFACE(StatusBars)
COMPONENT(StatusBars)
INTERFACE(Terrain)
COMPONENT(Terrain)
INTERFACE(UnitMotion)
COMPONENT(UnitMotion) // must be after Obstruction
+INTERFACE(Vision)
+COMPONENT(Vision)
+
INTERFACE(Visual)
COMPONENT(VisualActor)
INTERFACE(WaterManager)
COMPONENT(WaterManager)
Index: ps/trunk/source/simulation2/system/SimContext.h
===================================================================
--- ps/trunk/source/simulation2/system/SimContext.h (revision 8159)
+++ ps/trunk/source/simulation2/system/SimContext.h (revision 8160)
@@ -1,54 +1,60 @@
/* 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 .
*/
#ifndef INCLUDED_SIMCONTEXT
#define INCLUDED_SIMCONTEXT
class CComponentManager;
class CUnitManager;
class CTerrain;
class ScriptInterface;
/**
* Contains pointers to various 'global' objects that are needed by the simulation code,
* to allow easy access without using real (evil) global variables.
*/
class CSimContext
{
public:
CSimContext();
~CSimContext();
CComponentManager& GetComponentManager() const;
void SetComponentManager(CComponentManager* man);
bool HasUnitManager() const;
CUnitManager& GetUnitManager() const;
CTerrain& GetTerrain() const;
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;
CTerrain* m_Terrain;
friend class CSimulation2Impl;
};
#endif // INCLUDED_SIMCONTEXT
Index: ps/trunk/source/simulation2/system/SimContext.cpp
===================================================================
--- ps/trunk/source/simulation2/system/SimContext.cpp (revision 8159)
+++ ps/trunk/source/simulation2/system/SimContext.cpp (revision 8160)
@@ -1,65 +1,74 @@
/* 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 .
*/
#include "precompiled.h"
#include "SimContext.h"
#include "ComponentManager.h"
+#include "ps/Game.h"
+
CSimContext::CSimContext() :
m_ComponentManager(NULL), m_UnitManager(NULL), m_Terrain(NULL)
{
}
CSimContext::~CSimContext()
{
}
CComponentManager& CSimContext::GetComponentManager() const
{
debug_assert(m_ComponentManager);
return *m_ComponentManager;
}
bool CSimContext::HasUnitManager() const
{
return m_UnitManager != NULL;
}
CUnitManager& CSimContext::GetUnitManager() const
{
debug_assert(m_UnitManager);
return *m_UnitManager;
}
CTerrain& CSimContext::GetTerrain() const
{
debug_assert(m_Terrain);
return *m_Terrain;
}
void CSimContext::SetComponentManager(CComponentManager* man)
{
debug_assert(!m_ComponentManager);
m_ComponentManager = man;
}
ScriptInterface& CSimContext::GetScriptInterface() const
{
return GetComponentManager().GetScriptInterface();
}
+
+int CSimContext::GetCurrentDisplayedPlayer() const
+{
+ if (!g_Game)
+ return -1;
+ return g_Game->GetPlayerID();
+}
Index: ps/trunk/source/simulation2/components/ICmpRangeManager.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpRangeManager.cpp (revision 8159)
+++ ps/trunk/source/simulation2/components/ICmpRangeManager.cpp (revision 8160)
@@ -1,32 +1,33 @@
/* 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 .
*/
#include "precompiled.h"
#include "ICmpRangeManager.h"
#include "simulation2/system/InterfaceScripted.h"
BEGIN_INTERFACE_WRAPPER(RangeManager)
DEFINE_INTERFACE_METHOD_4("ExecuteQuery", std::vector, ICmpRangeManager, ExecuteQuery, entity_id_t, entity_pos_t, std::vector, int)
DEFINE_INTERFACE_METHOD_4("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, std::vector, int)
DEFINE_INTERFACE_METHOD_1("DestroyActiveQuery", void, ICmpRangeManager, DestroyActiveQuery, ICmpRangeManager::tag_t)
DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableActiveQuery, ICmpRangeManager::tag_t)
DEFINE_INTERFACE_METHOD_1("DisableActiveQuery", void, ICmpRangeManager, DisableActiveQuery, ICmpRangeManager::tag_t)
DEFINE_INTERFACE_METHOD_1("ResetActiveQuery", std::vector, 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)
Index: ps/trunk/source/simulation2/components/CCmpTerrain.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpTerrain.cpp (revision 8159)
+++ ps/trunk/source/simulation2/components/CCmpTerrain.cpp (revision 8160)
@@ -1,108 +1,109 @@
/* 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 .
*/
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpTerrain.h"
#include "ICmpObstructionManager.h"
#include "ICmpRangeManager.h"
#include "simulation2/MessageTypes.h"
#include "graphics/Terrain.h"
class CCmpTerrain : public ICmpTerrain
{
public:
static void ClassInit(CComponentManager& UNUSED(componentManager))
{
}
DEFAULT_COMPONENT_ALLOCATOR(Terrain)
CTerrain* m_Terrain; // not null
static std::string GetSchema()
{
return "";
}
virtual void Init(const CSimContext& context, const CParamNode& UNUSED(paramNode))
{
m_Terrain = &context.GetTerrain();
}
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 CFixedVector3D CalcNormal(entity_pos_t x, entity_pos_t z)
{
CFixedVector3D normal;
m_Terrain->CalcNormalFixed((x / (int)CELL_SIZE).ToInt_RoundToZero(), (z / (int)CELL_SIZE).ToInt_RoundToZero(), normal);
return normal;
}
virtual entity_pos_t GetGroundLevel(entity_pos_t x, entity_pos_t z)
{
return m_Terrain->GetExactGroundLevelFixed(x, z);
}
virtual float GetExactGroundLevel(float x, float z)
{
return m_Terrain->GetExactGroundLevel(x, z);
}
virtual void ReloadTerrain()
{
// TODO: should refactor this code to be nicer
CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (!cmpObstructionManager.null())
{
cmpObstructionManager->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));
}
CmpPtr cmpRangeManager(GetSimContext(), SYSTEM_ENTITY);
if (!cmpRangeManager.null())
{
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());
}
}
virtual void MakeDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)
{
CMessageTerrainChanged msg(i0, j0, i1, j1);
GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
}
};
REGISTER_COMPONENT_TYPE(Terrain)
Index: ps/trunk/source/simulation2/components/CCmpVision.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpVision.cpp (nonexistent)
+++ ps/trunk/source/simulation2/components/CCmpVision.cpp (revision 8160)
@@ -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 .
+ */
+
+#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
+ ""
+ ""
+ ""
+ ""
+ ""
+ "";
+ }
+
+ 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)
Property changes on: ps/trunk/source/simulation2/components/CCmpVision.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/CCmpVisualActor.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpVisualActor.cpp (revision 8159)
+++ ps/trunk/source/simulation2/components/CCmpVisualActor.cpp (revision 8160)
@@ -1,395 +1,402 @@
/* 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 .
*/
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpVisual.h"
#include "ICmpPosition.h"
+#include "ICmpRangeManager.h"
+#include "ICmpVision.h"
#include "simulation2/MessageTypes.h"
#include "graphics/Frustum.h"
#include "graphics/Model.h"
#include "graphics/ObjectBase.h"
#include "graphics/ObjectEntry.h"
#include "graphics/Unit.h"
#include "graphics/UnitAnimation.h"
#include "graphics/UnitManager.h"
#include "lib/utf8.h"
#include "maths/Matrix3D.h"
#include "maths/Vector3D.h"
#include "ps/CLogger.h"
#include "renderer/Scene.h"
class CCmpVisualActor : public ICmpVisual
{
public:
static void ClassInit(CComponentManager& componentManager)
{
componentManager.SubscribeToMessageType(MT_Update_Final);
componentManager.SubscribeToMessageType(MT_Interpolate);
componentManager.SubscribeToMessageType(MT_RenderSubmit);
componentManager.SubscribeToMessageType(MT_OwnershipChanged);
}
DEFAULT_COMPONENT_ALLOCATOR(VisualActor)
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
std::string m_AnimName;
bool m_AnimOnce;
float m_AnimSpeed;
std::wstring m_SoundGroup;
float m_AnimDesync;
float m_AnimSyncRepeatTime; // 0.0 if not synced
static std::string GetSchema()
{
return
"Display the unit using the engine's actor system."
""
"units/hellenes/infantry_spearman_b.xml"
""
""
"structures/hellenes/barracks.xml"
"structures/fndn_4x4.xml"
""
""
""
""
""
""
""
""
""
""
""
""
""
"";
}
virtual void Init(const CSimContext& context, const CParamNode& paramNode)
{
m_Unit = NULL;
if (!context.HasUnitManager())
return; // do nothing if graphics are disabled
// TODO: we should do some fancy animation of under-construction buildings rising from the ground,
// but for now we'll just use the foundation actor and ignore the normal one
if (paramNode.GetChild("Foundation").IsOk() && paramNode.GetChild("FoundationActor").IsOk())
m_ActorName = paramNode.GetChild("FoundationActor").ToString();
else
m_ActorName = paramNode.GetChild("Actor").ToString();
std::set selections;
m_Unit = context.GetUnitManager().CreateUnit(m_ActorName, selections);
if (!m_Unit)
{
// The error will have already been logged
return;
}
m_Unit->SetID(GetEntityId());
SelectAnimation("idle", false, 0.f, L"");
}
virtual void Deinit(const CSimContext& context)
{
if (m_Unit)
{
context.GetUnitManager().DeleteUnit(m_Unit);
m_Unit = NULL;
}
}
virtual void Serialize(ISerializer& serialize)
{
// TODO: store the actor name, if !debug and it differs from the template
if (serialize.IsDebug())
{
serialize.String("actor", m_ActorName, 0, 256);
}
// TODO: store random variation. This ought to be synchronised across saved games
// and networks, so everyone sees the same thing. Saving the list of selection strings
// would be awfully inefficient, so actors should be changed to (by default) represent
// variations with a 16-bit RNG seed (selected randomly when creating new units, or
// when someone hits the "randomise" button in the map editor), only overridden with
// a list of strings if it really needs to be a specific variation.
// TODO: store animation state
// TODO: store shading colour
}
virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
{
Init(context, paramNode);
}
virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global))
{
// Quick exit for running in non-graphical mode
if (m_Unit == NULL)
return;
switch (msg.GetType())
{
case MT_Update_Final:
{
const CMessageUpdate_Final& msgData = static_cast (msg);
Update(msgData.turnLength);
break;
}
case MT_Interpolate:
{
const CMessageInterpolate& msgData = static_cast (msg);
Interpolate(msgData.frameTime, msgData.offset);
break;
}
case MT_RenderSubmit:
{
const CMessageRenderSubmit& msgData = static_cast (msg);
RenderSubmit(msgData.collector, msgData.frustum, msgData.culling);
break;
}
case MT_OwnershipChanged:
{
const CMessageOwnershipChanged& msgData = static_cast (msg);
m_Unit->GetModel().SetPlayerID(msgData.to);
break;
}
}
}
virtual CBound GetBounds()
{
if (!m_Unit)
return CBound();
return m_Unit->GetModel().GetBounds();
}
virtual CVector3D GetPosition()
{
if (!m_Unit)
return CVector3D(0, 0, 0);
return m_Unit->GetModel().GetTransform().GetTranslation();
}
virtual std::wstring GetActorShortName()
{
if (!m_Unit)
return L"";
return m_Unit->GetObject().m_Base->m_ShortName;
}
virtual std::wstring GetProjectileActor()
{
if (!m_Unit)
return L"";
return m_Unit->GetObject().m_ProjectileModelName;
}
virtual CVector3D GetProjectileLaunchPoint()
{
if (!m_Unit)
return CVector3D();
CModel* ammo = m_Unit->GetModel().FindFirstAmmoProp();
if (!ammo)
return CVector3D();
return ammo->GetTransform().GetTranslation();
}
virtual void SelectAnimation(std::string name, bool once, float speed, std::wstring soundgroup)
{
if (!m_Unit)
return;
if (!isfinite(speed) || speed < 0) // JS 'undefined' converts to NaN, which causes Bad Things
speed = 1.f;
m_AnimRunThreshold = 0.f;
m_AnimName = name;
m_AnimOnce = once;
m_AnimSpeed = speed;
m_SoundGroup = soundgroup;
m_AnimDesync = 0.05f; // TODO: make this an argument
m_AnimSyncRepeatTime = 0.0f;
m_Unit->GetAnimation().SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed, m_AnimDesync, false, m_SoundGroup.c_str());
}
virtual void SelectMovementAnimation(float runThreshold)
{
if (!m_Unit)
return;
m_AnimRunThreshold = runThreshold;
m_Unit->GetAnimation().SetAnimationState("walk", false, 1.f, 0.f, false, L"");
}
virtual void SetAnimationSyncRepeat(float repeattime)
{
if (!m_Unit)
return;
m_AnimSyncRepeatTime = repeattime;
m_Unit->GetAnimation().SetAnimationSyncRepeat(m_AnimSyncRepeatTime);
}
virtual void SetAnimationSyncOffset(float actiontime)
{
if (!m_Unit)
return;
m_Unit->GetAnimation().SetAnimationSyncOffset(actiontime);
}
virtual void SetShadingColour(fixed r, fixed g, fixed b, fixed a)
{
if (!m_Unit)
return;
m_Unit->GetModel().SetShadingColor(CColor(r.ToFloat(), g.ToFloat(), b.ToFloat(), a.ToFloat()));
}
virtual void Hotload(const std::wstring& name)
{
if (!m_Unit)
return;
if (name != m_ActorName)
return;
std::set selections;
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, selections);
if (!newUnit)
return;
// Save some data from the old unit
CColor shading = m_Unit->GetModel().GetShadingColor();
size_t playerID = m_Unit->GetModel().GetPlayerID();
// Replace with the new unit
GetSimContext().GetUnitManager().DeleteUnit(m_Unit);
m_Unit = newUnit;
m_Unit->SetID(GetEntityId());
m_Unit->GetAnimation().SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed, m_AnimDesync, false, m_SoundGroup.c_str());
// We'll lose the exact synchronisation but we should at least make sure it's going at the correct rate
if (m_AnimSyncRepeatTime != 0.0f)
m_Unit->GetAnimation().SetAnimationSyncRepeat(m_AnimSyncRepeatTime);
m_Unit->GetModel().SetShadingColor(shading);
m_Unit->GetModel().SetPlayerID(playerID);
}
private:
void Update(fixed turnLength);
void Interpolate(float frameTime, float frameOffset);
void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
};
REGISTER_COMPONENT_TYPE(VisualActor)
void CCmpVisualActor::Update(fixed turnLength)
{
if (m_Unit == NULL)
return;
// If we're in the special movement mode, select an appropriate animation
if (m_AnimRunThreshold)
{
CmpPtr cmpPosition(GetSimContext(), GetEntityId());
if (cmpPosition.null() || !cmpPosition->IsInWorld())
return;
float speed = cmpPosition->GetDistanceTravelled().ToFloat() / turnLength.ToFloat();
if (speed == 0.0f)
m_Unit->GetAnimation().SetAnimationState("idle", false, 1.f, 0.f, false, L"");
else if (speed < m_AnimRunThreshold)
m_Unit->GetAnimation().SetAnimationState("walk", false, speed, 0.f, false, L"");
else
m_Unit->GetAnimation().SetAnimationState("run", false, speed, 0.f, false, L"");
}
}
void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
{
if (m_Unit == NULL)
return;
CmpPtr cmpPosition(GetSimContext(), GetEntityId());
if (cmpPosition.null())
return;
// 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 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;
CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset, floating));
m_Unit->GetModel().SetTransform(transform);
m_Unit->UpdateModel(frameTime);
}
void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
{
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();
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);
}
Index: ps/trunk/source/simulation2/components/ICmpRangeManager.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpRangeManager.h (revision 8159)
+++ ps/trunk/source/simulation2/components/ICmpRangeManager.h (revision 8160)
@@ -1,129 +1,228 @@
/* 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 .
*/
#ifndef INCLUDED_ICMPRANGEMANAGER
#define INCLUDED_ICMPRANGEMANAGER
#include "simulation2/system/Interface.h"
#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
* to auto-attack.
* - auras let a unit have some effect on all units (or those of the same player, or of enemies)
* within a certain range.
* - capturable animals need to detect when a player-owned unit is nearby and no units of other
* players are in range.
* - scenario triggers may want to detect when units enter a given area.
* - units gathering from a resource that is exhausted need to find a new resource of the
* same type, near the old one and reachable.
* - projectile weapons with splash damage need to find all units within some distance
* of the target point.
* - ...
*
* In most cases the users are event-based and want notifications when something
* has entered or left the range, and the query can be set up once and rarely changed.
* These queries have to be fast. It's fine to approximate an entity as a point.
*
* Current design:
*
* This class handles just the most common parts of range queries:
* distance, target interface, and player ownership.
* The caller can then apply any more complex filtering that it needs.
*
* There are two types of query:
* Passive queries are performed by ExecuteQuery and immediately return the matching entities.
* Active queries are set up by CreateActiveQuery, and then a CMessageRangeUpdate message will be
* sent to the entity once per turn if anybody has entered or left the range since the last RangeUpdate.
* Queries can be disabled, in which case no message will be sent.
*/
class ICmpRangeManager : public IComponent
{
public:
/**
* External identifiers for active queries.
*/
typedef u32 tag_t;
/**
* 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.
* @param source the entity around which the range will be computed.
* @param maxRange maximum distance in metres (inclusive).
* @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
* @param requiredInterface if non-zero, an interface ID that matching entities must implement.
* @return list of entities matching the query, ordered by increasing distance from the source entity.
*/
virtual std::vector ExecuteQuery(entity_id_t source, entity_pos_t maxRange, std::vector owners, int requiredInterface) = 0;
/**
* Construct an active query. The query will be disabled by default.
* @param source the entity around which the range will be computed.
* @param maxRange maximum distance in metres (inclusive).
* @param owners list of player IDs that matching entities may have; -1 matches entities with no owner.
* @param requiredInterface if non-zero, an interface ID that matching entities must implement.
* @return unique non-zero identifier of query.
*/
virtual tag_t CreateActiveQuery(entity_id_t source, entity_pos_t maxRange, std::vector owners, int requiredInterface) = 0;
/**
* Destroy a query and clean up resources. This must be called when an entity no longer needs its
* query (e.g. when the entity is destroyed).
* @param tag identifier of query.
*/
virtual void DestroyActiveQuery(tag_t tag) = 0;
/**
* Re-enable the processing of a query.
* @param tag identifier of query.
*/
virtual void EnableActiveQuery(tag_t tag) = 0;
/**
* Disable the processing of a query (no RangeUpdate messages will be sent).
* @param tag identifier of query.
*/
virtual void DisableActiveQuery(tag_t tag) = 0;
/**
* Immediately execute a query, and re-enable it if disabled.
* The next RangeUpdate message will say who has entered/left since this call,
* so you won't miss any notifications.
* @param tag identifier of query.
* @return list of entities matching the query, ordered by increasing distance from the source entity.
*/
virtual std::vector ResetActiveQuery(tag_t tag) = 0;
/**
* Toggle the rendering of debug info.
*/
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& 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& 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)
};
#endif // INCLUDED_ICMPRANGEMANAGER
Index: ps/trunk/source/simulation2/components/CCmpTemplateManager.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpTemplateManager.cpp (revision 8159)
+++ ps/trunk/source/simulation2/components/CCmpTemplateManager.cpp (revision 8160)
@@ -1,481 +1,493 @@
/* 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 .
*/
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpTemplateManager.h"
#include "simulation2/MessageTypes.h"
#include "lib/utf8.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/XML/RelaxNG.h"
#include "ps/XML/Xeromyces.h"
static const wchar_t TEMPLATE_ROOT[] = L"simulation/templates/";
static const wchar_t ACTOR_ROOT[] = L"art/actors/";
class CCmpTemplateManager : public ICmpTemplateManager
{
public:
static void ClassInit(CComponentManager& componentManager)
{
componentManager.SubscribeGloballyToMessageType(MT_Destroy);
}
DEFAULT_COMPONENT_ALLOCATOR(TemplateManager)
static std::string GetSchema()
{
return "";
}
virtual void Init(const CSimContext& context, const CParamNode& UNUSED(paramNode))
{
m_DisableValidation = false;
m_Validator.LoadGrammar(context.GetComponentManager().GenerateSchema());
// TODO: handle errors loading the grammar here?
// TODO: support hotloading changes to the grammar
}
virtual void Deinit(const CSimContext& UNUSED(context))
{
}
virtual void Serialize(ISerializer& serialize)
{
size_t count = 0;
for (std::map::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it)
{
if (ENTITY_IS_LOCAL(it->first))
continue;
++count;
}
serialize.NumberU32_Unbounded("num entities", (u32)count);
for (std::map::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it)
{
if (ENTITY_IS_LOCAL(it->first))
continue;
serialize.NumberU32_Unbounded("id", it->first);
serialize.StringASCII("template", it->second, 0, 256);
}
// TODO: maybe we should do some kind of interning thing instead of printing so many strings?
// TODO: will need to serialize techs too, because we need to be giving out
// template data before other components (like the tech components) have been deserialized
}
virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize)
{
Init(context, paramNode);
u32 numEntities;
deserialize.NumberU32_Unbounded("num entities", numEntities);
for (u32 i = 0; i < numEntities; ++i)
{
entity_id_t ent;
std::string templateName;
deserialize.NumberU32_Unbounded("id", ent);
deserialize.StringASCII("template", templateName, 0, 256);
m_LatestTemplates[ent] = templateName;
}
}
virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global))
{
switch (msg.GetType())
{
case MT_Destroy:
{
const CMessageDestroy& msgData = static_cast (msg);
// Clean up m_LatestTemplates so it doesn't record any data for destroyed entities
m_LatestTemplates.erase(msgData.entity);
break;
}
}
}
virtual void DisableValidation()
{
m_DisableValidation = true;
}
virtual const CParamNode* LoadTemplate(entity_id_t ent, const std::string& templateName, int playerID);
virtual const CParamNode* GetTemplate(std::string templateName);
virtual const CParamNode* LoadLatestTemplate(entity_id_t ent);
virtual std::string GetCurrentTemplateName(entity_id_t ent);
virtual std::vector FindAllTemplates();
private:
// Entity template XML validator
RelaxNGValidator m_Validator;
// Disable validation, for test cases
bool m_DisableValidation;
// Map from template name (XML filename or special |-separated string) to the most recently
// loaded non-broken template data. This includes files that will fail schema validation.
// (Failed loads won't remove existing entries under the same name, so we behave more nicely
// when hotloading broken files)
std::map m_TemplateFileData;
// Map from template name to schema validation status.
// (Some files, e.g. inherited parent templates, may not be valid themselves but we still need to load
// them and use them; we only reject invalid templates that were requested directly by GetTemplate/etc)
std::map m_TemplateSchemaValidity;
// Remember the template used by each entity, so we can return them
// again for deserialization.
// TODO: should store player ID etc.
std::map m_LatestTemplates;
// (Re)loads the given template, regardless of whether it exists already,
// and saves into m_TemplateFileData. Also loads any parents that are not yet
// loaded. Returns false on error.
// @param templateName XML filename to load (not a |-separated string)
bool LoadTemplateFile(const std::string& templateName, int depth);
// Constructs a standard static-decorative-object template for the given actor
void ConstructTemplateActor(const std::string& actorName, CParamNode& out);
// Copy the non-interactive components of an entity template (position, actor, etc) into
// a new entity template
void CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse);
// Copy the components of an entity necessary for a construction foundation
// (position, actor, armour, health, etc) into a new entity template
void CopyFoundationSubset(CParamNode& out, const CParamNode& in);
};
REGISTER_COMPONENT_TYPE(TemplateManager)
const CParamNode* CCmpTemplateManager::LoadTemplate(entity_id_t ent, const std::string& templateName, int UNUSED(playerID))
{
m_LatestTemplates[ent] = templateName;
const CParamNode* templateRoot = GetTemplate(templateName);
if (!templateRoot)
return NULL;
// TODO: Eventually we need to support techs in here, and return a different template per playerID
return templateRoot;
}
const CParamNode* CCmpTemplateManager::GetTemplate(std::string templateName)
{
// Load the template if necessary
if (!LoadTemplateFile(templateName, 0))
{
LOGERROR(L"Failed to load entity template '%hs'", templateName.c_str());
return NULL;
}
if (!m_DisableValidation)
{
// Compute validity, if it's not computed before
if (m_TemplateSchemaValidity.find(templateName) == m_TemplateSchemaValidity.end())
m_TemplateSchemaValidity[templateName] = m_Validator.Validate(CStrW(templateName), m_TemplateFileData[templateName].ToXML());
// Refuse to return invalid templates
if (!m_TemplateSchemaValidity[templateName])
return NULL;
}
const CParamNode& templateRoot = m_TemplateFileData[templateName].GetChild("Entity");
if (!templateRoot.IsOk())
{
// The validator should never let this happen
LOGERROR(L"Invalid root element in entity template '%hs'", templateName.c_str());
return NULL;
}
return &templateRoot;
}
const CParamNode* CCmpTemplateManager::LoadLatestTemplate(entity_id_t ent)
{
std::map::const_iterator it = m_LatestTemplates.find(ent);
if (it == m_LatestTemplates.end())
return NULL;
return LoadTemplate(ent, it->second, -1);
}
std::string CCmpTemplateManager::GetCurrentTemplateName(entity_id_t ent)
{
std::map::const_iterator it = m_LatestTemplates.find(ent);
if (it == m_LatestTemplates.end())
return "";
return it->second;
}
bool CCmpTemplateManager::LoadTemplateFile(const std::string& templateName, int depth)
{
// If this file was already loaded, we don't need to do anything
if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end())
return true;
// Handle infinite loops more gracefully than running out of stack space and crashing
if (depth > 100)
{
LOGERROR(L"Probable infinite inheritance loop in entity template '%hs'", templateName.c_str());
return false;
}
// Handle special case "actor|foo"
if (templateName.find("actor|") == 0)
{
ConstructTemplateActor(templateName.substr(6), m_TemplateFileData[templateName]);
return true;
}
// Handle special case "preview|foo"
if (templateName.find("preview|") == 0)
{
// Load the base entity template, if it wasn't already loaded
std::string baseName = templateName.substr(8);
if (!LoadTemplateFile(baseName, depth+1))
{
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
return false;
}
// Copy a subset to the requested template
CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], false);
return true;
}
// Handle special case "corpse|foo"
if (templateName.find("corpse|") == 0)
{
// Load the base entity template, if it wasn't already loaded
std::string baseName = templateName.substr(7);
if (!LoadTemplateFile(baseName, depth+1))
{
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
return false;
}
// Copy a subset to the requested template
CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], true);
return true;
}
// Handle special case "foundation|foo"
if (templateName.find("foundation|") == 0)
{
// Load the base entity template, if it wasn't already loaded
std::string baseName = templateName.substr(11);
if (!LoadTemplateFile(baseName, depth+1))
{
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
return false;
}
// Copy a subset to the requested template
CopyFoundationSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
return true;
}
// Normal case: templateName is an XML file:
VfsPath path = VfsPath(TEMPLATE_ROOT) / (std::wstring)CStrW(templateName + ".xml");
CXeromyces xero;
PSRETURN ok = xero.Load(g_VFS, path);
if (ok != PSRETURN_OK)
return false; // (Xeromyces already logged an error with the full filename)
int attr_parent = xero.GetAttributeID("parent");
utf16string parentStr = xero.GetRoot().GetAttributes().GetNamedItem(attr_parent);
if (!parentStr.empty())
{
std::string parentName(parentStr.begin(), parentStr.end());
// To prevent needless complexity in template design, we don't allow |-separated strings as parents
if (parentName.find('|') != parentName.npos)
{
LOGERROR(L"Invalid parent '%hs' in entity template '%hs'", parentName.c_str(), templateName.c_str());
return false;
}
// Ensure the parent is loaded
if (!LoadTemplateFile(parentName, depth+1))
{
LOGERROR(L"Failed to load parent '%hs' of entity template '%hs'", parentName.c_str(), templateName.c_str());
return false;
}
CParamNode& parentData = m_TemplateFileData[parentName];
// Initialise this template with its parent
m_TemplateFileData[templateName] = parentData;
}
// Load the new file into the template data (overriding parent values)
CParamNode::LoadXML(m_TemplateFileData[templateName], xero);
return true;
}
void CCmpTemplateManager::ConstructTemplateActor(const std::string& actorName, CParamNode& out)
{
std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(CStrW(actorName)));
std::string xml = ""
""
""
"upright"
"0"
"false"
""
""
"" + name + ""
""
"";
out.LoadXMLString(out, xml.c_str());
}
static LibError AddToTemplates(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)
{
std::vector& templates = *(std::vector*)cbData;
// Strip the .xml extension
VfsPath pathstem = change_extension(pathname, L"");
// Strip the root from the path
std::wstring name = pathstem.string().substr(ARRAY_SIZE(TEMPLATE_ROOT)-1);
// We want to ignore template_*.xml templates, since they should never be built in the editor
if (name.substr(0, 9) == L"template_")
return INFO::OK;
templates.push_back(name);
return INFO::OK;
}
static LibError AddActorToTemplates(const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), const uintptr_t cbData)
{
std::vector& templates = *(std::vector*)cbData;
// Strip the root from the path
std::wstring name = pathname.string().substr(ARRAY_SIZE(ACTOR_ROOT)-1);
templates.push_back(L"actor|" + name);
return INFO::OK;
}
std::vector CCmpTemplateManager::FindAllTemplates()
{
// TODO: eventually this should probably read all the template files and look for flags to
// determine which should be displayed in the editor (and in what categories etc); for now we'll
// just return all the files
std::vector templates;
LibError ok;
// Find all the normal entity templates first
ok = fs_util::ForEachFile(g_VFS, TEMPLATE_ROOT, AddToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE);
WARN_ERR(ok);
// Add all the actors too
ok = fs_util::ForEachFile(g_VFS, ACTOR_ROOT, AddActorToTemplates, (uintptr_t)&templates, L"*.xml", fs_util::DIR_RECURSIVE);
WARN_ERR(ok);
return templates;
}
void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& in, bool corpse)
{
// We only want to include components which are necessary (for the visual previewing of an entity)
// and safe (i.e. won't do anything that affects the synchronised simulation state), so additions
// to this list should be carefully considered
std::set permittedComponentTypes;
permittedComponentTypes.insert("Ownership");
permittedComponentTypes.insert("Position");
permittedComponentTypes.insert("VisualActor");
permittedComponentTypes.insert("Footprint");
permittedComponentTypes.insert("Obstruction");
permittedComponentTypes.insert("Decay");
// Need these for the Actor Viewer:
permittedComponentTypes.insert("Attack");
permittedComponentTypes.insert("UnitMotion");
permittedComponentTypes.insert("Sound");
// (This set could be initialised once and reused, but it's not worth the effort)
CParamNode::LoadXMLString(out, "");
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
// Disable the Obstruction component (if there is one) so it doesn't affect pathfinding
// (but can still be used for testing this entity for collisions against others)
if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
CParamNode::LoadXMLString(out, "");
- // Corpses should include decay components and un-inactivate them
+ if (!corpse)
+ {
+ // Previews should always be visible in fog-of-war
+ CParamNode::LoadXMLString(out, "0true");
+ }
+
if (corpse)
{
+ // Corpses should include decay components and un-inactivate them
if (out.GetChild("Entity").GetChild("Decay").IsOk())
CParamNode::LoadXMLString(out, "");
}
}
void CCmpTemplateManager::CopyFoundationSubset(CParamNode& out, const CParamNode& in)
{
// TODO: this is all kind of yucky and hard-coded; it'd be nice to have a more generic
// extensible scriptable way to define these subsets
std::set permittedComponentTypes;
permittedComponentTypes.insert("Ownership");
permittedComponentTypes.insert("Position");
permittedComponentTypes.insert("VisualActor");
permittedComponentTypes.insert("Identity");
permittedComponentTypes.insert("Obstruction");
permittedComponentTypes.insert("Selectable");
permittedComponentTypes.insert("Footprint");
permittedComponentTypes.insert("Armour");
permittedComponentTypes.insert("Health");
permittedComponentTypes.insert("Decay");
permittedComponentTypes.insert("Cost");
permittedComponentTypes.insert("Sound");
+ permittedComponentTypes.insert("Vision");
CParamNode::LoadXMLString(out, "");
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
// TODO: the foundation shouldn't be considered an obstruction by default, until construction has
// really started, to prevent players abusing it to block their opponents
// Switch the actor to foundation mode
CParamNode::LoadXMLString(out, "");
// Add the Foundation component, to deal with the construction process
CParamNode::LoadXMLString(out, "");
// Initialise health to 1
CParamNode::LoadXMLString(out, "1");
// Don't provide population bonuses yet (but still do take up population cost)
if (out.GetChild("Entity").GetChild("Cost").IsOk())
CParamNode::LoadXMLString(out, "0");
+
+ // 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, "0");
}
Index: ps/trunk/source/simulation2/components/CCmpRangeManager.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpRangeManager.cpp (revision 8159)
+++ ps/trunk/source/simulation2/components/CCmpRangeManager.cpp (revision 8160)
@@ -1,578 +1,791 @@
/* 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 .
*/
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpRangeManager.h"
#include "ICmpPosition.h"
+#include "ICmpVision.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/helpers/Render.h"
#include "simulation2/helpers/Spatial.h"
#include "graphics/Overlay.h"
#include "graphics/Terrain.h"
#include "lib/timer.h"
#include "maths/FixedVector2D.h"
#include "ps/CLogger.h"
#include "ps/Overlay.h"
#include "ps/Profile.h"
#include "renderer/Scene.h"
/**
* Representation of a range query.
*/
struct Query
{
bool enabled;
entity_id_t source;
entity_pos_t maxRange;
u32 ownersMask;
int interface;
std::vector lastMatch;
};
/**
* 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
}
/**
* Representation of an entity, with the data needed for queries.
*/
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.
* It must only be passed entities that are in 'entities'
* and are currently in the world.
*/
struct EntityDistanceOrdering
{
EntityDistanceOrdering(const std::map& entities, const CFixedVector2D& source) :
m_EntityData(entities), m_Source(source)
{
}
bool operator()(entity_id_t a, entity_id_t b)
{
const EntityData& da = m_EntityData.find(a)->second;
const EntityData& db = m_EntityData.find(b)->second;
CFixedVector2D vecA = CFixedVector2D(da.x, da.z) - m_Source;
CFixedVector2D vecB = CFixedVector2D(db.x, db.z) - m_Source;
return (vecA.CompareLength(vecB) < 0);
}
const std::map& m_EntityData;
CFixedVector2D m_Source;
private:
EntityDistanceOrdering& operator=(const EntityDistanceOrdering&);
};
/**
- * 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
{
public:
static void ClassInit(CComponentManager& componentManager)
{
componentManager.SubscribeGloballyToMessageType(MT_Create);
componentManager.SubscribeGloballyToMessageType(MT_PositionChanged);
componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged);
componentManager.SubscribeGloballyToMessageType(MT_Destroy);
componentManager.SubscribeToMessageType(MT_Update);
componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
}
DEFAULT_COMPONENT_ALLOCATOR(RangeManager)
bool m_DebugOverlayEnabled;
bool m_DebugOverlayDirty;
std::vector m_DebugOverlayLines;
SpatialSubdivision m_Subdivision;
+ // Range query state:
tag_t m_QueryNext; // next allocated id
std::map m_Queries;
std::map 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 > m_LosPlayerCounts;
+
+ // 2-bit ELosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive)
+ std::vector m_LosState;
+ static const int MAX_LOS_PLAYER_ID = 16;
+
static std::string GetSchema()
{
return "";
}
virtual void Init(const CSimContext& UNUSED(context), const CParamNode& UNUSED(paramNode))
{
m_QueryNext = 1;
m_DebugOverlayEnabled = false;
m_DebugOverlayDirty = true;
// 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))
{
}
virtual void Serialize(ISerializer& UNUSED(serialize))
{
// TODO
}
virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
{
Init(context, paramNode);
}
virtual void HandleMessage(const CSimContext& UNUSED(context), const CMessage& msg, bool UNUSED(global))
{
switch (msg.GetType())
{
case MT_Create:
{
const CMessageCreate& msgData = static_cast (msg);
entity_id_t ent = msgData.entity;
// Ignore local entities - we shouldn't let them influence anything
if (ENTITY_IS_LOCAL(ent))
break;
// Ignore non-positional entities
CmpPtr cmpPosition(GetSimContext(), ent);
if (cmpPosition.null())
break;
// The newly-created entity will have owner -1 and position out-of-world
// (any initialisation of those values will happen later), so we can just
// use the default-constructed EntityData here
EntityData entdata;
+ // Store the LOS data, if any
+ CmpPtr 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));
break;
}
case MT_PositionChanged:
{
const CMessagePositionChanged& msgData = static_cast (msg);
entity_id_t ent = msgData.entity;
std::map::iterator it = m_EntityData.find(ent);
// Ignore if we're not already tracking this entity
if (it == m_EntityData.end())
break;
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;
it->second.z = msgData.z;
}
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();
it->second.z = entity_pos_t::Zero();
}
break;
}
case MT_OwnershipChanged:
{
const CMessageOwnershipChanged& msgData = static_cast (msg);
entity_id_t ent = msgData.entity;
std::map::iterator it = m_EntityData.find(ent);
// Ignore if we're not already tracking this entity
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;
}
case MT_Destroy:
{
const CMessageDestroy& msgData = static_cast (msg);
entity_id_t ent = msgData.entity;
std::map::iterator it = m_EntityData.find(ent);
// Ignore if we're not already tracking this entity
if (it == m_EntityData.end())
break;
if (it->second.inWorld)
m_Subdivision.Remove(ent, CFixedVector2D(it->second.x, it->second.z));
m_EntityData.erase(it);
break;
}
case MT_Update:
{
m_DebugOverlayDirty = true;
ExecuteActiveQueries();
break;
}
case MT_RenderSubmit:
{
const CMessageRenderSubmit& msgData = static_cast (msg);
RenderSubmit(msgData.collector);
break;
}
}
}
- 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::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)
{
// Use 8x8 tile subdivisions
// (TODO: find the optimal number instead of blindly guessing)
m_Subdivision.Reset(x1, z1, entity_pos_t::FromInt(8*CELL_SIZE));
for (std::map::const_iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it)
{
if (it->second.inWorld)
m_Subdivision.Add(it->first, CFixedVector2D(it->second.x, it->second.z));
}
}
virtual tag_t CreateActiveQuery(entity_id_t source, entity_pos_t maxRange,
std::vector owners, int requiredInterface)
{
size_t id = m_QueryNext++;
m_Queries[id] = ConstructQuery(source, maxRange, owners, requiredInterface);
return (tag_t)id;
}
virtual void DestroyActiveQuery(tag_t tag)
{
if (m_Queries.find(tag) == m_Queries.end())
{
LOGERROR(L"CCmpRangeManager: DestroyActiveQuery called with invalid tag %d", tag);
return;
}
m_Queries.erase(tag);
}
virtual void EnableActiveQuery(tag_t tag)
{
std::map::iterator it = m_Queries.find(tag);
if (it == m_Queries.end())
{
LOGERROR(L"CCmpRangeManager: EnableActiveQuery called with invalid tag %d", tag);
return;
}
Query& q = it->second;
q.enabled = true;
}
virtual void DisableActiveQuery(tag_t tag)
{
std::map::iterator it = m_Queries.find(tag);
if (it == m_Queries.end())
{
LOGERROR(L"CCmpRangeManager: DisableActiveQuery called with invalid tag %d", tag);
return;
}
Query& q = it->second;
q.enabled = false;
}
virtual std::vector ExecuteQuery(entity_id_t source, entity_pos_t maxRange,
std::vector owners, int requiredInterface)
{
PROFILE("ExecuteQuery");
Query q = ConstructQuery(source, maxRange, owners, requiredInterface);
std::vector r;
CmpPtr cmpSourcePosition(GetSimContext(), q.source);
if (cmpSourcePosition.null() || !cmpSourcePosition->IsInWorld())
{
// If the source doesn't have a position, then the result is just the empty list
return r;
}
PerformQuery(q, r);
// Return the list sorted by distance from the entity
CFixedVector2D pos = cmpSourcePosition->GetPosition2D();
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos));
return r;
}
virtual std::vector ResetActiveQuery(tag_t tag)
{
PROFILE("ResetActiveQuery");
std::vector r;
std::map::iterator it = m_Queries.find(tag);
if (it == m_Queries.end())
{
LOGERROR(L"CCmpRangeManager: ResetActiveQuery called with invalid tag %d", tag);
return r;
}
Query& q = it->second;
q.enabled = true;
CmpPtr cmpSourcePosition(GetSimContext(), q.source);
if (cmpSourcePosition.null() || !cmpSourcePosition->IsInWorld())
{
// If the source doesn't have a position, then the result is just the empty list
q.lastMatch = r;
return r;
}
PerformQuery(q, r);
q.lastMatch = r;
// Return the list sorted by distance from the entity
CFixedVector2D pos = cmpSourcePosition->GetPosition2D();
std::stable_sort(r.begin(), r.end(), EntityDistanceOrdering(m_EntityData, pos));
return r;
}
virtual void SetDebugOverlay(bool enabled)
{
m_DebugOverlayEnabled = enabled;
m_DebugOverlayDirty = true;
if (!enabled)
m_DebugOverlayLines.clear();
}
private:
/**
* Update all currently-enabled active queries.
*/
void ExecuteActiveQueries()
{
PROFILE("ExecuteActiveQueries");
// Store a queue of all messages before sending any, so we can assume
// no entities will move until we've finished checking all the ranges
std::vector > messages;
for (std::map::iterator it = m_Queries.begin(); it != m_Queries.end(); ++it)
{
Query& q = it->second;
if (!q.enabled)
continue;
CmpPtr cmpSourcePosition(GetSimContext(), q.source);
if (cmpSourcePosition.null() || !cmpSourcePosition->IsInWorld())
continue;
std::vector r;
r.reserve(q.lastMatch.size());
PerformQuery(q, r);
// Compute the changes vs the last match
std::vector added;
std::vector removed;
std::set_difference(r.begin(), r.end(), q.lastMatch.begin(), q.lastMatch.end(), std::back_inserter(added));
std::set_difference(q.lastMatch.begin(), q.lastMatch.end(), r.begin(), r.end(), std::back_inserter(removed));
if (added.empty() && removed.empty())
continue;
// Return the 'added' list sorted by distance from the entity
// (Don't bother sorting 'removed' because they might not even have positions or exist any more)
CFixedVector2D pos = cmpSourcePosition->GetPosition2D();
std::stable_sort(added.begin(), added.end(), EntityDistanceOrdering(m_EntityData, pos));
messages.push_back(std::make_pair(q.source, CMessageRangeUpdate(it->first)));
messages.back().second.added.swap(added);
messages.back().second.removed.swap(removed);
it->second.lastMatch.swap(r);
}
for (size_t i = 0; i < messages.size(); ++i)
GetSimContext().GetComponentManager().PostMessage(messages[i].first, messages[i].second);
}
/**
* Returns a list of distinct entity IDs that match the given query, sorted by ID.
*/
void PerformQuery(const Query& q, std::vector& r)
{
CmpPtr cmpSourcePosition(GetSimContext(), q.source);
if (cmpSourcePosition.null() || !cmpSourcePosition->IsInWorld())
return;
CFixedVector2D pos = cmpSourcePosition->GetPosition2D();
// Get a quick list of entities that are potentially in range
std::vector ents = m_Subdivision.GetNear(pos, q.maxRange);
for (size_t i = 0; i < ents.size(); ++i)
{
std::map::const_iterator it = m_EntityData.find(ents[i]);
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
if (!it->second.inWorld)
continue;
int distVsMax = (CFixedVector2D(it->second.x, it->second.z) - pos).CompareLength(q.maxRange);
if (distVsMax > 0)
continue;
// Ignore self
if (it->first == q.source)
continue;
// Ignore if it's missing the required interface
if (q.interface && !GetSimContext().GetComponentManager().QueryInterface(it->first, q.interface))
continue;
r.push_back(it->first);
}
}
Query ConstructQuery(entity_id_t source, entity_pos_t maxRange, std::vector owners, int requiredInterface)
{
Query q;
q.enabled = false;
q.source = source;
q.maxRange = maxRange;
q.ownersMask = 0;
for (size_t i = 0; i < owners.size(); ++i)
q.ownersMask |= CalcOwnerMask(owners[i]);
q.interface = requiredInterface;
return q;
}
void RenderSubmit(SceneCollector& collector)
{
if (!m_DebugOverlayEnabled)
return;
CColor enabledRingColour(0, 1, 0, 1);
CColor disabledRingColour(1, 0, 0, 1);
CColor rayColour(1, 1, 0, 0.2f);
if (m_DebugOverlayDirty)
{
m_DebugOverlayLines.clear();
for (std::map::iterator it = m_Queries.begin(); it != m_Queries.end(); ++it)
{
Query& q = it->second;
CmpPtr cmpSourcePosition(GetSimContext(), q.source);
if (cmpSourcePosition.null() || !cmpSourcePosition->IsInWorld())
continue;
CFixedVector2D pos = cmpSourcePosition->GetPosition2D();
// Draw the range circle
m_DebugOverlayLines.push_back(SOverlayLine());
m_DebugOverlayLines.back().m_Color = (q.enabled ? enabledRingColour : disabledRingColour);
SimRender::ConstructCircleOnGround(GetSimContext(), pos.X.ToFloat(), pos.Y.ToDouble(), q.maxRange.ToFloat(), m_DebugOverlayLines.back(), true);
// Draw a ray from the source to each matched entity
for (size_t i = 0; i < q.lastMatch.size(); ++i)
{
CmpPtr cmpTargetPosition(GetSimContext(), q.lastMatch[i]);
if (cmpTargetPosition.null() || !cmpTargetPosition->IsInWorld())
continue;
CFixedVector2D targetPos = cmpTargetPosition->GetPosition2D();
std::vector coords;
coords.push_back(pos.X.ToFloat());
coords.push_back(pos.Y.ToFloat());
coords.push_back(targetPos.X.ToFloat());
coords.push_back(targetPos.Y.ToFloat());
m_DebugOverlayLines.push_back(SOverlayLine());
m_DebugOverlayLines.back().m_Color = rayColour;
SimRender::ConstructLineOnGround(GetSimContext(), coords, m_DebugOverlayLines.back(), true);
}
}
m_DebugOverlayDirty = false;
}
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 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 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& 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& 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)
Index: ps/trunk/source/simulation2/components/ICmpMinimap.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpMinimap.h (revision 8159)
+++ ps/trunk/source/simulation2/components/ICmpMinimap.h (revision 8160)
@@ -1,39 +1,41 @@
/* 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 .
*/
#ifndef INCLUDED_ICMPMINIMAP
#define INCLUDED_ICMPMINIMAP
#include "simulation2/system/Interface.h"
+#include "simulation2/helpers/Position.h"
+
/**
* Per-unit minimap data.
*/
class ICmpMinimap : public IComponent
{
public:
/**
* Get the data for rendering this entity on the minimap.
* 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)
};
#endif // INCLUDED_ICMPMINIMAP
Index: ps/trunk/source/simulation2/components/ICmpVision.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpVision.cpp (nonexistent)
+++ ps/trunk/source/simulation2/components/ICmpVision.cpp (revision 8160)
@@ -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 .
+ */
+
+#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)
Property changes on: ps/trunk/source/simulation2/components/ICmpVision.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/simulation2/components/CCmpMinimap.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpMinimap.cpp (revision 8159)
+++ ps/trunk/source/simulation2/components/CCmpMinimap.cpp (revision 8160)
@@ -1,177 +1,177 @@
/* 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 .
*/
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpMinimap.h"
#include "simulation2/components/ICmpPlayerManager.h"
#include "simulation2/components/ICmpPlayer.h"
#include "simulation2/MessageTypes.h"
#include "ps/Overlay.h"
class CCmpMinimap : public ICmpMinimap
{
public:
static void ClassInit(CComponentManager& componentManager)
{
componentManager.SubscribeToMessageType(MT_PositionChanged);
componentManager.SubscribeToMessageType(MT_OwnershipChanged);
}
DEFAULT_COMPONENT_ALLOCATOR(Minimap)
bool m_Active;
bool m_UsePlayerColour;
u8 m_R, m_G, m_B;
entity_pos_t m_X, m_Z; // cache the latest position for more efficient rendering
static std::string GetSchema()
{
return
""
""
"food"
"wood"
"stone"
"metal"
"structure"
"settlement"
"unit"
"support"
"hero"
""
""
""
""
""
"0255"
""
""
"0255"
""
""
"0255"
""
""
"";
}
virtual void Init(const CSimContext& UNUSED(context), const CParamNode& paramNode)
{
m_Active = true;
const CParamNode& colour = paramNode.GetChild("Colour");
if (colour.IsOk())
{
m_UsePlayerColour = false;
m_R = colour.GetChild("@r").ToInt();
m_G = colour.GetChild("@g").ToInt();
m_B = colour.GetChild("@b").ToInt();
}
else
{
m_UsePlayerColour = true;
// Choose a bogus colour which will get replaced once we have an owner
m_R = 255;
m_G = 0;
m_B = 255;
}
}
virtual void Deinit(const CSimContext& UNUSED(context))
{
}
virtual void Serialize(ISerializer& serialize)
{
// TODO
}
virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& deserialize)
{
Init(context, paramNode);
// TODO
}
virtual void HandleMessage(const CSimContext& context, const CMessage& msg, bool UNUSED(global))
{
switch (msg.GetType())
{
case MT_PositionChanged:
{
const CMessagePositionChanged& data = static_cast (msg);
if (data.inWorld)
{
m_Active = true;
m_X = data.x;
m_Z = data.z;
}
else
{
m_Active = false;
}
break;
}
case MT_OwnershipChanged:
{
if (!m_UsePlayerColour)
break;
const CMessageOwnershipChanged& msgData = static_cast (msg);
// If there's no new owner (e.g. the unit is dying) then don't try updating the colour
if (msgData.to == -1)
break;
// Find the new player's colour
CmpPtr cmpPlayerManager(context, SYSTEM_ENTITY);
if (cmpPlayerManager.null())
break;
CmpPtr cmpPlayer(context, cmpPlayerManager->GetPlayerByID(msgData.to));
if (cmpPlayer.null())
break;
CColor colour = cmpPlayer->GetColour();
m_R = (int)(colour.r*255.0);
m_G = (int)(colour.g*255.0);
m_B = (int)(colour.b*255.0);
break;
}
}
}
- 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;
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;
}
};
REGISTER_COMPONENT_TYPE(Minimap)
Index: ps/trunk/source/simulation2/components/ICmpVision.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpVision.h (nonexistent)
+++ ps/trunk/source/simulation2/components/ICmpVision.h (revision 8160)
@@ -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 .
+ */
+
+#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
Property changes on: ps/trunk/source/simulation2/components/ICmpVision.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/source/renderer/PatchRData.cpp
===================================================================
--- ps/trunk/source/renderer/PatchRData.cpp (revision 8159)
+++ ps/trunk/source/renderer/PatchRData.cpp (revision 8160)
@@ -1,642 +1,631 @@
/* 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 .
*/
#include "precompiled.h"
#include
#include
#include "ps/Pyrogenesis.h"
#include "graphics/LightEnv.h"
#include "Renderer.h"
#include "renderer/PatchRData.h"
#include "AlphaMapCalculator.h"
#include "ps/CLogger.h"
#include "ps/Profile.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "maths/MathUtil.h"
#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 },
{ -1, -1 },
{ -1, 0 },
{ -1, 1 },
{ 0, 1 },
{ 1, 1 },
{ 1, 0 },
{ 1, -1 }
};
///////////////////////////////////////////////////////////////////
// CPatchRData constructor
CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_VBBase(0), m_VBBlends(0), m_Vertices(0)
{
debug_assert(patch);
Build();
}
///////////////////////////////////////////////////////////////////
// CPatchRData destructor
CPatchRData::~CPatchRData()
{
// delete copy of vertex data
delete[] m_Vertices;
// release vertex buffer chunks
if (m_VBBase) g_VBMan.Release(m_VBBase);
if (m_VBBlends) g_VBMan.Release(m_VBBlends);
}
static CTerrainTextureEntry* GetTerrainTileTexture(CTerrain* terrain, ssize_t gx, ssize_t gz)
{
CMiniPatch* mp = terrain->GetTile(gx, gz);
if (!mp)
return 0;
return mp->GetTextureEntry();
}
const float uvFactor = 0.125f / sqrt(2.f);
static void CalculateUV(float uv[2], ssize_t x, ssize_t z)
{
// The UV axes are offset 45 degrees from XZ
uv[0] = ( x-z)*uvFactor;
uv[1] = (-x-z)*uvFactor;
}
struct STmpSplat {
CTerrainTextureEntry* m_Texture;
u16 m_Indices[4];
};
void CPatchRData::BuildBlends()
{
m_BlendIndices.clear();
m_BlendSplats.clear();
m_BlendVertices.clear();
m_BlendVertexIndices.clear();
CTerrain* terrain=m_Patch->m_Parent;
// temporary list of splats
std::vector splats;
// set of textures used for splats
std::set splatTextures;
// for each tile in patch ..
for (ssize_t j=0;jm_MiniPatches[j][i];
ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
// build list of textures of higher priority than current tile that are used by neighbouring tiles
std::vector neighbourTextures;
for (int m=-1;m<=1;m++) {
for (int k=-1;k<=1;k++) {
CMiniPatch* nmp=terrain->GetTile(gx+k,gz+m);
if (nmp && nmp->GetTextureEntry() != mp->GetTextureEntry()) {
if (nmp->GetPriority() > mp->GetPriority() || (nmp->GetPriority() == mp->GetPriority() && nmp->GetTextureEntry() > mp->GetTextureEntry())) {
STex tex;
tex.m_Texture=nmp->GetTextureEntry();
tex.m_Priority=nmp->GetPriority();
if (std::find(neighbourTextures.begin(),neighbourTextures.end(),tex)==neighbourTextures.end()) {
neighbourTextures.push_back(tex);
}
}
}
}
}
if (neighbourTextures.size()>0) {
// sort textures from lowest to highest priority
std::sort(neighbourTextures.begin(),neighbourTextures.end());
// for each of the neighbouring textures ..
size_t count=neighbourTextures.size();
for (size_t k=0;km_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]);
// now build outgoing splats
m_BlendSplats.resize(splatTextures.size());
size_t splatCount=0;
debug_assert(m_VBBlends->m_Index < 65536);
unsigned short base = (unsigned short)m_VBBlends->m_Index;
std::set::iterator iter=splatTextures.begin();
for (;iter!=splatTextures.end();++iter) {
CTerrainTextureEntry* tex=*iter;
SSplat& splat=m_BlendSplats[splatCount];
splat.m_IndexStart=m_BlendIndices.size();
splat.m_Texture=tex;
for (size_t k=0;k textures;
CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
for (ssize_t j=0;jm_MiniPatches[j][i].GetTextureEntry();
texgrid[j][i]=tex;
if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
textures.push_back(tex);
}
}
}
// now build base splats from interior textures
m_Splats.resize(textures.size());
// build indices for base splats
size_t base=m_VBBase->m_Index;
for (size_t i=0;im_X;
ssize_t pz=m_Patch->m_Z;
CTerrain* terrain=m_Patch->m_Parent;
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
// build vertices
for (ssize_t j=0;jCalcPosition(ix,iz,vertices[v].m_Position);
vertices[v].m_LOSColor = SColor4ub(0, 0, 0, 0); // will be set to the proper value in Update()
CalculateUV(vertices[v].m_UVs, ix, iz);
// Calculate diffuse lighting for this vertex
// Ambient is added by the lighting pass (since ambient is the same
// for all vertices, it need not be stored in the vertex structure)
terrain->CalcNormal(ix,iz,normal);
RGBColor diffuse;
lightEnv.EvaluateDirect(normal, diffuse);
vertices[v].m_DiffuseColor = ConvertRGBColorTo4ub(diffuse);
}
}
// upload to vertex buffer
if (!m_VBBase) {
m_VBBase=g_VBMan.Allocate(sizeof(SBaseVertex),vsize*vsize,true);
}
m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices);
}
void CPatchRData::Build()
{
BuildVertices();
BuildIndices();
BuildBlends();
}
void CPatchRData::Update()
{
if (m_UpdateFlags!=0) {
// TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
// than everything; it's complicated slightly because the blends are dependent
// on both vertex and index data
BuildVertices();
BuildIndices();
BuildBlends();
m_UpdateFlags=0;
}
// Update vertex colors, which are affected by LOS
ssize_t px=m_Patch->m_X;
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= 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 cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
+ if (cmpRangeManager.null() || cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID()))
{
for (ssize_t j = 0; j < vsize; ++j)
{
for (ssize_t i = 0; i < vsize; ++i)
{
ssize_t v = (j*vsize)+i;
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
m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase,m_Vertices);
// update blend colors by copying them from vertex colors
for(size_t i=0; im_Owner->UpdateChunkVertices(m_VBBlends,&m_BlendVertices[0]);
}
}
void CPatchRData::RenderBase(bool losColor)
{
debug_assert(m_UpdateFlags==0);
SBaseVertex *base=(SBaseVertex *)m_VBBase->m_Owner->Bind();
// setup data pointers
GLsizei stride=sizeof(SBaseVertex);
glVertexPointer(3,GL_FLOAT,stride,&base->m_Position[0]);
glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor);
glTexCoordPointer(2,GL_FLOAT,stride,&base->m_UVs[0]);
// render each splat
for (size_t i=0;iGetTexture()->Bind();
else
g_Renderer.GetTextureManager().GetErrorTexture()->Bind();
if (!g_Renderer.m_SkipSubmit) {
glDrawElements(GL_QUADS, (GLsizei)splat.m_IndexCount,
GL_UNSIGNED_SHORT, &m_Indices[splat.m_IndexStart]);
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2;
}
CVertexBuffer::Unbind();
}
void CPatchRData::RenderStreams(int streamflags, bool losColor)
{
debug_assert(m_UpdateFlags==0);
SBaseVertex* base=(SBaseVertex *)m_VBBase->m_Owner->Bind();
// setup data pointers
GLsizei stride=sizeof(SBaseVertex);
glVertexPointer(3, GL_FLOAT, stride, &base->m_Position);
if (streamflags & STREAM_UV0) {
glTexCoordPointer(2, GL_FLOAT, stride, &base->m_UVs);
} else if (streamflags & STREAM_POSTOUV0) {
glTexCoordPointer(3, GL_FLOAT, stride, &base->m_Position);
}
if (streamflags & STREAM_COLOR)
{
glColorPointer(4,GL_UNSIGNED_BYTE,stride,losColor ? &base->m_LOSColor : &base->m_DiffuseColor);
}
// render all base splats at once
if (!g_Renderer.m_SkipSubmit) {
glDrawElements(GL_QUADS,(GLsizei)m_Indices.size(),GL_UNSIGNED_SHORT,&m_Indices[0]);
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_TerrainTris+=m_Indices.size()/2;
CVertexBuffer::Unbind();
}
void CPatchRData::RenderBlends()
{
debug_assert(m_UpdateFlags==0);
if (m_BlendVertices.size()==0) return;
u8* base=m_VBBlends->m_Owner->Bind();
// setup data pointers
GLsizei stride=sizeof(SBlendVertex);
// ((GCC warns about offsetof: SBlendVertex contains a CVector3D which has
// a constructor, and so is not a POD type, and so offsetof is theoretically
// invalid - see http://gcc.gnu.org/ml/gcc/2003-11/msg00281.html - but it
// doesn't seem to be worth changing this code since it works anyway.))
glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position));
glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_LOSColor));
pglClientActiveTextureARB(GL_TEXTURE0);
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0]));
pglClientActiveTextureARB(GL_TEXTURE1);
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0]));
for (size_t i=0;iGetTexture()->Bind();
else
g_Renderer.GetTextureManager().GetErrorTexture()->Bind();
if (!g_Renderer.m_SkipSubmit) {
glDrawElements(GL_QUADS, (GLsizei)splat.m_IndexCount,
GL_UNSIGNED_SHORT, &m_BlendIndices[splat.m_IndexStart]);
}
// bump stats
g_Renderer.m_Stats.m_DrawCalls++;
g_Renderer.m_Stats.m_BlendSplats++;
g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2;
}
CVertexBuffer::Unbind();
}
void CPatchRData::RenderOutline()
{
size_t vsize=PATCH_SIZE+1;
glBegin(GL_LINES);
for (ssize_t i=0;i.
*/
/*
* Terrain rendering (everything related to patches and water) is
* encapsulated in TerrainRenderer
*/
#include "precompiled.h"
#include "graphics/Camera.h"
#include "graphics/LightEnv.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "graphics/GameView.h"
#include "maths/MathUtil.h"
#include "ps/Filesystem.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/Pyrogenesis.h" // MICROLOG
#include "ps/World.h"
#include "simulation2/Simulation2.h"
+#include "simulation2/components/ICmpRangeManager.h"
#include "renderer/PatchRData.h"
#include "renderer/Renderer.h"
#include "renderer/ShadowMap.h"
#include "renderer/TerrainRenderer.h"
#include "renderer/WaterManager.h"
#include "lib/res/graphics/ogl_shader.h"
#define LOG_CATEGORY L"graphics"
///////////////////////////////////////////////////////////////////////////////////////////////
// TerrainRenderer implementation
/**
* TerrainRenderer keeps track of which phase it is in, to detect
* when Submit, PrepareForRendering etc. are called in the wrong order.
*/
enum Phase {
Phase_Submit,
Phase_Render
};
/**
* Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class.
*/
struct TerrainRendererInternals
{
/// Which phase (submitting or rendering patches) are we in right now?
Phase phase;
/**
* VisiblePatches: Patches that were submitted for this frame
*
* @todo Merge this list with CPatchRData list
*/
std::vector visiblePatches;
/// Fancy water shader
Handle fancyWaterShader;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
TerrainRenderer::TerrainRenderer()
{
m = new TerrainRendererInternals();
m->phase = Phase_Submit;
m->fancyWaterShader = 0;
}
TerrainRenderer::~TerrainRenderer()
{
if( m->fancyWaterShader )
{
ogl_program_free( m->fancyWaterShader );
}
delete m;
}
///////////////////////////////////////////////////////////////////
// Submit a patch for rendering
void TerrainRenderer::Submit(CPatch* patch)
{
debug_assert(m->phase == Phase_Submit);
CPatchRData* data=(CPatchRData*) patch->GetRenderData();
if (data == 0)
{
// no renderdata for patch, create it now
data = new CPatchRData(patch);
patch->SetRenderData(data);
}
data->Update();
m->visiblePatches.push_back(patch);
}
///////////////////////////////////////////////////////////////////
// Prepare for rendering
void TerrainRenderer::PrepareForRendering()
{
debug_assert(m->phase == Phase_Submit);
m->phase = Phase_Render;
}
///////////////////////////////////////////////////////////////////
// Clear submissions lists
void TerrainRenderer::EndFrame()
{
debug_assert(m->phase == Phase_Render);
m->visiblePatches.clear();
m->phase = Phase_Submit;
}
///////////////////////////////////////////////////////////////////
// Query if patches have been submitted this frame
bool TerrainRenderer::HaveSubmissions()
{
return !m->visiblePatches.empty();
}
///////////////////////////////////////////////////////////////////
// Full-featured terrain rendering with blending and everything
void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
{
debug_assert(m->phase == Phase_Render);
// switch on required client states
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// render everything fullbright
// set up texture environment for base pass
MICROLOG(L"base splat textures");
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
// Set alpha to 1.0
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
static const float one[4] = { 1.f, 1.f, 1.f, 1.f };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one);
for(size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderBase(true); // with LOS color
}
// render blends
// switch on the composite alpha map texture
(void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
// switch on second uv set
pglClientActiveTextureARB(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// setup additional texenv required by blend pass
pglActiveTextureARB(GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA);
// switch on blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
// no need to write to the depth buffer a second time
glDepthMask(0);
// render blend passes for each patch
for(size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderBlends();
}
// Disable second texcoord array
pglClientActiveTextureARB(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
// Now apply lighting
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
glBlendFunc(GL_DST_COLOR, GL_ZERO);
// GL_TEXTURE_ENV_COLOR requires four floats, so we shouldn't use the RGBColor directly
float terrainAmbientColor[4] = {
lightEnv.m_TerrainAmbientColor.X,
lightEnv.m_TerrainAmbientColor.Y,
lightEnv.m_TerrainAmbientColor.Z,
1.f
};
if (!shadow)
{
pglActiveTextureARB(GL_TEXTURE0);
// We're not going to use a texture here, but we have to have a valid texture
// bound else the texture unit will be disabled.
// We should still have a bound splat texture from some earlier rendering,
// so assume that's still valid to use.
// (TODO: That's a bit of an ugly hack.)
// Shadow rendering disabled: Ambient + Diffuse
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor);
}
else
{
const CMatrix3D& texturematrix = shadow->GetTextureMatrix();
pglActiveTextureARB(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(&texturematrix._11);
glMatrixMode(GL_MODELVIEW);
glBindTexture(GL_TEXTURE_2D, shadow->GetTexture());
if (shadow->GetUseDepthTexture())
{
// Ambient + ShTranslucency * Diffuse * (1 - Shadow) + Diffuse * Shadow
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
float shadowTransp = g_Renderer.GetLightEnv().GetTerrainShadowTransparency();
float color[4] = { shadowTransp, shadowTransp, shadowTransp, 1.0 };
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
pglActiveTextureARB(GL_TEXTURE1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
pglActiveTextureARB(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, shadow->GetTexture()); // Need a valid texture or the unit will be disabled
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor);
}
else
{
// Ambient + Diffuse * Shadow
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
pglActiveTextureARB(GL_TEXTURE1); // + Ambient
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor);
}
}
pglActiveTextureARB(GL_TEXTURE0);
pglClientActiveTextureARB(GL_TEXTURE0);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderStreams(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0, false);
}
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
// restore OpenGL state
if (shadow)
{
if (shadow->GetUseDepthTexture())
g_Renderer.BindTexture(2,0);
}
g_Renderer.BindTexture(1,0);
pglClientActiveTextureARB(GL_TEXTURE0);
pglActiveTextureARB(GL_TEXTURE0);
glDepthMask(1);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render un-textured patches as polygons
void TerrainRenderer::RenderPatches()
{
debug_assert(m->phase == Phase_Render);
glEnableClientState(GL_VERTEX_ARRAY);
for(size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderStreams(STREAM_POS, true);
}
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render outlines of submitted patches as lines
void TerrainRenderer::RenderOutlines()
{
glEnableClientState(GL_VERTEX_ARRAY);
for(size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
patchdata->RenderOutline();
}
glDisableClientState(GL_VERTEX_ARRAY);
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater()
{
PROFILE( "render water" );
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
bool fancy = WaterMgr->WillRenderFancyWater();
// If we're using fancy water, make sure its shader is loaded
if(fancy && !m->fancyWaterShader)
{
Handle h = ogl_program_load(g_VFS, L"shaders/water_high.xml");
if (h < 0)
{
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to load water shader. Falling back to non-fancy water.\n");
g_Renderer.m_Options.m_FancyWater = false;
fancy = false;
}
else
{
m->fancyWaterShader = h;
}
}
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
-// int mapSize = terrain->GetVerticesPerSide();
-// CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
+ CmpPtr 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);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 1.6;
int curTex = (int)(time*60/period) % 60;
if(fancy)
{
WaterMgr->m_NormalMap[curTex]->Bind();
}
else
{
WaterMgr->m_WaterTexture[curTex]->Bind();
}
// Shift the texture coordinates by these amounts to make the water "flow"
float tx = -fmod(time, 81.0)/81.0;
float ty = -fmod(time, 34.0)/34.0;
if(!fancy)
{
// Perform the shifting by modifying the texture matrix
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslatef(tx, ty, 0);
// Set up texture environment to multiply vertex RGB by texture RGB and use vertex alpha
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
}
// Set the proper LOD bias
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
const CCamera& camera = g_Renderer.GetViewCamera();
CVector3D camPos = camera.m_Orientation.GetTranslation();
GLint vertexDepth = 0; // water depth attribute, if using fancy water
GLint losMultiplier = 0; // LOS multiplier, if using fancy water
if(fancy)
{
// Bind reflection and refraction textures on texture units 1 and 2
pglActiveTextureARB( GL_TEXTURE1_ARB );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, WaterMgr->m_ReflectionTexture );
pglActiveTextureARB( GL_TEXTURE2_ARB );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, WaterMgr->m_RefractionTexture );
// Bind water shader and set arguments
ogl_program_use( m->fancyWaterShader );
GLint ambient = ogl_program_get_uniform_location( m->fancyWaterShader, "ambient" );
GLint sunDir = ogl_program_get_uniform_location( m->fancyWaterShader, "sunDir" );
GLint sunColor = ogl_program_get_uniform_location( m->fancyWaterShader, "sunColor" );
GLint cameraPos = ogl_program_get_uniform_location( m->fancyWaterShader, "cameraPos" );
GLint shininess = ogl_program_get_uniform_location( m->fancyWaterShader, "shininess" );
GLint specularStrength = ogl_program_get_uniform_location( m->fancyWaterShader, "specularStrength" );
GLint waviness = ogl_program_get_uniform_location( m->fancyWaterShader, "waviness" );
GLint murkiness = ogl_program_get_uniform_location( m->fancyWaterShader, "murkiness" );
GLint fullDepth = ogl_program_get_uniform_location( m->fancyWaterShader, "fullDepth" );
GLint tint = ogl_program_get_uniform_location( m->fancyWaterShader, "tint" );
GLint reflectionTint = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionTint" );
GLint reflectionTintStrength = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionTintStrength" );
GLint translation = ogl_program_get_uniform_location( m->fancyWaterShader, "translation" );
GLint reflectionMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionMatrix" );
GLint refractionMatrix = ogl_program_get_uniform_location( m->fancyWaterShader, "refractionMatrix" );
GLint normalMap = ogl_program_get_uniform_location( m->fancyWaterShader, "normalMap" );
GLint reflectionMap = ogl_program_get_uniform_location( m->fancyWaterShader, "reflectionMap" );
GLint refractionMap = ogl_program_get_uniform_location( m->fancyWaterShader, "refractionMap" );
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
pglUniform3fvARB( ambient, 1, &lightEnv.m_TerrainAmbientColor.X );
pglUniform3fvARB( sunDir, 1, &lightEnv.GetSunDir().X );
pglUniform3fvARB( sunColor, 1, &lightEnv.m_SunColor.X );
pglUniform1fARB( shininess, WaterMgr->m_Shininess );
pglUniform1fARB( specularStrength, WaterMgr->m_SpecularStrength );
pglUniform1fARB( waviness, WaterMgr->m_Waviness );
pglUniform1fARB( murkiness, WaterMgr->m_Murkiness );
pglUniform1fARB( fullDepth, WaterMgr->m_WaterFullDepth );
pglUniform3fvARB( tint, 1, WaterMgr->m_WaterTint.FloatArray() );
pglUniform1fARB( reflectionTintStrength, WaterMgr->m_ReflectionTintStrength );
pglUniform3fvARB( reflectionTint, 1, WaterMgr->m_ReflectionTint.FloatArray() );
pglUniform4fARB( translation, tx, ty, 0, 0 );
pglUniformMatrix4fvARB( reflectionMatrix, 1, false, &WaterMgr->m_ReflectionMatrix._11 );
pglUniformMatrix4fvARB( refractionMatrix, 1, false, &WaterMgr->m_RefractionMatrix._11 );
pglUniform1iARB( normalMap, 0 ); // texture unit 0
pglUniform1iARB( reflectionMap, 1 ); // texture unit 1
pglUniform1iARB( refractionMap, 2 ); // texture unit 2
pglUniform3fvARB( cameraPos, 1, &camPos.X );
vertexDepth = ogl_program_get_attrib_location( m->fancyWaterShader, "vertexDepth" );
losMultiplier = ogl_program_get_attrib_location( m->fancyWaterShader, "losMultiplier" );
}
float repeatPeriod = (fancy ? WaterMgr->m_RepeatPeriod : 16.0f);
glBegin(GL_QUADS);
for(size_t i=0; ivisiblePatches.size(); i++)
{
CPatch* patch = m->visiblePatches[i];
for(ssize_t dx=0; dxm_X*PATCH_SIZE + dx);
ssize_t z = (patch->m_Z*PATCH_SIZE + dz);
// Some offsets used to go around counterclockwise while keeping code concise
const int DX[] = {1,1,0,0};
const int DZ[] = {0,1,1,0};
// 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)
{
pglVertexAttrib1fARB(vertexDepth, WaterMgr->m_WaterHeight - terrainHeight);
pglVertexAttrib1fARB(losMultiplier, losMod);
pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/repeatPeriod, vertZ/repeatPeriod);
glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ);
}
else
{
float alpha = clamp( (WaterMgr->m_WaterHeight - terrainHeight) / WaterMgr->m_WaterFullDepth + WaterMgr->m_WaterAlphaOffset,
WaterMgr->m_WaterAlphaOffset, WaterMgr->m_WaterMaxAlpha);
// (Crappy) fresnel effect
CVector3D CamFaceVertex=CVector3D(vertX,WaterMgr->m_WaterHeight,vertZ)-camPos;
CamFaceVertex.Normalize();
float FresnelScalar = CamFaceVertex.Dot(CVector3D(0.0f, -1.0f, 0.0f));
// Invert and set boundaries
FresnelScalar = 1.f - (FresnelScalar * 0.6);
glColor4f(WaterMgr->m_WaterColor.r*losMod,
WaterMgr->m_WaterColor.g*losMod,
WaterMgr->m_WaterColor.b*losMod,
alpha * FresnelScalar);
pglMultiTexCoord2fARB(GL_TEXTURE0, vertX/repeatPeriod, vertZ/repeatPeriod);
glVertex3f(vertX, WaterMgr->m_WaterHeight, vertZ);
}
}
} //end of x loop
} //end of z loop
}
glEnd();
if(fancy)
{
// Unbind the refraction/reflection textures and the shader
pglActiveTextureARB( GL_TEXTURE1_ARB );
glBindTexture( GL_TEXTURE_2D, 0 );
glDisable( GL_TEXTURE_2D );
pglActiveTextureARB( GL_TEXTURE2_ARB );
glBindTexture( GL_TEXTURE_2D, 0 );
glDisable( GL_TEXTURE_2D );
pglActiveTextureARB( GL_TEXTURE0_ARB );
ogl_program_use( 0 );
}
if(!fancy)
{
// Clean up the texture matrix and blend mode
glLoadIdentity();
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
glMatrixMode(GL_MODELVIEW);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
-
-
Index: ps/trunk/source/maths/Fixed.h
===================================================================
--- ps/trunk/source/maths/Fixed.h (revision 8159)
+++ ps/trunk/source/maths/Fixed.h (revision 8160)
@@ -1,345 +1,358 @@
/* 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 .
*/
#ifndef INCLUDED_FIXED
#define INCLUDED_FIXED
#include "lib/types.h"
#include "maths/Sqrt.h"
class CStr8;
class CStrW;
#ifndef NDEBUG
#define USE_FIXED_OVERFLOW_CHECKS
#endif // NDEBUG
//define overflow macros
#ifndef USE_FIXED_OVERFLOW_CHECKS
#define CheckSignedSubtractionOverflow(type, left, right, overflowWarning, underflowWarning)
#define CheckSignedAdditionOverflow(type, left, right, overflowWarning, underflowWarning)
#define CheckCastOverflow(var, targetType, overflowWarning, underflowWarning)
#define CheckU32CastOverflow(var, targetType, overflowWarning)
#define CheckUnsignedAdditionOverflow(result, operand, overflowWarning)
#define CheckUnsignedSubtractionOverflow(result, operand, overflowWarning)
#define CheckNegationOverflow(var, type, overflowWarning)
#define CheckMultiplicationOverflow(type, left, right, overflowWarning, underflowWarning)
#define CheckDivisionOverflow(type, left, right, overflowWarning)
#else // USE_FIXED_OVERFLOW_CHECKS
#define CheckSignedSubtractionOverflow(type, left, right, overflowWarning, underflowWarning) \
if(left > 0 && right < 0 && left > std::numeric_limits::max() + right) \
debug_warn(overflowWarning); \
else if(left < 0 && right > 0 && left < std::numeric_limits::min() + right) \
debug_warn(underflowWarning);
#define CheckSignedAdditionOverflow(type, left, right, overflowWarning, underflowWarning) \
if(left > 0 && right > 0 && std::numeric_limits::max() - left < right) \
debug_warn(overflowWarning); \
else if(left < 0 && right < 0 && std::numeric_limits::min() - left > right) \
debug_warn(underflowWarning);
#define CheckCastOverflow(var, targetType, overflowWarning, underflowWarning) \
if(var > std::numeric_limits::max()) \
debug_warn(overflowWarning); \
else if(var < std::numeric_limits::min()) \
debug_warn(underflowWarning);
#define CheckU32CastOverflow(var, targetType, overflowWarning) \
if(var > (u32)std::numeric_limits::max()) \
debug_warn(overflowWarning);
#define CheckUnsignedAdditionOverflow(result, operand, overflowWarning) \
if(result < operand) \
debug_warn(overflowWarning);
#define CheckUnsignedSubtractionOverflow(result, left, overflowWarning) \
if(result > left) \
debug_warn(overflowWarning);
#define CheckNegationOverflow(var, type, overflowWarning) \
if(value == std::numeric_limits::min()) \
debug_warn(overflowWarning);
#define CheckMultiplicationOverflow(type, left, right, overflowWarning, underflowWarning) \
i64 res##left = (i64)left * (i64)right; \
CheckCastOverflow(res##left, type, overflowWarning, underflowWarning)
#define CheckDivisionOverflow(type, left, right, overflowWarning) \
if(right == -1) { CheckNegationOverflow(left, type, overflowWarning) }
#endif // USE_FIXED_OVERFLOW_CHECKS
template
inline T round_away_from_zero(float value)
{
return (T)(value >= 0 ? value + 0.5f : value - 0.5f);
}
template
inline T round_away_from_zero(double value)
{
return (T)(value >= 0 ? value + 0.5 : value - 0.5);
}
/**
* A simple fixed-point number class.
*
* Use 'fixed' rather than using this class directly.
*/
template
class CFixed
{
private:
T value;
explicit CFixed(T v) : value(v) { }
public:
enum { fract_bits = fract_bits_ };
CFixed() : value(0) { }
static CFixed Zero() { return CFixed(0); }
static CFixed Pi();
T GetInternalValue() const { return value; }
void SetInternalValue(T n) { value = n; }
// Conversion to/from primitive types:
static CFixed FromInt(int n)
{
return CFixed(n << fract_bits);
}
static CFixed FromFloat(float n)
{
if (!isfinite(n))
return CFixed(0);
float scaled = n * fract_pow2;
return CFixed(round_away_from_zero(scaled));
}
static CFixed FromDouble(double n)
{
if (!isfinite(n))
return CFixed(0);
double scaled = n * fract_pow2;
return CFixed(round_away_from_zero(scaled));
}
static CFixed FromString(const CStr8& s);
static CFixed FromString(const CStrW& s);
float ToFloat() const
{
return value / (float)fract_pow2;
}
double ToDouble() const
{
return value / (double)fract_pow2;
}
int ToInt_RoundToZero() const
{
if (value > 0)
return value >> fract_bits;
else
return (value + fract_pow2 - 1) >> fract_bits;
}
int ToInt_RoundToInfinity() const
{
return (value + fract_pow2 - 1) >> fract_bits;
}
int ToInt_RoundToNegInfinity() const
{
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;
/// Returns true if the number is precisely 0.
bool IsZero() const { return value == 0; }
/// Equality.
bool operator==(CFixed n) const { return (value == n.value); }
/// Inequality.
bool operator!=(CFixed n) const { return (value != n.value); }
/// Numeric comparison.
bool operator<=(CFixed n) const { return (value <= n.value); }
/// Numeric comparison.
bool operator<(CFixed n) const { return (value < n.value); }
/// Numeric comparison.
bool operator>=(CFixed n) const { return (value >= n.value); }
/// Numeric comparison.
bool operator>(CFixed n) const { return (value > n.value); }
// Basic arithmetic:
/// Add a CFixed. Might overflow.
CFixed operator+(CFixed n) const
{
CheckSignedAdditionOverflow(T, value, n.value, L"Overflow in CFixed::operator+(CFixed n)", L"Underflow in CFixed::operator+(CFixed n)")
return CFixed(value + n.value);
}
/// Subtract a CFixed. Might overflow.
CFixed operator-(CFixed n) const
{
CheckSignedSubtractionOverflow(T, value, n.value, L"Overflow in CFixed::operator-(CFixed n)", L"Underflow in CFixed::operator-(CFixed n)")
return CFixed(value - n.value);
}
/// Add a CFixed. Might overflow.
CFixed& operator+=(CFixed n) { *this = *this + n; return *this; }
/// Subtract a CFixed. Might overflow.
CFixed& operator-=(CFixed n) { *this = *this - n; return *this; }
/// Negate a CFixed.
CFixed operator-() const
{
CheckNegationOverflow(value, T, L"Overflow in CFixed::operator-()")
return CFixed(-value);
}
/// Divide by a CFixed. Must not have n.IsZero(). Might overflow.
CFixed operator/(CFixed n) const
{
i64 t = (i64)value << fract_bits;
i64 result = t / (i64)n.value;
CheckCastOverflow(result, T, L"Overflow in CFixed::operator/(CFixed n)", L"Underflow in CFixed::operator/(CFixed n)")
return CFixed((T)result);
}
/// Multiply by an integer. Might overflow.
CFixed operator*(int n) const
{
CheckMultiplicationOverflow(T, value, n, L"Overflow in CFixed::operator*(int n)", L"Underflow in CFixed::operator*(int n)")
return CFixed(value * n);
}
/// Divide by an integer. Must not have n == 0. Cannot overflow unless n == -1.
CFixed operator/(int n) const
{
CheckDivisionOverflow(T, value, n, L"Overflow in CFixed::operator/(int n)")
return CFixed(value / n);
}
/// Mod by a fixed. Must not have n == 0. Result has the same sign as n.
CFixed operator%(CFixed n) const
{
T t = value % n.value;
if (n.value > 0 && t < 0)
t += n.value;
else if (n.value < 0 && t > 0)
t += n.value;
return CFixed(t);
}
CFixed Absolute() const { return CFixed(abs(value)); }
/**
* Multiply by a CFixed. Likely to overflow if both numbers are large,
* so we use an ugly name instead of operator* to make it obvious.
*/
CFixed Multiply(CFixed n) const
{
i64 t = (i64)value * (i64)n.value;
t >>= fract_bits;
CheckCastOverflow(t, T, L"Overflow in CFixed::Multiply(CFixed n)", L"Underflow in CFixed::Multiply(CFixed n)")
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.
*/
CFixed MulDiv(CFixed m, CFixed d) const
{
i64 t = ((i64)value * (i64)m.value) / (i64)d.value;
CheckCastOverflow(t, T, L"Overflow in CFixed::Multiply(CFixed n)", L"Underflow in CFixed::Multiply(CFixed n)")
return CFixed((T)t);
}
CFixed Sqrt() const
{
if (value <= 0)
return CFixed(0);
u32 s = isqrt64((u64)value << fract_bits);
return CFixed(s);
}
private:
// Prevent dangerous accidental implicit conversions of floats to ints in certain operations
CFixed operator*(float n) const;
CFixed operator/(float n) const;
};
/**
* A fixed-point number class with 1-bit sign, 15-bit integral part, 16-bit fractional part.
*/
typedef CFixed CFixed_15_16;
/**
* Default fixed-point type used by the engine.
*/
typedef CFixed_15_16 fixed;
namespace std
{
/**
* std::numeric_limits specialisation, currently just providing min and max
*/
template
struct numeric_limits >
{
typedef CFixed fixed;
public:
static const bool is_specialized = true;
static fixed min() throw() { fixed f; f.SetInternalValue(std::numeric_limits::min()); return f; }
static fixed max() throw() { fixed f; f.SetInternalValue(std::numeric_limits::max()); return f; }
};
}
/**
* Inaccurate approximation of atan2 over fixed-point numbers.
* Maximum error is almost 0.08 radians (4.5 degrees).
*/
CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x);
/**
* Compute sin(a) and cos(a).
* Maximum error for -2pi < a < 2pi is almost 0.0005.
*/
void sincos_approx(CFixed_15_16 a, CFixed_15_16& sin_out, CFixed_15_16& cos_out);
#endif // INCLUDED_FIXED
Index: ps/trunk/binaries/data/mods/public/maps/scenarios/Pathfinding_terrain_demo.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scenarios/Pathfinding_terrain_demo.xml (revision 8159)
+++ ps/trunk/binaries/data/mods/public/maps/scenarios/Pathfinding_terrain_demo.xml (revision 8160)
@@ -1,261 +1,262 @@
defaultdefault19.304915080.450
units/hele_ship_bireme
1
units/hele_ship_merchant
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_mechanical_siege_lithobolos
1
units/hele_cavalry_swordsman_b
1
units/hele_cavalry_swordsman_b
1
units/hele_cavalry_swordsman_b
1
structures/hele_barracks
1
structures/hele_dock
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
units/hele_infantry_spearman_b
1
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/public/maps/scenarios/Combat_demo_(huge).xml
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scenarios/Combat_demo_(huge).xml (revision 8159)
+++ ps/trunk/binaries/data/mods/public/maps/scenarios/Combat_demo_(huge).xml (revision 8160)
@@ -1,87 +1,88 @@
defaultdefault515080.450
Index: ps/trunk/binaries/data/mods/public/maps/scenarios/Combat_demo.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scenarios/Combat_demo.xml (revision 8159)
+++ ps/trunk/binaries/data/mods/public/maps/scenarios/Combat_demo.xml (revision 8160)
@@ -1,375 +1,376 @@
defaultdefault515080.450
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_archer_b
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_spearman_e
1
units/hele_infantry_archer_e
2
units/hele_infantry_archer_e
2
units/hele_infantry_archer_e
2
units/hele_infantry_archer_e
2
units/hele_infantry_archer_e
2
units/hele_infantry_archer_e
2
units/hele_infantry_archer_e
2
units/hele_infantry_archer_e
2
units/hele_infantry_javelinist_e
2
units/hele_infantry_javelinist_e
2
units/hele_infantry_javelinist_e
2
units/hele_infantry_javelinist_e
2
units/hele_infantry_javelinist_e
2
units/hele_infantry_javelinist_e
2
units/hele_infantry_javelinist_e
2
units/hele_infantry_javelinist_e
2
units/hele_infantry_spearman_b
2
units/hele_infantry_spearman_b
2
units/hele_infantry_spearman_b
2
units/hele_infantry_spearman_b
2
units/hele_infantry_spearman_b
2
units/hele_infantry_spearman_b
2
units/hele_infantry_spearman_b
2
units/hele_infantry_spearman_b
2
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js (revision 8159)
+++ ps/trunk/binaries/data/mods/public/simulation/helpers/Setup.js (revision 8160)
@@ -1,17 +1,24 @@
function LoadMapSettings(settings)
{
// Default settings for old maps
if (!settings)
settings = {};
if (settings.DefaultStance)
{
for each (var ent in Engine.GetEntitiesWithInterface(IID_UnitAI))
{
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
cmpUnitAI.SetStance(settings.DefaultStance);
}
}
+
+ if (settings.RevealMap)
+ {
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ if (cmpRangeManager)
+ cmpRangeManager.SetLosRevealAll(true);
+ }
}
Engine.RegisterGlobal("LoadMapSettings", LoadMapSettings);