#include "camera/editing/CameraTarget.h" #include "zeroerr/assert.h" #include "zeroerr/unittest.h" using namespace eve::camera_editing; using namespace eve::editing; namespace { void apply(CameraDocumentTarget& target, EditorResult operation) { REQUIRE(operation.ok()); REQUIRE(target.applyDomainOperation(operation.value()).ok()); } SelectionSnapshot select(const CameraDocumentTarget& target, const char* id) { SelectionSnapshot selection; selection.channel = "camera"; selection.items.push_back({SelectionDomain::Asset, TargetId(target.targetId()), StableId(id), "camera.rig"}); return selection; } CameraRigValue rig(const char* id, const char* mode = "follow") { CameraRigValue value; value.id = ObjectId(id); value.name = id; value.mode = mode; return value; } } TEST_CASE("editor.camera.rigs_are_property_editable_reversible_and_persistent") { CameraDocumentTarget target("shot"); apply(target, target.makeCreateRig(rig("hero", "orbit"))); const auto selection = select(target, "hero"); auto edit = target.makeSet(selection, PropertyPath("rig.fov"), 72.0, PropertySetMode::Absolute); REQUIRE(edit.ok()); REQUIRE(target.applyDomainOperation(edit.value()).ok()); CHECK_EQ(*target.read(selection, PropertyPath("rig.fov")).value.getIf(), 72.0); DomainOperation undo = edit.value(); undo.payload = edit.value().inverse; REQUIRE(target.applyDomainOperation(undo).ok()); CHECK_EQ(*target.read(selection, PropertyPath("rig.fov")).value.getIf(), 60.0); CameraDocumentTarget restored("copy"); REQUIRE(restored.loadSnapshot(target.snapshotValue()).ok()); CHECK_EQ(restored.rigs().front().id.value(), std::string("hero")); } TEST_CASE("editor.camera.timeline_has_stable_keys_reference_safety_and_scrub") { CameraDocumentTarget target("shot"); auto hero = rig("hero", "follow"); hero.offset = {0, 3, 8}; apply(target, target.makeCreateRig(hero)); CameraTimelineKeyValue cut; cut.id=ObjectId("cut-1"); cut.kind="cut"; cut.time=1; cut.rig=ObjectId("hero"); cut.blend=.5f; apply(target, target.makeCreateKey(cut)); CameraTimelineKeyValue fov0; fov0.id=ObjectId("fov-0"); fov0.kind="float"; fov0.time=1; fov0.property="fov"; fov0.value=60; apply(target, target.makeCreateKey(fov0)); CameraTimelineKeyValue fov; fov.id=ObjectId("fov-1"); fov.kind="float"; fov.time=3; fov.property="fov"; fov.value=80; apply(target, target.makeCreateKey(fov)); CHECK_EQ(static_cast(target.makeDeleteRig(ObjectId("hero")).code()), static_cast(EditorStatus::Conflict)); CameraPreview preview; auto pose=preview.evaluate(target,2); REQUIRE(pose.ok()); CHECK_EQ(pose.value().rig.value(),std::string("hero")); CHECK_EQ(pose.value().fov,70.f); auto overlay=preview.gizmo(target,2); REQUIRE(overlay.ok()); CHECK_EQ(overlay.value().primitives.size(),static_cast(3)); } TEST_CASE("editor.camera.invalid_snapshot_and_runtime_generation_are_atomic") { CameraDocumentTarget target("shot"); apply(target,target.makeCreateRig(rig("hero"))); const auto before=target.snapshotValue(); EditorValue invalid=before; auto* root=invalid.getIf(); auto* content=(*root)["content"].getIf(); auto* rigs=(*content)["rigs"].getIf(); auto* first=(*rigs)[0].getIf(); (*first)["fov"]=200.0; CHECK_EQ(static_cast(target.loadSnapshot(invalid).code()),static_cast(EditorStatus::Rejected)); CHECK_EQ(target.snapshotValue(),before); CameraDocumentRuntime runtime; REQUIRE(runtime.publish(target,nullptr).ok()); auto* generation=runtime.controller(); const auto revision=runtime.revision(); CameraDocumentTarget empty("empty"); REQUIRE(runtime.publish(empty,nullptr).ok()); CHECK(runtime.controller()!=generation); CHECK(runtime.revision()!=revision); }