92 lines
3.1 KiB
C++
92 lines
3.1 KiB
C++
|
|
#include "vde/foundation/io_gltf.h"
|
||
|
|
#include <fstream>
|
||
|
|
#include <sstream>
|
||
|
|
#include <algorithm>
|
||
|
|
#include <cmath>
|
||
|
|
|
||
|
|
namespace vde::foundation {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
std::string vec3_to_json(double x, double y, double z) {
|
||
|
|
return "[" + std::to_string(x) + "," + std::to_string(y) + "," + std::to_string(z) + "]";
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
bool write_gltf(const std::string& filepath, const mesh::HalfedgeMesh& mesh) {
|
||
|
|
std::ofstream file(filepath);
|
||
|
|
if (!file) return false;
|
||
|
|
|
||
|
|
size_t nv = mesh.num_vertices();
|
||
|
|
size_t nf = mesh.num_faces();
|
||
|
|
|
||
|
|
// Count total indices (triangles * 3)
|
||
|
|
size_t index_count = nf * 3;
|
||
|
|
|
||
|
|
// Build min/max for accessor bounds
|
||
|
|
core::AABB3D bounds = mesh.bounds();
|
||
|
|
|
||
|
|
file << "{\n";
|
||
|
|
file << " \"asset\": {\"version\": \"2.0\", \"generator\": \"ViewDesignEngine\"},\n";
|
||
|
|
file << " \"scene\": 0,\n";
|
||
|
|
file << " \"scenes\": [{\"nodes\": [0]}],\n";
|
||
|
|
file << " \"nodes\": [{\"mesh\": 0}],\n";
|
||
|
|
file << " \"meshes\": [{\n";
|
||
|
|
file << " \"primitives\": [{\n";
|
||
|
|
file << " \"attributes\": {\"POSITION\": 0},\n";
|
||
|
|
file << " \"indices\": 1\n";
|
||
|
|
file << " }]\n";
|
||
|
|
file << " }],\n";
|
||
|
|
|
||
|
|
// Buffers and bufferViews
|
||
|
|
file << " \"buffers\": [{\"uri\": \"data:application/octet-stream;base64,";
|
||
|
|
|
||
|
|
// Write binary data as base64 (simplified: write inline as hex-ish)
|
||
|
|
// For simplicity, write a separate .bin file reference
|
||
|
|
file << "mesh.bin\",\"byteLength\": " << (nv*12 + index_count*4) << "}],\n";
|
||
|
|
|
||
|
|
file << " \"bufferViews\": [\n";
|
||
|
|
file << " {\"buffer\": 0, \"byteOffset\": 0, \"byteLength\": " << (nv*12) << "},\n";
|
||
|
|
file << " {\"buffer\": 0, \"byteOffset\": " << (nv*12) << ", \"byteLength\": " << (index_count*4) << "}\n";
|
||
|
|
file << " ],\n";
|
||
|
|
|
||
|
|
// Accessors
|
||
|
|
file << " \"accessors\": [\n";
|
||
|
|
file << " {\"bufferView\": 0, \"componentType\": 5126, \"count\": " << nv
|
||
|
|
<< ", \"type\": \"VEC3\", \"max\": " << vec3_to_json(bounds.max().x(),bounds.max().y(),bounds.max().z())
|
||
|
|
<< ", \"min\": " << vec3_to_json(bounds.min().x(),bounds.min().y(),bounds.min().z()) << "},\n";
|
||
|
|
file << " {\"bufferView\": 1, \"componentType\": 5125, \"count\": " << index_count
|
||
|
|
<< ", \"type\": \"SCALAR\"}\n";
|
||
|
|
file << " ]\n";
|
||
|
|
file << "}\n";
|
||
|
|
|
||
|
|
// Write binary data file
|
||
|
|
std::string binpath = filepath;
|
||
|
|
size_t dot = binpath.rfind(.);
|
||
|
|
if (dot != std::string::npos) binpath = binpath.substr(0, dot);
|
||
|
|
binpath += ".bin";
|
||
|
|
|
||
|
|
std::ofstream bin(binpath, std::ios::binary);
|
||
|
|
// Vertices (float32)
|
||
|
|
for (size_t i = 0; i < nv; ++i) {
|
||
|
|
const auto& v = mesh.vertex(i);
|
||
|
|
float fx=v.x(), fy=v.y(), fz=v.z();
|
||
|
|
bin.write(reinterpret_cast<const char*>(&fx),4);
|
||
|
|
bin.write(reinterpret_cast<const char*>(&fy),4);
|
||
|
|
bin.write(reinterpret_cast<const char*>(&fz),4);
|
||
|
|
}
|
||
|
|
// Indices (uint32)
|
||
|
|
for (size_t i = 0; i < nf; ++i) {
|
||
|
|
auto vis = mesh.face_vertices(static_cast<int>(i));
|
||
|
|
for (size_t j = 0; j < std::min(vis.size(), size_t(3)); ++j) {
|
||
|
|
uint32_t idx = vis[j];
|
||
|
|
bin.write(reinterpret_cast<const char*>(&idx),4);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace vde::foundation
|