14 "invalid canonical mesh or byte budget", {}, {},
"asset.mesh"));
16 const auto count =
mesh.positions.size() / 3;
18 mesh.indices.size() % 3 ||
mesh.indices.size() >
limits.maximumIndices ||
19 (!
mesh.normals.empty() &&
mesh.normals.size() !=
mesh.positions.size()) ||
21 (!
mesh.colors.empty() && !
mesh.attributes.empty()) ||
mesh.texcoords.size() > UINT32_MAX ||
22 mesh.attributes.size() > UINT32_MAX)
24 const bool extended = !
mesh.attributes.empty();
25 uint64_t floats = 3 + (
mesh.normals.empty() ? 0 : 3) + uint64_t(
mesh.texcoords.size()) * 2 +
26 (
mesh.colors.empty() ? 0 : 4);
27 auto validValues = [](
const auto&
values) {
28 return std::all_of(
values.begin(),
values.end(), [](
float v) { return std::isfinite(v); });
30 if (!validValues(
mesh.positions) || !validValues(
mesh.normals) || !validValues(
mesh.colors))
return invalid();
31 for (
const auto& [set,
values] :
mesh.texcoords)
33 for (
const auto& [
name, attribute] :
mesh.attributes) {
34 if (
name.empty() ||
name.size() > 63 ||
name ==
"POSITION" ||
name ==
"NORMAL" ||
35 name.starts_with(
"TEXCOORD_") || !attribute.components || attribute.components > 4 ||
36 attribute.values.size() !=
count * attribute.components || !validValues(attribute.values) ||
37 !std::all_of(
name.begin(),
name.end(),
38 [](
char c) { return (c >=
'A' && c <=
'Z') || (c >=
'0' && c <=
'9') || c ==
'_'; }))
40 floats += attribute.components;
44 const uint64_t header =
45 (extended ? 28ull : 24ull) + uint64_t(
mesh.texcoords.size()) * 4 + uint64_t(
mesh.attributes.size()) * 72;
46 const uint64_t indexBytes = uint64_t(
mesh.indices.size()) * 4;
47 if (header >
limits.maximumDecodedBytes || indexBytes >
limits.maximumDecodedBytes - header ||
48 floats > (
limits.maximumDecodedBytes - header - indexBytes) / 4 /
count)
50 const uint64_t
size = header + uint64_t(
count) * floats * 4 + indexBytes;
51 if (
size > std::numeric_limits<size_t>::max())
return invalid();
53 std::vector<uint8_t>
bytes;
56 {
'E',
'V',
'M',
'E',
'S',
'H', 0, uint8_t(extended || !mesh.colors.empty() ? 3 : 2)});
57 auto put = [&](uint32_t
value) {
58 for (
unsigned i = 0; i < 4; ++i)
bytes.push_back(uint8_t(
value >> (i * 8)));
61 put(uint32_t(
mesh.indices.size()));
62 put((
mesh.normals.empty() ? 0
u : 1u) | (
mesh.colors.empty() ? 0
u : 2u));
63 put(uint32_t(
mesh.texcoords.size()));
64 if (extended) put(uint32_t(
mesh.attributes.size()));
65 for (
const auto& [set,
values] :
mesh.texcoords) put(set);
66 for (
const auto& [
name, attribute] :
mesh.attributes) {
69 put(attribute.components);
72 auto stream = [&](
const auto&
values,
size_t vertex,
size_t components) {
73 for (
size_t j = 0; j < components; ++j) put(std::bit_cast<uint32_t>(
values[vertex * components + j]));
76 stream(
mesh.positions,
v, 3);
77 if (!
mesh.normals.empty()) stream(
mesh.normals,
v, 3);
79 for (
const auto& [
name, attribute] :
mesh.attributes) stream(attribute.values,
v, attribute.components);
80 if (!
mesh.colors.empty()) stream(
mesh.colors,
v, 4);
83 return Output::success(std::move(
bytes));
84 }
catch (
const std::bad_alloc&) {
85 return Output::failure(
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.
EVENGINE_API_FOUNDATION Result< std::vector< std::uint8_t > > encodeCanonicalMesh(const CanonicalMeshData &mesh, const CanonicalMeshLimits &limits={})
Encode an owning snapshot to EVMESH v3, or v2 when no named attributes exist. Borrows input synchrono...