550 lines
21 KiB
C++
550 lines
21 KiB
C++
#include "procgen/Biome.h"
|
|
#include "procgen/PointGraph.h"
|
|
#include "procgen/ShapeGrammar.h"
|
|
|
|
#include <cmath>
|
|
#include "zeroerr/assert.h"
|
|
#include "zeroerr/unittest.h"
|
|
|
|
using namespace eve::procgen;
|
|
|
|
TEST_CASE("procgen.pointGraph.executesDagAndCachesResults") {
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("sample", "spatial.sample"));
|
|
CHECK(graph.addNode("exclude", "spatial.filter"));
|
|
CHECK(graph.addNode("prune", "self.prune"));
|
|
CHECK(graph.connect("sample", "exclude"));
|
|
CHECK(graph.connect("exclude", "prune"));
|
|
|
|
SpatialData domain = SpatialData::box(0.f, 0.f, 0.f, 4.f, 0.f, 4.f);
|
|
SpatialData hole = SpatialData::sphere(2.f, 0.f, 2.f, 0.75f);
|
|
CHECK(graph.setNodeSpatial("sample", &domain));
|
|
CHECK(graph.setNodeFloat("sample", "spacing", 2.f));
|
|
CHECK(graph.setNodeInt("sample", "seed", 42));
|
|
CHECK(graph.setNodeSpatial("exclude", &hole));
|
|
CHECK(graph.setNodeInt("exclude", "invert", 1));
|
|
CHECK(graph.setNodeFloat("prune", "radius", 1.f));
|
|
CHECK(graph.validate());
|
|
|
|
PointSet* first = graph.execute("prune");
|
|
REQUIRE(bool(first));
|
|
CHECK_EQ(first->getCount(), 8);
|
|
CHECK(graph.getMetricCount() >= 3);
|
|
PointSet* debug = graph.getNodeOutput("sample");
|
|
REQUIRE(bool(debug));
|
|
CHECK_EQ(debug->getCount(), 9);
|
|
|
|
PointSet* second = graph.execute("prune");
|
|
REQUIRE(bool(second));
|
|
CHECK_EQ(second->getCount(), 8);
|
|
CHECK_EQ(graph.getCacheHitCount(), 1);
|
|
CHECK(graph.isMetricCacheHit(0));
|
|
|
|
delete second;
|
|
delete debug;
|
|
delete first;
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.rejectsCyclesAndBadInputs") {
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("a", "self.prune"));
|
|
CHECK(graph.addNode("b", "jitter"));
|
|
CHECK(graph.connect("a", "b"));
|
|
CHECK(graph.connect("b", "a"));
|
|
CHECK(!graph.validate());
|
|
CHECK(graph.getError().find("cycle") != std::string::npos);
|
|
CHECK(!graph.execute("a"));
|
|
CHECK(graph.getError().find("cycle") != std::string::npos);
|
|
CHECK(!graph.addNode("unknown", "not-an-operation"));
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.filtersProjectedTerrainSlope") {
|
|
PointSet source;
|
|
source.add(0.f, 0.f, 0.f);
|
|
source.add(1.f, 0.f, 0.f);
|
|
source.setNormal(0, 0.f, 1.f, 0.f);
|
|
source.setNormal(1, 1.f, 0.f, 0.f);
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("slope", "filter.slope"));
|
|
CHECK(graph.setNodePoints("source", &source));
|
|
CHECK(graph.connect("source", "slope"));
|
|
CHECK(graph.setNodeFloat("slope", "minDegrees", 0.f));
|
|
CHECK(graph.setNodeFloat("slope", "maxDegrees", 45.f));
|
|
CHECK(graph.validate());
|
|
std::unique_ptr<PointSet> result(graph.execute("slope"));
|
|
REQUIRE(bool(result));
|
|
CHECK_EQ(result->getCount(), 1);
|
|
CHECK_EQ(result->getNormalY(0), 1.f);
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.supportsBranchesAndNestedGraphs") {
|
|
PointSet source;
|
|
source.add(1.f, 0.f, 0.f);
|
|
|
|
PointGraph nested;
|
|
CHECK(nested.addNode("in", "input"));
|
|
CHECK(nested.addNode("move", "transform"));
|
|
CHECK(nested.setNodePoints("in", &source));
|
|
CHECK(nested.connect("in", "move"));
|
|
CHECK(nested.setNodeFloat("move", "x", 5.f));
|
|
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("nested", "subgraph"));
|
|
CHECK(graph.addNode("fallback", "transform"));
|
|
CHECK(graph.addNode("choose", "branch"));
|
|
CHECK(graph.setNodePoints("source", &source));
|
|
CHECK(graph.connect("source", "nested"));
|
|
CHECK(graph.setNodeSubgraph("nested", &nested, "in", "move"));
|
|
CHECK(graph.connect("source", "fallback"));
|
|
CHECK(graph.setNodeFloat("fallback", "x", -5.f));
|
|
CHECK(graph.connect("nested", "choose", 0));
|
|
CHECK(graph.connect("fallback", "choose", 1));
|
|
CHECK(graph.setNodeInt("choose", "condition", 1));
|
|
|
|
PointSet* result = graph.execute("choose");
|
|
REQUIRE(bool(result));
|
|
CHECK_EQ(result->getCount(), 1);
|
|
CHECK_EQ(result->getX(0), 6.f);
|
|
delete result;
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.reflectsOperationsForEditors") {
|
|
CHECK(PointGraph::getOperationCount() >= 15);
|
|
CHECK_EQ(PointGraph::getOperationInputCount("merge"), 2);
|
|
CHECK_EQ(PointGraph::getOperationInputCount("spatial.sample"), 0);
|
|
CHECK_EQ(PointGraph::getOperationParamCount("transform"), 7);
|
|
CHECK_EQ(PointGraph::getOperationParamKey("spatial.sample", 0), std::string("spacing"));
|
|
CHECK_EQ(PointGraph::getOperationParamKind("spatial.sample", 1), std::string("int"));
|
|
CHECK_EQ(PointGraph::getOperationParamDefault("branch", 0), std::string("false"));
|
|
CHECK_EQ(PointGraph::getOperationInputCount("filter.slope"), 1);
|
|
CHECK_EQ(PointGraph::getOperationInputCount("missing"), -1);
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.enforcesDeclaredPinsAndValidatesExternalSubgraphInput") {
|
|
PointGraph nested;
|
|
CHECK(nested.addNode("in", "input"));
|
|
CHECK(nested.addNode("move", "transform"));
|
|
CHECK(nested.connect("in", "move"));
|
|
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("nested", "subgraph"));
|
|
CHECK(graph.connect("source", "nested"));
|
|
CHECK(!graph.connect("source", "nested", 1));
|
|
CHECK(!graph.connect("source", "source", 0));
|
|
CHECK(graph.setNodeSubgraph("nested", &nested, "in", "move"));
|
|
|
|
PointSet input;
|
|
input.add(1.f, 2.f, 3.f);
|
|
CHECK(graph.setNodePoints("source", &input));
|
|
CHECK(graph.validate());
|
|
PointSet* output = graph.execute("nested");
|
|
REQUIRE(bool(output));
|
|
CHECK_EQ(output->getCount(), 1);
|
|
delete output;
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.definitionRoundTripsNestedTopologyAndParams") {
|
|
PointSet source;
|
|
source.add(1.f, 0.f, 0.f);
|
|
|
|
PointGraph nested;
|
|
nested.addNode("in", "input");
|
|
nested.addNode("move", "transform");
|
|
nested.setNodePoints("in", &source);
|
|
nested.connect("in", "move");
|
|
nested.setNodeFloat("move", "x", 5.f);
|
|
|
|
PointGraph original;
|
|
original.addNode("source", "input");
|
|
original.addNode("nested", "subgraph");
|
|
original.addNode("tag", "attribute.set.string");
|
|
original.setNodePoints("source", &source);
|
|
original.connect("source", "nested");
|
|
original.setNodeSubgraph("nested", &nested, "in", "move");
|
|
original.connect("nested", "tag");
|
|
original.setNodeString("tag", "attribute", "asset");
|
|
original.setNodeString("tag", "value", "oak \"old\"");
|
|
|
|
const std::string definition = original.serializeDefinition();
|
|
CHECK(definition.find("EVPCG_POINT_GRAPH 1") == 0);
|
|
PointGraph loaded;
|
|
CHECK(loaded.deserializeDefinition(definition));
|
|
CHECK_EQ(loaded.getNodeCount(), 3);
|
|
CHECK_EQ(loaded.getInputNode("tag", 0), std::string("nested"));
|
|
CHECK(loaded.setNodePoints("source", &source));
|
|
PointSet* result = loaded.execute("tag");
|
|
REQUIRE(bool(result));
|
|
CHECK_EQ(result->getX(0), 6.f);
|
|
CHECK_EQ(result->getStringAttribute(0, "asset", ""), std::string("oak \"old\""));
|
|
delete result;
|
|
|
|
CHECK(!loaded.deserializeDefinition("broken"));
|
|
CHECK(loaded.hasNode("source"));
|
|
CHECK(!loaded.deserializeDefinition(
|
|
"EVPCG_POINT_GRAPH 1\nNODE \"move\" \"transform\"\nS \"move\" \"x\" \"bad\"\nEND\n"));
|
|
CHECK(loaded.hasNode("source"));
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.invalidatesOnlyChangedNodeDescendants") {
|
|
PointSet points;
|
|
points.add(1.f, 0.f, 0.f);
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("left", "transform"));
|
|
CHECK(graph.addNode("right", "transform"));
|
|
CHECK(graph.connect("source", "left"));
|
|
CHECK(graph.connect("source", "right"));
|
|
CHECK(graph.setNodePoints("source", &points));
|
|
PointSet* left = graph.execute("left");
|
|
PointSet* right = graph.execute("right");
|
|
REQUIRE(bool(left));
|
|
REQUIRE(bool(right));
|
|
delete right;
|
|
delete left;
|
|
|
|
const uint64_t revision = graph.getRevision();
|
|
CHECK(graph.setNodeFloat("left", "x", 5.f));
|
|
CHECK(graph.getRevision() > revision);
|
|
PointSet* sourceCache = graph.getNodeOutput("source");
|
|
PointSet* rightCache = graph.getNodeOutput("right");
|
|
CHECK(bool(sourceCache));
|
|
CHECK(bool(rightCache));
|
|
CHECK(!graph.getNodeOutput("left"));
|
|
delete rightCache;
|
|
delete sourceCache;
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.definitionIsStableAcrossParameterInsertionOrder") {
|
|
PointGraph first;
|
|
PointGraph second;
|
|
CHECK(first.addNode("move", "transform"));
|
|
CHECK(second.addNode("move", "transform"));
|
|
CHECK(first.addNode("tag", "attribute.set.string"));
|
|
CHECK(second.addNode("tag", "attribute.set.string"));
|
|
CHECK(first.setNodeFloat("move", "z", 3.f));
|
|
CHECK(first.setNodeFloat("move", "x", 1.f));
|
|
CHECK(first.setNodeString("tag", "value", "oak"));
|
|
CHECK(first.setNodeString("tag", "attribute", "asset"));
|
|
CHECK(second.setNodeString("tag", "attribute", "asset"));
|
|
CHECK(second.setNodeString("tag", "value", "oak"));
|
|
CHECK(second.setNodeFloat("move", "x", 1.f));
|
|
CHECK(second.setNodeFloat("move", "z", 3.f));
|
|
CHECK_EQ(first.serializeDefinition(), second.serializeDefinition());
|
|
CHECK(!first.setNodeString("move", "typo", "rejected"));
|
|
CHECK(!first.setNodeInt("move", "x", 1));
|
|
CHECK(!first.setNodeFloat("tag", "value", 1.f));
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.exposesTypedInstanceParameters") {
|
|
PointSet points;
|
|
points.add(1.f, 0.f, 0.f);
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("move", "transform"));
|
|
CHECK(graph.connect("source", "move"));
|
|
CHECK(graph.setNodePoints("source", &points));
|
|
CHECK(graph.setNodeFloat("move", "x", 2.f));
|
|
CHECK(graph.exposeParameter("offset", "move", "x"));
|
|
CHECK_EQ(graph.getParameterCount(), 1);
|
|
CHECK_EQ(graph.getParameterName(0), std::string("offset"));
|
|
CHECK_EQ(graph.getParameterKind("offset"), std::string("float"));
|
|
CHECK_EQ(graph.getParameterFloat("offset", -1.f), 2.f);
|
|
CHECK(!graph.exposeParameter("alias", "move", "x"));
|
|
CHECK(!graph.exposeParameter("bad", "move", "missing"));
|
|
CHECK(!graph.setParameterInt("offset", 7));
|
|
|
|
PointSet* result = graph.execute("move");
|
|
REQUIRE(bool(result));
|
|
CHECK_EQ(result->getX(0), 3.f);
|
|
delete result;
|
|
|
|
CHECK(graph.setParameterFloat("offset", 5.f));
|
|
CHECK(graph.hasParameterOverride("offset"));
|
|
CHECK_EQ(graph.getParameterFloat("offset", -1.f), 5.f);
|
|
result = graph.execute("move");
|
|
REQUIRE(bool(result));
|
|
CHECK_EQ(result->getX(0), 6.f);
|
|
delete result;
|
|
|
|
const std::string definition = graph.serializeDefinition();
|
|
PointGraph loaded;
|
|
CHECK(loaded.deserializeDefinition(definition));
|
|
CHECK_EQ(loaded.getParameterKind("offset"), std::string("float"));
|
|
CHECK(!loaded.hasParameterOverride("offset"));
|
|
CHECK_EQ(loaded.getParameterFloat("offset", -1.f), 2.f);
|
|
CHECK(loaded.setNodePoints("source", &points));
|
|
result = loaded.execute("move");
|
|
REQUIRE(bool(result));
|
|
CHECK_EQ(result->getX(0), 3.f);
|
|
delete result;
|
|
|
|
CHECK(graph.clearParameterOverride("offset"));
|
|
CHECK(!graph.hasParameterOverride("offset"));
|
|
CHECK_EQ(graph.getParameterFloat("offset", -1.f), 2.f);
|
|
CHECK(graph.removeNode("move"));
|
|
CHECK_EQ(graph.getParameterCount(), 0);
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.serializesExposedParametersDeterministically") {
|
|
PointGraph first;
|
|
PointGraph second;
|
|
for (PointGraph* graph : {&first, &second}) {
|
|
CHECK(graph->addNode("move", "transform"));
|
|
CHECK(graph->addNode("tag", "attribute.set.string"));
|
|
}
|
|
CHECK(first.exposeParameter("zOffset", "move", "z"));
|
|
CHECK(first.exposeParameter("asset", "tag", "value"));
|
|
CHECK(second.exposeParameter("asset", "tag", "value"));
|
|
CHECK(second.exposeParameter("zOffset", "move", "z"));
|
|
CHECK_EQ(first.serializeDefinition(), second.serializeDefinition());
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.cooperativelyBudgetsAndCancelsExecution") {
|
|
PointSet points;
|
|
points.add(0.f, 0.f, 0.f);
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("first", "transform"));
|
|
CHECK(graph.addNode("second", "transform"));
|
|
CHECK(graph.connect("source", "first"));
|
|
CHECK(graph.connect("first", "second"));
|
|
CHECK(graph.setNodePoints("source", &points));
|
|
graph.setExecutionNodeBudget(2);
|
|
CHECK_EQ(graph.getExecutionNodeBudget(), 2);
|
|
CHECK(!graph.execute("second"));
|
|
CHECK(graph.wasCancelled());
|
|
CHECK(graph.getError().find("budget exceeded") != std::string::npos);
|
|
PointSet* partial = graph.getNodeOutput("first");
|
|
REQUIRE(bool(partial));
|
|
delete partial;
|
|
|
|
graph.setExecutionNodeBudget(3);
|
|
PointSet* completed = graph.execute("second");
|
|
REQUIRE(bool(completed));
|
|
CHECK(!graph.wasCancelled());
|
|
CHECK(graph.getCacheHitCount() > 0);
|
|
delete completed;
|
|
|
|
graph.requestCancel();
|
|
CHECK(!graph.execute("second"));
|
|
CHECK(graph.wasCancelled());
|
|
CHECK(graph.getError().find("cancelled") != std::string::npos);
|
|
graph.resetCancellation();
|
|
CHECK(!graph.wasCancelled());
|
|
completed = graph.execute("second");
|
|
REQUIRE(bool(completed));
|
|
delete completed;
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.copiesSourcePointsAcrossTargetsWithBoundedOutput") {
|
|
PointSet source;
|
|
const int sourcePoint = source.add(1.f, 2.f, 0.f);
|
|
source.setPointSeed(sourcePoint, 11);
|
|
source.setFloatAttribute(sourcePoint, "source", 2.f);
|
|
source.setStringAttribute(sourcePoint, "shared", "source");
|
|
PointSet targets;
|
|
const int firstTarget = targets.add(10.f, 20.f, 30.f);
|
|
targets.setYaw(firstTarget, 90.f);
|
|
targets.setScale(firstTarget, 2.f, 3.f, 4.f);
|
|
targets.setDensity(firstTarget, 0.5f);
|
|
targets.setPointSeed(firstTarget, 101);
|
|
targets.setFloatAttribute(firstTarget, "target", 3.f);
|
|
targets.setStringAttribute(firstTarget, "shared", "target");
|
|
targets.add(-10.f, 0.f, 0.f);
|
|
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("targets", "input"));
|
|
CHECK(graph.addNode("copies", "copy.points"));
|
|
CHECK(graph.setNodePoints("source", &source));
|
|
CHECK(graph.setNodePoints("targets", &targets));
|
|
CHECK(graph.connect("source", "copies", 0));
|
|
CHECK(graph.connect("targets", "copies", 1));
|
|
CHECK(graph.setNodeInt("copies", "maxPoints", 2));
|
|
PointSet* result = graph.execute("copies");
|
|
REQUIRE(bool(result));
|
|
CHECK_EQ(result->getCount(), 2);
|
|
CHECK(std::abs(result->getX(0) - 10.f) < 0.0001f);
|
|
CHECK_EQ(result->getY(0), 26.f);
|
|
CHECK(std::abs(result->getZ(0) - 32.f) < 0.0001f);
|
|
CHECK_EQ(result->getYaw(0), 90.f);
|
|
CHECK_EQ(result->getScaleX(0), 2.f);
|
|
CHECK_EQ(result->getDensity(0), 0.5f);
|
|
CHECK_EQ(result->getFloatAttribute(0, "target", -1.f), 3.f);
|
|
CHECK_EQ(result->getFloatAttribute(0, "source", -1.f), 2.f);
|
|
CHECK_EQ(result->getStringAttribute(0, "shared", ""), std::string("source"));
|
|
delete result;
|
|
|
|
CHECK(graph.setNodeInt("copies", "maxPoints", 1));
|
|
CHECK(!graph.execute("copies"));
|
|
CHECK(graph.getError().find("maxPoints") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.validatesEveryRequiredInput") {
|
|
PointSet points;
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("copies", "copy.points"));
|
|
CHECK(graph.setNodePoints("source", &points));
|
|
CHECK(graph.connect("source", "copies", 0));
|
|
CHECK(!graph.validate());
|
|
CHECK(graph.getError().find("input 1") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.remapsDensityAndComputesFloatMetadata") {
|
|
PointSet points;
|
|
const int first = points.add(0.f, 0.f, 0.f);
|
|
const int second = points.add(1.f, 0.f, 0.f);
|
|
points.setDensity(first, -1.f);
|
|
points.setDensity(second, 0.5f);
|
|
points.setFloatAttribute(first, "age", 3.f);
|
|
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.addNode("density", "density.remap"));
|
|
CHECK(graph.addNode("math", "attribute.math.float"));
|
|
CHECK(graph.setNodePoints("source", &points));
|
|
CHECK(graph.connect("source", "density"));
|
|
CHECK(graph.connect("density", "math"));
|
|
CHECK(graph.setNodeFloat("density", "inputMin", 0.f));
|
|
CHECK(graph.setNodeFloat("density", "inputMax", 1.f));
|
|
CHECK(graph.setNodeFloat("density", "outputMin", 10.f));
|
|
CHECK(graph.setNodeFloat("density", "outputMax", 20.f));
|
|
CHECK(graph.setNodeString("math", "attribute", "age"));
|
|
CHECK(graph.setNodeString("math", "outputAttribute", "score"));
|
|
CHECK(graph.setNodeString("math", "operation", "multiply"));
|
|
CHECK(graph.setNodeFloat("math", "operand", 2.f));
|
|
CHECK(graph.setNodeFloat("math", "defaultValue", 4.f));
|
|
PointSet* result = graph.execute("math");
|
|
REQUIRE(bool(result));
|
|
CHECK_EQ(result->getDensity(0), 10.f);
|
|
CHECK_EQ(result->getDensity(1), 15.f);
|
|
CHECK_EQ(result->getFloatAttribute(0, "score", -1.f), 6.f);
|
|
CHECK_EQ(result->getFloatAttribute(1, "score", -1.f), 8.f);
|
|
delete result;
|
|
|
|
CHECK(graph.setNodeString("math", "operation", "divide"));
|
|
CHECK(graph.setNodeFloat("math", "operand", 0.f));
|
|
CHECK(!graph.execute("math"));
|
|
CHECK(graph.getError().find("divide by zero") != std::string::npos);
|
|
CHECK(graph.setNodeFloat("density", "inputMax", 0.f));
|
|
CHECK(!graph.execute("density"));
|
|
CHECK(graph.getError().find("non-zero input range") != std::string::npos);
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.cachesSpatialMetricsForDebugVisualization") {
|
|
PointSet points;
|
|
const int first = points.add(-2.f, 3.f, 4.f);
|
|
const int second = points.add(6.f, -1.f, 8.f);
|
|
points.setDensity(first, 0.25f);
|
|
points.setDensity(second, 0.75f);
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("source", "input"));
|
|
CHECK(graph.setNodePoints("source", &points));
|
|
PointSet* output = graph.execute("source");
|
|
REQUIRE(bool(output));
|
|
delete output;
|
|
CHECK_EQ(graph.getMetricCount(), 1);
|
|
CHECK_EQ(graph.getMetricMinX(0), -2.f);
|
|
CHECK_EQ(graph.getMetricMinY(0), -1.f);
|
|
CHECK_EQ(graph.getMetricMinZ(0), 4.f);
|
|
CHECK_EQ(graph.getMetricMaxX(0), 6.f);
|
|
CHECK_EQ(graph.getMetricMaxY(0), 3.f);
|
|
CHECK_EQ(graph.getMetricMaxZ(0), 8.f);
|
|
CHECK_EQ(graph.getMetricAverageDensity(0), 0.5f);
|
|
|
|
output = graph.execute("source");
|
|
REQUIRE(bool(output));
|
|
delete output;
|
|
CHECK(graph.isMetricCacheHit(0));
|
|
CHECK_EQ(graph.getMetricMinX(0), -2.f);
|
|
CHECK_EQ(graph.getMetricAverageDensity(0), 0.5f);
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.instantiatesIsolatedRuntimeState") {
|
|
PointSet points;
|
|
points.add(1.f, 0.f, 0.f);
|
|
PointGraph asset;
|
|
CHECK(asset.addNode("source", "input"));
|
|
CHECK(asset.addNode("move", "transform"));
|
|
CHECK(asset.connect("source", "move"));
|
|
CHECK(asset.setNodePoints("source", &points));
|
|
CHECK(asset.setNodeFloat("move", "x", 2.f));
|
|
CHECK(asset.exposeParameter("offset", "move", "x"));
|
|
CHECK(asset.setParameterFloat("offset", 9.f));
|
|
|
|
PointGraph* instance = asset.instantiate();
|
|
REQUIRE(bool(instance));
|
|
CHECK(!instance->hasParameterOverride("offset"));
|
|
CHECK_EQ(instance->getParameterFloat("offset", -1.f), 2.f);
|
|
CHECK(!instance->execute("move"));
|
|
CHECK(instance->getError().find("no points") != std::string::npos);
|
|
CHECK(instance->setNodePoints("source", &points));
|
|
CHECK(instance->setParameterFloat("offset", 4.f));
|
|
PointSet* output = instance->execute("move");
|
|
REQUIRE(bool(output));
|
|
CHECK_EQ(output->getX(0), 5.f);
|
|
delete output;
|
|
CHECK_EQ(asset.getParameterFloat("offset", -1.f), 9.f);
|
|
delete instance;
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.generatesBiomeThroughReflectedNode") {
|
|
SpatialData domain = SpatialData::box(0.f, 0.f, 0.f, 4.f, 0.f, 4.f);
|
|
BiomeRules rules;
|
|
CHECK(rules.addLayer("forest", &domain, 1, 1.f));
|
|
CHECK(rules.addAsset("forest", "oak", 1.f, 1.f, 1.f, false));
|
|
|
|
PointGraph graph;
|
|
CHECK(graph.addNode("biome", "biome.generate"));
|
|
CHECK(graph.setNodeSpatial("biome", &domain));
|
|
CHECK(graph.setNodeBiomeRules("biome", &rules));
|
|
CHECK(graph.setNodeFloat("biome", "spacing", 2.f));
|
|
CHECK(graph.setNodeInt("biome", "seed", 42));
|
|
CHECK(graph.validate());
|
|
PointSet* output = graph.execute("biome");
|
|
REQUIRE(bool(output));
|
|
CHECK_EQ(output->getCount(), 9);
|
|
CHECK_EQ(output->getStringAttribute(0, "biome", ""), std::string("forest"));
|
|
CHECK_EQ(output->getStringAttribute(0, "asset", ""), std::string("oak"));
|
|
delete output;
|
|
}
|
|
|
|
TEST_CASE("procgen.pointGraph.expandsShapeGrammarAndRebindsExternalAssets") {
|
|
PointSet controlPoints;
|
|
controlPoints.add(0.f, 0.f, 0.f);
|
|
controlPoints.add(6.f, 0.f, 0.f);
|
|
ShapeGrammar grammar;
|
|
CHECK(grammar.addModule("A", "wall", 2.f, 1.f));
|
|
|
|
PointGraph asset;
|
|
CHECK(asset.addNode("path", "input"));
|
|
CHECK(asset.addNode("facade", "grammar.generate"));
|
|
CHECK(asset.connect("path", "facade"));
|
|
CHECK(asset.setNodePoints("path", &controlPoints));
|
|
CHECK(asset.setNodeShapeGrammar("facade", &grammar));
|
|
CHECK(asset.setNodeString("facade", "grammar", "A+"));
|
|
CHECK(asset.validate());
|
|
PointSet* output = asset.execute("facade");
|
|
REQUIRE(bool(output));
|
|
CHECK_EQ(output->getCount(), 3);
|
|
CHECK_EQ(output->getStringAttribute(0, "asset", ""), std::string("wall"));
|
|
delete output;
|
|
|
|
PointGraph* instance = asset.instantiate();
|
|
REQUIRE(bool(instance));
|
|
CHECK(instance->setNodePoints("path", &controlPoints));
|
|
CHECK(!instance->validate());
|
|
CHECK(instance->getError().find("shape grammar") != std::string::npos);
|
|
CHECK(instance->setNodeShapeGrammar("facade", &grammar));
|
|
CHECK(instance->validate());
|
|
output = instance->execute("facade");
|
|
REQUIRE(bool(output));
|
|
CHECK_EQ(output->getCount(), 3);
|
|
delete output;
|
|
delete instance;
|
|
}
|