Files
EVEngine/test/fluids_volume_collision_material.cpp

145 lines
6.1 KiB
C++

#include <glm/geometric.hpp>
#include "fluids/VolumeFluid.h"
#include "fluids/VolumeFluidCodec.h"
#include "zeroerr/assert.h"
#include "zeroerr/unittest.h"
using namespace eve::fluids;
namespace {
std::unique_ptr<VolumeFluid> makeContactSolver(VolumeFluidParticle particle) {
VolumeFluidSettings settings;
settings.gravity = {0.f, 0.f, 0.f};
settings.iterations = 1;
auto created = VolumeFluid::create(settings);
REQUIRE(created.ok());
auto solver = std::move(created).takeValue();
REQUIRE(solver->emit(std::span(&particle, 1)).ok());
return solver;
}
eve::Value withoutV17MaterialFields(const eve::Value& current) {
eve::Value legacy(eve::Value::Object{});
for (const auto& key : current.keys()) {
if (key == "version" || key == "stitches") continue;
const auto* source = current.find(key);
if (key == "particles") {
eve::Value::Array particles;
for (size_t i = 0; i < source->arraySize(); ++i) {
auto particle = source->at(i);
const auto* materialSource = particle.find("material");
eve::Value material(eve::Value::Object{});
for (const auto& field : materialSource->keys())
if (field != "stickiness" && field != "stickDistance" && field != "frictionCombine" &&
field != "stickinessCombine")
material.set(field, *materialSource->find(field));
particle.set("material", std::move(material));
particles.push_back(std::move(particle));
}
legacy.set(key, eve::Value(std::move(particles)));
continue;
}
if (key == "colliders" || key == "sdfColliders" || key == "heightFieldColliders") {
eve::Value::Array colliders;
for (size_t i = 0; i < source->arraySize(); ++i) {
eve::Value collider(eve::Value::Object{});
for (const auto& field : source->at(i).keys())
if (field != "staticFriction" && field != "rollingFriction" && field != "stickiness" &&
field != "stickDistance" && field != "frictionCombine" && field != "stickinessCombine" &&
field != "rollingContacts")
collider.set(field, *source->at(i).find(field));
colliders.push_back(std::move(collider));
}
legacy.set(key, eve::Value(std::move(colliders)));
continue;
}
legacy.set(key, *source);
}
legacy.set("version", 16);
return legacy;
}
} // namespace
TEST_CASE("fluids.volume.collisionMaterialAdhesionUsesFluid3DCombinePriority") {
VolumeFluidParticle particle;
particle.position = {.56f, 0.f, 0.f};
particle.material.stickiness = 0.f;
particle.material.stickDistance = .1f;
particle.material.stickinessCombine = VolumeFluidMaterialCombineMode::Minimum;
VolumeFluidCollider sphere;
sphere.center = {0.f, 0.f, 0.f};
sphere.radius = .5f;
sphere.stickiness = 10.f;
sphere.stickDistance = .1f;
sphere.stickinessCombine = VolumeFluidMaterialCombineMode::Minimum;
auto minimum = makeContactSolver(particle);
REQUIRE(minimum->setColliders(std::span(&sphere, 1)).ok());
REQUIRE(minimum->step(.01f, 1).ok());
REQUIRE(std::abs(minimum->particles()[0].position.x - .56f) < 1e-6f);
sphere.stickinessCombine = VolumeFluidMaterialCombineMode::Maximum;
auto maximum = makeContactSolver(particle);
REQUIRE(maximum->setColliders(std::span(&sphere, 1)).ok());
REQUIRE(maximum->step(.01f, 1).ok());
REQUIRE(maximum->particles()[0].position.x < .56f);
REQUIRE(maximum->particles()[0].position.x >= .46f - 1e-6f);
REQUIRE(maximum->contacts().size() == 1);
REQUIRE(maximum->contacts()[0].impulse.x > 0.f);
}
TEST_CASE("fluids.volume.collisionMaterialValidationIsAtomic") {
auto created = VolumeFluid::create({});
REQUIRE(created.ok());
auto solver = std::move(created).takeValue();
VolumeFluidCollider valid;
valid.label = 7;
REQUIRE(solver->setColliders(std::span(&valid, 1)).ok());
auto invalid = valid;
invalid.label = 8;
invalid.frictionCombine = static_cast<VolumeFluidMaterialCombineMode>(99);
REQUIRE(!solver->setColliders(std::span(&invalid, 1)).ok());
REQUIRE(solver->snapshot().colliders.size() == 1);
REQUIRE(solver->snapshot().colliders[0].label == 7);
VolumeFluidParticle particle;
particle.material.stickDistance = 10.01f;
REQUIRE(!solver->emit(std::span(&particle, 1)).ok());
REQUIRE(solver->particleCount() == 0);
}
TEST_CASE("fluids.volume.collisionMaterialV16MigrationIsStrict") {
auto created = VolumeFluid::create({});
REQUIRE(created.ok());
auto solver = std::move(created).takeValue();
VolumeFluidParticle particle;
particle.position = {0.f, 1.f, 0.f};
REQUIRE(solver->emit(std::span(&particle, 1)).ok());
VolumeFluidCollider analytic;
analytic.label = 1;
REQUIRE(solver->setColliders(std::span(&analytic, 1)).ok());
VolumeFluidSdfCollider sdf;
sdf.label = 2;
sdf.sdf = MeshSdf::makeSphere(glm::vec3(0.f), .5f, {8, 8, 8});
REQUIRE(solver->setSdfColliders(std::span(&sdf, 1)).ok());
VolumeFluidHeightFieldCollider terrain;
terrain.label = 3;
terrain.heights = {0.f, 0.f, 0.f, 0.f};
REQUIRE(solver->setHeightFieldColliders(std::span(&terrain, 1)).ok());
auto legacy = withoutV17MaterialFields(encodeVolumeFluid(solver->snapshot()));
auto migrated = decodeVolumeFluid(legacy);
REQUIRE(migrated.ok());
REQUIRE(migrated.value().version == 20);
REQUIRE(migrated.value().particles[0].material.stickiness == 0.f);
REQUIRE(migrated.value().colliders[0].staticFriction == migrated.value().colliders[0].friction);
REQUIRE(!migrated.value().sdfColliders[0].rollingContacts);
REQUIRE(migrated.value().heightFieldColliders[0].stickDistance == 0.f);
legacy.find("particles")->at(0).find("material")->set("stickiness", 1.f);
REQUIRE(!decodeVolumeFluid(legacy).ok());
}