载入中...
搜索中...
未找到
EditorPhysicsAsset.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <limits>
6
7namespace eve::editor {
8namespace {
9const EditorValue* field(const EditorValue& value, const char* key) {
10 const auto* object = value.getIf<EditorValue::Object>(); if (!object) return nullptr;
11 const auto found = object->find(key); return found == object->end() ? nullptr : &found->second;
12}
13Result<PhysicsColliderAssetGeometry> error(EditorStatus status, const char* rule, std::string message) {
14 return eve::editing::failed<PhysicsColliderAssetGeometry>(status, RuleId(rule), std::move(message));
15}
16bool floats(const EditorValue* value, std::vector<float>& output) {
17 const auto* array = value ? value->getIf<EditorValue::Array>() : nullptr;
18 if (!array) return false;
19 output.reserve(array->size());
20 for (const auto& entry : *array) {
21 const auto* number = entry.getIf<double>();
22 if (!number || !std::isfinite(*number) || *number < -std::numeric_limits<float>::max() ||
23 *number > std::numeric_limits<float>::max()) return false;
24 output.push_back(static_cast<float>(*number));
25 }
26 return true;
27}
28bool indices(const EditorValue* value, std::vector<std::int32_t>& output) {
29 const auto* array = value ? value->getIf<EditorValue::Array>() : nullptr;
30 if (!array) return false;
31 output.reserve(array->size());
32 for (const auto& entry : *array) {
33 const auto* number = entry.getIf<int64_t>();
34 if (!number || *number < 0 || *number > std::numeric_limits<std::int32_t>::max()) return false;
35 output.push_back(static_cast<std::int32_t>(*number));
36 }
37 return true;
38}
39bool convexPolygon(const std::vector<float>& vertices) {
40 if (vertices.size() < 6 || vertices.size() > 16 || vertices.size() % 2 != 0) return false;
41 const size_t count = vertices.size() / 2;
42 double sign = 0.0;
43 for (size_t index = 0; index < count; ++index) {
44 const size_t next = (index + 1) % count;
45 const size_t after = (index + 2) % count;
46 const double ax = vertices[next * 2] - vertices[index * 2];
47 const double ay = vertices[next * 2 + 1] - vertices[index * 2 + 1];
48 const double bx = vertices[after * 2] - vertices[next * 2];
49 const double by = vertices[after * 2 + 1] - vertices[next * 2 + 1];
50 const double cross = ax * by - ay * bx;
51 if (std::abs(cross) <= 1e-8) return false;
52 if (sign == 0.0) sign = cross;
53 else if ((cross > 0.0) != (sign > 0.0)) return false;
54 }
55 return true;
56}
57}
58
60 const std::string& reference, const std::string& expectedKind) const {
61 if (!database_ || reference.empty()) return error(EditorStatus::Rejected,
62 "editor.physics.invalid-asset-reference", "Collider resolver requires AssetDB and asset reference");
63 auto found = database_->find(AssetGuid(reference));
64 if (!found.ok()) found = database_->findByUri(reference);
65 if (!found.ok()) return error(EditorStatus::NotFound, "editor.physics.collider-asset-not-found",
66 "Collider asset was not found by GUID or logical URI");
67 const AssetRecord& record = found.value();
68 if (record.status != AssetStatus::Ready && record.status != AssetStatus::Warning)
69 return error(EditorStatus::Conflict, "editor.physics.collider-asset-not-ready",
70 "Collider asset is not ready for runtime construction");
71 const auto* kindEntry = field(record.metadata, "kind");
72 const auto* kind = kindEntry ? kindEntry->getIf<std::string>() : nullptr;
73 if (!kind || *kind != expectedKind) return error(EditorStatus::Conflict,
74 "editor.physics.collider-kind-mismatch", "Collider asset kind does not match the authored shape");
75 PhysicsColliderAssetGeometry result; result.kind = *kind;
76 if (*kind == "polygon" || *kind == "chain") {
77 if (!floats(field(record.metadata, "vertices"), result.vertices) ||
78 result.vertices.size() % 2 != 0 ||
79 (*kind == "polygon" && !convexPolygon(result.vertices)) ||
80 (*kind == "chain" && (result.vertices.size() < 4 || result.vertices.size() > 200000)))
81 return error(EditorStatus::Rejected, "editor.physics.invalid-collider-vertices-2d",
82 "2D collider requires bounded finite packed XY vertices");
83 if (*kind == "chain") {
84 const auto* loopValue = field(record.metadata, "loop");
85 const auto* loop = loopValue ? loopValue->getIf<bool>() : nullptr;
86 result.loop = loop ? *loop : false;
87 if (result.loop && result.vertices.size() < 6)
88 return error(EditorStatus::Rejected, "editor.physics.invalid-chain-loop",
89 "Closed chain collider requires at least three vertices");
90 for (size_t index = 2; index < result.vertices.size(); index += 2)
91 if (result.vertices[index] == result.vertices[index - 2] &&
92 result.vertices[index + 1] == result.vertices[index - 1])
93 return error(EditorStatus::Rejected, "editor.physics.duplicate-chain-vertex",
94 "Chain collider contains consecutive duplicate vertices");
95 }
96 } else if (*kind == "convex-hull" || *kind == "triangle-mesh") {
97 if (!floats(field(record.metadata, "vertices"), result.vertices) || result.vertices.size() % 3 != 0 ||
98 result.vertices.size() > 3000000 || (*kind == "convex-hull" && result.vertices.size() < 12))
99 return error(EditorStatus::Rejected, "editor.physics.invalid-collider-vertices",
100 "Collider asset requires finite packed XYZ vertices");
101 if (*kind == "triangle-mesh") {
102 if (!indices(field(record.metadata, "indices"), result.indices) || result.indices.size() % 3 != 0 ||
103 result.indices.empty() || result.indices.size() > 6000000)
104 return error(EditorStatus::Rejected, "editor.physics.invalid-collider-indices",
105 "Triangle mesh requires bounded packed triangle indices");
106 const std::size_t vertexCount = result.vertices.size() / 3;
107 if (std::any_of(result.indices.begin(), result.indices.end(), [vertexCount](std::int32_t index) {
108 return static_cast<std::size_t>(index) >= vertexCount;
109 })) return error(EditorStatus::Rejected, "editor.physics.collider-index-range",
110 "Triangle mesh index exceeds its vertex count");
111 }
112 } else if (*kind == "height-field") {
113 const auto integer = [&](const char* key) -> const int64_t* {
114 const auto* value = field(record.metadata, key);
115 return value ? value->getIf<int64_t>() : nullptr;
116 };
117 const auto number = [&](const char* key) -> const double* {
118 const auto* value = field(record.metadata, key);
119 return value ? value->getIf<double>() : nullptr;
120 };
121 const auto* countX = integer("countX"); const auto* countZ = integer("countZ");
122 const auto* sizeX = number("cellSizeX"); const auto* sizeZ = number("cellSizeZ");
123 if (!countX || !countZ || *countX < 2 || *countZ < 2 || *countX > 100000 || *countZ > 100000 ||
124 static_cast<std::uint64_t>(*countX) * static_cast<std::uint64_t>(*countZ) > 100000000ULL ||
125 !sizeX || !sizeZ || !std::isfinite(*sizeX) || !std::isfinite(*sizeZ) || *sizeX <= 0.0 ||
126 *sizeZ <= 0.0 || !floats(field(record.metadata, "heights"), result.heights) ||
127 result.heights.size() != static_cast<std::size_t>(*countX) * static_cast<std::size_t>(*countZ))
128 return error(EditorStatus::Rejected, "editor.physics.invalid-height-field",
129 "Height field dimensions, spacing or samples are invalid");
130 result.countX = static_cast<int>(*countX); result.countZ = static_cast<int>(*countZ);
131 result.cellSizeX = static_cast<float>(*sizeX); result.cellSizeZ = static_cast<float>(*sizeZ);
132 const auto range = std::minmax_element(result.heights.begin(), result.heights.end());
133 result.minimumHeight = *range.first; result.maximumHeight = *range.second;
134 if (result.minimumHeight == result.maximumHeight) result.maximumHeight = result.minimumHeight + 0.001F;
135 } else {
136 return error(EditorStatus::Unsupported, "editor.physics.unsupported-collider-asset",
137 "Collider asset kind is unsupported");
138 }
139 return eve::editing::applied<PhysicsColliderAssetGeometry>(std::move(result));
140}
141
142} // namespace eve::editor
double value
std::string output
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
int by
Definition CaveMesh.cpp:114
std::string message
std::uint32_t vertexCount
std::uint32_t key
wgpu::PopErrorScopeStatus status
std::vector< std::uint32_t > indices
TokenKind kind
Range range
std::string error
Definition Package.cpp:60
std::vector< Point > vertices
double number
bool found
std::uint32_t count
uint32_t index
Move-only operation result carrying either a value or Status.
Definition Result.h:155
std::vector< Value > Array
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Result< PhysicsColliderAssetGeometry > resolve(const std::string &reference, const std::string &expectedKind) const override
Resolve.
Result< AssetRecord > find(const AssetGuid &guid) const
Find one asset by stable GUID.
Result< AssetRecord > findByUri(const std::string &logicalUri) const
Find one asset by logical content URI.
eve::editing::Value EditorValue
Compatibility alias for the UI-independent authoring value tree.
Definition EditorValue.h:8
eve::editing::RuleId RuleId
Definition EditorIds.h:32
eve::editing::AssetGuid AssetGuid
Definition EditorIds.h:39
eve::StatusCode EditorStatus
Definition EditorResult.h:8
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
double cross(const Vec2 &a, const Vec2 &b)
Cross.
Definition UrbanTypes.h:36
const EditorValue * field(const EditorValue &value, const char *name)
std::vector< float > floats(const RuntimeTensor &v)
Floats.
Value snapshot stored by the editor asset index.
Validated CPU geometry resolved from an authored collider asset.