Compare commits

..

14 Commits

Author SHA1 Message Date
Syoyo Fujita
fb99999c1a Support uint 2020-03-25 17:48:37 +09:00
Syoyo Fujita
be9ff66eeb Added support concatinating materials, images, textures and samplers. 2020-03-13 19:23:49 +09:00
Syoyo Fujita
0a4e8b761f Fix inequality. 2020-03-13 18:42:06 +09:00
Syoyo Fujita
4adeae9709 Skip counting the number of materials in target glTF if materials does not exist. 2020-03-13 15:08:42 +09:00
Syoyo Fujita
6224e021ce Support concatinating multiple glTF files. 2020-03-13 14:23:44 +09:00
Syoyo Fujita
5dcf778b31 Add feature to repace mesh.primitive.indices accessor. 2020-03-12 22:08:32 +09:00
Syoyo Fujita
289cc448ed Fix redundant printf 2020-03-12 21:57:41 +09:00
Syoyo Fujita
0ee2120965 Assign name for accessor.
Improve .obj export.
Use clipp to parse cmd options.
Add some handy python scripts.
2020-03-12 21:31:12 +09:00
Syoyo Fujita
cf30d42cbc Rename.
Add some missing files.
2020-03-11 22:12:55 +09:00
Syoyo Fujita
cb0a8794c3 Keep up with tinyobj's API update. 2020-03-11 19:30:57 +09:00
Syoyo Fujita
60e22f3281 Initial success to convert .obj to .glTF mesh. 2020-03-11 17:58:02 +09:00
Syoyo Fujita
4cfd2849fb Implement .obj to glTF like mesh data converter 2020-03-10 22:19:14 +09:00
Syoyo Fujita
a5416707c9 update tinyobjloader.
mesh-modify experiment.
2020-03-09 22:34:50 +09:00
Syoyo Fujita
8ec9af7f2e Initial mesh-dump example. 2020-03-08 22:13:39 +09:00
36 changed files with 11930 additions and 2963 deletions

View File

@@ -1 +0,0 @@
blank_issues_enabled: false

View File

@@ -1,30 +0,0 @@
---
name: Issue report
about: Create a report
title: ''
labels: ''
assignees: ''
---
**Describe the issue**
A clear and concise description of what the issue is.
**To Reproduce**
- OS
- Compiler, compiler version, compile options
- Please attach minimal and reproducible .glTF file if you got an issue related to .glTF data
**Expected behaviour**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

View File

@@ -1,165 +0,0 @@
name: C/C++ CI
on: [push, pull_request]
jobs:
# compile with older gcc4.8
build-gcc48:
runs-on: ubuntu-18.04
name: Build with gcc 4.8
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: |
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y gcc-4.8 g++-4.8
g++-4.8 -std=c++11 -o loader_example loader_example.cc
- name: NoexceptBuild
run: |
g++-4.8 -DTINYGLTF_NOEXCEPTION -std=c++11 -o loader_example loader_example.cc
- name: RapidjsonBuild
run: |
git clone https://github.com/Tencent/rapidjson
g++-4.8 -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -o loader_example loader_example.cc
# compile with mingw gcc cross
build-mingw-cross:
runs-on: ubuntu-18.04
name: Build with MinGW gcc cross
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: |
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y mingw-w64
x86_64-w64-mingw32-g++ -std=c++11 -o loader_example loader_example.cc
# Windows(x64) + Visual Studio 2019 build
build-windows-msvc:
runs-on: windows-latest
name: Build for Windows(x64, MSVC)
# Use system installed cmake
# https://help.github.com/en/actions/reference/software-installed-on-github-hosted-runners
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Configure
run: |
mkdir build
cd build
cmake -G "Visual Studio 16 2019" -DTINYGLTF_BUILD_LOADER_EXAMPLE=On -DTINYGLTF_BUILD_GL_EXAMPLES=Off -DTINYGLTF_BUILD_VALIDATOR_EXAMPLE=On ..
cd ..
- name: Build
run: cmake --build build --config Release
build-linux:
runs-on: ubuntu-latest
name: Buld with gcc
steps:
- uses: actions/checkout@v2
- name: build
run: |
g++ -std=c++11 -o loader_example loader_example.cc
- name: test
run: |
./loader_example models/Cube/Cube.gltf
- name: tests
run: |
cd tests
g++ -I../ -std=c++11 -g -O0 -o tester tester.cc
./tester
cd ..
- name: noexcept_tests
run: |
cd tests
g++ -DTINYGLTF_NOEXCEPTION -I../ -std=c++11 -g -O0 -o tester_noexcept tester.cc
./tester_noexcept
cd ..
build-rapidjson-linux:
runs-on: ubuntu-latest
name: Buld with gcc + rapidjson
steps:
- uses: actions/checkout@v2
- name: build
run: |
git clone https://github.com/Tencent/rapidjson
g++ -v
g++ -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -o loader_example loader_example.cc
- name: loader_example_test
run: |
./loader_example models/Cube/Cube.gltf
- name: tests
run: |
cd tests
g++ -DTINYGLTF_USE_RAPIDJSON -I../rapidjson/include/rapidjson -I../ -std=c++11 -g -O0 -o tester tester.cc
./tester
cd ..
- name: noexcept_tests
run: |
cd tests
g++ -DTINYGLTF_USE_RAPIDJSON -I../rapidjson/include/rapidjson -DTINYGLTF_NOEXCEPTION -I../ -std=c++11 -g -O0 -o tester_noexcept tester.cc
./tester_noexcept
cd ..
# Cross-compile for aarch64 linux target
build-cross-aarch64:
runs-on: ubuntu-18.04
name: Build on cross aarch64
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: |
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y gcc-8-aarch64-linux-gnu g++-8-aarch64-linux-gnu
git clone https://github.com/Tencent/rapidjson
aarch64-linux-gnu-g++-8 -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -g -O0 -o loader_example loader_example.cc
# macOS clang
build-macos:
runs-on: macos-latest
name: Build on macOS
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build
run: |
clang++ -std=c++11 -g -O0 -o loader_example loader_example.cc
./loader_example models/Cube/Cube.gltf
git clone https://github.com/Tencent/rapidjson
clang++ -DTINYGLTF_USE_RAPIDJSON -I./rapidjson/include/rapidjson -std=c++11 -g -O0 -o loader_example loader_example.cc

