Index: ps/trunk/source/graphics/tests/test_Terrain.h
===================================================================
--- ps/trunk/source/graphics/tests/test_Terrain.h (revision 23456)
+++ ps/trunk/source/graphics/tests/test_Terrain.h (revision 23457)
@@ -1,195 +1,341 @@
-/* Copyright (C) 2010 Wildfire Games.
+/* Copyright (C) 2020 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 "graphics/Terrain.h"
#include "graphics/Patch.h"
#include "graphics/RenderableObject.h"
-#include "maths/FixedVector3D.h"
#include "maths/Fixed.h"
+#include "maths/FixedVector3D.h"
+
+#include
+#include
class TestTerrain : public CxxTest::TestSuite
{
void SetVertex(CTerrain& terrain, ssize_t i, ssize_t j, u16 height)
{
terrain.GetHeightMap()[j*terrain.GetVerticesPerSide() + i] = height;
terrain.MakeDirty(RENDERDATA_UPDATE_VERTICES);
}
u16 GetVertex(CTerrain& terrain, ssize_t i, ssize_t j)
{
return terrain.GetHeightMap()[j*terrain.GetVerticesPerSide() + i];
}
void Set45Slope(CTerrain& terrain)
{
SetVertex(terrain, 0, 0, 100);
SetVertex(terrain, 0, 1, 100);
SetVertex(terrain, 0, 2, 100);
SetVertex(terrain, 1, 0, 100 + TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 1, 1, 100 + TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 1, 2, 100 + TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 2, 0, 100 + 2*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 2, 1, 100 + 2*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 2, 2, 100 + 2*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 3, 0, 100 + 2*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 3, 1, 100 + 2*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 3, 2, 100 + 2*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
}
void SetHighPlateau(CTerrain& terrain, int height)
{
SetVertex(terrain, 4, 0, 100 + height*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 4, 1, 100 + height*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 4, 2, 100 + height*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 5, 0, 100 + height*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 5, 1, 100 + height*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
SetVertex(terrain, 5, 2, 100 + height*TERRAIN_TILE_SIZE*HEIGHT_UNITS_PER_METRE);
}
public:
void test_GetExactGroundLevel()
{
CTerrain terrain;
terrain.Initialize(4, NULL);
Set45Slope(terrain);
SetHighPlateau(terrain, 20);
float ground;
ground = terrain.GetExactGroundLevel(0.f, 1.5f*TERRAIN_TILE_SIZE);
TS_ASSERT_DELTA(ground, 100.f/HEIGHT_UNITS_PER_METRE, 0.01f);
ground = terrain.GetExactGroundLevel(0.5f*TERRAIN_TILE_SIZE, 1.5f*TERRAIN_TILE_SIZE);
TS_ASSERT_DELTA(ground, 100.f/HEIGHT_UNITS_PER_METRE+0.5f*TERRAIN_TILE_SIZE, 0.01f);
ground = terrain.GetExactGroundLevel(1.5f*TERRAIN_TILE_SIZE, 1.5f*TERRAIN_TILE_SIZE);
TS_ASSERT_DELTA(ground, 100.f/HEIGHT_UNITS_PER_METRE+1.5f*TERRAIN_TILE_SIZE, 0.01f);
ground = terrain.GetExactGroundLevel(2.5f*TERRAIN_TILE_SIZE, 1.5f*TERRAIN_TILE_SIZE);
TS_ASSERT_DELTA(ground, 100.f/HEIGHT_UNITS_PER_METRE+2.f*TERRAIN_TILE_SIZE, 0.01f);
ground = terrain.GetExactGroundLevel(3.5f*TERRAIN_TILE_SIZE, 1.5f*TERRAIN_TILE_SIZE);
TS_ASSERT_DELTA(ground, 100.f/HEIGHT_UNITS_PER_METRE+11.f*TERRAIN_TILE_SIZE, 0.01f);
ground = terrain.GetExactGroundLevel(4.5f*TERRAIN_TILE_SIZE, 1.5f*TERRAIN_TILE_SIZE);
TS_ASSERT_DELTA(ground, 100.f/HEIGHT_UNITS_PER_METRE+20.f*TERRAIN_TILE_SIZE, 0.01f);
}
void test_GetExactGroundLevelFixed()
{
CTerrain terrain;
terrain.Initialize(4, NULL);
Set45Slope(terrain);
SetHighPlateau(terrain, 20);
const double maxDelta = 0.0001;
fixed ground;
ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(0.f), fixed::FromFloat(1.5f*TERRAIN_TILE_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(0.5f*TERRAIN_TILE_SIZE), fixed::FromFloat(1.5f*TERRAIN_TILE_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+0.5*TERRAIN_TILE_SIZE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(1.5f*TERRAIN_TILE_SIZE), fixed::FromFloat(1.5f*TERRAIN_TILE_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+1.5*TERRAIN_TILE_SIZE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(2.5f*TERRAIN_TILE_SIZE), fixed::FromFloat(1.5f*TERRAIN_TILE_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+2.0*TERRAIN_TILE_SIZE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(3.5f*TERRAIN_TILE_SIZE), fixed::FromFloat(1.5f*TERRAIN_TILE_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+11.0*TERRAIN_TILE_SIZE, maxDelta);
ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(4.5f*TERRAIN_TILE_SIZE), fixed::FromFloat(1.5f*TERRAIN_TILE_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 100.0/HEIGHT_UNITS_PER_METRE+20.0*TERRAIN_TILE_SIZE, maxDelta);
}
void test_GetExactGroundLevelFixed_max()
{
CTerrain terrain;
terrain.Initialize(4, NULL);
SetVertex(terrain, 0, 0, 65535);
SetVertex(terrain, 0, 1, 65535);
SetVertex(terrain, 1, 0, 65535);
SetVertex(terrain, 1, 1, 65535);
const double maxDelta = 0.024;
int p = 255;
for (int zi = 0; zi < p; ++zi)
{
for (int xi = 0; xi < p; ++xi)
{
fixed ground = terrain.GetExactGroundLevelFixed(fixed::FromFloat(xi/(float)p*TERRAIN_TILE_SIZE), fixed::FromFloat(zi/(float)p*TERRAIN_TILE_SIZE));
TS_ASSERT_DELTA(ground.ToDouble(), 65535.0/HEIGHT_UNITS_PER_METRE, maxDelta);
}
}
}
void test_CalcNormal()
{
CTerrain terrain;
terrain.Initialize(4, NULL);
Set45Slope(terrain);
CVector3D vec;
terrain.CalcNormal(1, 1, vec);
TS_ASSERT_DELTA(vec.X, -1.f/sqrt(2.f), 0.01f);
TS_ASSERT_DELTA(vec.Y, 1.f/sqrt(2.f), 0.01f);
TS_ASSERT_EQUALS(vec.Z, 0.f);
terrain.CalcNormal(2, 1, vec);
TS_ASSERT_DELTA(vec.X, (-1.f/sqrt(2.f)) / sqrt(2.f+sqrt(2.f)), 0.01f);
TS_ASSERT_DELTA(vec.Y, (1.f+1.f/sqrt(2.f)) / sqrt(2.f+sqrt(2.f)), 0.01f);
TS_ASSERT_EQUALS(vec.Z, 0);
terrain.CalcNormal(5, 1, vec);
TS_ASSERT_EQUALS(vec.X, 0.f);
TS_ASSERT_EQUALS(vec.Y, 1.f);
TS_ASSERT_EQUALS(vec.Z, 0.f);
}
void test_CalcNormalFixed()
{
CTerrain terrain;
terrain.Initialize(4, NULL);
Set45Slope(terrain);
CFixedVector3D vec;
terrain.CalcNormalFixed(1, 1, vec);
TS_ASSERT_DELTA(vec.X.ToFloat(), -1.f/sqrt(2.f), 0.01f);
TS_ASSERT_DELTA(vec.Y.ToFloat(), 1.f/sqrt(2.f), 0.01f);
TS_ASSERT_EQUALS(vec.Z.ToFloat(), 0.f);
terrain.CalcNormalFixed(2, 1, vec);
TS_ASSERT_DELTA(vec.X.ToFloat(), (-1.f/sqrt(2.f)) / sqrt(2.f+sqrt(2.f)), 0.01f);
TS_ASSERT_DELTA(vec.Y.ToFloat(), (1.f+1.f/sqrt(2.f)) / sqrt(2.f+sqrt(2.f)), 0.01f);
TS_ASSERT_EQUALS(vec.Z.ToFloat(), 0);
terrain.CalcNormalFixed(5, 1, vec);
TS_ASSERT_EQUALS(vec.X.ToFloat(), 0.f);
TS_ASSERT_EQUALS(vec.Y.ToFloat(), 1.f);
TS_ASSERT_EQUALS(vec.Z.ToFloat(), 0.f);
}
+
+ void test_Resize()
+ {
+ // We do resize by size in patches, so it doesn't make sense to
+ // fill each vertex with a different value. Instead we use a single
+ // value per a patch.
+ struct ResizeTestCase
+ {
+ std::vector> sourcePatches;
+ std::vector> expectedPatches;
+ };
+ const ResizeTestCase testCases[] = {
+ {
+ {
+ {42}
+ },
+ {
+ {42}
+ }
+ },
+ {
+ {
+ {1, 2},
+ {3, 4}
+ },
+ {
+ {1},
+ }
+ },
+ {
+ {
+ {1, 2},
+ {3, 4}
+ },
+ {
+ {1, 2, 0},
+ {3, 4, 0},
+ {0, 0, 0}
+ }
+ }
+ };
+
+ for (const ResizeTestCase& testCase : testCases)
+ {
+ const ssize_t sourceSize = testCase.sourcePatches.size();
+ const ssize_t expectedSize = testCase.expectedPatches.size();
+
+ TS_ASSERT_LESS_THAN(0, sourceSize);
+ TS_ASSERT_LESS_THAN(0, expectedSize);
+
+ const ssize_t sourceMapSize = sourceSize * PATCH_SIZE + 1;
+ const ssize_t expectedMapSize = expectedSize * PATCH_SIZE + 1;
+
+ CTerrain terrain;
+ {
+ std::vector heightmap(sourceMapSize * sourceMapSize);
+ for (ssize_t iTile = 0; iTile < sourceSize; ++iTile)
+ {
+ TS_ASSERT_EQUALS(sourceSize, testCase.sourcePatches[iTile].size());
+ for (ssize_t jTile = 0; jTile < sourceSize; ++jTile)
+ {
+ for (ssize_t i = 0; i < PATCH_SIZE; ++i)
+ for (ssize_t j = 0; j < PATCH_SIZE; ++j)
+ {
+ const ssize_t idx =
+ (jTile * PATCH_SIZE + j) * sourceMapSize +
+ iTile * PATCH_SIZE + i;
+ heightmap[idx] = testCase.sourcePatches[iTile][jTile];
+ }
+ }
+ }
+ terrain.Initialize(sourceSize, heightmap.data());
+ }
+
+ terrain.Resize(expectedSize);
+
+ TS_ASSERT_EQUALS(expectedMapSize, terrain.GetVerticesPerSide());
+ TS_ASSERT_EQUALS(expectedSize, terrain.GetPatchesPerSide());
+
+ for (ssize_t iTile = 0; iTile < expectedSize; ++iTile)
+ {
+ TS_ASSERT_EQUALS(
+ expectedSize, testCase.expectedPatches[iTile].size());
+ for (ssize_t jTile = 0; jTile < expectedSize; ++jTile)
+ {
+ for (ssize_t i = 0; i < PATCH_SIZE; ++i)
+ for (ssize_t j = 0; j < PATCH_SIZE; ++j)
+ {
+ // The whole patch should have the same height,
+ // since we resize by patches.
+ if (GetVertex(terrain, iTile * PATCH_SIZE,
+ jTile * PATCH_SIZE) ==
+ GetVertex(terrain, iTile * PATCH_SIZE + i,
+ jTile * PATCH_SIZE + j))
+ continue;
+ TS_FAIL("The whole patch should have the same height");
+ std::stringstream ss;
+ ss << "iTile=" << iTile << " jTile=" << jTile
+ << " i=" << i << " j=" << j;
+ TS_WARN(ss.str());
+ ss.str(std::string());
+ ss << "found=" << GetVertex(terrain, iTile * PATCH_SIZE + i,
+ jTile * PATCH_SIZE + j)
+ << " expected=" << GetVertex(terrain, iTile * PATCH_SIZE,
+ jTile * PATCH_SIZE);
+ TS_WARN(ss.str());
+ return;
+ }
+
+ if (testCase.expectedPatches[iTile][jTile] ==
+ GetVertex(terrain, iTile * PATCH_SIZE,
+ jTile * PATCH_SIZE))
+ continue;
+ std::stringstream ss;
+ ss << "The patch has wrong height"
+ << " (i=" << iTile << " j=" << jTile << "):"
+ << " found=" << GetVertex(terrain, iTile * PATCH_SIZE,
+ jTile * PATCH_SIZE)
+ << " expected=" << testCase.expectedPatches[iTile][jTile];
+ TS_FAIL(ss.str());
+ ss.str(std::string());
+ ss << "Terrain (" << terrain.GetPatchesPerSide()
+ << "x" << terrain.GetPatchesPerSide() << "):";
+ TS_WARN(ss.str());
+ for (ssize_t iTile = 0; iTile < expectedSize; ++iTile)
+ {
+ ss.str(std::string());
+ ss << "[";
+ for (ssize_t jTile = 0; jTile < expectedSize; ++jTile)
+ {
+ if (jTile)
+ ss << ", ";
+ ss << GetVertex(terrain, iTile * PATCH_SIZE,
+ jTile * PATCH_SIZE);
+ }
+ ss << "]";
+ TS_WARN(ss.str());
+ }
+ return;
+ }
+ }
+ }
+ }
};