mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-19 21:58:50 +00:00
Compare commits
14 Commits
github-act
...
mesh-conv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb99999c1a | ||
|
|
be9ff66eeb | ||
|
|
0a4e8b761f | ||
|
|
4adeae9709 | ||
|
|
6224e021ce | ||
|
|
5dcf778b31 | ||
|
|
289cc448ed | ||
|
|
0ee2120965 | ||
|
|
cf30d42cbc | ||
|
|
cb0a8794c3 | ||
|
|
60e22f3281 | ||
|
|
4cfd2849fb | ||
|
|
a5416707c9 | ||
|
|
8ec9af7f2e |
@@ -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
File diff suppressed because it is too large
Load Diff
5
examples/mesh-conv/Makefile
Normal file
5
examples/mesh-conv/Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#EXTRA_CXXFLAGS := -fsanitize=address -fno-omit-frame-pointer -Wall -Werror -Weverything -Wno-c++11-long-long -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded
|
||||||
|
EXTRA_CXXFLAGS :=
|
||||||
|
|
||||||
|
all:
|
||||||
|
clang++ -std=c++11 -g -O1 -I../../ -I../common $(EXTRA_CXXFLAGS) -o mesh-conv mesh-conv.cc mesh-util.cc tinygltf_impl.cc
|
||||||
5
examples/mesh-conv/Makefile.meson
Normal file
5
examples/mesh-conv/Makefile.meson
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
ninja
|
||||||
|
|
||||||
|
clean:
|
||||||
|
ninja -t clean
|
||||||
58
examples/mesh-conv/README.md
Normal file
58
examples/mesh-conv/README.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# 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 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 --op=obj2gltf input.obj
|
||||||
|
```
|
||||||
|
|
||||||
|
All shapes in .obj are concatenated and create single glTF mesh.
|
||||||
|
(tinyobjloader's skin weight extension `vw` supported)
|
||||||
|
|
||||||
|
#### Limitation
|
||||||
|
|
||||||
|
Buffer is stored as external file(`.bin`)
|
||||||
|
|
||||||
|
### glTF to Wavefront .obj
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mesh-modify --op=gltf2obj input.gltf
|
||||||
|
```
|
||||||
|
|
||||||
|
.obj will be created for each glTF Mesh.
|
||||||
|
(Skin weight data is exported to .obj using tinyobjloader's skin weight extension `vw`)
|
||||||
|
|
||||||
|
#### Limitation
|
||||||
|
|
||||||
|
Buffer is stored as external file(`.bin`)
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* [ ] obj2gltf: Assign glTF material from .mtl
|
||||||
|
* [ ] modify/patch mesh geometry in glTF
|
||||||
|
* By using JSON Patch or JSON Merge feature
|
||||||
3
examples/mesh-conv/bootstrap-meson.sh
Executable file
3
examples/mesh-conv/bootstrap-meson.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
rm -rf build
|
||||||
|
CXX=clang++ CC=clang meson build -Db_sanitize=address -Db_lundef=false --buildtype=debug
|
||||||
|
cp Makefile.meson build/Makefile
|
||||||
798
examples/mesh-conv/mesh-conv.cc
Normal file
798
examples/mesh-conv/mesh-conv.cc
Normal file
@@ -0,0 +1,798 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if !defined(__ANDROID__) && !defined(_WIN32)
|
||||||
|
#include <wordexp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Weverything"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../../json.hpp"
|
||||||
|
#include "../common/clipp.h"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "../../tiny_gltf.h"
|
||||||
|
#else
|
||||||
|
#include "tiny_gltf.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "mesh-util.hh"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#pragma clang diagnostic ignored "-Wunused-function"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static std::string PrintMode(int mode) {
|
||||||
|
if (mode == TINYGLTF_MODE_POINTS) {
|
||||||
|
return "POINTS";
|
||||||
|
} else if (mode == TINYGLTF_MODE_LINE) {
|
||||||
|
return "LINE";
|
||||||
|
} else if (mode == TINYGLTF_MODE_LINE_LOOP) {
|
||||||
|
return "LINE_LOOP";
|
||||||
|
} else if (mode == TINYGLTF_MODE_TRIANGLES) {
|
||||||
|
return "TRIANGLES";
|
||||||
|
} else if (mode == TINYGLTF_MODE_TRIANGLE_FAN) {
|
||||||
|
return "TRIANGLE_FAN";
|
||||||
|
} else if (mode == TINYGLTF_MODE_TRIANGLE_STRIP) {
|
||||||
|
return "TRIANGLE_STRIP";
|
||||||
|
}
|
||||||
|
return "**UNKNOWN**";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string PrintTarget(int target) {
|
||||||
|
if (target == 34962) {
|
||||||
|
return "GL_ARRAY_BUFFER";
|
||||||
|
} else if (target == 34963) {
|
||||||
|
return "GL_ELEMENT_ARRAY_BUFFER";
|
||||||
|
} else {
|
||||||
|
return "**UNKNOWN**";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string PrintType(int ty) {
|
||||||
|
if (ty == TINYGLTF_TYPE_SCALAR) {
|
||||||
|
return "SCALAR";
|
||||||
|
} else if (ty == TINYGLTF_TYPE_VECTOR) {
|
||||||
|
return "VECTOR";
|
||||||
|
} else if (ty == TINYGLTF_TYPE_VEC2) {
|
||||||
|
return "VEC2";
|
||||||
|
} else if (ty == TINYGLTF_TYPE_VEC3) {
|
||||||
|
return "VEC3";
|
||||||
|
} else if (ty == TINYGLTF_TYPE_VEC4) {
|
||||||
|
return "VEC4";
|
||||||
|
} else if (ty == TINYGLTF_TYPE_MATRIX) {
|
||||||
|
return "MATRIX";
|
||||||
|
} else if (ty == TINYGLTF_TYPE_MAT2) {
|
||||||
|
return "MAT2";
|
||||||
|
} else if (ty == TINYGLTF_TYPE_MAT3) {
|
||||||
|
return "MAT3";
|
||||||
|
} else if (ty == TINYGLTF_TYPE_MAT4) {
|
||||||
|
return "MAT4";
|
||||||
|
}
|
||||||
|
return "**UNKNOWN**";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string PrintComponentType(int ty) {
|
||||||
|
if (ty == TINYGLTF_COMPONENT_TYPE_BYTE) {
|
||||||
|
return "BYTE";
|
||||||
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
||||||
|
return "UNSIGNED_BYTE";
|
||||||
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
||||||
|
return "SHORT";
|
||||||
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
||||||
|
return "UNSIGNED_SHORT";
|
||||||
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_INT) {
|
||||||
|
return "INT";
|
||||||
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
|
||||||
|
return "UNSIGNED_INT";
|
||||||
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
||||||
|
return "FLOAT";
|
||||||
|
} else if (ty == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
|
||||||
|
return "DOUBLE";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "**UNKNOWN**";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// TODO(syoyo): Support sparse accessor(sparse vertex attribute).
|
||||||
|
// TODO(syoyo): Support more data type
|
||||||
|
struct VertexAttrib {
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
// Value are converted to float type.
|
||||||
|
std::vector<float> data;
|
||||||
|
|
||||||
|
// Corresponding info in binary data
|
||||||
|
int data_type; // e.g. TINYGLTF_TYPE_VEC2
|
||||||
|
int component_type; // storage type. e.g.
|
||||||
|
// TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
|
||||||
|
uint64_t buffer_offset{0};
|
||||||
|
size_t buffer_length{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MeshPrim {
|
||||||
|
std::string name;
|
||||||
|
int32_t id{-1};
|
||||||
|
|
||||||
|
int mode{TINYGLTF_MODE_TRIANGLES};
|
||||||
|
|
||||||
|
VertexAttrib position; // vec3
|
||||||
|
VertexAttrib normal; // vec3
|
||||||
|
VertexAttrib tangent; // vec4
|
||||||
|
VertexAttrib color; // vec3 or vec4
|
||||||
|
std::map<int, VertexAttrib> texcoords; // <slot, attrib> vec2
|
||||||
|
std::map<int, VertexAttrib> weights; // <slot, attrib>
|
||||||
|
std::map<int, VertexAttrib>
|
||||||
|
joints; // <slot, attrib> store data as float type
|
||||||
|
|
||||||
|
int indices_type{-1}; // storage type(componentType) of `indices`.
|
||||||
|
std::vector<uint32_t> indices; // vertex indices
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static std::string GetFilePathExtension(const std::string &FileName) {
|
||||||
|
if (FileName.find_last_of(".") != std::string::npos)
|
||||||
|
return FileName.substr(FileName.find_last_of(".") + 1);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ComponentTypeByteSize(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_BYTE:
|
||||||
|
return sizeof(char);
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_SHORT:
|
||||||
|
return sizeof(short);
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_INT:
|
||||||
|
return sizeof(int);
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_FLOAT:
|
||||||
|
return sizeof(float);
|
||||||
|
case TINYGLTF_COMPONENT_TYPE_DOUBLE:
|
||||||
|
return sizeof(double);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> LoadBin(const std::string &filename) {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
|
||||||
|
std::ifstream is(filename, std::ios::binary | std::ios::in | std::ios::ate);
|
||||||
|
|
||||||
|
if (is.is_open()) {
|
||||||
|
size_t size = size_t(is.tellg());
|
||||||
|
is.seekg(0, std::ios::beg);
|
||||||
|
if (size < 4) {
|
||||||
|
std::cerr << "File size is zero or too short: " << size << "\n";
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.resize(size);
|
||||||
|
is.read(reinterpret_cast<char *>(data.data()), std::streamsize(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(syoyo): Use C++17 like filesystem library
|
||||||
|
|
||||||
|
bool FileExists(const std::string &abs_filename) {
|
||||||
|
bool ret;
|
||||||
|
#ifdef _WIN32
|
||||||
|
// TODO(syoyo): Support utf8 filepath
|
||||||
|
FILE *fp = nullptr;
|
||||||
|
errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
|
||||||
|
if (err != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
FILE *fp = fopen(abs_filename.c_str(), "rb");
|
||||||
|
#endif
|
||||||
|
if (fp) {
|
||||||
|
ret = true;
|
||||||
|
fclose(fp);
|
||||||
|
} else {
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string JoinPath(const std::string &path0,
|
||||||
|
const std::string &path1) {
|
||||||
|
if (path0.empty()) {
|
||||||
|
return path1;
|
||||||
|
} else {
|
||||||
|
// check '/'
|
||||||
|
char lastChar = *path0.rbegin();
|
||||||
|
if (lastChar != '/') {
|
||||||
|
return path0 + std::string("/") + path1;
|
||||||
|
} else {
|
||||||
|
return path0 + path1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ExpandFilePath(const std::string &filepath) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
|
||||||
|
char *str = new char[len];
|
||||||
|
ExpandEnvironmentStringsA(filepath.c_str(), str, len);
|
||||||
|
|
||||||
|
std::string s(str);
|
||||||
|
|
||||||
|
delete[] str;
|
||||||
|
|
||||||
|
return s;
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
|
||||||
|
defined(__ANDROID__) || defined(__EMSCRIPTEN__)
|
||||||
|
// no expansion
|
||||||
|
std::string s = filepath;
|
||||||
|
#else
|
||||||
|
std::string s;
|
||||||
|
wordexp_t p;
|
||||||
|
|
||||||
|
if (filepath.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quote the string to keep any spaces in filepath intact.
|
||||||
|
std::string quoted_path = "\"" + filepath + "\"";
|
||||||
|
// char** w;
|
||||||
|
int ret = wordexp(quoted_path.c_str(), &p, 0);
|
||||||
|
if (ret) {
|
||||||
|
// err
|
||||||
|
s = filepath;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use first element only.
|
||||||
|
if (p.we_wordv) {
|
||||||
|
s = std::string(p.we_wordv[0]);
|
||||||
|
wordfree(&p);
|
||||||
|
} else {
|
||||||
|
s = filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return s;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string FindFile(const std::vector<std::string> &paths,
|
||||||
|
const std::string &filepath) {
|
||||||
|
for (size_t i = 0; i < paths.size(); i++) {
|
||||||
|
std::string absPath = ExpandFilePath(JoinPath(paths[i], filepath));
|
||||||
|
if (FileExists(absPath)) {
|
||||||
|
return absPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetBaseDir(const std::string &filepath) {
|
||||||
|
if (filepath.find_last_of("/\\") != std::string::npos)
|
||||||
|
return filepath.substr(0, filepath.find_last_of("/\\"));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetSlotId(const std::string &name) {
|
||||||
|
if (name.rfind("TEXCOORD_", 0) == 0) {
|
||||||
|
int id = 0;
|
||||||
|
sscanf(name.c_str(), "TEXCOORD_%d", &id);
|
||||||
|
return id;
|
||||||
|
} else if (name.rfind("WEIGHTS_", 0) == 0) {
|
||||||
|
int id = 0;
|
||||||
|
sscanf(name.c_str(), "WEIGHTS_%d", &id);
|
||||||
|
return id;
|
||||||
|
} else if (name.rfind("JOINTS_", 0) == 0) {
|
||||||
|
int id = 0;
|
||||||
|
sscanf(name.c_str(), "JOINTS_%d", &id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsAttributeSupported(const std::string &name) {
|
||||||
|
constexpr int max_slots = 8;
|
||||||
|
|
||||||
|
if (name.compare("POSITION") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.compare("NORMAL") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.compare("TANGENT") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < max_slots; i++) {
|
||||||
|
std::string n = "TEXCOORD_" + std::to_string(i);
|
||||||
|
if (n.compare(name) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < max_slots; i++) {
|
||||||
|
std::string n = "WEIGHTS_" + std::to_string(i);
|
||||||
|
if (n.compare(name) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < max_slots; i++) {
|
||||||
|
std::string n = "JOINTS_" + std::to_string(i);
|
||||||
|
if (n.compare(name) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float Unpack(const unsigned char *ptr, int type) {
|
||||||
|
if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
||||||
|
unsigned char data = *ptr;
|
||||||
|
return float(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_BYTE) {
|
||||||
|
char data = static_cast<char>(*ptr);
|
||||||
|
return float(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
||||||
|
uint16_t data = *reinterpret_cast<const uint16_t *>(ptr);
|
||||||
|
return float(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
||||||
|
int16_t data = *reinterpret_cast<const int16_t *>(ptr);
|
||||||
|
return float(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_FLOAT) {
|
||||||
|
float data = *reinterpret_cast<const float *>(ptr);
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
std::cerr << "???: Unsupported type: " << PrintComponentType(type) << "\n";
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t UnpackIndex(const unsigned char *ptr, int type) {
|
||||||
|
if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
|
||||||
|
unsigned char data = *ptr;
|
||||||
|
return uint32_t(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_BYTE) {
|
||||||
|
char data = static_cast<char>(*ptr);
|
||||||
|
return uint32_t(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
|
||||||
|
uint16_t data = *reinterpret_cast<const uint16_t *>(ptr);
|
||||||
|
return uint32_t(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
||||||
|
int16_t data = *reinterpret_cast<const int16_t *>(ptr);
|
||||||
|
return uint32_t(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_INT) {
|
||||||
|
// TODO(syoyo): Check overflow(2G+ index)
|
||||||
|
int32_t data = *reinterpret_cast<const int32_t *>(ptr);
|
||||||
|
return uint32_t(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
|
||||||
|
uint32_t data = *reinterpret_cast<const uint32_t *>(ptr);
|
||||||
|
return uint32_t(data);
|
||||||
|
} else if (type == TINYGLTF_COMPONENT_TYPE_SHORT) {
|
||||||
|
uint32_t data = *reinterpret_cast<const uint32_t *>(ptr);
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
std::cerr << "???: Unsupported type: " << PrintComponentType(type) << "\n";
|
||||||
|
return static_cast<uint32_t>(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DumpMesh(const tinygltf::Model &model, const tinygltf::Mesh &mesh,
|
||||||
|
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];
|
||||||
|
|
||||||
|
if (primitive.indices < 0) {
|
||||||
|
std::cerr << "Primitive indices must be provided\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
example::PrimSet out_prim;
|
||||||
|
|
||||||
|
// indices.
|
||||||
|
{
|
||||||
|
const tinygltf::Accessor &indexAccessor =
|
||||||
|
model.accessors[size_t(primitive.indices)];
|
||||||
|
|
||||||
|
size_t num_elements = indexAccessor.count;
|
||||||
|
std::cout << "index.elements = " << num_elements << "\n";
|
||||||
|
|
||||||
|
size_t byte_stride = ComponentTypeByteSize(indexAccessor.componentType);
|
||||||
|
|
||||||
|
const tinygltf::BufferView &indexBufferView =
|
||||||
|
model.bufferViews[size_t(indexAccessor.bufferView)];
|
||||||
|
|
||||||
|
// should be 34963(ELEMENT_ARRAY_BUFFER)
|
||||||
|
std::cout << "index.target = " << PrintTarget(indexBufferView.target)
|
||||||
|
<< "\n";
|
||||||
|
if (indexBufferView.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
|
||||||
|
std::cerr << "indexBufferView.target must be ELEMENT_ARRAY_BUFFER\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tinygltf::Buffer &indexBuffer =
|
||||||
|
model.buffers[size_t(indexBufferView.buffer)];
|
||||||
|
|
||||||
|
std::vector<uint32_t> indices;
|
||||||
|
|
||||||
|
for (size_t k = 0; k < num_elements; k++) {
|
||||||
|
// TODO(syoyo): out-of-bounds check.
|
||||||
|
const unsigned char *ptr = indexBuffer.data.data() +
|
||||||
|
indexBufferView.byteOffset +
|
||||||
|
(k * byte_stride) + indexAccessor.byteOffset;
|
||||||
|
|
||||||
|
uint32_t idx = UnpackIndex(ptr, indexAccessor.componentType);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
std::cout << "vertex_index[" << k << "] = " << idx << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
indices.push_back(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
out_prim.indices = indices;
|
||||||
|
out_prim.indices_type = indexAccessor.componentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributes
|
||||||
|
{
|
||||||
|
std::map<std::string, int>::const_iterator it(
|
||||||
|
primitive.attributes.begin());
|
||||||
|
std::map<std::string, int>::const_iterator itEnd(
|
||||||
|
primitive.attributes.end());
|
||||||
|
|
||||||
|
for (; it != itEnd; it++) {
|
||||||
|
// it->first would be "POSITION", "NORMAL", "TEXCOORD_0", ...
|
||||||
|
if (!IsAttributeSupported(it->first)) {
|
||||||
|
std::cout << "Unsupported attribute: " << it->first << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size_t(it->second) >= model.accessors.size()) {
|
||||||
|
std::cerr << "Invalid accessor id: " << it->second << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tinygltf::Accessor &accessor =
|
||||||
|
model.accessors[size_t(it->second)];
|
||||||
|
|
||||||
|
size_t elem_size = 1;
|
||||||
|
if (accessor.type == TINYGLTF_TYPE_SCALAR) {
|
||||||
|
elem_size = 1;
|
||||||
|
} else if (accessor.type == TINYGLTF_TYPE_VEC2) {
|
||||||
|
elem_size = 2;
|
||||||
|
} else if (accessor.type == TINYGLTF_TYPE_VEC3) {
|
||||||
|
elem_size = 3;
|
||||||
|
} else if (accessor.type == TINYGLTF_TYPE_VEC4) {
|
||||||
|
elem_size = 4;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Invalid or unsupported accessor type: "
|
||||||
|
<< PrintType(accessor.type) << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << PrintComponentType(accessor.componentType) << "\n";
|
||||||
|
|
||||||
|
size_t byte_stride = ComponentTypeByteSize(accessor.componentType);
|
||||||
|
|
||||||
|
std::cout << "attribute: " << it->first << "\n";
|
||||||
|
// if (gGLProgramState.attribs[it->first] >= 0) {
|
||||||
|
// Compute byteStride from Accessor + BufferView combination.
|
||||||
|
int byteStride =
|
||||||
|
accessor.ByteStride(model.bufferViews[size_t(accessor.bufferView)]);
|
||||||
|
assert(byteStride != -1);
|
||||||
|
|
||||||
|
std::cout << "byteOffset: " << accessor.byteOffset << "\n";
|
||||||
|
|
||||||
|
const tinygltf::BufferView &bufferView =
|
||||||
|
model.bufferViews[size_t(accessor.bufferView)];
|
||||||
|
const tinygltf::Buffer &buffer =
|
||||||
|
model.buffers[size_t(bufferView.buffer)];
|
||||||
|
|
||||||
|
size_t num_elems = accessor.count * elem_size;
|
||||||
|
|
||||||
|
example::VertexAttrib attrib;
|
||||||
|
for (size_t k = 0; k < num_elems; k++) {
|
||||||
|
// TODO(syoyo): out-of-bounds check.
|
||||||
|
const unsigned char *ptr = buffer.data.data() +
|
||||||
|
bufferView.byteOffset + (k * byte_stride) +
|
||||||
|
accessor.byteOffset;
|
||||||
|
float value = Unpack(ptr, accessor.componentType);
|
||||||
|
if (verbose) {
|
||||||
|
std::cout << "[" << k << "] value = " << value << "\n";
|
||||||
|
}
|
||||||
|
attrib.data.push_back(value);
|
||||||
|
}
|
||||||
|
attrib.component_type = accessor.componentType;
|
||||||
|
attrib.data_type = accessor.type;
|
||||||
|
attrib.name = it->first;
|
||||||
|
|
||||||
|
if (attrib.name.compare("POSITION") == 0) {
|
||||||
|
out_prim.position = attrib;
|
||||||
|
} else if (attrib.name.compare("NORMAL") == 0) {
|
||||||
|
out_prim.normal = attrib;
|
||||||
|
} else if (attrib.name.compare("TANGENT") == 0) {
|
||||||
|
out_prim.tangent = attrib;
|
||||||
|
} else if (attrib.name.rfind("TEXCOORD_", 0) == 0) {
|
||||||
|
int id = GetSlotId(attrib.name);
|
||||||
|
std::cout << "texcoord[" << id << "]\n";
|
||||||
|
out_prim.texcoords[id] = attrib;
|
||||||
|
} else if (attrib.name.rfind("JOINTS_", 0) == 0) {
|
||||||
|
int id = GetSlotId(attrib.name);
|
||||||
|
std::cout << "joints[" << id << "]\n";
|
||||||
|
out_prim.joints[id] = attrib;
|
||||||
|
} else if (attrib.name.rfind("WEIGHTS_", 0) == 0) {
|
||||||
|
int id = GetSlotId(attrib.name);
|
||||||
|
std::cout << "weights[" << id << "]\n";
|
||||||
|
out_prim.weights[id] = attrib;
|
||||||
|
} else {
|
||||||
|
std::cerr << "???: attrib.name = " << attrib.name << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tinygltf::Accessor &indexAccessor =
|
||||||
|
model.accessors[size_t(primitive.indices)];
|
||||||
|
(void)indexAccessor;
|
||||||
|
PrintMode(primitive.mode);
|
||||||
|
if (primitive.mode != TINYGLTF_MODE_TRIANGLES) {
|
||||||
|
std::cerr << "Supported Primitive mode is TRIANGLES only at the moment\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
bool verbose, std::vector<example::MeshPrim> *outs) {
|
||||||
|
// Get .bin data
|
||||||
|
{
|
||||||
|
if (model.buffers.size() != 1) {
|
||||||
|
std::cerr << "Single element of `buffers` is supported at the moment.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tinygltf::Buffer &buffer = model.buffers[0];
|
||||||
|
if (buffer.uri.empty()) {
|
||||||
|
std::cerr << "buffer.uri must be a filepath.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.data.size() < 4) {
|
||||||
|
std::cerr << "Invalid buffer.byteLength.\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> search_paths;
|
||||||
|
search_paths.push_back(asset_path);
|
||||||
|
|
||||||
|
std::string abs_filepath = FindFile(search_paths, 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 << " Searched absolute file path: " << abs_filepath << "\n";
|
||||||
|
std::cerr << " .bin size = " << bin.size()
|
||||||
|
<< ", size in 'buffer.uri' = " << buffer.data.size() << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &mesh : model.meshes) {
|
||||||
|
std::cout << "mesh.name: " << mesh.name << "\n";
|
||||||
|
|
||||||
|
example::MeshPrim output;
|
||||||
|
bool ret = DumpMesh(model, mesh, verbose, &output);
|
||||||
|
if (!ret) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outs->push_back(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == "gltf2obj") {
|
||||||
|
tinygltf::Model model;
|
||||||
|
tinygltf::TinyGLTF loader;
|
||||||
|
std::string err;
|
||||||
|
std::string warn;
|
||||||
|
|
||||||
|
std::string ext = GetFilePathExtension(input_filename);
|
||||||
|
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (ext.compare("glb") == 0) {
|
||||||
|
// assume binary glTF.
|
||||||
|
ret = loader.LoadBinaryFromFile(&model, &err, &warn,
|
||||||
|
input_filename.c_str());
|
||||||
|
} else {
|
||||||
|
// assume ascii glTF.
|
||||||
|
ret = loader.LoadASCIIFromFile(&model, &err, &warn,
|
||||||
|
input_filename.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!warn.empty()) {
|
||||||
|
printf("Warn: %s\n", warn.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!err.empty()) {
|
||||||
|
printf("ERR: %s\n", err.c_str());
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
printf("Failed to load .glTF : %s\n", argv[1]);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
json j;
|
||||||
|
{
|
||||||
|
std::ifstream i(input_filename);
|
||||||
|
i >> j;
|
||||||
|
}
|
||||||
|
std::cout << "j = " << j << "\n";
|
||||||
|
|
||||||
|
json j_patch = R"([
|
||||||
|
{ "op": "add", "path": "/buffers/-", "value": {
|
||||||
|
"name": "plane/data",
|
||||||
|
"byteLength": 480,
|
||||||
|
"uri": "plane1.bin"
|
||||||
|
} }
|
||||||
|
])"_json;
|
||||||
|
|
||||||
|
// a JSON value
|
||||||
|
json j_original = R"({
|
||||||
|
"baz": ["one", "two", "three"],
|
||||||
|
"foo": "bar"
|
||||||
|
})"_json;
|
||||||
|
|
||||||
|
//json j_patch = R"([
|
||||||
|
// { "op": "remove", "path": "/buffers" }
|
||||||
|
//])"_json;
|
||||||
|
|
||||||
|
std::cout << "patch = " << j_patch.dump(2) << "\n";
|
||||||
|
|
||||||
|
json j_ret = j.patch(j_patch);
|
||||||
|
std::cout << "patched = " << j_ret.dump(2) << "\n";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string basedir = GetBaseDir(input_filename);
|
||||||
|
std::vector<example::MeshPrim> 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 basename;
|
||||||
|
if (mesh.name.empty()) {
|
||||||
|
basename = "untitled-" + std::to_string(n);
|
||||||
|
} else {
|
||||||
|
basename = mesh.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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") {
|
||||||
|
// 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.
|
||||||
|
bool facevarying = false;
|
||||||
|
|
||||||
|
example::MeshPrim mesh;
|
||||||
|
bool ok = example::LoadObjMesh(input_filename, facevarying, &mesh);
|
||||||
|
if (!ok) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
PrintMeshPrim(mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = example::SaveAsGLTFMesh(output_filename, mesh);
|
||||||
|
if (!ok) {
|
||||||
|
std::cerr << "Failed to save mesh as glTF\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
std::cout << "Write glTF: " << output_filename << "\n";
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unknown operation: " << op << "\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
1479
examples/mesh-conv/mesh-util.cc
Normal file
1479
examples/mesh-conv/mesh-util.cc
Normal file
File diff suppressed because it is too large
Load Diff
95
examples/mesh-conv/mesh-util.hh
Normal file
95
examples/mesh-conv/mesh-util.hh
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace example {
|
||||||
|
|
||||||
|
// TODO(syoyo): Support sparse accessor(sparse vertex attribute).
|
||||||
|
// TODO(syoyo): Support more data type
|
||||||
|
struct VertexAttrib {
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
// Value are converted to float type.
|
||||||
|
std::vector<float> data;
|
||||||
|
|
||||||
|
// Corresponding info in binary data
|
||||||
|
int data_type; // e.g. TINYGLTF_TYPE_VEC2
|
||||||
|
int component_type; // storage type. e.g.
|
||||||
|
// TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
|
||||||
|
uint64_t buffer_offset{0};
|
||||||
|
size_t buffer_length{0};
|
||||||
|
|
||||||
|
// Optional.
|
||||||
|
std::vector<double> minValues;
|
||||||
|
std::vector<double> maxValues;
|
||||||
|
|
||||||
|
size_t numElements() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PrimSet {
|
||||||
|
|
||||||
|
int mode; // e.g. TRIANGLES
|
||||||
|
|
||||||
|
VertexAttrib position; // vec3
|
||||||
|
VertexAttrib normal; // vec3
|
||||||
|
VertexAttrib tangent; // vec4
|
||||||
|
VertexAttrib color; // vec3 or vec4
|
||||||
|
std::map<int, VertexAttrib> texcoords; // <slot, attrib> vec2
|
||||||
|
std::map<int, VertexAttrib> weights; // <slot, attrib> vec4
|
||||||
|
std::map<int, VertexAttrib>
|
||||||
|
joints; // <slot, attrib> store data as float type
|
||||||
|
|
||||||
|
|
||||||
|
// min/max of index value. -1 = undef
|
||||||
|
int indices_min{-1};
|
||||||
|
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
|
||||||
|
///
|
||||||
|
/// @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);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Loads .obj and convert to MeshPrim
|
||||||
|
///
|
||||||
|
/// @param[in] facevarying Construct mesh with facevarying vertex layout(default false)
|
||||||
|
///
|
||||||
|
bool LoadObjMesh(const std::string &filename, bool facevarying, MeshPrim *mesh);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Print MeshPrim datra
|
||||||
|
///
|
||||||
|
void PrintMeshPrim(const MeshPrim &mesh);
|
||||||
|
|
||||||
|
} // namespace example
|
||||||
7
examples/mesh-conv/meson.build
Normal file
7
examples/mesh-conv/meson.build
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
project('mesh-conv', 'cpp', default_options : ['cpp_std=c++11'])
|
||||||
|
|
||||||
|
thread_dep = dependency('threads')
|
||||||
|
|
||||||
|
incdir = include_directories(['../../', '../common'])
|
||||||
|
|
||||||
|
executable('mesh-conv', ['mesh-conv.cc', 'mesh-util.cc', 'tinygltf_impl.cc'], include_directories : incdir, dependencies : thread_dep)
|
||||||
27
examples/mesh-conv/sandbox/README.md
Normal file
27
examples/mesh-conv/sandbox/README.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# 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).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
concatenate sourceN.gltf to target.gltf and save it to merged.gltf
|
||||||
|
|
||||||
|
```
|
||||||
|
$ python concat_mesh.py source0.gltf <source1.gltf source2.gltf ...> target.gltf merged.gltf
|
||||||
|
```
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* [x] Support multiple glTFs to merge(concat)
|
||||||
|
* [ ] Support merging skin
|
||||||
|
* [ ] Support merging different node hierarchies
|
||||||
|
* [x] Support `images`, `textures`, `materials`, `samplers`
|
||||||
|
* [ ] Support other glTF info
|
||||||
|
|
||||||
|
# replace_attrib.py
|
||||||
|
|
||||||
|
Replace the accessor id of specified attribute.
|
||||||
272
examples/mesh-conv/sandbox/concat_mesh.py
Normal file
272
examples/mesh-conv/sandbox/concat_mesh.py
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
# concat mesh to glTF
|
||||||
|
# support multiple source glTF to me concatenated to target.gltf
|
||||||
|
|
||||||
|
# TODO: skins, nodes, scenes
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
g_prefix = "added/"
|
||||||
|
|
||||||
|
def adjustMaterialTextureIndex(mat, texture_index_offset):
|
||||||
|
# Args: mat(dict)
|
||||||
|
# texture_index_offset(int) Index offset
|
||||||
|
|
||||||
|
if "normalTexture" in mat:
|
||||||
|
if "index" in mat["normalTexture"]:
|
||||||
|
mat["normalTexture"]["index"] += texture_index_offset
|
||||||
|
|
||||||
|
if "occlusionTexture" in mat:
|
||||||
|
if "index" in mat["occlusionTexture"]:
|
||||||
|
mat["occlusionTexture"]["index"] += texture_index_offset
|
||||||
|
|
||||||
|
if "emissiveTexture" in mat:
|
||||||
|
if "index" in mat["emissiveTexture"]:
|
||||||
|
mat["emissiveTexture"]["index"] += texture_index_offset
|
||||||
|
|
||||||
|
if "pbrMetallicRoughness" in mat:
|
||||||
|
if "baseColorTexture" in mat["pbrMetallicRoughness"]:
|
||||||
|
if "index" in mat["pbrMetallicRoughness"]["baseColorTexture"]:
|
||||||
|
mat["pbrMetallicRoughness"]["baseColorTexture"]["index"] += texture_index_offset
|
||||||
|
|
||||||
|
if "metallicRoughnessTexture" in mat["pbrMetallicRoughness"]:
|
||||||
|
if "index" in mat["pbrMetallicRoughness"]["metallicRoughnessTexture"]:
|
||||||
|
mat["pbrMetallicRoughness"]["metallicRoughnessTexture"]["index"] += texture_index_offset
|
||||||
|
|
||||||
|
return mat
|
||||||
|
|
||||||
|
|
||||||
|
def concat(source, target, offsets, prefix, extraInfo):
|
||||||
|
# Args:
|
||||||
|
# source: dict(modified), target: dict(modified),
|
||||||
|
# offsets: dict(modified). contains offset table.
|
||||||
|
# prefix: str
|
||||||
|
# extraInfo: dict(inout)
|
||||||
|
|
||||||
|
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
|
||||||
|
num_source_samplers = len(source["samplers"]) if "samplers" in source else 0
|
||||||
|
num_source_images = len(source["images"]) if "images" in source else 0
|
||||||
|
num_source_textures = len(source["textures"]) if "textures" in source else 0
|
||||||
|
print("src =====")
|
||||||
|
print("num_src_meshes: ", num_source_meshes)
|
||||||
|
print("num_src_buffers: ", num_source_buffers)
|
||||||
|
print("num_src_bufferViews: ", num_source_bufferViews)
|
||||||
|
print("num_src_accessors: ", num_source_accessors)
|
||||||
|
print("num_src_materials: ", num_source_materials)
|
||||||
|
print("num_src_textures: ", num_source_textures)
|
||||||
|
print("num_src_images: ", num_source_images)
|
||||||
|
print("num_src_samplers: ", num_source_samplers)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Modify name and adjust index offset
|
||||||
|
#
|
||||||
|
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"] += offsets["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"] += offsets["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] += offsets["accessors"]
|
||||||
|
|
||||||
|
|
||||||
|
source["meshes"][i]["primitives"][primid]["indices"] += offsets["accessors"]
|
||||||
|
if "material" in source["meshes"][i]["primitives"][primid]:
|
||||||
|
source["meshes"][i]["primitives"][primid]["material"] += offsets["materials"]
|
||||||
|
|
||||||
|
|
||||||
|
if "materials" in source:
|
||||||
|
for i in range(len(source["materials"])):
|
||||||
|
if "name" in source["materials"][i]:
|
||||||
|
source["materials"][i]["name"] = prefix + source["materials"][i]["name"]
|
||||||
|
source["materials"][i] = adjustMaterialTextureIndex(source["materials"][i], offsets["textures"])
|
||||||
|
|
||||||
|
if "images" in source:
|
||||||
|
for i in range(len(source["images"])):
|
||||||
|
if "name" in source["images"][i]:
|
||||||
|
source["images"][i]["name"] = prefix + source["images"][i]["name"]
|
||||||
|
|
||||||
|
if "samplers" in source:
|
||||||
|
for i in range(len(source["samplers"])):
|
||||||
|
if "name" in source["samplers"][i]:
|
||||||
|
source["samplers"][i]["name"] = prefix + source["samplers"][i]["name"]
|
||||||
|
|
||||||
|
if "textures" in source:
|
||||||
|
for i in range(len(source["textures"])):
|
||||||
|
if "name" in source["textures"][i]:
|
||||||
|
source["textures"][i]["name"] = prefix + source["textures"][i]["name"]
|
||||||
|
source["textures"][i]["sampler"] += offsets["samplers"]
|
||||||
|
source["textures"][i]["source"] += offsets["images"]
|
||||||
|
|
||||||
|
#
|
||||||
|
# Append mesh info
|
||||||
|
#
|
||||||
|
target["buffers"] += source["buffers"]
|
||||||
|
target["bufferViews"] += source["bufferViews"]
|
||||||
|
target["meshes"] += source["meshes"]
|
||||||
|
target["accessors"] += source["accessors"]
|
||||||
|
if "materials" in source:
|
||||||
|
if "materials" in target:
|
||||||
|
target["materials"] += source["materials"]
|
||||||
|
else:
|
||||||
|
target["materials"] = source["materials"]
|
||||||
|
if "images" in source:
|
||||||
|
if "images" in target:
|
||||||
|
target["images"] += source["images"]
|
||||||
|
else:
|
||||||
|
target["images"] = source["images"]
|
||||||
|
if "textures" in source:
|
||||||
|
if "textures" in target:
|
||||||
|
target["textures"] += source["textures"]
|
||||||
|
else:
|
||||||
|
target["textures"] = source["textures"]
|
||||||
|
if "samplers" in source:
|
||||||
|
if "samplers" in target:
|
||||||
|
target["samplers"] += source["samplers"]
|
||||||
|
else:
|
||||||
|
target["samplers"] = source["samplers"]
|
||||||
|
|
||||||
|
# assume `prefix` is unique
|
||||||
|
extraInfo["num_{}_meshes".format(prefix)] = num_source_meshes
|
||||||
|
extraInfo["num_{}_buffers".format(prefix)] = num_source_buffers
|
||||||
|
extraInfo["num_{}_bufferViews".format(prefix)] = num_source_bufferViews
|
||||||
|
extraInfo["num_{}_accessors".format(prefix)] = num_source_accessors
|
||||||
|
extraInfo["num_{}_materials".format(prefix)] = num_source_materials
|
||||||
|
extraInfo["num_{}_images".format(prefix)] = num_source_images
|
||||||
|
extraInfo["num_{}_samplers".format(prefix)] = num_source_samplers
|
||||||
|
extraInfo["num_{}_textures".format(prefix)] = num_source_textures
|
||||||
|
|
||||||
|
# update offsets
|
||||||
|
offsets["meshes"] += num_source_meshes
|
||||||
|
offsets["buffers"] += num_source_buffers
|
||||||
|
offsets["bufferViews"] += num_source_bufferViews
|
||||||
|
offsets["accessors"] += num_source_accessors
|
||||||
|
offsets["materials"] += num_source_materials
|
||||||
|
offsets["textures"] += num_source_textures
|
||||||
|
offsets["images"] += num_source_images
|
||||||
|
offsets["samplers"] += num_source_samplers
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 4:
|
||||||
|
print("Needs source0.gltf <source1.gltf, ...> target.gltf output.gltf")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
num_args = len(sys.argv)
|
||||||
|
print("num_args = ", num_args)
|
||||||
|
|
||||||
|
source_filenames = []
|
||||||
|
|
||||||
|
num_srcs = num_args - 3
|
||||||
|
|
||||||
|
for i in range(num_srcs):
|
||||||
|
source_filenames.append(sys.argv[1+i])
|
||||||
|
print("source[{}] = {}".format(i, sys.argv[1+i]))
|
||||||
|
|
||||||
|
target_filename = sys.argv[num_args - 2]
|
||||||
|
output_filename = sys.argv[num_args - 1]
|
||||||
|
print("target = ", target_filename)
|
||||||
|
print("output = ", output_filename)
|
||||||
|
|
||||||
|
sources = []
|
||||||
|
for i in range(num_srcs):
|
||||||
|
sources.append(json.loads(open(source_filenames[i]).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 = 0
|
||||||
|
if "materials" in target:
|
||||||
|
num_target_materials = len(target["materials"])
|
||||||
|
|
||||||
|
num_target_images = 0
|
||||||
|
if "images" in target:
|
||||||
|
num_target_images = len(target["images"])
|
||||||
|
|
||||||
|
num_target_samplers = 0
|
||||||
|
if "samplers" in target:
|
||||||
|
num_target_samplers = len(target["samplers"])
|
||||||
|
|
||||||
|
num_target_textures = 0
|
||||||
|
if "textures" in target:
|
||||||
|
num_target_textures = len(target["textures"])
|
||||||
|
|
||||||
|
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_materials)
|
||||||
|
print("num_target_textures: ", num_target_textures)
|
||||||
|
print("num_target_images: ", num_target_images)
|
||||||
|
print("num_target_samplers: ", num_target_samplers)
|
||||||
|
|
||||||
|
#
|
||||||
|
# add some info to asset.extras
|
||||||
|
#
|
||||||
|
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_target_textures"] = num_target_textures
|
||||||
|
extraInfo["num_target_images"] = num_target_images
|
||||||
|
extraInfo["num_target_samplers"] = num_target_samplers
|
||||||
|
|
||||||
|
offsets = {}
|
||||||
|
offsets["meshes"] = num_target_meshes
|
||||||
|
offsets["buffers"] = num_target_buffers
|
||||||
|
offsets["bufferViews"] = num_target_bufferViews
|
||||||
|
offsets["accessors"] = num_target_accessors
|
||||||
|
offsets["materials"] = num_target_materials
|
||||||
|
offsets["textures"] = num_target_textures
|
||||||
|
offsets["samplers"] = num_target_samplers
|
||||||
|
offsets["images"] = num_target_images
|
||||||
|
|
||||||
|
for i in range(num_srcs):
|
||||||
|
source = sources[i]
|
||||||
|
|
||||||
|
prefix = g_prefix + "{}/".format(source_filenames[i])
|
||||||
|
concat(source, target, offsets, prefix, extraInfo)
|
||||||
|
|
||||||
|
extraInfo["num_total_meshes"] = offsets["meshes"]
|
||||||
|
extraInfo["num_total_buffers"] = offsets["buffers"]
|
||||||
|
extraInfo["num_total_bufferViews"] = offsets["bufferViews"]
|
||||||
|
extraInfo["num_total_accessors"] = offsets["accessors"]
|
||||||
|
extraInfo["num_total_materials"] = offsets["materials"]
|
||||||
|
extraInfo["num_total_textures"] = offsets["textures"]
|
||||||
|
extraInfo["num_total_images"] = offsets["images"]
|
||||||
|
extraInfo["num_total_samplers"] = offsets["samplers"]
|
||||||
|
|
||||||
|
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()
|
||||||
107
examples/mesh-conv/sandbox/replace_attrib.py
Normal file
107
examples/mesh-conv/sandbox/replace_attrib.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
replace_indices = False
|
||||||
|
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]
|
||||||
|
|
||||||
|
if replace_indices:
|
||||||
|
target_indices_id = target_prim["indices"]
|
||||||
|
src_indices_id = source_prim["indices"]
|
||||||
|
|
||||||
|
print("Replace vertex indices from {} to {}".format(target_indices_id, src_indices_id))
|
||||||
|
gltf["meshes"][target_mesh_id]["primitives"][target_primid]["indices"] = gltf["meshes"][source_mesh_id]["primitives"][source_primid]["indices"]
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
9
examples/mesh-conv/tinygltf_impl.cc
Normal file
9
examples/mesh-conv/tinygltf_impl.cc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#define TINYGLTF_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "../../tiny_gltf.h"
|
||||||
|
#else
|
||||||
|
#include "tiny_gltf.h"
|
||||||
|
#endif
|
||||||
@@ -150,6 +150,7 @@ bool LoadObj(const std::string &filename, float scale,
|
|||||||
tinyobj::attrib_t attrib;
|
tinyobj::attrib_t attrib;
|
||||||
std::vector<tinyobj::shape_t> shapes;
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
std::vector<tinyobj::material_t> materials;
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
std::string warn;
|
||||||
std::string err;
|
std::string err;
|
||||||
|
|
||||||
std::string basedir = GetBaseDir(filename) + "/";
|
std::string basedir = GetBaseDir(filename) + "/";
|
||||||
@@ -158,12 +159,16 @@ bool LoadObj(const std::string &filename, float scale,
|
|||||||
// auto t_start = std::chrono::system_clock::now();
|
// auto t_start = std::chrono::system_clock::now();
|
||||||
|
|
||||||
bool ret =
|
bool ret =
|
||||||
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str(),
|
tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename.c_str(),
|
||||||
basepath, /* triangulate */ true);
|
basepath, /* triangulate */ true);
|
||||||
|
|
||||||
// auto t_end = std::chrono::system_clock::now();
|
// auto t_end = std::chrono::system_clock::now();
|
||||||
// std::chrono::duration<double, std::milli> ms = t_end - t_start;
|
// std::chrono::duration<double, std::milli> ms = t_end - t_start;
|
||||||
|
|
||||||
|
if (!warn.empty()) {
|
||||||
|
std::cout << warn << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
if (!err.empty()) {
|
if (!err.empty()) {
|
||||||
std::cerr << err << std::endl;
|
std::cerr << err << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user