11 const auto found =
object->find(
key);
return found ==
object->end() ? nullptr : &
found->second;
14 return eve::editing::failed<PhysicsColliderAssetGeometry>(
status,
RuleId(rule), std::move(
message));
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;
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;
39bool convexPolygon(
const std::vector<float>&
vertices) {
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;
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");
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");
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");
76 if (*
kind ==
"polygon" || *
kind ==
"chain") {
79 (*
kind ==
"polygon" && !convexPolygon(result.
vertices)) ||
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;
88 return error(EditorStatus::Rejected,
"editor.physics.invalid-chain-loop",
89 "Closed chain collider requires at least three vertices");
93 return error(EditorStatus::Rejected,
"editor.physics.duplicate-chain-vertex",
94 "Chain collider contains consecutive duplicate vertices");
96 }
else if (*
kind ==
"convex-hull" || *
kind ==
"triangle-mesh") {
99 return error(EditorStatus::Rejected,
"editor.physics.invalid-collider-vertices",
100 "Collider asset requires finite packed XYZ vertices");
101 if (*
kind ==
"triangle-mesh") {
104 return error(EditorStatus::Rejected,
"editor.physics.invalid-collider-indices",
105 "Triangle mesh requires bounded packed triangle indices");
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");
112 }
else if (*
kind ==
"height-field") {
113 const auto integer = [&](
const char*
key) ->
const int64_t* {
115 return value ?
value->getIf<int64_t>() :
nullptr;
117 const auto number = [&](
const char*
key) ->
const double* {
119 return value ?
value->getIf<
double>() :
nullptr;
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);
136 return error(EditorStatus::Unsupported,
"editor.physics.unsupported-collider-asset",
137 "Collider asset kind is unsupported");
139 return eve::editing::applied<PhysicsColliderAssetGeometry>(std::move(result));
AuthorityStoreHandleRef reference
std::uint32_t vertexCount
wgpu::PopErrorScopeStatus status
std::vector< std::uint32_t > indices
std::vector< Point > vertices
Move-only operation result carrying either a value or Status.
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.
eve::editing::RuleId RuleId
eve::editing::AssetGuid AssetGuid
eve::StatusCode EditorStatus
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
double cross(const Vec2 &a, const Vec2 &b)
Cross.
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.
std::vector< float > heights
std::vector< float > vertices
std::vector< std::int32_t > indices