Files
trellis2-ROCm/tests/test_mesh_export.cpp
2026-08-16 18:33:03 +07:00

127 lines
5.1 KiB
C++

#include "mesh_export.h"
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
int main() {
const float verts[] = {
0,0,0, 1,0,0, 0,1,0, 0,0,1,
};
const int32_t tris[] = {
0,2,1, 0,1,3, 0,3,2, 1,2,3,
};
// base RGB, metallic, roughness, alpha
const float pbr[] = {
1,0,0, 0,0.4f,0.5f,
0,1,0, 0,0.5f,1.0f,
0,0,1, 1,0.6f,1.0f,
1,1,1, 0,0.7f,1.0f,
};
t2glb::MeshExportOptions opt;
opt.texture_size = 64;
opt.dilate = 2;
std::vector<uint8_t> glb;
std::string err;
if (!t2glb::mesh_to_glb(verts, 4, tris, 4, pbr, opt, glb, err)) {
std::fprintf(stderr, "mesh_to_glb failed: %s\n", err.c_str());
return 1;
}
if (glb.size() < 20 || std::memcmp(glb.data(), "glTF", 4) != 0) {
std::fprintf(stderr, "invalid GLB header\n");
return 1;
}
const std::string bytes((const char *) glb.data(), glb.size());
if (bytes.find("\"alphaMode\":\"BLEND\"") == std::string::npos) {
std::fprintf(stderr, "alpha material was not preserved\n");
return 1;
}
if (bytes.find("\"COLOR_0\":2") == std::string::npos ||
bytes.find("\"_METALLIC_ROUGHNESS\":3") == std::string::npos ||
bytes.find("\"baseColorTexture\"") != std::string::npos) {
std::fprintf(stderr, "portable vertex material attributes are missing\n");
return 1;
}
// The old per-triangle grid duplicated all three vertices and collapsed to
// sub-texel UV cells on production meshes. The direct path must retain the
// tetrahedron's four vertices and normalised RGBA bytes exactly.
auto u32 = [&](size_t off) {
return (uint32_t)(uint8_t)glb[off] |
((uint32_t)(uint8_t)glb[off+1] << 8) |
((uint32_t)(uint8_t)glb[off+2] << 16) |
((uint32_t)(uint8_t)glb[off+3] << 24);
};
const size_t bin = 20 + u32(12) + 8;
const size_t color = bin + 4 * 3 * sizeof(float) * 2;
const uint8_t expected_first_color[] = {255, 255, 0, 0, 0, 0, 0, 128};
if (color + sizeof expected_first_color > glb.size() ||
std::memcmp(glb.data() + color, expected_first_color,
sizeof expected_first_color) != 0 ||
bytes.find("\"count\":4,\"type\":\"VEC4\"") == std::string::npos) {
std::fprintf(stderr, "vertex colours were changed or vertices were duplicated\n");
return 1;
}
// A tiny near-opaque decoder outlier must not put the whole primitive into
// alpha blending (which disables depth writes and resembles missing faces).
float nearly_opaque[24];
std::memcpy(nearly_opaque, pbr, sizeof nearly_opaque);
for (int i = 0; i < 4; ++i) nearly_opaque[6*i+5] = 1.0f;
nearly_opaque[5] = 0.994f;
if (!t2glb::mesh_to_glb(verts, 4, tris, 4, nearly_opaque, opt, glb, err) ||
std::string((const char *) glb.data(), glb.size()).find("\"alphaMode\":\"BLEND\"") != std::string::npos) {
std::fprintf(stderr, "near-opaque PBR noise enabled alpha blending\n");
return 1;
}
// Preview preparation uses the same geometry path and can keep only the
// largest disconnected component for background-plane cleanup.
const float two_component_verts[] = {
0,0,0, 1,0,0, 0,1,0, 0,0,1,
3,0,0, 4,0,0, 3,1,0,
};
const int32_t two_component_tris[] = {
0,2,1, 0,1,3, 0,3,2, 1,2,3,
4,5,6,
};
t2glb::PreparedMesh prepared;
opt.components = t2glb::ComponentFilter::KeepLargest;
if (!t2glb::prepare_mesh(two_component_verts, 7, two_component_tris, 5,
nullptr, opt, prepared, err)) {
std::fprintf(stderr, "prepare_mesh failed: %s\n", err.c_str());
return 1;
}
if (prepared.tris.size() / 3 != 4 || prepared.verts.size() / 3 != 4 ||
prepared.normals.size() != prepared.verts.size()) {
std::fprintf(stderr, "largest-component preview is wrong: %zu verts, %zu tris, %zu normals\n",
prepared.verts.size() / 3, prepared.tris.size() / 3,
prepared.normals.size() / 3);
return 1;
}
// Keeping all components must retain every valid source triangle; export no
// longer performs polygon decimation.
opt.components = t2glb::ComponentFilter::KeepAll;
if (!t2glb::prepare_mesh(two_component_verts, 7, two_component_tris, 5,
nullptr, opt, prepared, err) || prepared.tris.size() / 3 != 5) {
std::fprintf(stderr, "full-density export changed the polygon count\n");
return 1;
}
// The API remains present in portable builds without CGAL and must fail
// explicitly instead of creating an untextured or incorrectly mapped GLB.
if (!t2glb::print_remesh_available()) {
if (t2glb::mesh_to_projected_glb(verts, 4, tris, 4,
verts, 4, tris, 4, pbr,
opt, glb, err) ||
err.find("unavailable") == std::string::npos) {
std::fprintf(stderr, "CGAL-free projected bake did not report unavailable\n");
return 1;
}
}
std::puts("RESULT: PASS");
return 0;
}