update tinyobjloader.

mesh-modify experiment.
This commit is contained in:
Syoyo Fujita 2020-03-09 22:34:50 +09:00
parent 8ec9af7f2e
commit a5416707c9
8 changed files with 2523 additions and 457 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
EXTRA_CXXFLAGS := -fsanitize=address -fno-omit-frame-pointer -Wall -Werror -Weverything -Wno-c++11-long-long -Wno-c++98-compat
all:
clang++ -std=c++11 -I../../ $(EXTRA_CXXFLAGS) -o mesh-dump mesh-dump.cc

View File

@ -0,0 +1,4 @@
EXTRA_CXXFLAGS := -fsanitize=address -fno-omit-frame-pointer -Wall -Werror -Weverything -Wno-c++11-long-long -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded
all:
clang++ -std=c++11 -g -O1 -I../../ -I../common $(EXTRA_CXXFLAGS) -o mesh-modify mesh-modify.cc mesh-util.cc

View File

@ -1,12 +1,12 @@
# Mesh dump experiment # Mesh modify experiment
Sometimes we want to tweak mesh attributes(e.g. vertex position, uv coord, etc). Sometimes we want to tweak mesh attributes(e.g. vertex position, uv coord, etc).
glTF itself does not allow ASCII representation of such data. glTF itself does not allow ASCII representation of such data.
This example show how to This example show how to
- Export mesh data from .bin to .csv - Export mesh data from .bin to .obj
- Import mesh data to .bin(update corresponding buffer data) from .csv - Import mesh data to .bin(update corresponding buffer data) from .obj
## Requirement ## Requirement

View File

