载入中...
搜索中...
未找到
EvpackSceneTemplateLoader.cpp
浏览该文件的文档.
2
3#include "common/Value.h"
5
6#include <algorithm>
7#include <array>
8#include <cctype>
9#include <cmath>
10#include <functional>
11#include <limits>
12#include <map>
13#include <set>
14
16namespace {
17
18const Value* field(const Value::Object& object, std::string_view name) {
19 const auto found = object.find(std::string(name));
20 return found == object.end() ? nullptr : &found->second;
21}
22
23bool number(const Value& value, float& result) {
24 if (!value.isNumeric()) return false;
25 const double decoded = value.isInt64() ? double(value.asInt()) : value.asDouble();
26 if (!std::isfinite(decoded) || decoded < -std::numeric_limits<float>::max() ||
27 decoded > std::numeric_limits<float>::max())
28 return false;
29 result = static_cast<float>(decoded);
30 return true;
31}
32
33bool validGuid(std::string_view value) {
34 return value.size() == 32 && std::all_of(value.begin(), value.end(), [](unsigned char character) {
35 return std::isxdigit(character) != 0;
36 });
37}
38
39template <std::size_t N>
40bool vector(const Value* value, std::array<float, N>& result) {
41 const auto* values = value ? value->getIf<Value::Array>() : nullptr;
42 if (!values || values->size() != N) return false;
43 for (std::size_t i = 0; i < N; ++i)
44 if (!number((*values)[i], result[i])) return false;
45 return true;
46}
47
48struct FlatNode {
49 std::uint64_t sourceId = 0;
50 std::uint64_t parentId = 0;
51 scene::NodeDesc desc;
52};
53
54} // namespace
55
57 const AssetRef& sceneTemplate, const asset::EvpackCapabilities& capabilities,
58 const SceneTemplateLoadLimits& limits) const {
59 auto payload = reader_.read(sceneTemplate, "eve.scene-template/3", capabilities, limits.maximumDecodedBytes);
60 // N-1 packs remain readable with legacy all-on shadow defaults.
61 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
62 payload = reader_.read(sceneTemplate, "eve.scene-template/2", capabilities, limits.maximumDecodedBytes);
64 const asset::RuntimeAssetChunk* definition = nullptr;
65 for (const auto& chunk : payload.value().chunks) {
66 if (chunk.kind != asset::EvpackChunkKind::Definition) continue;
67 if (definition)
69 DiagnosticCode::Conflict, "scene template has duplicate definitions", {}, {}, "asset.scene.template"));
70 definition = &chunk;
71 }
72 if (!definition)
74 DiagnosticCode::NotFound, "scene template definition is missing", {}, {}, "asset.scene.template"));
75 asset::RuntimeDefinitionLimits definitionLimits;
76 definitionLimits.maximumBytes = limits.maximumDecodedBytes;
77 auto metadata = asset::decodeRuntimeDefinition(definition->bytes, definitionLimits);
78 if (!metadata) return Result<LoadedSceneTemplate>::failure(metadata.status());
79 const auto* root = metadata.value().getIf<Value::Object>();
80 const Value* schema = root ? field(*root, "schema") : nullptr;
81 const Value* version = root ? field(*root, "schemaVersion") : nullptr;
82 const Value* sourceGuid = root ? field(*root, "sourceGuid") : nullptr;
83 const Value* coordinate = root ? field(*root, "coordinateSystem") : nullptr;
84 const Value* nodesValue = root ? field(*root, "nodes") : nullptr;
85 const auto* nodes = nodesValue ? nodesValue->getIf<Value::Array>() : nullptr;
86 if (!schema || !schema->isString() || schema->asString() != "eve.scene-template" || !version ||
87 !version->isInt64() || version->asInt() != std::int64_t(payload.value().schemaVersion.value()) ||
88 (sourceGuid && (!sourceGuid->isString() || !validGuid(sourceGuid->asString()))) || !coordinate ||
89 !coordinate->isString() || coordinate->asString() != "right-handed-x-right-y-up-minus-z-forward" || !nodes ||
90 nodes->empty() || nodes->size() > limits.maximumNodes)
92 DiagnosticCode::ParseError, "scene-template metadata is invalid", {}, {}, "asset.scene.template"));
93 std::map<std::uint64_t, FlatNode> flat;
94 std::set<SceneObjectId> objectIds;
95 constexpr float radiansToDegrees = 57.29577951308232f;
96 for (std::size_t i = 0; i < nodes->size(); ++i) {
97 const auto* node = (*nodes)[i].getIf<Value::Object>();
98 const Value* sourceId = node ? field(*node, "sourceFileId") : nullptr;
99 const Value* parentId = node ? field(*node, "parentSourceFileId") : nullptr;
100 const Value* objectId = node ? field(*node, "objectId") : nullptr;
101 const Value* name = node ? field(*node, "name") : nullptr;
102 std::array<float, 3> position{}, scale{};
103 std::array<float, 4> rotation{};
104 if (!sourceId || !sourceId->isInt64() || sourceId->asInt() <= 0 || !parentId ||
105 !parentId->isInt64() || parentId->asInt() < 0 || !objectId || !objectId->isString() ||
106 !name || !name->isString() || !vector(node ? field(*node, "position") : nullptr, position) ||
107 !vector(node ? field(*node, "rotation") : nullptr, rotation) ||
108 !vector(node ? field(*node, "scale") : nullptr, scale))
110 Diagnostic::error(DiagnosticCode::ParseError, "scene node is malformed",
111 "$.nodes[" + std::to_string(i) + "]", {}, "asset.scene.template"));
112 auto persistentId = SceneObjectId::parse(objectId->asString());
113 const float length = std::sqrt(rotation[0] * rotation[0] + rotation[1] * rotation[1] +
114 rotation[2] * rotation[2] + rotation[3] * rotation[3]);
115 if (!persistentId || persistentId->isNil() || length < 0.999f || length > 1.001f ||
116 scale[0] == 0.f || scale[1] == 0.f || scale[2] == 0.f)
118 Diagnostic::error(DiagnosticCode::InvalidArgument, "scene identity, rotation, or scale is invalid",
119 "$.nodes[" + std::to_string(i) + "]", {}, "asset.scene.template"));
120 const float x = rotation[0] / length, y = rotation[1] / length;
121 const float z = rotation[2] / length, w = rotation[3] / length;
122 // Scene TransformSystem composes Ry * Rx * Rz, not the common Rz * Ry * Rx convention.
123 const float sinPitch = std::clamp(2.f * (w * x - y * z), -1.f, 1.f);
124 const float pitch = std::asin(sinPitch);
125 const bool gimbal = std::abs(sinPitch) > 0.999999f;
126 const float yaw = gimbal ? std::atan2(2.f * (w * y - x * z), 1.f - 2.f * (y * y + z * z))
127 : std::atan2(2.f * (x * z + w * y), 1.f - 2.f * (x * x + y * y));
128 const float roll = gimbal ? 0.f : std::atan2(2.f * (x * y + w * z), 1.f - 2.f * (x * x + z * z));
130 desc.id = std::to_string(sourceId->asInt());
131 desc.key = desc.id;
132 desc.name = name->asString();
133 if (const auto* visible = field(*node, "visible")) {
134 if (!visible->isBool())
136 DiagnosticCode::ParseError, "invalid node visibility", {}, {}, "asset.scene.template"));
137 desc.visible = visible->asBool();
138 }
139 desc.persistentId = *persistentId;
140 if (!objectIds.insert(*persistentId).second)
142 DiagnosticCode::Conflict, "duplicate scene object identity", {}, {}, "asset.scene.template"));
143 desc.withPosition(position[0], position[1], position[2])
144 .withRotation(yaw * radiansToDegrees, pitch * radiansToDegrees,
145 roll * radiansToDegrees)
146 .withScaleXYZ(scale[0], scale[1], scale[2]);
147 const std::uint64_t id = static_cast<std::uint64_t>(sourceId->asInt());
148 if (!flat.emplace(id, FlatNode{id, static_cast<std::uint64_t>(parentId->asInt()),
149 std::move(desc)}).second)
151 DiagnosticCode::Conflict, "scene contains duplicate sourceFileId", {}, {}, "asset.scene.template"));
152 }
153 for (const auto& [id, node] : flat)
154 if (node.parentId != 0 && !flat.contains(node.parentId))
156 Diagnostic::error(DiagnosticCode::NotFound, "scene parent does not exist", std::to_string(id), {},
157 "asset.scene.template"));
158 std::set<std::uint64_t> visiting, completed;
159 std::function<Result<scene::NodeDesc>(std::uint64_t, std::uint32_t)> build =
160 [&](std::uint64_t id, std::uint32_t depth) -> Result<scene::NodeDesc> {
161 if (depth > limits.maximumDepth)
163 "scene hierarchy exceeds depth budget", {}, {},
164 "asset.scene.template"));
165 if (!visiting.emplace(id).second)
167 DiagnosticCode::Conflict, "scene hierarchy contains a cycle", {}, {}, "asset.scene.template"));
168 scene::NodeDesc result = flat.at(id).desc;
169 for (const auto& [childId, child] : flat) {
170 if (child.parentId != id) continue;
171 auto built = build(childId, depth + 1);
172 if (!built) return built;
173 result.children.push_back(std::move(built).takeValue());
174 }
175 visiting.erase(id);
176 completed.emplace(id);
177 return Result<scene::NodeDesc>::success(std::move(result));
178 };
179 scene::NodeDesc resultRoot;
180 resultRoot.id = "asset-root";
181 resultRoot.key = "asset-root";
182 resultRoot.name = "Asset Scene Template";
183 for (const auto& [id, node] : flat) {
184 if (node.parentId != 0) continue;
185 auto built = build(id, 1);
186 if (!built) return Result<LoadedSceneTemplate>::failure(built.status());
187 resultRoot.children.push_back(std::move(built).takeValue());
188 }
189 if (resultRoot.children.empty() || completed.size() != flat.size())
191 DiagnosticCode::Conflict, "scene hierarchy has no valid root", {}, {}, "asset.scene.template"));
192 std::vector<SceneMeshBinding> bindings;
193 const Value* renderersValue = field(*root, "renderers");
194 if (version->asInt() == 2 || version->asInt() == 3) {
195 const auto* renderers = renderersValue ? renderersValue->getIf<Value::Array>() : nullptr;
196 if (!renderers || renderers->size() > limits.maximumNodes)
198 DiagnosticCode::ParseError, "invalid renderer bindings", {}, {}, "asset.scene.template"));
199 std::set<SceneObjectId> bound;
200 for (const auto& value : *renderers) {
201 const auto* binding = value.getIf<Value::Object>();
202 const auto* id = binding ? field(*binding, "objectId") : nullptr;
203 const auto* mesh = binding ? field(*binding, "mesh") : nullptr;
204 const auto* material = binding ? field(*binding, "material") : nullptr;
205 const auto* enabled = binding ? field(*binding, "enabled") : nullptr;
206 const auto* castShadows = binding ? field(*binding, "castShadows") : nullptr;
207 const auto* receiveShadows = binding ? field(*binding, "receiveShadows") : nullptr;
208 if (!id || !id->isString() || !mesh || !mesh->isString() || !material || !material->isString() ||
209 !enabled || !enabled->isBool() ||
210 (version->asInt() == 3 && (!castShadows || !castShadows->isBool() ||
211 !receiveShadows || !receiveShadows->isBool())))
213 DiagnosticCode::ParseError, "malformed renderer binding", {}, {}, "asset.scene.template"));
214 auto object = SceneObjectId::parse(id->asString());
215 auto meshRef = AssetRef::parse(mesh->asString());
216 auto materialRef = AssetRef::parse(material->asString());
217 const bool meshValid = meshRef.ok();
218 const bool materialValid = materialRef.ok();
219 if (!object || !objectIds.contains(*object) || !bound.insert(*object).second || !meshValid ||
220 !materialValid)
222 Diagnostic::error(DiagnosticCode::ParseError, "renderer identity or reference is invalid", {}, {},
223 "asset.scene.template"));
224 bindings.push_back(
225 {*object, std::move(meshRef).takeValue(), std::move(materialRef).takeValue(), enabled->asBool(),
226 version->asInt() == 2 || castShadows->asBool(),
227 version->asInt() == 2 || receiveShadows->asBool()});
228 }
229 }
231 {sceneTemplate, sourceGuid ? sourceGuid->asString() : std::string{}, std::move(resultRoot),
232 std::move(payload).takeValue().variant,
233 std::move(bindings)});
234}
235
236} // namespace eve::asset_scene
double value
Value::Object payload
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
float length
Definition CaveMesh.cpp:94
std::map< std::string, Var > values
scene::NodeDesc desc
std::uint64_t sourceId
std::uint64_t parentId
Runtime decoding of canonical scene templates.
std::array< float, 4 > rotation
std::array< float, 3 > position
std::array< float, 3 > scale
std::string name
std::vector< BvhNode > nodes
std::vector< std::shared_ptr< DeviceBytes > > bindings
Definition OnnxGpgpu.cpp:38
eve::action::ActionVfxBinding binding
std::string id
Definition PlayHost.cpp:108
Mesh * mesh
Material * material
const RoadNode * node
double number
bool found
JSON-free versioned runtime metadata codec.
bool visible
Json object
std::set< std::string > visiting
std::uint32_t depth
const AssetImportLimits & limits
Owning, renderer-independent dynamic values.
Stable asset identity backed by PersistentId.
Definition ResourceRef.h:53
static Result< AssetRef > parse(std::string_view text)
Parse an asset://<canonical UUID> reference.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
const std::string & asString() const
Return the string payload; the caller must have checked the kind.
Definition Value.cpp:87
bool isInt64() const noexcept
Return true when this value is an Int64.
Definition Value.h:91
std::map< std::string, Value > Object
Definition Value.h:34
bool isString() const noexcept
Return true when this value is a string.
Definition Value.h:95
std::int64_t asInt() const
Return the Int64 payload; the caller must have checked the kind.
Definition Value.cpp:69
std::vector< Value > Array
Definition Value.h:33
const T * getIf() const noexcept
Return a typed pointer, or nullptr when the kind differs.
Definition Value.h:180
Result< RuntimeAssetPayload > read(const AssetRef &asset, std::string_view expectedType, const EvpackCapabilities &capabilities, std::uint64_t maximumDecodedBytes) const
Resolve, type-check, variant-select and decode a canonical runtime asset.
Result< LoadedSceneTemplate > load(const AssetRef &sceneTemplate, const asset::EvpackCapabilities &capabilities, const SceneTemplateLoadLimits &limits={}) const
Validate identities, hierarchy and finite TRS values before building a tree.
static std::optional< Id128 > parse(std::string_view text) noexcept
Parses canonical UUID text.
Definition Identity.h:151
Result< Value > decodeRuntimeDefinition(std::span< const std::uint8_t > bytes, const RuntimeDefinitionLimits &limits)
Decode a bounded EVDEF\0\1 metadata tree without a JSON parser.
@ TypeMismatch
A stable reference resolved to a different canonical domain type.
bool enabled
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
One decoded runtime chunk with its admitted semantic metadata.
Structural and allocation limits for untrusted EVDEF metadata.
Bounds for untrusted scene-template metadata.
Declarative scene-node description (build once / on dirty → flatten into SceneHost::Tree)....
Definition NodeDesc.h:19
std::string id
Definition NodeDesc.h:20
std::string key
Reconciliation key; defaults to id when empty.
Definition NodeDesc.h:30
std::vector< NodeDesc > children
Definition NodeDesc.h:45
std::string name
Definition NodeDesc.h:31