228 lines
8.8 KiB
C++
228 lines
8.8 KiB
C++
#include "zeroerr/assert.h"
|
|
#include "zeroerr/unittest.h"
|
|
|
|
#include "common/Exception.h"
|
|
#include "graphics/Canvas.h"
|
|
#include "graphics/Graphics.h"
|
|
#include "stylize/MeshEffect.h"
|
|
#include "stylize/MeshEffectRenderer.h"
|
|
#include "stylize/MeshVfxRenderBatch.h"
|
|
#include "stylize/SkillMeshEffect.h"
|
|
#include "stylize/Stylize.h"
|
|
#include "stylize/TrailEffect.h"
|
|
#include "window/Window.h"
|
|
|
|
#include <cmath>
|
|
|
|
using eve::stylize::MeshEffectPlayback;
|
|
using eve::stylize::MeshEffectState;
|
|
using eve::stylize::MeshEffectTargetHandle;
|
|
using eve::stylize::Stylize;
|
|
using eve::stylize::SkillMeshEffectKind;
|
|
using eve::stylize::TrailAppendResult;
|
|
using eve::stylize::TrailEmitter;
|
|
using eve::stylize::TrailSettings;
|
|
using eve::stylize::prepareTrailUpload;
|
|
|
|
TEST_CASE("stylize.meshVfx.playbackAndBinding") {
|
|
auto* module = Stylize::create();
|
|
auto effect = module->createMeshEffect("ember");
|
|
REQUIRE(effect.get() != nullptr);
|
|
CHECK(!effect->isBound());
|
|
effect->bindTarget(MeshEffectTargetHandle(7, 2));
|
|
CHECK(effect->isBound());
|
|
CHECK_EQ(effect->target().index(), 7u);
|
|
CHECK_EQ(effect->target().generation(), 2u);
|
|
effect->setPlayback(MeshEffectPlayback{0.1f, 0.2f, 0.1f, false});
|
|
effect->play();
|
|
CHECK(static_cast<int>(effect->state()) == static_cast<int>(MeshEffectState::FadingIn));
|
|
effect->update(0.05f);
|
|
CHECK(std::fabs(effect->intensity() - 0.5f) < 0.001f);
|
|
effect->update(0.10f);
|
|
CHECK(static_cast<int>(effect->state()) == static_cast<int>(MeshEffectState::Active));
|
|
CHECK(std::fabs(effect->style().getFloat("time") - 0.15f) < 0.001f);
|
|
effect->stop(0.1f);
|
|
effect->update(0.05f);
|
|
CHECK(std::fabs(effect->intensity() - 0.5f) < 0.001f);
|
|
effect->update(0.05f);
|
|
CHECK(static_cast<int>(effect->state()) == static_cast<int>(MeshEffectState::Finished));
|
|
effect->unbindTarget();
|
|
CHECK(!effect->isBound());
|
|
}
|
|
|
|
TEST_CASE("stylize.meshVfx.ribbonGeometryAndDiscontinuity") {
|
|
TrailEmitter trail(TrailSettings{8, 1.f, 0.01f, 1.f});
|
|
CHECK(static_cast<int>(trail.append({0.f, 0.f, 0.f}, {0.f, 1.f, 0.f})) ==
|
|
static_cast<int>(TrailAppendResult::StartedSegment));
|
|
CHECK(static_cast<int>(trail.append({0.5f, 0.f, 0.f}, {0.5f, 1.f, 0.f})) ==
|
|
static_cast<int>(TrailAppendResult::Added));
|
|
auto mesh = trail.buildMesh();
|
|
CHECK_EQ(mesh.vertices.size(), 4u);
|
|
CHECK_EQ(mesh.indices.size(), 6u);
|
|
CHECK_EQ(mesh.vertices[0].uv.x, 0.f);
|
|
CHECK_EQ(mesh.vertices[2].uv.x, 1.f);
|
|
CHECK(static_cast<int>(trail.append({4.f, 0.f, 0.f}, {4.f, 1.f, 0.f})) ==
|
|
static_cast<int>(TrailAppendResult::StartedSegment));
|
|
mesh = trail.buildMesh();
|
|
CHECK_EQ(mesh.vertices.size(), 6u);
|
|
CHECK_EQ(mesh.indices.size(), 6u);
|
|
trail.update(1.f);
|
|
CHECK_EQ(trail.sampleCount(), 0u);
|
|
CHECK(trail.buildMesh().indices.empty());
|
|
}
|
|
|
|
TEST_CASE("stylize.meshVfx.rejectsInvalidConfiguration") {
|
|
bool threw = false;
|
|
try {
|
|
TrailEmitter invalid(TrailSettings{1, 1.f, 0.f, 1.f});
|
|
} catch (const eve::Exception&) {
|
|
threw = true;
|
|
}
|
|
CHECK(threw);
|
|
threw = false;
|
|
try {
|
|
auto* module = Stylize::create();
|
|
auto effect = module->createMeshEffect("vignette");
|
|
(void)effect;
|
|
} catch (const eve::Exception&) {
|
|
threw = true;
|
|
}
|
|
CHECK(threw);
|
|
}
|
|
|
|
TEST_CASE("stylize.meshVfx.preparesBackendUpload") {
|
|
TrailEmitter trail(TrailSettings{8, 1.f, 0.f, 2.f});
|
|
(void)trail.append({0.f, 0.f, 0.f}, {0.f, 1.f, 0.f});
|
|
(void)trail.append({0.5f, 0.f, 0.f}, {0.5f, 1.f, 0.f});
|
|
auto prepared = prepareTrailUpload(trail.buildMesh());
|
|
REQUIRE(prepared.ok());
|
|
const auto& upload = prepared.value();
|
|
CHECK_EQ(upload.positions.size(), 12u);
|
|
CHECK_EQ(upload.normals.size(), 12u);
|
|
CHECK_EQ(upload.uvs.size(), 8u);
|
|
CHECK_EQ(upload.indices.size(), 6u);
|
|
CHECK(std::fabs(upload.normals[2]) > 0.9f);
|
|
}
|
|
|
|
TEST_CASE("stylize.meshVfx.gpuRibbonSubmission") {
|
|
auto* window = eve::window::Window::create();
|
|
auto* graphics = eve::graphics::Graphics::create();
|
|
REQUIRE(window != nullptr);
|
|
REQUIRE(graphics != nullptr);
|
|
eve::window::WindowSettings settings;
|
|
settings.width = 64;
|
|
settings.height = 64;
|
|
settings.centered = true;
|
|
REQUIRE(window->setWindowSettings(settings));
|
|
|
|
auto* stylize = Stylize::create();
|
|
auto effect = stylize->createMeshEffect("slash");
|
|
effect->setPlayback(MeshEffectPlayback{0.f, 1.f, 0.f, false});
|
|
effect->play();
|
|
auto renderer = stylize->createMeshEffectRenderer(*graphics);
|
|
|
|
TrailEmitter trail(TrailSettings{8, 1.f, 0.f, 2.f});
|
|
(void)trail.append({-0.8f, -0.25f, 0.f}, {-0.8f, 0.25f, 0.f});
|
|
(void)trail.append({0.8f, -0.25f, 0.f}, {0.8f, 0.25f, 0.f});
|
|
|
|
graphics->setMesh3DViewProj(glm::mat4(1.f));
|
|
graphics->setMesh3DView(glm::mat4(1.f));
|
|
graphics->setMesh3DCameraPos({0.f, 0.f, 2.f});
|
|
auto* target = graphics->newCanvas(64, 64);
|
|
REQUIRE(target != nullptr);
|
|
graphics->begin3DFrameToCanvas(target);
|
|
auto submitted = renderer->submitTrail(*effect, trail.buildMesh());
|
|
REQUIRE(submitted.ok());
|
|
CHECK(static_cast<int>(submitted.value()) ==
|
|
static_cast<int>(eve::stylize::MeshEffectSubmitStatus::Drawn));
|
|
graphics->end3DFrameToCanvas();
|
|
|
|
auto descriptor = graphics->describeMesh(renderer->trailMesh());
|
|
REQUIRE(descriptor.has_value());
|
|
CHECK_EQ(descriptor->vertexCount, 4u);
|
|
CHECK_EQ(descriptor->indexCount, 6u);
|
|
const eve::graphics::Color center = target->getPixel(32, 32);
|
|
const bool renderedColor = center.r > 0.05f || center.g > 0.05f || center.b > 0.05f;
|
|
CHECK(renderedColor);
|
|
window->close();
|
|
}
|
|
|
|
TEST_CASE("stylize.meshVfx.skillRecipesComposeRuntime") {
|
|
auto* stylize = Stylize::create();
|
|
auto slash = stylize->createSkillMeshEffect(SkillMeshEffectKind::WeaponSlash);
|
|
REQUIRE(slash.get() != nullptr);
|
|
CHECK(slash->hasTrail());
|
|
CHECK_EQ(slash->effect().style().getStyle(), "slash");
|
|
slash->bindTarget(MeshEffectTargetHandle(4, 9));
|
|
slash->play();
|
|
CHECK(static_cast<int>(slash->appendBlade({0.f, 0.f, 0.f}, {0.f, 1.f, 0.f})) ==
|
|
static_cast<int>(TrailAppendResult::StartedSegment));
|
|
slash->update(0.03f);
|
|
CHECK(slash->effect().intensity() > 0.f);
|
|
CHECK_EQ(slash->trail().sampleCount(), 1u);
|
|
|
|
auto impact = stylize->createSkillMeshEffect(SkillMeshEffectKind::ImpactFlash);
|
|
REQUIRE(impact.get() != nullptr);
|
|
CHECK(!impact->hasTrail());
|
|
CHECK_EQ(impact->effect().style().getStyle(), "rim");
|
|
impact->play();
|
|
impact->update(0.2f);
|
|
CHECK(static_cast<int>(impact->effect().state()) == static_cast<int>(MeshEffectState::Finished));
|
|
|
|
bool rejectedBladeSample = false;
|
|
try {
|
|
(void)impact->appendBlade({0.f, 0.f, 0.f}, {0.f, 1.f, 0.f});
|
|
} catch (const eve::Exception&) {
|
|
rejectedBladeSample = true;
|
|
}
|
|
CHECK(rejectedBladeSample);
|
|
}
|
|
|
|
TEST_CASE("stylize.meshVfx.gpuBatchQueueSubmission") {
|
|
auto* window = eve::window::Window::create();
|
|
auto* graphics = eve::graphics::Graphics::create();
|
|
REQUIRE(window != nullptr);
|
|
REQUIRE(graphics != nullptr);
|
|
eve::window::WindowSettings settings;
|
|
settings.width = 64;
|
|
settings.height = 64;
|
|
REQUIRE(window->setWindowSettings(settings));
|
|
|
|
auto* stylize = Stylize::create();
|
|
auto effect = stylize->createMeshEffect("slash");
|
|
effect->setPlayback(MeshEffectPlayback{0.f, 1.f, 0.f, false});
|
|
effect->play();
|
|
auto renderer = stylize->createMeshEffectRenderer(*graphics);
|
|
|
|
TrailEmitter trail(TrailSettings{8, 1.f, 0.f, 2.f});
|
|
(void)trail.append({-0.8f, -0.25f, 0.f}, {-0.8f, 0.25f, 0.f});
|
|
(void)trail.append({0.8f, -0.25f, 0.f}, {0.8f, 0.25f, 0.f});
|
|
eve::stylize::MeshVfxRendererCommand command;
|
|
command.stableInstanceId = 17;
|
|
command.kind = eve::stylize::MeshVfxRendererCommand::Kind::Trail;
|
|
command.effect = effect.get();
|
|
command.trail = trail.buildMesh();
|
|
|
|
eve::stylize::MeshVfxRenderQueue queue;
|
|
eve::stylize::MeshVfxRenderBatch batch;
|
|
batch.key.blend = eve::stylize::MeshVfxBatchBlend::Additive;
|
|
batch.draws.push_back({17, eve::stylize::MeshVfxLodTier::Full,
|
|
eve::stylize::MeshVfxMeshUpdate::Refresh, 1});
|
|
queue.batches.push_back(std::move(batch));
|
|
|
|
graphics->setMesh3DViewProj(glm::mat4(1.f));
|
|
graphics->setMesh3DView(glm::mat4(1.f));
|
|
graphics->setMesh3DCameraPos({0.f, 0.f, 2.f});
|
|
auto* target = graphics->newCanvas(64, 64);
|
|
REQUIRE(target != nullptr);
|
|
graphics->begin3DFrameToCanvas(target);
|
|
const auto submitted = renderer->submitQueue(queue, std::span<const eve::stylize::MeshVfxRendererCommand>(&command, 1));
|
|
REQUIRE(submitted.ok());
|
|
CHECK(static_cast<int>(submitted.value().status) ==
|
|
static_cast<int>(eve::stylize::MeshVfxQueueSubmitStatus::Complete));
|
|
CHECK_EQ(submitted.value().acceptedBatches, 1u);
|
|
CHECK_EQ(submitted.value().acceptedDraws, 1u);
|
|
graphics->end3DFrameToCanvas();
|
|
window->close();
|
|
}
|