Files
EVEngine/test/water_underwater_effects.cpp

303 lines
13 KiB
C++

#include "zeroerr/unittest.h"
#include "graphics/WaterUnderwaterEffects.h"
#include "graphics/Canvas.h"
#include "graphics/Graphics.h"
#include "graphics/RenderSystem3D.h"
#include "graphics/Volumetric.h"
#include "physics/Body3D.h"
#include "physics/Physics.h"
#include "physics/Shape3D.h"
#include "physics/World3D.h"
#include "window/Window.h"
#include "Fixtures.h"
#include <cmath>
#include <limits>
#include <memory>
using namespace eve::graphics;
namespace {
UnderwaterColorGradient depthGradient() {
UnderwaterColorGradient gradient;
REQUIRE(static_cast<bool>(gradient.addStop(0.0F, 0.4F, 0.7F, 1.0F)));
REQUIRE(static_cast<bool>(gradient.addStop(1.0F, 0.03F, 0.08F, 0.18F)));
return gradient;
}
UnderwaterColorGradient timeGradient() {
UnderwaterColorGradient gradient;
REQUIRE(static_cast<bool>(gradient.addStop(0.0F, 0.1F, 0.2F, 0.4F)));
REQUIRE(static_cast<bool>(gradient.addStop(1.0F, 0.8F, 0.5F, 0.2F)));
return gradient;
}
UnderwaterScalarCurve exposureCurve() {
UnderwaterScalarCurve curve;
REQUIRE(static_cast<bool>(curve.addKey(0.0F, -0.5F)));
REQUIRE(static_cast<bool>(curve.addKey(1.0F, 0.5F)));
return curve;
}
} // namespace
TEST_CASE("graphics.WaterUnderwaterDisableTrigger.consumesRealBox3DEvents") {
constexpr int zoneTag = 8101;
constexpr int playerTag = 8102;
auto* physics = eve::physics::Physics::create();
std::unique_ptr<eve::physics::World3D> world(
physics->newWorld3D(0.0F, 0.0F, 0.0F, false));
auto* zone = world->newBody("static", 0.0F, 0.0F, 0.0F);
auto* sensor = zone->newBoxShape(4.0F, 4.0F, 4.0F);
sensor->setTag(zoneTag);
sensor->setSensor(true);
auto* player = world->newBody("dynamic", 0.0F, 0.0F, 0.0F);
player->newSphereShape(0.5F)->setTag(playerTag);
WaterUnderwaterDisableTriggerState state;
world->update(1.0F / 60.0F);
REQUIRE_EQ(world->getBeginTriggerCount(), 1);
WaterUnderwaterTriggerEvent enter{world->getBeginTriggerSensorShapeTag(0),
world->getBeginTriggerVisitorShapeTag(0), true, false};
REQUIRE(static_cast<bool>(
advanceWaterUnderwaterDisableTrigger(state, enter, zoneTag, playerTag)));
CHECK(!state.effectsEnabled);
const auto before = state;
WaterUnderwaterTriggerEvent malformed{zoneTag, playerTag, true, true};
CHECK(!advanceWaterUnderwaterDisableTrigger(state, malformed, zoneTag, playerTag));
CHECK(state.effectsEnabled == before.effectsEnabled);
WaterUnderwaterTriggerEvent unrelated{zoneTag, 9999, false, true};
REQUIRE(static_cast<bool>(
advanceWaterUnderwaterDisableTrigger(state, unrelated, zoneTag, playerTag)));
CHECK(!state.effectsEnabled);
player->setPosition(10.0F, 0.0F, 0.0F);
world->update(1.0F / 60.0F);
REQUIRE_EQ(world->getEndTriggerCount(), 1);
WaterUnderwaterTriggerEvent exit{world->getEndTriggerSensorShapeTag(0),
world->getEndTriggerVisitorShapeTag(0), false, true};
REQUIRE(static_cast<bool>(
advanceWaterUnderwaterDisableTrigger(state, exit, zoneTag, playerTag)));
CHECK(state.effectsEnabled);
}
TEST_CASE("graphics.WaterUnderwater.transitionsFogCausticsAndSurfaceRestore") {
WaterUnderwaterSettings settings;
settings.causticTextureCount = 16;
settings.fogColorMultiplier = {0.1F, -1.0F, 0.0F};
WaterUnderwaterInput input;
input.seaLevel = 50.0F;
input.cameraY = 60.0F;
input.mainLightColor = {0.7F, 0.8F, 0.9F};
WaterUnderwaterState state;
WaterUnderwaterOutput output;
const auto depth = depthGradient();
const auto time = timeGradient();
const auto exposure = exposureCurve();
const auto postColor = timeGradient();
WaterUnderwaterState transitionState;
WaterUnderwaterOutput transitionOutput;
WaterUnderwaterInput transitionInput = input;
transitionInput.cameraY = 50.04F;
REQUIRE(static_cast<bool>(advanceWaterUnderwaterEffects(
transitionState, transitionOutput, settings, transitionInput, depth, time, exposure,
postColor)));
transitionInput.cameraY = 49.96F;
REQUIRE(static_cast<bool>(advanceWaterUnderwaterEffects(
transitionState, transitionOutput, settings, transitionInput, depth, time, exposure,
postColor)));
CHECK(transitionOutput.entered);
CHECK(std::abs(transitionOutput.transitionWeight - 0.4F) < 0.0002F);
CHECK(std::abs(transitionOutput.transitionVignette - 0.1F) < 0.0002F);
CHECK(std::abs(transitionOutput.transitionVignetteSmoothness - 0.44F) < 0.0002F);
CHECK(std::abs(transitionOutput.transitionLensDistortion - 0.1008F) < 0.0002F);
CHECK(std::abs(transitionOutput.transitionLensScale - 1.008F) < 0.0002F);
CHECK(std::abs(transitionOutput.transitionLift.r - settings.transitionLift.r * 0.4F) < 0.0002F);
CHECK(std::abs(transitionOutput.transitionInverseGamma.r -
(1.0F + (settings.transitionInverseGamma.r - 1.0F) * 0.4F)) < 0.0002F);
CHECK(std::abs(transitionOutput.transitionGain.r -
(1.0F + (settings.transitionGain.r - 1.0F) * 0.4F)) < 0.0002F);
CHECK(std::abs(transitionOutput.transitionColorFilter.r - 0.6F) < 0.0002F);
REQUIRE(static_cast<bool>(advanceWaterUnderwaterEffects(state, output, settings, input, depth, time, exposure, postColor)));
CHECK(!output.isUnderwater);
CHECK(output.surfaceVfx);
input.cameraY = 25.0F;
input.causticTicks = 18;
REQUIRE(static_cast<bool>(advanceWaterUnderwaterEffects(state, output, settings, input, depth, time, exposure, postColor)));
CHECK(output.entered);
CHECK(output.playSubmergeDown);
CHECK(output.loopAudio);
CHECK(output.particles);
CHECK(output.horizon);
CHECK(output.causticFrame == 2);
CHECK(std::abs(output.depth01 - 0.25F) < 0.0001F);
CHECK(std::abs(output.fogColor.r - 0.4075F) < 0.0001F);
CHECK(std::abs(output.fogColor.g - 0.545F) < 0.0001F);
CHECK(output.underwaterMaterialColor == input.mainLightColor);
input.cameraY = 51.0F;
input.causticTicks = 0;
REQUIRE(static_cast<bool>(advanceWaterUnderwaterEffects(state, output, settings, input, depth, time, exposure, postColor)));
CHECK(output.exited);
CHECK(output.playSubmergeUp);
CHECK(!output.loopAudio);
CHECK(output.surfaceVfx);
CHECK(state.causticFrame == 0);
}
TEST_CASE("graphics.WaterUnderwater.hdrpOverrideAndAtomicFailure") {
WaterUnderwaterSettings settings;
settings.hdrp = true;
settings.overrideFogColor = true;
settings.timeDrivenPostFx = true;
settings.overrideFogMultiplier = {0.5F, 0.8F, 1.0F};
settings.overrideFogCurve = 0.75F;
WaterUnderwaterInput input;
input.seaLevel = 100.0F;
input.cameraY = 0.0F;
input.timeOfDay = 0.5F;
input.hasMainLight = false;
WaterUnderwaterState state;
WaterUnderwaterOutput output;
const auto depth = depthGradient();
const auto time = timeGradient();
const auto exposure = exposureCurve();
const auto postColor = timeGradient();
REQUIRE(static_cast<bool>(advanceWaterUnderwaterEffects(state, output, settings, input, depth, time, exposure, postColor)));
CHECK(output.isUnderwater);
CHECK(!output.horizon);
CHECK(output.hdrpBaseHeight == 6000.0F);
CHECK(output.hdrpMeanFreePath == 1.0F);
CHECK(output.hdrpProbeDimmer == 0.325F);
CHECK(output.hdrpDepthExtent == 300.0F);
CHECK(std::abs(output.fogColor.r - 0.028125F) < 0.0001F);
CHECK(std::abs(output.postExposure) < 0.0001F);
CHECK(std::abs(output.postColor.r - 0.45F) < 0.0001F);
CHECK(std::abs(output.underwaterMaterialColor.r - 207.0F / 255.0F) < 0.0001F);
const WaterUnderwaterState beforeState = state;
const WaterUnderwaterOutput beforeOutput = output;
input.cameraY = std::numeric_limits<float>::quiet_NaN();
auto invalid = advanceWaterUnderwaterEffects(state, output, settings, input, depth, time, exposure, postColor);
CHECK(!invalid);
CHECK(state.isUnderwater == beforeState.isUnderwater);
CHECK(output.hdrpBaseHeight == beforeOutput.hdrpBaseHeight);
}
TEST_CASE("graphics.WaterUnderwaterFog.appliesAndRestoresVolumetricProvider") {
eve::window::Window* window = nullptr;
Graphics* graphics = nullptr;
openGfxWindow(window, graphics);
auto* volumetric = graphics->newVolumetric();
REQUIRE(volumetric != nullptr);
WaterUnderwaterOutput output;
output.isUnderwater = true;
output.fogColor = {0.12F, 0.32F, 0.48F};
output.fogDensity = 0.045F;
output.fogStart = -4.0F;
output.fogEnd = 45.0F;
output.fogHeight = 12.0F;
WaterSurfaceFogSnapshot surface;
surface.color = {0.5F, 0.6F, 0.7F};
surface.density = 0.006F;
REQUIRE(static_cast<bool>(applyWaterUnderwaterFog(volumetric, output, surface)));
CHECK(volumetric->getMode() == "fog");
CHECK(volumetric->getFloat("fogR") == 0.12F);
CHECK(volumetric->getFloat("fogG") == 0.32F);
CHECK(volumetric->getFloat("density") == 0.045F);
output.isUnderwater = false;
REQUIRE(static_cast<bool>(applyWaterUnderwaterFog(volumetric, output, surface)));
CHECK(volumetric->getFloat("fogR") == 0.5F);
CHECK(volumetric->getFloat("fogB") == 0.7F);
CHECK(volumetric->getFloat("density") == 0.006F);
auto* camera = Camera3D::createCamera();
output.isUnderwater = true;
output.postFx = true;
output.postExposure = -1.25F;
output.postColor = {0.2F, 0.6F, 0.8F};
output.transitionFx = true;
output.transitionVignette = 0.25F;
output.transitionVignetteSmoothness = 0.8F;
output.transitionLensDistortion = 0.252F;
output.transitionLensScale = 1.02F;
output.transitionLift = {-0.02F, -0.01F, 0.005F};
output.transitionInverseGamma = {1.35F, 1.36F, 1.27F};
output.transitionGain = {0.67F, 0.69F, 0.72F};
output.transitionColorFilter = {0.6F, 0.6F, 0.6F};
WaterSurfacePostFxSnapshot surfacePostFx;
surfacePostFx.exposureEv = 0.4F;
surfacePostFx.color = {0.9F, 0.8F, 0.7F};
REQUIRE(static_cast<bool>(applyWaterUnderwaterPostFx(graphics, camera, output, surfacePostFx)));
CHECK(camera->getExposure() == -1.25F);
CHECK(graphics->getSceneColorFilter() == glm::vec3(0.12F, 0.36F, 0.48F));
CHECK(graphics->getSceneTransitionVignette() == 0.25F);
CHECK(graphics->getSceneTransitionVignetteSmoothness() == 0.8F);
CHECK(graphics->getSceneTransitionLensDistortion() == 0.252F);
CHECK(graphics->getSceneTransitionLensScale() == 1.02F);
CHECK(graphics->getSceneLift() == output.transitionLift);
CHECK(graphics->getSceneInverseGamma() == output.transitionInverseGamma);
CHECK(graphics->getSceneGain() == output.transitionGain);
output.isUnderwater = false;
REQUIRE(static_cast<bool>(applyWaterUnderwaterPostFx(graphics, camera, output, surfacePostFx)));
CHECK(camera->getExposure() == 0.4F);
CHECK(graphics->getSceneColorFilter() == surfacePostFx.color);
CHECK(graphics->getSceneTransitionVignette() == 0.0F);
CHECK(graphics->getSceneTransitionVignetteSmoothness() == 0.2F);
CHECK(graphics->getSceneTransitionLensDistortion() == 0.0F);
CHECK(graphics->getSceneTransitionLensScale() == 1.0F);
CHECK(graphics->getSceneLift() == glm::vec3(0.0F));
CHECK(graphics->getSceneInverseGamma() == glm::vec3(1.0F));
CHECK(graphics->getSceneGain() == glm::vec3(1.0F));
surfacePostFx.color.r = -1.0F;
CHECK(!applyWaterUnderwaterPostFx(graphics, camera, output, surfacePostFx));
CHECK(camera->getExposure() == 0.4F);
CHECK(graphics->getSceneColorFilter() == glm::vec3(0.9F, 0.8F, 0.7F));
auto* horizon = Renderable3D::create();
output.horizon = true;
REQUIRE(static_cast<bool>(applyWaterUnderwaterHorizon(horizon, output)));
CHECK(horizon->getVisible());
output.horizon = false;
REQUIRE(static_cast<bool>(applyWaterUnderwaterHorizon(horizon, output)));
CHECK(!horizon->getVisible());
WaterSurfaceMaterialSnapshot surfaceMaterial;
surfaceMaterial.color = {0.12F, 0.34F, 0.56F};
surfaceMaterial.alpha = 0.8F;
output.isUnderwater = true;
output.underwaterMaterialColor = {0.7F, 0.8F, 0.9F};
REQUIRE(static_cast<bool>(
applyWaterUnderwaterMaterial(horizon, output, surfaceMaterial)));
CHECK(horizon->getTintR() == 0.7F);
CHECK(horizon->getTintG() == 0.8F);
output.isUnderwater = false;
REQUIRE(static_cast<bool>(
applyWaterUnderwaterMaterial(horizon, output, surfaceMaterial)));
CHECK(horizon->getTintR() == 0.12F);
CHECK(horizon->getTintB() == 0.56F);
surfaceMaterial.alpha = 2.0F;
CHECK(!applyWaterUnderwaterMaterial(horizon, output, surfaceMaterial));
CHECK(horizon->getTintR() == 0.12F);
auto* cookie = graphics->newCanvas(8, 8);
REQUIRE(cookie != nullptr);
CHECK(static_cast<bool>(volumetric->projectDirectionalCookie(
graphics, cookie->getTexture(), cookie->getTexture(), 15.0F, 0.5F)));
CHECK(!volumetric->projectDirectionalCookie(graphics, cookie->getTexture(),
cookie->getTexture(), 0.0F, 0.5F));
surface.end = surface.start;
CHECK(!applyWaterUnderwaterFog(volumetric, output, surface));
CHECK(volumetric->getFloat("density") == 0.006F);
delete volumetric;
window->close();
}