载入中...
搜索中...
未找到
SceneSnapshot.cpp
浏览该文件的文档.
2
3#include <cmath>
4#include <limits>
5#include <set>
6
7namespace eve::scene_editing {
8
9Result<std::map<ObjectId, SceneObjectSnapshot>> SceneTargetBase::parseSnapshot(const EditorValue& value) {
10 using Objects = std::map<ObjectId, SceneObjectSnapshot>;
11 const auto reject = [](const char* message) {
12 return editing::failed<Objects>(EditorStatus::Rejected, RuleId("scene.snapshot.invalid"), message);
13 };
14 const auto* root = value.getIf<EditorValue::Object>();
15 if (!root || !root->contains("schemaVersion") || !root->contains("objects") ||
16 root->size() != (root->contains("schemaId") ? 3u : 2u))
17 return reject("Scene snapshot requires schemaId, schemaVersion and objects");
18 if (root->contains("schemaId")) {
19 const auto* schema = root->at("schemaId").getIf<std::string>();
20 if (!schema || *schema != "eve.scene.hierarchy") return reject("Unknown scene schema id");
21 }
22 const auto* version = root->at("schemaVersion").getIf<std::int64_t>();
23 const auto* entries = root->at("objects").getIf<EditorValue::Array>();
24 if (!version || *version != 1 || !entries) return reject("Unsupported scene snapshot schema");
25 Objects objects;
26 for (const auto& entry : *entries) {
27 const auto* fields = entry.getIf<EditorValue::Object>();
28 if (!fields || fields->size() != 4 || !fields->contains("transform"))
29 return reject("Scene object requires id, parent, name and transform");
30 const auto* transform = fields->at("transform").getIf<EditorValue::Object>();
31 if (!transform || transform->size() != 9) return reject("Scene transform requires all nine TRS fields");
32 // JSON parsers may decode integral floating point values as integers.
33 EditorValue normalized = entry;
34 auto& normalizedTransform =
35 *normalized.getIf<EditorValue::Object>()->at("transform").getIf<EditorValue::Object>();
36 for (const auto* key : {"x", "y", "z", "rotationX", "rotationY", "rotationZ", "scaleX", "scaleY", "scaleZ"}) {
37 const auto found = transform->find(key);
38 if (found == transform->end()) return reject("Unknown or missing transform field");
39 double number;
40 if (const auto* real = found->second.getIf<double>())
41 number = *real;
42 else if (const auto* integer = found->second.getIf<std::int64_t>())
43 number = static_cast<double>(*integer);
44 else
45 return reject("Transform fields must be numbers");
46 if (!std::isfinite(number) || std::abs(number) > std::numeric_limits<float>::max())
47 return reject("Transform must be finite and representable by the scene runtime");
48 if (std::string_view(key).starts_with("scale") && static_cast<float>(number) == 0.f)
49 return reject("Scene scale must be nonzero");
50 normalizedTransform[key] = number;
51 }
52 auto parsed = parseObject(normalized);
53 if (!parsed.ok()) return Result<Objects>::failure(parsed.status());
54 auto object = std::move(parsed.value());
55 if (object.id.empty() || object.name.empty() || objects.contains(object.id))
56 return reject("Object ids must be unique and names nonempty");
57 objects.emplace(object.id, std::move(object));
58 }
59 for (const auto& [id, object] : objects) {
60 std::set<ObjectId> visited{id};
61 auto parent = object.parent;
62 while (!parent.empty()) {
63 if (!objects.contains(parent)) return reject("Scene parent does not exist");
64 if (!visited.insert(parent).second) return reject("Scene hierarchy contains a cycle");
65 parent = objects.at(parent).parent;
66 }
67 }
68 return editing::applied<Objects>(std::move(objects));
69}
70
72 auto validated = parseSnapshot(snapshot);
73 if (!validated.ok()) return Result<DomainOperation>::failure(validated.status());
75 operation.type = operation.inverseType = "scene.snapshot.restore.v1";
76 operation.target = targetId();
77 operation.payload = snapshot;
78 operation.inverse = snapshotValue();
79 operation.hasInverse = true;
80 for (const auto& [id, object] : objects_) operation.affectedObjects.push_back({targetId(), id.value(), 0});
81 for (const auto& [id, object] : validated.value())
82 if (!objects_.contains(id)) operation.affectedObjects.push_back({targetId(), id.value(), 0});
83 return editing::applied<DomainOperation>(std::move(operation));
84}
85} // namespace eve::scene_editing
ActionParameterOperation operation
double value
int root
Definition AnimSmr.cpp:119
std::unordered_map< std::string, QuestRuntime > entries
std::string message
std::uint32_t key
std::int32_t parent
std::string name
std::vector< std::string > fields
Definition PlayHost.cpp:111
std::string id
Definition PlayHost.cpp:108
std::uint32_t targetId
double number
bool found
std::size_t at
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Result< DomainOperation > makeRestore(const EditorValue &snapshot) const
Plan atomic replacement of hierarchy/TRS from a versioned snapshot.
TargetId targetId() const override
Target id.
Definition SceneTarget.h:94
EditorValue snapshotValue() const override
Capture deterministic scene content for document persistence.
editing::Value EditorValue
DomainOperation public API.