View File

@@ -4,24 +4,18 @@ PROJECT (tinygltf)
SET(CMAKE_CXX_STANDARD 11)
option(TINYGLTF_BUILD_LOADER_EXAMPLE "Build loader_example" ON)
option(TINYGLTF_BUILD_GL_EXAMPLES "Build GL exampels(requires glfw, OpenGL, etc)" OFF)
option(TINYGLTF_BUILD_VALIDATOR_EXAMPLE "Build validator exampe" OFF)
option(TINYGLTF_BUILD_EXAMPLES "Build examples" ON)
if (TINYGLTF_BUILD_LOADER_EXAMPLE)
if (TINYGLTF_BUILD_EXAMPLES)
ADD_EXECUTABLE ( loader_example
loader_example.cc
)
endif (TINYGLTF_BUILD_LOADER_EXAMPLE)
if (TINYGLTF_BUILD_GL_EXAMPLES)
ADD_SUBDIRECTORY ( examples/gltfutil )
ADD_SUBDIRECTORY ( examples/glview )
endif (TINYGLTF_BUILD_GL_EXAMPLES)
if (TINYGLTF_BUILD_VALIDATOR_EXAMPLE)
ADD_SUBDIRECTORY ( examples/validator )
endif (TINYGLTF_BUILD_VALIDATOR_EXAMPLE)
endif (TINYGLTF_BUILD_EXAMPLES)
#
# TinuGLTF is a header-only library, so no library build. just install header files.
#

View File

