Files
EVEngine/test/grass_billboard.cpp

146 lines
6.6 KiB
C++

#include <limits>
#include "graphics/Grass.h"
#include "graphics/VegetationWind.h"
#include "zeroerr/unittest.h"
TEST_CASE("graphics.VegetationWind.packetControlsAndExplicitTime") {
eve::graphics::VegetationWindState state{{1, -0.5F, 0.2F}, 0.8F, 0.17F};
eve::graphics::VegetationWindProfile profile;
auto disabled = eve::graphics::packVegetationWind(state, profile, 2.0);
REQUIRE(disabled.ok());
CHECK(disabled.value()[5] == 0);
profile.enabled = true;
profile.billboard = true;
profile.alphaTest = false;
auto packet = eve::graphics::packVegetationWind(state, profile, 2.0);
REQUIRE(packet.ok());
const auto& data = packet.value();
CHECK(data[0] == state.direction.x);
CHECK(data[1] == state.direction.y);
CHECK(data[2] == state.direction.z);
CHECK(data[3] == state.strength);
CHECK(data[4] == state.phase);
CHECK(data[5] == -100);
CHECK(data[6] == profile.flex.x);
CHECK(data[7] == profile.flex.y);
CHECK(data[8] == 0);
CHECK(data[9] == profile.frequency.x);
CHECK(data[10] == profile.frequency.y);
CHECK(data[11] == profile.frequency.z);
CHECK(std::abs(data[12] - 0.479425539F) < 0.000001F);
CHECK(std::abs(data[13] - 0.909297427F) < 0.000001F);
profile.billboard = false;
profile.alphaTest = true;
auto complete = eve::graphics::packVegetationWind(state, profile, 2.0);
REQUIRE(complete.ok());
CHECK(complete.value()[5] == 100);
CHECK(complete.value()[8] == profile.flex.z);
}
TEST_CASE("graphics.VegetationWind.packetRejectsInvalidDisabledInputs") {
eve::graphics::VegetationWindState state;
eve::graphics::VegetationWindProfile profile;
CHECK(!eve::graphics::packVegetationWind(state, profile, std::numeric_limits<double>::infinity()).ok());
profile.maximumDistance = 0;
CHECK(!eve::graphics::packVegetationWind(state, profile, 0).ok());
profile.maximumDistance = 100;
profile.frequency.x = std::numeric_limits<float>::quiet_NaN();
CHECK(!eve::graphics::packVegetationWind(state, profile, 0).ok());
profile.frequency.x = 0.25F;
state.strength = 2;
CHECK(!eve::graphics::packVegetationWind(state, profile, 0).ok());
CHECK(!profile.enabled);
CHECK(state.strength == 2);
}
TEST_CASE("graphics.Grass.independentBillboardDimensions") {
std::vector<eve::graphics::grass::Point> points(1);
points[0].position = {3, 4, 5};
points[0].id = 11;
points[0].scale = 2;
points[0].widthScale = 0.25F;
const auto dense = eve::graphics::grass::buildBillboards(points, 1, 1, false);
const auto dark = eve::graphics::grass::buildBillboards(points, 1, 1, true);
for (int i = 0; i < 4; ++i) {
CHECK(dense.posXYZ[size_t(i) * 3] == 3);
CHECK(dense.posXYZ[size_t(i) * 3 + 1] == 4);
CHECK(dense.nrmXYZ[size_t(i) * 3] == 11);
CHECK(dense.nrmXYZ[size_t(i) * 3 + 1] == 2);
CHECK(dense.nrmXYZ[size_t(i) * 3 + 2] == -0.25F);
CHECK(dark.nrmXYZ[size_t(i) * 3 + 2] == 1.25F);
}
points[0].widthScale = 0;
const auto legacy = eve::graphics::grass::buildBillboards(points, 1, 1, false);
const auto legacyDark = eve::graphics::grass::buildBillboards(points, 1, 1, true);
CHECK(legacy.nrmXYZ[2] == 0);
CHECK(legacyDark.nrmXYZ[2] == 1);
CHECK(legacy.uvST == dense.uvST);
CHECK(legacy.indices == dense.indices);
points[0].tint = {0.2F, 0.4F, 0.8F};
const auto tinted = eve::graphics::grass::buildBillboards(points, 1, 1, false);
const uint32_t packedRgb = (51u << 16) | (102u << 8) | 204u;
for (int i = 0; i < 4; ++i) CHECK(tinted.nrmXYZ[size_t(i) * 3] == -float(packedRgb + 1u));
}
TEST_CASE("graphics.VegetationWind.phaseAndSmoothing") {
eve::graphics::VegetationWindState state;
REQUIRE(eve::graphics::advanceVegetationWind(state, {1, 0, 0}, 1, 4).ok());
CHECK(state.direction.x == 1);
CHECK(state.direction.y == -0.5F);
CHECK(state.strength == 1);
CHECK(std::abs(state.phase - 0.4F) < 0.000001F);
state.phase = 99.9F;
state.updatePhase = 99.9F;
REQUIRE(eve::graphics::advanceVegetationWind(state, {1, 0, 0}, 1, 4).ok());
CHECK(std::abs(state.phase - 0.3F) < 0.00001F);
const auto saved = state;
CHECK(!eve::graphics::advanceVegetationWind(state, {1, 0, 0}, 1, -1).ok());
CHECK(state.phase == saved.phase);
}
TEST_CASE("graphics.VegetationWind.rootDistanceAndBillboardGolden") {
eve::graphics::VegetationWindInput input;
eve::graphics::VegetationWindState wind;
wind.direction = {1, 0, 0};
wind.strength = 1;
auto root = eve::graphics::evaluateVegetationWind(input, wind);
REQUIRE(root.ok());
CHECK(root.value() == glm::vec3(0));
input.position = {0, 1, 0};
input.billboard = true;
input.flex = {1, 0, 0};
// phase=0, sine time=0: gust=(0.5+1)*0.7=1.05; source volume normalization is 1/(1+1.05^2).
auto bent = eve::graphics::evaluateVegetationWind(input, wind);
REQUIRE(bent.ok());
CHECK(std::abs(bent.value().x - float(1.05 / 2.1025)) < 0.000001F);
CHECK(std::abs(bent.value().y - float(1 / 2.1025)) < 0.000001F);
input.cameraPosition = {100, 0, 0};
auto far = eve::graphics::evaluateVegetationWind(input, wind);
REQUIRE(far.ok());
CHECK(far.value() == input.position);
input.objectToWorld = glm::mat3(0);
CHECK(!eve::graphics::evaluateVegetationWind(input, wind).ok());
}
TEST_CASE("graphics.VegetationWind.branchLeafAndScaledTransformGolden") {
eve::graphics::VegetationWindInput input;
input.position = {0.2F, 0.8F, 0.1F};
input.worldOffset = {3, 4, 5};
input.cameraPosition = input.worldOffset;
input.objectToWorld[0][0] = 2;
input.objectToWorld[1][1] = 3;
input.objectToWorld[2][2] = 0.5F;
input.sinTimeQuarter = 0.3F;
input.sinTimeFull = -0.2F;
eve::graphics::VegetationWindState state;
state.direction = {1, -0.5F, 0.2F};
state.strength = 0.8F;
state.phase = 0.17F;
const glm::vec3 expected[] = {{0.535718825F, 0.604993208F, 0.383200569F},
{0.664822256F, 0.513757707F, 0.489274523F},
{0.664999698F, 0.513719712F, 0.491201362F}};
for (int i = 0; i < 3; ++i) {
input.billboard = i == 0;
input.alphaTest = i != 1;
auto result = eve::graphics::evaluateVegetationWind(input, state);
REQUIRE(result.ok());
for (int axis = 0; axis < 3; ++axis) CHECK(std::abs(result.value()[axis] - expected[i][axis]) < 0.000002F);
}
}