@ -7,6 +7,19 @@
#include <string> #include <string>
#include <vector> #include <vector>
#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
#define TINYGLTF_IMPLEMENTATION #define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
@ -17,6 +30,8 @@
#include "tiny_gltf.h" #include "tiny_gltf.h"
#endif #endif
#include "mesh-util.hh"
namespace { namespace {
static std::string PrintMode(int mode) { static std::string PrintMode(int mode) {
@ -36,7 +51,6 @@ static std::string PrintMode(int mode) {
return "**UNKNOWN**"; return "**UNKNOWN**";
} }
#if 0
static std::string PrintTarget(int target) { static std::string PrintTarget(int target) {
if (target == 34962) { if (target == 34962) {
return "GL_ARRAY_BUFFER"; return "GL_ARRAY_BUFFER";
@ -46,7 +60,6 @@ static std::string PrintTarget(int target) {
return "**UNKNOWN**"; return "**UNKNOWN**";
} }
} }
#endif
static std::string PrintType(int ty) { static std::string PrintType(int ty) {
if (ty == TINYGLTF_TYPE_SCALAR) { if (ty == TINYGLTF_TYPE_SCALAR) {
@ -93,6 +106,7 @@ static std::string PrintComponentType(int ty) {
return "**UNKNOWN**"; return "**UNKNOWN**";
} }
#if 0
// TODO(syoyo): Support sparse accessor(sparse vertex attribute). // TODO(syoyo): Support sparse accessor(sparse vertex attribute).
// TODO(syoyo): Support more data type // TODO(syoyo): Support more data type
struct VertexAttrib { struct VertexAttrib {
@ -123,7 +137,11 @@ struct MeshPrim {
std::map<int, VertexAttrib> weights; // <slot, attrib> std::map<int, VertexAttrib> weights; // <slot, attrib>
std::map<int, VertexAttrib> std::map<int, VertexAttrib>
joints; // <slot, attrib> store data as float type 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
static std::string GetFilePathExtension(const std::string &FileName) { static std::string GetFilePathExtension(const std::string &FileName) {
if (FileName.find_last_of(".") != std::string::npos) if (FileName.find_last_of(".") != std::string::npos)
@ -356,13 +374,86 @@ static float Unpack(const unsigned char *ptr, int type) {
} }
} }
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, static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
MeshPrim *out) { example::MeshPrim *out) {
for (size_t i = 0; i < mesh.primitives.size(); i++) { for (size_t i = 0; i < mesh.primitives.size(); i++) {
const tinygltf::Primitive &primitive = mesh.primitives[i]; const tinygltf::Primitive &primitive = mesh.primitives[i];
if (primitive.indices < 0) return false; 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( std::map<std::string, int>::const_iterator it(
primitive.attributes.begin()); primitive.attributes.begin());
@ -419,7 +510,7 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
size_t num_elems = accessor.count * elem_size; size_t num_elems = accessor.count * elem_size;
VertexAttrib attrib; example::VertexAttrib attrib;
for (size_t k = 0; k < num_elems; k++) { for (size_t k = 0; k < num_elems; k++) {
// TODO(syoyo): out-of-bounds check. // TODO(syoyo): out-of-bounds check.
const unsigned char *ptr = buffer.data.data() + const unsigned char *ptr = buffer.data.data() +
@ -469,13 +560,14 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
} }
out->mode = primitive.mode; out->mode = primitive.mode;
out->name = mesh.name;
} }
return true; return true;
} }
static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model, static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
std::vector<MeshPrim> *outs) { std::vector<example::MeshPrim> *outs) {
// Get .bin data // Get .bin data
{ {
if (model.buffers.size() != 1) { if (model.buffers.size() != 1) {
@ -502,6 +594,7 @@ static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
if (bin.size() != buffer.data.size()) { if (bin.size() != buffer.data.size()) {
std::cerr << "Byte size mismatch. Failed to load file: " << buffer.uri std::cerr << "Byte size mismatch. Failed to load file: " << buffer.uri
<< "\n"; << "\n";
std::cerr << " .bin size = " << bin.size() << ", size in 'buffer.uri' = " << buffer.data.size() << "\n";
return false; return false;
} }
} }
@ -509,7 +602,7 @@ static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
for (const auto &mesh : model.meshes) { for (const auto &mesh : model.meshes) {
std::cout << "mesh.name: " << mesh.name << "\n"; std::cout << "mesh.name: " << mesh.name << "\n";
MeshPrim output; example::MeshPrim output;
bool ret = DumpMesh(model, mesh, &output); bool ret = DumpMesh(model, mesh, &output);
if (!ret) { if (!ret) {
return false; return false;
@ -528,6 +621,8 @@ int main(int argc, char **argv) {
std::cout << "mesh-dump input.gltf" << std::endl; std::cout << "mesh-dump input.gltf" << std::endl;
} }
#if 0
tinygltf::Model model; tinygltf::Model model;
tinygltf::TinyGLTF loader; tinygltf::TinyGLTF loader;
std::string err; std::string err;
@ -567,9 +662,75 @@ int main(int argc, char **argv) {
} }
} }
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::string basedir = GetBaseDir(input_filename);
std::vector<MeshPrim> meshes; std::vector<example::MeshPrim> meshes;
bool ret = ExtractMesh(basedir, model, &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++;
}
#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;
}
}
#endif
return ret ? EXIT_SUCCESS : EXIT_FAILURE; return ret ? EXIT_SUCCESS : EXIT_FAILURE;
} }

View File

@ -0,0 +1,372 @@
#include "mesh-util.hh"
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
// ../common/
#define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h"
namespace example {
// TODO(syoyo): Specify CCW(Counter-ClockWise) or CW(ClockWise)
static void CalcNormal(float N[3], float v0[3], float v1[3], float v2[3]) {
float v10[3];
v10[0] = v1[0] - v0[0];
v10[1] = v1[1] - v0[1];
v10[2] = v1[2] - v0[2];
float v20[3];
v20[0] = v2[0] - v0[0];
v20[1] = v2[1] - v0[1];
v20[2] = v2[2] - v0[2];
N[0] = v20[1] * v10[2] - v20[2] * v10[1];
N[1] = v20[2] * v10[0] - v20[0] * v10[2];
N[2] = v20[0] * v10[1] - v20[1] * v10[0];
float len2 = N[0] * N[0] + N[1] * N[1] + N[2] * N[2];
if (len2 > 0.0f) {
float len = sqrtf(len2);
N[0] /= len;
N[1] /= len;
N[2] /= len;
}
}
static std::string make_triple(int i, bool has_vn, bool has_vt) {
std::stringstream ss;
if (has_vn && has_vt) {
ss << i << "/" << i << "/" << i;
} else if (has_vn) {
ss << i << "//" << i;
} else if (has_vt) {
ss << i << "/" << i;
} else {
ss << i;
}
return ss.str();
}
bool SaveAsObjMesh(const std::string &filename, const MeshPrim &mesh,
bool flip_texcoord_y) {
std::ofstream ofs(filename);
if (!ofs) {
std::cerr << "Failed to open .obj to write: " << filename << "\n";
return false;
}
bool has_vn = false;
bool has_vt = false;
has_vn = mesh.normal.data.size() == mesh.position.data.size();
has_vt = mesh.texcoords.count(0) &&
(mesh.texcoords.at(0).data.size() > 0); // TEXCOORD_0
// v
for (size_t i = 0; i < mesh.position.data.size() / 3; i++) {
ofs << "v " << mesh.position.data[3 * i + 0] << " "
<< mesh.position.data[3 * i + 1] << " " << mesh.position.data[3 * i + 2]
<< "\n";
}
// vn
for (size_t i = 0; i < mesh.normal.data.size() / 3; i++) {
ofs << "vn " << mesh.normal.data[3 * i + 0] << " "
<< mesh.normal.data[3 * i + 1] << " " << mesh.normal.data[3 * i + 2]
<< "\n";
}
assert((mesh.texcoords.at(0).data.size() / 2) ==
(mesh.position.data.size() / 3));
// vt
for (size_t i = 0; i < mesh.texcoords.at(0).data.size() / 2; i++) {
float y = mesh.texcoords.at(0).data[2 * i + 1];
if (flip_texcoord_y) {
y = 1.0f - y;
}
ofs << "vt " << mesh.texcoords.at(0).data[2 * i + 0] << " " << y << "\n";
}
// v, vn, vt has same index
for (size_t i = 0; i < mesh.indices.size() / 3; i++) {
// .obj's index start with 1.
int f0 = int(mesh.indices[3 * i + 0]) + 1;
int f1 = int(mesh.indices[3 * i + 1]) + 1;
int f2 = int(mesh.indices[3 * i + 2]) + 1;
ofs << "f " << make_triple(f0, has_vn, has_vt) << " "
<< make_triple(f1, has_vn, has_vt) << " "
<< make_triple(f2, has_vn, has_vt) << "\n";
}
// TODO(syoyo): Write joints/weights
return true;
}
bool RequireFacevaringLayout(const tinyobj::attrib_t &attrib,
const std::vector<tinyobj::shape_t> &shapes) {
// Check if all normals and texcoords has same index with vertex
if ((attrib.texcoords.size() / 3) != attrib.vertices.size() / 3) {
std::cerr << "Texcoords and Vertices length mismatch. Mesh data cannot be "
"represented as non-facevarying\n";
return true;
}
if ((attrib.normals.size() / 2) != attrib.vertices.size() / 3) {
std::cerr << "Normals and Vertices length mismatch. Mesh data cannot be "
"represented as non-facevarying\n";
return true;
}
// Check indices.
for (size_t s = 0; s < shapes.size(); s++) {
const tinyobj::shape_t &shape = shapes[s];
for (size_t f = 0; f < shape.mesh.indices.size() / 3; f++) {
tinyobj::index_t idx0 = shape.mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shape.mesh.indices[3 * f + 1];
tinyobj::index_t idx2 = shape.mesh.indices[3 * f + 2];
// index must be all same
if ((idx0.vertex_index != idx0.normal_index) ||
(idx0.vertex_index != idx0.texcoord_index)) {
return true;
}
if ((idx1.vertex_index != idx1.normal_index) ||
(idx1.vertex_index != idx1.texcoord_index)) {
return true;
}
if ((idx2.vertex_index != idx2.normal_index) ||
(idx2.vertex_index != idx2.texcoord_index)) {
return true;
}
}
}
return false;
}
bool LoadObjMesh(const std::string &filename, bool facevarying,
MeshPrim *mesh) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn;
std::string err;
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
filename.c_str(), /* base_path */ nullptr,
/* triangulate */ true);
if (!warn.empty()) {
std::cout << "WARN: " << warn << std::endl;
}
if (!err.empty()) {
std::cerr << "ERR: " << err << std::endl;
}
if (!ret) {
std::cerr << "Failed to load wavefront .obj\n";
return false;
}
if (!facevarying) {
// TODO(syoyo): Allow per-shape non-facevarying layout
facevarying = RequireFacevaringLayout(attrib, shapes);
}
bool has_texcoord = (attrib.texcoords.size() > 0);
bool has_normal = (attrib.normals.size() > 0);
float bmin[3];
float bmax[3];
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
// reorder texcoords and normals so that it has same indexing to vertices.
if (facevarying) {
mesh->position.data.clear();
mesh->normal.data.clear();
mesh->texcoords[0] = VertexAttrib();
// Concat shapes
for (size_t s = 0; s < shapes.size(); s++) {
const tinyobj::shape_t &shape = shapes[s];
for (size_t f = 0; f < shape.mesh.indices.size() / 3; f++) {
tinyobj::index_t idx0 = shape.mesh.indices[3 * f + 0];
tinyobj::index_t idx1 = shape.mesh.indices[3 * f + 1];
tinyobj::index_t idx2 = shape.mesh.indices[3 * f + 2];
float tc[3][2];
if (has_texcoord) {
if ((idx0.texcoord_index < 0) || (idx1.texcoord_index < 0) ||
(idx2.texcoord_index < 0)) {
// This face does contain valid texcoord
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
} else {
assert(attrib.texcoords.size() >
size_t(2 * idx0.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx1.texcoord_index + 1));
assert(attrib.texcoords.size() >
size_t(2 * idx2.texcoord_index + 1));
// Flip Y coord.
tc[0][0] = attrib.texcoords[2 * idx0.texcoord_index];
tc[0][1] = 1.0f - attrib.texcoords[2 * idx0.texcoord_index + 1];
tc[1][0] = attrib.texcoords[2 * idx1.texcoord_index];
tc[1][1] = 1.0f - attrib.texcoords[2 * idx1.texcoord_index + 1];
tc[2][0] = attrib.texcoords[2 * idx2.texcoord_index];
tc[2][1] = 1.0f - attrib.texcoords[2 * idx2.texcoord_index + 1];
}
} else {
tc[0][0] = 0.0f;
tc[0][1] = 0.0f;
tc[1][0] = 0.0f;
tc[1][1] = 0.0f;
tc[2][0] = 0.0f;
tc[2][1] = 0.0f;
}
float v[3][3];
for (int k = 0; k < 3; k++) {
int f0 = idx0.vertex_index;
int f1 = idx1.vertex_index;
int f2 = idx2.vertex_index;
assert(f0 >= 0);
assert(f1 >= 0);
assert(f2 >= 0);
v[0][k] = attrib.vertices[3 * f0 + k];
v[1][k] = attrib.vertices[3 * f1 + k];
v[2][k] = attrib.vertices[3 * f2 + k];
bmin[k] = std::min(v[0][k], bmin[k]);
bmin[k] = std::min(v[1][k], bmin[k]);
bmin[k] = std::min(v[2][k], bmin[k]);
bmax[k] = std::max(v[0][k], bmax[k]);
bmax[k] = std::max(v[1][k], bmax[k]);
bmax[k] = std::max(v[2][k], bmax[k]);
}
float n[3][3];
if (has_normal) {
if ((idx0.normal_index < 0) || (idx1.normal_index < 0) ||
(idx2.normal_index < 0)) {
// This face does contain valid normal
CalcNormal(n[0], v[0], v[1], v[2]);
n[1][0] = n[0][0];
n[1][1] = n[0][1];
n[1][2] = n[0][2];
n[2][0] = n[0][0];
n[2][1] = n[0][1];
n[2][2] = n[0][2];
} else {
int nf0 = idx0.normal_index;
int nf1 = idx1.normal_index;
int nf2 = idx2.normal_index;
for (int k = 0; k < 3; k++) {
assert(size_t(3 * nf0 + k) < attrib.normals.size());
assert(size_t(3 * nf1 + k) < attrib.normals.size());
assert(size_t(3 * nf2 + k) < attrib.normals.size());
n[0][k] = attrib.normals[3 * nf0 + k];
n[1][k] = attrib.normals[3 * nf1 + k];
n[2][k] = attrib.normals[3 * nf2 + k];
}
}
}
mesh->position.data.push_back(v[0][0]);
mesh->position.data.push_back(v[0][1]);
mesh->position.data.push_back(v[0][2]);
mesh->position.data.push_back(v[1][0]);
mesh->position.data.push_back(v[1][1]);
mesh->position.data.push_back(v[1][2]);
mesh->position.data.push_back(v[2][0]);
mesh->position.data.push_back(v[2][1]);
mesh->position.data.push_back(v[2][2]);
mesh->normal.data.push_back(n[0][0]);
mesh->normal.data.push_back(n[0][1]);
mesh->normal.data.push_back(n[0][2]);
mesh->normal.data.push_back(n[1][0]);
mesh->normal.data.push_back(n[1][1]);
mesh->normal.data.push_back(n[1][2]);
mesh->normal.data.push_back(n[2][0]);
mesh->normal.data.push_back(n[2][1]);
mesh->normal.data.push_back(n[2][2]);
mesh->texcoords[0].data.push_back(tc[0][0]);
mesh->texcoords[0].data.push_back(tc[0][1]);
mesh->texcoords[0].data.push_back(tc[1][0]);
mesh->texcoords[0].data.push_back(tc[1][1]);
mesh->texcoords[0].data.push_back(tc[2][0]);
mesh->texcoords[0].data.push_back(tc[2][1]);
size_t idx = mesh->indices.size();
mesh->indices.push_back(int(idx) + 0);
mesh->indices.push_back(int(idx) + 1);
mesh->indices.push_back(int(idx) + 2);
}
}
} else {
// position/texcoord/normal can be represented in shared vertex manner
mesh->position.data.clear();
for (size_t v = 0; v < attrib.vertices.size(); v++) {
mesh->position.data.push_back(attrib.vertices[v]);
}
mesh->normal.data.clear();
for (size_t v = 0; v < attrib.normals.size(); v++) {
mesh->normal.data.push_back(attrib.normals[v]);
}
mesh->texcoords[0] = VertexAttrib();
for (size_t v = 0; v < attrib.texcoords.size(); v++) {
mesh->texcoords[0].data.push_back(attrib.texcoords[v]);
}
mesh->indices.clear();
size_t face_index_offset = 0;
for (size_t s = 0; s < shapes.size(); s++) {
const tinyobj::shape_t &shape = shapes[s];
for (size_t f = 0; f < shape.mesh.indices.size(); f++) {
mesh->indices.push_back(uint32_t(face_index_offset) + uint32_t(shape.mesh.indices[f].vertex_index));
}
face_index_offset = mesh->indices.size();
}
}
return true;
}
} // namespace example

View File

@ -0,0 +1,55 @@
#pragma once
#include <vector>
#include <map>
namespace example {
// 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; // e.g. 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
};
///
/// Save MeshPrim as wavefront .obj
///
bool SaveAsObjMesh(const std::string &filename, const MeshPrim &mesh);
///
/// Loads .obj and convert to MeshPrim
///
/// @param[in] facevarying Construct mesh with facevarying vertex layout(default false)
///
bool LoadObjMesh(const std::string &filename, bool facevarying, MeshPrim *mesh);
} // namespace example

View File

@ -0,0 +1,7 @@
project('mesh-modify', 'cpp', default_options : ['cpp_std=c++11'])
thread_dep = dependency('threads')
incdir = include_directories(['../../', '../common'])
executable('mesh-modify', ['mesh-modify.cc', 'mesh-util.cc'], include_directories : incdir, dependencies : thread_dep)