added tinygltf
parent
077db8b04e
commit
8b2dc08ec3
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
BasedOnStyle: Google
|
||||
IndentWidth: 2
|
||||
TabWidth: 2
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Attach
|
||||
Standard: Cpp03
|
|
@ -0,0 +1,70 @@
|
|||
# CMake
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
|
||||
#Files created by the CI scripts (downloading and installing premake)
|
||||
premake5
|
||||
premake5.tar.gz
|
||||
|
||||
#built examples
|
||||
/examples/raytrace/bin/
|
||||
|
||||
#visual studio files
|
||||
*.sln
|
||||
*.vcxproj*
|
||||
.vs
|
||||
|
||||
#binary directories
|
||||
bin/
|
||||
obj/
|
||||
|
||||
#runtime gui config
|
||||
imgui.ini
|
||||
|
||||
#visual stuido code
|
||||
.vscode
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
loader_example
|
||||
tests/tester
|
||||
tests/tester_noexcept
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ "$TRAVIS_OS_NAME" == "osx" ]]
|
||||
then
|
||||
brew upgrade
|
||||
curl -o premake5.tar.gz https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-macosx.tar.gz
|
||||
else
|
||||
wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha12/premake-5.0.0-alpha12-linux.tar.gz -O premake5.tar.gz
|
||||
fi
|
||||
tar xzf premake5.tar.gz
|
|
@ -0,0 +1,47 @@
|
|||
language: cpp
|
||||
sudo: false
|
||||
matrix:
|
||||
include:
|
||||
- addons: &1
|
||||
apt:
|
||||
sources:
|
||||
- george-edison55-precise-backports
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.7
|
||||
packages:
|
||||
- g++-4.9
|
||||
- clang-3.7
|
||||
compiler: clang
|
||||
env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug
|
||||
- addons: *1
|
||||
compiler: clang
|
||||
env: COMPILER_VERSION=3.7 BUILD_TYPE=Release
|
||||
- addons: &2
|
||||
apt:
|
||||
sources:
|
||||
- george-edison55-precise-backports
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.9
|
||||
compiler: gcc
|
||||
env: COMPILER_VERSION=4.9 BUILD_TYPE=Debug EXTRA_CXXFLAGS="-fsanitize=address"
|
||||
- addons: *2
|
||||
compiler: gcc
|
||||
env: COMPILER_VERSION=4.9 BUILD_TYPE=Release EXTRA_CXXFLAGS="-fsanitize=address"
|
||||
- addons: *1
|
||||
compiler: clang
|
||||
env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
|
||||
|
||||
before_install:
|
||||
- ./.travis-before-install.sh
|
||||
|
||||
|
||||
script:
|
||||
- export CC="${CC}-${COMPILER_VERSION}"
|
||||
- export CXX="${CXX}-${COMPILER_VERSION}"
|
||||
- ${CC} -v
|
||||
- ${CXX} ${EXTRA_CXXFLAGS} -std=c++11 -Wall -g -o loader_example loader_example.cc
|
||||
- ./loader_example ./models/Cube/Cube.gltf
|
||||
- cd examples/raytrace
|
||||
- ../../premake5 gmake
|
||||
- make
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Syoyo Fujita, Aurélien Chatelain and many contributors
|
||||
|
||||
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.
|
|
@ -0,0 +1,140 @@
|
|||
# Header only C++ tiny glTF library(loader/saver).
|
||||
|
||||
`TinyGLTF` is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library.
|
||||
|
||||
## Status
|
||||
|
||||
Work in process(`devel` branch). Very near to release, but need more tests and examples.
|
||||
|
||||
`TinyGLTF` uses Niels Lohmann's json library(https://github.com/nlohmann/json), so now it requires C++11 compiler.
|
||||
If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
|
||||
## Builds
|
||||
|
||||
[![Build Status](https://travis-ci.org/syoyo/tinygltf.svg?branch=devel)](https://travis-ci.org/syoyo/tinygltf)
|
||||
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/warngenu9wjjhlm8?svg=true)](https://ci.appveyor.com/project/syoyo/tinygltf)
|
||||
|
||||
## Features
|
||||
|
||||
* Written in portable C++. C++-11 with STL dependency only.
|
||||
* [x] macOS + clang(LLVM)
|
||||
* [x] iOS + clang
|
||||
* [x] Linux + gcc/clang
|
||||
* [x] Windows + MinGW
|
||||
* [x] Windows + Visual Studio 2015 or later.
|
||||
* Visual Studio 2013 is not supported since they have limited C++11 support and failed to compile `json.hpp`.
|
||||
* [x] Android + CrystaX(NDK drop-in replacement) GCC
|
||||
* [x] Web using Emscripten(LLVM)
|
||||
* Moderate parsing time and memory consumption.
|
||||
* glTF specification v2.0.0
|
||||
* [x] ASCII glTF
|
||||
* [x] Binary glTF(GLB)
|
||||
* [x] PBR material description
|
||||
* Buffers
|
||||
* [x] Parse BASE64 encoded embedded buffer fata(DataURI).
|
||||
* [x] Load `.bin` file.
|
||||
* Image(Using stb_image)
|
||||
* [x] Parse BASE64 encoded embedded image fata(DataURI).
|
||||
* [x] Load external image file.
|
||||
* [x] PNG(8bit only)
|
||||
* [x] JPEG(8bit only)
|
||||
* [x] BMP
|
||||
* [x] GIF
|
||||
|
||||
## Examples
|
||||
|
||||
* [glview](examples/glview) : Simple glTF geometry viewer.
|
||||
* [validator](examples/validator) : Simple glTF validator with JSON schema.
|
||||
|
||||
## TODOs
|
||||
|
||||
* [ ] Write C++ code generator from json schema for robust parsing.
|
||||
* [x] Serialization
|
||||
* [ ] Compression/decompression(Open3DGC, etc)
|
||||
* [ ] Support `extensions` and `extras` property
|
||||
* [ ] HDR image?
|
||||
* [ ] Write tests for `animation` and `skin`
|
||||
|
||||
## Licenses
|
||||
|
||||
TinyGLTF is licensed under MIT license.
|
||||
|
||||
TinyGLTF uses the following third party libraries.
|
||||
|
||||
* json.hpp : Copyright (c) 2013-2017 Niels Lohmann. MIT license.
|
||||
* base64 : Copyright (C) 2004-2008 René Nyffenegger
|
||||
* stb_image.h : v2.08 - public domain image loader - http://nothings.org/stb_image.h
|
||||
|
||||
|
||||
## Build and example
|
||||
|
||||
Copy `stb_image.h`, `json.hpp` and `tiny_gltf.h` to your project.
|
||||
|
||||
### Loading glTF 2.0 model
|
||||
|
||||
```c++
|
||||
// Define these only in *one* .cc file.
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
// #define TINYGLTF_NOEXCEPTION // optional. disable exception handling.
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
using namespace tinygltf;
|
||||
|
||||
Model model;
|
||||
TinyGLTF loader;
|
||||
std::string err;
|
||||
|
||||
bool ret = loader.LoadASCIIFromFile(&model, &err, argv[1]);
|
||||
//bool ret = loader.LoadBinaryFromFile(&model, &err, argv[1]); // for binary glTF(.glb)
|
||||
if (!err.empty()) {
|
||||
printf("Err: %s\n", err.c_str());
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
printf("Failed to parse glTF\n");
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
## Compile options
|
||||
|
||||
* `TINYGLTF_NOEXCEPTION` : Disable C++ exception in JSON parsing. You can use `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION` and `TINYGLTF_NOEXCEPTION` to fully remove C++ exception codes when compiling TinyGLTF.
|
||||
* `TINYGLTF_NO_STB_IMAGE` : Do not load images with stb_image. Instead use `TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)` to set a callback for loading images.
|
||||
|
||||
### Saving gltTF 2.0 model
|
||||
|
||||
T.B.W.
|
||||
|
||||
## Running tests.
|
||||
|
||||
### glTF parsing test
|
||||
|
||||
#### Setup
|
||||
|
||||
Python 2.6 or 2.7 required.
|
||||
Git clone https://github.com/KhronosGroup/glTF-Sample-Models to your local dir.
|
||||
|
||||
#### Run parsing test
|
||||
|
||||
After building `loader_example`, edit `test_runner.py`, then,
|
||||
|
||||
```bash
|
||||
$ python test_runner.py
|
||||
```
|
||||
|
||||
### Unit tests
|
||||
|
||||
```bash
|
||||
$ cd tests
|
||||
$ make
|
||||
$ ./tester
|
||||
$ ./tester_noexcept
|
||||
```
|
||||
|
||||
## Third party licenses
|
||||
|
||||
* json.hpp : Licensed under the MIT License <http://opensource.org/licenses/MIT>. Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
* stb_image : Public domain.
|
||||
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
|
|
@ -0,0 +1,18 @@
|
|||
version: 0.9.{build}
|
||||
|
||||
image:
|
||||
- Visual Studio 2015
|
||||
|
||||
# scripts that runs after repo cloning.
|
||||
install:
|
||||
- vcsetup.bat
|
||||
|
||||
platform: x64
|
||||
configuration: Release
|
||||
|
||||
build:
|
||||
parallel: true
|
||||
project: TinyGLTFSolution.sln
|
||||
|
||||
after_build:
|
||||
- examples.bat
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
cd examples\raytrace
|
||||
..\..\tools\windows\premake5.exe vs2015
|
||||
msbuild NanoSGSolution.sln /property:Configuration=Release
|
|
@ -0,0 +1,34 @@
|
|||
Simple OpenGL viewer for glTF geometry.
|
||||
|
||||
## Requirements
|
||||
|
||||
* premake5 : Requires recent `premake5`(alpha12 or later) for macosx and linux. `premake5` for windows is included in `$tinygltf/tools/window` directory.
|
||||
* GLEW
|
||||
* Ubuntu 16.04: sudo apt install libglew-dev
|
||||
* glfw3
|
||||
* Ubuntu 16.04: sudo apt install libglfw3-dev
|
||||
|
||||
### MacOSX and Linux
|
||||
|
||||
|
||||
# optional. set pkg-config path to find glfw3
|
||||
$ export PKG_CONFIG_PATH=/path/to/pkgconfig
|
||||
|
||||
> premake4 gmake
|
||||
$ make
|
||||
|
||||
### Windows(not tested well)
|
||||
|
||||
Edit glew and glfw path in `premake5.lua`, then
|
||||
|
||||
> premake5.exe vs2013
|
||||
|
||||
Open .sln in Visual Studio 2013
|
||||
|
||||
When running .exe, glew and glfw dll must exist in the working directory.
|
||||
|
||||
## TODO
|
||||
|
||||
* [ ] PBR Material
|
||||
* [ ] PBR Texture.
|
||||
* [ ] Animation
|
|
@ -0,0 +1,861 @@
|
|||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#define GLFW_INCLUDE_GLU
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "trackball.h"
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
|
||||
#define CheckGLErrors(desc) \
|
||||
{ \
|
||||
GLenum e = glGetError(); \
|
||||
if (e != GL_NO_ERROR) { \
|
||||
printf("OpenGL error in \"%s\": %d (%d) %s:%d\n", desc, e, e, __FILE__, \
|
||||
__LINE__); \
|
||||
exit(20); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CAM_Z (3.0f)
|
||||
int width = 768;
|
||||
int height = 768;
|
||||
|
||||
double prevMouseX, prevMouseY;
|
||||
bool mouseLeftPressed;
|
||||
bool mouseMiddlePressed;
|
||||
bool mouseRightPressed;
|
||||
float curr_quat[4];
|
||||
float prev_quat[4];
|
||||
float eye[3], lookat[3], up[3];
|
||||
|
||||
GLFWwindow *window;
|
||||
|
||||
typedef struct { GLuint vb; } GLBufferState;
|
||||
|
||||
typedef struct {
|
||||
std::vector<GLuint> diffuseTex; // for each primitive in mesh
|
||||
} GLMeshState;
|
||||
|
||||
typedef struct {
|
||||
std::map<std::string, GLint> attribs;
|
||||
std::map<std::string, GLint> uniforms;
|
||||
} GLProgramState;
|
||||
|
||||
typedef struct {
|
||||
GLuint vb; // vertex buffer
|
||||
size_t count; // byte count
|
||||
} GLCurvesState;
|
||||
|
||||
std::map<int, GLBufferState> gBufferState;
|
||||
std::map<std::string, GLMeshState> gMeshState;
|
||||
std::map<int, GLCurvesState> gCurvesMesh;
|
||||
GLProgramState gGLProgramState;
|
||||
|
||||
void CheckErrors(std::string desc) {
|
||||
GLenum e = glGetError();
|
||||
if (e != GL_NO_ERROR) {
|
||||
fprintf(stderr, "OpenGL error in \"%s\": %d (%d)\n", desc.c_str(), e, e);
|
||||
exit(20);
|
||||
}
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
bool LoadShader(GLenum shaderType, // GL_VERTEX_SHADER or GL_FRAGMENT_SHADER(or
|
||||
// maybe GL_COMPUTE_SHADER)
|
||||
GLuint &shader, const char *shaderSourceFilename) {
|
||||
GLint val = 0;
|
||||
|
||||
// free old shader/program
|
||||
if (shader != 0) {
|
||||
glDeleteShader(shader);
|
||||
}
|
||||
|
||||
std::vector<GLchar> srcbuf;
|
||||
FILE *fp = fopen(shaderSourceFilename, "rb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "failed to load shader: %s\n", shaderSourceFilename);
|
||||
return false;
|
||||
}
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t len = ftell(fp);
|
||||
rewind(fp);
|
||||
srcbuf.resize(len + 1);
|
||||
len = fread(&srcbuf.at(0), 1, len, fp);
|
||||
srcbuf[len] = 0;
|
||||
fclose(fp);
|
||||
|
||||
const GLchar *srcs[1];
|
||||
srcs[0] = &srcbuf.at(0);
|
||||
|
||||
shader = glCreateShader(shaderType);
|
||||
glShaderSource(shader, 1, srcs, NULL);
|
||||
glCompileShader(shader);
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &val);
|
||||
if (val != GL_TRUE) {
|
||||
char log[4096];
|
||||
GLsizei msglen;
|
||||
glGetShaderInfoLog(shader, 4096, &msglen, log);
|
||||
printf("%s\n", log);
|
||||
// assert(val == GL_TRUE && "failed to compile shader");
|
||||
printf("ERR: Failed to load or compile shader [ %s ]\n",
|
||||
shaderSourceFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Load shader [ %s ] OK\n", shaderSourceFilename);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinkShader(GLuint &prog, GLuint &vertShader, GLuint &fragShader) {
|
||||
GLint val = 0;
|
||||
|
||||
if (prog != 0) {
|
||||
glDeleteProgram(prog);
|
||||
}
|
||||
|
||||
prog = glCreateProgram();
|
||||
|
||||
glAttachShader(prog, vertShader);
|
||||
glAttachShader(prog, fragShader);
|
||||
glLinkProgram(prog);
|
||||
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &val);
|
||||
assert(val == GL_TRUE && "failed to link shader");
|
||||
|
||||
printf("Link shader OK\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void reshapeFunc(GLFWwindow *window, int w, int h) {
|
||||
(void)window;
|
||||
int fb_w, fb_h;
|
||||
glfwGetFramebufferSize(window, &fb_w, &fb_h);
|
||||
glViewport(0, 0, fb_w, fb_h);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluPerspective(45.0, (float)w / (float)h, 0.1f, 1000.0f);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
width = w;
|
||||
height = h;
|
||||
}
|
||||
|
||||
void keyboardFunc(GLFWwindow *window, int key, int scancode, int action,
|
||||
int mods) {
|
||||
(void)scancode;
|
||||
(void)mods;
|
||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||
// Close window
|
||||
if (key == GLFW_KEY_Q || key == GLFW_KEY_ESCAPE) {
|
||||
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clickFunc(GLFWwindow *window, int button, int action, int mods) {
|
||||
double x, y;
|
||||
glfwGetCursorPos(window, &x, &y);
|
||||
|
||||
bool shiftPressed = (mods & GLFW_MOD_SHIFT);
|
||||
bool ctrlPressed = (mods & GLFW_MOD_CONTROL);
|
||||
|
||||
if ((button == GLFW_MOUSE_BUTTON_LEFT) && (!shiftPressed) && (!ctrlPressed)) {
|
||||
mouseLeftPressed = true;
|
||||
mouseMiddlePressed = false;
|
||||
mouseRightPressed = false;
|
||||
if (action == GLFW_PRESS) {
|
||||
int id = -1;
|
||||
// int id = ui.Proc(x, y);
|
||||
if (id < 0) { // outside of UI
|
||||
trackball(prev_quat, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
mouseLeftPressed = false;
|
||||
}
|
||||
}
|
||||
if ((button == GLFW_MOUSE_BUTTON_RIGHT) ||
|
||||
((button == GLFW_MOUSE_BUTTON_LEFT) && ctrlPressed)) {
|
||||
if (action == GLFW_PRESS) {
|
||||
mouseRightPressed = true;
|
||||
mouseLeftPressed = false;
|
||||
mouseMiddlePressed = false;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
mouseRightPressed = false;
|
||||
}
|
||||
}
|
||||
if ((button == GLFW_MOUSE_BUTTON_MIDDLE) ||
|
||||
((button == GLFW_MOUSE_BUTTON_LEFT) && shiftPressed)) {
|
||||
if (action == GLFW_PRESS) {
|
||||
mouseMiddlePressed = true;
|
||||
mouseLeftPressed = false;
|
||||
mouseRightPressed = false;
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
mouseMiddlePressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void motionFunc(GLFWwindow *window, double mouse_x, double mouse_y) {
|
||||
(void)window;
|
||||
float rotScale = 1.0f;
|
||||
float transScale = 2.0f;
|
||||
|
||||
if (mouseLeftPressed) {
|
||||
trackball(prev_quat, rotScale * (2.0f * prevMouseX - width) / (float)width,
|
||||
rotScale * (height - 2.0f * prevMouseY) / (float)height,
|
||||
rotScale * (2.0f * mouse_x - width) / (float)width,
|
||||
rotScale * (height - 2.0f * mouse_y) / (float)height);
|
||||
|
||||
add_quats(prev_quat, curr_quat, curr_quat);
|
||||
} else if (mouseMiddlePressed) {
|
||||
eye[0] += -transScale * (mouse_x - prevMouseX) / (float)width;
|
||||
lookat[0] += -transScale * (mouse_x - prevMouseX) / (float)width;
|
||||
eye[1] += transScale * (mouse_y - prevMouseY) / (float)height;
|
||||
lookat[1] += transScale * (mouse_y - prevMouseY) / (float)height;
|
||||
} else if (mouseRightPressed) {
|
||||
eye[2] += transScale * (mouse_y - prevMouseY) / (float)height;
|
||||
lookat[2] += transScale * (mouse_y - prevMouseY) / (float)height;
|
||||
}
|
||||
|
||||
// Update mouse point
|
||||
prevMouseX = mouse_x;
|
||||
prevMouseY = mouse_y;
|
||||
}
|
||||
|
||||
static void SetupMeshState(tinygltf::Model &model, GLuint progId) {
|
||||
// Buffer
|
||||
{
|
||||
for (size_t i = 0; i < model.bufferViews.size(); i++) {
|
||||
const tinygltf::BufferView &bufferView = model.bufferViews[i];
|
||||
if (bufferView.target == 0) {
|
||||
std::cout << "WARN: bufferView.target is zero" << std::endl;
|
||||
continue; // Unsupported bufferView.
|
||||
}
|
||||
|
||||
const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer];
|
||||
GLBufferState state;
|
||||
glGenBuffers(1, &state.vb);
|
||||
glBindBuffer(bufferView.target, state.vb);
|
||||
std::cout << "buffer.size= " << buffer.data.size()
|
||||
<< ", byteOffset = " << bufferView.byteOffset << std::endl;
|
||||
glBufferData(bufferView.target, bufferView.byteLength,
|
||||
&buffer.data.at(0) + bufferView.byteOffset, GL_STATIC_DRAW);
|
||||
glBindBuffer(bufferView.target, 0);
|
||||
|
||||
gBufferState[i] = state;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // TODO(syoyo): Implement
|
||||
// Texture
|
||||
{
|
||||
for (size_t i = 0; i < model.meshes.size(); i++) {
|
||||
const tinygltf::Mesh &mesh = model.meshes[i];
|
||||
|
||||
gMeshState[mesh.name].diffuseTex.resize(mesh.primitives.size());
|
||||
for (size_t primId = 0; primId < mesh.primitives.size(); primId++) {
|
||||
const tinygltf::Primitive &primitive = mesh.primitives[primId];
|
||||
|
||||
gMeshState[mesh.name].diffuseTex[primId] = 0;
|
||||
|
||||
if (primitive.material < 0) {
|
||||
continue;
|
||||
}
|
||||
tinygltf::Material &mat = model.materials[primitive.material];
|
||||
// printf("material.name = %s\n", mat.name.c_str());
|
||||
if (mat.values.find("diffuse") != mat.values.end()) {
|
||||
std::string diffuseTexName = mat.values["diffuse"].string_value;
|
||||
if (model.textures.find(diffuseTexName) != model.textures.end()) {
|
||||
tinygltf::Texture &tex = model.textures[diffuseTexName];
|
||||
if (scene.images.find(tex.source) != model.images.end()) {
|
||||
tinygltf::Image &image = model.images[tex.source];
|
||||
GLuint texId;
|
||||
glGenTextures(1, &texId);
|
||||
glBindTexture(tex.target, texId);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexParameterf(tex.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(tex.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// Ignore Texture.fomat.
|
||||
GLenum format = GL_RGBA;
|
||||
if (image.component == 3) {
|
||||
format = GL_RGB;
|
||||
}
|
||||
glTexImage2D(tex.target, 0, tex.internalFormat, image.width,
|
||||
image.height, 0, format, tex.type,
|
||||
&image.image.at(0));
|
||||
|
||||
CheckErrors("texImage2D");
|
||||
glBindTexture(tex.target, 0);
|
||||
|
||||
printf("TexId = %d\n", texId);
|
||||
gMeshState[mesh.name].diffuseTex[primId] = texId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
glUseProgram(progId);
|
||||
GLint vtloc = glGetAttribLocation(progId, "in_vertex");
|
||||
GLint nrmloc = glGetAttribLocation(progId, "in_normal");
|
||||
GLint uvloc = glGetAttribLocation(progId, "in_texcoord");
|
||||
|
||||
// GLint diffuseTexLoc = glGetUniformLocation(progId, "diffuseTex");
|
||||
GLint isCurvesLoc = glGetUniformLocation(progId, "uIsCurves");
|
||||
|
||||
gGLProgramState.attribs["POSITION"] = vtloc;
|
||||
gGLProgramState.attribs["NORMAL"] = nrmloc;
|
||||
gGLProgramState.attribs["TEXCOORD_0"] = uvloc;
|
||||
// gGLProgramState.uniforms["diffuseTex"] = diffuseTexLoc;
|
||||
gGLProgramState.uniforms["isCurvesLoc"] = isCurvesLoc;
|
||||
};
|
||||
|
||||
#if 0 // TODO(syoyo): Implement
|
||||
// Setup curves geometry extension
|
||||
static void SetupCurvesState(tinygltf::Scene &scene, GLuint progId) {
|
||||
// Find curves primitive.
|
||||
{
|
||||
std::map<std::string, tinygltf::Mesh>::const_iterator it(
|
||||
scene.meshes.begin());
|
||||
std::map<std::string, tinygltf::Mesh>::const_iterator itEnd(
|
||||
scene.meshes.end());
|
||||
|
||||
for (; it != itEnd; it++) {
|
||||
const tinygltf::Mesh &mesh = it->second;
|
||||
|
||||
// Currently we only support one primitive per mesh.
|
||||
if (mesh.primitives.size() > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t primId = 0; primId < mesh.primitives.size(); primId++) {
|
||||
const tinygltf::Primitive &primitive = mesh.primitives[primId];
|
||||
|
||||
gMeshState[mesh.name].diffuseTex[primId] = 0;
|
||||
|
||||
if (primitive.material.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool has_curves = false;
|
||||
if (primitive.extras.IsObject()) {
|
||||
if (primitive.extras.Has("ext_mode")) {
|
||||
const tinygltf::Value::Object &o =
|
||||
primitive.extras.Get<tinygltf::Value::Object>();
|
||||
const tinygltf::Value &ext_mode = o.find("ext_mode")->second;
|
||||
|
||||
if (ext_mode.IsString()) {
|
||||
const std::string &str = ext_mode.Get<std::string>();
|
||||
if (str.compare("curves") == 0) {
|
||||
has_curves = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_curves) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Construct curves buffer
|
||||
const tinygltf::Accessor &vtx_accessor =
|
||||
scene.accessors[primitive.attributes.find("POSITION")->second];
|
||||
const tinygltf::Accessor &nverts_accessor =
|
||||
scene.accessors[primitive.attributes.find("NVERTS")->second];
|
||||
const tinygltf::BufferView &vtx_bufferView =
|
||||
scene.bufferViews[vtx_accessor.bufferView];
|
||||
const tinygltf::BufferView &nverts_bufferView =
|
||||
scene.bufferViews[nverts_accessor.bufferView];
|
||||
const tinygltf::Buffer &vtx_buffer =
|
||||
scene.buffers[vtx_bufferView.buffer];
|
||||
const tinygltf::Buffer &nverts_buffer =
|
||||
scene.buffers[nverts_bufferView.buffer];
|
||||
|
||||
// std::cout << "vtx_bufferView = " << vtx_accessor.bufferView <<
|
||||
// std::endl;
|
||||
// std::cout << "nverts_bufferView = " << nverts_accessor.bufferView <<
|
||||
// std::endl;
|
||||
// std::cout << "vtx_buffer.size = " << vtx_buffer.data.size() <<
|
||||
// std::endl;
|
||||
// std::cout << "nverts_buffer.size = " << nverts_buffer.data.size() <<
|
||||
// std::endl;
|
||||
|
||||
const int *nverts =
|
||||
reinterpret_cast<const int *>(nverts_buffer.data.data());
|
||||
const float *vtx =
|
||||
reinterpret_cast<const float *>(vtx_buffer.data.data());
|
||||
|
||||
// Convert to GL_LINES data.
|
||||
std::vector<float> line_pts;
|
||||
size_t vtx_offset = 0;
|
||||
for (int k = 0; k < static_cast<int>(nverts_accessor.count); k++) {
|
||||
for (int n = 0; n < nverts[k] - 1; n++) {
|
||||
|
||||
line_pts.push_back(vtx[3 * (vtx_offset + n) + 0]);
|
||||
line_pts.push_back(vtx[3 * (vtx_offset + n) + 1]);
|
||||
line_pts.push_back(vtx[3 * (vtx_offset + n) + 2]);
|
||||
|
||||
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 0]);
|
||||
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 1]);
|
||||
line_pts.push_back(vtx[3 * (vtx_offset + n + 1) + 2]);
|
||||
|
||||
// std::cout << "p0 " << vtx[3 * (vtx_offset + n) + 0] << ", "
|
||||
// << vtx[3 * (vtx_offset + n) + 1] << ", "
|
||||
// << vtx[3 * (vtx_offset + n) + 2] << std::endl;
|
||||
|
||||
// std::cout << "p1 " << vtx[3 * (vtx_offset + n+1) + 0] << ", "
|
||||
// << vtx[3 * (vtx_offset + n+1) + 1] << ", "
|
||||
// << vtx[3 * (vtx_offset + n+1) + 2] << std::endl;
|
||||
}
|
||||
|
||||
vtx_offset += nverts[k];
|
||||
}
|
||||
|
||||
GLCurvesState state;
|
||||
glGenBuffers(1, &state.vb);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, state.vb);
|
||||
glBufferData(GL_ARRAY_BUFFER, line_pts.size() * sizeof(float),
|
||||
line_pts.data(), GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
state.count = line_pts.size() / 3;
|
||||
gCurvesMesh[mesh.name] = state;
|
||||
|
||||
// Material
|
||||
tinygltf::Material &mat = scene.materials[primitive.material];
|
||||
// printf("material.name = %s\n", mat.name.c_str());
|
||||
if (mat.values.find("diffuse") != mat.values.end()) {
|
||||
std::string diffuseTexName = mat.values["diffuse"].string_value;
|
||||
if (scene.textures.find(diffuseTexName) != scene.textures.end()) {
|
||||
tinygltf::Texture &tex = scene.textures[diffuseTexName];
|
||||
if (scene.images.find(tex.source) != scene.images.end()) {
|
||||
tinygltf::Image &image = scene.images[tex.source];
|
||||
GLuint texId;
|
||||
glGenTextures(1, &texId);
|
||||
glBindTexture(tex.target, texId);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexParameterf(tex.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(tex.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// Ignore Texture.fomat.
|
||||
GLenum format = GL_RGBA;
|
||||
if (image.component == 3) {
|
||||
format = GL_RGB;
|
||||
}
|
||||
glTexImage2D(tex.target, 0, tex.internalFormat, image.width,
|
||||
image.height, 0, format, tex.type,
|
||||
&image.image.at(0));
|
||||
|
||||
CheckErrors("texImage2D");
|
||||
glBindTexture(tex.target, 0);
|
||||
|
||||
printf("TexId = %d\n", texId);
|
||||
gMeshState[mesh.name].diffuseTex[primId] = texId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glUseProgram(progId);
|
||||
GLint vtloc = glGetAttribLocation(progId, "in_vertex");
|
||||
GLint nrmloc = glGetAttribLocation(progId, "in_normal");
|
||||
GLint uvloc = glGetAttribLocation(progId, "in_texcoord");
|
||||
|
||||
GLint diffuseTexLoc = glGetUniformLocation(progId, "diffuseTex");
|
||||
GLint isCurvesLoc = glGetUniformLocation(progId, "uIsCurves");
|
||||
|
||||
gGLProgramState.attribs["POSITION"] = vtloc;
|
||||
gGLProgramState.attribs["NORMAL"] = nrmloc;
|
||||
gGLProgramState.attribs["TEXCOORD_0"] = uvloc;
|
||||
gGLProgramState.uniforms["diffuseTex"] = diffuseTexLoc;
|
||||
gGLProgramState.uniforms["uIsCurves"] = isCurvesLoc;
|
||||
};
|
||||
#endif
|
||||
|
||||
static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) {
|
||||
//// Skip curves primitive.
|
||||
// if (gCurvesMesh.find(mesh.name) != gCurvesMesh.end()) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
// if (gGLProgramState.uniforms["diffuseTex"] >= 0) {
|
||||
// glUniform1i(gGLProgramState.uniforms["diffuseTex"], 0); // TEXTURE0
|
||||
//}
|
||||
|
||||
if (gGLProgramState.uniforms["isCurvesLoc"] >= 0) {
|
||||
glUniform1i(gGLProgramState.uniforms["isCurvesLoc"], 0);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mesh.primitives.size(); i++) {
|
||||
const tinygltf::Primitive &primitive = mesh.primitives[i];
|
||||
|
||||
if (primitive.indices < 0) return;
|
||||
|
||||
// Assume TEXTURE_2D target for the texture object.
|
||||
// glBindTexture(GL_TEXTURE_2D, gMeshState[mesh.name].diffuseTex[i]);
|
||||
|
||||
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++) {
|
||||
assert(it->second >= 0);
|
||||
const tinygltf::Accessor &accessor = model.accessors[it->second];
|
||||
glBindBuffer(GL_ARRAY_BUFFER, gBufferState[accessor.bufferView].vb);
|
||||
CheckErrors("bind buffer");
|
||||
int size = 1;
|
||||
if (accessor.type == TINYGLTF_TYPE_SCALAR) {
|
||||
size = 1;
|
||||
} else if (accessor.type == TINYGLTF_TYPE_VEC2) {
|
||||
size = 2;
|
||||
} else if (accessor.type == TINYGLTF_TYPE_VEC3) {
|
||||
size = 3;
|
||||
} else if (accessor.type == TINYGLTF_TYPE_VEC4) {
|
||||
size = 4;
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
// it->first would be "POSITION", "NORMAL", "TEXCOORD_0", ...
|
||||
if ((it->first.compare("POSITION") == 0) ||
|
||||
(it->first.compare("NORMAL") == 0) ||
|
||||
(it->first.compare("TEXCOORD_0") == 0)) {
|
||||
if (gGLProgramState.attribs[it->first] >= 0) {
|
||||
// Compute byteStride from Accessor + BufferView combination.
|
||||
int byteStride = accessor.ByteStride(model.bufferViews[accessor.bufferView]);
|
||||
assert(byteStride != -1);
|
||||
glVertexAttribPointer(gGLProgramState.attribs[it->first], size,
|
||||
accessor.componentType, accessor.normalized ? GL_TRUE : GL_FALSE,
|
||||
byteStride,
|
||||
BUFFER_OFFSET(accessor.byteOffset));
|
||||
CheckErrors("vertex attrib pointer");
|
||||
glEnableVertexAttribArray(gGLProgramState.attribs[it->first]);
|
||||
CheckErrors("enable vertex attrib array");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tinygltf::Accessor &indexAccessor =
|
||||
model.accessors[primitive.indices];
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
|
||||
gBufferState[indexAccessor.bufferView].vb);
|
||||
CheckErrors("bind buffer");
|
||||
int mode = -1;
|
||||
if (primitive.mode == TINYGLTF_MODE_TRIANGLES) {
|
||||
mode = GL_TRIANGLES;
|
||||
} else if (primitive.mode == TINYGLTF_MODE_TRIANGLE_STRIP) {
|
||||
mode = GL_TRIANGLE_STRIP;
|
||||
} else if (primitive.mode == TINYGLTF_MODE_TRIANGLE_FAN) {
|
||||
mode = GL_TRIANGLE_FAN;
|
||||
} else if (primitive.mode == TINYGLTF_MODE_POINTS) {
|
||||
mode = GL_POINTS;
|
||||
} else if (primitive.mode == TINYGLTF_MODE_LINE) {
|
||||
mode = GL_LINES;
|
||||
} else if (primitive.mode == TINYGLTF_MODE_LINE_LOOP) {
|
||||
mode = GL_LINE_LOOP;
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
glDrawElements(mode, indexAccessor.count, indexAccessor.componentType,
|
||||
BUFFER_OFFSET(indexAccessor.byteOffset));
|
||||
CheckErrors("draw elements");
|
||||
|
||||
{
|
||||
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++) {
|
||||
if ((it->first.compare("POSITION") == 0) ||
|
||||
(it->first.compare("NORMAL") == 0) ||
|
||||
(it->first.compare("TEXCOORD_0") == 0)) {
|
||||
if (gGLProgramState.attribs[it->first] >= 0) {
|
||||
glDisableVertexAttribArray(gGLProgramState.attribs[it->first]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // TODO(syoyo): Implement
|
||||
static void DrawCurves(tinygltf::Scene &scene, const tinygltf::Mesh &mesh) {
|
||||
(void)scene;
|
||||
|
||||
if (gCurvesMesh.find(mesh.name) == gCurvesMesh.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gGLProgramState.uniforms["isCurvesLoc"] >= 0) {
|
||||
glUniform1i(gGLProgramState.uniforms["isCurvesLoc"], 1);
|
||||
}
|
||||
|
||||
GLCurvesState &state = gCurvesMesh[mesh.name];
|
||||
|
||||
if (gGLProgramState.attribs["POSITION"] >= 0) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, state.vb);
|
||||
glVertexAttribPointer(gGLProgramState.attribs["POSITION"], 3, GL_FLOAT,
|
||||
GL_FALSE, /* stride */ 0, BUFFER_OFFSET(0));
|
||||
CheckErrors("curve: vertex attrib pointer");
|
||||
glEnableVertexAttribArray(gGLProgramState.attribs["POSITION"]);
|
||||
CheckErrors("curve: enable vertex attrib array");
|
||||
}
|
||||
|
||||
glDrawArrays(GL_LINES, 0, state.count);
|
||||
|
||||
if (gGLProgramState.attribs["POSITION"] >= 0) {
|
||||
glDisableVertexAttribArray(gGLProgramState.attribs["POSITION"]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Hierarchically draw nodes
|
||||
static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
|
||||
// Apply xform
|
||||
|
||||
glPushMatrix();
|
||||
if (node.matrix.size() == 16) {
|
||||
// Use `matrix' attribute
|
||||
glMultMatrixd(node.matrix.data());
|
||||
} else {
|
||||
// Assume Trans x Rotate x Scale order
|
||||
if (node.scale.size() == 3) {
|
||||
glScaled(node.scale[0], node.scale[1], node.scale[2]);
|
||||
}
|
||||
|
||||
if (node.rotation.size() == 4) {
|
||||
glRotated(node.rotation[0], node.rotation[1], node.rotation[2],
|
||||
node.rotation[3]);
|
||||
}
|
||||
|
||||
if (node.translation.size() == 3) {
|
||||
glTranslated(node.translation[0], node.translation[1],
|
||||
node.translation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
// std::cout << "node " << node.name << ", Meshes " << node.meshes.size() <<
|
||||
// std::endl;
|
||||
|
||||
// std::cout << it->first << std::endl;
|
||||
// FIXME(syoyo): Refactor.
|
||||
// DrawCurves(scene, it->second);
|
||||
DrawMesh(model, model.meshes[node.mesh]);
|
||||
|
||||
// Draw child nodes.
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
DrawNode(model, model.nodes[node.children[i]]);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
static void DrawModel(tinygltf::Model &model) {
|
||||
#if 0
|
||||
std::map<std::string, tinygltf::Mesh>::const_iterator it(scene.meshes.begin());
|
||||
std::map<std::string, tinygltf::Mesh>::const_iterator itEnd(scene.meshes.end());
|
||||
|
||||
for (; it != itEnd; it++) {
|
||||
DrawMesh(scene, it->second);
|
||||
DrawCurves(scene, it->second);
|
||||
}
|
||||
#else
|
||||
|
||||
// TODO(syoyo): Support non-default scenes.
|
||||
assert(model.defaultScene >= 0);
|
||||
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
|
||||
for (size_t i = 0; i < scene.nodes.size(); i++) {
|
||||
DrawNode(model, model.nodes[scene.nodes[i]]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Init() {
|
||||
trackball(curr_quat, 0, 0, 0, 0);
|
||||
|
||||
eye[0] = 0.0f;
|
||||
eye[1] = 0.0f;
|
||||
eye[2] = CAM_Z;
|
||||
|
||||
lookat[0] = 0.0f;
|
||||
lookat[1] = 0.0f;
|
||||
lookat[2] = 0.0f;
|
||||
|
||||
up[0] = 0.0f;
|
||||
up[1] = 1.0f;
|
||||
up[2] = 0.0f;
|
||||
}
|
||||
|
||||
static void PrintNodes(const tinygltf::Scene &scene) {
|
||||
for (size_t i = 0; i < scene.nodes.size(); i++) {
|
||||
std::cout << "node.name : " << scene.nodes[i] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "glview input.gltf <scale>\n" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
float scale = 1.0f;
|
||||
if (argc > 2) {
|
||||
scale = atof(argv[2]);
|
||||
}
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string err;
|
||||
std::string input_filename(argv[1]);
|
||||
std::string ext = GetFilePathExtension(input_filename);
|
||||
|
||||
bool ret = false;
|
||||
if (ext.compare("glb") == 0) {
|
||||
// assume binary glTF.
|
||||
ret = loader.LoadBinaryFromFile(&model, &err, input_filename.c_str());
|
||||
} else {
|
||||
// assume ascii glTF.
|
||||
ret = loader.LoadASCIIFromFile(&model, &err, input_filename.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);
|
||||
}
|
||||
|
||||
Init();
|
||||
|
||||
// DBG
|
||||
PrintNodes(model.scenes[model.defaultScene]);
|
||||
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "Failed to initialize GLFW." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char title[1024];
|
||||
sprintf(title, "Simple glTF viewer: %s", input_filename.c_str());
|
||||
|
||||
window = glfwCreateWindow(width, height, title, NULL, NULL);
|
||||
if (window == NULL) {
|
||||
std::cerr << "Failed to open GLFW window. " << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
|
||||
glfwGetWindowSize(window, &width, &height);
|
||||
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
// Callback
|
||||
glfwSetWindowSizeCallback(window, reshapeFunc);
|
||||
glfwSetKeyCallback(window, keyboardFunc);
|
||||
glfwSetMouseButtonCallback(window, clickFunc);
|
||||
glfwSetCursorPosCallback(window, motionFunc);
|
||||
|
||||
glewExperimental = true; // This may be only true for linux environment.
|
||||
if (glewInit() != GLEW_OK) {
|
||||
std::cerr << "Failed to initialize GLEW." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
reshapeFunc(window, width, height);
|
||||
|
||||
GLuint vertId = 0, fragId = 0, progId = 0;
|
||||
if (false == LoadShader(GL_VERTEX_SHADER, vertId, "shader.vert")) {
|
||||
return -1;
|
||||
}
|
||||
CheckErrors("load vert shader");
|
||||
|
||||
if (false == LoadShader(GL_FRAGMENT_SHADER, fragId, "shader.frag")) {
|
||||
return -1;
|
||||
}
|
||||
CheckErrors("load frag shader");
|
||||
|
||||
if (false == LinkShader(progId, vertId, fragId)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
CheckErrors("link");
|
||||
|
||||
{
|
||||
// At least `in_vertex` should be used in the shader.
|
||||
GLint vtxLoc = glGetAttribLocation(progId, "in_vertex");
|
||||
if (vtxLoc < 0) {
|
||||
printf("vertex loc not found.\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
glUseProgram(progId);
|
||||
CheckErrors("useProgram");
|
||||
|
||||
SetupMeshState(model, progId);
|
||||
// SetupCurvesState(model, progId);
|
||||
CheckErrors("SetupGLState");
|
||||
|
||||
std::cout << "# of meshes = " << model.meshes.size() << std::endl;
|
||||
|
||||
while (glfwWindowShouldClose(window) == GL_FALSE) {
|
||||
glfwPollEvents();
|
||||
glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
GLfloat mat[4][4];
|
||||
build_rotmatrix(mat, curr_quat);
|
||||
|
||||
// camera(define it in projection matrix)
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
gluLookAt(eye[0], eye[1], eye[2], lookat[0], lookat[1], lookat[2], up[0],
|
||||
up[1], up[2]);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glMultMatrixf(&mat[0][0]);
|
||||
|
||||
glScalef(scale, scale, scale);
|
||||
|
||||
DrawModel(model);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
|
||||
glFlush();
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
glfwTerminate();
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
solution "glview"
|
||||
-- location ( "build" )
|
||||
configurations { "Debug", "Release" }
|
||||
platforms {"native", "x64", "x32"}
|
||||
|
||||
project "glview"
|
||||
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
cppdialect "C++11"
|
||||
files { "glview.cc", "trackball.cc" }
|
||||
includedirs { "./" }
|
||||
includedirs { "../../" }
|
||||
|
||||
configuration { "linux" }
|
||||
linkoptions { "`pkg-config --libs glfw3`" }
|
||||
links { "GL", "GLU", "m", "GLEW", "X11", "Xrandr", "Xinerama", "Xi", "Xxf86vm", "Xcursor", "dl" }
|
||||
|
||||
configuration { "windows" }
|
||||
-- Edit path to glew and GLFW3 fit to your environment.
|
||||
includedirs { "../../../../local/glew-1.13.0/include/" }
|
||||
includedirs { "../../../../local/glfw-3.2.bin.WIN32/include/" }
|
||||
libdirs { "../../../../local/glew-1.13.0/lib/Release/Win32/" }
|
||||
libdirs { "../../../../local/glfw-3.2.bin.WIN32/lib-vc2013/" }
|
||||
links { "glfw3", "gdi32", "winmm", "user32", "glew32", "glu32","opengl32", "kernel32" }
|
||||
defines { "_CRT_SECURE_NO_WARNINGS" }
|
||||
|
||||
configuration { "macosx" }
|
||||
includedirs { "/usr/local/include" }
|
||||
buildoptions { "-Wno-deprecated-declarations" }
|
||||
libdirs { "/usr/local/lib" }
|
||||
links { "glfw3", "GLEW" }
|
||||
linkoptions { "-framework OpenGL", "-framework Cocoa", "-framework IOKit", "-framework CoreVideo" }
|
||||
|
||||
configuration "Debug"
|
||||
defines { "DEBUG" }
|
||||
symbols "On"
|
||||
warnings "Extra"
|
||||
|
||||
configuration "Release"
|
||||
defines { "NDEBUG" }
|
||||
optimize "On"
|
||||
warnings "Extra"
|
|
@ -0,0 +1,16 @@
|
|||
uniform sampler2D diffuseTex;
|
||||
uniform int uIsCurve;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec2 texcoord;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
//gl_FragColor = vec4(0.5 * normalize(normal) + 0.5, 1.0);
|
||||
//gl_FragColor = vec4(texcoord, 0.0, 1.0);
|
||||
if (uIsCurve > 0) {
|
||||
gl_FragColor = texture2D(diffuseTex, texcoord);
|
||||
} else {
|
||||
gl_FragColor = vec4(0.5 * normalize(normal) + 0.5, 1.0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
attribute vec3 in_vertex;
|
||||
attribute vec3 in_normal;
|
||||
attribute vec2 in_texcoord;
|
||||
|
||||
varying vec3 normal;
|
||||
varying vec2 texcoord;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vec4 p = gl_ModelViewProjectionMatrix * vec4(in_vertex, 1);
|
||||
gl_Position = p;
|
||||
vec4 nn = gl_ModelViewMatrixInverseTranspose * vec4(normalize(in_normal), 0);
|
||||
normal = nn.xyz;
|
||||
|
||||
texcoord = in_texcoord;
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* (c) Copyright 1993, 1994, Silicon Graphics, Inc.
|
||||
* ALL RIGHTS RESERVED
|
||||
* Permission to use, copy, modify, and distribute this software for
|
||||
* any purpose and without fee is hereby granted, provided that the above
|
||||
* copyright notice appear in all copies and that both the copyright notice
|
||||
* and this permission notice appear in supporting documentation, and that
|
||||
* the name of Silicon Graphics, Inc. not be used in advertising
|
||||
* or publicity pertaining to distribution of the software without specific,
|
||||
* written prior permission.
|
||||
*
|
||||
* THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
|
||||
* AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
|
||||
* GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
|
||||
* SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
|
||||
* KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
|
||||
* LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
|
||||
* THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* US Government Users Restricted Rights
|
||||
* Use, duplication, or disclosure by the Government is subject to
|
||||
* restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
|
||||
* (c)(1)(ii) of the Rights in Technical Data and Computer Software
|
||||
* clause at DFARS 252.227-7013 and/or in similar or successor
|
||||
* clauses in the FAR or the DOD or NASA FAR Supplement.
|
||||
* Unpublished-- rights reserved under the copyright laws of the
|
||||
* United States. Contractor/manufacturer is Silicon Graphics,
|
||||
* Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311.
|
||||
*
|
||||
* OpenGL(TM) is a trademark of Silicon Graphics, Inc.
|
||||
*/
|
||||
/*
|
||||
* Trackball code:
|
||||
*
|
||||
* Implementation of a virtual trackball.
|
||||
* Implemented by Gavin Bell, lots of ideas from Thant Tessman and
|
||||
* the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
|
||||
*
|
||||
* Vector manip code:
|
||||
*
|
||||
* Original code from:
|
||||
* David M. Ciemiewicz, Mark Grossman, Henry Moreton, and Paul Haeberli
|
||||
*
|
||||
* Much mucking with by:
|
||||
* Gavin Bell
|
||||
*/
|
||||
#include <math.h>
|
||||
#include "trackball.h"
|
||||
|
||||
/*
|
||||
* This size should really be based on the distance from the center of
|
||||
* rotation to the point on the object underneath the mouse. That
|
||||
* point would then track the mouse as closely as possible. This is a
|
||||
* simple example, though, so that is left as an Exercise for the
|
||||
* Programmer.
|
||||
*/
|
||||
#define TRACKBALLSIZE (0.8)
|
||||
|
||||
/*
|
||||
* Local function prototypes (not defined in trackball.h)
|
||||
*/
|
||||
static float tb_project_to_sphere(float, float, float);
|
||||
static void normalize_quat(float[4]);
|
||||
|
||||
static void vzero(float *v) {
|
||||
v[0] = 0.0;
|
||||
v[1] = 0.0;
|
||||
v[2] = 0.0;
|
||||
}
|
||||
|
||||
static void vset(float *v, float x, float y, float z) {
|
||||
v[0] = x;
|
||||
v[1] = y;
|
||||
v[2] = z;
|
||||
}
|
||||
|
||||
static void vsub(const float *src1, const float *src2, float *dst) {
|
||||
dst[0] = src1[0] - src2[0];
|
||||
dst[1] = src1[1] - src2[1];
|
||||
dst[2] = src1[2] - src2[2];
|
||||
}
|
||||
|
||||
static void vcopy(const float *v1, float *v2) {
|
||||
register int i;
|
||||
for (i = 0; i < 3; i++)
|
||||
v2[i] = v1[i];
|
||||
}
|
||||
|
||||
static void vcross(const float *v1, const float *v2, float *cross) {
|
||||
float temp[3];
|
||||
|
||||
temp[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
|
||||
temp[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
|
||||
temp[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
|
||||
vcopy(temp, cross);
|
||||
}
|
||||
|
||||
static float vlength(const float *v) {
|
||||
return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
||||
}
|
||||
|
||||
static void vscale(float *v, float div) {
|
||||
v[0] *= div;
|
||||
v[1] *= div;
|
||||
v[2] *= div;
|
||||
}
|
||||
|
||||
static void vnormal(float *v) { vscale(v, 1.0 / vlength(v)); }
|
||||
|
||||
static float vdot(const float *v1, const float *v2) {
|
||||
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
|
||||
}
|
||||
|
||||
static void vadd(const float *src1, const float *src2, float *dst) {
|
||||
dst[0] = src1[0] + src2[0];
|
||||
dst[1] = src1[1] + src2[1];
|
||||
dst[2] = src1[2] + src2[2];
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, simulate a track-ball. Project the points onto the virtual
|
||||
* trackball, then figure out the axis of rotation, which is the cross
|
||||
* product of P1 P2 and O P1 (O is the center of the ball, 0,0,0)
|
||||
* Note: This is a deformed trackball-- is a trackball in the center,
|
||||
* but is deformed into a hyperbolic sheet of rotation away from the
|
||||
* center. This particular function was chosen after trying out
|
||||
* several variations.
|
||||
*
|
||||
* It is assumed that the arguments to this routine are in the range
|
||||
* (-1.0 ... 1.0)
|
||||
*/
|
||||
void trackball(float q[4], float p1x, float p1y, float p2x, float p2y) {
|
||||
float a[3]; /* Axis of rotation */
|
||||
float phi; /* how much to rotate about axis */
|
||||
float p1[3], p2[3], d[3];
|
||||
float t;
|
||||
|
||||
if (p1x == p2x && p1y == p2y) {
|
||||
/* Zero rotation */
|
||||
vzero(q);
|
||||
q[3] = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* First, figure out z-coordinates for projection of P1 and P2 to
|
||||
* deformed sphere
|
||||
*/
|
||||
vset(p1, p1x, p1y, tb_project_to_sphere(TRACKBALLSIZE, p1x, p1y));
|
||||
vset(p2, p2x, p2y, tb_project_to_sphere(TRACKBALLSIZE, p2x, p2y));
|
||||
|
||||
/*
|
||||
* Now, we want the cross product of P1 and P2
|
||||
*/
|
||||
vcross(p2, p1, a);
|
||||
|
||||
/*
|
||||
* Figure out how much to rotate around that axis.
|
||||
*/
|
||||
vsub(p1, p2, d);
|
||||
t = vlength(d) / (2.0 * TRACKBALLSIZE);
|
||||
|
||||
/*
|
||||
* Avoid problems with out-of-control values...
|
||||
*/
|
||||
if (t > 1.0)
|
||||
t = 1.0;
|
||||
if (t < -1.0)
|
||||
t = -1.0;
|
||||
phi = 2.0 * asin(t);
|
||||
|
||||
axis_to_quat(a, phi, q);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an axis and angle, compute quaternion.
|
||||
*/
|
||||
void axis_to_quat(float a[3], float phi, float q[4]) {
|
||||
vnormal(a);
|
||||
vcopy(a, q);
|
||||
vscale(q, sin(phi / 2.0));
|
||||
q[3] = cos(phi / 2.0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
|
||||
* if we are away from the center of the sphere.
|
||||
*/
|
||||
static float tb_project_to_sphere(float r, float x, float y) {
|
||||
float d, t, z;
|
||||
|
||||
d = sqrt(x * x + y * y);
|
||||
if (d < r * 0.70710678118654752440) { /* Inside sphere */
|
||||
z = sqrt(r * r - d * d);
|
||||
} else { /* On hyperbola */
|
||||
t = r / 1.41421356237309504880;
|
||||
z = t * t / d;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given two rotations, e1 and e2, expressed as quaternion rotations,
|
||||
* figure out the equivalent single rotation and stuff it into dest.
|
||||
*
|
||||
* This routine also normalizes the result every RENORMCOUNT times it is
|
||||
* called, to keep error from creeping in.
|
||||
*
|
||||
* NOTE: This routine is written so that q1 or q2 may be the same
|
||||
* as dest (or each other).
|
||||
*/
|
||||
|
||||
#define RENORMCOUNT 97
|
||||
|
||||
void add_quats(float q1[4], float q2[4], float dest[4]) {
|
||||
static int count = 0;
|
||||
float t1[4], t2[4], t3[4];
|
||||
float tf[4];
|
||||
|
||||
vcopy(q1, t1);
|
||||
vscale(t1, q2[3]);
|
||||
|
||||
vcopy(q2, t2);
|
||||
vscale(t2, q1[3]);
|
||||
|
||||
vcross(q2, q1, t3);
|
||||
vadd(t1, t2, tf);
|
||||
vadd(t3, tf, tf);
|
||||
tf[3] = q1[3] * q2[3] - vdot(q1, q2);
|
||||
|
||||
dest[0] = tf[0];
|
||||
dest[1] = tf[1];
|
||||
dest[2] = tf[2];
|
||||
dest[3] = tf[3];
|
||||
|
||||
if (++count > RENORMCOUNT) {
|
||||
count = 0;
|
||||
normalize_quat(dest);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Quaternions always obey: a^2 + b^2 + c^2 + d^2 = 1.0
|
||||
* If they don't add up to 1.0, dividing by their magnitued will
|
||||
* renormalize them.
|
||||
*
|
||||
* Note: See the following for more information on quaternions:
|
||||
*
|
||||
* - Shoemake, K., Animating rotation with quaternion curves, Computer
|
||||
* Graphics 19, No 3 (Proc. SIGGRAPH'85), 245-254, 1985.
|
||||
* - Pletinckx, D., Quaternion calculus as a basic tool in computer
|
||||
* graphics, The Visual Computer 5, 2-13, 1989.
|
||||
*/
|
||||
static void normalize_quat(float q[4]) {
|
||||
int i;
|
||||
float mag;
|
||||
|
||||
mag = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
|
||||
for (i = 0; i < 4; i++)
|
||||
q[i] /= mag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a rotation matrix, given a quaternion rotation.
|
||||
*
|
||||
*/
|
||||
void build_rotmatrix(float m[4][4], const float q[4]) {
|
||||
m[0][0] = 1.0 - 2.0 * (q[1] * q[1] + q[2] * q[2]);
|
||||
m[0][1] = 2.0 * (q[0] * q[1] - q[2] * q[3]);
|
||||
m[0][2] = 2.0 * (q[2] * q[0] + q[1] * q[3]);
|
||||
m[0][3] = 0.0;
|
||||
|
||||
m[1][0] = 2.0 * (q[0] * q[1] + q[2] * q[3]);
|
||||
m[1][1] = 1.0 - 2.0 * (q[2] * q[2] + q[0] * q[0]);
|
||||
m[1][2] = 2.0 * (q[1] * q[2] - q[0] * q[3]);
|
||||
m[1][3] = 0.0;
|
||||
|
||||
m[2][0] = 2.0 * (q[2] * q[0] - q[1] * q[3]);
|
||||
m[2][1] = 2.0 * (q[1] * q[2] + q[0] * q[3]);
|
||||
m[2][2] = 1.0 - 2.0 * (q[1] * q[1] + q[0] * q[0]);
|
||||
m[2][3] = 0.0;
|
||||
|
||||
m[3][0] = 0.0;
|
||||
m[3][1] = 0.0;
|
||||
m[3][2] = 0.0;
|
||||
m[3][3] = 1.0;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* (c) Copyright 1993, 1994, Silicon Graphics, Inc.
|
||||
* ALL RIGHTS RESERVED
|
||||
* Permission to use, copy, modify, and distribute this software for
|
||||
* any purpose and without fee is hereby granted, provided that the above
|
||||
* copyright notice appear in all copies and that both the copyright notice
|
||||
* and this permission notice appear in supporting documentation, and that
|
||||
* the name of Silicon Graphics, Inc. not be used in advertising
|
||||
* or publicity pertaining to distribution of the software without specific,
|
||||
* written prior permission.
|
||||
*
|
||||
* THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
|
||||
* AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
|
||||
* GRAPHICS, INC. BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
|
||||
* SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
|
||||
* KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
|
||||
* LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
|
||||
* THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC. HAS BEEN
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
* POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* US Government Users Restricted Rights
|
||||
* Use, duplication, or disclosure by the Government is subject to
|
||||
* restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
|
||||
* (c)(1)(ii) of the Rights in Technical Data and Computer Software
|
||||
* clause at DFARS 252.227-7013 and/or in similar or successor
|
||||
* clauses in the FAR or the DOD or NASA FAR Supplement.
|
||||
* Unpublished-- rights reserved under the copyright laws of the
|
||||
* United States. Contractor/manufacturer is Silicon Graphics,
|
||||
* Inc., 2011 N. Shoreline Blvd., Mountain View, CA 94039-7311.
|
||||
*
|
||||
* OpenGL(TM) is a trademark of Silicon Graphics, Inc.
|
||||
*/
|
||||
/*
|
||||
* trackball.h
|
||||
* A virtual trackball implementation
|
||||
* Written by Gavin Bell for Silicon Graphics, November 1988.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Pass the x and y coordinates of the last and current positions of
|
||||
* the mouse, scaled so they are from (-1.0 ... 1.0).
|
||||
*
|
||||
* The resulting rotation is returned as a quaternion rotation in the
|
||||
* first paramater.
|
||||
*/
|
||||
void trackball(float q[4], float p1x, float p1y, float p2x, float p2y);
|
||||
|
||||
void negate_quat(float *q, float *qn);
|
||||
|
||||
/*
|
||||
* Given two quaternions, add them together to get a third quaternion.
|
||||
* Adding quaternions to get a compound rotation is analagous to adding
|
||||
* translations to get a compound translation. When incrementally
|
||||
* adding rotations, the first argument here should be the new
|
||||
* rotation, the second and third the total rotation (which will be
|
||||
* over-written with the resulting new total rotation).
|
||||
*/
|
||||
void add_quats(float *q1, float *q2, float *dest);
|
||||
|
||||
/*
|
||||
* A useful function, builds a rotation matrix in Matrix based on
|
||||
* given quaternion.
|
||||
*/
|
||||
void build_rotmatrix(float m[4][4], const float q[4]);
|
||||
|
||||
/*
|
||||
* This function computes a quaternion based on an axis (defined by
|
||||
* the given vector) and an angle about which to rotate. The angle is
|
||||
* expressed in radians. The result is put into the third argument.
|
||||
*/
|
||||
void axis_to_quat(float a[3], float phi, float q[4]);
|
|
@ -0,0 +1,130 @@
|
|||
# NanoSG
|
||||
|
||||
Simple, minimal and header-only scene graph library for NanoRT.
|
||||
|
||||
NanoSG itself shoud be compiled with C++-03 compiler, but demo code uses C++11 features.
|
||||
|
||||
![screenshot of the demo program](images/nanosg-demo.png)
|
||||
|
||||
![Animation showing node manipulation](https://media.giphy.com/media/l3JDO29fMFndyObHW/giphy.gif)
|
||||
|
||||
## Build
|
||||
|
||||
### Linux or macOS
|
||||
|
||||
```bash
|
||||
premake5 gmake
|
||||
make
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```bash
|
||||
premake5 vs2015
|
||||
```
|
||||
|
||||
## Data structure
|
||||
|
||||
### Node
|
||||
|
||||
Node represents scene graph node. Tansformation node or Mesh(shape) node.
|
||||
Node is interpreted as transformation node when passing `nullptr` to Node class constructure.
|
||||
|
||||
Node can contain multiple children.
|
||||
|
||||
### Scene
|
||||
|
||||
Scene contains root nodes and provides the method to find an intersection of nodes.
|
||||
|
||||
## User defined data structure
|
||||
|
||||
Following are required in user application.
|
||||
|
||||
### Mesh class
|
||||
|
||||
Current example code assumes mesh is all composed of triangle meshes.
|
||||
|
||||
Following method must be implemented for `Scene::Traversal`.
|
||||
|
||||
```cpp
|
||||
///
|
||||
/// Get the geometric normal and the shading normal at `face_idx' th face.
|
||||
///
|
||||
template<typename T>
|
||||
void GetNormal(T Ng[3], T Ns[3], const unsigned int face_idx, const T u, const T v) const;
|
||||
```
|
||||
|
||||
### Intersection class
|
||||
|
||||
Represents intersection(hit) information.
|
||||
|
||||
### Transform
|
||||
|
||||
Transformation is done in the following procedure.
|
||||
|
||||
`M' = parent_xform x local_xform x local_pivot`
|
||||
|
||||
## Memory management
|
||||
|
||||
`Scene` and `Node` does not create a copy of asset data(e.g. vertices, indices). Thus user must care about memory management of scene assets in user side.
|
||||
|
||||
## API
|
||||
|
||||
API is still subject to change.
|
||||
|
||||
### Node
|
||||
|
||||
```cpp
|
||||
void Node::SetName(const std::string &name);
|
||||
```
|
||||
|
||||
Set (unique) name for the node.
|
||||
|
||||
```cpp
|
||||
void Node::AddChild(const type &child);
|
||||
```
|
||||
|
||||
Add node as child node.
|
||||
|
||||
```cpp
|
||||
void Node::SetLocalXform(const T xform[4][4]) {
|
||||
```
|
||||
|
||||
Set local transformation matrix. Default is identity matrix.
|
||||
|
||||
### Scene
|
||||
|
||||
```cpp
|
||||
bool Scene::AddNode(const Node<T, M> &node);
|
||||
```
|
||||
|
||||
Add a node to the scene.
|
||||
|
||||
```cpp
|
||||
bool Scene::Commit() {
|
||||
```
|
||||
|
||||
Commit the scene. After adding nodes to the scene or changed transformation matrix, call this `Commit` before tracing rays.
|
||||
`Commit` triggers BVH build in each nodes and updates node's transformation matrix.
|
||||
|
||||
```cpp
|
||||
template<class H>
|
||||
bool Scene::Traverse(nanort::Ray<T> &ray, H *isect, const bool cull_back_face = false) const;
|
||||
```
|
||||
|
||||
Trace ray into the scene and find an intersection.
|
||||
Returns `true` when there is an intersection and hit information is stored in `isect`.
|
||||
|
||||
## TODO
|
||||
|
||||
* [ ] Compute pivot point of each node(mesh).
|
||||
|
||||
## Third party libraries and its icenses
|
||||
|
||||
* picojson : BSD license.
|
||||
* bt3gui : zlib license.
|
||||
* glew : BSD/MIT license.
|
||||
* tinyobjloader : MIT license.
|
||||
* glm : The Happy Bunny License (Modified MIT License). Copyright (c) 2005 - 2017 G-Truc Creation
|
||||
* ImGui : The MIT License (MIT). Copyright (c) 2014-2015 Omar Cornut and ImGui contributors
|
||||
* ImGuizmo : The MIT License (MIT). Copyright (c) 2016 Cedric Guillemet
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"commented_out_obj_filename": "cornellbox_suzanne.obj",
|
||||
"gltf_filename": "../../models/Cube/Cube.gltf",
|
||||
"scene_scale": 1.0,
|
||||
"width": 512,
|
||||
"height": 512,
|
||||
"eye": [
|
||||
0,
|
||||
2.5,
|
||||
15
|
||||
],
|
||||
"up": [
|
||||
0,
|
||||
1,
|
||||
0
|
||||
],
|
||||
"look_at": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"dummy": 0
|
||||
}
|
|
@ -0,0 +1,491 @@
|
|||
#include "gltf-loader.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory> // c++11
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#include <tiny_gltf.h>
|
||||
|
||||
namespace example {
|
||||
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 "";
|
||||
}
|
||||
|
||||
///
|
||||
/// Loads glTF 2.0 mesh
|
||||
///
|
||||
bool LoadGLTF(const std::string &filename, float scale,
|
||||
std::vector<Mesh<float> > *meshes,
|
||||
std::vector<Material> *materials,
|
||||
std::vector<Texture> *textures) {
|
||||
// TODO(syoyo): Texture
|
||||
// TODO(syoyo): Material
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string err;
|
||||
const std::string ext = GetFilePathExtension(filename);
|
||||
|
||||
bool ret = false;
|
||||
if (ext.compare("glb") == 0) {
|
||||
// assume binary glTF.
|
||||
ret = loader.LoadBinaryFromFile(&model, &err, filename.c_str());
|
||||
} else {
|
||||
// assume ascii glTF.
|
||||
ret = loader.LoadASCIIFromFile(&model, &err, filename.c_str());
|
||||
}
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cerr << "glTF parse error: " << err << std::endl;
|
||||
}
|
||||
if (!ret) {
|
||||
std::cerr << "Failed to load glTF: " << filename << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "loaded glTF file has:\n"
|
||||
<< model.accessors.size() << " accessors\n"
|
||||
<< model.animations.size() << " animations\n"
|
||||
<< model.buffers.size() << " buffers\n"
|
||||
<< model.bufferViews.size() << " bufferViews\n"
|
||||
<< model.materials.size() << " materials\n"
|
||||
<< model.meshes.size() << " meshes\n"
|
||||
<< model.nodes.size() << " nodes\n"
|
||||
<< model.textures.size() << " textures\n"
|
||||
<< model.images.size() << " images\n"
|
||||
<< model.skins.size() << " skins\n"
|
||||
<< model.samplers.size() << " samplers\n"
|
||||
<< model.cameras.size() << " cameras\n"
|
||||
<< model.scenes.size() << " scenes\n"
|
||||
<< model.lights.size() << " lights\n";
|
||||
|
||||
// Iterate through all the meshes in the glTF file
|
||||
for (const auto &gltfMesh : model.meshes) {
|
||||
std::cout << "Current mesh has " << gltfMesh.primitives.size()
|
||||
<< " primitives:\n";
|
||||
|
||||
// Create a mesh object
|
||||
Mesh<float> loadedMesh(sizeof(float) * 3);
|
||||
|
||||
// To store the min and max of the buffer (as 3D vector of floats)
|
||||
v3f pMin = {}, pMax = {};
|
||||
|
||||
// Store the name of the glTF mesh (if defined)
|
||||
loadedMesh.name = gltfMesh.name;
|
||||
|
||||
// For each primitive
|
||||
for (const auto &meshPrimitive : gltfMesh.primitives) {
|
||||
// Boolean used to check if we have converted the vertex buffer format
|
||||
bool convertedToTriangleList = false;
|
||||
// This permit to get a type agnostic way of reading the index buffer
|
||||
std::unique_ptr<intArrayBase> indicesArrayPtr = nullptr;
|
||||
{
|
||||
const auto &indicesAccessor = model.accessors[meshPrimitive.indices];
|
||||
const auto &bufferView = model.bufferViews[indicesAccessor.bufferView];
|
||||
const auto &buffer = model.buffers[bufferView.buffer];
|
||||
const auto dataAddress = buffer.data.data() + bufferView.byteOffset +
|
||||
indicesAccessor.byteOffset;
|
||||
const auto byteStride = indicesAccessor.ByteStride(bufferView);
|
||||
const auto count = indicesAccessor.count;
|
||||
|
||||
// Allocate the index array in the pointer-to-base declared in the
|
||||
// parent scope
|
||||
switch (indicesAccessor.componentType) {
|
||||
case TINYGLTF_COMPONENT_TYPE_BYTE:
|
||||
indicesArrayPtr =
|
||||
std::unique_ptr<intArray<char> >(new intArray<char>(
|
||||
arrayAdapter<char>(dataAddress, count, byteStride)));
|
||||
break;
|
||||
|
||||
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
|
||||
indicesArrayPtr = std::unique_ptr<intArray<unsigned char> >(
|
||||
new intArray<unsigned char>(arrayAdapter<unsigned char>(
|
||||
dataAddress, count, byteStride)));
|
||||
break;
|
||||
|
||||
case TINYGLTF_COMPONENT_TYPE_SHORT:
|
||||
indicesArrayPtr =
|
||||
std::unique_ptr<intArray<short> >(new intArray<short>(
|
||||
arrayAdapter<short>(dataAddress, count, byteStride)));
|
||||
break;
|
||||
|
||||
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
|
||||
indicesArrayPtr = std::unique_ptr<intArray<unsigned short> >(
|
||||
new intArray<unsigned short>(arrayAdapter<unsigned short>(
|
||||
dataAddress, count, byteStride)));
|
||||
break;
|
||||
|
||||
case TINYGLTF_COMPONENT_TYPE_INT:
|
||||
indicesArrayPtr = std::unique_ptr<intArray<int> >(new intArray<int>(
|
||||
arrayAdapter<int>(dataAddress, count, byteStride)));
|
||||
break;
|
||||
|
||||
case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
|
||||
indicesArrayPtr = std::unique_ptr<intArray<unsigned int> >(
|
||||
new intArray<unsigned int>(arrayAdapter<unsigned int>(
|
||||
dataAddress, count, byteStride)));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto &indices = *indicesArrayPtr;
|
||||
|
||||
if (indicesArrayPtr) {
|
||||
std::cout << "indices: ";
|
||||
for (size_t i(0); i < indicesArrayPtr->size(); ++i) {
|
||||
std::cout << indices[i] << " ";
|
||||
loadedMesh.faces.push_back(indices[i]);
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
switch (meshPrimitive.mode) {
|
||||
// We re-arrange the indices so that it describe a simple list of
|
||||
// triangles
|
||||
case TINYGLTF_MODE_TRIANGLE_FAN:
|
||||
if (!convertedToTriangleList) {
|
||||
std::cout << "TRIANGLE_FAN\n";
|
||||
// This only has to be done once per primitive
|
||||
convertedToTriangleList = true;
|
||||
|
||||
// We steal the guts of the vector
|
||||
auto triangleFan = std::move(loadedMesh.faces);
|
||||
loadedMesh.faces.clear();
|
||||
|
||||
// Push back the indices that describe just one triangle one by one
|
||||
for (size_t i{2}; i < triangleFan.size(); ++i) {
|
||||
loadedMesh.faces.push_back(triangleFan[0]);
|
||||
loadedMesh.faces.push_back(triangleFan[i - 1]);
|
||||
loadedMesh.faces.push_back(triangleFan[i]);
|
||||
}
|
||||
}
|
||||
case TINYGLTF_MODE_TRIANGLE_STRIP:
|
||||
if (!convertedToTriangleList) {
|
||||
std::cout << "TRIANGLE_STRIP\n";
|
||||
// This only has to be done once per primitive
|
||||
convertedToTriangleList = true;
|
||||
|
||||
auto triangleStrip = std::move(loadedMesh.faces);
|
||||
loadedMesh.faces.clear();
|
||||
|
||||
for (size_t i{2}; i < triangleStrip.size(); ++i) {
|
||||
loadedMesh.faces.push_back(triangleStrip[i - 2]);
|
||||
loadedMesh.faces.push_back(triangleStrip[i - 1]);
|
||||
loadedMesh.faces.push_back(triangleStrip[i]);
|
||||
}
|
||||
}
|
||||
case TINYGLTF_MODE_TRIANGLES: // this is the simpliest case to handle
|
||||
|
||||
{
|
||||
std::cout << "TRIANGLES\n";
|
||||
|
||||
for (const auto &attribute : meshPrimitive.attributes) {
|
||||
const auto attribAccessor = model.accessors[attribute.second];
|
||||
const auto &bufferView =
|
||||
model.bufferViews[attribAccessor.bufferView];
|
||||
const auto &buffer = model.buffers[bufferView.buffer];
|
||||
const auto dataPtr = buffer.data.data() + bufferView.byteOffset +
|
||||
attribAccessor.byteOffset;
|
||||
const auto byte_stride = attribAccessor.ByteStride(bufferView);
|
||||
const auto count = attribAccessor.count;
|
||||
|
||||
std::cout << "current attribute has count " << count
|
||||
<< " and stride " << byte_stride << " bytes\n";
|
||||
|
||||
std::cout << "attribute string is : " << attribute.first << '\n';
|
||||
if (attribute.first == "POSITION") {
|
||||
std::cout << "found position attribute\n";
|
||||
|
||||
// get the position min/max for computing the boundingbox
|
||||
pMin.x = attribAccessor.minValues[0];
|
||||
pMin.y = attribAccessor.minValues[1];
|
||||
pMin.z = attribAccessor.minValues[2];
|
||||
pMax.x = attribAccessor.maxValues[0];
|
||||
pMax.y = attribAccessor.maxValues[1];
|
||||
pMax.z = attribAccessor.maxValues[2];
|
||||
|
||||
switch (attribAccessor.type) {
|
||||
case TINYGLTF_TYPE_VEC3: {
|
||||
switch (attribAccessor.componentType) {
|
||||
case TINYGLTF_COMPONENT_TYPE_FLOAT:
|
||||
std::cout << "Type is FLOAT\n";
|
||||
// 3D vector of float
|
||||
v3fArray positions(
|
||||
arrayAdapter<v3f>(dataPtr, count, byte_stride));
|
||||
|
||||
std::cout << "positions's size : " << positions.size()
|
||||
<< '\n';
|
||||
|
||||
for (size_t i{0}; i < positions.size(); ++i) {
|
||||
const auto v = positions[i];
|
||||
std::cout << "positions[" << i << "]: (" << v.x << ", "
|
||||
<< v.y << ", " << v.z << ")\n";
|
||||
|
||||
loadedMesh.vertices.push_back(v.x * scale);
|
||||
loadedMesh.vertices.push_back(v.y * scale);
|
||||
loadedMesh.vertices.push_back(v.z * scale);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TINYGLTF_COMPONENT_TYPE_DOUBLE: {
|
||||
std::cout << "Type is DOUBLE\n";
|
||||
switch (attribAccessor.type) {
|
||||
case TINYGLTF_TYPE_VEC3: {
|
||||
v3dArray positions(
|
||||
arrayAdapter<v3d>(dataPtr, count, byte_stride));
|
||||
for (size_t i{0}; i < positions.size(); ++i) {
|
||||
const auto v = positions[i];
|
||||
std::cout << "positions[" << i << "]: (" << v.x
|
||||
<< ", " << v.y << ", " << v.z << ")\n";
|
||||
|
||||
loadedMesh.vertices.push_back(v.x * scale);
|
||||
loadedMesh.vertices.push_back(v.y * scale);
|
||||
loadedMesh.vertices.push_back(v.z * scale);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
// TODO Handle error
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
if (attribute.first == "NORMAL") {
|
||||
std::cout << "found normal attribute\n";
|
||||
|
||||
switch (attribAccessor.type) {
|
||||
case TINYGLTF_TYPE_VEC3: {
|
||||
std::cout << "Normal is VEC3\n";
|
||||
switch (attribAccessor.componentType) {
|
||||
case TINYGLTF_COMPONENT_TYPE_FLOAT: {
|
||||
std::cout << "Normal is FLOAT\n";
|
||||
v3fArray normals(
|
||||
arrayAdapter<v3f>(dataPtr, count, byte_stride));
|
||||
|
||||
// IMPORTANT: We need to reorder normals (and texture
|
||||
// coordinates into "facevarying" order) for each face
|
||||
|
||||
// For each triangle :
|
||||
for (size_t i{0}; i < indices.size() / 3; ++i) {
|
||||
// get the i'th triange's indexes
|
||||
auto f0 = indices[3 * i + 0];
|
||||
auto f1 = indices[3 * i + 1];
|
||||
auto f2 = indices[3 * i + 2];
|
||||
|
||||
// get the 3 normal vectors for that face
|
||||
v3f n0, n1, n2;
|
||||
n0 = normals[f0];
|
||||
n1 = normals[f1];
|
||||
n2 = normals[f2];
|
||||
|
||||
// Put them in the array in the correct order
|
||||
loadedMesh.facevarying_normals.push_back(n0.x);
|
||||
loadedMesh.facevarying_normals.push_back(n0.y);
|
||||
loadedMesh.facevarying_normals.push_back(n0.z);
|
||||
|
||||
loadedMesh.facevarying_normals.push_back(n1.x);
|
||||
loadedMesh.facevarying_normals.push_back(n1.y);
|
||||
loadedMesh.facevarying_normals.push_back(n2.z);
|
||||
|
||||
loadedMesh.facevarying_normals.push_back(n2.x);
|
||||
loadedMesh.facevarying_normals.push_back(n2.y);
|
||||
loadedMesh.facevarying_normals.push_back(n2.z);
|
||||
}
|
||||
} break;
|
||||
case TINYGLTF_COMPONENT_TYPE_DOUBLE: {
|
||||
std::cout << "Normal is DOUBLE\n";
|
||||
v3dArray normals(
|
||||
arrayAdapter<v3d>(dataPtr, count, byte_stride));
|
||||
|
||||
// IMPORTANT: We need to reorder normals (and texture
|
||||
// coordinates into "facevarying" order) for each face
|
||||
|
||||
// For each triangle :
|
||||
for (size_t i{0}; i < indices.size() / 3; ++i) {
|
||||
// get the i'th triange's indexes
|
||||
auto f0 = indices[3 * i + 0];
|
||||
auto f1 = indices[3 * i + 1];
|
||||
auto f2 = indices[3 * i + 2];
|
||||
|
||||
// get the 3 normal vectors for that face
|
||||
v3d n0, n1, n2;
|
||||
n0 = normals[f0];
|
||||
n1 = normals[f1];
|
||||
n2 = normals[f2];
|
||||
|
||||
// Put them in the array in the correct order
|
||||
loadedMesh.facevarying_normals.push_back(n0.x);
|
||||
loadedMesh.facevarying_normals.push_back(n0.y);
|
||||
loadedMesh.facevarying_normals.push_back(n0.z);
|
||||
|
||||
loadedMesh.facevarying_normals.push_back(n1.x);
|
||||
loadedMesh.facevarying_normals.push_back(n1.y);
|
||||
loadedMesh.facevarying_normals.push_back(n2.z);
|
||||
|
||||
loadedMesh.facevarying_normals.push_back(n2.x);
|
||||
loadedMesh.facevarying_normals.push_back(n2.y);
|
||||
loadedMesh.facevarying_normals.push_back(n2.z);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
std::cerr << "Unhandeled componant type for normal\n";
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
std::cerr << "Unhandeled vector type for normal\n";
|
||||
}
|
||||
|
||||
// Face varying comment on the normals is also true for the UVs
|
||||
if (attribute.first == "TEXCOORD_0") {
|
||||
std::cout << "Found texture coordinates\n";
|
||||
|
||||
switch (attribAccessor.type) {
|
||||
case TINYGLTF_TYPE_VEC2: {
|
||||
std::cout << "TEXTCOORD is VEC2\n";
|
||||
switch (attribAccessor.componentType) {
|
||||
case TINYGLTF_COMPONENT_TYPE_FLOAT: {
|
||||
std::cout << "TEXTCOORD is FLOAT\n";
|
||||
v2fArray uvs(
|
||||
arrayAdapter<v2f>(dataPtr, count, byte_stride));
|
||||
|
||||
for (size_t i{0}; i < indices.size() / 3; ++i) {
|
||||
// get the i'th triange's indexes
|
||||
auto f0 = indices[3 * i + 0];
|
||||
auto f1 = indices[3 * i + 1];
|
||||
auto f2 = indices[3 * i + 2];
|
||||
|
||||
// get the texture coordinates for each triangle's
|
||||
// vertices
|
||||
v2f uv0, uv1, uv2;
|
||||
uv0 = uvs[f0];
|
||||
uv1 = uvs[f1];
|
||||
uv2 = uvs[f2];
|
||||
|
||||
// push them in order into the mesh data
|
||||
loadedMesh.facevarying_uvs.push_back(uv0.x);
|
||||
loadedMesh.facevarying_uvs.push_back(uv0.y);
|
||||
|
||||
loadedMesh.facevarying_uvs.push_back(uv1.x);
|
||||
loadedMesh.facevarying_uvs.push_back(uv1.y);
|
||||
|
||||
loadedMesh.facevarying_uvs.push_back(uv2.x);
|
||||
loadedMesh.facevarying_uvs.push_back(uv2.y);
|
||||
}
|
||||
|
||||
} break;
|
||||
case TINYGLTF_COMPONENT_TYPE_DOUBLE: {
|
||||
std::cout << "TEXTCOORD is DOUBLE\n";
|
||||
v2dArray uvs(
|
||||
arrayAdapter<v2d>(dataPtr, count, byte_stride));
|
||||
|
||||
for (size_t i{0}; i < indices.size() / 3; ++i) {
|
||||
// get the i'th triange's indexes
|
||||
auto f0 = indices[3 * i + 0];
|
||||
auto f1 = indices[3 * i + 1];
|
||||
auto f2 = indices[3 * i + 2];
|
||||
|
||||
v2d uv0, uv1, uv2;
|
||||
uv0 = uvs[f0];
|
||||
uv1 = uvs[f1];
|
||||
uv2 = uvs[f2];
|
||||
|
||||
loadedMesh.facevarying_uvs.push_back(uv0.x);
|
||||
loadedMesh.facevarying_uvs.push_back(uv0.y);
|
||||
|
||||
loadedMesh.facevarying_uvs.push_back(uv1.x);
|
||||
loadedMesh.facevarying_uvs.push_back(uv1.y);
|
||||
|
||||
loadedMesh.facevarying_uvs.push_back(uv2.x);
|
||||
loadedMesh.facevarying_uvs.push_back(uv2.y);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
std::cerr << "unrecognized vector type for UV";
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
std::cerr << "unreconized componant type for UV";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cerr << "primitive mode not implemented";
|
||||
break;
|
||||
|
||||
// These aren't triangles:
|
||||
case TINYGLTF_MODE_POINTS:
|
||||
case TINYGLTF_MODE_LINE:
|
||||
case TINYGLTF_MODE_LINE_LOOP:
|
||||
std::cerr << "primitive is not triangle based, ignoring";
|
||||
}
|
||||
}
|
||||
|
||||
// bbox :
|
||||
v3f bCenter;
|
||||
bCenter.x = 0.5f * (pMax.x - pMin.x) + pMin.x;
|
||||
bCenter.y = 0.5f * (pMax.y - pMin.y) + pMin.y;
|
||||
bCenter.z = 0.5f * (pMax.z - pMin.z) + pMin.z;
|
||||
|
||||
for (size_t v = 0; v < loadedMesh.vertices.size() / 3; v++) {
|
||||
loadedMesh.vertices[3 * v + 0] -= bCenter.x;
|
||||
loadedMesh.vertices[3 * v + 1] -= bCenter.y;
|
||||
loadedMesh.vertices[3 * v + 2] -= bCenter.z;
|
||||
}
|
||||
|
||||
loadedMesh.pivot_xform[0][0] = 1.0f;
|
||||
loadedMesh.pivot_xform[0][1] = 0.0f;
|
||||
loadedMesh.pivot_xform[0][2] = 0.0f;
|
||||
loadedMesh.pivot_xform[0][3] = 0.0f;
|
||||
|
||||
loadedMesh.pivot_xform[1][0] = 0.0f;
|
||||
loadedMesh.pivot_xform[1][1] = 1.0f;
|
||||
loadedMesh.pivot_xform[1][2] = 0.0f;
|
||||
loadedMesh.pivot_xform[1][3] = 0.0f;
|
||||
|
||||
loadedMesh.pivot_xform[2][0] = 0.0f;
|
||||
loadedMesh.pivot_xform[2][1] = 0.0f;
|
||||
loadedMesh.pivot_xform[2][2] = 1.0f;
|
||||
loadedMesh.pivot_xform[2][3] = 0.0f;
|
||||
|
||||
loadedMesh.pivot_xform[3][0] = bCenter.x;
|
||||
loadedMesh.pivot_xform[3][1] = bCenter.y;
|
||||
loadedMesh.pivot_xform[3][2] = bCenter.z;
|
||||
loadedMesh.pivot_xform[3][3] = 1.0f;
|
||||
|
||||
// TODO handle materials
|
||||
for (size_t i{0}; i < loadedMesh.faces.size(); ++i)
|
||||
loadedMesh.material_ids.push_back(materials->at(0).id);
|
||||
|
||||
meshes->push_back(loadedMesh);
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through all texture declaration in glTF file
|
||||
for (const auto &gltfTexture : model.textures) {
|
||||
std::cout << "Found texture!";
|
||||
Texture loadedTexture;
|
||||
const auto &image = model.images[gltfTexture.source];
|
||||
loadedTexture.components = image.component;
|
||||
loadedTexture.width = image.width;
|
||||
loadedTexture.height = image.height;
|
||||
|
||||
const auto size =
|
||||
image.component * image.width * image.height * sizeof(unsigned char);
|
||||
loadedTexture.image = new unsigned char[size];
|
||||
memcpy(loadedTexture.image, image.image.data(), size);
|
||||
textures->push_back(loadedTexture);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} // namespace example
|
|
@ -0,0 +1,166 @@
|
|||
#ifndef EXAMPLE_GLTF_LOADER_H_
|
||||
#define EXAMPLE_GLTF_LOADER_H_
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "material.h"
|
||||
#include "mesh.h"
|
||||
|
||||
namespace example {
|
||||
|
||||
/// Adapts an array of bytes to an array of T. Will advace of byte_stride each
|
||||
/// elements.
|
||||
template <typename T>
|
||||
struct arrayAdapter {
|
||||
/// Pointer to the bytes
|
||||
const unsigned char *dataPtr;
|
||||
/// Number of elements in the array
|
||||
const size_t elemCount;
|
||||
/// Stride in bytes between two elements
|
||||
const size_t stride;
|
||||
|
||||
/// Construct an array adapter.
|
||||
/// \param ptr Pointer to the start of the data, with offset applied
|
||||
/// \param count Number of elements in the array
|
||||
/// \param byte_stride Stride betweens elements in the array
|
||||
arrayAdapter(const unsigned char *ptr, size_t count, size_t byte_stride)
|
||||
: dataPtr(ptr), elemCount(count), stride(byte_stride) {}
|
||||
|
||||
/// Returns a *copy* of a single element. Can't be used to modify it.
|
||||
T operator[](size_t pos) const {
|
||||
if (pos >= elemCount)
|
||||
throw std::out_of_range(
|
||||
"Tried to access beyond the last element of an array adapter with "
|
||||
"count " +
|
||||
std::to_string(elemCount) + " while getting elemnet number " +
|
||||
std::to_string(pos));
|
||||
return *(reinterpret_cast<const T *>(dataPtr + pos * stride));
|
||||
}
|
||||
};
|
||||
|
||||
/// Interface of any adapted array that returns ingeger data
|
||||
struct intArrayBase {
|
||||
virtual ~intArrayBase() = default;
|
||||
virtual unsigned int operator[](size_t) const = 0;
|
||||
virtual size_t size() const = 0;
|
||||
};
|
||||
|
||||
/// Interface of any adapted array that returns float data
|
||||
struct floatArrayBase {
|
||||
virtual ~floatArrayBase() = default;
|
||||
virtual float operator[](size_t) const = 0;
|
||||
virtual size_t size() const = 0;
|
||||
};
|
||||
|
||||
/// An array that loads interger types, returns them as int
|
||||
template <class T>
|
||||
struct intArray : public intArrayBase {
|
||||
arrayAdapter<T> adapter;
|
||||
|
||||
intArray(const arrayAdapter<T> &a) : adapter(a) {}
|
||||
unsigned int operator[](size_t position) const override {
|
||||
return static_cast<unsigned int>(adapter[position]);
|
||||
}
|
||||
|
||||
size_t size() const override { return adapter.elemCount; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct floatArray : public floatArrayBase {
|
||||
arrayAdapter<T> adapter;
|
||||
|
||||
floatArray(const arrayAdapter<T> &a) : adapter(a) {}
|
||||
float operator[](size_t position) const override {
|
||||
return static_cast<float>(adapter[position]);
|
||||
}
|
||||
|
||||
size_t size() const override { return adapter.elemCount; }
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
template <typename T>
|
||||
struct v2 {
|
||||
T x, y;
|
||||
};
|
||||
/// 3D vector of floats without padding
|
||||
template <typename T>
|
||||
struct v3 {
|
||||
T x, y, z;
|
||||
};
|
||||
|
||||
/// 4D vector of floats without padding
|
||||
template <typename T>
|
||||
struct v4 {
|
||||
T x, y, z, w;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
using v2f = v2<float>;
|
||||
using v3f = v3<float>;
|
||||
using v4f = v4<float>;
|
||||
using v2d = v2<double>;
|
||||
using v3d = v3<double>;
|
||||
using v4d = v4<double>;
|
||||
|
||||
struct v2fArray {
|
||||
arrayAdapter<v2f> adapter;
|
||||
v2fArray(const arrayAdapter<v2f> &a) : adapter(a) {}
|
||||
|
||||
v2f operator[](size_t position) const { return adapter[position]; }
|
||||
size_t size() const { return adapter.elemCount; }
|
||||
};
|
||||
|
||||
struct v3fArray {
|
||||
arrayAdapter<v3f> adapter;
|
||||
v3fArray(const arrayAdapter<v3f> &a) : adapter(a) {}
|
||||
|
||||
v3f operator[](size_t position) const { return adapter[position]; }
|
||||
size_t size() const { return adapter.elemCount; }
|
||||
};
|
||||
|
||||
struct v4fArray {
|
||||
arrayAdapter<v4f> adapter;
|
||||
v4fArray(const arrayAdapter<v4f> &a) : adapter(a) {}
|
||||
|
||||
v4f operator[](size_t position) const { return adapter[position]; }
|
||||
size_t size() const { return adapter.elemCount; }
|
||||
};
|
||||
|
||||
struct v2dArray {
|
||||
arrayAdapter<v2d> adapter;
|
||||
v2dArray(const arrayAdapter<v2d> &a) : adapter(a) {}
|
||||
|
||||
v2d operator[](size_t position) const { return adapter[position]; }
|
||||
size_t size() const { return adapter.elemCount; }
|
||||
};
|
||||
|
||||
struct v3dArray {
|
||||
arrayAdapter<v3d> adapter;
|
||||
v3dArray(const arrayAdapter<v3d> &a) : adapter(a) {}
|
||||
|
||||
v3d operator[](size_t position) const { return adapter[position]; }
|
||||
size_t size() const { return adapter.elemCount; }
|
||||
};
|
||||
|
||||
struct v4dArray {
|
||||
arrayAdapter<v4d> adapter;
|
||||
v4dArray(const arrayAdapter<v4d> &a) : adapter(a) {}
|
||||
|
||||
v4d operator[](size_t position) const { return adapter[position]; }
|
||||
size_t size() const { return adapter.elemCount; }
|
||||
};
|
||||
|
||||
///
|
||||
/// Loads glTF 2.0 mesh
|
||||
///
|
||||
bool LoadGLTF(const std::string &filename, float scale,
|
||||
std::vector<Mesh<float> > *meshes,
|
||||
std::vector<Material> *materials, std::vector<Texture> *textures);
|
||||
|
||||
} // namespace example
|
||||
|
||||
#endif // EXAMPLE_GLTF_LOADER_H_
|
Binary file not shown.
After Width: | Height: | Size: 122 KiB |
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,75 @@
|
|||
#ifndef EXAMPLE_MATERIAL_H_
|
||||
#define EXAMPLE_MATERIAL_H_
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#if __has_warning("-Wzero-as-null-pointer-constant")
|
||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace example {
|
||||
|
||||
struct Material {
|
||||
// float ambient[3];
|
||||
float diffuse[3];
|
||||
float specular[3];
|
||||
// float reflection[3];
|
||||
// float refraction[3];
|
||||
int id;
|
||||
int diffuse_texid;
|
||||
int specular_texid;
|
||||
// int reflection_texid;
|
||||
// int transparency_texid;
|
||||
// int bump_texid;
|
||||
// int normal_texid; // normal map
|
||||
// int alpha_texid; // alpha map
|
||||
|
||||
Material() {
|
||||
// ambient[0] = 0.0;
|
||||
// ambient[1] = 0.0;
|
||||
// ambient[2] = 0.0;
|
||||
diffuse[0] = 0.5;
|
||||
diffuse[1] = 0.5;
|
||||
diffuse[2] = 0.5;
|
||||
specular[0] = 0.5;
|
||||
specular[1] = 0.5;
|
||||
specular[2] = 0.5;
|
||||
// reflection[0] = 0.0;
|
||||
// reflection[1] = 0.0;
|
||||
// reflection[2] = 0.0;
|
||||
// refraction[0] = 0.0;
|
||||
// refraction[1] = 0.0;
|
||||
// refraction[2] = 0.0;
|
||||
id = -1;
|
||||
diffuse_texid = -1;
|
||||
specular_texid = -1;
|
||||
// reflection_texid = -1;
|
||||
// transparency_texid = -1;
|
||||
// bump_texid = -1;
|
||||
// normal_texid = -1;
|
||||
// alpha_texid = -1;
|
||||
}
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
int width;
|
||||
int height;
|
||||
int components;
|
||||
int _pad_;
|
||||
unsigned char* image;
|
||||
|
||||
Texture() {
|
||||
width = -1;
|
||||
height = -1;
|
||||
components = -1;
|
||||
image = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace example
|
||||
|
||||
|
||||
#endif // EXAMPLE_MATERIAL_H_
|
|
@ -0,0 +1,216 @@
|
|||
#include <cstdio>
|
||||
#include <cmath>
|
||||
|
||||
#include "matrix.h"
|
||||
|
||||
//using namespace mallie;
|
||||
|
||||
static inline float vdot(float a[3], float b[3]) {
|
||||
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||
}
|
||||
|
||||
static inline void vcross(float c[3], float a[3], float b[3]) {
|
||||
c[0] = a[1] * b[2] - a[2] * b[1];
|
||||
c[1] = a[2] * b[0] - a[0] * b[2];
|
||||
c[2] = a[0] * b[1] - a[1] * b[0];
|
||||
}
|
||||
|
||||
static inline float vlength(float v[3]) {
|
||||
float len2 = vdot(v, v);
|
||||
if (std::abs(len2) > 1.0e-30) {
|
||||
return sqrt(len2);
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static void vnormalize(float v[3]) {
|
||||
float len = vlength(v);
|
||||
if (std::abs(len) > 1.0e-30) {
|
||||
float inv_len = 1.0f / len;
|
||||
v[0] *= inv_len;
|
||||
v[1] *= inv_len;
|
||||
v[2] *= inv_len;
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix::Print(float m[4][4]) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
printf("m[%d] = %f, %f, %f, %f\n", i, m[i][0], m[i][1], m[i][2], m[i][3]);
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix::LookAt(float m[4][4], float eye[3], float lookat[3],
|
||||
float up[3]) {
|
||||
|
||||
float u[3], v[3];
|
||||
float look[3];
|
||||
look[0] = lookat[0] - eye[0];
|
||||
look[1] = lookat[1] - eye[1];
|
||||
look[2] = lookat[2] - eye[2];
|
||||
vnormalize(look);
|
||||
|
||||
vcross(u, look, up);
|
||||
vnormalize(u);
|
||||
|
||||
vcross(v, u, look);
|
||||
vnormalize(v);
|
||||
|
||||
#if 0
|
||||
m[0][0] = u[0];
|
||||
m[0][1] = v[0];
|
||||
m[0][2] = -look[0];
|
||||
m[0][3] = 0.0;
|
||||
|
||||
m[1][0] = u[1];
|
||||
m[1][1] = v[1];
|
||||
m[1][2] = -look[1];
|
||||
m[1][3] = 0.0;
|
||||
|
||||
m[2][0] = u[2];
|
||||
m[2][1] = v[2];
|
||||
m[2][2] = -look[2];
|
||||
m[2][3] = 0.0;
|
||||
|
||||
m[3][0] = eye[0];
|
||||
m[3][1] = eye[1];
|
||||
m[3][2] = eye[2];
|
||||
m[3][3] = 1.0;
|
||||
#else
|
||||
m[0][0] = u[0];
|
||||
m[1][0] = v[0];
|
||||
m[2][0] = -look[0];
|
||||
m[3][0] = eye[0];
|
||||
|
||||
m[0][1] = u[1];
|
||||
m[1][1] = v[1];
|
||||
m[2][1] = -look[1];
|
||||
m[3][1] = eye[1];
|
||||
|
||||
m[0][2] = u[2];
|
||||
m[1][2] = v[2];
|
||||
m[2][2] = -look[2];
|
||||
m[3][2] = eye[2];
|
||||
|
||||
m[0][3] = 0.0;
|
||||
m[1][3] = 0.0;
|
||||
m[2][3] = 0.0;
|
||||
m[3][3] = 1.0;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void Matrix::Inverse(float m[4][4]) {
|
||||
/*
|
||||
* codes from intel web
|
||||
* cramer's rule version
|
||||
*/
|
||||
int i, j;
|
||||
float tmp[12]; /* tmp array for pairs */
|
||||
float tsrc[16]; /* array of transpose source matrix */
|
||||
float det; /* determinant */
|
||||
|
||||
/* transpose matrix */
|
||||
for (i = 0; i < 4; i++) {
|
||||
tsrc[i] = m[i][0];
|
||||
tsrc[i + 4] = m[i][1];
|
||||
tsrc[i + 8] = m[i][2];
|
||||
tsrc[i + 12] = m[i][3];
|
||||
}
|
||||
|
||||
/* calculate pair for first 8 elements(cofactors) */
|
||||
tmp[0] = tsrc[10] * tsrc[15];
|
||||
tmp[1] = tsrc[11] * tsrc[14];
|
||||
tmp[2] = tsrc[9] * tsrc[15];
|
||||
tmp[3] = tsrc[11] * tsrc[13];
|
||||
tmp[4] = tsrc[9] * tsrc[14];
|
||||
tmp[5] = tsrc[10] * tsrc[13];
|
||||
tmp[6] = tsrc[8] * tsrc[15];
|
||||
tmp[7] = tsrc[11] * tsrc[12];
|
||||
tmp[8] = tsrc[8] * tsrc[14];
|
||||
tmp[9] = tsrc[10] * tsrc[12];
|
||||
tmp[10] = tsrc[8] * tsrc[13];
|
||||
tmp[11] = tsrc[9] * tsrc[12];
|
||||
|
||||
/* calculate first 8 elements(cofactors) */
|
||||
m[0][0] = tmp[0] * tsrc[5] + tmp[3] * tsrc[6] + tmp[4] * tsrc[7];
|
||||
m[0][0] -= tmp[1] * tsrc[5] + tmp[2] * tsrc[6] + tmp[5] * tsrc[7];
|
||||
m[0][1] = tmp[1] * tsrc[4] + tmp[6] * tsrc[6] + tmp[9] * tsrc[7];
|
||||
m[0][1] -= tmp[0] * tsrc[4] + tmp[7] * tsrc[6] + tmp[8] * tsrc[7];
|
||||
m[0][2] = tmp[2] * tsrc[4] + tmp[7] * tsrc[5] + tmp[10] * tsrc[7];
|
||||
m[0][2] -= tmp[3] * tsrc[4] + tmp[6] * tsrc[5] + tmp[11] * tsrc[7];
|
||||
m[0][3] = tmp[5] * tsrc[4] + tmp[8] * tsrc[5] + tmp[11] * tsrc[6];
|
||||
m[0][3] -= tmp[4] * tsrc[4] + tmp[9] * tsrc[5] + tmp[10] * tsrc[6];
|
||||
m[1][0] = tmp[1] * tsrc[1] + tmp[2] * tsrc[2] + tmp[5] * tsrc[3];
|
||||
m[1][0] -= tmp[0] * tsrc[1] + tmp[3] * tsrc[2] + tmp[4] * tsrc[3];
|
||||
m[1][1] = tmp[0] * tsrc[0] + tmp[7] * tsrc[2] + tmp[8] * tsrc[3];
|
||||
m[1][1] -= tmp[1] * tsrc[0] + tmp[6] * tsrc[2] + tmp[9] * tsrc[3];
|
||||
m[1][2] = tmp[3] * tsrc[0] + tmp[6] * tsrc[1] + tmp[11] * tsrc[3];
|
||||
m[1][2] -= tmp[2] * tsrc[0] + tmp[7] * tsrc[1] + tmp[10] * tsrc[3];
|
||||
m[1][3] = tmp[4] * tsrc[0] + tmp[9] * tsrc[1] + tmp[10] * tsrc[2];
|
||||
m[1][3] -= tmp[5] * tsrc[0] + tmp[8] * tsrc[1] + tmp[11] * tsrc[2];
|
||||
|
||||
/* calculate pairs for second 8 elements(cofactors) */
|
||||
tmp[0] = tsrc[2] * tsrc[7];
|
||||
tmp[1] = tsrc[3] * tsrc[6];
|
||||
tmp[2] = tsrc[1] * tsrc[7];
|
||||
tmp[3] = tsrc[3] * tsrc[5];
|
||||
tmp[4] = tsrc[1] * tsrc[6];
|
||||
tmp[5] = tsrc[2] * tsrc[5];
|
||||
tmp[6] = tsrc[0] * tsrc[7];
|
||||
tmp[7] = tsrc[3] * tsrc[4];
|
||||
tmp[8] = tsrc[0] * tsrc[6];
|
||||
tmp[9] = tsrc[2] * tsrc[4];
|
||||
tmp[10] = tsrc[0] * tsrc[5];
|
||||
tmp[11] = tsrc[1] * tsrc[4];
|
||||
|
||||
/* calculate second 8 elements(cofactors) */
|
||||
m[2][0] = tmp[0] * tsrc[13] + tmp[3] * tsrc[14] + tmp[4] * tsrc[15];
|
||||
m[2][0] -= tmp[1] * tsrc[13] + tmp[2] * tsrc[14] + tmp[5] * tsrc[15];
|
||||
m[2][1] = tmp[1] * tsrc[12] + tmp[6] * tsrc[14] + tmp[9] * tsrc[15];
|
||||
m[2][1] -= tmp[0] * tsrc[12] + tmp[7] * tsrc[14] + tmp[8] * tsrc[15];
|
||||
m[2][2] = tmp[2] * tsrc[12] + tmp[7] * tsrc[13] + tmp[10] * tsrc[15];
|
||||
m[2][2] -= tmp[3] * tsrc[12] + tmp[6] * tsrc[13] + tmp[11] * tsrc[15];
|
||||
m[2][3] = tmp[5] * tsrc[12] + tmp[8] * tsrc[13] + tmp[11] * tsrc[14];
|
||||
m[2][3] -= tmp[4] * tsrc[12] + tmp[9] * tsrc[13] + tmp[10] * tsrc[14];
|
||||
m[3][0] = tmp[2] * tsrc[10] + tmp[5] * tsrc[11] + tmp[1] * tsrc[9];
|
||||
m[3][0] -= tmp[4] * tsrc[11] + tmp[0] * tsrc[9] + tmp[3] * tsrc[10];
|
||||
m[3][1] = tmp[8] * tsrc[11] + tmp[0] * tsrc[8] + tmp[7] * tsrc[10];
|
||||
m[3][1] -= tmp[6] * tsrc[10] + tmp[9] * tsrc[11] + tmp[1] * tsrc[8];
|
||||
m[3][2] = tmp[6] * tsrc[9] + tmp[11] * tsrc[11] + tmp[3] * tsrc[8];
|
||||
m[3][2] -= tmp[10] * tsrc[11] + tmp[2] * tsrc[8] + tmp[7] * tsrc[9];
|
||||
m[3][3] = tmp[10] * tsrc[10] + tmp[4] * tsrc[8] + tmp[9] * tsrc[9];
|
||||
m[3][3] -= tmp[8] * tsrc[9] + tmp[11] * tsrc[0] + tmp[5] * tsrc[8];
|
||||
|
||||
/* calculate determinant */
|
||||
det = tsrc[0] * m[0][0] + tsrc[1] * m[0][1] + tsrc[2] * m[0][2] +
|
||||
tsrc[3] * m[0][3];
|
||||
|
||||
/* calculate matrix inverse */
|
||||
det = 1.0f / det;
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
m[j][i] *= det;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix::Mult(float dst[4][4], float m0[4][4], float m1[4][4]) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst[i][j] = 0;
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
dst[i][j] += m0[k][j] * m1[i][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix::MultV(float dst[3], float m[4][4], float v[3]) {
|
||||
// printf("v = %f, %f, %f\n", v[0], v[1], v[2]);
|
||||
dst[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
|
||||
dst[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
|
||||
dst[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2];
|
||||
// printf("m = %f, %f, %f\n", m[3][0], m[3][1], m[3][2]);
|
||||
// printf("dst = %f, %f, %f\n", dst[0], dst[1], dst[2]);
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
#ifndef EXAMPLE_MESH_H_
|
||||
#define EXAMPLE_MESH_H_
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace example {
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline void lerp(T dst[3], const T v0[3], const T v1[3], const T v2[3], float u, float v) {
|
||||
dst[0] = (static_cast<T>(1.0) - u - v) * v0[0] + u * v1[0] + v * v2[0];
|
||||
dst[1] = (static_cast<T>(1.0) - u - v) * v0[1] + u * v1[1] + v * v2[1];
|
||||
dst[2] = (static_cast<T>(1.0) - u - v) * v0[2] + u * v1[2] + v * v2[2];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T vlength(const T v[3]) {
|
||||
const T d = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
||||
if (std::fabs(d) > std::numeric_limits<T>::epsilon()) {
|
||||
return std::sqrt(d);
|
||||
} else {
|
||||
return static_cast<T>(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void vnormalize(T dst[3], const T v[3]) {
|
||||
dst[0] = v[0];
|
||||
dst[1] = v[1];
|
||||
dst[2] = v[2];
|
||||
const T len = vlength(v);
|
||||
if (std::fabs(len) > std::numeric_limits<T>::epsilon()) {
|
||||
const T inv_len = static_cast<T>(1.0) / len;
|
||||
dst[0] *= inv_len;
|
||||
dst[1] *= inv_len;
|
||||
dst[2] *= inv_len;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void vcross(T dst[3], const T a[3], const T b[3]) {
|
||||
dst[0] = a[1] * b[2] - a[2] * b[1];
|
||||
dst[1] = a[2] * b[0] - a[0] * b[2];
|
||||
dst[2] = a[0] * b[1] - a[1] * b[0];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void vsub(T dst[3], const T a[3], const T b[3]) {
|
||||
dst[0] = a[0] - b[0];
|
||||
dst[1] = a[1] - b[1];
|
||||
dst[2] = a[2] - b[2];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void calculate_normal(T Nn[3], const T v0[3], const T v1[3], const T v2[3]) {
|
||||
T v10[3];
|
||||
T v20[3];
|
||||
|
||||
vsub(v10, v1, v0);
|
||||
vsub(v20, v2, v0);
|
||||
|
||||
T N[3];
|
||||
vcross(N, v20, v10);
|
||||
vnormalize(Nn, N);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class Mesh {
|
||||
public:
|
||||
explicit Mesh(const size_t vertex_stride) :
|
||||
stride(vertex_stride) {
|
||||
}
|
||||
|
||||
std::string name;
|
||||
|
||||
std::vector<T> vertices; /// stride * num_vertices
|
||||
std::vector<T> facevarying_normals; /// [xyz] * 3(triangle) * num_faces
|
||||
std::vector<T> facevarying_tangents; /// [xyz] * 3(triangle) * num_faces
|
||||
std::vector<T> facevarying_binormals; /// [xyz] * 3(triangle) * num_faces
|
||||
std::vector<T> facevarying_uvs; /// [xy] * 3(triangle) * num_faces
|
||||
std::vector<T>
|
||||
facevarying_vertex_colors; /// [xyz] * 3(triangle) * num_faces
|
||||
std::vector<unsigned int> faces; /// triangle x num_faces
|
||||
std::vector<unsigned int> material_ids; /// index x num_faces
|
||||
|
||||
T pivot_xform[4][4];
|
||||
size_t stride; /// stride for vertex data.
|
||||
|
||||
// --- Required methods in Scene::Traversal. ---
|
||||
|
||||
///
|
||||
/// Get the geometric normal and the shading normal at `face_idx' th face.
|
||||
///
|
||||
void GetNormal(T Ng[3], T Ns[3], const unsigned int face_idx, const T u, const T v) const {
|
||||
// Compute geometric normal.
|
||||
unsigned int f0, f1, f2;
|
||||
T v0[3], v1[3], v2[3];
|
||||
|
||||
f0 = faces[3 * face_idx + 0];
|
||||
f1 = faces[3 * face_idx + 1];
|
||||
f2 = faces[3 * face_idx + 2];
|
||||
|
||||
v0[0] = vertices[3 * f0 + 0];
|
||||
v0[1] = vertices[3 * f0 + 1];
|
||||
v0[2] = vertices[3 * f0 + 2];
|
||||
|
||||
v1[0] = vertices[3 * f1 + 0];
|
||||
v1[1] = vertices[3 * f1 + 1];
|
||||
v1[2] = vertices[3 * f1 + 2];
|
||||
|
||||
v2[0] = vertices[3 * f2 + 0];
|
||||
v2[1] = vertices[3 * f2 + 1];
|
||||
v2[2] = vertices[3 * f2 + 2];
|
||||
|
||||
calculate_normal(Ng, v0, v1, v2);
|
||||
|
||||
if (facevarying_normals.size() > 0) {
|
||||
|
||||
T n0[3], n1[3], n2[3];
|
||||
|
||||
n0[0] = facevarying_normals[9 * face_idx + 0];
|
||||
n0[1] = facevarying_normals[9 * face_idx + 1];
|
||||
n0[2] = facevarying_normals[9 * face_idx + 2];
|
||||
|
||||
n1[0] = facevarying_normals[9 * face_idx + 3];
|
||||
n1[1] = facevarying_normals[9 * face_idx + 4];
|
||||
n1[2] = facevarying_normals[9 * face_idx + 5];
|
||||
|
||||
n2[0] = facevarying_normals[9 * face_idx + 6];
|
||||
n2[1] = facevarying_normals[9 * face_idx + 7];
|
||||
n2[2] = facevarying_normals[9 * face_idx + 8];
|
||||
|
||||
lerp(Ns, n0, n1, n2, u, v);
|
||||
|
||||
} else {
|
||||
|
||||
// Use geometric normal.
|
||||
Ns[0] = Ng[0];
|
||||
Ns[1] = Ng[1];
|
||||
Ns[2] = Ng[2];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// --- end of required methods in Scene::Traversal. ---
|
||||
|
||||
///
|
||||
/// Get texture coordinate at `face_idx' th face.
|
||||
///
|
||||
void GetTexCoord(T tcoord[3], const unsigned int face_idx, const T u, const T v) {
|
||||
|
||||
if (facevarying_uvs.size() > 0) {
|
||||
|
||||
T t0[3], t1[3], t2[3];
|
||||
|
||||
t0[0] = facevarying_uvs[6 * face_idx + 0];
|
||||
t0[1] = facevarying_uvs[6 * face_idx + 1];
|
||||
t0[2] = static_cast<T>(0.0);
|
||||
|
||||
t1[0] = facevarying_uvs[6 * face_idx + 2];
|
||||
t1[1] = facevarying_uvs[6 * face_idx + 3];
|
||||
t1[2] = static_cast<T>(0.0);
|
||||
|
||||
t2[0] = facevarying_uvs[6 * face_idx + 4];
|
||||
t2[1] = facevarying_uvs[6 * face_idx + 5];
|
||||
t2[2] = static_cast<T>(0.0);
|
||||
|
||||
lerp(tcoord, t0, t1, t2, u, v);
|
||||
|
||||
} else {
|
||||
|
||||
tcoord[0] = static_cast<T>(0.0);
|
||||
tcoord[1] = static_cast<T>(0.0);
|
||||
tcoord[2] = static_cast<T>(0.0);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace example
|
||||
|
||||
#endif // EXAMPLE_MESH_H_
|
|
@ -0,0 +1 @@
|
|||
#include "nanort.h"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,882 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Light Transport Entertainment, Inc.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef NANOSG_H_
|
||||
#define NANOSG_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "nanort.h"
|
||||
|
||||
namespace nanosg {
|
||||
|
||||
template <class T>
|
||||
class PrimitiveInterface;
|
||||
|
||||
template <class T>
|
||||
class PrimitiveInterface {
|
||||
public:
|
||||
void print() { static_cast<T &>(this)->print(); }
|
||||
};
|
||||
|
||||
class SpherePrimitive : PrimitiveInterface<SpherePrimitive> {
|
||||
public:
|
||||
void print() { std::cout << "Sphere" << std::endl; }
|
||||
};
|
||||
|
||||
// 4x4 matrix
|
||||
template <typename T>
|
||||
class Matrix {
|
||||
public:
|
||||
Matrix();
|
||||
~Matrix();
|
||||
|
||||
static void Print(const T m[4][4]) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
printf("m[%d] = %f, %f, %f, %f\n", i, m[i][0], m[i][1], m[i][2], m[i][3]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Identity(T m[4][4]) {
|
||||
m[0][0] = static_cast<T>(1);
|
||||
m[0][1] = static_cast<T>(0);
|
||||
m[0][2] = static_cast<T>(0);
|
||||
m[0][3] = static_cast<T>(0);
|
||||
m[1][0] = static_cast<T>(0);
|
||||
m[1][1] = static_cast<T>(1);
|
||||
m[1][2] = static_cast<T>(0);
|
||||
m[1][3] = static_cast<T>(0);
|
||||
m[2][0] = static_cast<T>(0);
|
||||
m[2][1] = static_cast<T>(0);
|
||||
m[2][2] = static_cast<T>(1);
|
||||
m[2][3] = static_cast<T>(0);
|
||||
m[3][0] = static_cast<T>(0);
|
||||
m[3][1] = static_cast<T>(0);
|
||||
m[3][2] = static_cast<T>(0);
|
||||
m[3][3] = static_cast<T>(1);
|
||||
}
|
||||
|
||||
static void Copy(T dst[4][4], const T src[4][4]) {
|
||||
memcpy(dst, src, sizeof(T) * 16);
|
||||
}
|
||||
|
||||
static void Inverse(T m[4][4]) {
|
||||
/*
|
||||
* codes from intel web
|
||||
* cramer's rule version
|
||||
*/
|
||||
int i, j;
|
||||
T tmp[12]; /* tmp array for pairs */
|
||||
T tsrc[16]; /* array of transpose source matrix */
|
||||
T det; /* determinant */
|
||||
|
||||
/* transpose matrix */
|
||||
for (i = 0; i < 4; i++) {
|
||||
tsrc[i] = m[i][0];
|
||||
tsrc[i + 4] = m[i][1];
|
||||
tsrc[i + 8] = m[i][2];
|
||||
tsrc[i + 12] = m[i][3];
|
||||
}
|
||||
|
||||
/* calculate pair for first 8 elements(cofactors) */
|
||||
tmp[0] = tsrc[10] * tsrc[15];
|
||||
tmp[1] = tsrc[11] * tsrc[14];
|
||||
tmp[2] = tsrc[9] * tsrc[15];
|
||||
tmp[3] = tsrc[11] * tsrc[13];
|
||||
tmp[4] = tsrc[9] * tsrc[14];
|
||||
tmp[5] = tsrc[10] * tsrc[13];
|
||||
tmp[6] = tsrc[8] * tsrc[15];
|
||||
tmp[7] = tsrc[11] * tsrc[12];
|
||||
tmp[8] = tsrc[8] * tsrc[14];
|
||||
tmp[9] = tsrc[10] * tsrc[12];
|
||||
tmp[10] = tsrc[8] * tsrc[13];
|
||||
tmp[11] = tsrc[9] * tsrc[12];
|
||||
|
||||
/* calculate first 8 elements(cofactors) */
|
||||
m[0][0] = tmp[0] * tsrc[5] + tmp[3] * tsrc[6] + tmp[4] * tsrc[7];
|
||||
m[0][0] -= tmp[1] * tsrc[5] + tmp[2] * tsrc[6] + tmp[5] * tsrc[7];
|
||||
m[0][1] = tmp[1] * tsrc[4] + tmp[6] * tsrc[6] + tmp[9] * tsrc[7];
|
||||
m[0][1] -= tmp[0] * tsrc[4] + tmp[7] * tsrc[6] + tmp[8] * tsrc[7];
|
||||
m[0][2] = tmp[2] * tsrc[4] + tmp[7] * tsrc[5] + tmp[10] * tsrc[7];
|
||||
m[0][2] -= tmp[3] * tsrc[4] + tmp[6] * tsrc[5] + tmp[11] * tsrc[7];
|
||||
m[0][3] = tmp[5] * tsrc[4] + tmp[8] * tsrc[5] + tmp[11] * tsrc[6];
|
||||
m[0][3] -= tmp[4] * tsrc[4] + tmp[9] * tsrc[5] + tmp[10] * tsrc[6];
|
||||
m[1][0] = tmp[1] * tsrc[1] + tmp[2] * tsrc[2] + tmp[5] * tsrc[3];
|
||||
m[1][0] -= tmp[0] * tsrc[1] + tmp[3] * tsrc[2] + tmp[4] * tsrc[3];
|
||||
m[1][1] = tmp[0] * tsrc[0] + tmp[7] * tsrc[2] + tmp[8] * tsrc[3];
|
||||
m[1][1] -= tmp[1] * tsrc[0] + tmp[6] * tsrc[2] + tmp[9] * tsrc[3];
|
||||
m[1][2] = tmp[3] * tsrc[0] + tmp[6] * tsrc[1] + tmp[11] * tsrc[3];
|
||||
m[1][2] -= tmp[2] * tsrc[0] + tmp[7] * tsrc[1] + tmp[10] * tsrc[3];
|
||||
m[1][3] = tmp[4] * tsrc[0] + tmp[9] * tsrc[1] + tmp[10] * tsrc[2];
|
||||
m[1][3] -= tmp[5] * tsrc[0] + tmp[8] * tsrc[1] + tmp[11] * tsrc[2];
|
||||
|
||||
/* calculate pairs for second 8 elements(cofactors) */
|
||||
tmp[0] = tsrc[2] * tsrc[7];
|
||||
tmp[1] = tsrc[3] * tsrc[6];
|
||||
tmp[2] = tsrc[1] * tsrc[7];
|
||||
tmp[3] = tsrc[3] * tsrc[5];
|
||||
tmp[4] = tsrc[1] * tsrc[6];
|
||||
tmp[5] = tsrc[2] * tsrc[5];
|
||||
tmp[6] = tsrc[0] * tsrc[7];
|
||||
tmp[7] = tsrc[3] * tsrc[4];
|
||||
tmp[8] = tsrc[0] * tsrc[6];
|
||||
tmp[9] = tsrc[2] * tsrc[4];
|
||||
tmp[10] = tsrc[0] * tsrc[5];
|
||||
tmp[11] = tsrc[1] * tsrc[4];
|
||||
|
||||
/* calculate second 8 elements(cofactors) */
|
||||
m[2][0] = tmp[0] * tsrc[13] + tmp[3] * tsrc[14] + tmp[4] * tsrc[15];
|
||||
m[2][0] -= tmp[1] * tsrc[13] + tmp[2] * tsrc[14] + tmp[5] * tsrc[15];
|
||||
m[2][1] = tmp[1] * tsrc[12] + tmp[6] * tsrc[14] + tmp[9] * tsrc[15];
|
||||
m[2][1] -= tmp[0] * tsrc[12] + tmp[7] * tsrc[14] + tmp[8] * tsrc[15];
|
||||
m[2][2] = tmp[2] * tsrc[12] + tmp[7] * tsrc[13] + tmp[10] * tsrc[15];
|
||||
m[2][2] -= tmp[3] * tsrc[12] + tmp[6] * tsrc[13] + tmp[11] * tsrc[15];
|
||||
m[2][3] = tmp[5] * tsrc[12] + tmp[8] * tsrc[13] + tmp[11] * tsrc[14];
|
||||
m[2][3] -= tmp[4] * tsrc[12] + tmp[9] * tsrc[13] + tmp[10] * tsrc[14];
|
||||
m[3][0] = tmp[2] * tsrc[10] + tmp[5] * tsrc[11] + tmp[1] * tsrc[9];
|
||||
m[3][0] -= tmp[4] * tsrc[11] + tmp[0] * tsrc[9] + tmp[3] * tsrc[10];
|
||||
m[3][1] = tmp[8] * tsrc[11] + tmp[0] * tsrc[8] + tmp[7] * tsrc[10];
|
||||
m[3][1] -= tmp[6] * tsrc[10] + tmp[9] * tsrc[11] + tmp[1] * tsrc[8];
|
||||
m[3][2] = tmp[6] * tsrc[9] + tmp[11] * tsrc[11] + tmp[3] * tsrc[8];
|
||||
m[3][2] -= tmp[10] * tsrc[11] + tmp[2] * tsrc[8] + tmp[7] * tsrc[9];
|
||||
m[3][3] = tmp[10] * tsrc[10] + tmp[4] * tsrc[8] + tmp[9] * tsrc[9];
|
||||
m[3][3] -= tmp[8] * tsrc[9] + tmp[11] * tsrc[0] + tmp[5] * tsrc[8];
|
||||
|
||||
/* calculate determinant */
|
||||
det = tsrc[0] * m[0][0] + tsrc[1] * m[0][1] + tsrc[2] * m[0][2] +
|
||||
tsrc[3] * m[0][3];
|
||||
|
||||
/* calculate matrix inverse */
|
||||
det = static_cast<T>(1.0) / det;
|
||||
|
||||
for (j = 0; j < 4; j++) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
m[j][i] *= det;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Transpose(T m[4][4]) {
|
||||
T t[4][4];
|
||||
|
||||
// Transpose
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
t[j][i] = m[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Copy
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
m[j][i] = t[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Mult(T dst[4][4], const T m0[4][4], const T m1[4][4]) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
dst[i][j] = 0;
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
dst[i][j] += m0[k][j] * m1[i][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MultV(T dst[3], const T m[4][4], const T v[3]) {
|
||||
T tmp[3];
|
||||
tmp[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
|
||||
tmp[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
|
||||
tmp[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2];
|
||||
dst[0] = tmp[0];
|
||||
dst[1] = tmp[1];
|
||||
dst[2] = tmp[2];
|
||||
}
|
||||
|
||||
static void MultV(nanort::real3<T> &dst, const T m[4][4], const T v[3]) {
|
||||
T tmp[3];
|
||||
tmp[0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0];
|
||||
tmp[1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1];
|
||||
tmp[2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2];
|
||||
dst[0] = tmp[0];
|
||||
dst[1] = tmp[1];
|
||||
dst[2] = tmp[2];
|
||||
}
|
||||
};
|
||||
|
||||
// typedef Matrix<float> Matrixf;
|
||||
// typedef Matrix<double> Matrixd;
|
||||
|
||||
template <typename T>
|
||||
static void XformBoundingBox(T xbmin[3], // out
|
||||
T xbmax[3], // out
|
||||
T bmin[3], T bmax[3], T m[4][4]) {
|
||||
// create bounding vertex from (bmin, bmax)
|
||||
T b[8][3];
|
||||
|
||||
b[0][0] = bmin[0];
|
||||
b[0][1] = bmin[1];
|
||||
b[0][2] = bmin[2];
|
||||
b[1][0] = bmax[0];
|
||||
b[1][1] = bmin[1];
|
||||
b[1][2] = bmin[2];
|
||||
b[2][0] = bmin[0];
|
||||
b[2][1] = bmax[1];
|
||||
b[2][2] = bmin[2];
|
||||
b[3][0] = bmax[0];
|
||||
b[3][1] = bmax[1];
|
||||
b[3][2] = bmin[2];
|
||||
|
||||
b[4][0] = bmin[0];
|
||||
b[4][1] = bmin[1];
|
||||
b[4][2] = bmax[2];
|
||||
b[5][0] = bmax[0];
|
||||
b[5][1] = bmin[1];
|
||||
b[5][2] = bmax[2];
|
||||
b[6][0] = bmin[0];
|
||||
b[6][1] = bmax[1];
|
||||
b[6][2] = bmax[2];
|
||||
b[7][0] = bmax[0];
|
||||
b[7][1] = bmax[1];
|
||||
b[7][2] = bmax[2];
|
||||
|
||||
T xb[8][3];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Matrix<T>::MultV(xb[i], m, b[i]);
|
||||
}
|
||||
|
||||
xbmin[0] = xb[0][0];
|
||||
xbmin[1] = xb[0][1];
|
||||
xbmin[2] = xb[0][2];
|
||||
xbmax[0] = xb[0][0];
|
||||
xbmax[1] = xb[0][1];
|
||||
xbmax[2] = xb[0][2];
|
||||
|
||||
for (int i = 1; i < 8; i++) {
|
||||
xbmin[0] = std::min(xb[i][0], xbmin[0]);
|
||||
xbmin[1] = std::min(xb[i][1], xbmin[1]);
|
||||
xbmin[2] = std::min(xb[i][2], xbmin[2]);
|
||||
|
||||
xbmax[0] = std::max(xb[i][0], xbmax[0]);
|
||||
xbmax[1] = std::max(xb[i][1], xbmax[1]);
|
||||
xbmax[2] = std::max(xb[i][2], xbmax[2]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Intersection {
|
||||
// required fields.
|
||||
T t; // hit distance
|
||||
unsigned int prim_id; // primitive ID of the hit
|
||||
float u;
|
||||
float v;
|
||||
|
||||
unsigned int node_id; // node ID of the hit.
|
||||
nanort::real3<T> P; // intersection point
|
||||
nanort::real3<T> Ns; // shading normal
|
||||
nanort::real3<T> Ng; // geometric normal
|
||||
};
|
||||
|
||||
///
|
||||
/// Renderable node
|
||||
///
|
||||
template <typename T, class M>
|
||||
class Node {
|
||||
public:
|
||||
typedef Node<T, M> type;
|
||||
|
||||
explicit Node(const M *mesh) : mesh_(mesh) {
|
||||
xbmin_[0] = xbmin_[1] = xbmin_[2] = std::numeric_limits<T>::max();
|
||||
xbmax_[0] = xbmax_[1] = xbmax_[2] = -std::numeric_limits<T>::max();
|
||||
|
||||
lbmin_[0] = lbmin_[1] = lbmin_[2] = std::numeric_limits<T>::max();
|
||||
lbmax_[0] = lbmax_[1] = lbmax_[2] = -std::numeric_limits<T>::max();
|
||||
|
||||
Matrix<T>::Identity(local_xform_);
|
||||
Matrix<T>::Identity(xform_);
|
||||
Matrix<T>::Identity(inv_xform_);
|
||||
Matrix<T>::Identity(inv_xform33_);
|
||||
inv_xform33_[3][3] = static_cast<T>(0.0);
|
||||
Matrix<T>::Identity(inv_transpose_xform33_);
|
||||
inv_transpose_xform33_[3][3] = static_cast<T>(0.0);
|
||||
}
|
||||
|
||||
~Node() {}
|
||||
|
||||
void Copy(const type &rhs) {
|
||||
Matrix<T>::Copy(local_xform_, rhs.local_xform_);
|
||||
Matrix<T>::Copy(xform_, rhs.xform_);
|
||||
Matrix<T>::Copy(inv_xform_, rhs.inv_xform_);
|
||||
Matrix<T>::Copy(inv_xform33_, rhs.inv_xform33_);
|
||||
Matrix<T>::Copy(inv_transpose_xform33_, rhs.inv_transpose_xform33_);
|
||||
|
||||
lbmin_[0] = rhs.lbmin_[0];
|
||||
lbmin_[1] = rhs.lbmin_[1];
|
||||
lbmin_[2] = rhs.lbmin_[2];
|
||||
|
||||
lbmax_[0] = rhs.lbmax_[0];
|
||||
lbmax_[1] = rhs.lbmax_[1];
|
||||
lbmax_[2] = rhs.lbmax_[2];
|
||||
|
||||
xbmin_[0] = rhs.xbmin_[0];
|
||||
xbmin_[1] = rhs.xbmin_[1];
|
||||
xbmin_[2] = rhs.xbmin_[2];
|
||||
|
||||
xbmax_[0] = rhs.xbmax_[0];
|
||||
xbmax_[1] = rhs.xbmax_[1];
|
||||
xbmax_[2] = rhs.xbmax_[2];
|
||||
|
||||
mesh_ = rhs.mesh_;
|
||||
name_ = rhs.name_;
|
||||
|
||||
children_ = rhs.children_;
|
||||
}
|
||||
|
||||
Node(const type &rhs) { Copy(rhs); }
|
||||
|
||||
const type &operator=(const type &rhs) {
|
||||
Copy(rhs);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
void SetName(const std::string &name) { name_ = name; }
|
||||
|
||||
const std::string &GetName() const { return name_; }
|
||||
|
||||
///
|
||||
/// Add child node.
|
||||
///
|
||||
void AddChild(const type &child) { children_.push_back(child); }
|
||||
|
||||
///
|
||||
/// Get chidren
|
||||
///
|
||||
const std::vector<type> &GetChildren() const { return children_; }
|
||||
|
||||
std::vector<type> &GetChildren() { return children_; }
|
||||
|
||||
///
|
||||
/// Update internal state.
|
||||
///
|
||||
void Update(const T parent_xform[4][4]) {
|
||||
if (!accel_.IsValid() && mesh_ && (mesh_->vertices.size() > 3) &&
|
||||
(mesh_->faces.size() >= 3)) {
|
||||
// Assume mesh is composed of triangle faces only.
|
||||
nanort::TriangleMesh<float> triangle_mesh(
|
||||
mesh_->vertices.data(), mesh_->faces.data(), mesh_->stride);
|
||||
nanort::TriangleSAHPred<float> triangle_pred(
|
||||
mesh_->vertices.data(), mesh_->faces.data(), mesh_->stride);
|
||||
|
||||
bool ret =
|
||||
accel_.Build(static_cast<unsigned int>(mesh_->faces.size()) / 3,
|
||||
triangle_mesh, triangle_pred);
|
||||
|
||||
// Update local bbox.
|
||||
if (ret) {
|
||||
accel_.BoundingBox(lbmin_, lbmax_);
|
||||
}
|
||||
}
|
||||
|
||||
// xform = parent_xform x local_xform
|
||||
Matrix<T>::Mult(xform_, parent_xform, local_xform_);
|
||||
|
||||
// Compute the bounding box in world coordinate.
|
||||
XformBoundingBox(xbmin_, xbmax_, lbmin_, lbmax_, xform_);
|
||||
|
||||
// Inverse(xform)
|
||||
Matrix<T>::Copy(inv_xform_, xform_);
|
||||
Matrix<T>::Inverse(inv_xform_);
|
||||
|
||||
// Clear translation, then inverse(xform)
|
||||
Matrix<T>::Copy(inv_xform33_, xform_);
|
||||
inv_xform33_[3][0] = static_cast<T>(0.0);
|
||||
inv_xform33_[3][1] = static_cast<T>(0.0);
|
||||
inv_xform33_[3][2] = static_cast<T>(0.0);
|
||||
Matrix<T>::Inverse(inv_xform33_);
|
||||
|
||||
// Inverse transpose of xform33
|
||||
Matrix<T>::Copy(inv_transpose_xform33_, inv_xform33_);
|
||||
Matrix<T>::Transpose(inv_transpose_xform33_);
|
||||
|
||||
// Update children nodes
|
||||
for (size_t i = 0; i < children_.size(); i++) {
|
||||
children_[i].Update(xform_);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set local transformation.
|
||||
///
|
||||
void SetLocalXform(const T xform[4][4]) {
|
||||
memcpy(local_xform_, xform, sizeof(float) * 16);
|
||||
}
|
||||
|
||||
const T *GetLocalXformPtr() const { return &local_xform_[0][0]; }
|
||||
|
||||
const T *GetXformPtr() const { return &xform_[0][0]; }
|
||||
|
||||
const M *GetMesh() const { return mesh_; }
|
||||
|
||||
const nanort::BVHAccel<T> &GetAccel() const { return accel_; }
|
||||
|
||||
inline void GetWorldBoundingBox(T bmin[3], T bmax[3]) const {
|
||||
bmin[0] = xbmin_[0];
|
||||
bmin[1] = xbmin_[1];
|
||||
bmin[2] = xbmin_[2];
|
||||
|
||||
bmax[0] = xbmax_[0];
|
||||
bmax[1] = xbmax_[1];
|
||||
bmax[2] = xbmax_[2];
|
||||
}
|
||||
|
||||
inline void GetLocalBoundingBox(T bmin[3], T bmax[3]) const {
|
||||
bmin[0] = lbmin_[0];
|
||||
bmin[1] = lbmin_[1];
|
||||
bmin[2] = lbmin_[2];
|
||||
|
||||
bmax[0] = lbmax_[0];
|
||||
bmax[1] = lbmax_[1];
|
||||
bmax[2] = lbmax_[2];
|
||||
}
|
||||
|
||||
T local_xform_[4][4]; // Node's local transformation matrix.
|
||||
T xform_[4][4]; // Parent xform x local_xform.
|
||||
T inv_xform_[4][4]; // inverse(xform). world -> local
|
||||
T inv_xform33_[4][4]; // inverse(xform0 with upper-left 3x3 elemets only(for
|
||||
// transforming direction vector)
|
||||
T inv_transpose_xform33_[4][4]; // inverse(transpose(xform)) with upper-left
|
||||
// 3x3 elements only(for transforming normal
|
||||
// vector)
|
||||
|
||||
private:
|
||||
// bounding box(local space)
|
||||
T lbmin_[3];
|
||||
T lbmax_[3];
|
||||
|
||||
// bounding box after xform(world space)
|
||||
T xbmin_[3];
|
||||
T xbmax_[3];
|
||||
|
||||
nanort::BVHAccel<T> accel_;
|
||||
|
||||
std::string name_;
|
||||
|
||||
const M *mesh_;
|
||||
|
||||
std::vector<type> children_;
|
||||
};
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
// Predefined SAH predicator for cube.
|
||||
template <typename T, class M>
|
||||
class NodeBBoxPred {
|
||||
public:
|
||||
NodeBBoxPred(const std::vector<Node<T, M> > *nodes)
|
||||
: axis_(0), pos_(0.0f), nodes_(nodes) {}
|
||||
|
||||
void Set(int axis, float pos) const {
|
||||
axis_ = axis;
|
||||
pos_ = pos;
|
||||
}
|
||||
|
||||
bool operator()(unsigned int i) const {
|
||||
int axis = axis_;
|
||||
float pos = pos_;
|
||||
|
||||
T bmin[3], bmax[3];
|
||||
|
||||
(*nodes_)[i].GetWorldBoundingBox(bmin, bmax);
|
||||
|
||||
T center = bmax[axis] - bmin[axis];
|
||||
|
||||
return (center < pos);
|
||||
}
|
||||
|
||||
private:
|
||||
mutable int axis_;
|
||||
mutable float pos_;
|
||||
const std::vector<Node<T, M> > *nodes_;
|
||||
};
|
||||
|
||||
template <typename T, class M>
|
||||
class NodeBBoxGeometry {
|
||||
public:
|
||||
NodeBBoxGeometry(const std::vector<Node<T, M> > *nodes) : nodes_(nodes) {}
|
||||
|
||||
/// Compute bounding box for `prim_index`th cube.
|
||||
/// This function is called for each primitive in BVH build.
|
||||
void BoundingBox(nanort::real3<T> *bmin, nanort::real3<T> *bmax,
|
||||
unsigned int prim_index) const {
|
||||
T a[3], b[3];
|
||||
(*nodes_)[prim_index].GetWorldBoundingBox(a, b);
|
||||
(*bmin)[0] = a[0];
|
||||
(*bmin)[1] = a[1];
|
||||
(*bmin)[2] = a[2];
|
||||
(*bmax)[0] = b[0];
|
||||
(*bmax)[1] = b[1];
|
||||
(*bmax)[2] = b[2];
|
||||
}
|
||||
|
||||
const std::vector<Node<T, M> > *nodes_;
|
||||
mutable nanort::real3<T> ray_org_;
|
||||
mutable nanort::real3<T> ray_dir_;
|
||||
mutable nanort::BVHTraceOptions trace_options_;
|
||||
int _pad_;
|
||||
};
|
||||
|
||||
class NodeBBoxIntersection {
|
||||
public:
|
||||
NodeBBoxIntersection() {}
|
||||
|
||||
float normal[3];
|
||||
|
||||
// Required member variables.
|
||||
float t;
|
||||
unsigned int prim_id;
|
||||
};
|
||||
|
||||
template <typename T, class M>
|
||||
class NodeBBoxIntersector {
|
||||
public:
|
||||
NodeBBoxIntersector(const std::vector<Node<T, M> > *nodes) : nodes_(nodes) {}
|
||||
|
||||
bool Intersect(float *out_t_min, float *out_t_max,
|
||||
unsigned int prim_index) const {
|
||||
T bmin[3], bmax[3];
|
||||
|
||||
(*nodes_)[prim_index].GetWorldBoundingBox(bmin, bmax);
|
||||
|
||||
float tmin, tmax;
|
||||
|
||||
const float min_x = ray_dir_sign_[0] ? bmax[0] : bmin[0];
|
||||
const float min_y = ray_dir_sign_[1] ? bmax[1] : bmin[1];
|
||||
const float min_z = ray_dir_sign_[2] ? bmax[2] : bmin[2];
|
||||
const float max_x = ray_dir_sign_[0] ? bmin[0] : bmax[0];
|
||||
const float max_y = ray_dir_sign_[1] ? bmin[1] : bmax[1];
|
||||
const float max_z = ray_dir_sign_[2] ? bmin[2] : bmax[2];
|
||||
|
||||
// X
|
||||
const float tmin_x = (min_x - ray_org_[0]) * ray_inv_dir_[0];
|
||||
const float tmax_x = (max_x - ray_org_[0]) * ray_inv_dir_[0];
|
||||
|
||||
// Y
|
||||
const float tmin_y = (min_y - ray_org_[1]) * ray_inv_dir_[1];
|
||||
const float tmax_y = (max_y - ray_org_[1]) * ray_inv_dir_[1];
|
||||
|
||||
// Z
|
||||
const float tmin_z = (min_z - ray_org_[2]) * ray_inv_dir_[2];
|
||||
const float tmax_z = (max_z - ray_org_[2]) * ray_inv_dir_[2];
|
||||
|
||||
tmin = nanort::safemax(tmin_z, nanort::safemax(tmin_y, tmin_x));
|
||||
tmax = nanort::safemin(tmax_z, nanort::safemin(tmax_y, tmax_x));
|
||||
|
||||
if (tmin <= tmax) {
|
||||
(*out_t_min) = tmin;
|
||||
(*out_t_max) = tmax;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Prepare BVH traversal(e.g. compute inverse ray direction)
|
||||
/// This function is called only once in BVH traversal.
|
||||
void PrepareTraversal(const nanort::Ray<float> &ray) const {
|
||||
ray_org_[0] = ray.org[0];
|
||||
ray_org_[1] = ray.org[1];
|
||||
ray_org_[2] = ray.org[2];
|
||||
|
||||
ray_dir_[0] = ray.dir[0];
|
||||
ray_dir_[1] = ray.dir[1];
|
||||
ray_dir_[2] = ray.dir[2];
|
||||
|
||||
// FIXME(syoyo): Consider zero div case.
|
||||
ray_inv_dir_[0] = static_cast<T>(1.0) / ray.dir[0];
|
||||
ray_inv_dir_[1] = static_cast<T>(1.0) / ray.dir[1];
|
||||
ray_inv_dir_[2] = static_cast<T>(1.0) / ray.dir[2];
|
||||
|
||||
ray_dir_sign_[0] = ray.dir[0] < static_cast<T>(0.0) ? 1 : 0;
|
||||
ray_dir_sign_[1] = ray.dir[1] < static_cast<T>(0.0) ? 1 : 0;
|
||||
ray_dir_sign_[2] = ray.dir[2] < static_cast<T>(0.0) ? 1 : 0;
|
||||
}
|
||||
|
||||
const std::vector<Node<T, M> > *nodes_;
|
||||
mutable nanort::real3<T> ray_org_;
|
||||
mutable nanort::real3<T> ray_dir_;
|
||||
mutable nanort::real3<T> ray_inv_dir_;
|
||||
mutable int ray_dir_sign_[3];
|
||||
};
|
||||
|
||||
template <typename T, class M>
|
||||
class Scene {
|
||||
public:
|
||||
Scene() {
|
||||
bmin_[0] = bmin_[1] = bmin_[2] = std::numeric_limits<T>::max();
|
||||
bmax_[0] = bmax_[1] = bmax_[2] = -std::numeric_limits<T>::max();
|
||||
}
|
||||
|
||||
~Scene() {}
|
||||
|
||||
///
|
||||
/// Add intersectable node to the scene.
|
||||
///
|
||||
bool AddNode(const Node<T, M> &node) {
|
||||
nodes_.push_back(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<Node<T, M> > &GetNodes() const { return nodes_; }
|
||||
|
||||
bool FindNode(const std::string &name, Node<T, M> **found_node) {
|
||||
if (!found_node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Simple exhaustive search.
|
||||
for (size_t i = 0; i < nodes_.size(); i++) {
|
||||
if (FindNodeRecursive(name, &(nodes_[i]), found_node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///
|
||||
/// Commit the scene. Must be called before tracing rays into the scene.
|
||||
///
|
||||
bool Commit() {
|
||||
// the scene should contains something
|
||||
if (nodes_.size() == 0) {
|
||||
std::cerr << "You are attempting to commit an empty scene!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update nodes.
|
||||
for (size_t i = 0; i < nodes_.size(); i++) {
|
||||
T ident[4][4];
|
||||
Matrix<T>::Identity(ident);
|
||||
|
||||
nodes_[i].Update(ident);
|
||||
}
|
||||
|
||||
// Build toplevel BVH.
|
||||
NodeBBoxGeometry<T, M> geom(&nodes_);
|
||||
NodeBBoxPred<T, M> pred(&nodes_);
|
||||
|
||||
// FIXME(LTE): Limit one leaf contains one node bbox primitive. This would
|
||||
// work, but would be inefficient.
|
||||
// e.g. will miss some node when constructed BVH depth is larger than the
|
||||
// value of BVHBuildOptions.
|
||||
// Implement more better and efficient BVH build and traverse for Toplevel
|
||||
// BVH.
|
||||
nanort::BVHBuildOptions<T> build_options;
|
||||
build_options.min_leaf_primitives = 1;
|
||||
|
||||
bool ret = toplevel_accel_.Build(static_cast<unsigned int>(nodes_.size()),
|
||||
geom, pred, build_options);
|
||||
|
||||
nanort::BVHBuildStatistics stats = toplevel_accel_.GetStatistics();
|
||||
(void)stats;
|
||||
|
||||
// toplevel_accel_.Debug();
|
||||
|
||||
if (ret) {
|
||||
toplevel_accel_.BoundingBox(bmin_, bmax_);
|
||||
} else {
|
||||
// Set invalid bbox value.
|
||||
bmin_[0] = std::numeric_limits<T>::max();
|
||||
bmin_[1] = std::numeric_limits<T>::max();
|
||||
bmin_[2] = std::numeric_limits<T>::max();
|
||||
|
||||
bmax_[0] = -std::numeric_limits<T>::max();
|
||||
bmax_[1] = -std::numeric_limits<T>::max();
|
||||
bmax_[2] = -std::numeric_limits<T>::max();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the scene bounding box.
|
||||
///
|
||||
void GetBoundingBox(T bmin[3], T bmax[3]) const {
|
||||
bmin[0] = bmin_[0];
|
||||
bmin[1] = bmin_[1];
|
||||
bmin[2] = bmin_[2];
|
||||
|
||||
bmax[0] = bmax_[0];
|
||||
bmax[1] = bmax_[1];
|
||||
bmax[2] = bmax_[2];
|
||||
}
|
||||
|
||||
///
|
||||
/// Trace the ray into the scene.
|
||||
/// First find the intersection of nodes' bounding box using toplevel BVH.
|
||||
/// Then, trace into the hit node to find the intersection of the primitive.
|
||||
///
|
||||
template <class H>
|
||||
bool Traverse(nanort::Ray<T> &ray, H *isect,
|
||||
const bool cull_back_face = false) const {
|
||||
if (!toplevel_accel_.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int kMaxIntersections = 64;
|
||||
|
||||
bool has_hit = false;
|
||||
|
||||
NodeBBoxIntersector<T, M> isector(&nodes_);
|
||||
nanort::StackVector<nanort::NodeHit<T>, 128> node_hits;
|
||||
bool may_hit = toplevel_accel_.ListNodeIntersections(ray, kMaxIntersections,
|
||||
isector, &node_hits);
|
||||
|
||||
if (may_hit) {
|
||||
T t_max = std::numeric_limits<T>::max();
|
||||
T t_nearest = t_max;
|
||||
|
||||
nanort::BVHTraceOptions trace_options;
|
||||
trace_options.cull_back_face = cull_back_face;
|
||||
|
||||
// Find actual intersection point.
|
||||
for (size_t i = 0; i < node_hits->size(); i++) {
|
||||
// Early cull test.
|
||||
if (t_nearest < node_hits[i].t_min) {
|
||||
// printf("near: %f, t_min: %f, t_max: %f\n", t_nearest,
|
||||
// node_hits[i].t_min, node_hits[i].t_max);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(node_hits[i].node_id < nodes_.size());
|
||||
const Node<T, M> &node = nodes_[node_hits[i].node_id];
|
||||
|
||||
// Transform ray into node's local space
|
||||
// TODO(LTE): Set ray tmin and tmax
|
||||
nanort::Ray<T> local_ray;
|
||||
Matrix<T>::MultV(local_ray.org, node.inv_xform_, ray.org);
|
||||
Matrix<T>::MultV(local_ray.dir, node.inv_xform33_, ray.dir);
|
||||
|
||||
nanort::TriangleIntersector<T, H> triangle_intersector(
|
||||
node.GetMesh()->vertices.data(), node.GetMesh()->faces.data(),
|
||||
node.GetMesh()->stride);
|
||||
H local_isect;
|
||||
|
||||
bool hit = node.GetAccel().Traverse(local_ray, triangle_intersector,
|
||||
&local_isect);
|
||||
|
||||
if (hit) {
|
||||
// Calulcate hit distance in world coordiante.
|
||||
T local_P[3];
|
||||
local_P[0] = local_ray.org[0] + local_isect.t * local_ray.dir[0];
|
||||
local_P[1] = local_ray.org[1] + local_isect.t * local_ray.dir[1];
|
||||
local_P[2] = local_ray.org[2] + local_isect.t * local_ray.dir[2];
|
||||
|
||||
T world_P[3];
|
||||
Matrix<T>::MultV(world_P, node.xform_, local_P);
|
||||
|
||||
nanort::real3<T> po;
|
||||
po[0] = world_P[0] - ray.org[0];
|
||||
po[1] = world_P[1] - ray.org[1];
|
||||
po[2] = world_P[2] - ray.org[2];
|
||||
|
||||
float t_world = vlength(po);
|
||||
// printf("tworld %f, tnear %f\n", t_world, t_nearest);
|
||||
|
||||
if (t_world < t_nearest) {
|
||||
t_nearest = t_world;
|
||||
has_hit = true;
|
||||
//(*isect) = local_isect;
|
||||
isect->node_id = node_hits[i].node_id;
|
||||
isect->prim_id = local_isect.prim_id;
|
||||
isect->u = local_isect.u;
|
||||
isect->v = local_isect.v;
|
||||
|
||||
// TODO(LTE): Implement
|
||||
T Ng[3], Ns[3]; // geometric normal, shading normal.
|
||||
|
||||
node.GetMesh()->GetNormal(Ng, Ns, isect->prim_id, isect->u,
|
||||
isect->v);
|
||||
|
||||
// Convert position and normal into world coordinate.
|
||||
isect->t = t_world;
|
||||
Matrix<T>::MultV(isect->P, node.xform_, local_P);
|
||||
Matrix<T>::MultV(isect->Ng, node.inv_transpose_xform33_, Ng);
|
||||
Matrix<T>::MultV(isect->Ns, node.inv_transpose_xform33_, Ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return has_hit;
|
||||
}
|
||||
|
||||
private:
|
||||
///
|
||||
/// Find a node by name.
|
||||
///
|
||||
bool FindNodeRecursive(const std::string &name, Node<T, M> *root,
|
||||
Node<T, M> **found_node) {
|
||||
if (root->GetName().compare(name) == 0) {
|
||||
(*found_node) = root;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Simple exhaustive search.
|
||||
for (size_t i = 0; i < root->GetChildren().size(); i++) {
|
||||
if (FindNodeRecursive(name, &(root->GetChildren()[i]), found_node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scene bounding box.
|
||||
// Valid after calling `Commit()`.
|
||||
T bmin_[3];
|
||||
T bmax_[3];
|
||||
|
||||
// Toplevel BVH accel.
|
||||
nanort::BVHAccel<T> toplevel_accel_;
|
||||
std::vector<Node<T, M> > nodes_;
|
||||
};
|
||||
|
||||
} // namespace nanosg
|
||||
|
||||
#endif // NANOSG_H_
|
|
@ -0,0 +1,458 @@
|
|||
#include "obj-loader.h"
|
||||
#include "nanort.h" // for float3
|
||||
|
||||
#define TINYOBJLOADER_IMPLEMENTATION
|
||||
#include "tiny_obj_loader.h"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#pragma clang diagnostic ignored "-Wreserved-id-macro"
|
||||
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
|
||||
#pragma clang diagnostic ignored "-Wcast-align"
|
||||
#pragma clang diagnostic ignored "-Wpadded"
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion"
|
||||
#pragma clang diagnostic ignored "-Wvariadic-macros"
|
||||
#pragma clang diagnostic ignored "-Wc++11-extensions"
|
||||
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
|
||||
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
|
||||
#if __has_warning("-Wdouble-promotion")
|
||||
#pragma clang diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
#if __has_warning("-Wcomma")
|
||||
#pragma clang diagnostic ignored "-Wcomma"
|
||||
#endif
|
||||
#if __has_warning("-Wcast-qual")
|
||||
#pragma clang diagnostic ignored "-Wcast-qual"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "stb_image.h"
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef NANOSG_USE_CXX11
|
||||
#include <unordered_map>
|
||||
#else
|
||||
#include <map>
|
||||
#endif
|
||||
|
||||
#define USE_TEX_CACHE 1
|
||||
|
||||
namespace example {
|
||||
|
||||
typedef nanort::real3<float> float3;
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||
#pragma clang diagnostic ignored "-Wglobal-constructors"
|
||||
#endif
|
||||
|
||||
// TODO(LTE): Remove global static definition.
|
||||
#ifdef NANOSG_USE_CXX11
|
||||
static std::unordered_map<std::string, int> hashed_tex;
|
||||
#else
|
||||
static std::map<std::string, int> hashed_tex;
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
inline void CalcNormal(float3 &N, float3 v0, float3 v1, float3 v2) {
|
||||
float3 v10 = v1 - v0;
|
||||
float3 v20 = v2 - v0;
|
||||
|
||||
N = vcross(v20, v10);
|
||||
N = vnormalize(N);
|
||||
}
|
||||
|
||||
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 LoadTexture(const std::string &filename,
|
||||
std::vector<Texture> *textures) {
|
||||
int idx;
|
||||
|
||||
if (filename.empty()) return -1;
|
||||
|
||||
std::cout << " Loading texture : " << filename << std::endl;
|
||||
Texture texture;
|
||||
|
||||
// tigra: find in cache. get index
|
||||
if (USE_TEX_CACHE) {
|
||||
if (hashed_tex.find(filename) != hashed_tex.end()) {
|
||||
puts("from cache");
|
||||
return hashed_tex[filename];
|
||||
}
|
||||
}
|
||||
|
||||
int w, h, n;
|
||||
unsigned char *data = stbi_load(filename.c_str(), &w, &h, &n, 0);
|
||||
if (data) {
|
||||
texture.width = w;
|
||||
texture.height = h;
|
||||
texture.components = n;
|
||||
|
||||
size_t n_elem = size_t(w * h * n);
|
||||
texture.image = new unsigned char[n_elem];
|
||||
for (size_t i = 0; i < n_elem; i++) {
|
||||
texture.image[i] = data[i];
|
||||
}
|
||||
|
||||
free(data);
|
||||
|
||||
textures->push_back(texture);
|
||||
|
||||
idx = int(textures->size()) - 1;
|
||||
|
||||
// tigra: store index to cache
|
||||
if (USE_TEX_CACHE) {
|
||||
hashed_tex[filename] = idx;
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
std::cout << " Failed to load : " << filename << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ComputeBoundingBoxOfMesh(float bmin[3], float bmax[3],
|
||||
const example::Mesh<float> &mesh) {
|
||||
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
|
||||
bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
|
||||
|
||||
for (size_t i = 0; i < mesh.vertices.size() / 3; i++) {
|
||||
bmin[0] = std::min(bmin[0], mesh.vertices[3 * i + 0]);
|
||||
bmin[1] = std::min(bmin[1], mesh.vertices[3 * i + 1]);
|
||||
bmin[2] = std::min(bmin[1], mesh.vertices[3 * i + 2]);
|
||||
|
||||
bmax[0] = std::max(bmax[0], mesh.vertices[3 * i + 0]);
|
||||
bmax[1] = std::max(bmax[1], mesh.vertices[3 * i + 1]);
|
||||
bmax[2] = std::max(bmax[2], mesh.vertices[3 * i + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadObj(const std::string &filename, float scale,
|
||||
std::vector<Mesh<float> > *meshes,
|
||||
std::vector<Material> *out_materials,
|
||||
std::vector<Texture> *out_textures) {
|
||||
tinyobj::attrib_t attrib;
|
||||
std::vector<tinyobj::shape_t> shapes;
|
||||
std::vector<tinyobj::material_t> materials;
|
||||
std::string err;
|
||||
|
||||
std::string basedir = GetBaseDir(filename) + "/";
|
||||
const char *basepath = (basedir.compare("/") == 0) ? NULL : basedir.c_str();
|
||||
|
||||
// auto t_start = std::chrono::system_clock::now();
|
||||
|
||||
bool ret =
|
||||
tinyobj::LoadObj(&attrib, &shapes, &materials, &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 (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// std::cout << "[LoadOBJ] Parse time : " << ms.count() << " [msecs]"
|
||||
// << std::endl;
|
||||
|
||||
std::cout << "[LoadOBJ] # of shapes in .obj : " << shapes.size() << std::endl;
|
||||
std::cout << "[LoadOBJ] # of materials in .obj : " << materials.size()
|
||||
<< std::endl;
|
||||
|
||||
{
|
||||
size_t total_num_vertices = 0;
|
||||
size_t total_num_faces = 0;
|
||||
|
||||
total_num_vertices = attrib.vertices.size() / 3;
|
||||
std::cout << " vertices : " << attrib.vertices.size() / 3 << std::endl;
|
||||
|
||||
for (size_t i = 0; i < shapes.size(); i++) {
|
||||
std::cout << " shape[" << i << "].name : " << shapes[i].name
|
||||
<< std::endl;
|
||||
std::cout << " shape[" << i
|
||||
<< "].indices : " << shapes[i].mesh.indices.size() << std::endl;
|
||||
assert((shapes[i].mesh.indices.size() % 3) == 0);
|
||||
|
||||
total_num_faces += shapes[i].mesh.indices.size() / 3;
|
||||
|
||||
// tigra: empty name convert to _id
|
||||
if (shapes[i].name.length() == 0) {
|
||||
#ifdef NANOSG_USE_CXX11
|
||||
shapes[i].name = "_" + std::to_string(i);
|
||||
#else
|
||||
std::stringstream ss;
|
||||
ss << i;
|
||||
shapes[i].name = "_" + ss.str();
|
||||
#endif
|
||||
std::cout << " EMPTY shape[" << i << "].name, new : " << shapes[i].name
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "[LoadOBJ] # of faces: " << total_num_faces << std::endl;
|
||||
std::cout << "[LoadOBJ] # of vertices: " << total_num_vertices << std::endl;
|
||||
}
|
||||
|
||||
// TODO(LTE): Implement tangents and binormals
|
||||
|
||||
for (size_t i = 0; i < shapes.size(); i++) {
|
||||
Mesh<float> mesh(/* stride */ sizeof(float) * 3);
|
||||
|
||||
mesh.name = shapes[i].name;
|
||||
|
||||
const size_t num_faces = shapes[i].mesh.indices.size() / 3;
|
||||
mesh.faces.resize(num_faces * 3);
|
||||
mesh.material_ids.resize(num_faces);
|
||||
mesh.facevarying_normals.resize(num_faces * 3 * 3);
|
||||
mesh.facevarying_uvs.resize(num_faces * 3 * 2);
|
||||
mesh.vertices.resize(num_faces * 3 * 3);
|
||||
|
||||
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
|
||||
// reorder vertices. may create duplicated vertices.
|
||||
size_t f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
|
||||
size_t f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
|
||||
size_t f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
|
||||
|
||||
mesh.vertices[9 * f + 0] = scale * attrib.vertices[3 * f0 + 0];
|
||||
mesh.vertices[9 * f + 1] = scale * attrib.vertices[3 * f0 + 1];
|
||||
mesh.vertices[9 * f + 2] = scale * attrib.vertices[3 * f0 + 2];
|
||||
|
||||
mesh.vertices[9 * f + 3] = scale * attrib.vertices[3 * f1 + 0];
|
||||
mesh.vertices[9 * f + 4] = scale * attrib.vertices[3 * f1 + 1];
|
||||
mesh.vertices[9 * f + 5] = scale * attrib.vertices[3 * f1 + 2];
|
||||
|
||||
mesh.vertices[9 * f + 6] = scale * attrib.vertices[3 * f2 + 0];
|
||||
mesh.vertices[9 * f + 7] = scale * attrib.vertices[3 * f2 + 1];
|
||||
mesh.vertices[9 * f + 8] = scale * attrib.vertices[3 * f2 + 2];
|
||||
|
||||
mesh.faces[3 * f + 0] = static_cast<unsigned int>(3 * f + 0);
|
||||
mesh.faces[3 * f + 1] = static_cast<unsigned int>(3 * f + 1);
|
||||
mesh.faces[3 * f + 2] = static_cast<unsigned int>(3 * f + 2);
|
||||
|
||||
mesh.material_ids[f] =
|
||||
static_cast<unsigned int>(shapes[i].mesh.material_ids[f]);
|
||||
}
|
||||
|
||||
if (attrib.normals.size() > 0) {
|
||||
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
|
||||
size_t f0, f1, f2;
|
||||
|
||||
f0 = size_t(shapes[i].mesh.indices[3 * f + 0].normal_index);
|
||||
f1 = size_t(shapes[i].mesh.indices[3 * f + 1].normal_index);
|
||||
f2 = size_t(shapes[i].mesh.indices[3 * f + 2].normal_index);
|
||||
|
||||
if (f0 > 0 && f1 > 0 && f2 > 0) {
|
||||
float n0[3], n1[3], n2[3];
|
||||
|
||||
n0[0] = attrib.normals[3 * f0 + 0];
|
||||
n0[1] = attrib.normals[3 * f0 + 1];
|
||||
n0[2] = attrib.normals[3 * f0 + 2];
|
||||
|
||||
n1[0] = attrib.normals[3 * f1 + 0];
|
||||
n1[1] = attrib.normals[3 * f1 + 1];
|
||||
n1[2] = attrib.normals[3 * f1 + 2];
|
||||
|
||||
n2[0] = attrib.normals[3 * f2 + 0];
|
||||
n2[1] = attrib.normals[3 * f2 + 1];
|
||||
n2[2] = attrib.normals[3 * f2 + 2];
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 0] = n0[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 1] = n0[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 2] = n0[2];
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 0] = n1[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 1] = n1[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 2] = n1[2];
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 0] = n2[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 1] = n2[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 2] = n2[2];
|
||||
} else { // face contains invalid normal index. calc geometric normal.
|
||||
f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
|
||||
f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
|
||||
f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
|
||||
|
||||
float3 v0, v1, v2;
|
||||
|
||||
v0[0] = attrib.vertices[3 * f0 + 0];
|
||||
v0[1] = attrib.vertices[3 * f0 + 1];
|
||||
v0[2] = attrib.vertices[3 * f0 + 2];
|
||||
|
||||
v1[0] = attrib.vertices[3 * f1 + 0];
|
||||
v1[1] = attrib.vertices[3 * f1 + 1];
|
||||
v1[2] = attrib.vertices[3 * f1 + 2];
|
||||
|
||||
v2[0] = attrib.vertices[3 * f2 + 0];
|
||||
v2[1] = attrib.vertices[3 * f2 + 1];
|
||||
v2[2] = attrib.vertices[3 * f2 + 2];
|
||||
|
||||
float3 N;
|
||||
CalcNormal(N, v0, v1, v2);
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2];
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2];
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// calc geometric normal
|
||||
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
|
||||
size_t f0, f1, f2;
|
||||
|
||||
f0 = size_t(shapes[i].mesh.indices[3 * f + 0].vertex_index);
|
||||
f1 = size_t(shapes[i].mesh.indices[3 * f + 1].vertex_index);
|
||||
f2 = size_t(shapes[i].mesh.indices[3 * f + 2].vertex_index);
|
||||
|
||||
float3 v0, v1, v2;
|
||||
|
||||
v0[0] = attrib.vertices[3 * f0 + 0];
|
||||
v0[1] = attrib.vertices[3 * f0 + 1];
|
||||
v0[2] = attrib.vertices[3 * f0 + 2];
|
||||
|
||||
v1[0] = attrib.vertices[3 * f1 + 0];
|
||||
v1[1] = attrib.vertices[3 * f1 + 1];
|
||||
v1[2] = attrib.vertices[3 * f1 + 2];
|
||||
|
||||
v2[0] = attrib.vertices[3 * f2 + 0];
|
||||
v2[1] = attrib.vertices[3 * f2 + 1];
|
||||
v2[2] = attrib.vertices[3 * f2 + 2];
|
||||
|
||||
float3 N;
|
||||
CalcNormal(N, v0, v1, v2);
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 0] = N[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 1] = N[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 0) + 2] = N[2];
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 0] = N[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 1] = N[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 1) + 2] = N[2];
|
||||
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 0] = N[0];
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 1] = N[1];
|
||||
mesh.facevarying_normals[3 * (3 * f + 2) + 2] = N[2];
|
||||
}
|
||||
}
|
||||
|
||||
if (attrib.texcoords.size() > 0) {
|
||||
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
|
||||
size_t f0, f1, f2;
|
||||
|
||||
f0 = size_t(shapes[i].mesh.indices[3 * f + 0].texcoord_index);
|
||||
f1 = size_t(shapes[i].mesh.indices[3 * f + 1].texcoord_index);
|
||||
f2 = size_t(shapes[i].mesh.indices[3 * f + 2].texcoord_index);
|
||||
|
||||
if (f0 > 0 && f1 > 0 && f2 > 0) {
|
||||
float3 n0, n1, n2;
|
||||
|
||||
n0[0] = attrib.texcoords[2 * f0 + 0];
|
||||
n0[1] = attrib.texcoords[2 * f0 + 1];
|
||||
|
||||
n1[0] = attrib.texcoords[2 * f1 + 0];
|
||||
n1[1] = attrib.texcoords[2 * f1 + 1];
|
||||
|
||||
n2[0] = attrib.texcoords[2 * f2 + 0];
|
||||
n2[1] = attrib.texcoords[2 * f2 + 1];
|
||||
|
||||
mesh.facevarying_uvs[2 * (3 * f + 0) + 0] = n0[0];
|
||||
mesh.facevarying_uvs[2 * (3 * f + 0) + 1] = n0[1];
|
||||
|
||||
mesh.facevarying_uvs[2 * (3 * f + 1) + 0] = n1[0];
|
||||
mesh.facevarying_uvs[2 * (3 * f + 1) + 1] = n1[1];
|
||||
|
||||
mesh.facevarying_uvs[2 * (3 * f + 2) + 0] = n2[0];
|
||||
mesh.facevarying_uvs[2 * (3 * f + 2) + 1] = n2[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute pivot translation and add offset to the vertices.
|
||||
float bmin[3], bmax[3];
|
||||
ComputeBoundingBoxOfMesh(bmin, bmax, mesh);
|
||||
|
||||
float bcenter[3];
|
||||
bcenter[0] = 0.5f * (bmax[0] - bmin[0]) + bmin[0];
|
||||
bcenter[1] = 0.5f * (bmax[1] - bmin[1]) + bmin[1];
|
||||
bcenter[2] = 0.5f * (bmax[2] - bmin[2]) + bmin[2];
|
||||
|
||||
for (size_t v = 0; v < mesh.vertices.size() / 3; v++) {
|
||||
mesh.vertices[3 * v + 0] -= bcenter[0];
|
||||
mesh.vertices[3 * v + 1] -= bcenter[1];
|
||||
mesh.vertices[3 * v + 2] -= bcenter[2];
|
||||
}
|
||||
|
||||
mesh.pivot_xform[0][0] = 1.0f;
|
||||
mesh.pivot_xform[0][1] = 0.0f;
|
||||
mesh.pivot_xform[0][2] = 0.0f;
|
||||
mesh.pivot_xform[0][3] = 0.0f;
|
||||
|
||||
mesh.pivot_xform[1][0] = 0.0f;
|
||||
mesh.pivot_xform[1][1] = 1.0f;
|
||||
mesh.pivot_xform[1][2] = 0.0f;
|
||||
mesh.pivot_xform[1][3] = 0.0f;
|
||||
|
||||
mesh.pivot_xform[2][0] = 0.0f;
|
||||
mesh.pivot_xform[2][1] = 0.0f;
|
||||
mesh.pivot_xform[2][2] = 1.0f;
|
||||
mesh.pivot_xform[2][3] = 0.0f;
|
||||
|
||||
mesh.pivot_xform[3][0] = bcenter[0];
|
||||
mesh.pivot_xform[3][1] = bcenter[1];
|
||||
mesh.pivot_xform[3][2] = bcenter[2];
|
||||
mesh.pivot_xform[3][3] = 1.0f;
|
||||
|
||||
meshes->push_back(mesh);
|
||||
}
|
||||
|
||||
// material_t -> Material and Texture
|
||||
out_materials->resize(materials.size());
|
||||
out_textures->resize(0);
|
||||
for (size_t i = 0; i < materials.size(); i++) {
|
||||
(*out_materials)[i].diffuse[0] = materials[i].diffuse[0];
|
||||
(*out_materials)[i].diffuse[1] = materials[i].diffuse[1];
|
||||
(*out_materials)[i].diffuse[2] = materials[i].diffuse[2];
|
||||
(*out_materials)[i].specular[0] = materials[i].specular[0];
|
||||
(*out_materials)[i].specular[1] = materials[i].specular[1];
|
||||
(*out_materials)[i].specular[2] = materials[i].specular[2];
|
||||
|
||||
(*out_materials)[i].id = int(i);
|
||||
|
||||
// map_Kd
|
||||
(*out_materials)[i].diffuse_texid =
|
||||
LoadTexture(materials[i].diffuse_texname, out_textures);
|
||||
// map_Ks
|
||||
(*out_materials)[i].specular_texid =
|
||||
LoadTexture(materials[i].specular_texname, out_textures);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace example
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef EXAMPLE_OBJ_LOADER_H_
|
||||
#define EXAMPLE_OBJ_LOADER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "mesh.h"
|
||||
#include "material.h"
|
||||
|
||||
namespace example {
|
||||
|
||||
///
|
||||
/// Loads wavefront .obj mesh
|
||||
///
|
||||
bool LoadObj(const std::string &filename, float scale, std::vector<Mesh<float> > *meshes, std::vector<Material> *materials, std::vector<Texture> *textures);
|
||||
|
||||
}
|
||||
|
||||
#endif // EXAMPLE_OBJ_LOADER_H_
|
|
@ -0,0 +1,115 @@
|
|||
newoption {
|
||||
trigger = "with-gtk3nfd",
|
||||
description = "Build with native file dialog support(GTK3 required. Linux only)"
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "asan",
|
||||
description = "Enable Address Sanitizer(gcc5+ ang clang only)"
|
||||
}
|
||||
|
||||
sources = {
|
||||
"stbi-impl.cc",
|
||||
"main.cc",
|
||||
"render.cc",
|
||||
"render-config.cc",
|
||||
"obj-loader.cc",
|
||||
"gltf-loader.cc",
|
||||
"matrix.cc",
|
||||
"../common/trackball.cc",
|
||||
"../common/imgui/imgui.cpp",
|
||||
"../common/imgui/imgui_draw.cpp",
|
||||
"../common/imgui/imgui_impl_btgui.cpp",
|
||||
"../common/imgui/ImGuizmo.cpp",
|
||||
}
|
||||
|
||||
solution "NanoSGSolution"
|
||||
configurations { "Release", "Debug" }
|
||||
|
||||
if os.is("Windows") then
|
||||
platforms { "x64", "x32" }
|
||||
else
|
||||
platforms { "native", "x64", "x32" }
|
||||
end
|
||||
|
||||
|
||||
-- RootDir for OpenGLWindow
|
||||
projectRootDir = os.getcwd() .. "/../common/"
|
||||
dofile ("../common/findOpenGLGlewGlut.lua")
|
||||
initOpenGL()
|
||||
initGlew()
|
||||
|
||||
-- Use c++11
|
||||
flags { "c++11" }
|
||||
|
||||
-- A project defines one build target
|
||||
project "viwewer"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
files { sources }
|
||||
|
||||
includedirs { "./", "../../" }
|
||||
includedirs { "../common" }
|
||||
includedirs { "../common/imgui" }
|
||||
includedirs { "../common/glm" }
|
||||
--includedirs { "../common/nativefiledialog/src/include" }
|
||||
|
||||
if _OPTIONS['asan'] then
|
||||
buildoptions { "-fsanitize=address" }
|
||||
linkoptions { "-fsanitize=address" }
|
||||
end
|
||||
|
||||
if os.is("Windows") then
|
||||
warnings "Extra" -- /W4
|
||||
|
||||
defines { "NOMINMAX" }
|
||||
defines { "USE_NATIVEFILEDIALOG" }
|
||||
buildoptions { "/W4" } -- raise compile error level.
|
||||
files{
|
||||
"../common/OpenGLWindow/Win32OpenGLWindow.cpp",
|
||||
"../common/OpenGLWindow/Win32OpenGLWindow.h",
|
||||
"../common/OpenGLWindow/Win32Window.cpp",
|
||||
"../common/OpenGLWindow/Win32Window.h",
|
||||
}
|
||||
includedirs { "./../common/nativefiledialog/src/include" }
|
||||
files { "../common/nativefiledialog/src/nfd_common.c",
|
||||
"../common/nativefiledialog/src/nfd_win.cpp" }
|
||||
end
|
||||
if os.is("Linux") then
|
||||
files {
|
||||
"../common/OpenGLWindow/X11OpenGLWindow.cpp",
|
||||
"../common/OpenGLWindow/X11OpenGLWindows.h"
|
||||
}
|
||||
links {"X11", "pthread", "dl"}
|
||||
if _OPTIONS["with-gtk3nfd"] then
|
||||
defines { "USE_NATIVEFILEDIALOG" }
|
||||
includedirs { "./../common/nativefiledialog/src/include" }
|
||||
files { "../common/nativefiledialog/src/nfd_gtk.c",
|
||||
"../common/nativefiledialog/src/nfd_common.c"
|
||||
}
|
||||
buildoptions { "`pkg-config --cflags gtk+-3.0`" }
|
||||
linkoptions { "`pkg-config --libs gtk+-3.0`" }
|
||||
end
|
||||
end
|
||||
if os.is("MacOSX") then
|
||||
defines { "USE_NATIVEFILEDIALOG" }
|
||||
links {"Cocoa.framework"}
|
||||
files {
|
||||
"../common/OpenGLWindow/MacOpenGLWindow.h",
|
||||
"../common/OpenGLWindow/MacOpenGLWindow.mm",
|
||||
}
|
||||
includedirs { "./../common/nativefiledialog/src/include" }
|
||||
files { "../common/nativefiledialog/src/nfd_cocoa.m",
|
||||
"../common/nativefiledialog/src/nfd_common.c" }
|
||||
end
|
||||
|
||||
configuration "Debug"
|
||||
defines { "DEBUG" } -- -DDEBUG
|
||||
symbols "On"
|
||||
targetname "view_debug"
|
||||
|
||||
configuration "Release"
|
||||
-- defines { "NDEBUG" } -- -NDEBUG
|
||||
symbols "On"
|
||||
optimize "On"
|
||||
targetname "view"
|
|
@ -0,0 +1,122 @@
|
|||
#include "render-config.h"
|
||||
|
||||
#include "picojson.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
|
||||
namespace example {
|
||||
|
||||
bool LoadRenderConfig(example::RenderConfig* config, const char* filename) {
|
||||
std::ifstream is(filename);
|
||||
if (is.fail()) {
|
||||
std::cerr << "Cannot open " << filename << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::istream_iterator<char> input(is);
|
||||
std::string err;
|
||||
picojson::value v;
|
||||
input = picojson::parse(v, input, std::istream_iterator<char>(), &err);
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
|
||||
if (!v.is<picojson::object>()) {
|
||||
std::cerr << "Not a JSON object" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
picojson::object o = v.get<picojson::object>();
|
||||
|
||||
if (o.find("obj_filename") != o.end()) {
|
||||
if (o["obj_filename"].is<std::string>()) {
|
||||
config->obj_filename = o["obj_filename"].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
if (o.find("gltf_filename") != o.end()) {
|
||||
if (o["gltf_filename"].is<std::string>()) {
|
||||
config->gltf_filename = o["gltf_filename"].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
if (o.find("eson_filename") != o.end()) {
|
||||
if (o["eson_filename"].is<std::string>()) {
|
||||
config->eson_filename = o["eson_filename"].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
config->scene_scale = 1.0f;
|
||||
if (o.find("scene_scale") != o.end()) {
|
||||
if (o["scene_scale"].is<double>()) {
|
||||
config->scene_scale = static_cast<float>(o["scene_scale"].get<double>());
|
||||
}
|
||||
}
|
||||
|
||||
config->eye[0] = 0.0f;
|
||||
config->eye[1] = 0.0f;
|
||||
config->eye[2] = 5.0f;
|
||||
if (o.find("eye") != o.end()) {
|
||||
if (o["eye"].is<picojson::array>()) {
|
||||
picojson::array arr = o["eye"].get<picojson::array>();
|
||||
if (arr.size() == 3) {
|
||||
config->eye[0] = static_cast<float>(arr[0].get<double>());
|
||||
config->eye[1] = static_cast<float>(arr[1].get<double>());
|
||||
config->eye[2] = static_cast<float>(arr[2].get<double>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config->up[0] = 0.0f;
|
||||
config->up[1] = 1.0f;
|
||||
config->up[2] = 0.0f;
|
||||
if (o.find("up") != o.end()) {
|
||||
if (o["up"].is<picojson::array>()) {
|
||||
picojson::array arr = o["up"].get<picojson::array>();
|
||||
if (arr.size() == 3) {
|
||||
config->up[0] = static_cast<float>(arr[0].get<double>());
|
||||
config->up[1] = static_cast<float>(arr[1].get<double>());
|
||||
config->up[2] = static_cast<float>(arr[2].get<double>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config->look_at[0] = 0.0f;
|
||||
config->look_at[1] = 0.0f;
|
||||
config->look_at[2] = 0.0f;
|
||||
if (o.find("look_at") != o.end()) {
|
||||
if (o["look_at"].is<picojson::array>()) {
|
||||
picojson::array arr = o["look_at"].get<picojson::array>();
|
||||
if (arr.size() == 3) {
|
||||
config->look_at[0] = static_cast<float>(arr[0].get<double>());
|
||||
config->look_at[1] = static_cast<float>(arr[1].get<double>());
|
||||
config->look_at[2] = static_cast<float>(arr[2].get<double>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config->fov = 45.0f;
|
||||
if (o.find("fov") != o.end()) {
|
||||
if (o["fov"].is<double>()) {
|
||||
config->fov = static_cast<float>(o["fov"].get<double>());
|
||||
}
|
||||
}
|
||||
|
||||
config->width = 512;
|
||||
if (o.find("width") != o.end()) {
|
||||
if (o["width"].is<double>()) {
|
||||
config->width = static_cast<int>(o["width"].get<double>());
|
||||
}
|
||||
}
|
||||
|
||||
config->height = 512;
|
||||
if (o.find("height") != o.end()) {
|
||||
if (o["height"].is<double>()) {
|
||||
config->height = static_cast<int>(o["height"].get<double>());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace example
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef RENDER_CONFIG_H
|
||||
#define RENDER_CONFIG_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace example {
|
||||
|
||||
typedef struct {
|
||||
// framebuffer
|
||||
int width;
|
||||
int height;
|
||||
|
||||
// camera
|
||||
float eye[3];
|
||||
float up[3];
|
||||
float look_at[3];
|
||||
float fov; // vertical fov in degree.
|
||||
|
||||
// render pass
|
||||
int pass;
|
||||
int max_passes;
|
||||
|
||||
// For debugging. Array size = width * height * 4.
|
||||
float *normalImage;
|
||||
float *positionImage;
|
||||
float *depthImage;
|
||||
float *texcoordImage;
|
||||
float *varycoordImage;
|
||||
|
||||
// Scene input info
|
||||
std::string obj_filename;
|
||||
std::string gltf_filename;
|
||||
std::string eson_filename;
|
||||
float scene_scale;
|
||||
|
||||
} RenderConfig;
|
||||
|
||||
/// Loads config from JSON file.
|
||||
bool LoadRenderConfig(example::RenderConfig *config, const char *filename);
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // RENDER_CONFIG_H
|
|
@ -0,0 +1,560 @@
|
|||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 - 2016 Light Transport Entertainment, Inc.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4018)
|
||||
#pragma warning(disable : 4244)
|
||||
#pragma warning(disable : 4189)
|
||||
#pragma warning(disable : 4996)
|
||||
#pragma warning(disable : 4267)
|
||||
#pragma warning(disable : 4477)
|
||||
#endif
|
||||
|
||||
#include "render.h"
|
||||
|
||||
#include <chrono> // C++11
|
||||
#include <sstream>
|
||||
#include <thread> // C++11
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "nanort.h"
|
||||
#include "matrix.h"
|
||||
#include "material.h"
|
||||
#include "mesh.h"
|
||||
|
||||
|
||||
#include "trackball.h"
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace example {
|
||||
|
||||
// PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
|
||||
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
|
||||
// http://www.pcg-random.org/
|
||||
typedef struct {
|
||||
unsigned long long state;
|
||||
unsigned long long inc; // not used?
|
||||
} pcg32_state_t;
|
||||
|
||||
#define PCG32_INITIALIZER \
|
||||
{ 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }
|
||||
|
||||
float pcg32_random(pcg32_state_t* rng) {
|
||||
unsigned long long oldstate = rng->state;
|
||||
rng->state = oldstate * 6364136223846793005ULL + rng->inc;
|
||||
unsigned int xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
|
||||
unsigned int rot = oldstate >> 59u;
|
||||
unsigned int ret = (xorshifted >> rot) | (xorshifted << ((-static_cast<int>(rot)) & 31));
|
||||
|
||||
return (float)((double)ret / (double)4294967296.0);
|
||||
}
|
||||
|
||||
void pcg32_srandom(pcg32_state_t* rng, uint64_t initstate, uint64_t initseq) {
|
||||
rng->state = 0U;
|
||||
rng->inc = (initseq << 1U) | 1U;
|
||||
pcg32_random(rng);
|
||||
rng->state += initstate;
|
||||
pcg32_random(rng);
|
||||
}
|
||||
|
||||
const float kPI = 3.141592f;
|
||||
|
||||
typedef nanort::real3<float> float3;
|
||||
|
||||
inline float3 Lerp3(float3 v0, float3 v1, float3 v2, float u, float v) {
|
||||
return (1.0f - u - v) * v0 + u * v1 + v * v2;
|
||||
}
|
||||
|
||||
inline void CalcNormal(float3& N, float3 v0, float3 v1, float3 v2) {
|
||||
float3 v10 = v1 - v0;
|
||||
float3 v20 = v2 - v0;
|
||||
|
||||
N = vcross(v20, v10);
|
||||
N = vnormalize(N);
|
||||
}
|
||||
|
||||
void BuildCameraFrame(float3* origin, float3* corner, float3* u, float3* v,
|
||||
float quat[4], float eye[3], float lookat[3], float up[3],
|
||||
float fov, int width, int height) {
|
||||
float e[4][4];
|
||||
|
||||
Matrix::LookAt(e, eye, lookat, up);
|
||||
|
||||
float r[4][4];
|
||||
build_rotmatrix(r, quat);
|
||||
|
||||
float3 lo;
|
||||
lo[0] = lookat[0] - eye[0];
|
||||
lo[1] = lookat[1] - eye[1];
|
||||
lo[2] = lookat[2] - eye[2];
|
||||
float dist = vlength(lo);
|
||||
|
||||
float dir[3];
|
||||
dir[0] = 0.0;
|
||||
dir[1] = 0.0;
|
||||
dir[2] = dist;
|
||||
|
||||
Matrix::Inverse(r);
|
||||
|
||||
float rr[4][4];
|
||||
float re[4][4];
|
||||
float zero[3] = {0.0f, 0.0f, 0.0f};
|
||||
float localUp[3] = {0.0f, 1.0f, 0.0f};
|
||||
Matrix::LookAt(re, dir, zero, localUp);
|
||||
|
||||
// translate
|
||||
re[3][0] += eye[0]; // 0.0; //lo[0];
|
||||
re[3][1] += eye[1]; // 0.0; //lo[1];
|
||||
re[3][2] += (eye[2] - dist);
|
||||
|
||||
// rot -> trans
|
||||
Matrix::Mult(rr, r, re);
|
||||
|
||||
float m[4][4];
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
m[j][i] = rr[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
float vzero[3] = {0.0f, 0.0f, 0.0f};
|
||||
float eye1[3];
|
||||
Matrix::MultV(eye1, m, vzero);
|
||||
|
||||
float lookat1d[3];
|
||||
dir[2] = -dir[2];
|
||||
Matrix::MultV(lookat1d, m, dir);
|
||||
float3 lookat1(lookat1d[0], lookat1d[1], lookat1d[2]);
|
||||
|
||||
float up1d[3];
|
||||
Matrix::MultV(up1d, m, up);
|
||||
|
||||
float3 up1(up1d[0], up1d[1], up1d[2]);
|
||||
|
||||
// absolute -> relative
|
||||
up1[0] -= eye1[0];
|
||||
up1[1] -= eye1[1];
|
||||
up1[2] -= eye1[2];
|
||||
// printf("up1(after) = %f, %f, %f\n", up1[0], up1[1], up1[2]);
|
||||
|
||||
// Use original up vector
|
||||
// up1[0] = up[0];
|
||||
// up1[1] = up[1];
|
||||
// up1[2] = up[2];
|
||||
|
||||
{
|
||||
float flen =
|
||||
(0.5f * (float)height / tanf(0.5f * (float)(fov * kPI / 180.0f)));
|
||||
float3 look1;
|
||||
look1[0] = lookat1[0] - eye1[0];
|
||||
look1[1] = lookat1[1] - eye1[1];
|
||||
look1[2] = lookat1[2] - eye1[2];
|
||||
// vcross(u, up1, look1);
|
||||
// flip
|
||||
(*u) = nanort::vcross(look1, up1);
|
||||
(*u) = vnormalize((*u));
|
||||
|
||||
(*v) = vcross(look1, (*u));
|
||||
(*v) = vnormalize((*v));
|
||||
|
||||
look1 = vnormalize(look1);
|
||||
look1[0] = flen * look1[0] + eye1[0];
|
||||
look1[1] = flen * look1[1] + eye1[1];
|
||||
look1[2] = flen * look1[2] + eye1[2];
|
||||
(*corner)[0] = look1[0] - 0.5f * (width * (*u)[0] + height * (*v)[0]);
|
||||
(*corner)[1] = look1[1] - 0.5f * (width * (*u)[1] + height * (*v)[1]);
|
||||
(*corner)[2] = look1[2] - 0.5f * (width * (*u)[2] + height * (*v)[2]);
|
||||
|
||||
(*origin)[0] = eye1[0];
|
||||
(*origin)[1] = eye1[1];
|
||||
(*origin)[2] = eye1[2];
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // TODO(LTE): Not used method. Delete.
|
||||
nanort::Ray<float> GenerateRay(const float3& origin, const float3& corner,
|
||||
const float3& du, const float3& dv, float u,
|
||||
float v) {
|
||||
float3 dir;
|
||||
|
||||
dir[0] = (corner[0] + u * du[0] + v * dv[0]) - origin[0];
|
||||
dir[1] = (corner[1] + u * du[1] + v * dv[1]) - origin[1];
|
||||
dir[2] = (corner[2] + u * du[2] + v * dv[2]) - origin[2];
|
||||
dir = vnormalize(dir);
|
||||
|
||||
float3 org;
|
||||
|
||||
nanort::Ray<float> ray;
|
||||
ray.org[0] = origin[0];
|
||||
ray.org[1] = origin[1];
|
||||
ray.org[2] = origin[2];
|
||||
ray.dir[0] = dir[0];
|
||||
ray.dir[1] = dir[1];
|
||||
ray.dir[2] = dir[2];
|
||||
|
||||
return ray;
|
||||
}
|
||||
#endif
|
||||
|
||||
void FetchTexture(const Texture &texture, float u, float v, float* col) {
|
||||
int tx = u * texture.width;
|
||||
int ty = (1.0f - v) * texture.height;
|
||||
int idx_offset = (ty * texture.width + tx) * texture.components;
|
||||
col[0] = texture.image[idx_offset + 0] / 255.f;
|
||||
col[1] = texture.image[idx_offset + 1] / 255.f;
|
||||
col[2] = texture.image[idx_offset + 2] / 255.f;
|
||||
}
|
||||
|
||||
bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
|
||||
float quat[4],
|
||||
const nanosg::Scene<float, example::Mesh<float>> &scene,
|
||||
const example::Asset &asset,
|
||||
const RenderConfig& config,
|
||||
std::atomic<bool>& cancelFlag,
|
||||
int &_showBufferMode
|
||||
) {
|
||||
//if (!gAccel.IsValid()) {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
int width = config.width;
|
||||
int height = config.height;
|
||||
|
||||
// camera
|
||||
float eye[3] = {config.eye[0], config.eye[1], config.eye[2]};
|
||||
float look_at[3] = {config.look_at[0], config.look_at[1], config.look_at[2]};
|
||||
float up[3] = {config.up[0], config.up[1], config.up[2]};
|
||||
float fov = config.fov;
|
||||
float3 origin, corner, u, v;
|
||||
BuildCameraFrame(&origin, &corner, &u, &v, quat, eye, look_at, up, fov, width,
|
||||
height);
|
||||
|
||||
auto kCancelFlagCheckMilliSeconds = 300;
|
||||
|
||||
std::vector<std::thread> workers;
|
||||
std::atomic<int> i(0);
|
||||
|
||||
uint32_t num_threads = std::max(1U, std::thread::hardware_concurrency());
|
||||
|
||||
auto startT = std::chrono::system_clock::now();
|
||||
|
||||
// Initialize RNG.
|
||||
|
||||
for (auto t = 0; t < num_threads; t++) {
|
||||
workers.emplace_back(std::thread([&, t]() {
|
||||
pcg32_state_t rng;
|
||||
pcg32_srandom(&rng, config.pass,
|
||||
t); // seed = combination of render pass + thread no.
|
||||
|
||||
int y = 0;
|
||||
while ((y = i++) < config.height) {
|
||||
auto currT = std::chrono::system_clock::now();
|
||||
|
||||
std::chrono::duration<double, std::milli> ms = currT - startT;
|
||||
// Check cancel flag
|
||||
if (ms.count() > kCancelFlagCheckMilliSeconds) {
|
||||
if (cancelFlag) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// draw dash line to aux buffer for progress.
|
||||
// for (int x = 0; x < config.width; x++) {
|
||||
// float c = (x / 8) % 2;
|
||||
// aux_rgba[4*(y*config.width+x)+0] = c;
|
||||
// aux_rgba[4*(y*config.width+x)+1] = c;
|
||||
// aux_rgba[4*(y*config.width+x)+2] = c;
|
||||
// aux_rgba[4*(y*config.width+x)+3] = 0.0f;
|
||||
//}
|
||||
|
||||
for (int x = 0; x < config.width; x++) {
|
||||
nanort::Ray<float> ray;
|
||||
ray.org[0] = origin[0];
|
||||
ray.org[1] = origin[1];
|
||||
ray.org[2] = origin[2];
|
||||
|
||||
float u0 = pcg32_random(&rng);
|
||||
float u1 = pcg32_random(&rng);
|
||||
|
||||
float3 dir;
|
||||
|
||||
//for modes not a "color"
|
||||
if(_showBufferMode != SHOW_BUFFER_COLOR)
|
||||
{
|
||||
//only one pass
|
||||
if(config.pass > 0)
|
||||
continue;
|
||||
|
||||
//to the center of pixel
|
||||
u0 = 0.5f;
|
||||
u1 = 0.5f;
|
||||
}
|
||||
|
||||
dir = corner + (float(x) + u0) * u +
|
||||
(float(config.height - y - 1) + u1) * v;
|
||||
dir = vnormalize(dir);
|
||||
ray.dir[0] = dir[0];
|
||||
ray.dir[1] = dir[1];
|
||||
ray.dir[2] = dir[2];
|
||||
|
||||
float kFar = 1.0e+30f;
|
||||
ray.min_t = 0.0f;
|
||||
ray.max_t = kFar;
|
||||
|
||||
|
||||
nanosg::Intersection<float> isect;
|
||||
bool hit = scene.Traverse(ray, &isect, /* cull_back_face */false);
|
||||
|
||||
if (hit) {
|
||||
|
||||
const std::vector<Material> &materials = asset.materials;
|
||||
const std::vector<Texture> &textures = asset.textures;
|
||||
const Mesh<float> &mesh = asset.meshes[isect.node_id];
|
||||
|
||||
//tigra: add default material
|
||||
const Material &default_material = asset.default_material;
|
||||
|
||||
float3 p;
|
||||
p[0] =
|
||||
ray.org[0] + isect.t * ray.dir[0];
|
||||
p[1] =
|
||||
ray.org[1] + isect.t * ray.dir[1];
|
||||
p[2] =
|
||||
ray.org[2] + isect.t * ray.dir[2];
|
||||
|
||||
config.positionImage[4 * (y * config.width + x) + 0] = p.x();
|
||||
config.positionImage[4 * (y * config.width + x) + 1] = p.y();
|
||||
config.positionImage[4 * (y * config.width + x) + 2] = p.z();
|
||||
config.positionImage[4 * (y * config.width + x) + 3] = 1.0f;
|
||||
|
||||
config.varycoordImage[4 * (y * config.width + x) + 0] =
|
||||
isect.u;
|
||||
config.varycoordImage[4 * (y * config.width + x) + 1] =
|
||||
isect.v;
|
||||
config.varycoordImage[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
config.varycoordImage[4 * (y * config.width + x) + 3] = 1.0f;
|
||||
|
||||
unsigned int prim_id = isect.prim_id;
|
||||
|
||||
float3 N;
|
||||
if (mesh.facevarying_normals.size() > 0) {
|
||||
float3 n0, n1, n2;
|
||||
n0[0] = mesh.facevarying_normals[9 * prim_id + 0];
|
||||
n0[1] = mesh.facevarying_normals[9 * prim_id + 1];
|
||||
n0[2] = mesh.facevarying_normals[9 * prim_id + 2];
|
||||
n1[0] = mesh.facevarying_normals[9 * prim_id + 3];
|
||||
n1[1] = mesh.facevarying_normals[9 * prim_id + 4];
|
||||
n1[2] = mesh.facevarying_normals[9 * prim_id + 5];
|
||||
n2[0] = mesh.facevarying_normals[9 * prim_id + 6];
|
||||
n2[1] = mesh.facevarying_normals[9 * prim_id + 7];
|
||||
n2[2] = mesh.facevarying_normals[9 * prim_id + 8];
|
||||
N = Lerp3(n0, n1, n2, isect.u, isect.v);
|
||||
} else {
|
||||
unsigned int f0, f1, f2;
|
||||
f0 = mesh.faces[3 * prim_id + 0];
|
||||
f1 = mesh.faces[3 * prim_id + 1];
|
||||
f2 = mesh.faces[3 * prim_id + 2];
|
||||
|
||||
float3 v0, v1, v2;
|
||||
v0[0] = mesh.vertices[3 * f0 + 0];
|
||||
v0[1] = mesh.vertices[3 * f0 + 1];
|
||||
v0[2] = mesh.vertices[3 * f0 + 2];
|
||||
v1[0] = mesh.vertices[3 * f1 + 0];
|
||||
v1[1] = mesh.vertices[3 * f1 + 1];
|
||||
v1[2] = mesh.vertices[3 * f1 + 2];
|
||||
v2[0] = mesh.vertices[3 * f2 + 0];
|
||||
v2[1] = mesh.vertices[3 * f2 + 1];
|
||||
v2[2] = mesh.vertices[3 * f2 + 2];
|
||||
CalcNormal(N, v0, v1, v2);
|
||||
}
|
||||
|
||||
config.normalImage[4 * (y * config.width + x) + 0] =
|
||||
0.5f * N[0] + 0.5f;
|
||||
config.normalImage[4 * (y * config.width + x) + 1] =
|
||||
0.5f * N[1] + 0.5f;
|
||||
config.normalImage[4 * (y * config.width + x) + 2] =
|
||||
0.5f * N[2] + 0.5f;
|
||||
config.normalImage[4 * (y * config.width + x) + 3] = 1.0f;
|
||||
|
||||
config.depthImage[4 * (y * config.width + x) + 0] =
|
||||
isect.t;
|
||||
config.depthImage[4 * (y * config.width + x) + 1] =
|
||||
isect.t;
|
||||
config.depthImage[4 * (y * config.width + x) + 2] =
|
||||
isect.t;
|
||||
config.depthImage[4 * (y * config.width + x) + 3] = 1.0f;
|
||||
|
||||
float3 UV;
|
||||
if (mesh.facevarying_uvs.size() > 0) {
|
||||
float3 uv0, uv1, uv2;
|
||||
uv0[0] = mesh.facevarying_uvs[6 * prim_id + 0];
|
||||
uv0[1] = mesh.facevarying_uvs[6 * prim_id + 1];
|
||||
uv1[0] = mesh.facevarying_uvs[6 * prim_id + 2];
|
||||
uv1[1] = mesh.facevarying_uvs[6 * prim_id + 3];
|
||||
uv2[0] = mesh.facevarying_uvs[6 * prim_id + 4];
|
||||
uv2[1] = mesh.facevarying_uvs[6 * prim_id + 5];
|
||||
|
||||
UV = Lerp3(uv0, uv1, uv2, isect.u, isect.v);
|
||||
|
||||
config.texcoordImage[4 * (y * config.width + x) + 0] = UV[0];
|
||||
config.texcoordImage[4 * (y * config.width + x) + 1] = UV[1];
|
||||
}
|
||||
|
||||
// Fetch texture
|
||||
unsigned int material_id =
|
||||
mesh.material_ids[isect.prim_id];
|
||||
|
||||
//printf("material_id=%d materials=%lld\n", material_id, materials.size());
|
||||
|
||||
float diffuse_col[3];
|
||||
|
||||
float specular_col[3];
|
||||
|
||||
//tigra: material_id is ok
|
||||
if(material_id<materials.size())
|
||||
{
|
||||
//printf("ok mat\n");
|
||||
|
||||
int diffuse_texid = materials[material_id].diffuse_texid;
|
||||
if (diffuse_texid >= 0) {
|
||||
FetchTexture(textures[diffuse_texid], UV[0], UV[1], diffuse_col);
|
||||
} else {
|
||||
diffuse_col[0] = materials[material_id].diffuse[0];
|
||||
diffuse_col[1] = materials[material_id].diffuse[1];
|
||||
diffuse_col[2] = materials[material_id].diffuse[2];
|
||||
}
|
||||
|
||||
int specular_texid = materials[material_id].specular_texid;
|
||||
if (specular_texid >= 0) {
|
||||
FetchTexture(textures[specular_texid], UV[0], UV[1], specular_col);
|
||||
} else {
|
||||
specular_col[0] = materials[material_id].specular[0];
|
||||
specular_col[1] = materials[material_id].specular[1];
|
||||
specular_col[2] = materials[material_id].specular[2];
|
||||
}
|
||||
}
|
||||
else
|
||||
//tigra: wrong material_id, use default_material
|
||||
{
|
||||
|
||||
//printf("default_material\n");
|
||||
|
||||
diffuse_col[0] = default_material.diffuse[0];
|
||||
diffuse_col[1] = default_material.diffuse[1];
|
||||
diffuse_col[2] = default_material.diffuse[2];
|
||||
specular_col[0] = default_material.specular[0];
|
||||
specular_col[1] = default_material.specular[1];
|
||||
specular_col[2] = default_material.specular[2];
|
||||
}
|
||||
|
||||
// Simple shading
|
||||
float NdotV = fabsf(vdot(N, dir));
|
||||
|
||||
if (config.pass == 0) {
|
||||
rgba[4 * (y * config.width + x) + 0] = NdotV * diffuse_col[0];
|
||||
rgba[4 * (y * config.width + x) + 1] = NdotV * diffuse_col[1];
|
||||
rgba[4 * (y * config.width + x) + 2] = NdotV * diffuse_col[2];
|
||||
rgba[4 * (y * config.width + x) + 3] = 1.0f;
|
||||
sample_counts[y * config.width + x] =
|
||||
1; // Set 1 for the first pass
|
||||
} else { // additive.
|
||||
rgba[4 * (y * config.width + x) + 0] += NdotV * diffuse_col[0];
|
||||
rgba[4 * (y * config.width + x) + 1] += NdotV * diffuse_col[1];
|
||||
rgba[4 * (y * config.width + x) + 2] += NdotV * diffuse_col[2];
|
||||
rgba[4 * (y * config.width + x) + 3] += 1.0f;
|
||||
sample_counts[y * config.width + x]++;
|
||||
}
|
||||
|
||||
} else {
|
||||
{
|
||||
if (config.pass == 0) {
|
||||
// clear pixel
|
||||
rgba[4 * (y * config.width + x) + 0] = 0.0f;
|
||||
rgba[4 * (y * config.width + x) + 1] = 0.0f;
|
||||
rgba[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
rgba[4 * (y * config.width + x) + 3] = 0.0f;
|
||||
aux_rgba[4 * (y * config.width + x) + 0] = 0.0f;
|
||||
aux_rgba[4 * (y * config.width + x) + 1] = 0.0f;
|
||||
aux_rgba[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
aux_rgba[4 * (y * config.width + x) + 3] = 0.0f;
|
||||
sample_counts[y * config.width + x] =
|
||||
1; // Set 1 for the first pass
|
||||
} else {
|
||||
sample_counts[y * config.width + x]++;
|
||||
}
|
||||
|
||||
// No super sampling
|
||||
config.normalImage[4 * (y * config.width + x) + 0] = 0.0f;
|
||||
config.normalImage[4 * (y * config.width + x) + 1] = 0.0f;
|
||||
config.normalImage[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
config.normalImage[4 * (y * config.width + x) + 3] = 0.0f;
|
||||
config.positionImage[4 * (y * config.width + x) + 0] = 0.0f;
|
||||
config.positionImage[4 * (y * config.width + x) + 1] = 0.0f;
|
||||
config.positionImage[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
config.positionImage[4 * (y * config.width + x) + 3] = 0.0f;
|
||||
config.depthImage[4 * (y * config.width + x) + 0] = 0.0f;
|
||||
config.depthImage[4 * (y * config.width + x) + 1] = 0.0f;
|
||||
config.depthImage[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
config.depthImage[4 * (y * config.width + x) + 3] = 0.0f;
|
||||
config.texcoordImage[4 * (y * config.width + x) + 0] = 0.0f;
|
||||
config.texcoordImage[4 * (y * config.width + x) + 1] = 0.0f;
|
||||
config.texcoordImage[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
config.texcoordImage[4 * (y * config.width + x) + 3] = 0.0f;
|
||||
config.varycoordImage[4 * (y * config.width + x) + 0] = 0.0f;
|
||||
config.varycoordImage[4 * (y * config.width + x) + 1] = 0.0f;
|
||||
config.varycoordImage[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
config.varycoordImage[4 * (y * config.width + x) + 3] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < config.width; x++) {
|
||||
aux_rgba[4 * (y * config.width + x) + 0] = 0.0f;
|
||||
aux_rgba[4 * (y * config.width + x) + 1] = 0.0f;
|
||||
aux_rgba[4 * (y * config.width + x) + 2] = 0.0f;
|
||||
aux_rgba[4 * (y * config.width + x) + 3] = 0.0f;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for (auto& t : workers) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
return (!cancelFlag);
|
||||
};
|
||||
|
||||
} // namespace example
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef EXAMPLE_RENDER_H_
|
||||
#define EXAMPLE_RENDER_H_
|
||||
|
||||
#include <atomic> // C++11
|
||||
|
||||
//mode definitions now here
|
||||
|
||||
#define SHOW_BUFFER_COLOR (0)
|
||||
#define SHOW_BUFFER_NORMAL (1)
|
||||
#define SHOW_BUFFER_POSITION (2)
|
||||
#define SHOW_BUFFER_DEPTH (3)
|
||||
#define SHOW_BUFFER_TEXCOORD (4)
|
||||
#define SHOW_BUFFER_VARYCOORD (5)
|
||||
|
||||
#include "render-config.h"
|
||||
#include "nanosg.h"
|
||||
#include "mesh.h"
|
||||
#include "material.h"
|
||||
|
||||
namespace example {
|
||||
|
||||
struct Asset {
|
||||
std::vector<Mesh<float> > meshes;
|
||||
std::vector<Material> materials;
|
||||
|
||||
//tigra: add default material
|
||||
Material default_material;
|
||||
std::vector<Texture> textures;
|
||||
};
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer() {}
|
||||
~Renderer() {}
|
||||
|
||||
/// Returns false when the rendering was canceled.
|
||||
static bool Render(float* rgba, float* aux_rgba, int *sample_counts, float quat[4],
|
||||
const nanosg::Scene<float, Mesh<float>> &scene, const Asset &asset, const RenderConfig& config,
|
||||
std::atomic<bool>& cancel_flag,
|
||||
int& _showBufferMode
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
#endif // EXAMPLE_RENDER_H_
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
|
@ -0,0 +1,357 @@
|
|||
# GNU Make project makefile autogenerated by Premake
|
||||
|
||||
ifndef config
|
||||
config=release_native
|
||||
endif
|
||||
|
||||
ifndef verbose
|
||||
SILENT = @
|
||||
endif
|
||||
|
||||
.PHONY: clean prebuild prelink
|
||||
|
||||
ifeq ($(config),release_native)
|
||||
RESCOMP = windres
|
||||
TARGETDIR = bin/native/Release
|
||||
TARGET = $(TARGETDIR)/view
|
||||
OBJDIR = obj/native/Release
|
||||
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1
|
||||
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O2 -g
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O2 -g -std=c++11
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += -lX11 -lpthread -ldl
|
||||
LDDEPS +=
|
||||
ALL_LDFLAGS += $(LDFLAGS)
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
define PREBUILDCMDS
|
||||
endef
|
||||
define PRELINKCMDS
|
||||
endef
|
||||
define POSTBUILDCMDS
|
||||
endef
|
||||
all: prebuild prelink $(TARGET)
|
||||
@:
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(config),release_x64)
|
||||
RESCOMP = windres
|
||||
TARGETDIR = bin/x64/Release
|
||||
TARGET = $(TARGETDIR)/view
|
||||
OBJDIR = obj/x64/Release
|
||||
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1
|
||||
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -O2 -g
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m64 -O2 -g -std=c++11
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += -lX11 -lpthread -ldl
|
||||
LDDEPS +=
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
define PREBUILDCMDS
|
||||
endef
|
||||
define PRELINKCMDS
|
||||
endef
|
||||
define POSTBUILDCMDS
|
||||
endef
|
||||
all: prebuild prelink $(TARGET)
|
||||
@:
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(config),release_x32)
|
||||
RESCOMP = windres
|
||||
TARGETDIR = bin/x32/Release
|
||||
TARGET = $(TARGETDIR)/view
|
||||
OBJDIR = obj/x32/Release
|
||||
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1
|
||||
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -O2 -g
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m32 -O2 -g -std=c++11
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += -lX11 -lpthread -ldl
|
||||
LDDEPS +=
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
define PREBUILDCMDS
|
||||
endef
|
||||
define PRELINKCMDS
|
||||
endef
|
||||
define POSTBUILDCMDS
|
||||
endef
|
||||
all: prebuild prelink $(TARGET)
|
||||
@:
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(config),debug_native)
|
||||
RESCOMP = windres
|
||||
TARGETDIR = bin/native/Debug
|
||||
TARGET = $(TARGETDIR)/view_debug
|
||||
OBJDIR = obj/native/Debug
|
||||
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG
|
||||
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -g
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -g -std=c++11
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += -lX11 -lpthread -ldl
|
||||
LDDEPS +=
|
||||
ALL_LDFLAGS += $(LDFLAGS)
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
define PREBUILDCMDS
|
||||
endef
|
||||
define PRELINKCMDS
|
||||
endef
|
||||
define POSTBUILDCMDS
|
||||
endef
|
||||
all: prebuild prelink $(TARGET)
|
||||
@:
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(config),debug_x64)
|
||||
RESCOMP = windres
|
||||
TARGETDIR = bin/x64/Debug
|
||||
TARGET = $(TARGETDIR)/view_debug
|
||||
OBJDIR = obj/x64/Debug
|
||||
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG
|
||||
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m64 -g -std=c++11
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += -lX11 -lpthread -ldl
|
||||
LDDEPS +=
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
define PREBUILDCMDS
|
||||
endef
|
||||
define PRELINKCMDS
|
||||
endef
|
||||
define POSTBUILDCMDS
|
||||
endef
|
||||
all: prebuild prelink $(TARGET)
|
||||
@:
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(config),debug_x32)
|
||||
RESCOMP = windres
|
||||
TARGETDIR = bin/x32/Debug
|
||||
TARGET = $(TARGETDIR)/view_debug
|
||||
OBJDIR = obj/x32/Debug
|
||||
DEFINES += -DGLEW_INIT_OPENGL11_FUNCTIONS=1 -DGLEW_STATIC -DGLEW_DYNAMIC_LOAD_ALL_GLX_FUNCTIONS=1 -DDEBUG
|
||||
INCLUDES += -I../common/ThirdPartyLibs/Glew -I. -I../.. -I../common -I../common/imgui -I../common/glm
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -m32 -g -std=c++11
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += -lX11 -lpthread -ldl
|
||||
LDDEPS +=
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
define PREBUILDCMDS
|
||||
endef
|
||||
define PRELINKCMDS
|
||||
endef
|
||||
define POSTBUILDCMDS
|
||||
endef
|
||||
all: prebuild prelink $(TARGET)
|
||||
@:
|
||||
|
||||
endif
|
||||
|
||||
OBJECTS := \
|
||||
$(OBJDIR)/X11OpenGLWindow.o \
|
||||
$(OBJDIR)/glew.o \
|
||||
$(OBJDIR)/ImGuizmo.o \
|
||||
$(OBJDIR)/imgui.o \
|
||||
$(OBJDIR)/imgui_draw.o \
|
||||
$(OBJDIR)/imgui_impl_btgui.o \
|
||||
$(OBJDIR)/trackball.o \
|
||||
$(OBJDIR)/gltf-loader.o \
|
||||
$(OBJDIR)/main.o \
|
||||
$(OBJDIR)/matrix.o \
|
||||
$(OBJDIR)/obj-loader.o \
|
||||
$(OBJDIR)/render-config.o \
|
||||
$(OBJDIR)/render.o \
|
||||
$(OBJDIR)/stbi-impl.o \
|
||||
|
||||
RESOURCES := \
|
||||
|
||||
CUSTOMFILES := \
|
||||
|
||||
SHELLTYPE := msdos
|
||||
ifeq (,$(ComSpec)$(COMSPEC))
|
||||
SHELLTYPE := posix
|
||||
endif
|
||||
ifeq (/bin,$(findstring /bin,$(SHELL)))
|
||||
SHELLTYPE := posix
|
||||
endif
|
||||
|
||||
$(TARGET): $(GCH) ${CUSTOMFILES} $(OBJECTS) $(LDDEPS) $(RESOURCES)
|
||||
@echo Linking viwewer
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(TARGETDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(TARGETDIR))
|
||||
endif
|
||||
$(SILENT) $(LINKCMD)
|
||||
$(POSTBUILDCMDS)
|
||||
|
||||
clean:
|
||||
@echo Cleaning viwewer
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) rm -f $(TARGET)
|
||||
$(SILENT) rm -rf $(OBJDIR)
|
||||
else
|
||||
$(SILENT) if exist $(subst /,\\,$(TARGET)) del $(subst /,\\,$(TARGET))
|
||||
$(SILENT) if exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
|
||||
prebuild:
|
||||
$(PREBUILDCMDS)
|
||||
|
||||
prelink:
|
||||
$(PRELINKCMDS)
|
||||
|
||||
ifneq (,$(PCH))
|
||||
$(OBJECTS): $(GCH) $(PCH)
|
||||
$(GCH): $(PCH)
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o "$@" -MF "$(@:%.gch=%.d)" -c "$<"
|
||||
endif
|
||||
|
||||
$(OBJDIR)/X11OpenGLWindow.o: ../common/OpenGLWindow/X11OpenGLWindow.cpp
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/glew.o: ../common/ThirdPartyLibs/Glew/glew.c
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/ImGuizmo.o: ../common/imgui/ImGuizmo.cpp
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/imgui.o: ../common/imgui/imgui.cpp
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/imgui_draw.o: ../common/imgui/imgui_draw.cpp
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/imgui_impl_btgui.o: ../common/imgui/imgui_impl_btgui.cpp
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/trackball.o: ../common/trackball.cc
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/gltf-loader.o: gltf-loader.cc
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/main.o: main.cc
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/matrix.o: matrix.cc
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/obj-loader.o: obj-loader.cc
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/render-config.o: render-config.cc
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/render.o: render.cc
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/stbi-impl.o: stbi-impl.cc
|
||||
@echo $(notdir $<)
|
||||
ifeq (posix,$(SHELLTYPE))
|
||||
$(SILENT) mkdir -p $(OBJDIR)
|
||||
else
|
||||
$(SILENT) mkdir $(subst /,\\,$(OBJDIR))
|
||||
endif
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
||||
-include $(OBJECTS:%.o=%.d)
|
||||
ifneq (,$(PCH))
|
||||
-include $(OBJDIR)/$(notdir $(PCH)).d
|
||||
endif
|
|
@ -0,0 +1,2 @@
|
|||
all:
|
||||
clang++ -std=c++11 -I../../ -g -O1 -o saver main.cc
|
|
@ -0,0 +1 @@
|
|||
# Simple serialization API sample.
|
|
@ -0,0 +1,33 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 3) {
|
||||
std::cout << "Needs input.gltf output.gltf" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string err;
|
||||
std::string input_filename(argv[1]);
|
||||
std::string output_filename(argv[2]);
|
||||
|
||||
// assume ascii glTF.
|
||||
bool ret = loader.LoadASCIIFromFile(&model, &err, input_filename.c_str());
|
||||
if (!ret) {
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
loader.WriteGltfSceneToFile(&model, output_filename);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
project(tinygltf-validator CXX)
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
# exe
|
||||
add_executable(tinygltf-validator
|
||||
app/tinygltf-validate.cc
|
||||
src/json-schema.hpp
|
||||
src/json-schema-draft4.json.cpp
|
||||
src/json-uri.cpp
|
||||
src/json-validator.cpp)
|
||||
|
||||
target_include_directories(tinygltf-validator
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
|
||||
target_compile_features(tinygltf-validator
|
||||
PUBLIC
|
||||
cxx_range_for) # for C++11 - flags
|
||||
# Enable more compiler warnings, except when using Visual Studio compiler
|
||||
if(NOT MSVC)
|
||||
target_compile_options(tinygltf-validator
|
||||
PUBLIC
|
||||
-Wall -Wextra)
|
||||
endif()
|
||||
target_compile_definitions(tinygltf-validator
|
||||
PRIVATE
|
||||
-DJSON_SCHEMA_VALIDATOR_EXPORTS)
|
||||
|
||||
# regex with boost if gcc < 4.8 - default is std::regex
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9.0")
|
||||
find_package(Boost COMPONENTS regex)
|
||||
if(NOT Boost_FOUND)
|
||||
message(STATUS "GCC less then 4.9 and boost-regex NOT found - no regex used")
|
||||
target_compile_definitions(tinygltf-validator PRIVATE -DJSON_SCHEMA_NO_REGEX)
|
||||
else()
|
||||
message(STATUS "GCC less then 4.9 and boost-regex FOUND - using boost::regex")
|
||||
target_compile_definitions(tinygltf-validator PRIVATE -DJSON_SCHEMA_BOOST_REGEX)
|
||||
target_include_directories(tinygltf-validator PRIVATE ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(tinygltf-validator PRIVATE ${Boost_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# test-zone
|
||||
# enable_testing()
|
|
@ -0,0 +1,22 @@
|
|||
Modern C++ JSON schema validator is licensed under the MIT License
|
||||
<http://opensource.org/licenses/MIT>:
|
||||
|
||||
Copyright (c) 2016 Patrick Boettcher
|
||||
|
||||
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.
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2013-2017 Niels Lohmann
|
||||
|
||||
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.
|
|
@ -0,0 +1,32 @@
|
|||
# tinygltf-validator
|
||||
|
||||
TinyGLTF validator based on Modern C++ JSON schema validator https://github.com/pboettch/json-schema-validator
|
||||
|
||||
## Status
|
||||
|
||||
Experimental. W.I.P.
|
||||
|
||||
## Requirements
|
||||
|
||||
* C++11 compiler
|
||||
* CMake
|
||||
|
||||
## How to build
|
||||
|
||||
```
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
```
|
||||
$ gltf-validator /path/to/file.gltf /path/to/gltf-schema
|
||||
```
|
||||
|
||||
## Third party licenses
|
||||
|
||||
* json.hpp https://github.com/nlohmann/json : MIT
|
||||
* json-schema-validator https://github.com/pboettch/json-schema-validator : MIT
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Modern C++ JSON schema validator
|
||||
*
|
||||
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*
|
||||
* Copyright (c) 2016 Patrick Boettcher <patrick.boettcher@posteo.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.
|
||||
*/
|
||||
#include <json-schema.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_uri;
|
||||
using nlohmann::json_schema_draft4::json_validator;
|
||||
|
||||
static void usage(const char *name)
|
||||
{
|
||||
std::cerr << "Usage: " << name << " <gltf file> <gltf schema dir>\n";
|
||||
std::cerr << " schema dir : $glTF/specification/2.0/schema\n";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
resolver r(nlohmann::json_schema_draft4::root_schema,
|
||||
nlohmann::json_schema_draft4::root_schema["id"]);
|
||||
schema_refs_.insert(r.schema_refs.begin(), r.schema_refs.end());
|
||||
assert(r.undefined_refs.size() == 0);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
static void loader(const json_uri &uri, json &schema)
|
||||
{
|
||||
std::fstream lf("." + uri.path());
|
||||
if (!lf.good())
|
||||
throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path());
|
||||
|
||||
try {
|
||||
lf >> schema;
|
||||
} catch (std::exception &e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool validate(const std::string &schema_dir, const std::string &filename)
|
||||
{
|
||||
std::string gltf_schema = schema_dir + "/glTF.schema.json";
|
||||
|
||||
std::fstream f(gltf_schema);
|
||||
if (!f.good()) {
|
||||
std::cerr << "could not open " << gltf_schema << " for reading\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1) Read the schema for the document you want to validate
|
||||
json schema;
|
||||
try {
|
||||
f >> schema;
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << e.what() << " at " << f.tellp() << " - while parsing the schema\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) create the validator and
|
||||
json_validator validator([&schema_dir](const json_uri &uri, json &schema) {
|
||||
std::cout << "uri.url : " << uri.url() << std::endl;
|
||||
std::cout << "uri.path : " << uri.path() << std::endl;
|
||||
|
||||
std::fstream lf(schema_dir + "/" + uri.path());
|
||||
if (!lf.good())
|
||||
throw std::invalid_argument("could not open " + uri.url() + " tried with " + uri.path());
|
||||
|
||||
try {
|
||||
lf >> schema;
|
||||
} catch (std::exception &e) {
|
||||
throw e;
|
||||
}
|
||||
}, [](const std::string &, const std::string &) {});
|
||||
|
||||
try {
|
||||
// insert this schema as the root to the validator
|
||||
// this resolves remote-schemas, sub-schemas and references via the given loader-function
|
||||
validator.set_root_schema(schema);
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "setting root schema failed\n";
|
||||
std::cerr << e.what() << "\n";
|
||||
}
|
||||
|
||||
// 3) do the actual validation of the document
|
||||
json document;
|
||||
|
||||
std::fstream d(filename);
|
||||
if (!d.good()) {
|
||||
std::cerr << "could not open " << filename << " for reading\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
d >> document;
|
||||
validator.validate(document);
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "schema validation failed\n";
|
||||
std::cerr << e.what() << " at offset: " << d.tellg() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cerr << "document is valid\n";
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 3)
|
||||
usage(argv[0]);
|
||||
|
||||
bool ret = validate(argv[1], argv[2]);
|
||||
|
||||
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
#include <json-schema.hpp>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
namespace json_schema_draft4
|
||||
{
|
||||
|
||||
json draft4_schema_builtin = R"( {
|
||||
"id": "http://json-schema.org/draft-04/schema#",
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"description": "Core schema meta-schema",
|
||||
"definitions": {
|
||||
"schemaArray": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#" }
|
||||
},
|
||||
"positiveInteger": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"positiveIntegerDefault0": {
|
||||
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
|
||||
},
|
||||
"simpleTypes": {
|
||||
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
|
||||
},
|
||||
"stringArray": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": {},
|
||||
"multipleOf": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"exclusiveMinimum": true
|
||||
},
|
||||
"maximum": {
|
||||
"type": "number"
|
||||
},
|
||||
"exclusiveMaximum": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"minimum": {
|
||||
"type": "number"
|
||||
},
|
||||
"exclusiveMinimum": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"maxLength": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"pattern": {
|
||||
"type": "string",
|
||||
"format": "regex"
|
||||
},
|
||||
"additionalItems": {
|
||||
"anyOf": [
|
||||
{ "type": "boolean" },
|
||||
{ "$ref": "#" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#" },
|
||||
{ "$ref": "#/definitions/schemaArray" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"maxItems": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"uniqueItems": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"maxProperties": { "$ref": "#/definitions/positiveInteger" },
|
||||
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
||||
"required": { "$ref": "#/definitions/stringArray" },
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{ "type": "boolean" },
|
||||
{ "$ref": "#" }
|
||||
],
|
||||
"default": {}
|
||||
},
|
||||
"definitions": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"patternProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#" },
|
||||
"default": {}
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#" },
|
||||
{ "$ref": "#/definitions/stringArray" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"enum": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"type": {
|
||||
"anyOf": [
|
||||
{ "$ref": "#/definitions/simpleTypes" },
|
||||
{
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/simpleTypes" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"allOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"anyOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"oneOf": { "$ref": "#/definitions/schemaArray" },
|
||||
"not": { "$ref": "#" }
|
||||
},
|
||||
"dependencies": {
|
||||
"exclusiveMaximum": [ "maximum" ],
|
||||
"exclusiveMinimum": [ "minimum" ]
|
||||
},
|
||||
"default": {}
|
||||
} )"_json;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Modern C++ JSON schema validator
|
||||
*
|
||||
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*
|
||||
* Copyright (c) 2016 Patrick Boettcher <patrick.boettcher@posteo.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.
|
||||
*/
|
||||
#ifndef NLOHMANN_JSON_SCHEMA_HPP__
|
||||
#define NLOHMANN_JSON_SCHEMA_HPP__
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifdef JSON_SCHEMA_VALIDATOR_EXPORTS
|
||||
# define JSON_SCHEMA_VALIDATOR_API __declspec(dllexport)
|
||||
# else
|
||||
# define JSON_SCHEMA_VALIDATOR_API __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define JSON_SCHEMA_VALIDATOR_API
|
||||
#endif
|
||||
|
||||
#include <json.hpp>
|
||||
|
||||
// make yourself a home - welcome to nlohmann's namespace
|
||||
namespace nlohmann
|
||||
{
|
||||
|
||||
// a class representing a JSON-pointer RFC6901
|
||||
//
|
||||
// examples of JSON pointers
|
||||
//
|
||||
// # - root of the current document
|
||||
// #item - refers to the object which is identified ("id") by `item`
|
||||
// in the current document
|
||||
// #/path/to/element
|
||||
// - refers to the element in /path/to from the root-document
|
||||
//
|
||||
//
|
||||
// The json_pointer-class stores everything in a string, which might seem bizarre
|
||||
// as parsing is done from a string to a string, but from_string() is also
|
||||
// doing some formatting.
|
||||
//
|
||||
// TODO
|
||||
// ~ and % - codec
|
||||
// needs testing and clarification regarding the '#' at the beginning
|
||||
|
||||
class json_pointer
|
||||
{
|
||||
std::string str_;
|
||||
|
||||
void from_string(const std::string &r);
|
||||
|
||||
public:
|
||||
json_pointer(const std::string &s = "")
|
||||
{
|
||||
from_string(s);
|
||||
}
|
||||
|
||||
void append(const std::string &elem)
|
||||
{
|
||||
str_.append(elem);
|
||||
}
|
||||
|
||||
const std::string &to_string() const
|
||||
{
|
||||
return str_;
|
||||
}
|
||||
};
|
||||
|
||||
// A class representing a JSON-URI for schemas derived from
|
||||
// section 8 of JSON Schema: A Media Type for Describing JSON Documents
|
||||
// draft-wright-json-schema-00
|
||||
//
|
||||
// New URIs can be derived from it using the derive()-method.
|
||||
// This is useful for resolving refs or subschema-IDs in json-schemas.
|
||||
//
|
||||
// This is done implement the requirements described in section 8.2.
|
||||
//
|
||||
class JSON_SCHEMA_VALIDATOR_API json_uri
|
||||
{
|
||||
std::string urn_;
|
||||
|
||||
std::string proto_;
|
||||
std::string hostname_;
|
||||
std::string path_;
|
||||
json_pointer pointer_;
|
||||
|
||||
protected:
|
||||
// decodes a JSON uri and replaces all or part of the currently stored values
|
||||
void from_string(const std::string &uri);
|
||||
|
||||
std::tuple<std::string, std::string, std::string, std::string, std::string> tie() const
|
||||
{
|
||||
return std::tie(urn_, proto_, hostname_, path_, pointer_.to_string());
|
||||
}
|
||||
|
||||
public:
|
||||
json_uri(const std::string &uri)
|
||||
{
|
||||
from_string(uri);
|
||||
}
|
||||
|
||||
const std::string protocol() const { return proto_; }
|
||||
const std::string hostname() const { return hostname_; }
|
||||
const std::string path() const { return path_; }
|
||||
const json_pointer pointer() const { return pointer_; }
|
||||
|
||||
const std::string url() const;
|
||||
|
||||
// decode and encode strings for ~ and % escape sequences
|
||||
static std::string unescape(const std::string &);
|
||||
static std::string escape(const std::string &);
|
||||
|
||||
// create a new json_uri based in this one and the given uri
|
||||
// resolves relative changes (pathes or pointers) and resets part if proto or hostname changes
|
||||
json_uri derive(const std::string &uri) const
|
||||
{
|
||||
json_uri u = *this;
|
||||
u.from_string(uri);
|
||||
return u;
|
||||
}
|
||||
|
||||
// append a pointer-field to the pointer-part of this uri
|
||||
json_uri append(const std::string &field) const
|
||||
{
|
||||
json_uri u = *this;
|
||||
u.pointer_.append("/" + field);
|
||||
return u;
|
||||
}
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
friend bool operator<(const json_uri &l, const json_uri &r)
|
||||
{
|
||||
return l.tie() < r.tie();
|
||||
}
|
||||
|
||||
friend bool operator==(const json_uri &l, const json_uri &r)
|
||||
{
|
||||
return l.tie() == r.tie();
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const json_uri &u);
|
||||
};
|
||||
|
||||
namespace json_schema_draft4
|
||||
{
|
||||
|
||||
extern json draft4_schema_builtin;
|
||||
|
||||
class JSON_SCHEMA_VALIDATOR_API json_validator
|
||||
{
|
||||
std::vector<std::shared_ptr<json>> schema_store_;
|
||||
std::shared_ptr<json> root_schema_;
|
||||
std::function<void(const json_uri &, json &)> schema_loader_ = nullptr;
|
||||
std::function<void(const std::string &, const std::string &)> format_check_ = nullptr;
|
||||
|
||||
std::map<json_uri, const json *> schema_refs_;
|
||||
|
||||
void validate(const json &instance, const json &schema_, const std::string &name);
|
||||
void validate_array(const json &instance, const json &schema_, const std::string &name);
|
||||
void validate_object(const json &instance, const json &schema_, const std::string &name);
|
||||
void validate_string(const json &instance, const json &schema, const std::string &name);
|
||||
|
||||
void insert_schema(const json &input, const json_uri &id);
|
||||
|
||||
public:
|
||||
json_validator(std::function<void(const json_uri &, json &)> loader = nullptr,
|
||||
std::function<void(const std::string &, const std::string &)> format = nullptr)
|
||||
: schema_loader_(loader), format_check_(format)
|
||||
{
|
||||
}
|
||||
|
||||
// insert and set a root-schema
|
||||
void set_root_schema(const json &);
|
||||
|
||||
// validate a json-document based on the root-schema
|
||||
void validate(const json &instance);
|
||||
};
|
||||
|
||||
} // json_schema_draft4
|
||||
} // nlohmann
|
||||
|
||||
#endif /* NLOHMANN_JSON_SCHEMA_HPP__ */
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Modern C++ JSON schema validator
|
||||
*
|
||||
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*
|
||||
* Copyright (c) 2016 Patrick Boettcher <patrick.boettcher@posteo.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.
|
||||
*/
|
||||
#include "json-schema.hpp"
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
|
||||
void json_pointer::from_string(const std::string &r)
|
||||
{
|
||||
str_ = "#";
|
||||
|
||||
if (r.size() == 0)
|
||||
return;
|
||||
|
||||
if (r[0] != '#')
|
||||
throw std::invalid_argument("not a valid JSON pointer - missing # at the beginning");
|
||||
|
||||
if (r.size() == 1)
|
||||
return;
|
||||
|
||||
std::size_t pos = 1;
|
||||
|
||||
do {
|
||||
std::size_t next = r.find('/', pos + 1);
|
||||
str_.append(r.substr(pos, next - pos));
|
||||
pos = next;
|
||||
} while (pos != std::string::npos);
|
||||
}
|
||||
|
||||
void json_uri::from_string(const std::string &uri)
|
||||
{
|
||||
// if it is an urn take it as it is - maybe there is more to be done
|
||||
if (uri.find("urn:") == 0) {
|
||||
urn_ = uri;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string pointer = "#"; // default pointer is the root
|
||||
|
||||
// first split the URI into URL and JSON-pointer
|
||||
auto pointer_separator = uri.find('#');
|
||||
if (pointer_separator != std::string::npos) // and extract the JSON-pointer-string if found
|
||||
pointer = uri.substr(pointer_separator);
|
||||
|
||||
// the rest is an URL
|
||||
std::string url = uri.substr(0, pointer_separator);
|
||||
if (url.size()) { // if an URL is part of the URI
|
||||
|
||||
std::size_t pos = 0;
|
||||
auto proto = url.find("://", pos);
|
||||
if (proto != std::string::npos) { // extract the protocol
|
||||
proto_ = url.substr(pos, proto - pos);
|
||||
pos = 3 + proto; // 3 == "://"
|
||||
|
||||
auto hostname = url.find("/", pos);
|
||||
if (hostname != std::string::npos) { // and the hostname (no proto without hostname)
|
||||
hostname_ = url.substr(pos, hostname - pos);
|
||||
pos = hostname;
|
||||
}
|
||||
}
|
||||
|
||||
// the rest is the path
|
||||
auto path = url.substr(pos);
|
||||
if (path[0] == '/') // if it starts with a / it is root-path
|
||||
path_ = path;
|
||||
else { // otherwise it is a subfolder
|
||||
// HACK(syoyo): Force append '/' for glTF json schemas
|
||||
path_ = path;
|
||||
//path_.append(path);
|
||||
}
|
||||
|
||||
pointer_ = json_pointer("");
|
||||
}
|
||||
|
||||
if (pointer.size() > 0)
|
||||
pointer_ = pointer;
|
||||
}
|
||||
|
||||
const std::string json_uri::url() const
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
if (proto_.size() > 0)
|
||||
s << proto_ << "://";
|
||||
|
||||
s << hostname_
|
||||
<< path_;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::string json_uri::to_string() const
|
||||
{
|
||||
std::stringstream s;
|
||||
|
||||
s << urn_
|
||||
<< url()
|
||||
<< pointer_.to_string();
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const json_uri &u)
|
||||
{
|
||||
return os << u.to_string();
|
||||
}
|
||||
|
||||
std::string json_uri::unescape(const std::string &src)
|
||||
{
|
||||
std::string l = src;
|
||||
std::size_t pos = src.size() - 1;
|
||||
|
||||
do {
|
||||
pos = l.rfind('~', pos);
|
||||
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
if (pos < l.size() - 1) {
|
||||
switch (l[pos + 1]) {
|
||||
case '0':
|
||||
l.replace(pos, 2, "~");
|
||||
break;
|
||||
|
||||
case '1':
|
||||
l.replace(pos, 2, "/");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == 0)
|
||||
break;
|
||||
pos--;
|
||||
} while (pos != std::string::npos);
|
||||
|
||||
// TODO - percent handling
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
std::string json_uri::escape(const std::string &src)
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> chars = {
|
||||
{"~", "~0"},
|
||||
{"/", "~1"},
|
||||
{"%", "%25"}};
|
||||
|
||||
std::string l = src;
|
||||
|
||||
for (const auto &c : chars) {
|
||||
std::size_t pos = 0;
|
||||
do {
|
||||
pos = l.find(c.first, pos);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
l.replace(pos, 1, c.second);
|
||||
pos += c.second.size();
|
||||
} while (1);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
} // nlohmann
|
|
@ -0,0 +1,738 @@
|
|||
/*
|
||||
* Modern C++ JSON schema validator
|
||||
*
|
||||
* Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*
|
||||
* Copyright (c) 2016 Patrick Boettcher <patrick.boettcher@posteo.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.
|
||||
*/
|
||||
#include <json-schema.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_uri;
|
||||
|
||||
#ifdef JSON_SCHEMA_BOOST_REGEX
|
||||
#include <boost/regex.hpp>
|
||||
#define REGEX_NAMESPACE boost
|
||||
#elif defined(JSON_SCHEMA_NO_REGEX)
|
||||
#define NO_STD_REGEX
|
||||
#else
|
||||
#include <regex>
|
||||
#define REGEX_NAMESPACE std
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class resolver
|
||||
{
|
||||
void resolve(json &schema, json_uri id)
|
||||
{
|
||||
// look for the id-field in this schema
|
||||
auto fid = schema.find("id");
|
||||
|
||||
// found?
|
||||
if (fid != schema.end() &&
|
||||
fid.value().type() == json::value_t::string)
|
||||
id = id.derive(fid.value()); // resolve to a full id with URL + path based on the parent
|
||||
|
||||
// already existing - error
|
||||
if (schema_refs.find(id) != schema_refs.end())
|
||||
throw std::invalid_argument("schema " + id.to_string() + " already present in local resolver");
|
||||
|
||||
// store a raw pointer to this (sub-)schema referenced by its absolute json_uri
|
||||
// this (sub-)schema is part of a schema stored inside schema_store_ so we can the a raw-pointer-ref
|
||||
schema_refs[id] = &schema;
|
||||
|
||||
for (auto i = schema.begin(), end = schema.end(); i != end; ++i) {
|
||||
// FIXME: this inhibits the user adding properties with the key "default"
|
||||
if (i.key() == "default") /* default value can be objects, but are not schemas */
|
||||
continue;
|
||||
|
||||
switch (i.value().type()) {
|
||||
|
||||
case json::value_t::object: // child is object, it is a schema
|
||||
resolve(i.value(), id.append(json_uri::escape(i.key())));
|
||||
break;
|
||||
|
||||
case json::value_t::array: {
|
||||
std::size_t index = 0;
|
||||
auto child_id = id.append(json_uri::escape(i.key()));
|
||||
for (auto &v : i.value()) {
|
||||
if (v.type() == json::value_t::object) // array element is object
|
||||
resolve(v, child_id.append(std::to_string(index)));
|
||||
index++;
|
||||
}
|
||||
} break;
|
||||
|
||||
case json::value_t::string:
|
||||
if (i.key() == "$ref") {
|
||||
json_uri ref = id.derive(i.value());
|
||||
i.value() = ref.to_string();
|
||||
refs.insert(ref);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::set<json_uri> refs;
|
||||
|
||||
public:
|
||||
std::set<json_uri> undefined_refs;
|
||||
|
||||
std::map<json_uri, const json *> schema_refs;
|
||||
|
||||
resolver(json &schema, json_uri id)
|
||||
{
|
||||
// if schema has an id use it as name and to retrieve the namespace (URL)
|
||||
auto fid = schema.find("id");
|
||||
if (fid != schema.end())
|
||||
id = id.derive(fid.value());
|
||||
|
||||
resolve(schema, id);
|
||||
|
||||
// refs now contains all references
|
||||
//
|
||||
// local references should be resolvable inside the same URL
|
||||
//
|
||||
// undefined_refs will only contain external references
|
||||
for (auto r : refs) {
|
||||
if (schema_refs.find(r) == schema_refs.end()) {
|
||||
if (r.url() == id.url()) // same url means referencing a sub-schema
|
||||
// of the same document, which has not been found
|
||||
throw std::invalid_argument("sub-schema " + r.pointer().to_string() +
|
||||
" in schema " + id.to_string() + " not found");
|
||||
undefined_refs.insert(r.url());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void validate_type(const json &schema, const std::string &expected_type, const std::string &name)
|
||||
{
|
||||
const auto &type_it = schema.find("type");
|
||||
if (type_it == schema.end())
|
||||
/* TODO something needs to be done here, I think */
|
||||
return;
|
||||
|
||||
const auto &type_instance = type_it.value();
|
||||
|
||||
// any of the types in this array
|
||||
if (type_instance.type() == json::value_t::array) {
|
||||
if ((std::find(type_instance.begin(),
|
||||
type_instance.end(),
|
||||
expected_type) != type_instance.end()) ||
|
||||
(expected_type == "integer" &&
|
||||
std::find(type_instance.begin(),
|
||||
type_instance.end(),
|
||||
"number") != type_instance.end()))
|
||||
return;
|
||||
|
||||
std::ostringstream s;
|
||||
s << expected_type << " is not any of " << type_instance << " for " << name;
|
||||
throw std::invalid_argument(s.str());
|
||||
|
||||
} else { // type_instance is a string
|
||||
if (type_instance == expected_type ||
|
||||
(type_instance == "number" && expected_type == "integer"))
|
||||
return;
|
||||
|
||||
throw std::invalid_argument(name + " is " + expected_type +
|
||||
", but required type is " + type_instance.get<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
void validate_enum(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
const auto &enum_value = schema.find("enum");
|
||||
if (enum_value == schema.end())
|
||||
return;
|
||||
|
||||
if (std::find(enum_value.value().begin(), enum_value.value().end(), instance) != enum_value.value().end())
|
||||
return;
|
||||
|
||||
std::ostringstream s;
|
||||
s << "invalid enum-value '" << instance << "' "
|
||||
<< "for instance '" << name << "'. Candidates are " << enum_value.value() << ".";
|
||||
|
||||
throw std::invalid_argument(s.str());
|
||||
}
|
||||
|
||||
void validate_boolean(const json & /*instance*/, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "boolean", name);
|
||||
}
|
||||
|
||||
void validate_numeric(const json &schema, const std::string &name, double value)
|
||||
{
|
||||
// multipleOf - if the rest of the division is 0 -> OK
|
||||
const auto &multipleOf = schema.find("multipleOf");
|
||||
if (multipleOf != schema.end()) {
|
||||
if (multipleOf.value().get<double>() != 0.0) {
|
||||
|
||||
double v = value;
|
||||
v /= multipleOf.value().get<double>();
|
||||
|
||||
if (v != (double) (long) v)
|
||||
throw std::out_of_range(name + " is not a multiple ...");
|
||||
}
|
||||
}
|
||||
|
||||
const auto &maximum = schema.find("maximum");
|
||||
if (maximum != schema.end()) {
|
||||
double maxi = maximum.value();
|
||||
auto ex = std::out_of_range(name + " exceeds maximum of " + std::to_string(maxi));
|
||||
if (schema.find("exclusiveMaximum") != schema.end()) {
|
||||
if (value >= maxi)
|
||||
throw ex;
|
||||
} else {
|
||||
if (value > maxi)
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
const auto &minimum = schema.find("minimum");
|
||||
if (minimum != schema.end()) {
|
||||
double mini = minimum.value();
|
||||
auto ex = std::out_of_range(name + " exceeds minimum of " + std::to_string(mini));
|
||||
if (schema.find("exclusiveMinimum") != schema.end()) {
|
||||
if (value <= mini)
|
||||
throw ex;
|
||||
} else {
|
||||
if (value < mini)
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_integer(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "integer", name);
|
||||
validate_numeric(schema, name, instance.get<int>());
|
||||
}
|
||||
|
||||
void validate_unsigned(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "integer", name);
|
||||
validate_numeric(schema, name, instance.get<unsigned>());
|
||||
}
|
||||
|
||||
void validate_float(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "number", name);
|
||||
validate_numeric(schema, name, instance.get<double>());
|
||||
}
|
||||
|
||||
void validate_null(const json & /*instance*/, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "null", name);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
namespace json_schema_draft4
|
||||
{
|
||||
|
||||
void json_validator::insert_schema(const json &input, const json_uri &id)
|
||||
{
|
||||
// allocate create a copy for later storage - if resolving reference works
|
||||
std::shared_ptr<json> schema = std::make_shared<json>(input);
|
||||
|
||||
do {
|
||||
// resolve all local schemas and references
|
||||
resolver r(*schema, id);
|
||||
|
||||
// check whether all undefined schema references can be resolved with existing ones
|
||||
std::set<json_uri> undefined;
|
||||
for (auto &ref : r.undefined_refs)
|
||||
if (schema_refs_.find(ref) == schema_refs_.end()) // exact schema reference not found
|
||||
undefined.insert(ref);
|
||||
|
||||
if (undefined.size() == 0) { // no undefined references
|
||||
// now insert all schema-references
|
||||
// check whether all schema-references are new
|
||||
for (auto &sref : r.schema_refs) {
|
||||
if (schema_refs_.find(sref.first) != schema_refs_.end())
|
||||
// HACK(syoyo): Skip duplicated schema.
|
||||
break;
|
||||
//throw std::invalid_argument("schema " + sref.first.to_string() + " already present in validator.");
|
||||
}
|
||||
// no undefined references and no duplicated schema - store the schema
|
||||
schema_store_.push_back(schema);
|
||||
|
||||
// and insert all references
|
||||
schema_refs_.insert(r.schema_refs.begin(), r.schema_refs.end());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (schema_loader_ == nullptr)
|
||||
throw std::invalid_argument("schema contains undefined references to other schemas, needed schema-loader.");
|
||||
|
||||
for (auto undef : undefined) {
|
||||
json ext;
|
||||
|
||||
schema_loader_(undef, ext);
|
||||
insert_schema(ext, undef.url());
|
||||
}
|
||||
} while (1);
|
||||
|
||||
// store the document root-schema
|
||||
if (id == json_uri("#"))
|
||||
root_schema_ = schema;
|
||||
}
|
||||
|
||||
void json_validator::validate(const json &instance)
|
||||
{
|
||||
if (root_schema_ == nullptr)
|
||||
throw std::invalid_argument("no root-schema has been inserted. Cannot validate an instance without it.");
|
||||
|
||||
validate(instance, *root_schema_, "root");
|
||||
}
|
||||
|
||||
void json_validator::set_root_schema(const json &schema)
|
||||
{
|
||||
insert_schema(schema, json_uri("#"));
|
||||
}
|
||||
|
||||
void json_validator::validate(const json &instance, const json &schema_, const std::string &name)
|
||||
{
|
||||
const json *schema = &schema_;
|
||||
|
||||
// $ref resolution
|
||||
do {
|
||||
const auto &ref = schema->find("$ref");
|
||||
if (ref == schema->end())
|
||||
break;
|
||||
|
||||
auto it = schema_refs_.find(ref.value().get<std::string>());
|
||||
|
||||
if (it == schema_refs_.end())
|
||||
throw std::invalid_argument("schema reference " + ref.value().get<std::string>() + " not found. Make sure all schemas have been inserted before validation.");
|
||||
|
||||
schema = it->second;
|
||||
} while (1); // loop in case of nested refs
|
||||
|
||||
// not
|
||||
const auto attr = schema->find("not");
|
||||
if (attr != schema->end()) {
|
||||
bool ok;
|
||||
|
||||
try {
|
||||
validate(instance, attr.value(), name);
|
||||
ok = false;
|
||||
} catch (std::exception &) {
|
||||
ok = true;
|
||||
}
|
||||
if (!ok)
|
||||
throw std::invalid_argument("schema match for " + name + " but a not-match is defined by schema.");
|
||||
return; // return here - not cannot be mixed with based-schemas?
|
||||
}
|
||||
|
||||
// allOf, anyOf, oneOf
|
||||
const json *combined_schemas = nullptr;
|
||||
enum {
|
||||
none,
|
||||
allOf,
|
||||
anyOf,
|
||||
oneOf
|
||||
} combine_logic = none;
|
||||
|
||||
{
|
||||
const auto &attr = schema->find("allOf");
|
||||
if (attr != schema->end()) {
|
||||
combine_logic = allOf;
|
||||
combined_schemas = &attr.value();
|
||||
}
|
||||
}
|
||||
{
|
||||
const auto &attr = schema->find("anyOf");
|
||||
if (attr != schema->end()) {
|
||||
combine_logic = anyOf;
|
||||
combined_schemas = &attr.value();
|
||||
}
|
||||
}
|
||||
{
|
||||
const auto &attr = schema->find("oneOf");
|
||||
if (attr != schema->end()) {
|
||||
combine_logic = oneOf;
|
||||
combined_schemas = &attr.value();
|
||||
}
|
||||
}
|
||||
|
||||
if (combine_logic != none) {
|
||||
std::size_t count = 0;
|
||||
std::ostringstream sub_schema_err;
|
||||
|
||||
for (const auto &s : *combined_schemas) {
|
||||
try {
|
||||
validate(instance, s, name);
|
||||
count++;
|
||||
} catch (std::exception &e) {
|
||||
sub_schema_err << " one schema failed because: " << e.what() << "\n";
|
||||
|
||||
if (combine_logic == allOf)
|
||||
throw std::out_of_range("At least one schema has failed for " + name + " where allOf them were requested.\n" + sub_schema_err.str());
|
||||
}
|
||||
if (combine_logic == oneOf && count > 1)
|
||||
throw std::out_of_range("More than one schema has succeeded for " + name + " where only oneOf them was requested.\n" + sub_schema_err.str());
|
||||
}
|
||||
if ((combine_logic == anyOf || combine_logic == oneOf) && count == 0)
|
||||
throw std::out_of_range("No schema has succeeded for " + name + " but anyOf/oneOf them should have worked.\n" + sub_schema_err.str());
|
||||
}
|
||||
|
||||
// check (base) schema
|
||||
validate_enum(instance, *schema, name);
|
||||
|
||||
switch (instance.type()) {
|
||||
case json::value_t::object:
|
||||
validate_object(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::array:
|
||||
validate_array(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::string:
|
||||
validate_string(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::number_unsigned:
|
||||
validate_unsigned(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::number_integer:
|
||||
validate_integer(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::number_float:
|
||||
validate_float(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::boolean:
|
||||
validate_boolean(instance, *schema, name);
|
||||
break;
|
||||
|
||||
case json::value_t::null:
|
||||
validate_null(instance, *schema, name);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0 && "unexpected instance type for validation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void json_validator::validate_array(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "array", name);
|
||||
|
||||
// maxItems
|
||||
const auto &maxItems = schema.find("maxItems");
|
||||
if (maxItems != schema.end())
|
||||
if (instance.size() > maxItems.value().get<size_t>())
|
||||
throw std::out_of_range(name + " has too many items.");
|
||||
|
||||
// minItems
|
||||
const auto &minItems = schema.find("minItems");
|
||||
if (minItems != schema.end())
|
||||
if (instance.size() < minItems.value().get<size_t>())
|
||||
throw std::out_of_range(name + " has too few items.");
|
||||
|
||||
// uniqueItems
|
||||
const auto &uniqueItems = schema.find("uniqueItems");
|
||||
if (uniqueItems != schema.end())
|
||||
if (uniqueItems.value().get<bool>() == true) {
|
||||
std::set<json> array_to_set;
|
||||
for (auto v : instance) {
|
||||
auto ret = array_to_set.insert(v);
|
||||
if (ret.second == false)
|
||||
throw std::out_of_range(name + " should have only unique items.");
|
||||
}
|
||||
}
|
||||
|
||||
// items and additionalItems
|
||||
// default to empty schemas
|
||||
auto items_iter = schema.find("items");
|
||||
json items = {};
|
||||
if (items_iter != schema.end())
|
||||
items = items_iter.value();
|
||||
|
||||
auto additionalItems_iter = schema.find("additionalItems");
|
||||
json additionalItems = {};
|
||||
if (additionalItems_iter != schema.end())
|
||||
additionalItems = additionalItems_iter.value();
|
||||
|
||||
size_t i = 0;
|
||||
bool validation_done = false;
|
||||
|
||||
for (auto &value : instance) {
|
||||
std::string sub_name = name + "[" + std::to_string(i) + "]";
|
||||
|
||||
switch (items.type()) {
|
||||
|
||||
case json::value_t::array:
|
||||
|
||||
if (i < items.size())
|
||||
validate(value, items[i], sub_name);
|
||||
else {
|
||||
switch (additionalItems.type()) { // items is an array
|
||||
// we need to take into consideration additionalItems
|
||||
case json::value_t::object:
|
||||
validate(value, additionalItems, sub_name);
|
||||
break;
|
||||
|
||||
case json::value_t::boolean:
|
||||
if (additionalItems.get<bool>() == false)
|
||||
throw std::out_of_range("additional values in array are not allowed for " + sub_name);
|
||||
else
|
||||
validation_done = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case json::value_t::object: // items is a schema
|
||||
validate(value, items, sub_name);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (validation_done)
|
||||
break;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void json_validator::validate_object(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "object", name);
|
||||
|
||||
json properties = {};
|
||||
if (schema.find("properties") != schema.end())
|
||||
properties = schema["properties"];
|
||||
|
||||
#if 0
|
||||
// check for default values of properties
|
||||
// and insert them into this object, if they don't exists
|
||||
// works only for object properties for the moment
|
||||
if (default_value_insertion)
|
||||
for (auto it = properties.begin(); it != properties.end(); ++it) {
|
||||
|
||||
const auto &default_value = it.value().find("default");
|
||||
if (default_value == it.value().end())
|
||||
continue; /* no default value -> continue */
|
||||
|
||||
if (instance.find(it.key()) != instance.end())
|
||||
continue; /* value is present */
|
||||
|
||||
/* create element from default value */
|
||||
instance[it.key()] = default_value.value();
|
||||
}
|
||||
#endif
|
||||
// maxProperties
|
||||
const auto &maxProperties = schema.find("maxProperties");
|
||||
if (maxProperties != schema.end())
|
||||
if (instance.size() > maxProperties.value().get<size_t>())
|
||||
throw std::out_of_range(name + " has too many properties.");
|
||||
|
||||
// minProperties
|
||||
const auto &minProperties = schema.find("minProperties");
|
||||
if (minProperties != schema.end())
|
||||
if (instance.size() < minProperties.value().get<size_t>())
|
||||
throw std::out_of_range(name + " has too few properties.");
|
||||
|
||||
// additionalProperties
|
||||
enum {
|
||||
True,
|
||||
False,
|
||||
Object
|
||||
} additionalProperties = True;
|
||||
|
||||
const auto &additionalPropertiesVal = schema.find("additionalProperties");
|
||||
if (additionalPropertiesVal != schema.end()) {
|
||||
if (additionalPropertiesVal.value().type() == json::value_t::boolean)
|
||||
additionalProperties = additionalPropertiesVal.value().get<bool>() == true ? True : False;
|
||||
else
|
||||
additionalProperties = Object;
|
||||
}
|
||||
|
||||
// patternProperties
|
||||
json patternProperties = {};
|
||||
if (schema.find("patternProperties") != schema.end())
|
||||
patternProperties = schema["patternProperties"];
|
||||
|
||||
// check all elements in object
|
||||
for (auto child = instance.begin(); child != instance.end(); ++child) {
|
||||
std::string child_name = name + "." + child.key();
|
||||
|
||||
bool property_or_patternProperties_has_validated = false;
|
||||
// is this a property which is described in the schema
|
||||
const auto &object_prop = properties.find(child.key());
|
||||
if (object_prop != properties.end()) {
|
||||
// validate the element with its schema
|
||||
validate(child.value(), object_prop.value(), child_name);
|
||||
property_or_patternProperties_has_validated = true;
|
||||
}
|
||||
|
||||
for (auto pp = patternProperties.begin();
|
||||
pp != patternProperties.end(); ++pp) {
|
||||
#ifndef NO_STD_REGEX
|
||||
REGEX_NAMESPACE::regex re(pp.key(), REGEX_NAMESPACE::regex::ECMAScript);
|
||||
|
||||
if (REGEX_NAMESPACE::regex_search(child.key(), re)) {
|
||||
validate(child.value(), pp.value(), child_name);
|
||||
property_or_patternProperties_has_validated = true;
|
||||
}
|
||||
#else
|
||||
// accept everything in case of a patternProperty
|
||||
property_or_patternProperties_has_validated = true;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (property_or_patternProperties_has_validated)
|
||||
continue;
|
||||
|
||||
switch (additionalProperties) {
|
||||
case True:
|
||||
break;
|
||||
|
||||
case Object:
|
||||
validate(child.value(), additionalPropertiesVal.value(), child_name);
|
||||
break;
|
||||
|
||||
case False:
|
||||
throw std::invalid_argument("unknown property '" + child.key() + "' in object '" + name + "'");
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
// required
|
||||
const auto &required = schema.find("required");
|
||||
if (required != schema.end())
|
||||
for (const auto &element : required.value()) {
|
||||
if (instance.find(element) == instance.end()) {
|
||||
throw std::invalid_argument("required element '" + element.get<std::string>() +
|
||||
"' not found in object '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// dependencies
|
||||
const auto &dependencies = schema.find("dependencies");
|
||||
if (dependencies == schema.end())
|
||||
return;
|
||||
|
||||
for (auto dep = dependencies.value().cbegin();
|
||||
dep != dependencies.value().cend();
|
||||
++dep) {
|
||||
|
||||
// property not present in this instance - next
|
||||
if (instance.find(dep.key()) == instance.end())
|
||||
continue;
|
||||
|
||||
std::string sub_name = name + ".dependency-of-" + dep.key();
|
||||
|
||||
switch (dep.value().type()) {
|
||||
|
||||
case json::value_t::object:
|
||||
validate(instance, dep.value(), sub_name);
|
||||
break;
|
||||
|
||||
case json::value_t::array:
|
||||
for (const auto &prop : dep.value())
|
||||
if (instance.find(prop) == instance.end())
|
||||
throw std::invalid_argument("failed dependency for " + sub_name + ". Need property " + prop.get<std::string>());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::size_t utf8_length(const std::string &s)
|
||||
{
|
||||
size_t len = 0;
|
||||
for (const unsigned char &c : s)
|
||||
if ((c & 0xc0) != 0x80)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
void json_validator::validate_string(const json &instance, const json &schema, const std::string &name)
|
||||
{
|
||||
validate_type(schema, "string", name);
|
||||
|
||||
// minLength
|
||||
auto attr = schema.find("minLength");
|
||||
if (attr != schema.end())
|
||||
if (utf8_length(instance) < attr.value().get<size_t>()) {
|
||||
std::ostringstream s;
|
||||
s << "'" << name << "' of value '" << instance << "' is too short as per minLength ("
|
||||
<< attr.value() << ")";
|
||||
throw std::out_of_range(s.str());
|
||||
}
|
||||
|
||||
// maxLength
|
||||
attr = schema.find("maxLength");
|
||||
if (attr != schema.end())
|
||||
if (utf8_length(instance) > attr.value().get<size_t>()) {
|
||||
std::ostringstream s;
|
||||
s << "'" << name << "' of value '" << instance << "' is too long as per maxLength ("
|
||||
<< attr.value() << ")";
|
||||
throw std::out_of_range(s.str());
|
||||
}
|
||||
|
||||
#ifndef NO_STD_REGEX
|
||||
// pattern
|
||||
attr = schema.find("pattern");
|
||||
if (attr != schema.end()) {
|
||||
REGEX_NAMESPACE::regex re(attr.value().get<std::string>(), REGEX_NAMESPACE::regex::ECMAScript);
|
||||
if (!REGEX_NAMESPACE::regex_search(instance.get<std::string>(), re))
|
||||
throw std::invalid_argument(instance.get<std::string>() + " does not match regex pattern: " + attr.value().get<std::string>() + " for " + name);
|
||||
}
|
||||
#endif
|
||||
|
||||
// format
|
||||
attr = schema.find("format");
|
||||
if (attr != schema.end()) {
|
||||
if (format_check_ == nullptr)
|
||||
throw std::logic_error("A format checker was not provided but a format-attribute for this string is present. " +
|
||||
name + " cannot be validated for " + attr.value().get<std::string>());
|
||||
format_check_(attr.value(), instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,570 @@
|
|||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
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 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
|
||||
static std::string PrintParameterType(int ty) {
|
||||
if (ty == TINYGLTF_PARAMETER_TYPE_BYTE) {
|
||||
return "BYTE";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE) {
|
||||
return "UNSIGNED_BYTE";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_SHORT) {
|
||||
return "SHORT";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT) {
|
||||
return "UNSIGNED_SHORT";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_INT) {
|
||||
return "INT";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT) {
|
||||
return "UNSIGNED_INT";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_FLOAT) {
|
||||
return "FLOAT";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2) {
|
||||
return "FLOAT_VEC2";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3) {
|
||||
return "FLOAT_VEC3";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4) {
|
||||
return "FLOAT_VEC4";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_INT_VEC2) {
|
||||
return "INT_VEC2";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_INT_VEC3) {
|
||||
return "INT_VEC3";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_INT_VEC4) {
|
||||
return "INT_VEC4";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_BOOL) {
|
||||
return "BOOL";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_BOOL_VEC2) {
|
||||
return "BOOL_VEC2";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_BOOL_VEC3) {
|
||||
return "BOOL_VEC3";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_BOOL_VEC4) {
|
||||
return "BOOL_VEC4";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2) {
|
||||
return "FLOAT_MAT2";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3) {
|
||||
return "FLOAT_MAT3";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4) {
|
||||
return "FLOAT_MAT4";
|
||||
} else if (ty == TINYGLTF_PARAMETER_TYPE_SAMPLER_2D) {
|
||||
return "SAMPLER_2D";
|
||||
}
|
||||
|
||||
return "**UNKNOWN**";
|
||||
}
|
||||
#endif
|
||||
|
||||
static std::string PrintWrapMode(int mode) {
|
||||
if (mode == TINYGLTF_TEXTURE_WRAP_REPEAT) {
|
||||
return "REPEAT";
|
||||
} else if (mode == TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE) {
|
||||
return "CLAMP_TO_EDGE";
|
||||
} else if (mode == TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT) {
|
||||
return "MIRRORED_REPEAT";
|
||||
}
|
||||
|
||||
return "**UNKNOWN**";
|
||||
}
|
||||
|
||||
static std::string PrintFilterMode(int mode) {
|
||||
if (mode == TINYGLTF_TEXTURE_FILTER_NEAREST) {
|
||||
return "NEAREST";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_LINEAR) {
|
||||
return "LINEAR";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST) {
|
||||
return "NEAREST_MIPMAP_NEAREST";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR) {
|
||||
return "NEAREST_MIPMAP_LINEAR";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST) {
|
||||
return "LINEAR_MIPMAP_NEAREST";
|
||||
} else if (mode == TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR) {
|
||||
return "LINEAR_MIPMAP_LINEAR";
|
||||
}
|
||||
return "**UNKNOWN**";
|
||||
}
|
||||
|
||||
static std::string PrintIntArray(const std::vector<int> &arr) {
|
||||
if (arr.size() == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "[ ";
|
||||
for (size_t i = 0; i < arr.size(); i++) {
|
||||
ss << arr[i] << ((i != arr.size() - 1) ? ", " : "");
|
||||
}
|
||||
ss << " ]";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static std::string PrintFloatArray(const std::vector<double> &arr) {
|
||||
if (arr.size() == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "[ ";
|
||||
for (size_t i = 0; i < arr.size(); i++) {
|
||||
ss << arr[i] << ((i != arr.size() - 1) ? ", " : "");
|
||||
}
|
||||
ss << " ]";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static std::string Indent(const int indent) {
|
||||
std::string s;
|
||||
for (int i = 0; i < indent; i++) {
|
||||
s += " ";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static std::string PrintParameterValue(const tinygltf::Parameter ¶m) {
|
||||
if (!param.number_array.empty()) {
|
||||
return PrintFloatArray(param.number_array);
|
||||
} else {
|
||||
return param.string_value;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string PrintValue(const std::string &name,
|
||||
const tinygltf::Value &value, const int indent) {
|
||||
std::stringstream ss;
|
||||
|
||||
if (value.IsObject()) {
|
||||
const tinygltf::Value::Object &o = value.Get<tinygltf::Value::Object>();
|
||||
tinygltf::Value::Object::const_iterator it(o.begin());
|
||||
tinygltf::Value::Object::const_iterator itEnd(o.end());
|
||||
for (; it != itEnd; it++) {
|
||||
ss << PrintValue(name, it->second, indent + 1);
|
||||
}
|
||||
} else if (value.IsString()) {
|
||||
ss << Indent(indent) << name << " : " << value.Get<std::string>()
|
||||
<< std::endl;
|
||||
} else if (value.IsBool()) {
|
||||
ss << Indent(indent) << name << " : " << value.Get<bool>() << std::endl;
|
||||
} else if (value.IsNumber()) {
|
||||
ss << Indent(indent) << name << " : " << value.Get<double>() << std::endl;
|
||||
} else if (value.IsInt()) {
|
||||
ss << Indent(indent) << name << " : " << value.Get<int>() << std::endl;
|
||||
}
|
||||
// @todo { binary, array }
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static void DumpNode(const tinygltf::Node &node, int indent) {
|
||||
std::cout << Indent(indent) << "name : " << node.name << std::endl;
|
||||
std::cout << Indent(indent) << "camera : " << node.camera << std::endl;
|
||||
std::cout << Indent(indent) << "mesh : " << node.mesh << std::endl;
|
||||
if (!node.rotation.empty()) {
|
||||
std::cout << Indent(indent)
|
||||
<< "rotation : " << PrintFloatArray(node.rotation)
|
||||
<< std::endl;
|
||||
}
|
||||
if (!node.scale.empty()) {
|
||||
std::cout << Indent(indent)
|
||||
<< "scale : " << PrintFloatArray(node.scale) << std::endl;
|
||||
}
|
||||
if (!node.translation.empty()) {
|
||||
std::cout << Indent(indent)
|
||||
<< "translation : " << PrintFloatArray(node.translation)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (!node.matrix.empty()) {
|
||||
std::cout << Indent(indent)
|
||||
<< "matrix : " << PrintFloatArray(node.matrix) << std::endl;
|
||||
}
|
||||
|
||||
std::cout << Indent(indent)
|
||||
<< "children : " << PrintIntArray(node.children) << std::endl;
|
||||
}
|
||||
|
||||
static void DumpStringIntMap(const std::map<std::string, int> &m, int indent) {
|
||||
std::map<std::string, int>::const_iterator it(m.begin());
|
||||
std::map<std::string, int>::const_iterator itEnd(m.end());
|
||||
for (; it != itEnd; it++) {
|
||||
std::cout << Indent(indent) << it->first << ": " << it->second << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static void DumpPrimitive(const tinygltf::Primitive &primitive, int indent) {
|
||||
std::cout << Indent(indent) << "material : " << primitive.material
|
||||
<< std::endl;
|
||||
std::cout << Indent(indent) << "indices : " << primitive.indices << std::endl;
|
||||
std::cout << Indent(indent) << "mode : " << PrintMode(primitive.mode)
|
||||
<< "(" << primitive.mode << ")" << std::endl;
|
||||
std::cout << Indent(indent)
|
||||
<< "attributes(items=" << primitive.attributes.size() << ")"
|
||||
<< std::endl;
|
||||
DumpStringIntMap(primitive.attributes, indent + 1);
|
||||
|
||||
std::cout << Indent(indent) << "extras :" << std::endl
|
||||
<< PrintValue("extras", primitive.extras, indent + 1) << std::endl;
|
||||
}
|
||||
|
||||
static void Dump(const tinygltf::Model &model) {
|
||||
std::cout << "=== Dump glTF ===" << std::endl;
|
||||
std::cout << "asset.copyright : " << model.asset.copyright
|
||||
<< std::endl;
|
||||
std::cout << "asset.generator : " << model.asset.generator
|
||||
<< std::endl;
|
||||
std::cout << "asset.version : " << model.asset.version
|
||||
<< std::endl;
|
||||
std::cout << "asset.minVersion : " << model.asset.minVersion
|
||||
<< std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout << "=== Dump scene ===" << std::endl;
|
||||
std::cout << "defaultScene: " << model.defaultScene << std::endl;
|
||||
|
||||
{
|
||||
std::cout << "scenes(items=" << model.scenes.size() << ")" << std::endl;
|
||||
for (size_t i = 0; i < model.scenes.size(); i++) {
|
||||
std::cout << Indent(1) << "scene[" << i
|
||||
<< "] name : " << model.scenes[i].name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "meshes(item=" << model.meshes.size() << ")" << std::endl;
|
||||
for (size_t i = 0; i < model.meshes.size(); i++) {
|
||||
std::cout << Indent(1) << "name : " << model.meshes[i].name
|
||||
<< std::endl;
|
||||
std::cout << Indent(1)
|
||||
<< "primitives(items=" << model.meshes[i].primitives.size()
|
||||
<< "): " << std::endl;
|
||||
|
||||
for (size_t k = 0; k < model.meshes[i].primitives.size(); k++) {
|
||||
DumpPrimitive(model.meshes[i].primitives[k], 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (size_t i = 0; i < model.accessors.size(); i++) {
|
||||
const tinygltf::Accessor &accessor = model.accessors[i];
|
||||
std::cout << Indent(1) << "name : " << accessor.name << std::endl;
|
||||
std::cout << Indent(2) << "bufferView : " << accessor.bufferView
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "byteOffset : " << accessor.byteOffset
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "componentType: "
|
||||
<< PrintComponentType(accessor.componentType) << "("
|
||||
<< accessor.componentType << ")" << std::endl;
|
||||
std::cout << Indent(2) << "count : " << accessor.count
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "type : " << PrintType(accessor.type)
|
||||
<< std::endl;
|
||||
if (!accessor.minValues.empty()) {
|
||||
std::cout << Indent(2) << "min : [";
|
||||
for (size_t k = 0; k < accessor.minValues.size(); k++) {
|
||||
std::cout << accessor.minValues[k]
|
||||
<< ((i != accessor.minValues.size() - 1) ? ", " : "");
|
||||
}
|
||||
std::cout << "]" << std::endl;
|
||||
}
|
||||
if (!accessor.maxValues.empty()) {
|
||||
std::cout << Indent(2) << "max : [";
|
||||
for (size_t k = 0; k < accessor.maxValues.size(); k++) {
|
||||
std::cout << accessor.maxValues[k]
|
||||
<< ((i != accessor.maxValues.size() - 1) ? ", " : "");
|
||||
}
|
||||
std::cout << "]" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "animations(items=" << model.animations.size() << ")"
|
||||
<< std::endl;
|
||||
for (size_t i = 0; i < model.animations.size(); i++) {
|
||||
const tinygltf::Animation &animation = model.animations[i];
|
||||
std::cout << Indent(1) << "name : " << animation.name
|
||||
<< std::endl;
|
||||
|
||||
std::cout << Indent(1) << "channels : [ " << std::endl;
|
||||
for (size_t j = 0; i < animation.channels.size(); i++) {
|
||||
std::cout << Indent(2)
|
||||
<< "sampler : " << animation.channels[j].sampler
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "target.id : " << animation.channels[j].target_node
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "target.path : " << animation.channels[j].target_path
|
||||
<< std::endl;
|
||||
std::cout << ((i != (animation.channels.size() - 1)) ? " , " : "");
|
||||
}
|
||||
std::cout << " ]" << std::endl;
|
||||
|
||||
std::cout << Indent(1) << "samplers(items=" << animation.samplers.size()
|
||||
<< ")" << std::endl;
|
||||
for (size_t j = 0; j < animation.samplers.size(); j++) {
|
||||
const tinygltf::AnimationSampler &sampler = animation.samplers[j];
|
||||
std::cout << Indent(2) << "input : " << sampler.input
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "interpolation : " << sampler.interpolation
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "output : " << sampler.output
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "bufferViews(items=" << model.bufferViews.size() << ")"
|
||||
<< std::endl;
|
||||
for (size_t i = 0; i < model.bufferViews.size(); i++) {
|
||||
const tinygltf::BufferView &bufferView = model.bufferViews[i];
|
||||
std::cout << Indent(1) << "name : " << bufferView.name
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "buffer : " << bufferView.buffer
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "byteLength : " << bufferView.byteLength
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "byteOffset : " << bufferView.byteOffset
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "byteStride : " << bufferView.byteStride
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "target : " << PrintTarget(bufferView.target)
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "buffers(items=" << model.buffers.size() << ")" << std::endl;
|
||||
for (size_t i = 0; i < model.buffers.size(); i++) {
|
||||
const tinygltf::Buffer &buffer = model.buffers[i];
|
||||
std::cout << Indent(1) << "name : " << buffer.name << std::endl;
|
||||
std::cout << Indent(2) << "byteLength : " << buffer.data.size()
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "materials(items=" << model.materials.size() << ")"
|
||||
<< std::endl;
|
||||
for (size_t i = 0; i < model.materials.size(); i++) {
|
||||
const tinygltf::Material &material = model.materials[i];
|
||||
std::cout << Indent(1) << "name : " << material.name << std::endl;
|
||||
std::cout << Indent(1) << "values(items=" << material.values.size() << ")"
|
||||
<< std::endl;
|
||||
|
||||
tinygltf::ParameterMap::const_iterator p(material.values.begin());
|
||||
tinygltf::ParameterMap::const_iterator pEnd(material.values.end());
|
||||
for (; p != pEnd; p++) {
|
||||
std::cout << Indent(2) << p->first << ": "
|
||||
<< PrintParameterValue(p->second) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "nodes(items=" << model.nodes.size() << ")" << std::endl;
|
||||
for (size_t i = 0; i < model.nodes.size(); i++) {
|
||||
const tinygltf::Node &node = model.nodes[i];
|
||||
std::cout << Indent(1) << "name : " << node.name << std::endl;
|
||||
|
||||
DumpNode(node, 2);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "images(items=" << model.images.size() << ")" << std::endl;
|
||||
for (size_t i = 0; i < model.images.size(); i++) {
|
||||
const tinygltf::Image &image = model.images[i];
|
||||
std::cout << Indent(1) << "name : " << image.name << std::endl;
|
||||
|
||||
std::cout << Indent(2) << "width : " << image.width << std::endl;
|
||||
std::cout << Indent(2) << "height : " << image.height << std::endl;
|
||||
std::cout << Indent(2) << "component : " << image.component << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "textures(items=" << model.textures.size() << ")" << std::endl;
|
||||
for (size_t i = 0; i < model.textures.size(); i++) {
|
||||
const tinygltf::Texture &texture = model.textures[i];
|
||||
std::cout << Indent(1) << "sampler : " << texture.sampler
|
||||
<< std::endl;
|
||||
std::cout << Indent(1) << "source : " << texture.source
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "samplers(items=" << model.samplers.size() << ")" << std::endl;
|
||||
|
||||
for (size_t i = 0; i < model.samplers.size(); i++) {
|
||||
const tinygltf::Sampler &sampler = model.samplers[i];
|
||||
std::cout << Indent(1) << "name (id) : " << sampler.name << std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "minFilter : " << PrintFilterMode(sampler.minFilter)
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "magFilter : " << PrintFilterMode(sampler.magFilter)
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "wrapS : " << PrintWrapMode(sampler.wrapS)
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "wrapT : " << PrintWrapMode(sampler.wrapT)
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "cameras(items=" << model.cameras.size() << ")" << std::endl;
|
||||
|
||||
for (size_t i = 0; i < model.cameras.size(); i++) {
|
||||
const tinygltf::Camera &camera = model.cameras[i];
|
||||
std::cout << Indent(1) << "name (id) : " << camera.name << std::endl;
|
||||
std::cout << Indent(1) << "type : " << camera.type << std::endl;
|
||||
|
||||
if (camera.type.compare("perspective") == 0) {
|
||||
std::cout << Indent(2)
|
||||
<< "aspectRatio : " << camera.perspective.aspectRatio
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "yfov : " << camera.perspective.yfov
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "zfar : " << camera.perspective.zfar
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "znear : " << camera.perspective.znear
|
||||
<< std::endl;
|
||||
} else if (camera.type.compare("orthographic") == 0) {
|
||||
std::cout << Indent(2) << "xmag : " << camera.orthographic.xmag
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "ymag : " << camera.orthographic.ymag
|
||||
<< std::endl;
|
||||
std::cout << Indent(2) << "zfar : " << camera.orthographic.zfar
|
||||
<< std::endl;
|
||||
std::cout << Indent(2)
|
||||
<< "znear : " << camera.orthographic.znear
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
printf("Needs input.gltf\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF gltf_ctx;
|
||||
std::string err;
|
||||
std::string input_filename(argv[1]);
|
||||
std::string ext = GetFilePathExtension(input_filename);
|
||||
|
||||
bool ret = false;
|
||||
if (ext.compare("glb") == 0) {
|
||||
std::cout << "Reading binary glTF" << std::endl;
|
||||
// assume binary glTF.
|
||||
ret = gltf_ctx.LoadBinaryFromFile(&model, &err, input_filename.c_str());
|
||||
} else {
|
||||
std::cout << "Reading ASCII glTF" << std::endl;
|
||||
// assume ascii glTF.
|
||||
ret = gltf_ctx.LoadASCIIFromFile(&model, &err, input_filename.c_str());
|
||||
}
|
||||
|
||||
if (!err.empty()) {
|
||||
printf("Err: %s\n", err.c_str());
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
printf("Failed to parse glTF\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Dump(model);
|
||||
|
||||
return 0;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,193 @@
|
|||
{
|
||||
"accessors" : [
|
||||
{
|
||||
"bufferView" : 0,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5123,
|
||||
"count" : 36,
|
||||
"max" : [
|
||||
35
|
||||
],
|
||||
"min" : [
|
||||
0
|
||||
],
|
||||
"type" : "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView" : 1,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 36,
|
||||
"max" : [
|
||||
1.000000,
|
||||
1.000000,
|
||||
1.000001
|
||||
],
|
||||
"min" : [
|
||||
-1.000000,
|
||||
-1.000000,
|
||||
-1.000000
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 2,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 36,
|
||||
"max" : [
|
||||
1.000000,
|
||||
1.000000,
|
||||
1.000000
|
||||
],
|
||||
"min" : [
|
||||
-1.000000,
|
||||
-1.000000,
|
||||
-1.000000
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 3,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 36,
|
||||
"max" : [
|
||||
1.000000,
|
||||
-0.000000,
|
||||
-0.000000,
|
||||
1.000000
|
||||
],
|
||||
"min" : [
|
||||
0.000000,
|
||||
-0.000000,
|
||||
-1.000000,
|
||||
-1.000000
|
||||
],
|
||||
"type" : "VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView" : 4,
|
||||
"byteOffset" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 36,
|
||||
"max" : [
|
||||
1.000000,
|
||||
1.000000
|
||||
],
|
||||
"min" : [
|
||||
-1.000000,
|
||||
-1.000000
|
||||
],
|
||||
"type" : "VEC2"
|
||||
}
|
||||
],
|
||||
"asset" : {
|
||||
"generator" : "VKTS glTF 2.0 exporter",
|
||||
"version" : "2.0"
|
||||
},
|
||||
"bufferViews" : [
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 72,
|
||||
"byteOffset" : 0,
|
||||
"target" : 34963
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 432,
|
||||
"byteOffset" : 72,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 432,
|
||||
"byteOffset" : 504,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 576,
|
||||
"byteOffset" : 936,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 288,
|
||||
"byteOffset" : 1512,
|
||||
"target" : 34962
|
||||
}
|
||||
],
|
||||
"buffers" : [
|
||||
{
|
||||
"byteLength" : 1800,
|
||||
"uri" : "Cube.bin"
|
||||
}
|
||||
],
|
||||
"images" : [
|
||||
{
|
||||
"uri" : "Cube_BaseColor.png"
|
||||
},
|
||||
{
|
||||
"uri" : "Cube_MetallicRoughness.png"
|
||||
}
|
||||
],
|
||||
"materials" : [
|
||||
{
|
||||
"name" : "Cube",
|
||||
"pbrMetallicRoughness" : {
|
||||
"baseColorTexture" : {
|
||||
"index" : 0
|
||||
},
|
||||
"metallicRoughnessTexture" : {
|
||||
"index" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes" : [
|
||||
{
|
||||
"name" : "Cube",
|
||||
"primitives" : [
|
||||
{
|
||||
"attributes" : {
|
||||
"NORMAL" : 2,
|
||||
"POSITION" : 1,
|
||||
"TANGENT" : 3,
|
||||
"TEXCOORD_0" : 4
|
||||
},
|
||||
"indices" : 0,
|
||||
"material" : 0,
|
||||
"mode" : 4
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes" : [
|
||||
{
|
||||
"mesh" : 0,
|
||||
"name" : "Cube"
|
||||
}
|
||||
],
|
||||
"samplers" : [
|
||||
{}
|
||||
],
|
||||
"scene" : 0,
|
||||
"scenes" : [
|
||||
{
|
||||
"nodes" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures" : [
|
||||
{
|
||||
"sampler" : 0,
|
||||
"source" : 0
|
||||
},
|
||||
{
|
||||
"sampler" : 0,
|
||||
"source" : 1
|
||||
}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 871 KiB |
Binary file not shown.
After Width: | Height: | Size: 319 B |
|
@ -0,0 +1,4 @@
|
|||
License: Donated by Norbert Nopper for glTF testing.
|
||||
|
||||
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Cube
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
sources = {
|
||||
"loader_example.cc",
|
||||
}
|
||||
|
||||
-- premake4.lua
|
||||
solution "TinyGLTFSolution"
|
||||
configurations { "Release", "Debug" }
|
||||
|
||||
if (os.is("windows")) then
|
||||
platforms { "x32", "x64" }
|
||||
else
|
||||
platforms { "native", "x32", "x64" }
|
||||
end
|
||||
|
||||
-- A project defines one build target
|
||||
project "tinygltf"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
files { sources }
|
||||
flags { "C++11" }
|
||||
|
||||
configuration "Debug"
|
||||
defines { "DEBUG" } -- -DDEBUG
|
||||
flags { "Symbols" }
|
||||
targetname "loader_example_tinygltf_debug"
|
||||
|
||||
configuration "Release"
|
||||
-- defines { "NDEBUG" } -- -NDEBUG
|
||||
flags { "Symbols", "Optimize" }
|
||||
targetname "loader_example_tinygltf"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Assume python 2.6 or 2.7
|
||||
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
## Simple test runner.
|
||||
|
||||
# -- config -----------------------
|
||||
|
||||
# Absolute path pointing to your cloned git repo of https://github.com/KhronosGroup/glTF-Sample-Models
|
||||
sample_model_dir = "/home/syoyo/work/glTF-Sample-Models"
|
||||
base_model_dir = os.path.join(sample_model_dir, "2.0")
|
||||
|
||||
kinds = [ "glTF", "glTF-Binary", "glTF-Embedded", "glTF-MaterialsCommon"]
|
||||
# ---------------------------------
|
||||
|
||||
failed = []
|
||||
success = []
|
||||
|
||||
def run(filename):
|
||||
|
||||
print("Testing: " + filename)
|
||||
cmd = ["./loader_example", filename]
|
||||
try:
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(stdout, stderr) = p.communicate()
|
||||
except:
|
||||
print "Failed to execute: ", cmd
|
||||
raise
|
||||
|
||||
if p.returncode != 0:
|
||||
failed.append(filename)
|
||||
print(stdout)
|
||||
print(stderr)
|
||||
else:
|
||||
success.append(filename)
|
||||
|
||||
|
||||
def test():
|
||||
|
||||
for d in os.listdir(base_model_dir):
|
||||
p = os.path.join(base_model_dir, d)
|
||||
if os.path.isdir(p):
|
||||
for k in kinds:
|
||||
targetDir = os.path.join(p, k)
|
||||
g = glob.glob(targetDir + "/*.gltf") + glob.glob(targetDir + "/*.glb")
|
||||
for gltf in g:
|
||||
run(gltf)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
test()
|
||||
|
||||
print("Success : {0}".format(len(success)))
|
||||
print("Failed : {0}".format(len(failed)))
|
||||
|
||||
for fail in failed:
|
||||
print("FAIL: " + fail)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
TEST_CASE("parse-error", "[parse]") {
|
||||
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF ctx;
|
||||
std::string err;
|
||||
|
||||
bool ret = ctx.LoadASCIIFromString(&model, &err, "bora", strlen("bora"), /* basedir*/ "");
|
||||
|
||||
REQUIRE(false == ret);
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
.\\tools\\windows\\premake5.exe vs2015
|
Loading…
Reference in New Issue