218 lines
10 KiB
C++
218 lines
10 KiB
C++
#include <glm/geometric.hpp>
|
|
|
|
#include "fluids/VolumeFluid.h"
|
|
#include "zeroerr/assert.h"
|
|
#include "zeroerr/unittest.h"
|
|
|
|
using namespace eve::fluids;
|
|
|
|
TEST_CASE("fluids.volume.gridDebuggerReturnsOnlyStableOccupiedCells") {
|
|
auto created = VolumeFluid::create({});
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
VolumeFluidParticle particles[3];
|
|
particles[0].position = {-1.90f, .10f, -.90f};
|
|
particles[1].position = {-1.85f, .15f, -.85f};
|
|
particles[2].position = {-1.50f, .10f, -.90f};
|
|
REQUIRE(solver->emit(particles).ok());
|
|
|
|
auto cells = solver->debugParticleGrid(2);
|
|
REQUIRE(cells.ok());
|
|
REQUIRE(cells.value().size() == 2);
|
|
REQUIRE(cells.value()[0].particleCount == 2);
|
|
REQUIRE(cells.value()[1].particleCount == 1);
|
|
REQUIRE(glm::length(cells.value()[0].center - glm::vec3(-1.9f, .1f, -.9f)) < 1e-5f);
|
|
REQUIRE(glm::length(cells.value()[0].size - glm::vec3(.2f)) < 1e-6f);
|
|
REQUIRE(cells.value()[0].center.x < cells.value()[1].center.x);
|
|
}
|
|
|
|
TEST_CASE("fluids.volume.gridDebuggerBudgetFailureDoesNotMutateSolver") {
|
|
auto created = VolumeFluid::create({});
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
VolumeFluidParticle particles[2];
|
|
particles[0].position = {-1.9f, .1f, -.9f};
|
|
particles[1].position = {1.9f, 2.9f, .9f};
|
|
REQUIRE(solver->emit(particles).ok());
|
|
const auto before = solver->snapshot();
|
|
REQUIRE(!solver->debugParticleGrid(1).ok());
|
|
REQUIRE(!solver->debugParticleGrid(0).ok());
|
|
const auto after = solver->snapshot();
|
|
REQUIRE(after.particles.size() == before.particles.size());
|
|
REQUIRE(after.particles[0].position == before.particles[0].position);
|
|
REQUIRE(after.particles[1].position == before.particles[1].position);
|
|
}
|
|
|
|
TEST_CASE("fluids.volume.particleFrameDebuggerReturnsActorLocalAxesOnDemand") {
|
|
auto created = VolumeFluid::create({});
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
VolumeFluidParticle particles[2];
|
|
particles[0].position = {.5f, 1.f, .5f};
|
|
particles[0].actorGroup = 7;
|
|
particles[0].orientation = {0.f, 0.f, .70710678f, .70710678f};
|
|
particles[1].position = {-.5f, 1.f, 0.f};
|
|
particles[1].actorGroup = 8;
|
|
REQUIRE(solver->emit(particles).ok());
|
|
auto frames = solver->debugParticleFrames(7, 2.f, 1);
|
|
REQUIRE(frames.ok());
|
|
REQUIRE(frames.value().size() == 1);
|
|
REQUIRE(frames.value()[0].particleIndex == 0);
|
|
REQUIRE(glm::length(frames.value()[0].x - glm::vec3(.5f, 3.f, .5f)) < 1e-5f);
|
|
REQUIRE(glm::length(frames.value()[0].y - glm::vec3(-1.5f, 1.f, .5f)) < 1e-5f);
|
|
REQUIRE(glm::length(frames.value()[0].z - glm::vec3(.5f, 1.f, 2.5f)) < 1e-5f);
|
|
}
|
|
|
|
TEST_CASE("fluids.volume.particleFrameDebuggerBudgetAndValidationAreAtomic") {
|
|
auto created = VolumeFluid::create({});
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
VolumeFluidParticle particles[2];
|
|
particles[0].actorGroup = particles[1].actorGroup = 3;
|
|
particles[0].position = {-.1f, 1.f, 0.f};
|
|
particles[1].position = {.1f, 1.f, 0.f};
|
|
REQUIRE(solver->emit(particles).ok());
|
|
const auto before = solver->snapshot();
|
|
REQUIRE(!solver->debugParticleFrames(3, 1.f, 1).ok());
|
|
REQUIRE(!solver->debugParticleFrames(3, 0.f, 2).ok());
|
|
REQUIRE(!solver->debugParticleFrames(4, 1.f, 2).ok());
|
|
const auto after = solver->snapshot();
|
|
REQUIRE(after.particles.size() == before.particles.size());
|
|
REQUIRE(after.particles[0].position == before.particles[0].position);
|
|
REQUIRE(after.particles[1].position == before.particles[1].position);
|
|
}
|
|
|
|
TEST_CASE("fluids.volume.particleInstancesFilterScaleColorAndInterpolate") {
|
|
VolumeFluidSettings settings;
|
|
settings.gravity = {0.f, 0.f, 0.f};
|
|
auto created = VolumeFluid::create(settings);
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
VolumeFluidParticle particles[3];
|
|
particles[0].position = {0.f, 1.f, 0.f};
|
|
particles[0].velocity = {1.f, 0.f, 0.f};
|
|
particles[0].radii = {.1f, .2f, .3f};
|
|
particles[0].orientation = {0.f, 0.f, .70710678f, .70710678f};
|
|
particles[0].color = {.2f, .3f, .4f, .5f};
|
|
particles[0].actorGroup = 7;
|
|
particles[1].position = {.5f, 1.f, 0.f};
|
|
particles[1].actorGroup = 8;
|
|
particles[2].position = {1.f, 1.f, 0.f};
|
|
particles[2].actorGroup = 7;
|
|
REQUIRE(solver->emit(particles).ok());
|
|
REQUIRE(solver->step(1.f / 120.f, 1).ok());
|
|
|
|
auto instances = solver->particleInstances(7, {2.f, 3.f, 4.f}, .5f, 2);
|
|
REQUIRE(instances.ok());
|
|
REQUIRE(instances.value().size() == 2);
|
|
REQUIRE(instances.value()[0].particleIndex == 0);
|
|
REQUIRE(instances.value()[1].particleIndex == 2);
|
|
CHECK(std::abs(instances.value()[0].position.x - 1.f / 240.f) < 1e-5f);
|
|
CHECK(glm::length(instances.value()[0].scale - glm::vec3(.2f, .6f, 1.2f)) < 1e-5f);
|
|
CHECK(instances.value()[0].orientation == particles[0].orientation);
|
|
CHECK(instances.value()[0].color == particles[0].color);
|
|
}
|
|
|
|
TEST_CASE("fluids.volume.particleInstancesBudgetAndValidationAreAtomic") {
|
|
auto created = VolumeFluid::create({});
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
VolumeFluidParticle particles[2];
|
|
particles[0].position = {0.f, 1.f, 0.f};
|
|
particles[1].position = {.2f, 1.f, 0.f};
|
|
particles[0].actorGroup = particles[1].actorGroup = 3;
|
|
REQUIRE(solver->emit(particles).ok());
|
|
const auto before = solver->snapshot();
|
|
REQUIRE(!solver->particleInstances(3, glm::vec3(1.f), 1.f, 1).ok());
|
|
REQUIRE(!solver->particleInstances(4, glm::vec3(1.f), 1.f, 2).ok());
|
|
REQUIRE(!solver->particleInstances(3, {0.f, 1.f, 1.f}, 1.f, 2).ok());
|
|
REQUIRE(!solver->particleInstances(3, glm::vec3(1.f), 1.1f, 2).ok());
|
|
const auto after = solver->snapshot();
|
|
REQUIRE(after.particles.size() == before.particles.size());
|
|
REQUIRE(after.particles[0].position == before.particles[0].position);
|
|
REQUIRE(after.particles[1].position == before.particles[1].position);
|
|
}
|
|
|
|
TEST_CASE("fluids.volume.particleImpostorsApplyRadiusTintAndInterpolation") {
|
|
VolumeFluidSettings settings;
|
|
settings.gravity = {0.f, 0.f, 0.f};
|
|
auto created = VolumeFluid::create(settings);
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
|
|
VolumeFluidParticle particle;
|
|
particle.position = {0.f, 1.f, 0.f};
|
|
particle.velocity = {2.f, 0.f, 0.f};
|
|
particle.radii = {.1f, .2f, .3f};
|
|
particle.orientation = {0.f, 0.f, .70710677f, .70710677f};
|
|
particle.color = {.8f, .5f, .25f, .75f};
|
|
particle.actorGroup = 9;
|
|
REQUIRE(solver->emit(std::span(&particle, 1)).ok());
|
|
REQUIRE(solver->step(1.f / 120.f, 1).ok());
|
|
|
|
auto instances = solver->particleImpostors(9, 2.f, {.5f, .25f, 1.f, .5f}, .5f, 1);
|
|
REQUIRE(instances.ok());
|
|
REQUIRE(instances.value().size() == 1);
|
|
CHECK(glm::length(instances.value()[0].position - glm::vec3(1.f / 120.f, 1.f, 0.f)) < 1e-5f);
|
|
CHECK(glm::length(instances.value()[0].scale - glm::vec3(.2f, .4f, .6f)) < 1e-6f);
|
|
CHECK(glm::length(instances.value()[0].color - glm::vec4(.4f, .125f, .25f, .375f)) < 1e-6f);
|
|
CHECK(glm::length(instances.value()[0].orientation - particle.orientation) < 1e-6f);
|
|
REQUIRE(!solver->particleImpostors(9, 0.f, glm::vec4(1.f), 1.f, 1).ok());
|
|
REQUIRE(!solver->particleImpostors(9, 1.f, {1.1f, 1.f, 1.f, 1.f}, 1.f, 1).ok());
|
|
REQUIRE(!solver->particleImpostors(9, 1.f, glm::vec4(1.f), 1.f, 0).ok());
|
|
|
|
std::vector<VolumeFluidParticleInstance> reused;
|
|
reused.reserve(2);
|
|
REQUIRE(solver->copyParticleImpostors(9, 1.f, glm::vec4(1.f), 1.f, 1, reused).ok());
|
|
const auto* storage = reused.data();
|
|
REQUIRE(solver->copyParticleImpostors(9, 3.f, glm::vec4(.5f), 0.f, 1, reused).ok());
|
|
CHECK(reused.data() == storage);
|
|
CHECK(glm::length(reused[0].scale - glm::vec3(.3f, .6f, .9f)) < 1e-6f);
|
|
const auto preserved = reused[0];
|
|
REQUIRE(!solver->copyParticleImpostors(9, -1.f, glm::vec4(1.f), 1.f, 1, reused).ok());
|
|
REQUIRE(reused.size() == 1);
|
|
CHECK(reused[0].position == preserved.position);
|
|
CHECK(reused[0].scale == preserved.scale);
|
|
CHECK(reused[0].color == preserved.color);
|
|
}
|
|
|
|
TEST_CASE("fluids.volume.sdfSliceDebuggerSamplesAndTransformsOnDemand") {
|
|
auto created = VolumeFluid::create({});
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
VolumeFluidSdfCollider collider;
|
|
collider.label = 42;
|
|
collider.sdf = MeshSdf::makeSphere({0.f, 0.f, 0.f}, .5f, {5, 7, 9});
|
|
collider.position = {1.f, 2.f, 3.f};
|
|
collider.rotation = {0.f, 0.f, .70710678f, .70710678f};
|
|
collider.scale = 2.f;
|
|
REQUIRE(solver->setSdfColliders(std::span(&collider, 1)).ok());
|
|
|
|
auto slice = solver->debugSdfSlice(42, VolumeFluidSdfSliceAxis::Z, .5f, .5f, 35);
|
|
REQUIRE(slice.ok());
|
|
REQUIRE(slice.value().width == 5);
|
|
REQUIRE(slice.value().height == 7);
|
|
REQUIRE(slice.value().values.size() == 35);
|
|
CHECK(glm::length(slice.value().stepX - glm::vec3(0.f, collider.sdf.cellSize * 2.f, 0.f)) < 1e-5f);
|
|
CHECK(glm::length(slice.value().stepY - glm::vec3(-collider.sdf.cellSize * 2.f, 0.f, 0.f)) < 1e-5f);
|
|
const auto center = slice.value().values[size_t(3) * 5 + 2];
|
|
CHECK(center < .1f);
|
|
}
|
|
|
|
TEST_CASE("fluids.volume.sdfSliceDebuggerRejectsWithoutPartialState") {
|
|
auto created = VolumeFluid::create({});
|
|
REQUIRE(created.ok());
|
|
auto solver = std::move(created).takeValue();
|
|
VolumeFluidSdfCollider collider;
|
|
collider.label = 7;
|
|
collider.sdf = MeshSdf::makeSphere(glm::vec3(0.f), .5f, {5, 5, 5});
|
|
REQUIRE(solver->setSdfColliders(std::span(&collider, 1)).ok());
|
|
const auto before = solver->snapshot();
|
|
REQUIRE(!solver->debugSdfSlice(8, VolumeFluidSdfSliceAxis::X, .5f, .5f, 25).ok());
|
|
REQUIRE(!solver->debugSdfSlice(7, VolumeFluidSdfSliceAxis::X, .5f, .5f, 24).ok());
|
|
REQUIRE(!solver->debugSdfSlice(7, VolumeFluidSdfSliceAxis(9), .5f, .5f, 25).ok());
|
|
const auto after = solver->snapshot();
|
|
REQUIRE(after.sdfColliders.size() == before.sdfColliders.size());
|
|
REQUIRE(after.sdfColliders[0].sdf.distances == before.sdfColliders[0].sdf.distances);
|
|
}
|