153 lines
5.6 KiB
C++
153 lines
5.6 KiB
C++
#include "zeroerr/unittest.h"
|
|
#include "zeroerr/assert.h"
|
|
|
|
#include "stylize/MeshParticleEmitter.h"
|
|
#include "stylize/MeshEffectRenderer.h"
|
|
#include "stylize/MeshVfxRenderBatch.h"
|
|
#include "stylize/MeshVfxScalability.h"
|
|
|
|
using namespace eve::stylize;
|
|
|
|
TEST_CASE("stylize.meshParticles.fixedStepDeterminismAndBurst") {
|
|
MeshParticleEmitterConfig config;
|
|
config.capacity = 32;
|
|
config.emissionRate = 10.f;
|
|
config.lifetimeMin = 2.f;
|
|
config.lifetimeMax = 2.f;
|
|
config.speedMin = 1.f;
|
|
config.speedMax = 1.f;
|
|
config.fixedStepSeconds = 0.1f;
|
|
config.maximumSubsteps = 8;
|
|
config.randomSeed = 42;
|
|
config.bursts = {{0.2f, 3}};
|
|
|
|
MeshParticleEmitter first(config);
|
|
MeshParticleEmitter second(config);
|
|
first.start();
|
|
second.start();
|
|
auto firstReport = first.advance(0.3f);
|
|
auto secondReport = second.advance(0.1f);
|
|
secondReport = second.advance(0.2f);
|
|
REQUIRE_EQ(firstReport.alive, 6u);
|
|
REQUIRE_EQ(first.snapshot().size(), second.snapshot().size());
|
|
const auto a = first.snapshot();
|
|
const auto b = second.snapshot();
|
|
for (std::size_t i = 0; i < a.size(); ++i) {
|
|
REQUIRE_EQ(a[i].stableId, b[i].stableId);
|
|
REQUIRE_EQ(a[i].model, b[i].model);
|
|
REQUIRE_EQ(a[i].color, b[i].color);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("stylize.meshParticles.capacityReportsDroppedSpawns") {
|
|
MeshParticleEmitterConfig config;
|
|
config.capacity = 4;
|
|
config.fixedStepSeconds = 0.1f;
|
|
config.bursts = {{0.f, 7}};
|
|
MeshParticleEmitter emitter(config);
|
|
emitter.start();
|
|
const auto report = emitter.advance(0.1f);
|
|
REQUIRE_EQ(report.spawned, 4u);
|
|
REQUIRE_EQ(report.dropped, 3u);
|
|
REQUIRE_EQ(report.alive, 4u);
|
|
}
|
|
|
|
TEST_CASE("stylize.meshParticles.substepGuardReportsDiscardedTime") {
|
|
MeshParticleEmitterConfig config;
|
|
config.fixedStepSeconds = 0.1f;
|
|
config.maximumSubsteps = 2;
|
|
MeshParticleEmitter emitter(config);
|
|
emitter.start();
|
|
const auto report = emitter.advance(0.55f);
|
|
REQUIRE_EQ(report.simulatedSteps, 2u);
|
|
REQUIRE(report.discardedTimeSeconds > 0.29f);
|
|
REQUIRE(report.discardedTimeSeconds < 0.31f);
|
|
}
|
|
|
|
TEST_CASE("stylize.meshParticles.bridgeUsesSharedLodAndAlphaOrdering") {
|
|
MeshParticleEmitterConfig config;
|
|
config.capacity = 3;
|
|
config.fixedStepSeconds = 0.1f;
|
|
MeshParticleEmitter emitter(config);
|
|
emitter.setOrigin({0.f, 0.f, 5.f});
|
|
const auto emitted = emitter.emit(3);
|
|
REQUIRE(emitted == std::size_t{3});
|
|
MeshEffectInstance effect("slash");
|
|
const MeshVfxBatchKey key{11, 12, 13, 0, MeshVfxBatchBlend::Alpha};
|
|
const auto inputs = buildMeshParticleRenderInputs(
|
|
emitter.snapshot(), &effect, reinterpret_cast<eve::graphics::Mesh*>(1), nullptr,
|
|
key, 77, glm::vec3(0.f), 100.f, 50.f, 2);
|
|
REQUIRE_EQ(inputs.lodCandidates.size(), 3u);
|
|
REQUIRE_EQ(inputs.renderItems.size(), 3u);
|
|
REQUIRE_EQ(inputs.commands.size(), 3u);
|
|
REQUIRE_EQ(inputs.commands[0].stableInstanceId, inputs.renderItems[0].stableInstanceId);
|
|
REQUIRE_EQ(static_cast<int>(inputs.commands[0].kind),
|
|
static_cast<int>(MeshVfxRendererCommand::Kind::Particle));
|
|
|
|
MeshVfxScalabilityPolicy policy;
|
|
policy.maximumDistance = 50.f;
|
|
policy.workUnitBudget = 12;
|
|
const auto decisions = MeshVfxScalabilityPlanner(policy).plan(inputs.lodCandidates);
|
|
const auto queue = MeshVfxRenderBatchPlanner{}.build(inputs.renderItems, decisions, 0);
|
|
REQUIRE_EQ(queue.stats.queuedDraws, 3u);
|
|
REQUIRE_EQ(queue.batches.size(), 1u);
|
|
}
|
|
|
|
TEST_CASE("stylize.meshParticles.curvesDriveScaleColorAndRotation") {
|
|
MeshParticleEmitterConfig config;
|
|
config.capacity = 1;
|
|
config.lifetimeMin = 1.f;
|
|
config.lifetimeMax = 1.f;
|
|
config.fixedStepSeconds = 0.25f;
|
|
config.rotationMinRadians = 0.f;
|
|
config.rotationMaxRadians = 0.f;
|
|
config.angularVelocityMin = 2.f;
|
|
config.angularVelocityMax = 2.f;
|
|
config.scaleCurve = {{1.f, 0.f}, {0.f, 2.f}, {0.5f, 1.f}};
|
|
config.colorGradient = {{0.f, {1.f, 0.f, 0.f, 1.f}},
|
|
{1.f, {0.f, 0.f, 1.f, 0.f}}};
|
|
MeshParticleEmitter emitter(config);
|
|
const auto emitted = emitter.emit(1);
|
|
REQUIRE(emitted == std::size_t{1});
|
|
const auto report = emitter.advance(0.5f);
|
|
REQUIRE_EQ(report.simulatedSteps, 2u);
|
|
const auto particles = emitter.snapshot();
|
|
REQUIRE_EQ(particles.size(), 1u);
|
|
REQUIRE(particles[0].color.r > 0.49f);
|
|
REQUIRE(particles[0].color.r < 0.51f);
|
|
REQUIRE(particles[0].color.b > 0.49f);
|
|
REQUIRE(particles[0].color.b < 0.51f);
|
|
const float axisScale = glm::length(glm::vec3(particles[0].model[0]));
|
|
REQUIRE(axisScale > 0.99f);
|
|
REQUIRE(axisScale < 1.01f);
|
|
}
|
|
|
|
TEST_CASE("stylize.meshParticles.volumeShapesStayInsideConfiguredBounds") {
|
|
MeshParticleEmitterConfig sphere;
|
|
sphere.capacity = 64;
|
|
sphere.shape = MeshParticleShape::Sphere;
|
|
sphere.sphereRadius = 3.f;
|
|
sphere.randomSeed = 7;
|
|
MeshParticleEmitter sphereEmitter(sphere);
|
|
const auto sphereEmitted = sphereEmitter.emit(64);
|
|
REQUIRE(sphereEmitted == std::size_t{64});
|
|
for (const auto& particle : sphereEmitter.snapshot()) {
|
|
REQUIRE(glm::length(glm::vec3(particle.model[3])) <= 3.0001f);
|
|
}
|
|
|
|
MeshParticleEmitterConfig box;
|
|
box.capacity = 64;
|
|
box.shape = MeshParticleShape::Box;
|
|
box.boxExtents = {1.f, 2.f, 3.f};
|
|
box.randomSeed = 9;
|
|
MeshParticleEmitter boxEmitter(box);
|
|
const auto boxEmitted = boxEmitter.emit(64);
|
|
REQUIRE(boxEmitted == std::size_t{64});
|
|
for (const auto& particle : boxEmitter.snapshot()) {
|
|
const glm::vec3 position(particle.model[3]);
|
|
REQUIRE(std::abs(position.x) <= 1.0001f);
|
|
REQUIRE(std::abs(position.y) <= 2.0001f);
|
|
REQUIRE(std::abs(position.z) <= 3.0001f);
|
|
}
|
|
}
|