@@ -19,8 +19,6 @@ If you are looking for old, C++03 version, please use `devel-picojson` branch.
[![Build status](https://ci.appveyor.com/api/projects/status/warngenu9wjjhlm8?svg=true)](https://ci.appveyor.com/project/syoyo/tinygltf)
![C/C++ CI](https://github.com/syoyo/tinygltf/workflows/C/C++%20CI/badge.svg)
## Features
* Written in portable C++. C++-11 with STL dependency only.
@@ -73,6 +71,7 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* [glview](examples/glview) : Simple glTF geometry viewer.
* [validator](examples/validator) : Simple glTF validator with JSON schema.
* [basic](examples/basic) : Basic glTF viewer with texturing support.
* [mesh-conv](examples/mesh-conv) : Convert glTF mesh to wavefront .obj, wavefront .obj to glTF mesh.
## Projects using TinyGLTF
@@ -85,7 +84,6 @@ In extension(`ExtensionMap`), JSON number value is parsed as int or float(number
* [QuickLook GLTF](https://github.com/toshiks/glTF-quicklook) - quicklook plugin for macos. Also SceneKit wrapper for tinygltf.
* [GlslViewer](https://github.com/patriciogonzalezvivo/glslViewer) - live GLSL coding for MacOS and Linux
* [Vulkan-Samples](https://github.com/KhronosGroup/Vulkan-Samples) - The Vulkan Samples is collection of resources to help you develop optimized Vulkan applications.
* [TDME2](https://github.com/andreasdr/tdme2) - TDME2 - ThreeDeeMiniEngine2 is a lightweight 3D engine including tools suited for 3D game development using C++11
* Your projects here! (Please send PR)
## TODOs
@@ -219,3 +217,7 @@ We may be better to introduce bounded memory size checking when parsing glTF dat
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
* RapidJSON : Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. http://rapidjson.org/
* dlib(uridecode, uriencode) : Copyright (C) 2003 Davis E. King Boost Software License 1.0. http://dlib.net/dlib/server/server_http.cpp.html
### Used in examples
* clipp: MIT License. https://github.com/muellan/clipp

View File

@@ -19,7 +19,3 @@ $ make
Plese use solution file located at `basic` folder.
## Limitation
There are so many limitations in this example(e.g. no PBR shader. the shader only shows texture of textures[0] if available).

View File

@@ -100,49 +100,42 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
std::cout << "vaa missing: " << attrib.first << std::endl;
}
if (model.textures.size() > 0) {
// fixme: Use material's baseColor
tinygltf::Texture &tex = model.textures[0];
GLuint texid;
glGenTextures(1, &texid);
if (tex.source > -1) {
tinygltf::Texture &tex = model.textures[0];
tinygltf::Image &image = model.images[tex.source];
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
tinygltf::Image &image = model.images[tex.source];
GLenum format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, texid);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GLenum format = GL_RGBA;
if (image.component == 1) {
format = GL_RED;
} else if (image.component == 2) {
format = GL_RG;
} else if (image.component == 3) {
format = GL_RGB;
} else {
// ???
}
GLenum type = GL_UNSIGNED_BYTE;
if (image.bits == 8) {
// ok
} else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT;
} else {
// ???
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
format, type, &image.image.at(0));
}
if (image.component == 1) {
format = GL_RED;
} else if (image.component == 2) {
format = GL_RG;
} else if (image.component == 3) {
format = GL_RGB;
} else {
// ???
}
GLenum type = GL_UNSIGNED_BYTE;
if (image.bits == 8) {
// ok
} else if (image.bits == 16) {
type = GL_UNSIGNED_SHORT;
} else {
// ???
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
format, type, &image.image.at(0));
}
return vbos;
@@ -151,12 +144,8 @@ std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
// bind models
void bindModelNodes(std::map<int, GLuint> vbos, tinygltf::Model &model,
tinygltf::Node &node) {
if ((node.mesh >= 0) && (node.mesh < model.meshes.size())) {
bindMesh(vbos, model, model.meshes[node.mesh]);
}
bindMesh(vbos, model, model.meshes[node.mesh]);
for (size_t i = 0; i < node.children.size(); i++) {
assert((node.children[i] >= 0) && (node.children[i] < model.nodes.size()));
bindModelNodes(vbos, model, model.nodes[node.children[i]]);
}
}
@@ -168,7 +157,6 @@ GLuint bindModel(tinygltf::Model &model) {
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
for (size_t i = 0; i < scene.nodes.size(); ++i) {
assert((scene.nodes[i] >= 0) && (scene.nodes[i] < model.nodes.size()));
bindModelNodes(vbos, model, model.nodes[scene.nodes[i]]);
}
@@ -194,9 +182,7 @@ void drawMesh(tinygltf::Model &model, tinygltf::Mesh &mesh) {
// recursively draw node and children nodes of model
void drawModelNodes(tinygltf::Model &model, tinygltf::Node &node) {
if ((node.mesh >= 0) && (node.mesh < model.meshes.size())) {
drawMesh(model, model.meshes[node.mesh]);
}
drawMesh(model, model.meshes[node.mesh]);
for (size_t i = 0; i < node.children.size(); i++) {
drawModelNodes(model, model.nodes[node.children[i]]);
}

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +0,0 @@
cmake_minimum_required(VERSION 3.5)
project(dxview)
find_package(glfw3 CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
add_executable(dxview
src/Viewer.h
src/Viewer.cc
src/dxview.cc
)
target_include_directories(dxview
PRIVATE
../../
)
target_compile_definitions(dxview
PRIVATE
DXVIEW_SWAP_CHAIN_BUFFER_COUNT=3
DXVIEW_RES_DIR=L"${PROJECT_SOURCE_DIR}/res"
)
target_link_libraries(dxview
PRIVATE
dxgi
d3dcompiler
d3d12
glfw
spdlog::spdlog
)
set_target_properties(dxview
PROPERTIES
CXX_STANDARD 17
)

View File

@@ -1,37 +0,0 @@
# DirectX glTF Viewer
## Overview
This project was motivated by a lack of sample code demonstrating the graphics API agnostic nature of the glTF specification. The sample code is written using modern C++ and DirectX 12 for the client application.
## Features
* [x] DirectX 12
* [ ] Loader
* [ ] Animation
* [ ] Morph Target
* [ ] Physical Base Rendering
* [ ] Environment Map
## Dependencies
* [CMake](https://github.com/Kitware/CMake)
* [Vcpkg](https://github.com/Microsoft/vcpkg)
* [GLFW](https://github.com/glfw/glfw)
* [spdlog](https://github.com/gabime/spdlog)
## Building
### Install dependencies
```
vcpkg install glfw3:x64-windows
vcpkg install spdlog:x64-windows
```
### Generate Project Files
```
mkdir build
cmake . -B build -DCMAKE_TOOLCHAIN_FILE=${VCPKG_DIR}/script/buildsystem/vcpkg.cmake
```

View File

@@ -1,3 +0,0 @@
float4 main() : SV_Target {
return float4(0.5, 0.5, 0.5, 1.0);
}

View File

@@ -1,52 +0,0 @@
struct RS2PS {
float4 position : SV_POSITION;
#ifdef HAS_TEXCOORD_0
float2 texcoord_0: TEXCOORD_0;
#endif
};
struct TextureInfo {
uint textureIndex;
uint samplerIndex;
};
struct PBRMetallicRoughness {
float4 baseColorFactor;
TextureInfo baseColorTexture;
float metallicFactor;
float roughnessFactor;
TextureInfo metallicRoughnessTexture;
};
cbuffer Material : register(b2) {
PBRMetallicRoughness pbrMetallicRoughness;
};
Texture2D textures[5] : register(t0);
SamplerState samplerState[5] : register(s0);
float4 getBaseColor(float2 uv) {
float4 baseColor = pbrMetallicRoughness.baseColorFactor;
#ifdef HAS_TEXCOORD_0
TextureInfo baseColorTexture = pbrMetallicRoughness.baseColorTexture;
if (baseColorTexture.textureIndex >= 0) {
baseColor *= textures[baseColorTexture.textureIndex].Sample(samplerState[baseColorTexture.samplerIndex], uv);
}
#endif
return baseColor;
}
float4 main(RS2PS input) : SV_Target {
float2 uv = float2(0.0, 0.0);
#ifdef HAS_TEXCOORD_0
uv = input.texcoord_0;
#endif
float4 color = getBaseColor(uv);
return color;
}

View File

@@ -1,46 +0,0 @@
struct IA2VS {
float3 position : POSITION;
#ifdef HAS_NORMAL
float3 normal : NORMAL;
#endif
#ifdef HAS_TANGENT
float4 tangent : TANGENT;
#endif
#ifdef HAS_TEXCOORD_0
float2 texcoord_0: TEXCOORD_0;
#endif
};
struct VS2RS {
float4 position : SV_POSITION;
#ifdef HAS_TEXCOORD_0
float2 texcoord_0: TEXCOORD_0;
#endif
};
cbuffer Camera : register(b0) {
float4x4 V;
float4x4 P;
float4x4 VP;
};
cbuffer Node : register(b1) {
float4x4 M;
};
VS2RS main(IA2VS input) {
VS2RS output;
output.position = mul(float4(input.position, 1.0), M);
output.position = mul(output.position, V);
output.position = mul(output.position, P);
#ifdef HAS_TEXCOORD_0
output.texcoord_0 = input.texcoord_0;
#endif
return output;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,130 +0,0 @@
#ifndef DXVIEW_VIEWER_GUARD
#define DXVIEW_VIEWER_GUARD
#include <DirectXMath.h>
#include <d3d12.h>
#include <dxgi1_6.h>
#include <tiny_gltf.h>
#include <wrl.h>
#include <filesystem>
#include <map>
#include <string>
#include <vector>
using Microsoft::WRL::ComPtr;
enum RenderPassType { RENDER_PASS_TYPE_PRESENT = 0, RENDER_PASS_TYPE_COUNT };
struct RenderTarget {
ComPtr<ID3D12Resource> pTexture;
D3D12_CPU_DESCRIPTOR_HANDLE viewDescriptor;
};
struct TextureInfo {
int32_t textureIndex;
int32_t samplerIndex;
};
struct PBRMetallicRoughness {
DirectX::XMFLOAT4 baseColorFactor;
TextureInfo baseColorTexture;
float metallicFactor;
float roughnessFactor;
TextureInfo metallicRoughnessTexture;
};
struct Material {
std::string name;
D3D12_BLEND_DESC blendDesc;
D3D12_RASTERIZER_DESC rasterizerDesc;
ComPtr<ID3D12Resource> pBuffer;
void* pBufferData;
ComPtr<ID3D12DescriptorHeap> pSRVDescriptorHeap;
ComPtr<ID3D12DescriptorHeap> pSamplerDescriptorHeap;
};
struct Attribute {
std::string name;
DXGI_FORMAT format;
D3D12_VERTEX_BUFFER_VIEW vertexBufferView;
};
struct Primitive {
std::vector<Attribute> attributes;
uint32_t vertexCount;
D3D12_PRIMITIVE_TOPOLOGY primitiveTopology;
D3D12_INDEX_BUFFER_VIEW indexBufferView;
uint32_t indexCount;
Material* pMaterial;
ComPtr<ID3D12RootSignature> pRootSignature;
ComPtr<ID3D12PipelineState> pPipelineState;
};
struct Mesh {
std::string name;
std::vector<Primitive> primitives;
};
struct Node {
DirectX::XMFLOAT4X4 M;
};
struct Camera {
DirectX::XMFLOAT4X4 V;
DirectX::XMFLOAT4X4 P;
DirectX::XMFLOAT4X4 VP;
};
class Viewer {
public:
Viewer(HWND window, tinygltf::Model* pModel);
void update(double deltaTime);
void render(double deltaTime);
private:
void initDirectX(HWND window);
void buildRenderTargets();
void buildResources();
void buildBuffers(std::vector<ComPtr<ID3D12Resource> >* pStagingResources);
void buildImages(std::vector<ComPtr<ID3D12Resource> >* pStagingResources);
void buildSamplerDescs();
void buildMaterials();
void buildMeshes();
void buildNodes();
void drawNode(uint64_t nodeIndex);
private:
tinygltf::Model* pModel_;
ComPtr<IDXGIFactory1> pFactory_;
ComPtr<IDXGIAdapter1> pAdapter_;
ComPtr<ID3D12Device> pDevice_;
ComPtr<ID3D12CommandQueue> pDirectCommandQueue_;
UINT64 directFenceValue_;
ComPtr<ID3D12Fence> pDirectFence_;
ComPtr<ID3D12CommandQueue> pCopyCommandQueue_;
UINT64 copyFenceValue_;
ComPtr<ID3D12Fence> pCopyFence_;
ComPtr<IDXGISwapChain3> pSwapChain_;
ComPtr<ID3D12Resource> pSwapChainBuffers_[DXVIEW_SWAP_CHAIN_BUFFER_COUNT];
ComPtr<ID3D12CommandAllocator>
pDirectCommandAllocators_[DXVIEW_SWAP_CHAIN_BUFFER_COUNT];
ComPtr<ID3D12GraphicsCommandList> pDirectCommandList_;
ComPtr<ID3D12CommandAllocator> pCopyCommandAllocator_;
ComPtr<ID3D12GraphicsCommandList> pCopyCommandList_;
UINT descriptorIncrementSize_[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
ComPtr<ID3D12DescriptorHeap>
pRTVDescriptorHeaps_[DXVIEW_SWAP_CHAIN_BUFFER_COUNT];
std::vector<RenderTarget> renderTargets_[DXVIEW_SWAP_CHAIN_BUFFER_COUNT];
std::vector<ComPtr<ID3D12Resource> > pBuffers_;
std::vector<ComPtr<ID3D12Resource> > pTextures_;
std::vector<D3D12_SAMPLER_DESC> samplerDescs_;
std::vector<Material> materials_;
std::vector<Mesh> meshes_;
std::vector<ComPtr<ID3D12Resource> > pNodeBuffers_;
ComPtr<ID3D12Resource> pCameraBuffer_;
};
#endif

View File

@@ -1,90 +0,0 @@
#define GLFW_EXPOSE_NATIVE_WIN32
#define TINYGLTF_IMPLEMENTATION
#define STBI_MSC_SECURE_CRT
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include <spdlog/spdlog.h>
#include <tiny_gltf.h>
#undef GLFW_EXPOSE_NATIVE_WIN32
#undef TINYGLTF_IMPLEMENTATION
#undef STBI_MSC_SECURE_CRT
#undef STB_IMAGE_IMPLEMENTATION
#undef STB_IMAGE_WRITE_IMPLEMENTATION
#include <iostream>
#include <string>
#include "Viewer.h"
static void onError(int error, const char* message) {
spdlog::error("[{}] {}", error, message);
}
static void onRender(Viewer* pViewer, double deltaTime) {
pViewer->update(deltaTime);
pViewer->render(deltaTime);
}
int main(int argc, char* argv[]) {
tinygltf::TinyGLTF context;
tinygltf::Model model;
std::string error;
std::string warning;
if (!context.LoadASCIIFromFile(&model, &error, &warning, argv[1])) {
if (!error.empty()) {
spdlog::error("{}", error);
}
if (!warning.empty()) {
spdlog::warn("{}", warning);
}
exit(EXIT_FAILURE);
}
GLFWwindow* pWindow;
glfwSetErrorCallback(onError);
if (!glfwInit()) {
spdlog::error("Fail to initialize GLFW!!!");
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
pWindow = glfwCreateWindow(512, 512, "dxview", nullptr, nullptr);
if (!pWindow) {
spdlog::error("Fail to create GLFWwindow!!!");
glfwTerminate();
exit(EXIT_FAILURE);
}
auto pViewer = std::make_unique<Viewer>(glfwGetWin32Window(pWindow), &model);
if (!pViewer) {
spdlog::error("Fail to create Viewer");
glfwDestroyWindow(pWindow);
glfwTerminate();
exit(EXIT_FAILURE);
}
auto prevTimeStamp = glfwGetTime();
while (!glfwWindowShouldClose(pWindow)) {
auto currTimeStamp = glfwGetTime();
onRender(pViewer.get(), currTimeStamp - prevTimeStamp);
prevTimeStamp = currTimeStamp;
glfwPollEvents();
}
glfwDestroyWindow(pWindow);
glfwTerminate();
exit(EXIT_SUCCESS);
}

View 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

View File

@@ -0,0 +1,5 @@
all:
ninja
clean:
ninja -t clean

View 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

View 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

View 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;
}
}

File diff suppressed because it is too large Load Diff

View 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

View 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)

View 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.

View 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()

View 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()

View 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

View File

@@ -150,6 +150,7 @@ bool LoadObj(const std::string &filename, float scale,
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn;
std::string err;
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();
bool ret =
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str(),
tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename.c_str(),
basepath, /* triangulate */ true);
// auto t_end = std::chrono::system_clock::now();
// std::chrono::duration<double, std::milli> ms = t_end - t_start;
if (!warn.empty()) {
std::cout << warn << std::endl;
}
if (!err.empty()) {
std::cerr << err << std::endl;
}

View File

@@ -1,376 +0,0 @@
{
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 8,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
},
{
"bufferView": 1,
"componentType": 5125,
"count": 36,
"type": "SCALAR"
}
],
"asset": {
"copyright": "NVIDIA Corporation",
"generator": "Iray glTF plugin",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 96,
"byteStride": 12
},
{
"buffer": 0,
"byteLength": 144,
"byteOffset": 96
}
],
"buffers": [
{
"byteLength": 240,
"uri": "issue-261.bin"
}
],
"cameras": [
{
"extensions": {
"NV_Iray": {
"mip_burn_highlights": 0.699999988079071,
"mip_burn_highlights_per_component": false,
"mip_camera_shutter": 4.0,
"mip_cm2_factor": 1.0,
"mip_crush_blacks": 0.5,
"mip_f_number": 1.0,
"mip_film_iso": 100.0,
"mip_gamma": 2.200000047683716,
"mip_saturation": 1.0,
"mip_vignetting": 0.00019999999494757503,
"mip_whitepoint": [
1.0,
1.0,
1.0,
1.0
],
"tm_enable_tonemapper": true,
"tm_tonemapper": "mia_exposure_photographic"
}
},
"extras": {
"resolution": [
640,
480
]
},
"name": "default",
"perspective": {
"aspectRatio": 1.3333333730697632,
"yfov": 0.9272952079772949,
"zfar": 1000.0,
"znear": 0.1
},
"type": "perspective"
}
],
"extensions": {
"KHR_lights_punctual": {
"lights": [
{
"color": [
1.0,
1.0,
1.0
],
"intensity": 1000.0,
"name": "light",
"type": "point"
}
]
},
"NV_MDL": {
"modules": [
"mdl::base",
"mdl::nvidia::core_definitions",
"mdl::state"
],
"shaders": [
{
"definition": "mdl::base::environment_spherical(texture_2d)",
"name": "env_shd"
},
{
"arguments": {
"base_color": [
0.0055217444896698,
0.36859095096588135,
0.0041161770932376385
],
"normal=": 2
},
"definition": "mdl::nvidia::core_definitions::flex_material",
"name": "cube_instance_material"
},
{
"definition": "mdl::state::normal()",
"name": "mdl::state::normal_341"
},
{
"arguments": {
"base_color": [
0.0055217444896698,
0.36859095096588135,
0.0041161770932376385
],
"normal=": 4
},
"definition": "mdl::nvidia::core_definitions::flex_material",
"name": "cube_instance_material"
},
{
"definition": "mdl::state::normal()",
"name": "mdl::state::normal_341"
}
]
}
},
"extensionsUsed": [
"NV_MDL",
"NV_Iray",
"KHR_lights_punctual"
],
"materials": [
{
"doubleSided": true,
"extras": {
"mdl_shader": 1
},
"name": "cube_instance_material",
"pbrMetallicRoughness": {
"baseColorFactor": [
0.0055217444896698,
0.36859095096588135,
0.0041161770932376385,
1.0
]
}
}
],
"meshes": [
{
"name": "cube",
"primitives": [
{
"attributes": {
"POSITION": 0
},
"indices": 1,
"material": 0,
"mode": 4
}
]
}
],
"nodes": [
{
"camera": 0,
"extensions": {
"NV_Iray": {
"iview:fkey": -1,
"iview:fov": 53.130104064941406,
"iview:interest": [
0.1342654824256897,
-0.14356137812137604,
0.037264324724674225
],
"iview:position": [
0.9729073643684387,
1.2592438459396362,
2.4199187755584717
],
"iview:roll": 0.0,
"iview:up": [
0.0,
1.0,
0.0
]
}
},
"matrix": [
0.9432751389105357,
-1.1769874756875739e-16,
-0.3320120665176343,
0.0,
-0.16119596696756341,
0.8742297945345237,
-0.45797175303889276,
0.0,
0.290254840694694,
0.48551237507207207,
0.8246392308792816,
0.0,
0.9729073377709113,
1.2592438262175363,
2.419918748461627,
1.0
],
"name": "CamInst"
},
{
"extensions": {
"NV_Iray": {
"caustic": true,
"caustic_cast": true,
"caustic_recv": true,
"face_back": true,
"face_front": true,
"finalgather": true,
"finalgather_cast": true,
"finalgather_recv": true,
"globillum": true,
"globillum_cast": true,
"globillum_recv": true,
"material=": 3,
"pickable": true,
"reflection_cast": true,
"reflection_recv": true,
"refraction_cast": true,
"refraction_recv": true,
"shadow_cast": true,
"shadow_recv": true,
"transparency_cast": true,
"transparency_recv": true,
"visible": true
}
},
"mesh": 0,
"name": "cube_instance"
},
{
"extensions": {
"KHR_lights_punctual": {
"light": 0
},
"NV_Iray": {
"shadow_cast": true,
"visible": false
}
},
"matrix": [
0.6988062355563571,
-7.76042172309776e-17,
-0.7153110128800992,
-0.0,
-0.4276881690736487,
0.8015668284138362,
-0.41781987700564616,
-0.0,
0.57336957992379,
0.5979051928078428,
0.5601398979107212,
0.0,
2.3640632834071384,
2.465226204472449,
2.309515908392224,
1.0
],
"name": "light_inst"
}
],
"scene": 0,
"scenes": [
{
"extensions": {
"NV_Iray": {
"CP_canny_threshold1": 40,
"CP_canny_threshold2": 120,
"CP_color_quantization": 8,
"IVP_color": [
1.0,
0.0,
0.0,
1.0
],
"TM_drago_bias": 0.8500000238418579,
"TM_drago_gamma2": 2.200000047683716,
"TM_drago_saturation": 1.0,
"TM_durand_contrast": 4.0,
"TM_durand_gamma": 2.200000047683716,
"TM_durand_saturation": 1.0,
"TM_durand_sigma_color": 2.0,
"TM_durand_sigma_space": 2.0,
"TM_linear_gamma": 2.200000047683716,
"TM_reinhard_color_adapt": 0.8999999761581421,
"TM_reinhard_gamma": 1.0,
"TM_reinhard_intensity": 0.0,
"TM_reinhard_light_adapt": 1.0,
"TM_reye_Ywhite": 1000000.0,
"TM_reye_bsize": 2,
"TM_reye_bthres": 3.0,
"TM_reye_gamma": 2.200000047683716,
"TM_reye_key": 0.5,
"TM_reye_saturation": 1.0,
"TM_reye_whitebalance": [
1.0,
0.9965101480484009,
0.9805564880371094,
0.0
],
"environment_dome_depth": 200.0,
"environment_dome_height": 200.0,
"environment_dome_mode": "infinite",
"environment_dome_position": [
0.0,
0.0,
0.0
],
"environment_dome_radius": 100.0,
"environment_dome_rotation_angle": 0.0,
"environment_dome_width": 200.0,
"environment_function=": 0,
"environment_function_intensity": 1.9900000095367432,
"iray_instancing": "off",
"iview::inline_color": [
1.0,
0.0,
0.0,
1.0
],
"iview::inline_width": 1.0,
"iview::magnifier_size": 300,
"iview::offset": 10.0,
"iview::outline_color": [
0.0,
0.0,
0.0,
1.0
],
"iview::outline_width": 2.0,
"iview::overview": true,
"iview::zoom_factor": 1.0,
"samples": 4.0,
"shadow_cast": true,
"shadow_recv": true
}
},
"nodes": [
0,
1,
2
]
}
]
}

View File

@@ -93,59 +93,6 @@ TEST_CASE("extension-with-empty-object", "[issue-97]") {
}
TEST_CASE("extension-overwrite", "[issue-261]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/Extensions-overwrite-issue261/issue-261.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(model.extensionsUsed.size() == 3);
{
bool has_ext_lights = false;
has_ext_lights |= (model.extensionsUsed[0].compare("KHR_lights_punctual") == 0);
has_ext_lights |= (model.extensionsUsed[1].compare("KHR_lights_punctual") == 0);
has_ext_lights |= (model.extensionsUsed[2].compare("KHR_lights_punctual") == 0);
REQUIRE(true == has_ext_lights);
}
{
REQUIRE(model.extensions.size() == 2);
REQUIRE(model.extensions.count("NV_MDL"));
REQUIRE(model.extensions.count("KHR_lights_punctual"));
}
// TODO(syoyo): create temp directory.
{
ret = ctx.WriteGltfSceneToFile(&model, "issue-261.gltf", true, true);
REQUIRE(true == ret);
tinygltf::Model m;
// read back serialized glTF
bool ret = ctx.LoadASCIIFromFile(&m, &err, &warn, "issue-261.gltf");
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
REQUIRE(m.extensionsUsed.size() == 3);
REQUIRE(m.extensions.size() == 2);
REQUIRE(m.extensions.count("NV_MDL"));
REQUIRE(m.extensions.count("KHR_lights_punctual"));
}
}
TEST_CASE("invalid-primitive-indices", "[bounds-checking]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
@@ -412,19 +359,3 @@ TEST_CASE("image-uri-spaces", "[issue-236]") {
REQUIRE(true == ret);
}
#ifndef TINYGLTF_NO_FS
TEST_CASE("expandpath-utf-8", "[pr-226]") {
std::string s1 = "\xe5\xaf\xb9"; // utf-8 string
std::string ret = tinygltf::ExpandFilePath(s1, /* userdata */nullptr);
// expected: E5 AF B9
REQUIRE(3 == ret.size());
REQUIRE(0xe5 == static_cast<uint8_t>(ret[0]));
REQUIRE(0xaf == static_cast<uint8_t>(ret[1]));
REQUIRE(0xb9 == static_cast<uint8_t>(ret[2]));
}
#endif

View File

@@ -52,7 +52,6 @@
#include <array>
#include <cassert>
#include <cmath> // std::fabs
#include <cstdint>
#include <cstdlib>
#include <cstring>
@@ -323,8 +322,7 @@ class Value {
}
// Use this function if you want to have number value as int.
// TODO(syoyo): Support int value larger than 32 bits
int GetNumberAsInt() const {
double GetNumberAsInt() const {
if (type_ == REAL_TYPE) {
return int(real_value_);
} else {
@@ -664,8 +662,6 @@ struct Image {
width = -1;
height = -1;
component = -1;
bits = -1;
pixel_type = -1;
}
DEFAULT_METHODS(Image)
@@ -998,7 +994,6 @@ struct Primitive {
Primitive() {
material = -1;
indices = -1;
mode = -1;
}
DEFAULT_METHODS(Primitive)
bool operator==(const Primitive &) const;
@@ -1118,9 +1113,9 @@ struct SpotLight {
struct Light {
std::string name;
std::vector<double> color;
double intensity{1.0};
double intensity;
std::string type;
double range{0.0}; // 0.0 = inifinite
double range;
SpotLight spot;
Light() : intensity(1.0), range(0.0) {}
@@ -1252,14 +1247,7 @@ struct FsCallbacks {
bool FileExists(const std::string &abs_filename, void *);
///
/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
/// `C:\Users\tinygltf\AppData`)
///
/// @param[in] filepath File path string. Assume UTF-8
/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
///
std::string ExpandFilePath(const std::string &filepath, void *userdata);
std::string ExpandFilePath(const std::string &filepath, void *);
bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
const std::string &filepath, void *);
@@ -2493,15 +2481,6 @@ static inline std::wstring UTF8ToWchar(const std::string &str) {
(int)wstr.size());
return wstr;
}
static inline std::string WcharToUTF8(const std::wstring &wstr) {
int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
nullptr, 0, NULL, NULL);
std::string str(str_size, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
(int)str.size(), NULL, NULL);
return str;
}
#endif
#ifndef TINYGLTF_NO_FS
@@ -2553,16 +2532,15 @@ bool FileExists(const std::string &abs_filename, void *) {
std::string ExpandFilePath(const std::string &filepath, void *) {
#ifdef _WIN32
// Assume input `filepath` is encoded in UTF-8
std::wstring wfilepath = UTF8ToWchar(filepath);
DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
wchar_t *wstr = new wchar_t[wlen];
ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
char *str = new char[len];
ExpandEnvironmentStringsA(filepath.c_str(), str, len);
std::wstring ws(wstr);
delete[] wstr;
return WcharToUTF8(ws);
std::string s(str);
delete[] str;
return s;
#else
#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
@@ -2614,12 +2592,11 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
return false;
}
size_t size = AAsset_getLength(asset);
if (size == 0) {
if (size <= 0) {
if (err) {
(*err) += "Invalid file size : " + filepath +
" (does the path point to a directory?)";
}
return false;
}
out->resize(size);
AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
@@ -2638,12 +2615,9 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
_wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
std::istream f(&wfile_buf);
#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
// For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
// `wchar_t *`
#elif defined(_MSC_VER)
std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
#else
// Unknown compiler/runtime
#else // clang?
std::ifstream f(filepath.c_str(), std::ifstream::binary);
#endif
#else
@@ -2685,10 +2659,9 @@ bool WriteWholeFile(std::string *err, const std::string &filepath,
const std::vector<unsigned char> &contents, void *) {
#ifdef _WIN32
#if defined(__GLIBCXX__) // mingw
int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(
file_descriptor, std::ios_base::out | std::ios_base::binary);
int file_descriptor =
_wopen(UTF8ToWchar(filepath).c_str(), _O_WRONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
std::ostream f(&wfile_buf);
#elif defined(_MSC_VER)
std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
@@ -2739,13 +2712,12 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
void *user_data = nullptr) {
std::string filename;
std::string ext;
// If image has uri, use it it as a filename
// If image have uri. Use it it as a filename
if (image.uri.size()) {
filename = GetBaseFilename(image.uri);
ext = GetFilePathExtension(filename);
} else if (image.bufferView != -1) {
// If there's no URI and the data exists in a buffer,
// don't change properties or write images
} else if (image.name.size()) {
ext = MimeToExt(image.mimeType);
// Otherwise use name as filename
@@ -2757,7 +2729,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
}
// If callback is set, modify image data object
if (*WriteImageData != nullptr && !filename.empty()) {
if (*WriteImageData != nullptr) {
std::string uri;
(*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
}
@@ -3002,9 +2974,7 @@ json_const_iterator ObjectEnd(const json &o) {
#endif
}
// Making this a const char* results in a pointer to a temporary when
// TINYGLTF_USE_RAPIDJSON is off.
std::string GetKey(json_const_iterator &it) {
const char *GetKey(json_const_iterator &it) {
#ifdef TINYGLTF_USE_RAPIDJSON
return it->name.GetString();
#else
@@ -4890,13 +4860,13 @@ static bool ParseAnimationChannel(
}
return false;
}
ParseExtensionsProperty(&channel->target_extensions, err, target_object);
if (store_original_json_for_extras_and_extensions) {
ParseExtensionsProperty(&channel->target_extensions, err, target_object);
if (store_original_json_for_extras_and_extensions) {
json_const_iterator it;
if (FindMember(target_object, "extensions", it)) {
channel->target_extensions_json_string = JsonToString(GetValue(it));
}
}
}
}
channel->sampler = samplerIndex;
@@ -5707,15 +5677,10 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
}
for (auto &target : primitive.targets) {
for (auto &attribute : target) {
auto bufferView =
model->accessors[size_t(attribute.second)].bufferView;
// bufferView could be null(-1) for sparse morph target
if (bufferView >= 0) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
}
for(auto &target : primitive.targets) {
for(auto &attribute : target) {
model->bufferViews[size_t(model->accessors[size_t(attribute.second)].bufferView)]
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
}
}
}
@@ -6300,13 +6265,6 @@ static void SerializeNumberProperty(const std::string &key, T number,
JsonAddMember(obj, key.c_str(), json(number));
}
#ifdef TINYGLTF_USE_RAPIDJSON
template <>
void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
}
#endif
template <typename T>
static void SerializeNumberArrayProperty(const std::string &key,
const std::vector<T> &value,
@@ -6457,10 +6415,9 @@ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
const std::string &binFilename) {
#ifdef _WIN32
#if defined(__GLIBCXX__) // mingw
int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(
file_descriptor, std::ios_base::out | std::ios_base::binary);
int file_descriptor =
_wopen(UTF8ToWchar(binFilename).c_str(), _O_WRONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
std::ostream output(&wfile_buf);
if (!wfile_buf.is_open()) return false;
#elif defined(_MSC_VER)
@@ -6543,10 +6500,9 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
}
static void SerializeGltfAccessor(Accessor &accessor, json &o) {
if (accessor.bufferView >= 0)
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
if (accessor.byteOffset != 0)
if (accessor.byteOffset != 0.0)
SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
SerializeNumberProperty<int>("componentType", accessor.componentType, o);
@@ -6595,7 +6551,7 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
SerializeNumberProperty("node", channel.target_node, target);
SerializeStringProperty("path", channel.target_path, target);
SerializeExtensionMap(channel.target_extensions, target);
SerializeExtensionMap(channel.target_extensions, target);
JsonAddMember(o, "target", std::move(target));
}
@@ -7002,9 +6958,7 @@ static void SerializeSpotLight(SpotLight &spot, json &o) {
static void SerializeGltfLight(Light &light, json &o) {
if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
SerializeNumberProperty("intensity", light.intensity, o);
if (light.range > 0.0) {
SerializeNumberProperty("range", light.range, o);
}
SerializeNumberProperty("range", light.range, o);
SerializeNumberArrayProperty("color", light.color, o);
SerializeStringProperty("type", light.type, o);
if (light.type == "spot") {
@@ -7118,11 +7072,6 @@ static void SerializeGltfCamera(const Camera &camera, json &o) {
} else {
// ???
}
if (camera.extras.Type() != NULL_TYPE) {
SerializeValue("extras", camera.extras, o);
}
SerializeExtensionMap(camera.extensions, o);
}
static void SerializeGltfScene(Scene &scene, json &o) {
@@ -7201,7 +7150,7 @@ static void SerializeGltfModel(Model *model, json &o) {
JsonAddMember(o, "asset", std::move(asset));
// BUFFERVIEWS
if (model->bufferViews.size()) {
if(model->bufferViews.size()) {
json bufferViews;
JsonReserveArray(bufferViews, model->bufferViews.size());
for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
@@ -7212,6 +7161,11 @@ static void SerializeGltfModel(Model *model, json &o) {
JsonAddMember(o, "bufferViews", std::move(bufferViews));
}
// Extensions used
if (model->extensionsUsed.size()) {
SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
}
// Extensions required
if (model->extensionsRequired.size()) {
SerializeStringArrayProperty("extensionsRequired",
@@ -7322,9 +7276,7 @@ static void SerializeGltfModel(Model *model, json &o) {
// EXTENSIONS
SerializeExtensionMap(model->extensions, o);
auto extensionsUsed = model->extensionsUsed;
// LIGHTS as KHR_lights_punctual
// LIGHTS as KHR_lights_cmn
if (model->lights.size()) {
json lights;
JsonReserveArray(lights, model->lights.size());
@@ -7339,7 +7291,7 @@ static void SerializeGltfModel(Model *model, json &o) {
{
json_const_iterator it;
if (FindMember(o, "extensions", it)) {
if (!FindMember(o, "extensions", it)) {
JsonAssign(ext_j, GetValue(it));
}
}
@@ -7347,24 +7299,6 @@ static void SerializeGltfModel(Model *model, json &o) {
JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
JsonAddMember(o, "extensions", std::move(ext_j));
// Also add "KHR_lights_punctual" to `extensionsUsed`
{
auto has_khr_lights_punctual =
std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
[](const std::string &s) {
return (s.compare("KHR_lights_punctual") == 0);
});
if (has_khr_lights_punctual == extensionsUsed.end()) {
extensionsUsed.push_back("KHR_lights_punctual");
}
}
}
// Extensions used
if (model->extensionsUsed.size()) {
SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
}
// EXTRAS
@@ -7384,10 +7318,9 @@ static bool WriteGltfFile(const std::string &output,
#if defined(_MSC_VER)
std::ofstream gltfFile(UTF8ToWchar(output).c_str());
#elif defined(__GLIBCXX__)
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(
file_descriptor, std::ios_base::out | std::ios_base::binary);
int file_descriptor =
_wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
std::ostream gltfFile(&wfile_buf);
if (!wfile_buf.is_open()) return false;
#else
@@ -7473,10 +7406,9 @@ static void WriteBinaryGltfFile(const std::string &output,
#if defined(_MSC_VER)
std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
#elif defined(__GLIBCXX__)
int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
_O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(
file_descriptor, std::ios_base::out | std::ios_base::binary);
int file_descriptor =
_wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
__gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
std::ostream gltfFile(&wfile_buf);
#else
std::ofstream gltfFile(output.c_str(), std::ios::binary);
@@ -7497,13 +7429,13 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
// BUFFERS
std::vector<unsigned char> binBuffer;
if (model->buffers.size()) {
if(model->buffers.size()) {
json buffers;
JsonReserveArray(buffers, model->buffers.size());
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
json buffer;
if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
if (writeBinary && i==0 && model->buffers[i].uri.empty()){
SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer);
} else {
SerializeGltfBuffer(model->buffers[i], buffer);
}
@@ -7520,9 +7452,9 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
json image;
std::string dummystring = "";
// UpdateImageObject need baseDir but only uses it if embeddedImages is
// enabled, since we won't write separate images when writing to a stream
// we
// UpdateImageObject need baseDir but only uses it if embededImages is
// enable, since we won't write separte images when writing to a stream we
// use a dummystring
UpdateImageObject(model->images[i], dummystring, int(i), false,
&this->WriteImageData, this->write_image_user_data_);
SerializeGltfImage(model->images[i], image);
@@ -7569,15 +7501,14 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
JsonReserveArray(buffers, model->buffers.size());
for (unsigned int i = 0; i < model->buffers.size(); ++i) {
json buffer;
if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
if (writeBinary && i==0 && model->buffers[i].uri.empty()){
SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer);
} else if (embedBuffers) {
SerializeGltfBuffer(model->buffers[i], buffer);
} else {
std::string binSavePath;
std::string binUri;
if (!model->buffers[i].uri.empty() &&
!IsDataURI(model->buffers[i].uri)) {
if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
binUri = model->buffers[i].uri;
} else {
binUri = defaultBinFilename + defaultBinFileExt;
@@ -7603,7 +7534,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
}
JsonPushBack(buffers, std::move(buffer));
}
JsonAddMember(output, "buffers", std::move(buffers));
JsonAddMember(output, "buffers", std::move(buffers));
}
// IMAGES