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.
|
* [glview](examples/glview) : Simple glTF geometry viewer.
|
||||||
* [validator](examples/validator) : Simple glTF validator with JSON schema.
|
* [validator](examples/validator) : Simple glTF validator with JSON schema.
|
||||||
* [basic](examples/basic) : Basic glTF viewer with texturing support.
|
* [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
|
## 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.
|
* 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/
|
* 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
|
* 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
|
# 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 so we need to write a converter.
|
||||||
|
|
||||||
This example show how to
|
This example show how to
|
||||||
|
|
||||||
- Export mesh data from .bin to .obj
|
- Export mesh data from .bin to .obj
|
||||||
- Import mesh data to .bin(update corresponding buffer data) from .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
|
## Usage
|
||||||
|
|
||||||
### Wavefront .obj to glTF
|
### 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.
|
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
|
### glTF to Wavefront .obj
|
||||||
|
|
||||||
```
|
```
|
||||||
$ mesh-modify gltf2obj input.gltf
|
$ mesh-modify --op=gltf2obj input.gltf
|
||||||
```
|
```
|
||||||
|
|
||||||
.obj will be created for each glTF Mesh.
|
.obj will be created for each glTF Mesh.
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#if !defined(__ANDROID__) && !defined(_WIN32)
|
#if !defined(__ANDROID__) && !defined(_WIN32)
|
||||||
#include <wordexp.h>
|
#include <wordexp.h>
|
||||||
@ -18,6 +18,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "../../json.hpp"
|
#include "../../json.hpp"
|
||||||
|
#include "../common/clipp.h"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
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,
|
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++) {
|
for (size_t i = 0; i < mesh.primitives.size(); i++) {
|
||||||
const tinygltf::Primitive &primitive = mesh.primitives[i];
|
const tinygltf::Primitive &primitive = mesh.primitives[i];
|
||||||
|
|
||||||
@ -415,10 +418,12 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
example::PrimSet out_prim;
|
||||||
|
|
||||||
// indices.
|
// indices.
|
||||||
{
|
{
|
||||||
const tinygltf::Accessor &indexAccessor =
|
const tinygltf::Accessor &indexAccessor =
|
||||||
model.accessors[size_t(primitive.indices)];
|
model.accessors[size_t(primitive.indices)];
|
||||||
|
|
||||||
size_t num_elements = indexAccessor.count;
|
size_t num_elements = indexAccessor.count;
|
||||||
std::cout << "index.elements = " << num_elements << "\n";
|
std::cout << "index.elements = " << num_elements << "\n";
|
||||||
@ -426,10 +431,11 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
|||||||
size_t byte_stride = ComponentTypeByteSize(indexAccessor.componentType);
|
size_t byte_stride = ComponentTypeByteSize(indexAccessor.componentType);
|
||||||
|
|
||||||
const tinygltf::BufferView &indexBufferView =
|
const tinygltf::BufferView &indexBufferView =
|
||||||
model.bufferViews[size_t(indexAccessor.bufferView)];
|
model.bufferViews[size_t(indexAccessor.bufferView)];
|
||||||
|
|
||||||
// should be 34963(ELEMENT_ARRAY_BUFFER)
|
// 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) {
|
if (indexBufferView.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
|
||||||
std::cerr << "indexBufferView.target must be ELEMENT_ARRAY_BUFFER\n";
|
std::cerr << "indexBufferView.target must be ELEMENT_ARRAY_BUFFER\n";
|
||||||
return false;
|
return false;
|
||||||
@ -441,21 +447,22 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
|||||||
std::vector<uint32_t> indices;
|
std::vector<uint32_t> indices;
|
||||||
|
|
||||||
for (size_t k = 0; k < num_elements; k++) {
|
for (size_t k = 0; k < num_elements; k++) {
|
||||||
|
|
||||||
// TODO(syoyo): out-of-bounds check.
|
// TODO(syoyo): out-of-bounds check.
|
||||||
const unsigned char *ptr = indexBuffer.data.data() +
|
const unsigned char *ptr = indexBuffer.data.data() +
|
||||||
indexBufferView.byteOffset + (k * byte_stride) +
|
indexBufferView.byteOffset +
|
||||||
indexAccessor.byteOffset;
|
(k * byte_stride) + indexAccessor.byteOffset;
|
||||||
|
|
||||||
uint32_t idx = UnpackIndex(ptr, indexAccessor.componentType);
|
uint32_t idx = UnpackIndex(ptr, indexAccessor.componentType);
|
||||||
|
|
||||||
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
|
if (verbose) {
|
||||||
|
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
indices.push_back(idx);
|
indices.push_back(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
out->indices = indices;
|
out_prim.indices = indices;
|
||||||
out->indices_type = indexAccessor.componentType;
|
out_prim.indices_type = indexAccessor.componentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// attributes
|
// attributes
|
||||||
@ -522,7 +529,9 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
|||||||
bufferView.byteOffset + (k * byte_stride) +
|
bufferView.byteOffset + (k * byte_stride) +
|
||||||
accessor.byteOffset;
|
accessor.byteOffset;
|
||||||
float value = Unpack(ptr, accessor.componentType);
|
float value = Unpack(ptr, accessor.componentType);
|
||||||
std::cout << "[" << k << "] value = " << value << "\n";
|
if (verbose) {
|
||||||
|
std::cout << "[" << k << "] value = " << value << "\n";
|
||||||
|
}
|
||||||
attrib.data.push_back(value);
|
attrib.data.push_back(value);
|
||||||
}
|
}
|
||||||
attrib.component_type = accessor.componentType;
|
attrib.component_type = accessor.componentType;
|
||||||
@ -530,28 +539,27 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
|||||||
attrib.name = it->first;
|
attrib.name = it->first;
|
||||||
|
|
||||||
if (attrib.name.compare("POSITION") == 0) {
|
if (attrib.name.compare("POSITION") == 0) {
|
||||||
out->position = attrib;
|
out_prim.position = attrib;
|
||||||
} else if (attrib.name.compare("NORMAL") == 0) {
|
} else if (attrib.name.compare("NORMAL") == 0) {
|
||||||
out->normal = attrib;
|
out_prim.normal = attrib;
|
||||||
} else if (attrib.name.compare("TANGENT") == 0) {
|
} else if (attrib.name.compare("TANGENT") == 0) {
|
||||||
out->tangent = attrib;
|
out_prim.tangent = attrib;
|
||||||
} else if (attrib.name.rfind("TEXCOORD_", 0) == 0) {
|
} else if (attrib.name.rfind("TEXCOORD_", 0) == 0) {
|
||||||
int id = GetSlotId(attrib.name);
|
int id = GetSlotId(attrib.name);
|
||||||
std::cout << "texcoord[" << id << "]\n";
|
std::cout << "texcoord[" << id << "]\n";
|
||||||
out->texcoords[id] = attrib;
|
out_prim.texcoords[id] = attrib;
|
||||||
} else if (attrib.name.rfind("JOINTS_", 0) == 0) {
|
} else if (attrib.name.rfind("JOINTS_", 0) == 0) {
|
||||||
int id = GetSlotId(attrib.name);
|
int id = GetSlotId(attrib.name);
|
||||||
std::cout << "joints[" << id << "]\n";
|
std::cout << "joints[" << id << "]\n";
|
||||||
out->joints[id] = attrib;
|
out_prim.joints[id] = attrib;
|
||||||
} else if (attrib.name.rfind("WEIGHTS_", 0) == 0) {
|
} else if (attrib.name.rfind("WEIGHTS_", 0) == 0) {
|
||||||
int id = GetSlotId(attrib.name);
|
int id = GetSlotId(attrib.name);
|
||||||
std::cout << "weights[" << id << "]\n";
|
std::cout << "weights[" << id << "]\n";
|
||||||
out->weights[id] = attrib;
|
out_prim.weights[id] = attrib;
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "???: attrib.name = " << attrib.name << "\n";
|
std::cerr << "???: attrib.name = " << attrib.name << "\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,15 +572,18 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
out->mode = primitive.mode;
|
out_prim.mode = primitive.mode;
|
||||||
out->name = mesh.name;
|
|
||||||
|
out->prims.push_back(out_prim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<example::MeshPrim> *outs) {
|
bool verbose, std::vector<example::MeshPrim> *outs) {
|
||||||
// Get .bin data
|
// Get .bin data
|
||||||
{
|
{
|
||||||
if (model.buffers.size() != 1) {
|
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);
|
search_paths.push_back(asset_path);
|
||||||
|
|
||||||
std::string abs_filepath = FindFile(search_paths, buffer.uri);
|
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()) {
|
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";
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -608,7 +621,7 @@ static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
|
|||||||
std::cout << "mesh.name: " << mesh.name << "\n";
|
std::cout << "mesh.name: " << mesh.name << "\n";
|
||||||
|
|
||||||
example::MeshPrim output;
|
example::MeshPrim output;
|
||||||
bool ret = DumpMesh(model, mesh, &output);
|
bool ret = DumpMesh(model, mesh, verbose, &output);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -622,31 +635,41 @@ static bool ExtractMesh(const std::string &asset_path, tinygltf::Model &model,
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
if (argc < 3) {
|
std::string op;
|
||||||
std::cout << "mesh-modify <op> <args>" << std::endl;
|
std::string input_filename;
|
||||||
std::cout << " op\n\n";
|
std::string output_filename = "output.gltf";
|
||||||
std::cout << " gltf2obj input.gltf <flip_texcoord_y>\n";
|
int uvset = 0;
|
||||||
std::cout << " obj2gltf input.obj <verbose>\n";
|
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;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string op = argv[1];
|
|
||||||
|
|
||||||
if (op == "gltf2obj") {
|
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::Model model;
|
||||||
tinygltf::TinyGLTF loader;
|
tinygltf::TinyGLTF loader;
|
||||||
std::string err;
|
std::string err;
|
||||||
std::string warn;
|
std::string warn;
|
||||||
|
|
||||||
std::string input_filename(argv[2]);
|
|
||||||
|
|
||||||
std::string ext = GetFilePathExtension(input_filename);
|
std::string ext = GetFilePathExtension(input_filename);
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -657,8 +680,8 @@ int main(int argc, char **argv) {
|
|||||||
input_filename.c_str());
|
input_filename.c_str());
|
||||||
} else {
|
} else {
|
||||||
// assume ascii glTF.
|
// assume ascii glTF.
|
||||||
ret =
|
ret = loader.LoadASCIIFromFile(&model, &err, &warn,
|
||||||
loader.LoadASCIIFromFile(&model, &err, &warn, input_filename.c_str());
|
input_filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!warn.empty()) {
|
if (!warn.empty()) {
|
||||||
@ -674,6 +697,7 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
json j;
|
json j;
|
||||||
{
|
{
|
||||||
std::ifstream i(input_filename);
|
std::ifstream i(input_filename);
|
||||||
@ -693,7 +717,7 @@ int main(int argc, char **argv) {
|
|||||||
json j_original = R"({
|
json j_original = R"({
|
||||||
"baz": ["one", "two", "three"],
|
"baz": ["one", "two", "three"],
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
})"_json;
|
})"_json;
|
||||||
|
|
||||||
//json j_patch = R"([
|
//json j_patch = R"([
|
||||||
// { "op": "remove", "path": "/buffers" }
|
// { "op": "remove", "path": "/buffers" }
|
||||||
@ -701,44 +725,50 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
std::cout << "patch = " << j_patch.dump(2) << "\n";
|
std::cout << "patch = " << j_patch.dump(2) << "\n";
|
||||||
|
|
||||||
json j_ret = j.patch(j_patch);
|
json j_ret = j.patch(j_patch);
|
||||||
std::cout << "patched = " << j_ret.dump(2) << "\n";
|
std::cout << "patched = " << j_ret.dump(2) << "\n";
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string basedir = GetBaseDir(input_filename);
|
std::string basedir = GetBaseDir(input_filename);
|
||||||
std::vector<example::MeshPrim> meshes;
|
std::vector<example::MeshPrim> meshes;
|
||||||
bool ret = ExtractMesh(basedir, model, &meshes);
|
bool ret = ExtractMesh(basedir, model, verbose, &meshes);
|
||||||
|
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
for (const auto &mesh : meshes) {
|
for (const auto &mesh : meshes) {
|
||||||
// Assume no duplicated name in .glTF data
|
// Assume no duplicated name in .glTF data
|
||||||
std::string filename;
|
std::string basename;
|
||||||
if (mesh.name.empty()) {
|
if (mesh.name.empty()) {
|
||||||
filename = "untitled-" + std::to_string(n) + ".obj";
|
basename = "untitled-" + std::to_string(n);
|
||||||
} else {
|
} else {
|
||||||
filename = mesh.name + ".obj";
|
basename = mesh.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = example::SaveAsObjMesh(filename, mesh, flip_texcoord_y);
|
|
||||||
if (!ok) {
|
for (size_t primid = 0; primid < mesh.prims.size(); primid++) {
|
||||||
return EXIT_FAILURE;
|
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) {
|
||||||
|
std::cout << "Failed to export mesh[" << mesh.name << "].primitives["
|
||||||
|
<< primid << "]\n";
|
||||||
|
// may ok;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
} else if (op == "obj2gltf") {
|
} 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?
|
// Require facevarying layout?
|
||||||
// facevarying representation is required if a vertex can have multiple normal/uv value.
|
// facevarying representation is required if a vertex can have multiple
|
||||||
// drawback of facevarying is mesh data increases.
|
// normal/uv value. drawback of facevarying is mesh data increases. false =
|
||||||
// false = try to keep shared vertex representation as much as possible.
|
// try to keep shared vertex representation as much as possible. true =
|
||||||
// true = reorder vertex data and re-assign vertex indices for facevarying data layout.
|
// reorder vertex data and re-assign vertex indices for facevarying data
|
||||||
|
// layout.
|
||||||
bool facevarying = false;
|
bool facevarying = false;
|
||||||
|
|
||||||
example::MeshPrim mesh;
|
example::MeshPrim mesh;
|
||||||
@ -751,8 +781,6 @@ int main(int argc, char **argv) {
|
|||||||
PrintMeshPrim(mesh);
|
PrintMeshPrim(mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string output_filename("output.gltf");
|
|
||||||
|
|
||||||
ok = example::SaveAsGLTFMesh(output_filename, mesh);
|
ok = example::SaveAsGLTFMesh(output_filename, mesh);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
std::cerr << "Failed to save mesh as glTF\n";
|
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";
|
std::cout << "Write glTF: " << output_filename << "\n";
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
std::cerr << "Unknown operation: " << op << "\n";
|
std::cerr << "Unknown operation: " << op << "\n";
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -28,9 +28,7 @@ struct VertexAttrib {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MeshPrim {
|
struct PrimSet {
|
||||||
std::string name;
|
|
||||||
int32_t id{-1};
|
|
||||||
|
|
||||||
int mode; // e.g. TRIANGLES
|
int mode; // e.g. TRIANGLES
|
||||||
|
|
||||||
@ -49,14 +47,35 @@ struct MeshPrim {
|
|||||||
int indices_max{-1};
|
int indices_max{-1};
|
||||||
int indices_type{-1}; // storage type(componentType) of `indices`.
|
int indices_type{-1}; // storage type(componentType) of `indices`.
|
||||||
std::vector<uint32_t> indices; // vertex 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
|
/// 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
|
/// Save MeshPrim as glTF mesh
|
||||||
///
|
///
|
||||||
bool SaveAsGLTFMesh(const std::string &filename, const MeshPrim &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