mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-08-02 19:00:39 +08:00
761 lines
20 KiB
C++
761 lines
20 KiB
C++
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <fstream>
|
|
|
|
#if !defined(__ANDROID__) && !defined(_WIN32)
|
|
#include <wordexp.h>
|
|
#endif
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Weverything"
|
|
#endif
|
|
|
|
#include "../../json.hpp"
|
|
|
|
using json = nlohmann::json;
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include "../../tiny_gltf.h"
|
|
#else
|
|
#include "tiny_gltf.h"
|
|
#endif
|
|
|
|
#include "mesh-util.hh"
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic ignored "-Wunused-function"
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
static std::string PrintMode(int mode) {
|
|
if (mode == TINYGLTF_MODE_POINTS) {
|
|
return "POINTS";
|
|
} else if (mode == TINYGLTF_MODE_LINE) {
|
|
return "LINE";
|
|
} else if (mode == TINYGLTF_MODE_LINE_LOOP) {
|
|
return "LINE_LOOP";
|
|
} else if (mode == TINYGLTF_MODE_TRIANGLES) {
|
|
return "TRIANGLES";
|
|
} else if (mode == TINYGLTF_MODE_TRIANGLE_FAN) {
|
|
return "TRIANGLE_FAN";
|
|
} else if (mode == TINYGLTF_MODE_TRIANGLE_STRIP) {
|
|
return "TRIANGLE_STRIP";
|
|
}
|
|
return "**UNKNOWN**";
|
|
}
|
|
|
|
static std::string PrintTarget(int target) {
|
|
if (target == 34962) {
|
|
return "GL_ARRAY_BUFFER";
|
|
} else if (target == 34963) {
|
|
return "GL_ELEMENT_ARRAY_BUFFER";
|
|
} else {
|
|
return "**UNKNOWN**";
|
|
}
|
|
}
|
|
|
|
static std::string PrintType(int ty) {
|
|
if (ty == TINYGLTF_TYPE_SCALAR) {
|
|
return "SCALAR";
|
|
} else if (ty == TINYGLTF_TYPE_VECTOR) {
|
|
return "VECTOR";
|
|
} else if (ty == TINYGLTF_TYPE_VEC2) {
|
|
return "VEC2";
|
|
} else if (ty == TINYGLTF_TYPE_VEC3) {
|
|
return "VEC3";
|
|
} else if (ty == TINYGLTF_TYPE_VEC4) {
|
|
return "VEC4";
|
|
} else if (ty == TINYGLTF_TYPE_MATRIX) {
|
|
return "MATRIX";
|
|
} else if (ty == TINYGLTF_TYPE_MAT2) {
|
|
return "MAT2";
|
|
} else if (ty == TINYGLTF_TYPE_MAT3) {
|
|
return "MAT3";
|
|
} else if (ty == TINYGLTF_TYPE_MAT4) {
|
|
return "MAT4";
|
|
}
|
|
return "**UNKNOWN**";
|
|
}
|
|
|
|
static std::string PrintComponentType(int ty) {
|
|
if (ty == TINYGLTF_COMPONENT_TYPE_BYTE) {
|
|
return "BYTE";
|
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
|
return "UNSIGNED_BYTE";
|
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
|
return "SHORT";
|
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
|
return "UNSIGNED_SHORT";
|
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_INT) {
|
|
return "INT";
|
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
|
|
return "UNSIGNED_INT";
|
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
|
return "FLOAT";
|
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
|
|
return "DOUBLE";
|
|
}
|
|
|
|
return "**UNKNOWN**";
|
|
}
|
|
|
|
#if 0
|
|
// TODO(syoyo): Support sparse accessor(sparse vertex attribute).
|
|
// TODO(syoyo): Support more data type
|
|
struct VertexAttrib {
|
|
std::string name;
|
|
|
|
// Value are converted to float type.
|
|
std::vector<float> data;
|
|
|
|
// Corresponding info in binary data
|
|
int data_type; // e.g. TINYGLTF_TYPE_VEC2
|
|
int component_type; // storage type. e.g.
|
|
// TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
|
|
uint64_t buffer_offset{0};
|
|
size_t buffer_length{0};
|
|
};
|
|
|
|
struct MeshPrim {
|
|
std::string name;
|
|
int32_t id{-1};
|
|
|
|
int mode{TINYGLTF_MODE_TRIANGLES};
|
|
|
|
VertexAttrib position; // vec3
|
|
VertexAttrib normal; // vec3
|
|
VertexAttrib tangent; // vec4
|
|
VertexAttrib color; // vec3 or vec4
|
|
std::map<int, VertexAttrib> texcoords; // <slot, attrib> vec2
|
|
std::map<int, VertexAttrib> weights; // <slot, attrib>
|
|
std::map<int, VertexAttrib>
|
|
joints; // <slot, attrib> store data as float type
|
|
|
|
int indices_type{-1}; // storage type(componentType) of `indices`.
|
|
std::vector<uint32_t> indices; // vertex indices
|
|
};
|
|
#endif
|
|
|
|
#if 0
|
|
static std::string GetFilePathExtension(const std::string &FileName) {
|
|
if (FileName.find_last_of(".") != std::string::npos)
|
|
return FileName.substr(FileName.find_last_of(".") + 1);
|
|
return "";
|
|
}
|
|
#endif
|
|
|
|
static size_t ComponentTypeByteSize(int type) {
|
|
switch (type) {
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
|
case TINYGLTF_COMPONENT_TYPE_BYTE:
|
|
return sizeof(char);
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
|
case TINYGLTF_COMPONENT_TYPE_SHORT:
|
|
return sizeof(short);
|
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
|
|
case TINYGLTF_COMPONENT_TYPE_INT:
|
|
return sizeof(int);
|
|
case TINYGLTF_COMPONENT_TYPE_FLOAT:
|
|
return sizeof(float);
|
|
case TINYGLTF_COMPONENT_TYPE_DOUBLE:
|
|
return sizeof(double);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
std::vector<uint8_t> LoadBin(const std::string &filename) {
|
|
std::vector<uint8_t> data;
|
|
|
|
std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate);
|
|
|
|
if (is.is_open()) {
|
|
size_t size = size_t(is.tellg());
|
|
is.seekg(0, std::ios::beg);
|
|
if (size < 4) {
|
|
std::cerr << "File size is zero or too short: " << size << "\n";
|
|
return data;
|
|
}
|
|
|
|
data.resize(size);
|
|
is.read(reinterpret_cast<char *>(data.data()), std::streamsize(size));
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// TODO(syoyo): Use C++17 like filesystem library
|
|
|
|
bool FileExists(const std::string &abs_filename) {
|
|
bool ret;
|
|
#ifdef _WIN32
|
|
// TODO(syoyo): Support utf8 filepath
|
|
FILE *fp = nullptr;
|
|
errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
|
|
if (err != 0) {
|
|
return false;
|
|
}
|
|
#else
|
|
FILE *fp = fopen(abs_filename.c_str(), "rb");
|
|
#endif
|
|
if (fp) {
|
|
ret = true;
|
|
fclose(fp);
|
|
} else {
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static std::string JoinPath(const std::string &path0,
|
|
const std::string &path1) {
|
|
if (path0.empty()) {
|
|
return path1;
|
|
} else {
|
|
// check '/'
|
|
char lastChar = *path0.rbegin();
|
|
if (lastChar != '/') {
|
|
return path0 + std::string("/") + path1;
|
|
} else {
|
|
return path0 + path1;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string ExpandFilePath(const std::string &filepath) {
|
|
#ifdef _WIN32
|
|
DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
|
|
char *str = new char[len];
|
|
ExpandEnvironmentStringsA(filepath.c_str(), str, len);
|
|
|
|
std::string s(str);
|
|
|
|
delete[] str;
|
|
|
|
return s;
|
|
#else
|
|
|
|
#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
|
|
defined(__ANDROID__) || defined(__EMSCRIPTEN__)
|
|
// no expansion
|
|
std::string s = filepath;
|
|
#else
|
|
std::string s;
|
|
wordexp_t p;
|
|
|
|
if (filepath.empty()) {
|
|
return "";
|
|
}
|
|
|
|
// Quote the string to keep any spaces in filepath intact.
|
|
std::string quoted_path = "\"" + filepath + "\"";
|
|
// char** w;
|
|
int ret = wordexp(quoted_path.c_str(), &p, 0);
|
|
if (ret) {
|
|
// err
|
|
s = filepath;
|
|
return s;
|
|
}
|
|
|
|
// Use first element only.
|
|
if (p.we_wordv) {
|
|
s = std::string(p.we_wordv[0]);
|
|
wordfree(&p);
|
|
} else {
|
|
s = filepath;
|
|
}
|
|
|
|
#endif
|
|
|
|
return s;
|
|
#endif
|
|
}
|
|
|
|
static std::string FindFile(const std::vector<std::string> &paths,
|
|
const std::string &filepath) {
|
|
for (size_t i = 0; i < paths.size(); i++) {
|
|
std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
|
|
if (FileExists(absPath)) {
|
|
return absPath;
|
|
}
|
|
}
|
|
|
|
return std::string();
|
|
}
|
|
|
|
#if 0
|
|
static std::string GetBaseDir(const std::string &filepath) {
|
|
if (filepath.find_last_of("/\\") != std::string::npos)
|
|
return filepath.substr(0, filepath.find_last_of("/\\"));
|
|
return "";
|
|
}
|
|
#endif
|
|
|
|
static int GetSlotId(const std::string &name) {
|
|
if (name.rfind("TEXCOORD_", 0) == 0) {
|
|
int id = 0;
|
|
sscanf(name.c_str(), "TEXCOORD_%d", &id);
|
|
return id;
|
|
} else if (name.rfind("WEIGHTS_", 0) == 0) {
|
|
int id = 0;
|
|
sscanf(name.c_str(), "WEIGHTS_%d", &id);
|
|
return id;
|
|
} else if (name.rfind("JOINTS_", 0) == 0) {
|
|
int id = 0;
|
|
sscanf(name.c_str(), "JOINTS_%d", &id);
|
|
return id;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static bool IsAttributeSupported(const std::string &name) {
|
|
constexpr int max_slots = 8;
|
|
|
|
if (name.compare("POSITION") == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (name.compare("NORMAL") == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (name.compare("TANGENT") == 0) {
|
|
return true;
|
|
}
|
|
|
|
for (int i = 0; i < max_slots; i++) {
|
|
std::string n = "TEXCOORD_" + std::to_string(i);
|
|
if (n.compare(name) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < max_slots; i++) {
|
|
std::string n = "WEIGHTS_" + std::to_string(i);
|
|
if (n.compare(name) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < max_slots; i++) {
|
|
std::string n = "JOINTS_" + std::to_string(i);
|
|
if (n.compare(name) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static float Unpack(const unsigned char *ptr, int type) {
|
|
if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
|
unsigned char data = *ptr;
|
|
return float(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_BYTE) {
|
|
char data = static_cast<char>(*ptr);
|
|
return float(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
|
uint16_t data = *reinterpret_cast<const uint16_t *>(ptr);
|
|
return float(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
|
int16_t data = *reinterpret_cast<const int16_t *>(ptr);
|
|
return float(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
|
float data = *reinterpret_cast<const float *>(ptr);
|
|
return data;
|
|
} else {
|
|
std::cerr << "???: Unsupported type: " << PrintComponentType(type) << "\n";
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
static uint32_t UnpackIndex(const unsigned char *ptr, int type) {
|
|
if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
|
unsigned char data = *ptr;
|
|
return uint32_t(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_BYTE) {
|
|
char data = static_cast<char>(*ptr);
|
|
return uint32_t(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
|
uint16_t data = *reinterpret_cast<const uint16_t *>(ptr);
|
|
return uint32_t(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
|
int16_t data = *reinterpret_cast<const int16_t *>(ptr);
|
|
return uint32_t(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_INT) {
|
|
// TODO(syoyo): Check overflow(2G+ index)
|
|
int32_t data = *reinterpret_cast<const int32_t *>(ptr);
|
|
return uint32_t(data);
|
|
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
|
uint32_t data = *reinterpret_cast<const uint32_t *>(ptr);
|
|
return data;
|
|
} else {
|
|
std::cerr << "???: Unsupported type: " << PrintComponentType(type) << "\n";
|
|
return static_cast<uint32_t>(-1);
|
|
}
|
|
}
|
|
|
|
static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
|
example::MeshPrim *out) {
|
|
for (size_t i = 0; i < mesh.primitives.size(); i++) {
|
|
const tinygltf::Primitive &primitive = mesh.primitives[i];
|
|
|
|
if (primitive.indices < 0) {
|
|
std::cerr << "Primitive indices must be provided\n";
|
|
return false;
|
|
}
|
|
|
|
// indices.
|
|
{
|
|
const tinygltf::Accessor &indexAccessor =
|
|
model.accessors[size_t(primitive.indices)];
|
|
|
|
size_t num_elements = indexAccessor.count;
|
|
std::cout << "index.elements = " << num_elements << "\n";
|
|
|
|
size_t byte_stride = ComponentTypeByteSize(indexAccessor.componentType);
|
|
|
|
const tinygltf::BufferView &indexBufferView =
|
|
model.bufferViews[size_t(indexAccessor.bufferView)];
|
|
|
|
// should be 34963(ELEMENT_ARRAY_BUFFER)
|
|
std::cout << "index.target = " << PrintTarget(indexBufferView.target) << "\n";
|
|
if (indexBufferView.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
|
|
std::cerr << "indexBufferView.target must be ELEMENT_ARRAY_BUFFER\n";
|
|
return false;
|
|
}
|
|
|
|
const tinygltf::Buffer &indexBuffer =
|
|
model.buffers[size_t(indexBufferView.buffer)];
|
|
|
|
std::vector<uint32_t> indices;
|
|
|
|
for (size_t k = 0; k < num_elements; k++) {
|
|
|
|
// TODO(syoyo): out-of-bounds check.
|
|
const unsigned char *ptr = indexBuffer.data.data() +
|
|
indexBufferView.byteOffset + (k * byte_stride) +
|
|
indexAccessor.byteOffset;
|
|
|
|
uint32_t idx = UnpackIndex(ptr, indexAccessor.componentType);
|
|
|
|
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
|
|
|
|
indices.push_back(idx);
|
|
}
|
|
|
|
out->indices = indices;
|
|
out->indices_type = indexAccessor.componentType;
|
|
}
|
|
|
|
// attributes
|
|
{
|
|
std::map<std::string, int>::const_iterator it(
|
|
primitive.attributes.begin());
|
|
std::map<std::string, int>::const_iterator itEnd(
|
|
primitive.attributes.end());
|
|
|
|
for (; it != itEnd; it++) {
|
|
// it->first would be "POSITION", "NORMAL", "TEXCOORD_0", ...
|
|
if (!IsAttributeSupported(it->first)) {
|
|
std::cout << "Unsupported attribute: " << it->first << "\n";
|
|
continue;
|
|
}
|
|
|
|
if (size_t(it->second) >= model.accessors.size()) {
|
|
std::cerr << "Invalid accessor id: " << it->second << "\n";
|
|
return false;
|
|
}
|
|
|
|
const tinygltf::Accessor &accessor =
|
|
model.accessors[size_t(it->second)];
|
|
|
|
size_t elem_size = 1;
|
|
if (accessor.type == TINYGLTF_TYPE_SCALAR) {
|
|
elem_size = 1;
|
|
} else if (accessor.type == TINYGLTF_TYPE_VEC2) {
|
|
elem_size = 2;
|
|
} else if (accessor.type == TINYGLTF_TYPE_VEC3) {
|
|
elem_size = 3;
|
|
} else if (accessor.type == TINYGLTF_TYPE_VEC4) {
|
|
elem_size = 4;
|
|
} else {
|
|
std::cerr << "Invalid or unsupported accessor type: "
|
|
<< PrintType(accessor.type) << "\n";
|
|
return false;
|
|
}
|
|
|
|
std::cout << PrintComponentType(accessor.componentType) << "\n";
|
|
|
|
size_t byte_stride = ComponentTypeByteSize(accessor.componentType);
|
|
|
|
std::cout << "attribute: " << it->first << "\n";
|
|
// if (gGLProgramState.attribs[it->first] >= 0) {
|
|
// Compute byteStride from Accessor + BufferView combination.
|
|
int byteStride =
|
|
accessor.ByteStride(model.bufferViews[size_t(accessor.bufferView)]);
|
|
assert(byteStride != -1);
|
|
|
|
std::cout << "byteOffset: " << accessor.byteOffset << "\n";
|
|
|
|
const tinygltf::BufferView &bufferView =
|
|
model.bufferViews[size_t(accessor.bufferView)];
|
|
const tinygltf::Buffer &buffer =
|
|
model.buffers[size_t(bufferView.buffer)];
|
|
|
|
size_t num_elems = accessor.count * elem_size;
|
|
|
|
example::VertexAttrib attrib;
|
|
for (size_t k = 0; k < num_elems; k++) {
|
|
// TODO(syoyo): out-of-bounds check.
|
|
const unsigned char *ptr = buffer.data.data() +
|
|
bufferView.byteOffset + (k * byte_stride) +
|
|
accessor.byteOffset;
|
|
float value = Unpack(ptr, accessor.componentType);
|
|
std::cout << "[" << k << "] value = " << value << "\n";
|
|
attrib.data.push_back(value);
|
|
}
|
|
attrib.component_type = accessor.componentType;
|
|
attrib.data_type = accessor.type;
|
|
attrib.name = it->first;
|
|
|
|
if (attrib.name.compare("POSITION") == 0) {
|
|
out->position = attrib;
|
|
} else if (attrib.name.compare("NORMAL") == 0) {
|
|
out->normal = attrib;
|
|
} else if (attrib.name.compare("TANGENT") == 0) {
|
|
out->tangent = attrib;
|
|
} else if (attrib.name.rfind("TEXCOORD_", 0) == 0) {
|
|
int id = GetSlotId(attrib.name);
|
|
std::cout << "texcoord[" << id << "]\n";
|
|
out->texcoords[id] = attrib;
|
|
} else if (attrib.name.rfind("JOINTS_", 0) == 0) {
|
|
int id = GetSlotId(attrib.name);
|
|
std::cout << "joints[" << id << "]\n";
|
|
out->joints[id] = attrib;
|
|
} else if (attrib.name.rfind("WEIGHTS_", 0) == 0) {
|
|
int id = GetSlotId(attrib.name);
|
|
std::cout << "weights[" << id << "]\n";
|
|
out->weights[id] = attrib;
|
|
} else {
|
|
std::cerr << "???: attrib.name = " << attrib.name << "\n";
|
|
return false;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
const tinygltf::Accessor &indexAccessor =
|
|
model.accessors[size_t(primitive.indices)];
|
|
(void)indexAccessor;
|
|
PrintMode(primitive.mode);
|
|
if (primitive.mode != TINYGLTF_MODE_TRIANGLES) {
|
|
std::cerr << "Supported Primitive mode is TRIANGLES only at the moment\n";
|
|
return false;
|
|
}
|
|
|
|
out->mode = primitive.mode;
|
|
out->name = mesh.name;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if 0
|
|
static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
|
|
std::vector<example::MeshPrim> *outs) {
|
|
// Get .bin data
|
|
{
|
|
if (model.buffers.size() != 1) {
|
|
std::cerr << "Single element of `buffers` is supported at the moment.\n";
|
|
return false;
|
|
}
|
|
|
|
const tinygltf::Buffer &buffer = model.buffers[0];
|
|
if (buffer.uri.empty()) {
|
|
std::cerr << "buffer.uri must be a filepath.\n";
|
|
return false;
|
|
}
|
|
|
|
if (buffer.data.size() < 4) {
|
|
std::cerr << "Invalid buffer.byteLength.\n";
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> search_paths;
|
|
search_paths.push_back(asset_path);
|
|
|
|
std::string abs_filepath = FindFile(search_paths, buffer.uri);
|
|
std::vector<uint8_t> bin = LoadBin(buffer.uri);
|
|
if (bin.size() != buffer.data.size()) {
|
|
std::cerr << "Byte size mismatch. Failed to load file: " << buffer.uri
|
|
<< "\n";
|
|
std::cerr << " .bin size = " << bin.size() << ", size in 'buffer.uri' = " << buffer.data.size() << "\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (const auto &mesh : model.meshes) {
|
|
std::cout << "mesh.name: " << mesh.name << "\n";
|
|
|
|
example::MeshPrim output;
|
|
bool ret = DumpMesh(model, mesh, &output);
|
|
if (!ret) {
|
|
return false;
|
|
}
|
|
|
|
outs->push_back(output);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc < 2) {
|
|
std::cout << "mesh-dump input.gltf" << std::endl;
|
|
}
|
|
|
|
#if 0
|
|
|
|
tinygltf::Model model;
|
|
tinygltf::TinyGLTF loader;
|
|
std::string err;
|
|
std::string warn;
|
|
|
|
#ifdef _WIN32
|
|
std::string input_filename(argv[1] ? argv[1]
|
|
: "../../../models/Cube/Cube.gltf");
|
|
#else
|
|
std::string input_filename(argv[1] ? argv[1] : "../../models/Cube/Cube.gltf");
|
|
#endif
|
|
|
|
std::string ext = GetFilePathExtension(input_filename);
|
|
|
|
{
|
|
bool ret = false;
|
|
if (ext.compare("glb") == 0) {
|
|
// assume binary glTF.
|
|
ret = loader.LoadBinaryFromFile(&model, &err, &warn,
|
|
input_filename.c_str());
|
|
} else {
|
|
// assume ascii glTF.
|
|
ret =
|
|
loader.LoadASCIIFromFile(&model, &err, &warn, input_filename.c_str());
|
|
}
|
|
|
|
if (!warn.empty()) {
|
|
printf("Warn: %s\n", warn.c_str());
|
|
}
|
|
|
|
if (!err.empty()) {
|
|
printf("ERR: %s\n", err.c_str());
|
|
}
|
|
if (!ret) {
|
|
printf("Failed to load .glTF : %s\n", argv[1]);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
json j;
|
|
{
|
|
std::ifstream i(input_filename);
|
|
i >> j;
|
|
}
|
|
std::cout << "j = " << j << "\n";
|
|
|
|
json j_patch = R"([
|
|
{ "op": "add", "path": "/buffers/-", "value": {
|
|
"name": "plane/data",
|
|
"byteLength": 480,
|
|
"uri": "plane1.bin"
|
|
} }
|
|
])"_json;
|
|
|
|
// a JSON value
|
|
json j_original = R"({
|
|
"baz": ["one", "two", "three"],
|
|
"foo": "bar"
|
|
})"_json;
|
|
|
|
//json j_patch = R"([
|
|
// { "op": "remove", "path": "/buffers" }
|
|
//])"_json;
|
|
|
|
std::cout << "patch = " << j_patch.dump(2) << "\n";
|
|
|
|
json j_ret = j.patch(j_patch);
|
|
std::cout << "patched = " << j_ret.dump(2) << "\n";
|
|
|
|
std::string basedir = GetBaseDir(input_filename);
|
|
std::vector<example::MeshPrim> meshes;
|
|
bool ret = ExtractMesh(basedir, model, &meshes);
|
|
|
|
size_t n = 0;
|
|
for (const auto &mesh : meshes) {
|
|
// Assume no duplicated name in .glTF data
|
|
std::string filename;
|
|
if (mesh.name.empty()) {
|
|
filename = "untitled-" + std::to_string(n) + ".obj";
|
|
} else {
|
|
filename = mesh.name + ".obj";
|
|
}
|
|
|
|
bool flip_y = true; // flip texcoord Y?
|
|
bool ok = example::SaveAsObjMesh(filename, mesh,);
|
|
if (!ok) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
n++;
|
|
}
|
|
|
|
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
#else
|
|
|
|
{
|
|
std::string input_filename(argv[1]);
|
|
|
|
// Require facevarying layout?
|
|
// false = try to keep GL-like mesh data as much as possible.
|
|
// true = reorder vertex data and re-assign vertex indices.
|
|
bool facevarying = false;
|
|
|
|
example::MeshPrim mesh;
|
|
bool ok = example::LoadObjMesh(input_filename, facevarying, &mesh);
|
|
if (!ok) {
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
PrintMeshPrim(mesh);
|
|
|
|
std::string output_filename("output.gltf");
|
|
|
|
ok = example::SaveAsGLTFMesh(output_filename, mesh);
|
|
if (!ok) {
|
|
std::cerr << "Failed to save mesh as glTF\n";
|
|
return EXIT_FAILURE;
|
|
}
|
|
std::cout << "Write glTF: " << output_filename << "\n";
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
#endif
|
|
|
|
}
|