mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-08-11 17:48:58 +08:00
Assign name for accessor.
Improve .obj export. Use clipp to parse cmd options. Add some handy python scripts.
This commit is contained in:
parent
cf30d42cbc
commit
0ee2120965
@ -71,6 +71,7 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
|
||||
* [glview](examples/glview) : Simple glTF geometry viewer.
|
||||
* [validator](examples/validator) : Simple glTF validator with JSON schema.
|
||||
* [basic](examples/basic) : Basic glTF viewer with texturing support.
|
||||
* [mesh-conv](examples/mesh-conv) : Convert glTF mesh to wavefront .obj, wavefront .obj to glTF mesh.
|
||||
|
||||
## Projects using TinyGLTF
|
||||
|
||||
@ -216,3 +217,7 @@ We may be better to introduce bounded memory size checking when parsing glTF dat
|
||||
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
|
||||
* RapidJSON : Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. http://rapidjson.org/
|
||||
* dlib(uridecode, uriencode) : Copyright (C) 2003 Davis E. King Boost Software License 1.0. http://dlib.net/dlib/server/server_http.cpp.html
|
||||
|
||||
### Used in examples
|
||||
|
||||
* clipp: MIT License. https://github.com/muellan/clipp
|
||||
|
20
examples/common/clipp.LICENSE
Normal file
20
examples/common/clipp.LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 André Müller; foss@andremueller-online.de
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
7024
examples/common/clipp.h
Normal file
7024
examples/common/clipp.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,34 @@
|
||||
# Mesh modify experiment
|
||||
|
||||
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 so we need to write a converter.
|
||||
|
||||
This example show how to
|
||||
|
||||
- Export mesh data from .bin to .obj
|
||||
- Import mesh data to .bin(update corresponding buffer data) from .obj
|
||||
|
||||
## Features
|
||||
|
||||
* Support skin weights(`JOINTS_N`, `WEIGHTS_N`)
|
||||
|
||||
## Supported attributes
|
||||
|
||||
* [x] POSITION
|
||||
* [x] NORMAL
|
||||
* [x] TANGENT
|
||||
* [ ] COLOR (vertex color)
|
||||
* [x] TEXCOORD_N
|
||||
* Only single texcoord(uv set) is supported
|
||||
* Specify `--uvset 1` to specify which UV to use.
|
||||
* [x] WEIGHTS_N, JOINTS_N
|
||||
|
||||
## Usage
|
||||
|
||||
### Wavefront .obj to glTF
|
||||
|
||||
```
|
||||
$ mesh-modify obj2gltf input.obj
|
||||
$ mesh-modify --op=obj2gltf input.obj
|
||||
```
|
||||
|
||||
All shapes in .obj are concatenated and create single glTF mesh.
|
||||
@ -26,7 +41,7 @@ Buffer is stored as external file(`.bin`)
|
||||
### glTF to Wavefront .obj
|
||||
|
||||
```
|
||||
$ mesh-modify gltf2obj input.gltf
|
||||
$ mesh-modify --op=gltf2obj input.gltf
|
||||
```
|
||||
|
||||
.obj will be created for each glTF Mesh.
|
||||
|
@ -2,11 +2,11 @@
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#if !defined(__ANDROID__) && !defined(_WIN32)
|
||||
#include <wordexp.h>
|
||||
@ -18,6 +18,7 @@
|
||||
#endif
|
||||
|
||||
#include "../../json.hpp"
|
||||
#include "../common/clipp.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@ -406,7 +407,9 @@ static uint32_t UnpackIndex(const unsigned char *ptr, int type) {
|
||||
}
|
||||
|
||||
static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||
example::MeshPrim *out) {
|
||||
bool verbose, example::MeshPrim *out) {
|
||||
out->prims.clear();
|
||||
|
||||
for (size_t i = 0; i < mesh.primitives.size(); i++) {
|
||||
const tinygltf::Primitive &primitive = mesh.primitives[i];
|
||||
|
||||
@ -415,6 +418,8 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||
return false;
|
||||
}
|
||||
|
||||
example::PrimSet out_prim;
|
||||
|
||||
// indices.
|
||||
{
|
||||
const tinygltf::Accessor &indexAccessor =
|
||||
@ -429,7 +434,8 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||
model.bufferViews[size_t(indexAccessor.bufferView)];
|
||||
|
||||
// should be 34963(ELEMENT_ARRAY_BUFFER)
|
||||
std::cout << "index.target = " << PrintTarget(indexBufferView.target) << "\n";
|
||||
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;
|
||||
@ -441,21 +447,22 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||
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;
|
||||
indexBufferView.byteOffset +
|
||||
(k * byte_stride) + indexAccessor.byteOffset;
|
||||
|
||||
uint32_t idx = UnpackIndex(ptr, indexAccessor.componentType);
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
|
||||
}
|
||||
|
||||
indices.push_back(idx);
|
||||
}
|
||||
|
||||
out->indices = indices;
|
||||
out->indices_type = indexAccessor.componentType;
|
||||
out_prim.indices = indices;
|
||||
out_prim.indices_type = indexAccessor.componentType;
|
||||
}
|
||||
|
||||
// attributes
|
||||
@ -522,7 +529,9 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||
bufferView.byteOffset + (k * byte_stride) +
|
||||
accessor.byteOffset;
|
||||
float value = Unpack(ptr, accessor.componentType);
|
||||
if (verbose) {
|
||||
std::cout << "[" << k << "] value = " << value << "\n";
|
||||
}
|
||||
attrib.data.push_back(value);
|
||||
}
|
||||
attrib.component_type = accessor.componentType;
|
||||
@ -530,28 +539,27 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||
attrib.name = it->first;
|
||||
|
||||
if (attrib.name.compare("POSITION") == 0) {
|
||||
out->position = attrib;
|
||||
out_prim.position = attrib;
|
||||
} else if (attrib.name.compare("NORMAL") == 0) {
|
||||
out->normal = attrib;
|
||||
out_prim.normal = attrib;
|
||||
} else if (attrib.name.compare("TANGENT") == 0) {
|
||||
out->tangent = attrib;
|
||||
out_prim.tangent = attrib;
|
||||
} else if (attrib.name.rfind("TEXCOORD_", 0) == 0) {
|
||||
int id = GetSlotId(attrib.name);
|
||||
std::cout << "texcoord[" << id << "]\n";
|
||||
out->texcoords[id] = attrib;
|
||||
out_prim.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;
|
||||
out_prim.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;
|
||||
out_prim.weights[id] = attrib;
|
||||
} else {
|
||||
std::cerr << "???: attrib.name = " << attrib.name << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,15 +572,18 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||
return false;
|
||||
}
|
||||
|
||||
out->mode = primitive.mode;
|
||||
out->name = mesh.name;
|
||||
out_prim.mode = primitive.mode;
|
||||
|
||||
out->prims.push_back(out_prim);
|
||||
}
|
||||
|
||||
out->name = mesh.name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
|
||||
std::vector<example::MeshPrim> *outs) {
|
||||
bool verbose, std::vector<example::MeshPrim> *outs) {
|
||||
// Get .bin data
|
||||
{
|
||||
if (model.buffers.size() != 1) {
|
||||
@ -595,11 +606,13 @@ static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
|
||||
search_paths.push_back(asset_path);
|
||||
|
||||
std::string abs_filepath = FindFile(search_paths, buffer.uri);
|
||||
std::vector<uint8_t> bin = LoadBin(buffer.uri);
|
||||
std::vector<uint8_t> bin = LoadBin(abs_filepath);
|
||||
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";
|
||||
std::cerr << " Searched absolute file path: " << abs_filepath << "\n";
|
||||
std::cerr << " .bin size = " << bin.size()
|
||||
<< ", size in 'buffer.uri' = " << buffer.data.size() << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -608,7 +621,7 @@ static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
|
||||
std::cout << "mesh.name: " << mesh.name << "\n";
|
||||
|
||||
example::MeshPrim output;
|
||||
bool ret = DumpMesh(model, mesh, &output);
|
||||
bool ret = DumpMesh(model, mesh, verbose, &output);
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
@ -622,31 +635,41 @@ static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3) {
|
||||
std::cout << "mesh-modify <op> <args>" << std::endl;
|
||||
std::cout << " op\n\n";
|
||||
std::cout << " gltf2obj input.gltf <flip_texcoord_y>\n";
|
||||
std::cout << " obj2gltf input.obj <verbose>\n";
|
||||
std::string op;
|
||||
std::string input_filename;
|
||||
std::string output_filename = "output.gltf";
|
||||
int uvset = 0;
|
||||
bool verbose = false;
|
||||
bool export_skinweight = true;
|
||||
bool no_flip_texcoord_y = false;
|
||||
|
||||
auto cli =
|
||||
(clipp::required("-i", "--input") &
|
||||
clipp::value("input filename", input_filename),
|
||||
clipp::option("-o", "--outout") &
|
||||
clipp::value("Output filename(obj2fltf)", output_filename),
|
||||
clipp::option("-v", "--verbose").set(verbose).doc("Verbose output"),
|
||||
clipp::option("--export_skinweight") &
|
||||
clipp::value("Export skin weights(gltf2obj). default true.",
|
||||
export_skinweight),
|
||||
clipp::option("--uvset").set(uvset).doc("UV set(TEXCOORD_N) to use"),
|
||||
clipp::option("--op") &
|
||||
clipp::value("operation mode(`gltf2obj`, `obj2gltf`", op),
|
||||
clipp::option("--no-flip-texcoord-y")
|
||||
.set(no_flip_texcoord_y)
|
||||
.doc("Do not flip texcoord Y"));
|
||||
|
||||
if (!clipp::parse(argc, argv, cli)) {
|
||||
std::cout << clipp::make_man_page(cli, argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::string op = argv[1];
|
||||
|
||||
if (op == "gltf2obj") {
|
||||
|
||||
bool flip_texcoord_y = true;
|
||||
if (argc > 3) {
|
||||
flip_texcoord_y = (std::atoi(argv[3]) > 0) ? true : false;
|
||||
}
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
std::string input_filename(argv[2]);
|
||||
|
||||
std::string ext = GetFilePathExtension(input_filename);
|
||||
|
||||
{
|
||||
@ -657,8 +680,8 @@ int main(int argc, char **argv) {
|
||||
input_filename.c_str());
|
||||
} else {
|
||||
// assume ascii glTF.
|
||||
ret =
|
||||
loader.LoadASCIIFromFile(&model, &err, &warn, input_filename.c_str());
|
||||
ret = loader.LoadASCIIFromFile(&model, &err, &warn,
|
||||
input_filename.c_str());
|
||||
}
|
||||
|
||||
if (!warn.empty()) {
|
||||
@ -674,6 +697,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
json j;
|
||||
{
|
||||
std::ifstream i(input_filename);
|
||||
@ -703,42 +727,48 @@ int main(int argc, char **argv) {
|
||||
|
||||
json j_ret = j.patch(j_patch);
|
||||
std::cout << "patched = " << j_ret.dump(2) << "\n";
|
||||
#endif
|
||||
|
||||
std::string basedir = GetBaseDir(input_filename);
|
||||
std::vector<example::MeshPrim> meshes;
|
||||
bool ret = ExtractMesh(basedir, model, &meshes);
|
||||
bool ret = ExtractMesh(basedir, model, verbose, &meshes);
|
||||
|
||||
size_t n = 0;
|
||||
for (const auto &mesh : meshes) {
|
||||
// Assume no duplicated name in .glTF data
|
||||
std::string filename;
|
||||
std::string basename;
|
||||
if (mesh.name.empty()) {
|
||||
filename = "untitled-" + std::to_string(n) + ".obj";
|
||||
basename = "untitled-" + std::to_string(n);
|
||||
} else {
|
||||
filename = mesh.name + ".obj";
|
||||
basename = mesh.name;
|
||||
}
|
||||
|
||||
bool ok = example::SaveAsObjMesh(filename, mesh, flip_texcoord_y);
|
||||
|
||||
for (size_t primid = 0; primid < mesh.prims.size(); primid++) {
|
||||
example::ObjExportOption options;
|
||||
options.primid = int(primid);
|
||||
options.export_skinweights = export_skinweight;
|
||||
options.uvset = uvset;
|
||||
options.flip_texcoord_y = !no_flip_texcoord_y;
|
||||
|
||||
bool ok = example::SaveAsObjMesh(basename, mesh, options);
|
||||
if (!ok) {
|
||||
return EXIT_FAILURE;
|
||||
std::cout << "Failed to export mesh[" << mesh.name << "].primitives["
|
||||
<< primid << "]\n";
|
||||
// may ok;
|
||||
}
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
} else if (op == "obj2gltf") {
|
||||
std::string input_filename(argv[2]);
|
||||
|
||||
bool verbose = false;
|
||||
if (argc > 3) {
|
||||
verbose = (std::atoi(argv[3]) > 0) ? true : false;
|
||||
}
|
||||
|
||||
// Require facevarying layout?
|
||||
// facevarying representation is required if a vertex can have multiple normal/uv value.
|
||||
// drawback of facevarying is mesh data increases.
|
||||
// false = try to keep shared vertex representation as much as possible.
|
||||
// true = reorder vertex data and re-assign vertex indices for facevarying data layout.
|
||||
// facevarying representation is required if a vertex can have multiple
|
||||
// normal/uv value. drawback of facevarying is mesh data increases. false =
|
||||
// try to keep shared vertex representation as much as possible. true =
|
||||
// reorder vertex data and re-assign vertex indices for facevarying data
|
||||
// layout.
|
||||
bool facevarying = false;
|
||||
|
||||
example::MeshPrim mesh;
|
||||
@ -751,8 +781,6 @@ int main(int argc, char **argv) {
|
||||
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";
|
||||
@ -761,9 +789,7 @@ int main(int argc, char **argv) {
|
||||
std::cout << "Write glTF: " << output_filename << "\n";
|
||||
return EXIT_SUCCESS;
|
||||
} else {
|
||||
|
||||
std::cerr << "Unknown operation: " << op << "\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -371,15 +371,18 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
std::vector<tinygltf::Accessor> *accessors,
|
||||
std::vector<tinygltf::BufferView> *bufferViews,
|
||||
tinygltf::Buffer *buffer) {
|
||||
int prim_id = 0;
|
||||
std::vector<uint8_t> buf;
|
||||
|
||||
// single primitive per mesh
|
||||
tinygltf::Primitive primitive;
|
||||
|
||||
const PrimSet &prim = mesh.prims[prim_id];
|
||||
|
||||
// vertex index
|
||||
{
|
||||
size_t s, e;
|
||||
if (!SerializeVertexIndicesToBuffer(mesh.indices, mesh.indices_type, &buf,
|
||||
if (!SerializeVertexIndicesToBuffer(prim.indices, prim.indices_type, &buf,
|
||||
&s, &e)) {
|
||||
return false;
|
||||
}
|
||||
@ -393,13 +396,14 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
bufferViews->push_back(bufferView);
|
||||
|
||||
tinygltf::Accessor accessor;
|
||||
accessor.name = mesh.name + "#" + std::to_string(prim_id) + "/indices";
|
||||
accessor.bufferView = bufferViews->size() - 1;
|
||||
accessor.minValues.resize(1);
|
||||
accessor.minValues[0] = mesh.indices_min;
|
||||
accessor.minValues[0] = prim.indices_min;
|
||||
accessor.maxValues.resize(1);
|
||||
accessor.maxValues[0] = mesh.indices_max;
|
||||
accessor.count = mesh.indices.size();
|
||||
accessor.componentType = mesh.indices_type;
|
||||
accessor.maxValues[0] = prim.indices_max;
|
||||
accessor.count = prim.indices.size();
|
||||
accessor.componentType = prim.indices_type;
|
||||
accessor.type = TINYGLTF_TYPE_SCALAR;
|
||||
accessors->push_back(accessor);
|
||||
|
||||
@ -409,7 +413,7 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
// position
|
||||
{
|
||||
size_t s, e;
|
||||
if (!SerializeVertexAttribToBuffer(mesh.position, &buf, &s, &e)) {
|
||||
if (!SerializeVertexAttribToBuffer(prim.position, &buf, &s, &e)) {
|
||||
return false;
|
||||
}
|
||||
std::cout << "postion.byteRange: [" << s << ", " << e << "]\n";
|
||||
@ -422,15 +426,16 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
bufferViews->push_back(bufferView);
|
||||
|
||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
||||
mesh.position, bufferViews->size() - 1, /* offset */ 0);
|
||||
prim.position, bufferViews->size() - 1, /* offset */ 0);
|
||||
accessor.name = mesh.name + "#" + std::to_string(prim_id) + "/POSITION";
|
||||
accessors->push_back(accessor);
|
||||
|
||||
primitive.attributes["POSITION"] = accessors->size() - 1;
|
||||
}
|
||||
|
||||
if (mesh.normal.data.size() > 0) {
|
||||
if (prim.normal.data.size() > 0) {
|
||||
size_t s, e;
|
||||
if (!SerializeVertexAttribToBuffer(mesh.normal, &buf, &s, &e)) {
|
||||
if (!SerializeVertexAttribToBuffer(prim.normal, &buf, &s, &e)) {
|
||||
return false;
|
||||
}
|
||||
std::cout << "normal.byteRange: [" << s << ", " << e << "]\n";
|
||||
@ -443,15 +448,16 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
bufferViews->push_back(bufferView);
|
||||
|
||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
||||
mesh.normal, bufferViews->size() - 1, /* offset */ 0);
|
||||
prim.normal, bufferViews->size() - 1, /* offset */ 0);
|
||||
accessor.name = mesh.name + "#" + std::to_string(prim_id) + "/NORMAL";
|
||||
accessors->push_back(accessor);
|
||||
|
||||
primitive.attributes["NORMAL"] = accessors->size() - 1;
|
||||
}
|
||||
|
||||
if (mesh.tangent.data.size() > 0) {
|
||||
if (prim.tangent.data.size() > 0) {
|
||||
size_t s, e;
|
||||
if (!SerializeVertexAttribToBuffer(mesh.tangent, &buf, &s, &e)) {
|
||||
if (!SerializeVertexAttribToBuffer(prim.tangent, &buf, &s, &e)) {
|
||||
return false;
|
||||
}
|
||||
std::cout << "tangent.byteRange: [" << s << ", " << e << "]\n";
|
||||
@ -464,14 +470,15 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
bufferViews->push_back(bufferView);
|
||||
|
||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
||||
mesh.tangent, bufferViews->size() - 1, /* offset */ 0);
|
||||
prim.tangent, bufferViews->size() - 1, /* offset */ 0);
|
||||
accessor.name = mesh.name + "#" + std::to_string(prim_id) + "/TANGENT";
|
||||
accessors->push_back(accessor);
|
||||
|
||||
primitive.attributes["TANGENT"] = accessors->size() - 1;
|
||||
}
|
||||
|
||||
if (mesh.texcoords.size() > 0) {
|
||||
for (const auto &item : mesh.texcoords) {
|
||||
if (prim.texcoords.size() > 0) {
|
||||
for (const auto &item : prim.texcoords) {
|
||||
size_t s, e;
|
||||
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
||||
return false;
|
||||
@ -487,6 +494,7 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
|
||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
||||
item.second, bufferViews->size() - 1, /* offset */ 0);
|
||||
accessor.name = mesh.name + "#" + std::to_string(prim_id) + "/TEXCOORD_" + std::to_string(item.first);
|
||||
accessors->push_back(accessor);
|
||||
|
||||
std::string target = "TEXCOORD_" + std::to_string(item.first);
|
||||
@ -494,8 +502,8 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh.joints.size() > 0) {
|
||||
for (const auto &item : mesh.joints) {
|
||||
if (prim.joints.size() > 0) {
|
||||
for (const auto &item : prim.joints) {
|
||||
size_t s, e;
|
||||
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
||||
return false;
|
||||
@ -511,7 +519,8 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
bufferViews->push_back(bufferView);
|
||||
|
||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
||||
mesh.tangent, bufferViews->size() - 1, /* offset */ 0);
|
||||
item.second, bufferViews->size() - 1, /* offset */ 0);
|
||||
accessor.name = mesh.name + "#" + std::to_string(prim_id) + "/JOINTS_" + std::to_string(item.first);
|
||||
accessors->push_back(accessor);
|
||||
|
||||
std::string target = "JOINTS_" + std::to_string(item.first);
|
||||
@ -519,8 +528,8 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh.weights.size() > 0) {
|
||||
for (const auto &item : mesh.weights) {
|
||||
if (prim.weights.size() > 0) {
|
||||
for (const auto &item : prim.weights) {
|
||||
size_t s, e;
|
||||
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
||||
return false;
|
||||
@ -536,7 +545,8 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
bufferViews->push_back(bufferView);
|
||||
|
||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
||||
mesh.tangent, bufferViews->size() - 1, /* offset */ 0);
|
||||
item.second, bufferViews->size() - 1, /* offset */ 0);
|
||||
accessor.name = mesh.name + "#" + std::to_string(prim_id) + "/WEIGHTS_" + std::to_string(item.first);
|
||||
accessors->push_back(accessor);
|
||||
|
||||
std::string target = "WEIGHTS_" + std::to_string(item.first);
|
||||
@ -544,7 +554,7 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
}
|
||||
}
|
||||
|
||||
primitive.mode = mesh.mode;
|
||||
primitive.mode = prim.mode;
|
||||
|
||||
gltfmesh->primitives.push_back(primitive);
|
||||
|
||||
@ -554,62 +564,134 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
bool HasValidSkinWeights(const PrimSet &prim)
|
||||
{
|
||||
if ((prim.weights.size() > 0) && (prim.weights.size() == prim.joints.size())) {
|
||||
|
||||
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";
|
||||
if (prim.weights.size() != prim.joints.size()) {
|
||||
std::cerr << "# of JOINTS(" << prim.joints.size() << ") and WEIGHTS(" << prim.weights.size() << ") differs\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_slots = prim.weights.size();
|
||||
|
||||
// Assume weight slots are tightly packed.
|
||||
for (size_t slot = 0; slot < num_slots; slot++) {
|
||||
if (!prim.weights.count(slot)) {
|
||||
std::cerr << "WEIGHTS_" << slot << " not found.\n";
|
||||
return false;;
|
||||
}
|
||||
|
||||
if (!prim.joints.count(slot)) {
|
||||
std::cerr << "JOINTS_" << slot << " not found.\n";
|
||||
return false;;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool SaveAsObjMesh(const std::string &basename, const MeshPrim &mesh, const ObjExportOption &options) {
|
||||
if (options.primid >= mesh.prims.size()) {
|
||||
std::cerr << "mesh( " << mesh.name << ") does not contain " << options.primid << "th primitive. mesh.primitives.length = " << mesh.prims.size() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const PrimSet &prim = mesh.prims[options.primid];
|
||||
|
||||
if (prim.texcoords.count(options.uvset)) {
|
||||
std::cerr << "Exporting uvset " << options.uvset << " requested, but mesh( " << mesh.name << ") does not contain TEXCOORD_" << options.uvset << "\n";
|
||||
std::cerr << "UV coord will not be exported to .obj\n";
|
||||
}
|
||||
|
||||
std::string obj_filename = basename + "_" + std::to_string(options.primid) + ".obj";
|
||||
std::ofstream ofs(obj_filename);
|
||||
if (!ofs) {
|
||||
std::cerr << "Failed to open .obj to write: " << obj_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
|
||||
has_vn = prim.normal.data.size() == prim.position.data.size();
|
||||
has_vt = prim.texcoords.count(options.uvset) &&
|
||||
(prim.texcoords.at(options.uvset).data.size() > 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]
|
||||
for (size_t i = 0; i < prim.position.data.size() / 3; i++) {
|
||||
ofs << "v " << prim.position.data[3 * i + 0] << " "
|
||||
<< prim.position.data[3 * i + 1] << " " << prim.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]
|
||||
for (size_t i = 0; i < prim.normal.data.size() / 3; i++) {
|
||||
ofs << "vn " << prim.normal.data[3 * i + 0] << " "
|
||||
<< prim.normal.data[3 * i + 1] << " " << prim.normal.data[3 * i + 2]
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
assert((mesh.texcoords.at(0).data.size() / 2) ==
|
||||
(mesh.position.data.size() / 3));
|
||||
|
||||
if (has_vt) {
|
||||
assert((prim.texcoords.at(options.uvset).data.size() / 2) == (prim.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) {
|
||||
for (size_t i = 0; i < prim.texcoords.at(options.uvset).data.size() / 2; i++) {
|
||||
float y = prim.texcoords.at(options.uvset).data[2 * i + 1];
|
||||
if (options.flip_texcoord_y) {
|
||||
y = 1.0f - y;
|
||||
}
|
||||
ofs << "vt " << mesh.texcoords.at(0).data[2 * i + 0] << " " << y << "\n";
|
||||
ofs << "vt " << prim.texcoords.at(options.uvset).data[2 * i + 0] << " " << y << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (options.export_skinweights && HasValidSkinWeights(prim)) {
|
||||
|
||||
// WEIGHTS_ and JOINTS_ slots are tightly packed.
|
||||
|
||||
size_t num_slots = prim.weights.size();
|
||||
|
||||
for (size_t v = 0; v < prim.position.data.size() / 3; v++) { // vec3
|
||||
|
||||
std::vector<float> weights(num_slots * 4, 0.0f);
|
||||
std::vector<float> joints(num_slots * 4, 0.0f);
|
||||
|
||||
for (size_t slot = 0; slot < num_slots; slot++) {
|
||||
for (size_t k = 0; k < 4; k++) {
|
||||
weights[slot * 4 + k] = prim.weights.at(slot).data[4 * v + k];
|
||||
joints[slot * 4 + k] = prim.joints.at(slot).data[4 * v + k];
|
||||
}
|
||||
}
|
||||
|
||||
// vertex index start with 0.
|
||||
ofs << "vw " << v << " " ;
|
||||
for (size_t i = 0; i < weights.size(); i++) {
|
||||
ofs << int(joints[i]) << " " << weights[i] << " ";
|
||||
}
|
||||
ofs << "\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// v, vn, vt has same index
|
||||
for (size_t i = 0; i < mesh.indices.size() / 3; i++) {
|
||||
for (size_t i = 0; i < prim.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;
|
||||
int f0 = int(prim.indices[3 * i + 0]) + 1;
|
||||
int f1 = int(prim.indices[3 * i + 1]) + 1;
|
||||
int f2 = int(prim.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;
|
||||
}
|
||||
@ -769,12 +851,14 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
||||
|
||||
PrimSet prim;
|
||||
|
||||
// reorder texcoords and normals so that it has same indexing to vertices.
|
||||
if (facevarying) {
|
||||
mesh->position.data.clear();
|
||||
mesh->normal.data.clear();
|
||||
mesh->tangent.data.clear();
|
||||
mesh->texcoords[0] = VertexAttrib();
|
||||
prim.position.data.clear();
|
||||
prim.normal.data.clear();
|
||||
prim.tangent.data.clear();
|
||||
prim.texcoords[0] = VertexAttrib();
|
||||
|
||||
// Concat shapes
|
||||
for (size_t s = 0; s < shapes.size(); s++) {
|
||||
@ -882,43 +966,43 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
n[2][2] = n[0][2];
|
||||
}
|
||||
|
||||
mesh->position.data.push_back(v[0][0]);
|
||||
mesh->position.data.push_back(v[0][1]);
|
||||
mesh->position.data.push_back(v[0][2]);
|
||||
prim.position.data.push_back(v[0][0]);
|
||||
prim.position.data.push_back(v[0][1]);
|
||||
prim.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]);
|
||||
prim.position.data.push_back(v[1][0]);
|
||||
prim.position.data.push_back(v[1][1]);
|
||||
prim.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]);
|
||||
prim.position.data.push_back(v[2][0]);
|
||||
prim.position.data.push_back(v[2][1]);
|
||||
prim.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]);
|
||||
prim.normal.data.push_back(n[0][0]);
|
||||
prim.normal.data.push_back(n[0][1]);
|
||||
prim.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]);
|
||||
prim.normal.data.push_back(n[1][0]);
|
||||
prim.normal.data.push_back(n[1][1]);
|
||||
prim.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]);
|
||||
prim.normal.data.push_back(n[2][0]);
|
||||
prim.normal.data.push_back(n[2][1]);
|
||||
prim.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]);
|
||||
prim.texcoords[0].data.push_back(tc[0][0]);
|
||||
prim.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]);
|
||||
prim.texcoords[0].data.push_back(tc[1][0]);
|
||||
prim.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]);
|
||||
prim.texcoords[0].data.push_back(tc[2][0]);
|
||||
prim.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);
|
||||
size_t idx = prim.indices.size();
|
||||
prim.indices.push_back(int(idx) + 0);
|
||||
prim.indices.push_back(int(idx) + 1);
|
||||
prim.indices.push_back(int(idx) + 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -935,16 +1019,17 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
maxn = std::max(vertex_skin_weights[i].weightValues.size(), maxn);
|
||||
}
|
||||
|
||||
std::cout << "Max # of weights = " << maxn << "\n";
|
||||
int num_slots = 0;
|
||||
if (maxn > 0) {
|
||||
num_slots = (((maxn - 1) / 4) + 1) * 4;
|
||||
num_slots = maxn / 4;
|
||||
}
|
||||
std::cout << "# of slots = " << num_slots << "\n";
|
||||
std::cout << "Max # of slots = " << num_slots << "\n";
|
||||
|
||||
for (size_t t = 0; t < size_t(num_slots); t++) {
|
||||
VertexAttrib weights, joints;
|
||||
|
||||
size_t num_faceverts = mesh->indices.size();
|
||||
size_t num_faceverts = prim.indices.size();
|
||||
|
||||
// facevarying weights/joints Fill with zeros
|
||||
weights.data.resize(4 * num_faceverts, 0.0f);
|
||||
@ -971,42 +1056,42 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
}
|
||||
}
|
||||
|
||||
mesh->weights[t] = weights;
|
||||
mesh->joints[t] = joints;
|
||||
prim.weights[t] = weights;
|
||||
prim.joints[t] = joints;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// position/texcoord/normal can be represented in shared vertex manner
|
||||
|
||||
mesh->position.data.clear();
|
||||
prim.position.data.clear();
|
||||
for (size_t v = 0; v < attrib.vertices.size(); v++) {
|
||||
mesh->position.data.push_back(attrib.vertices[v]);
|
||||
prim.position.data.push_back(attrib.vertices[v]);
|
||||
}
|
||||
|
||||
mesh->normal.data.clear();
|
||||
prim.normal.data.clear();
|
||||
for (size_t v = 0; v < attrib.normals.size(); v++) {
|
||||
mesh->normal.data.push_back(attrib.normals[v]);
|
||||
prim.normal.data.push_back(attrib.normals[v]);
|
||||
}
|
||||
|
||||
mesh->texcoords[0] = VertexAttrib();
|
||||
prim.texcoords[0] = VertexAttrib();
|
||||
for (size_t v = 0; v < attrib.texcoords.size(); v++) {
|
||||
mesh->texcoords[0].data.push_back(attrib.texcoords[v]);
|
||||
prim.texcoords[0].data.push_back(attrib.texcoords[v]);
|
||||
}
|
||||
|
||||
mesh->indices_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
|
||||
mesh->indices.clear();
|
||||
prim.indices_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
|
||||
prim.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) +
|
||||
prim.indices.push_back(uint32_t(face_index_offset) +
|
||||
uint32_t(shape.mesh.indices[f].vertex_index));
|
||||
}
|
||||
|
||||
face_index_offset = mesh->indices.size();
|
||||
face_index_offset = prim.indices.size();
|
||||
}
|
||||
|
||||
// weights/joints
|
||||
@ -1017,9 +1102,10 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
maxn = std::max(attrib.skin_weights[i].weightValues.size(), maxn);
|
||||
}
|
||||
|
||||
std::cout << "Max # of weights = " << maxn << "\n";
|
||||
int num_slots = 0;
|
||||
if (maxn > 0) {
|
||||
num_slots = (((maxn - 1) / 4) + 1) * 4;
|
||||
num_slots = maxn / 4;
|
||||
}
|
||||
std::cout << "# of slots = " << num_slots << "\n";
|
||||
|
||||
@ -1027,13 +1113,13 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
VertexAttrib weights, joints;
|
||||
|
||||
// Fill with zeros
|
||||
weights.data.resize(4 * (mesh->position.data.size() / 3), 0.0f);
|
||||
joints.data.resize(4 * (mesh->position.data.size() / 3), 0.0f);
|
||||
weights.data.resize(4 * (prim.position.data.size() / 3), 0.0f);
|
||||
joints.data.resize(4 * (prim.position.data.size() / 3), 0.0f);
|
||||
|
||||
for (size_t v = 0; v < attrib.skin_weights.size(); v++) {
|
||||
const tinyobj::skin_weight_t &sw = attrib.skin_weights[v];
|
||||
|
||||
assert(sw.vertex_id < (mesh->position.data.size() / 3));
|
||||
assert(sw.vertex_id < (prim.position.data.size() / 3));
|
||||
|
||||
size_t dst_vid = sw.vertex_id;
|
||||
|
||||
@ -1050,13 +1136,13 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
weights.data_type = TINYGLTF_TYPE_VEC4;
|
||||
weights.component_type =
|
||||
TINYGLTF_COMPONENT_TYPE_FLOAT; // storage format
|
||||
mesh->weights[s] = weights;
|
||||
prim.weights[s] = weights;
|
||||
|
||||
joints.data_type = TINYGLTF_TYPE_VEC4;
|
||||
joints.component_type =
|
||||
TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; // storage format
|
||||
|
||||
mesh->joints[s] = joints;
|
||||
prim.joints[s] = joints;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1066,15 +1152,15 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
{
|
||||
uint32_t minv = 0.0;
|
||||
uint32_t maxv = 0.0;
|
||||
for (size_t i = 0; i < mesh->indices.size(); i++) {
|
||||
minv = std::min(minv, uint32_t(mesh->indices[i]));
|
||||
maxv = std::max(maxv, uint32_t(mesh->indices[i]));
|
||||
for (size_t i = 0; i < prim.indices.size(); i++) {
|
||||
minv = std::min(minv, uint32_t(prim.indices[i]));
|
||||
maxv = std::max(maxv, uint32_t(prim.indices[i]));
|
||||
}
|
||||
|
||||
mesh->indices_min = int(minv);
|
||||
mesh->indices_max = int(maxv);
|
||||
prim.indices_min = int(minv);
|
||||
prim.indices_max = int(maxv);
|
||||
|
||||
mesh->indices_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
|
||||
prim.indices_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
|
||||
}
|
||||
|
||||
{
|
||||
@ -1083,25 +1169,25 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
||||
|
||||
for (size_t i = 0; i < mesh->position.data.size() / 3; i++) {
|
||||
for (size_t i = 0; i < prim.position.data.size() / 3; i++) {
|
||||
for (size_t k = 0; k < 3; k++) {
|
||||
bmin[k] = std::min(bmin[k], mesh->position.data[3 * i + k]);
|
||||
bmax[k] = std::max(bmax[k], mesh->position.data[3 * i + k]);
|
||||
bmin[k] = std::min(bmin[k], prim.position.data[3 * i + k]);
|
||||
bmax[k] = std::max(bmax[k], prim.position.data[3 * i + k]);
|
||||
}
|
||||
}
|
||||
|
||||
mesh->position.minValues.resize(3);
|
||||
mesh->position.minValues[0] = bmin[0];
|
||||
mesh->position.minValues[1] = bmin[1];
|
||||
mesh->position.minValues[2] = bmin[2];
|
||||
prim.position.minValues.resize(3);
|
||||
prim.position.minValues[0] = bmin[0];
|
||||
prim.position.minValues[1] = bmin[1];
|
||||
prim.position.minValues[2] = bmin[2];
|
||||
|
||||
mesh->position.maxValues.resize(3);
|
||||
mesh->position.maxValues[0] = bmax[0];
|
||||
mesh->position.maxValues[1] = bmax[1];
|
||||
mesh->position.maxValues[2] = bmax[2];
|
||||
prim.position.maxValues.resize(3);
|
||||
prim.position.maxValues[0] = bmax[0];
|
||||
prim.position.maxValues[1] = bmax[1];
|
||||
prim.position.maxValues[2] = bmax[2];
|
||||
|
||||
mesh->position.data_type = TINYGLTF_TYPE_VEC3;
|
||||
mesh->position.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||
prim.position.data_type = TINYGLTF_TYPE_VEC3;
|
||||
prim.position.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
{
|
||||
@ -1110,57 +1196,57 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
||||
|
||||
for (size_t i = 0; i < mesh->normal.data.size() / 3; i++) {
|
||||
for (size_t i = 0; i < prim.normal.data.size() / 3; i++) {
|
||||
for (size_t k = 0; k < 3; k++) {
|
||||
bmin[k] = std::min(bmin[k], mesh->normal.data[3 * i + k]);
|
||||
bmax[k] = std::max(bmax[k], mesh->normal.data[3 * i + k]);
|
||||
bmin[k] = std::min(bmin[k], prim.normal.data[3 * i + k]);
|
||||
bmax[k] = std::max(bmax[k], prim.normal.data[3 * i + k]);
|
||||
}
|
||||
}
|
||||
|
||||
mesh->normal.minValues.resize(3);
|
||||
mesh->normal.minValues[0] = bmin[0];
|
||||
mesh->normal.minValues[1] = bmin[1];
|
||||
mesh->normal.minValues[2] = bmin[2];
|
||||
prim.normal.minValues.resize(3);
|
||||
prim.normal.minValues[0] = bmin[0];
|
||||
prim.normal.minValues[1] = bmin[1];
|
||||
prim.normal.minValues[2] = bmin[2];
|
||||
|
||||
mesh->normal.maxValues.resize(3);
|
||||
mesh->normal.maxValues[0] = bmax[0];
|
||||
mesh->normal.maxValues[1] = bmax[1];
|
||||
mesh->normal.maxValues[2] = bmax[2];
|
||||
prim.normal.maxValues.resize(3);
|
||||
prim.normal.maxValues[0] = bmax[0];
|
||||
prim.normal.maxValues[1] = bmax[1];
|
||||
prim.normal.maxValues[2] = bmax[2];
|
||||
|
||||
mesh->normal.data_type = TINYGLTF_TYPE_VEC3;
|
||||
mesh->normal.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||
prim.normal.data_type = TINYGLTF_TYPE_VEC3;
|
||||
prim.normal.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
{
|
||||
float bmin[4];
|
||||
float bmax[4];
|
||||
bmin[0] = bmin[1] = bmin[2] = bmin[3] = std::numeric_limits<float>::max();
|
||||
bmax[0] = bmax[1] = bmax[2] = bmin[3] =
|
||||
bmax[0] = bmax[1] = bmax[2] = bmax[3] =
|
||||
-std::numeric_limits<float>::max();
|
||||
|
||||
size_t n = 3;
|
||||
|
||||
for (size_t i = 0; i < mesh->tangent.data.size() / n; i++) {
|
||||
for (size_t i = 0; i < prim.tangent.data.size() / n; i++) {
|
||||
for (size_t k = 0; k < n; k++) {
|
||||
bmin[k] = std::min(bmin[k], mesh->tangent.data[n * i + k]);
|
||||
bmax[k] = std::max(bmax[k], mesh->tangent.data[n * i + k]);
|
||||
bmin[k] = std::min(bmin[k], prim.tangent.data[n * i + k]);
|
||||
bmax[k] = std::max(bmax[k], prim.tangent.data[n * i + k]);
|
||||
}
|
||||
}
|
||||
|
||||
mesh->tangent.minValues.resize(n);
|
||||
mesh->tangent.maxValues.resize(n);
|
||||
prim.tangent.minValues.resize(n);
|
||||
prim.tangent.maxValues.resize(n);
|
||||
for (size_t k = 0; k < n; k++) {
|
||||
mesh->tangent.minValues[k] = bmin[k];
|
||||
mesh->tangent.maxValues[k] = bmax[k];
|
||||
prim.tangent.minValues[k] = bmin[k];
|
||||
prim.tangent.maxValues[k] = bmax[k];
|
||||
}
|
||||
|
||||
mesh->tangent.data_type =
|
||||
prim.tangent.data_type =
|
||||
(n == 3) ? TINYGLTF_TYPE_VEC3 : TINYGLTF_TYPE_VEC4;
|
||||
mesh->tangent.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||
prim.tangent.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
// texcoord
|
||||
for (auto &item : mesh->texcoords) {
|
||||
for (auto &item : prim.texcoords) {
|
||||
float bmin[2];
|
||||
float bmax[2];
|
||||
bmin[0] = bmin[1] = std::numeric_limits<float>::max();
|
||||
@ -1185,22 +1271,32 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
}
|
||||
|
||||
// joints
|
||||
for (auto &item : mesh->joints) {
|
||||
float bmin;
|
||||
float bmax;
|
||||
bmin = std::numeric_limits<float>::max();
|
||||
bmax = -std::numeric_limits<float>::max();
|
||||
for (auto &item : prim.joints) {
|
||||
|
||||
for (size_t i = 0; i < item.second.data.size(); i++) {
|
||||
bmin = std::min(bmin, item.second.data[i]);
|
||||
bmax = std::max(bmax, item.second.data[i]);
|
||||
std::cout << "joint -- " << item.first << "\n";
|
||||
|
||||
float bmin[4];
|
||||
float bmax[4];
|
||||
bmin[0] = bmin[1] = bmin[2] = bmin[3] = float(std::numeric_limits<uint16_t>::max());
|
||||
bmax[0] = bmax[1] = bmax[2] = bmax[3] =
|
||||
float(-std::numeric_limits<uint16_t>::max());
|
||||
|
||||
size_t n = 4;
|
||||
|
||||
for (size_t i = 0; i < item.second.data.size() / n; i++) {
|
||||
for (size_t k = 0; k < n; k++) {
|
||||
bmin[k] = std::min(bmin[k], item.second.data[n * i + k]);
|
||||
bmax[k] = std::max(bmax[k], item.second.data[n * i + k]);
|
||||
}
|
||||
}
|
||||
|
||||
item.second.minValues.resize(1);
|
||||
item.second.maxValues.resize(1);
|
||||
|
||||
item.second.minValues[0] = bmin;
|
||||
item.second.maxValues[0] = bmax;
|
||||
// TODO(syoyo): check if the value is within ushort max
|
||||
item.second.minValues.resize(n);
|
||||
item.second.maxValues.resize(n);
|
||||
for (size_t k = 0; k < n; k++) {
|
||||
item.second.minValues[k] = bmin[k];
|
||||
item.second.maxValues[k] = bmax[k];
|
||||
}
|
||||
|
||||
item.second.data_type = TINYGLTF_TYPE_VEC4;
|
||||
item.second.component_type =
|
||||
@ -1208,22 +1304,29 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
}
|
||||
|
||||
// weights
|
||||
for (auto &item : mesh->weights) {
|
||||
float bmin;
|
||||
float bmax;
|
||||
bmin = std::numeric_limits<float>::max();
|
||||
bmax = -std::numeric_limits<float>::max();
|
||||
for (auto &item : prim.weights) {
|
||||
|
||||
for (size_t i = 0; i < item.second.data.size(); i++) {
|
||||
bmin = std::min(bmin, item.second.data[i]);
|
||||
bmax = std::max(bmax, item.second.data[i]);
|
||||
float bmin[4];
|
||||
float bmax[4];
|
||||
bmin[0] = bmin[1] = bmin[2] = bmin[3] = std::numeric_limits<float>::max();
|
||||
// do not allow negative weight
|
||||
bmax[0] = bmax[1] = bmax[2] = bmax[3] = 0.0f;
|
||||
|
||||
size_t n = 4;
|
||||
|
||||
for (size_t i = 0; i < item.second.data.size() / n; i++) {
|
||||
for (size_t k = 0; k < 4; k++) {
|
||||
bmin[k] = std::min(bmin[k], item.second.data[n * i + k]);
|
||||
bmax[k] = std::max(bmax[k], item.second.data[n * i + k]);
|
||||
}
|
||||
}
|
||||
|
||||
item.second.minValues.resize(1);
|
||||
item.second.maxValues.resize(1);
|
||||
|
||||
item.second.minValues[0] = bmin;
|
||||
item.second.maxValues[0] = bmax;
|
||||
item.second.minValues.resize(n);
|
||||
item.second.maxValues.resize(n);
|
||||
for (size_t k = 0; k < n; k++) {
|
||||
item.second.minValues[k] = bmin[k];
|
||||
item.second.maxValues[k] = bmax[k];
|
||||
}
|
||||
|
||||
item.second.data_type = TINYGLTF_TYPE_VEC4;
|
||||
item.second.component_type =
|
||||
@ -1231,83 +1334,91 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
||||
}
|
||||
}
|
||||
|
||||
prim.mode = TINYGLTF_MODE_TRIANGLES;
|
||||
|
||||
mesh->prims.clear();
|
||||
mesh->prims.push_back(prim);
|
||||
|
||||
// Use filename as mesh's name
|
||||
mesh->name = GetBaseFilename(filename);
|
||||
|
||||
mesh->mode = TINYGLTF_MODE_TRIANGLES;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrintMeshPrim(const MeshPrim &mesh) {
|
||||
for (size_t p = 0; p < mesh.prims.size(); p++) {
|
||||
const PrimSet &prim = mesh.prims[p];
|
||||
std::cout << "--- primitive[" << p << "] ---\n";
|
||||
|
||||
std::cout << "indices.component_type : "
|
||||
<< PrintComponentType(mesh.indices_type) << "\n";
|
||||
std::cout << "# of indices : " << mesh.indices.size() << "\n";
|
||||
std::cout << " indices.min = " << mesh.indices_min
|
||||
<< ", max = " << mesh.indices_max << "\n";
|
||||
for (size_t i = 0; i < mesh.indices.size(); i++) {
|
||||
std::cout << " index[" << i << "] = " << mesh.indices[i] << "\n";
|
||||
<< PrintComponentType(prim.indices_type) << "\n";
|
||||
std::cout << "# of indices : " << prim.indices.size() << "\n";
|
||||
std::cout << " indices.min = " << prim.indices_min
|
||||
<< ", max = " << prim.indices_max << "\n";
|
||||
for (size_t i = 0; i < prim.indices.size(); i++) {
|
||||
std::cout << " index[" << i << "] = " << prim.indices[i] << "\n";
|
||||
}
|
||||
|
||||
std::cout << "position.type : " << PrintType(mesh.position.data_type) << "\n";
|
||||
std::cout << "position.type : " << PrintType(prim.position.data_type) << "\n";
|
||||
std::cout << "position.component_type : "
|
||||
<< PrintComponentType(mesh.position.component_type) << "\n";
|
||||
std::cout << "# of positions : " << mesh.position.data.size() / 3 << "\n";
|
||||
if ((mesh.position.minValues.size() == 3) &&
|
||||
(mesh.position.maxValues.size() == 3)) {
|
||||
std::cout << " position.min = " << mesh.position.minValues
|
||||
<< ", max = " << mesh.position.maxValues << "\n";
|
||||
<< PrintComponentType(prim.position.component_type) << "\n";
|
||||
std::cout << "# of positions : " << prim.position.data.size() / 3 << "\n";
|
||||
if ((prim.position.minValues.size() == 3) &&
|
||||
(prim.position.maxValues.size() == 3)) {
|
||||
std::cout << " position.min = " << prim.position.minValues
|
||||
<< ", max = " << prim.position.maxValues << "\n";
|
||||
}
|
||||
for (size_t i = 0; i < mesh.position.data.size() / 3; i++) {
|
||||
std::cout << " position[" << i << "] = " << mesh.position.data[3 * i + 0]
|
||||
<< ", " << mesh.position.data[3 * i + 1] << ", "
|
||||
<< mesh.position.data[3 * i + 2] << std::endl;
|
||||
for (size_t i = 0; i < prim.position.data.size() / 3; i++) {
|
||||
std::cout << " position[" << i << "] = " << prim.position.data[3 * i + 0]
|
||||
<< ", " << prim.position.data[3 * i + 1] << ", "
|
||||
<< prim.position.data[3 * i + 2] << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "normal.type : " << PrintType(mesh.normal.data_type) << "\n";
|
||||
std::cout << "normal.type : " << PrintType(prim.normal.data_type) << "\n";
|
||||
std::cout << "normal.component_type : "
|
||||
<< PrintComponentType(mesh.normal.component_type) << "\n";
|
||||
std::cout << "# of normals : " << mesh.normal.data.size() / 3 << "\n";
|
||||
if ((mesh.normal.minValues.size() == 3) &&
|
||||
(mesh.normal.maxValues.size() == 3)) {
|
||||
std::cout << " normal.min = " << mesh.normal.minValues
|
||||
<< ", max = " << mesh.normal.maxValues << "\n";
|
||||
<< PrintComponentType(prim.normal.component_type) << "\n";
|
||||
std::cout << "# of normals : " << prim.normal.data.size() / 3 << "\n";
|
||||
if ((prim.normal.minValues.size() == 3) &&
|
||||
(prim.normal.maxValues.size() == 3)) {
|
||||
std::cout << " normal.min = " << prim.normal.minValues
|
||||
<< ", max = " << prim.normal.maxValues << "\n";
|
||||
}
|
||||
for (size_t i = 0; i < mesh.normal.data.size() / 3; i++) {
|
||||
std::cout << " normal[" << i << "] = " << mesh.normal.data[3 * i + 0]
|
||||
<< ", " << mesh.normal.data[3 * i + 1] << ", "
|
||||
<< mesh.normal.data[3 * i + 2] << std::endl;
|
||||
for (size_t i = 0; i < prim.normal.data.size() / 3; i++) {
|
||||
std::cout << " normal[" << i << "] = " << prim.normal.data[3 * i + 0]
|
||||
<< ", " << prim.normal.data[3 * i + 1] << ", "
|
||||
<< prim.normal.data[3 * i + 2] << std::endl;
|
||||
}
|
||||
|
||||
if (mesh.tangent.data.size() > 0) {
|
||||
assert((mesh.tangent.data_type == TINYGLTF_TYPE_VEC3) ||
|
||||
(mesh.tangent.data_type == TINYGLTF_TYPE_VEC4));
|
||||
if (prim.tangent.data.size() > 0) {
|
||||
assert((prim.tangent.data_type == TINYGLTF_TYPE_VEC3) ||
|
||||
(prim.tangent.data_type == TINYGLTF_TYPE_VEC4));
|
||||
|
||||
size_t n = mesh.tangent.data_type == TINYGLTF_TYPE_VEC3 ? 3 : 4;
|
||||
size_t n = prim.tangent.data_type == TINYGLTF_TYPE_VEC3 ? 3 : 4;
|
||||
|
||||
std::cout << "tangent.type : " << PrintType(mesh.tangent.data_type) << "\n";
|
||||
std::cout << "tangent.type : " << PrintType(prim.tangent.data_type) << "\n";
|
||||
std::cout << "tangent.component_type : "
|
||||
<< PrintComponentType(mesh.tangent.component_type) << "\n";
|
||||
std::cout << "# of tangents : " << mesh.tangent.data.size() / n << "\n";
|
||||
if ((mesh.tangent.minValues.size() == 3) &&
|
||||
(mesh.tangent.maxValues.size() == 3)) {
|
||||
std::cout << " tangent.min = " << mesh.tangent.minValues
|
||||
<< ", max = " << mesh.tangent.maxValues << "\n";
|
||||
<< PrintComponentType(prim.tangent.component_type) << "\n";
|
||||
std::cout << "# of tangents : " << prim.tangent.data.size() / n << "\n";
|
||||
if ((prim.tangent.minValues.size() == 3) &&
|
||||
(prim.tangent.maxValues.size() == 3)) {
|
||||
std::cout << " tangent.min = " << prim.tangent.minValues
|
||||
<< ", max = " << prim.tangent.maxValues << "\n";
|
||||
}
|
||||
for (size_t i = 0; i < mesh.tangent.data.size() / n; i++) {
|
||||
std::cout << " tangent[" << i << "] = " << mesh.tangent.data[n * i + 0]
|
||||
<< ", " << mesh.tangent.data[n * i + 1] << ", "
|
||||
<< mesh.tangent.data[n * i + 2];
|
||||
for (size_t i = 0; i < prim.tangent.data.size() / n; i++) {
|
||||
std::cout << " tangent[" << i << "] = " << prim.tangent.data[n * i + 0]
|
||||
<< ", " << prim.tangent.data[n * i + 1] << ", "
|
||||
<< prim.tangent.data[n * i + 2];
|
||||
|
||||
if (n == 4) {
|
||||
std::cout << ", " << mesh.tangent.data[n * i + 3];
|
||||
std::cout << ", " << prim.tangent.data[n * i + 3];
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "# of texcoord slots : " << mesh.texcoords.size() << "\n";
|
||||
for (const auto &item : mesh.texcoords) {
|
||||
std::cout << "# of texcoord slots : " << prim.texcoords.size() << "\n";
|
||||
for (const auto &item : prim.texcoords) {
|
||||
std::cout << "TEXCOORD_" << item.first << "\n";
|
||||
|
||||
assert(item.second.data_type == TINYGLTF_TYPE_VEC2);
|
||||
@ -1327,10 +1438,10 @@ void PrintMeshPrim(const MeshPrim &mesh) {
|
||||
}
|
||||
}
|
||||
|
||||
assert(mesh.joints.size() == mesh.weights.size());
|
||||
std::cout << "# of joints/weights slots : " << mesh.joints.size() << "\n";
|
||||
for (const auto &item : mesh.joints) {
|
||||
assert(mesh.weights.count(item.first));
|
||||
assert(prim.joints.size() == prim.weights.size());
|
||||
std::cout << "# of joints/weights slots : " << prim.joints.size() << "\n";
|
||||
for (const auto &item : prim.joints) {
|
||||
assert(prim.weights.count(item.first));
|
||||
|
||||
assert(item.second.data_type == TINYGLTF_TYPE_VEC4);
|
||||
|
||||
@ -1349,7 +1460,7 @@ void PrintMeshPrim(const MeshPrim &mesh) {
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
const VertexAttrib &attrib = mesh.weights.at(item.first);
|
||||
const VertexAttrib &attrib = prim.weights.at(item.first);
|
||||
|
||||
// weight must be uint8 or uint16(normalized), or float
|
||||
assert((attrib.component_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) ||
|
||||
@ -1364,6 +1475,7 @@ void PrintMeshPrim(const MeshPrim &mesh) {
|
||||
std::cout << " weights[" << i << "] = " << attrib.data[i] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace example
|
||||
|
@ -28,9 +28,7 @@ struct VertexAttrib {
|
||||
|
||||
};
|
||||
|
||||
struct MeshPrim {
|
||||
std::string name;
|
||||
int32_t id{-1};
|
||||
struct PrimSet {
|
||||
|
||||
int mode; // e.g. TRIANGLES
|
||||
|
||||
@ -49,14 +47,35 @@ struct MeshPrim {
|
||||
int indices_max{-1};
|
||||
int indices_type{-1}; // storage type(componentType) of `indices`.
|
||||
std::vector<uint32_t> indices; // vertex indices
|
||||
|
||||
};
|
||||
|
||||
struct MeshPrim {
|
||||
std::string name;
|
||||
int32_t id{-1};
|
||||
|
||||
std::vector<PrimSet> prims;
|
||||
|
||||
};
|
||||
|
||||
struct ObjExportOption
|
||||
{
|
||||
bool export_skinweights{true};
|
||||
int primid{0}; /// Primitive id to export(default 0).
|
||||
int uvset{0}; /// Tex coord ID to export(default 0).
|
||||
bool flip_texcoord_y{true}; /// Flip texture coordinate V?(default true).
|
||||
};
|
||||
|
||||
///
|
||||
/// Save MeshPrim as wavefront .obj
|
||||
///
|
||||
bool SaveAsObjMesh(const std::string &filename, const MeshPrim &mesh, bool flip_texcoord_y = true);
|
||||
|
||||
/// @param[in] basename Base filename. ".obj" will be appended.
|
||||
/// @param[in] mesh MeshPrim.
|
||||
/// @param[in] option Export options
|
||||
///
|
||||
bool SaveAsObjMesh(const std::string &basename, const MeshPrim &mesh, const ObjExportOption &option);
|
||||
|
||||
//
|
||||
/// Save MeshPrim as glTF mesh
|
||||
///
|
||||
bool SaveAsGLTFMesh(const std::string &filename, const MeshPrim &mesh);
|
||||
|
18
examples/mesh-conv/sandbox/README.md
Normal file
18
examples/mesh-conv/sandbox/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# concat_mesh.py
|
||||
|
||||
Append(merge) mesh of glTF A to glTF B.
|
||||
|
||||
`meshes`, `accessors`, `bufferViews`, `materials` of glTF A is appended to glTF B(index to accessor, bufferViews, etc will be recomputed).
|
||||
|
||||
`skin`, `nodes`, etc are not appended(to be merged).
|
||||
|
||||
## TODO
|
||||
|
||||
* [ ] Support multiple glTFs to merge
|
||||
* [ ] Support merging skin
|
||||
* [ ] Support merging different node hierarchies
|
||||
* [ ] `images`, `textures`
|
||||
|
||||
# replace_attrib.py
|
||||
|
||||
Replace the accessor id of specified attribute.
|
110
examples/mesh-conv/sandbox/concat_mesh.py
Normal file
110
examples/mesh-conv/sandbox/concat_mesh.py
Normal file
@ -0,0 +1,110 @@
|
||||
# concat mesh to glTF
|
||||
|
||||
import json
|
||||
import sys, os
|
||||
|
||||
prefix = "added/"
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 4:
|
||||
print("Needs source.gltf target.gltf output.gltf")
|
||||
sys.exit(-1)
|
||||
|
||||
source_filename = sys.argv[1]
|
||||
target_filename = sys.argv[2]
|
||||
output_filename = sys.argv[3]
|
||||
|
||||
source = json.loads(open(source_filename).read())
|
||||
target = json.loads(open(target_filename).read())
|
||||
|
||||
num_target_meshes = len(target["meshes"])
|
||||
num_target_buffers = len(target["buffers"])
|
||||
num_target_bufferViews = len(target["bufferViews"])
|
||||
num_target_accessors = len(target["accessors"])
|
||||
num_target_materials = len(target["materials"])
|
||||
print("num_target_meshes: ", num_target_meshes)
|
||||
print("num_target_buffers: ", num_target_buffers)
|
||||
print("num_target_bufferViews: ", num_target_bufferViews)
|
||||
print("num_target_accessors: ", num_target_accessors)
|
||||
print("num_target_materials: ", num_target_accessors)
|
||||
|
||||
num_source_meshes = len(source["meshes"])
|
||||
num_source_buffers = len(source["buffers"])
|
||||
num_source_bufferViews = len(source["bufferViews"])
|
||||
num_source_accessors = len(source["accessors"])
|
||||
num_source_materials = len(source["materials"]) if "materials" in source else 0
|
||||
print("num_source_meshes: ", num_source_meshes)
|
||||
print("num_source_buffers: ", num_source_buffers)
|
||||
print("num_source_bufferViews: ", num_source_bufferViews)
|
||||
print("num_source_accessors: ", num_source_accessors)
|
||||
print("num_source_materials: ", num_source_materials)
|
||||
|
||||
#
|
||||
# Adjust name and index
|
||||
#
|
||||
for i in range(len(source["buffers"])):
|
||||
if "name" in source["buffers"][i]:
|
||||
source["buffers"][i]["name"] = prefix + source["buffers"][i]["name"]
|
||||
|
||||
for i in range(len(source["bufferViews"])):
|
||||
if "name" in source["bufferViews"][i]:
|
||||
source["bufferViews"][i]["name"] = prefix + source["bufferViews"][i]["name"]
|
||||
|
||||
source["bufferViews"][i]["buffer"] += num_target_buffers
|
||||
|
||||
for i in range(len(source["accessors"])):
|
||||
if "name" in source["accessors"][i]:
|
||||
source["accessors"][i]["name"] = prefix + source["accessors"][i]["name"]
|
||||
|
||||
source["accessors"][i]["bufferView"] += num_target_bufferViews
|
||||
|
||||
for i in range(len(source["meshes"])):
|
||||
mesh = source["meshes"][i]
|
||||
|
||||
if "name" in mesh:
|
||||
source["meshes"][i]["name"] = prefix + source["meshes"][i]["name"]
|
||||
|
||||
for primid in range(len(mesh["primitives"])):
|
||||
for attrib in mesh["primitives"][primid]["attributes"]:
|
||||
#print(source["meshes"][i]["primitives"][primid]["attributes"][attrib])
|
||||
source["meshes"][i]["primitives"][primid]["attributes"][attrib] += num_target_accessors
|
||||
|
||||
|
||||
source["meshes"][i]["primitives"][primid]["indices"] += num_target_accessors
|
||||
if "material" in source["meshes"][i]["primitives"][primid]:
|
||||
source["meshes"][i]["primitives"][primid]["material"] += num_target_materials
|
||||
|
||||
|
||||
#
|
||||
# Append mesh info
|
||||
#
|
||||
target["buffers"] += source["buffers"]
|
||||
target["bufferViews"] += source["bufferViews"]
|
||||
target["meshes"] += source["meshes"]
|
||||
target["accessors"] += source["accessors"]
|
||||
if "materials" in source:
|
||||
target["materials"] += source["materials"]
|
||||
|
||||
#
|
||||
# add some info
|
||||
#
|
||||
extraInfo = {}
|
||||
extraInfo["num_target_meshes"] = num_target_meshes
|
||||
extraInfo["num_target_buffers"] = num_target_buffers
|
||||
extraInfo["num_target_bufferViews"] = num_target_bufferViews
|
||||
extraInfo["num_target_accessors"] = num_target_accessors
|
||||
extraInfo["num_target_materials"] = num_target_materials
|
||||
|
||||
extraInfo["num_source_meshes"] = num_source_meshes
|
||||
extraInfo["num_source_buffers"] = num_source_buffers
|
||||
extraInfo["num_source_bufferViews"] = num_source_bufferViews
|
||||
extraInfo["num_source_accessors"] = num_source_accessors
|
||||
extraInfo["num_source_materials"] = num_source_materials
|
||||
|
||||
target["asset"]["extras"] = extraInfo
|
||||
|
||||
with open(output_filename, "w") as f:
|
||||
f.write(json.dumps(target, indent=2))
|
||||
|
||||
print("Merged glTF was exported to : ", output_filename)
|
||||
main()
|
97
examples/mesh-conv/sandbox/replace_attrib.py
Normal file
97
examples/mesh-conv/sandbox/replace_attrib.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Replace accessor id of attributes for speicified mesh
|
||||
# Usually called after concat_mesh.py
|
||||
# Example usecase is to replace UV coordinate of a mesh.
|
||||
|
||||
import json
|
||||
import sys, os
|
||||
|
||||
attrib_names = ["TEXCOORD_0"]
|
||||
|
||||
def check_accessor(src, target):
|
||||
if src["componentType"] != target["componentType"]:
|
||||
print("componentType mismatch!")
|
||||
return False
|
||||
|
||||
if src["count"] != target["count"]:
|
||||
print("`count` mismatch!")
|
||||
return False
|
||||
|
||||
if src["type"] != target["type"]:
|
||||
print("`type` mismatch!")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 5:
|
||||
print("Needs input.gltf output.gltf source_mesh_name target_mesh_name <source_primid> <target_primid>")
|
||||
sys.exit(-1)
|
||||
|
||||
input_filename = sys.argv[1]
|
||||
output_filename = sys.argv[2]
|
||||
source_mesh_name = sys.argv[3]
|
||||
target_mesh_name = sys.argv[4]
|
||||
|
||||
source_primid = 0
|
||||
target_primid = 0
|
||||
|
||||
if len(sys.argv) > 5:
|
||||
source_primid = int(sys.argv[5])
|
||||
|
||||
if len(sys.argv) > 6:
|
||||
target_primid = int(sys.argv[6])
|
||||
|
||||
gltf = json.loads(open(input_filename).read())
|
||||
|
||||
source_mesh_id = -1
|
||||
target_mesh_id = -1
|
||||
|
||||
for i in range(len(gltf["meshes"])):
|
||||
mesh = gltf["meshes"][i]
|
||||
print("mesh[{}].name = {}".format(i, mesh["name"]))
|
||||
|
||||
if target_mesh_name == mesh["name"]:
|
||||
target_mesh_id = i
|
||||
|
||||
if source_mesh_name == mesh["name"]:
|
||||
source_mesh_id = i
|
||||
|
||||
if source_mesh_id == -1:
|
||||
print("source mesh with name [{}] not found.".format(source_mesh_name))
|
||||
sys.exit(-1)
|
||||
|
||||
if target_mesh_id == -1:
|
||||
print("target mesh with name [{}] not found.".format(target_mesh_name))
|
||||
sys.exit(-1)
|
||||
|
||||
print("target: name = {}, id = {}".format(target_mesh_name, target_mesh_id))
|
||||
print("source: name = {}, id = {}".format(source_mesh_name, source_mesh_id))
|
||||
|
||||
source_mesh = gltf["meshes"][source_mesh_id]
|
||||
target_mesh = gltf["meshes"][target_mesh_id]
|
||||
|
||||
source_prim = source_mesh["primitives"][source_primid]
|
||||
target_prim = target_mesh["primitives"][target_primid]
|
||||
|
||||
for attrib in target_prim["attributes"]:
|
||||
print("attrib ", attrib)
|
||||
if attrib in attrib_names:
|
||||
if attrib in source_prim["attributes"]:
|
||||
target_accessor_id = target_prim["attributes"][attrib]
|
||||
src_accessor_id = source_prim["attributes"][attrib]
|
||||
|
||||
if check_accessor(gltf["accessors"][src_accessor_id], gltf["accessors"][target_accessor_id]):
|
||||
gltf["meshes"][target_mesh_id]["primitives"][target_primid]["attributes"][attrib] = src_accessor_id
|
||||
print("Replaced accessor id for attrib {} from {} to {}".format(attrib, target_accessor_id, src_accessor_id))
|
||||
else:
|
||||
print("Accessor type/format is not identical. Skip replace")
|
||||
print(" attrib {}".format(attrib))
|
||||
|
||||
else:
|
||||
print("attribute[{}] not found in source primitive: mesh[{}].primitives[{}]".format(attrib, source_mesh_name, source_primid))
|
||||
|
||||
with open(output_filename, "w") as f:
|
||||
f.write(json.dumps(gltf, indent=2))
|
||||
|
||||
print("Merged glTF was exported to : ", output_filename)
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user