mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-08-11 15:39:12 +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,10 +418,12 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||
return false;
|
||||
}
|
||||
|
||||
example::PrimSet out_prim;
|
||||
|
||||
// indices.
|
||||
{
|
||||
const tinygltf::Accessor &indexAccessor =
|
||||
model.accessors[size_t(primitive.indices)];
|
||||
model.accessors[size_t(primitive.indices)];
|
||||
|
||||
size_t num_elements = indexAccessor.count;
|
||||
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);
|
||||
|
||||
const tinygltf::BufferView &indexBufferView =
|
||||
model.bufferViews[size_t(indexAccessor.bufferView)];
|
||||
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);
|
||||
|
||||
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
|
||||
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);
|
||||
std::cout << "[" << k << "] value = " << value << "\n";
|
||||
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);
|
||||
@ -693,7 +717,7 @@ int main(int argc, char **argv) {
|
||||
json j_original = R"({
|
||||
"baz": ["one", "two", "three"],
|
||||
"foo": "bar"
|
||||
})"_json;
|
||||
})"_json;
|
||||
|
||||
//json j_patch = R"([
|
||||
// { "op": "remove", "path": "/buffers" }
|
||||
@ -701,44 +725,50 @@ int main(int argc, char **argv) {
|
||||
|
||||
std::cout << "patch = " << j_patch.dump(2) << "\n";
|
||||
|
||||
json j_ret = j.patch(j_patch);
|
||||
std::cout << "patched = " << j_ret.dump(2) << "\n";
|
||||
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);
|
||||
if (!ok) {
|
||||
return EXIT_FAILURE;
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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