mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-08-12 04:49:02 +08:00
update tinyobjloader.
mesh-modify experiment.
This commit is contained in:
parent
8ec9af7f2e
commit
a5416707c9
File diff suppressed because it is too large
Load Diff
@ -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
|
|
4
examples/mesh-modify/Makefile
Normal file
4
examples/mesh-modify/Makefile
Normal 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
|
@ -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
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
372
examples/mesh-modify/mesh-util.cc
Normal file
372
examples/mesh-modify/mesh-util.cc
Normal 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
|
55
examples/mesh-modify/mesh-util.hh
Normal file
55
examples/mesh-modify/mesh-util.hh
Normal 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
|
7
examples/mesh-modify/meson.build
Normal file
7
examples/mesh-modify/meson.build
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user