mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-08-12 11:49:05 +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,6 +418,8 @@ 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 =
|
||||||
@ -429,7 +434,8 @@ static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
|||||||
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);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
|
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);
|
||||||
|
if (verbose) {
|
||||||
std::cout << "[" << k << "] value = " << value << "\n";
|
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);
|
||||||
@ -703,42 +727,48 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
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);
|
|
||||||
|
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) {
|
if (!ok) {
|
||||||
return EXIT_FAILURE;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -371,15 +371,18 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
std::vector<tinygltf::Accessor> *accessors,
|
std::vector<tinygltf::Accessor> *accessors,
|
||||||
std::vector<tinygltf::BufferView> *bufferViews,
|
std::vector<tinygltf::BufferView> *bufferViews,
|
||||||
tinygltf::Buffer *buffer) {
|
tinygltf::Buffer *buffer) {
|
||||||
|
int prim_id = 0;
|
||||||
std::vector<uint8_t> buf;
|
std::vector<uint8_t> buf;
|
||||||
|
|
||||||
// single primitive per mesh
|
// single primitive per mesh
|
||||||
tinygltf::Primitive primitive;
|
tinygltf::Primitive primitive;
|
||||||
|
|
||||||
|
const PrimSet &prim = mesh.prims[prim_id];
|
||||||
|
|
||||||
// vertex index
|
// vertex index
|
||||||
{
|
{
|
||||||
size_t s, e;
|
size_t s, e;
|
||||||
if (!SerializeVertexIndicesToBuffer(mesh.indices, mesh.indices_type, &buf,
|
if (!SerializeVertexIndicesToBuffer(prim.indices, prim.indices_type, &buf,
|
||||||
&s, &e)) {
|
&s, &e)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -393,13 +396,14 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
bufferViews->push_back(bufferView);
|
bufferViews->push_back(bufferView);
|
||||||
|
|
||||||
tinygltf::Accessor accessor;
|
tinygltf::Accessor accessor;
|
||||||
|
accessor.name = mesh.name + "#" + std::to_string(prim_id) + "/indices";
|
||||||
accessor.bufferView = bufferViews->size() - 1;
|
accessor.bufferView = bufferViews->size() - 1;
|
||||||
accessor.minValues.resize(1);
|
accessor.minValues.resize(1);
|
||||||
accessor.minValues[0] = mesh.indices_min;
|
accessor.minValues[0] = prim.indices_min;
|
||||||
accessor.maxValues.resize(1);
|
accessor.maxValues.resize(1);
|
||||||
accessor.maxValues[0] = mesh.indices_max;
|
accessor.maxValues[0] = prim.indices_max;
|
||||||
accessor.count = mesh.indices.size();
|
accessor.count = prim.indices.size();
|
||||||
accessor.componentType = mesh.indices_type;
|
accessor.componentType = prim.indices_type;
|
||||||
accessor.type = TINYGLTF_TYPE_SCALAR;
|
accessor.type = TINYGLTF_TYPE_SCALAR;
|
||||||
accessors->push_back(accessor);
|
accessors->push_back(accessor);
|
||||||
|
|
||||||
@ -409,7 +413,7 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
// position
|
// position
|
||||||
{
|
{
|
||||||
size_t s, e;
|
size_t s, e;
|
||||||
if (!SerializeVertexAttribToBuffer(mesh.position, &buf, &s, &e)) {
|
if (!SerializeVertexAttribToBuffer(prim.position, &buf, &s, &e)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::cout << "postion.byteRange: [" << s << ", " << e << "]\n";
|
std::cout << "postion.byteRange: [" << s << ", " << e << "]\n";
|
||||||
@ -422,15 +426,16 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
bufferViews->push_back(bufferView);
|
bufferViews->push_back(bufferView);
|
||||||
|
|
||||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
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);
|
accessors->push_back(accessor);
|
||||||
|
|
||||||
primitive.attributes["POSITION"] = accessors->size() - 1;
|
primitive.attributes["POSITION"] = accessors->size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mesh.normal.data.size() > 0) {
|
if (prim.normal.data.size() > 0) {
|
||||||
size_t s, e;
|
size_t s, e;
|
||||||
if (!SerializeVertexAttribToBuffer(mesh.normal, &buf, &s, &e)) {
|
if (!SerializeVertexAttribToBuffer(prim.normal, &buf, &s, &e)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::cout << "normal.byteRange: [" << s << ", " << e << "]\n";
|
std::cout << "normal.byteRange: [" << s << ", " << e << "]\n";
|
||||||
@ -443,15 +448,16 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
bufferViews->push_back(bufferView);
|
bufferViews->push_back(bufferView);
|
||||||
|
|
||||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
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);
|
accessors->push_back(accessor);
|
||||||
|
|
||||||
primitive.attributes["NORMAL"] = accessors->size() - 1;
|
primitive.attributes["NORMAL"] = accessors->size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mesh.tangent.data.size() > 0) {
|
if (prim.tangent.data.size() > 0) {
|
||||||
size_t s, e;
|
size_t s, e;
|
||||||
if (!SerializeVertexAttribToBuffer(mesh.tangent, &buf, &s, &e)) {
|
if (!SerializeVertexAttribToBuffer(prim.tangent, &buf, &s, &e)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::cout << "tangent.byteRange: [" << s << ", " << e << "]\n";
|
std::cout << "tangent.byteRange: [" << s << ", " << e << "]\n";
|
||||||
@ -464,14 +470,15 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
bufferViews->push_back(bufferView);
|
bufferViews->push_back(bufferView);
|
||||||
|
|
||||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
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);
|
accessors->push_back(accessor);
|
||||||
|
|
||||||
primitive.attributes["TANGENT"] = accessors->size() - 1;
|
primitive.attributes["TANGENT"] = accessors->size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mesh.texcoords.size() > 0) {
|
if (prim.texcoords.size() > 0) {
|
||||||
for (const auto &item : mesh.texcoords) {
|
for (const auto &item : prim.texcoords) {
|
||||||
size_t s, e;
|
size_t s, e;
|
||||||
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
||||||
return false;
|
return false;
|
||||||
@ -487,6 +494,7 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
|
|
||||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
||||||
item.second, bufferViews->size() - 1, /* offset */ 0);
|
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);
|
accessors->push_back(accessor);
|
||||||
|
|
||||||
std::string target = "TEXCOORD_" + std::to_string(item.first);
|
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) {
|
if (prim.joints.size() > 0) {
|
||||||
for (const auto &item : mesh.joints) {
|
for (const auto &item : prim.joints) {
|
||||||
size_t s, e;
|
size_t s, e;
|
||||||
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
||||||
return false;
|
return false;
|
||||||
@ -511,7 +519,8 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
bufferViews->push_back(bufferView);
|
bufferViews->push_back(bufferView);
|
||||||
|
|
||||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
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);
|
accessors->push_back(accessor);
|
||||||
|
|
||||||
std::string target = "JOINTS_" + std::to_string(item.first);
|
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) {
|
if (prim.weights.size() > 0) {
|
||||||
for (const auto &item : mesh.weights) {
|
for (const auto &item : prim.weights) {
|
||||||
size_t s, e;
|
size_t s, e;
|
||||||
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
if (!SerializeVertexAttribToBuffer(item.second, &buf, &s, &e)) {
|
||||||
return false;
|
return false;
|
||||||
@ -536,7 +545,8 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
bufferViews->push_back(bufferView);
|
bufferViews->push_back(bufferView);
|
||||||
|
|
||||||
tinygltf::Accessor accessor = ConvertToGLTFAccessor(
|
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);
|
accessors->push_back(accessor);
|
||||||
|
|
||||||
std::string target = "WEIGHTS_" + std::to_string(item.first);
|
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);
|
gltfmesh->primitives.push_back(primitive);
|
||||||
|
|
||||||
@ -554,62 +564,134 @@ bool ConvertToGLTFMesh(const MeshPrim &mesh, int buffer_id,
|
|||||||
return true;
|
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,
|
if (prim.weights.size() != prim.joints.size()) {
|
||||||
bool flip_texcoord_y) {
|
std::cerr << "# of JOINTS(" << prim.joints.size() << ") and WEIGHTS(" << prim.weights.size() << ") differs\n";
|
||||||
std::ofstream ofs(filename);
|
|
||||||
if (!ofs) {
|
|
||||||
std::cerr << "Failed to open .obj to write: " << filename << "\n";
|
|
||||||
return false;
|
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_vn = false;
|
||||||
bool has_vt = false;
|
bool has_vt = false;
|
||||||
|
|
||||||
has_vn = mesh.normal.data.size() == mesh.position.data.size();
|
has_vn = prim.normal.data.size() == prim.position.data.size();
|
||||||
has_vt = mesh.texcoords.count(0) &&
|
has_vt = prim.texcoords.count(options.uvset) &&
|
||||||
(mesh.texcoords.at(0).data.size() > 0); // TEXCOORD_0
|
(prim.texcoords.at(options.uvset).data.size() > 0);
|
||||||
|
|
||||||
// v
|
// v
|
||||||
for (size_t i = 0; i < mesh.position.data.size() / 3; i++) {
|
for (size_t i = 0; i < prim.position.data.size() / 3; i++) {
|
||||||
ofs << "v " << mesh.position.data[3 * i + 0] << " "
|
ofs << "v " << prim.position.data[3 * i + 0] << " "
|
||||||
<< mesh.position.data[3 * i + 1] << " " << mesh.position.data[3 * i + 2]
|
<< prim.position.data[3 * i + 1] << " " << prim.position.data[3 * i + 2]
|
||||||
<< "\n";
|
<< "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// vn
|
// vn
|
||||||
for (size_t i = 0; i < mesh.normal.data.size() / 3; i++) {
|
for (size_t i = 0; i < prim.normal.data.size() / 3; i++) {
|
||||||
ofs << "vn " << mesh.normal.data[3 * i + 0] << " "
|
ofs << "vn " << prim.normal.data[3 * i + 0] << " "
|
||||||
<< mesh.normal.data[3 * i + 1] << " " << mesh.normal.data[3 * i + 2]
|
<< prim.normal.data[3 * i + 1] << " " << prim.normal.data[3 * i + 2]
|
||||||
<< "\n";
|
<< "\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
|
// vt
|
||||||
for (size_t i = 0; i < mesh.texcoords.at(0).data.size() / 2; i++) {
|
for (size_t i = 0; i < prim.texcoords.at(options.uvset).data.size() / 2; i++) {
|
||||||
float y = mesh.texcoords.at(0).data[2 * i + 1];
|
float y = prim.texcoords.at(options.uvset).data[2 * i + 1];
|
||||||
if (flip_texcoord_y) {
|
if (options.flip_texcoord_y) {
|
||||||
y = 1.0f - 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
|
// 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.
|
// .obj's index start with 1.
|
||||||
int f0 = int(mesh.indices[3 * i + 0]) + 1;
|
int f0 = int(prim.indices[3 * i + 0]) + 1;
|
||||||
int f1 = int(mesh.indices[3 * i + 1]) + 1;
|
int f1 = int(prim.indices[3 * i + 1]) + 1;
|
||||||
int f2 = int(mesh.indices[3 * i + 2]) + 1;
|
int f2 = int(prim.indices[3 * i + 2]) + 1;
|
||||||
|
|
||||||
ofs << "f " << make_triple(f0, has_vn, has_vt) << " "
|
ofs << "f " << make_triple(f0, has_vn, has_vt) << " "
|
||||||
<< make_triple(f1, has_vn, has_vt) << " "
|
<< make_triple(f1, has_vn, has_vt) << " "
|
||||||
<< make_triple(f2, has_vn, has_vt) << "\n";
|
<< make_triple(f2, has_vn, has_vt) << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(syoyo): Write joints/weights
|
|
||||||
|
|
||||||
return true;
|
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();
|
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||||
bmax[0] = bmax[1] = bmax[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.
|
// reorder texcoords and normals so that it has same indexing to vertices.
|
||||||
if (facevarying) {
|
if (facevarying) {
|
||||||
mesh->position.data.clear();
|
prim.position.data.clear();
|
||||||
mesh->normal.data.clear();
|
prim.normal.data.clear();
|
||||||
mesh->tangent.data.clear();
|
prim.tangent.data.clear();
|
||||||
mesh->texcoords[0] = VertexAttrib();
|
prim.texcoords[0] = VertexAttrib();
|
||||||
|
|
||||||
// Concat shapes
|
// Concat shapes
|
||||||
for (size_t s = 0; s < shapes.size(); s++) {
|
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];
|
n[2][2] = n[0][2];
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->position.data.push_back(v[0][0]);
|
prim.position.data.push_back(v[0][0]);
|
||||||
mesh->position.data.push_back(v[0][1]);
|
prim.position.data.push_back(v[0][1]);
|
||||||
mesh->position.data.push_back(v[0][2]);
|
prim.position.data.push_back(v[0][2]);
|
||||||
|
|
||||||
mesh->position.data.push_back(v[1][0]);
|
prim.position.data.push_back(v[1][0]);
|
||||||
mesh->position.data.push_back(v[1][1]);
|
prim.position.data.push_back(v[1][1]);
|
||||||
mesh->position.data.push_back(v[1][2]);
|
prim.position.data.push_back(v[1][2]);
|
||||||
|
|
||||||
mesh->position.data.push_back(v[2][0]);
|
prim.position.data.push_back(v[2][0]);
|
||||||
mesh->position.data.push_back(v[2][1]);
|
prim.position.data.push_back(v[2][1]);
|
||||||
mesh->position.data.push_back(v[2][2]);
|
prim.position.data.push_back(v[2][2]);
|
||||||
|
|
||||||
mesh->normal.data.push_back(n[0][0]);
|
prim.normal.data.push_back(n[0][0]);
|
||||||
mesh->normal.data.push_back(n[0][1]);
|
prim.normal.data.push_back(n[0][1]);
|
||||||
mesh->normal.data.push_back(n[0][2]);
|
prim.normal.data.push_back(n[0][2]);
|
||||||
|
|
||||||
mesh->normal.data.push_back(n[1][0]);
|
prim.normal.data.push_back(n[1][0]);
|
||||||
mesh->normal.data.push_back(n[1][1]);
|
prim.normal.data.push_back(n[1][1]);
|
||||||
mesh->normal.data.push_back(n[1][2]);
|
prim.normal.data.push_back(n[1][2]);
|
||||||
|
|
||||||
mesh->normal.data.push_back(n[2][0]);
|
prim.normal.data.push_back(n[2][0]);
|
||||||
mesh->normal.data.push_back(n[2][1]);
|
prim.normal.data.push_back(n[2][1]);
|
||||||
mesh->normal.data.push_back(n[2][2]);
|
prim.normal.data.push_back(n[2][2]);
|
||||||
|
|
||||||
mesh->texcoords[0].data.push_back(tc[0][0]);
|
prim.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][1]);
|
||||||
|
|
||||||
mesh->texcoords[0].data.push_back(tc[1][0]);
|
prim.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][1]);
|
||||||
|
|
||||||
mesh->texcoords[0].data.push_back(tc[2][0]);
|
prim.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][1]);
|
||||||
|
|
||||||
size_t idx = mesh->indices.size();
|
size_t idx = prim.indices.size();
|
||||||
mesh->indices.push_back(int(idx) + 0);
|
prim.indices.push_back(int(idx) + 0);
|
||||||
mesh->indices.push_back(int(idx) + 1);
|
prim.indices.push_back(int(idx) + 1);
|
||||||
mesh->indices.push_back(int(idx) + 2);
|
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);
|
maxn = std::max(vertex_skin_weights[i].weightValues.size(), maxn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "Max # of weights = " << maxn << "\n";
|
||||||
int num_slots = 0;
|
int num_slots = 0;
|
||||||
if (maxn > 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++) {
|
for (size_t t = 0; t < size_t(num_slots); t++) {
|
||||||
VertexAttrib weights, joints;
|
VertexAttrib weights, joints;
|
||||||
|
|
||||||
size_t num_faceverts = mesh->indices.size();
|
size_t num_faceverts = prim.indices.size();
|
||||||
|
|
||||||
// facevarying weights/joints Fill with zeros
|
// facevarying weights/joints Fill with zeros
|
||||||
weights.data.resize(4 * num_faceverts, 0.0f);
|
weights.data.resize(4 * num_faceverts, 0.0f);
|
||||||
@ -971,42 +1056,42 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->weights[t] = weights;
|
prim.weights[t] = weights;
|
||||||
mesh->joints[t] = joints;
|
prim.joints[t] = joints;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// position/texcoord/normal can be represented in shared vertex manner
|
// 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++) {
|
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++) {
|
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++) {
|
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;
|
prim.indices_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
|
||||||
mesh->indices.clear();
|
prim.indices.clear();
|
||||||
|
|
||||||
size_t face_index_offset = 0;
|
size_t face_index_offset = 0;
|
||||||
for (size_t s = 0; s < shapes.size(); s++) {
|
for (size_t s = 0; s < shapes.size(); s++) {
|
||||||
const tinyobj::shape_t &shape = shapes[s];
|
const tinyobj::shape_t &shape = shapes[s];
|
||||||
|
|
||||||
for (size_t f = 0; f < shape.mesh.indices.size(); f++) {
|
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));
|
uint32_t(shape.mesh.indices[f].vertex_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
face_index_offset = mesh->indices.size();
|
face_index_offset = prim.indices.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// weights/joints
|
// weights/joints
|
||||||
@ -1017,9 +1102,10 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
|||||||
maxn = std::max(attrib.skin_weights[i].weightValues.size(), maxn);
|
maxn = std::max(attrib.skin_weights[i].weightValues.size(), maxn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "Max # of weights = " << maxn << "\n";
|
||||||
int num_slots = 0;
|
int num_slots = 0;
|
||||||
if (maxn > 0) {
|
if (maxn > 0) {
|
||||||
num_slots = (((maxn - 1) / 4) + 1) * 4;
|
num_slots = maxn / 4;
|
||||||
}
|
}
|
||||||
std::cout << "# of slots = " << num_slots << "\n";
|
std::cout << "# of slots = " << num_slots << "\n";
|
||||||
|
|
||||||
@ -1027,13 +1113,13 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
|||||||
VertexAttrib weights, joints;
|
VertexAttrib weights, joints;
|
||||||
|
|
||||||
// Fill with zeros
|
// Fill with zeros
|
||||||
weights.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 * (mesh->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++) {
|
for (size_t v = 0; v < attrib.skin_weights.size(); v++) {
|
||||||
const tinyobj::skin_weight_t &sw = attrib.skin_weights[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;
|
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.data_type = TINYGLTF_TYPE_VEC4;
|
||||||
weights.component_type =
|
weights.component_type =
|
||||||
TINYGLTF_COMPONENT_TYPE_FLOAT; // storage format
|
TINYGLTF_COMPONENT_TYPE_FLOAT; // storage format
|
||||||
mesh->weights[s] = weights;
|
prim.weights[s] = weights;
|
||||||
|
|
||||||
joints.data_type = TINYGLTF_TYPE_VEC4;
|
joints.data_type = TINYGLTF_TYPE_VEC4;
|
||||||
joints.component_type =
|
joints.component_type =
|
||||||
TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; // storage format
|
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 minv = 0.0;
|
||||||
uint32_t maxv = 0.0;
|
uint32_t maxv = 0.0;
|
||||||
for (size_t i = 0; i < mesh->indices.size(); i++) {
|
for (size_t i = 0; i < prim.indices.size(); i++) {
|
||||||
minv = std::min(minv, uint32_t(mesh->indices[i]));
|
minv = std::min(minv, uint32_t(prim.indices[i]));
|
||||||
maxv = std::max(maxv, uint32_t(mesh->indices[i]));
|
maxv = std::max(maxv, uint32_t(prim.indices[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->indices_min = int(minv);
|
prim.indices_min = int(minv);
|
||||||
mesh->indices_max = int(maxv);
|
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();
|
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||||
bmax[0] = bmax[1] = bmax[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++) {
|
for (size_t k = 0; k < 3; k++) {
|
||||||
bmin[k] = std::min(bmin[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], mesh->position.data[3 * i + k]);
|
bmax[k] = std::max(bmax[k], prim.position.data[3 * i + k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->position.minValues.resize(3);
|
prim.position.minValues.resize(3);
|
||||||
mesh->position.minValues[0] = bmin[0];
|
prim.position.minValues[0] = bmin[0];
|
||||||
mesh->position.minValues[1] = bmin[1];
|
prim.position.minValues[1] = bmin[1];
|
||||||
mesh->position.minValues[2] = bmin[2];
|
prim.position.minValues[2] = bmin[2];
|
||||||
|
|
||||||
mesh->position.maxValues.resize(3);
|
prim.position.maxValues.resize(3);
|
||||||
mesh->position.maxValues[0] = bmax[0];
|
prim.position.maxValues[0] = bmax[0];
|
||||||
mesh->position.maxValues[1] = bmax[1];
|
prim.position.maxValues[1] = bmax[1];
|
||||||
mesh->position.maxValues[2] = bmax[2];
|
prim.position.maxValues[2] = bmax[2];
|
||||||
|
|
||||||
mesh->position.data_type = TINYGLTF_TYPE_VEC3;
|
prim.position.data_type = TINYGLTF_TYPE_VEC3;
|
||||||
mesh->position.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
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();
|
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||||
bmax[0] = bmax[1] = bmax[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++) {
|
for (size_t k = 0; k < 3; k++) {
|
||||||
bmin[k] = std::min(bmin[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], mesh->normal.data[3 * i + k]);
|
bmax[k] = std::max(bmax[k], prim.normal.data[3 * i + k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->normal.minValues.resize(3);
|
prim.normal.minValues.resize(3);
|
||||||
mesh->normal.minValues[0] = bmin[0];
|
prim.normal.minValues[0] = bmin[0];
|
||||||
mesh->normal.minValues[1] = bmin[1];
|
prim.normal.minValues[1] = bmin[1];
|
||||||
mesh->normal.minValues[2] = bmin[2];
|
prim.normal.minValues[2] = bmin[2];
|
||||||
|
|
||||||
mesh->normal.maxValues.resize(3);
|
prim.normal.maxValues.resize(3);
|
||||||
mesh->normal.maxValues[0] = bmax[0];
|
prim.normal.maxValues[0] = bmax[0];
|
||||||
mesh->normal.maxValues[1] = bmax[1];
|
prim.normal.maxValues[1] = bmax[1];
|
||||||
mesh->normal.maxValues[2] = bmax[2];
|
prim.normal.maxValues[2] = bmax[2];
|
||||||
|
|
||||||
mesh->normal.data_type = TINYGLTF_TYPE_VEC3;
|
prim.normal.data_type = TINYGLTF_TYPE_VEC3;
|
||||||
mesh->normal.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
prim.normal.component_type = TINYGLTF_COMPONENT_TYPE_FLOAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
float bmin[4];
|
float bmin[4];
|
||||||
float bmax[4];
|
float bmax[4];
|
||||||
bmin[0] = bmin[1] = bmin[2] = bmin[3] = std::numeric_limits<float>::max();
|
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();
|
-std::numeric_limits<float>::max();
|
||||||
|
|
||||||
size_t n = 3;
|
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++) {
|
for (size_t k = 0; k < n; k++) {
|
||||||
bmin[k] = std::min(bmin[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], mesh->tangent.data[n * i + k]);
|
bmax[k] = std::max(bmax[k], prim.tangent.data[n * i + k]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->tangent.minValues.resize(n);
|
prim.tangent.minValues.resize(n);
|
||||||
mesh->tangent.maxValues.resize(n);
|
prim.tangent.maxValues.resize(n);
|
||||||
for (size_t k = 0; k < n; k++) {
|
for (size_t k = 0; k < n; k++) {
|
||||||
mesh->tangent.minValues[k] = bmin[k];
|
prim.tangent.minValues[k] = bmin[k];
|
||||||
mesh->tangent.maxValues[k] = bmax[k];
|
prim.tangent.maxValues[k] = bmax[k];
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->tangent.data_type =
|
prim.tangent.data_type =
|
||||||
(n == 3) ? TINYGLTF_TYPE_VEC3 : TINYGLTF_TYPE_VEC4;
|
(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
|
// texcoord
|
||||||
for (auto &item : mesh->texcoords) {
|
for (auto &item : prim.texcoords) {
|
||||||
float bmin[2];
|
float bmin[2];
|
||||||
float bmax[2];
|
float bmax[2];
|
||||||
bmin[0] = bmin[1] = std::numeric_limits<float>::max();
|
bmin[0] = bmin[1] = std::numeric_limits<float>::max();
|
||||||
@ -1185,22 +1271,32 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// joints
|
// joints
|
||||||
for (auto &item : mesh->joints) {
|
for (auto &item : prim.joints) {
|
||||||
float bmin;
|
|
||||||
float bmax;
|
|
||||||
bmin = std::numeric_limits<float>::max();
|
|
||||||
bmax = -std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < item.second.data.size(); i++) {
|
std::cout << "joint -- " << item.first << "\n";
|
||||||
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] = 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);
|
// TODO(syoyo): check if the value is within ushort max
|
||||||
item.second.maxValues.resize(1);
|
item.second.minValues.resize(n);
|
||||||
|
item.second.maxValues.resize(n);
|
||||||
item.second.minValues[0] = bmin;
|
for (size_t k = 0; k < n; k++) {
|
||||||
item.second.maxValues[0] = bmax;
|
item.second.minValues[k] = bmin[k];
|
||||||
|
item.second.maxValues[k] = bmax[k];
|
||||||
|
}
|
||||||
|
|
||||||
item.second.data_type = TINYGLTF_TYPE_VEC4;
|
item.second.data_type = TINYGLTF_TYPE_VEC4;
|
||||||
item.second.component_type =
|
item.second.component_type =
|
||||||
@ -1208,22 +1304,29 @@ bool LoadObjMesh(const std::string &filename, bool facevarying,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// weights
|
// weights
|
||||||
for (auto &item : mesh->weights) {
|
for (auto &item : prim.weights) {
|
||||||
float bmin;
|
|
||||||
float bmax;
|
|
||||||
bmin = std::numeric_limits<float>::max();
|
|
||||||
bmax = -std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < item.second.data.size(); i++) {
|
float bmin[4];
|
||||||
bmin = std::min(bmin, item.second.data[i]);
|
float bmax[4];
|
||||||
bmax = std::max(bmax, item.second.data[i]);
|
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.minValues.resize(n);
|
||||||
item.second.maxValues.resize(1);
|
item.second.maxValues.resize(n);
|
||||||
|
for (size_t k = 0; k < n; k++) {
|
||||||
item.second.minValues[0] = bmin;
|
item.second.minValues[k] = bmin[k];
|
||||||
item.second.maxValues[0] = bmax;
|
item.second.maxValues[k] = bmax[k];
|
||||||
|
}
|
||||||
|
|
||||||
item.second.data_type = TINYGLTF_TYPE_VEC4;
|
item.second.data_type = TINYGLTF_TYPE_VEC4;
|
||||||
item.second.component_type =
|
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
|
// Use filename as mesh's name
|
||||||
mesh->name = GetBaseFilename(filename);
|
mesh->name = GetBaseFilename(filename);
|
||||||
|
|
||||||
mesh->mode = TINYGLTF_MODE_TRIANGLES;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintMeshPrim(const MeshPrim &mesh) {
|
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 : "
|
std::cout << "indices.component_type : "
|
||||||
<< PrintComponentType(mesh.indices_type) << "\n";
|
<< PrintComponentType(prim.indices_type) << "\n";
|
||||||
std::cout << "# of indices : " << mesh.indices.size() << "\n";
|
std::cout << "# of indices : " << prim.indices.size() << "\n";
|
||||||
std::cout << " indices.min = " << mesh.indices_min
|
std::cout << " indices.min = " << prim.indices_min
|
||||||
<< ", max = " << mesh.indices_max << "\n";
|
<< ", max = " << prim.indices_max << "\n";
|
||||||
for (size_t i = 0; i < mesh.indices.size(); i++) {
|
for (size_t i = 0; i < prim.indices.size(); i++) {
|
||||||
std::cout << " index[" << i << "] = " << mesh.indices[i] << "\n";
|
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 : "
|
std::cout << "position.component_type : "
|
||||||
<< PrintComponentType(mesh.position.component_type) << "\n";
|
<< PrintComponentType(prim.position.component_type) << "\n";
|
||||||
std::cout << "# of positions : " << mesh.position.data.size() / 3 << "\n";
|
std::cout << "# of positions : " << prim.position.data.size() / 3 << "\n";
|
||||||
if ((mesh.position.minValues.size() == 3) &&
|
if ((prim.position.minValues.size() == 3) &&
|
||||||
(mesh.position.maxValues.size() == 3)) {
|
(prim.position.maxValues.size() == 3)) {
|
||||||
std::cout << " position.min = " << mesh.position.minValues
|
std::cout << " position.min = " << prim.position.minValues
|
||||||
<< ", max = " << mesh.position.maxValues << "\n";
|
<< ", max = " << prim.position.maxValues << "\n";
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < mesh.position.data.size() / 3; i++) {
|
for (size_t i = 0; i < prim.position.data.size() / 3; i++) {
|
||||||
std::cout << " position[" << i << "] = " << mesh.position.data[3 * i + 0]
|
std::cout << " position[" << i << "] = " << prim.position.data[3 * i + 0]
|
||||||
<< ", " << mesh.position.data[3 * i + 1] << ", "
|
<< ", " << prim.position.data[3 * i + 1] << ", "
|
||||||
<< mesh.position.data[3 * i + 2] << std::endl;
|
<< 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 : "
|
std::cout << "normal.component_type : "
|
||||||
<< PrintComponentType(mesh.normal.component_type) << "\n";
|
<< PrintComponentType(prim.normal.component_type) << "\n";
|
||||||
std::cout << "# of normals : " << mesh.normal.data.size() / 3 << "\n";
|
std::cout << "# of normals : " << prim.normal.data.size() / 3 << "\n";
|
||||||
if ((mesh.normal.minValues.size() == 3) &&
|
if ((prim.normal.minValues.size() == 3) &&
|
||||||
(mesh.normal.maxValues.size() == 3)) {
|
(prim.normal.maxValues.size() == 3)) {
|
||||||
std::cout << " normal.min = " << mesh.normal.minValues
|
std::cout << " normal.min = " << prim.normal.minValues
|
||||||
<< ", max = " << mesh.normal.maxValues << "\n";
|
<< ", max = " << prim.normal.maxValues << "\n";
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < mesh.normal.data.size() / 3; i++) {
|
for (size_t i = 0; i < prim.normal.data.size() / 3; i++) {
|
||||||
std::cout << " normal[" << i << "] = " << mesh.normal.data[3 * i + 0]
|
std::cout << " normal[" << i << "] = " << prim.normal.data[3 * i + 0]
|
||||||
<< ", " << mesh.normal.data[3 * i + 1] << ", "
|
<< ", " << prim.normal.data[3 * i + 1] << ", "
|
||||||
<< mesh.normal.data[3 * i + 2] << std::endl;
|
<< prim.normal.data[3 * i + 2] << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mesh.tangent.data.size() > 0) {
|
if (prim.tangent.data.size() > 0) {
|
||||||
assert((mesh.tangent.data_type == TINYGLTF_TYPE_VEC3) ||
|
assert((prim.tangent.data_type == TINYGLTF_TYPE_VEC3) ||
|
||||||
(mesh.tangent.data_type == TINYGLTF_TYPE_VEC4));
|
(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 : "
|
std::cout << "tangent.component_type : "
|
||||||
<< PrintComponentType(mesh.tangent.component_type) << "\n";
|
<< PrintComponentType(prim.tangent.component_type) << "\n";
|
||||||
std::cout << "# of tangents : " << mesh.tangent.data.size() / n << "\n";
|
std::cout << "# of tangents : " << prim.tangent.data.size() / n << "\n";
|
||||||
if ((mesh.tangent.minValues.size() == 3) &&
|
if ((prim.tangent.minValues.size() == 3) &&
|
||||||
(mesh.tangent.maxValues.size() == 3)) {
|
(prim.tangent.maxValues.size() == 3)) {
|
||||||
std::cout << " tangent.min = " << mesh.tangent.minValues
|
std::cout << " tangent.min = " << prim.tangent.minValues
|
||||||
<< ", max = " << mesh.tangent.maxValues << "\n";
|
<< ", max = " << prim.tangent.maxValues << "\n";
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < mesh.tangent.data.size() / n; i++) {
|
for (size_t i = 0; i < prim.tangent.data.size() / n; i++) {
|
||||||
std::cout << " tangent[" << i << "] = " << mesh.tangent.data[n * i + 0]
|
std::cout << " tangent[" << i << "] = " << prim.tangent.data[n * i + 0]
|
||||||
<< ", " << mesh.tangent.data[n * i + 1] << ", "
|
<< ", " << prim.tangent.data[n * i + 1] << ", "
|
||||||
<< mesh.tangent.data[n * i + 2];
|
<< prim.tangent.data[n * i + 2];
|
||||||
|
|
||||||
if (n == 4) {
|
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 << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "# of texcoord slots : " << mesh.texcoords.size() << "\n";
|
std::cout << "# of texcoord slots : " << prim.texcoords.size() << "\n";
|
||||||
for (const auto &item : mesh.texcoords) {
|
for (const auto &item : prim.texcoords) {
|
||||||
std::cout << "TEXCOORD_" << item.first << "\n";
|
std::cout << "TEXCOORD_" << item.first << "\n";
|
||||||
|
|
||||||
assert(item.second.data_type == TINYGLTF_TYPE_VEC2);
|
assert(item.second.data_type == TINYGLTF_TYPE_VEC2);
|
||||||
@ -1327,10 +1438,10 @@ void PrintMeshPrim(const MeshPrim &mesh) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(mesh.joints.size() == mesh.weights.size());
|
assert(prim.joints.size() == prim.weights.size());
|
||||||
std::cout << "# of joints/weights slots : " << mesh.joints.size() << "\n";
|
std::cout << "# of joints/weights slots : " << prim.joints.size() << "\n";
|
||||||
for (const auto &item : mesh.joints) {
|
for (const auto &item : prim.joints) {
|
||||||
assert(mesh.weights.count(item.first));
|
assert(prim.weights.count(item.first));
|
||||||
|
|
||||||
assert(item.second.data_type == TINYGLTF_TYPE_VEC4);
|
assert(item.second.data_type == TINYGLTF_TYPE_VEC4);
|
||||||
|
|
||||||
@ -1349,7 +1460,7 @@ void PrintMeshPrim(const MeshPrim &mesh) {
|
|||||||
<< "\n";
|
<< "\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
|
// weight must be uint8 or uint16(normalized), or float
|
||||||
assert((attrib.component_type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) ||
|
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";
|
std::cout << " weights[" << i << "] = " << attrib.data[i] << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace example
|
} // namespace example
|
||||||
|
@ -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