mirror of
https://github.com/syoyo/tinygltf.git
synced 2026-06-15 11:48:52 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2026e03fab |
70
.gitignore
vendored
70
.gitignore
vendored
@@ -1,70 +0,0 @@
|
||||
# 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
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/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
|
||||
17
.travis.yml
17
.travis.yml
@@ -7,15 +7,15 @@ matrix:
|
||||
sources:
|
||||
- george-edison55-precise-backports
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-3.9
|
||||
- llvm-toolchain-precise-3.7
|
||||
packages:
|
||||
- g++-4.9
|
||||
- clang-3.9
|
||||
- clang-3.7
|
||||
compiler: clang
|
||||
env: COMPILER_VERSION=3.9 BUILD_TYPE=Debug
|
||||
env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug
|
||||
- addons: *1
|
||||
compiler: clang
|
||||
env: COMPILER_VERSION=3.9 BUILD_TYPE=Release
|
||||
env: COMPILER_VERSION=3.7 BUILD_TYPE=Release
|
||||
- addons: &2
|
||||
apt:
|
||||
sources:
|
||||
@@ -30,18 +30,15 @@ matrix:
|
||||
env: COMPILER_VERSION=4.9 BUILD_TYPE=Release EXTRA_CXXFLAGS="-fsanitize=address"
|
||||
- addons: *1
|
||||
compiler: clang
|
||||
env: COMPILER_VERSION=3.9 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
|
||||
env: COMPILER_VERSION=3.7 BUILD_TYPE=Debug CFLAGS="-O0" CXXFLAGS="-O0"
|
||||
|
||||
before_install:
|
||||
- ./.travis-before-install.sh
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade; fi
|
||||
|
||||
|
||||
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
|
||||
- ${CXX} ${EXTRA_CXXFLAGS} -Wall -g -o loader_example loader_example.cc
|
||||
- ./loader_example ./models/Cube/Cube.gltf
|
||||
- cd examples/raytrace
|
||||
- ../../premake5 gmake
|
||||
- make
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
|
||||
PROJECT (tinygltf)
|
||||
|
||||
SET(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
ADD_EXECUTABLE ( loader_example
|
||||
loader_example.cc
|
||||
)
|
||||
|
||||
ADD_SUBDIRECTORY ( examples/gltfutil )
|
||||
ADD_SUBDIRECTORY ( examples/glview )
|
||||
|
||||
INSTALL ( FILES
|
||||
stb_image.h
|
||||
stb_image_write.h
|
||||
tiny_gltf.h
|
||||
DESTINATION
|
||||
include
|
||||
)
|
||||
|
||||
INSTALL ( FILES
|
||||
cmake/TinyGLTFConfig.cmake
|
||||
DESTINATION
|
||||
cmake
|
||||
)
|
||||
6
Makefile
6
Makefile
@@ -1,9 +1,9 @@
|
||||
|
||||
# Use this for strict compilation check(will work on clang 3.8+)
|
||||
#EXTRA_CXXFLAGS := -fsanitize=address -Wall -Werror -Weverything -Wno-c++11-long-long -Wno-c++98-compat
|
||||
#EXTRA_CXXFLAGS := -fsanitize=address -Wall -Werror -Weverything -Wno-c++11-long-long -DTINYGLTF_APPLY_CLANG_WEVERYTHING
|
||||
|
||||
all:
|
||||
clang++ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o loader_example loader_example.cc
|
||||
clang++ $(EXTRA_CXXFLAGS) -g -O0 -o loader_example loader_example.cc
|
||||
|
||||
lint:
|
||||
deps/cpplint.py tiny_gltf.h
|
||||
./cpplint.py tiny_gltf_loader.h
|
||||
|
||||
114
README.md
114
README.md
@@ -1,14 +1,10 @@
|
||||
# Header only C++ tiny glTF library(loader/saver).
|
||||
|
||||
`TinyGLTF` is a header only C++11 glTF 2.0 https://github.com/KhronosGroup/glTF library.
|
||||
|
||||
`TinyGLTF` uses dropbox/json11 JSON library(https://github.com/dropbox/json11), so now it requires C++11 compiler.
|
||||
If you are looking for old, C++03 version, please use `devel-picojson` branch.
|
||||
`TinyGLTF` is a header only C++ glTF 2.0 https://github.com/KhronosGroup/glTF library.
|
||||
|
||||
## Status
|
||||
|
||||
v2.2.0 Use dropbox/json11 instead of nlohmann's json.hpp
|
||||
v2.0.0 release(22 Aug, 2018)!
|
||||
Work in process(`devel` branch). Very near to release, but need more tests and examples.
|
||||
|
||||
## Builds
|
||||
|
||||
@@ -18,65 +14,41 @@ v2.0.0 release(22 Aug, 2018)!
|
||||
|
||||
## Features
|
||||
|
||||
* Written in portable C++. C++-11 with STL dependency only.
|
||||
* Written in portable C++. C++-03 with STL dependency only.
|
||||
* [x] macOS + clang(LLVM)
|
||||
* [x] iOS + clang
|
||||
* [x] Linux + gcc/clang
|
||||
* [x] Windows + MinGW
|
||||
* [x] Windows + Visual Studio 2015 Update 3 or later.
|
||||
* Visual Studio 2013 is not supported since they have limited C++11 support.
|
||||
* [x] Android NDK
|
||||
* [x] Android + CrystaX(NDK drop-in replacement) GCC
|
||||
* [x] Web using Emscripten(LLVM)
|
||||
* [x] Can be compiled with no RTTI, noexception.
|
||||
* 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 data(DataURI).
|
||||
* [x] Parse BASE64 encoded embedded buffer fata(DataURI).
|
||||
* [x] Load `.bin` file.
|
||||
* Image(Using stb_image)
|
||||
* [x] Parse BASE64 encoded embedded image data(DataURI).
|
||||
* [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
|
||||
* [x] Custom Image decoder callback(e.g. for decoding OpenEXR image)
|
||||
* Load from memory
|
||||
* Custom callback handler
|
||||
* [x] Image load
|
||||
* Extensions
|
||||
* [x] Draco mesh decoding
|
||||
|
||||
## Examples
|
||||
|
||||
* [glview](examples/glview) : Simple glTF geometry viewer.
|
||||
* [validator](examples/validator) : Simple glTF validator with JSON schema.
|
||||
* [basic](examples/basic) : Basic glTF viewer with texturing support.
|
||||
|
||||
## Projects using TinyGLTF
|
||||
|
||||
* px_render Single header C++ Libraries for Thread Scheduling, Rendering, and so on... https://github.com/pplux/px
|
||||
* Physical based rendering with Vulkan using glTF 2.0 models https://github.com/SaschaWillems/Vulkan-glTF-PBR
|
||||
* GLTF loader plugin for OGRE 2.1. Support for PBR materials via HLMS/PBS https://github.com/Ybalrid/Ogre_glTF
|
||||
* [TinyGltfImporter](http://doc.magnum.graphics/magnum/classMagnum_1_1Trade_1_1TinyGltfImporter.html) plugin for [Magnum](https://github.com/mosra/magnum), a lightweight and modular C++11/C++14 graphics middleware for games and data visualization.
|
||||
* Your projects here! (Please send PR)
|
||||
|
||||
## TODOs
|
||||
|
||||
* [ ] Write C++ code generator which emits C++ code from JSON schema for robust parsing.
|
||||
* [ ] Mesh Compression/decompression(Open3DGC, etc)
|
||||
* [x] Load Draco compressed mesh
|
||||
* [x] Save Draco compressed mesh
|
||||
* [ ] Write C++ code generator from json schema for robust parsing.
|
||||
* [ ] Serialization
|
||||
* [ ] Compression/decompression(Open3DGC, etc)
|
||||
* [ ] Support `extensions` and `extras` property
|
||||
* [ ] HDR image?
|
||||
* [ ] OpenEXR extension through TinyEXR.
|
||||
* [ ] Write example and tests for `animation` and `skin`
|
||||
* [ ] Skinning
|
||||
* [ ] Morph targets
|
||||
* [ ] Write tests for `animation` and `skin`
|
||||
|
||||
## Licenses
|
||||
|
||||
@@ -84,27 +56,21 @@ TinyGLTF is licensed under MIT license.
|
||||
|
||||
TinyGLTF uses the following third party libraries.
|
||||
|
||||
* json11 : Copyright (c) 2013 Dropbox, Inc. MIT license.
|
||||
* picojson.h : Copyright 2009-2010 Cybozu Labs, Inc. Copyright 2011-2014 Kazuho Oku
|
||||
* base64 : Copyright (C) 2004-2008 René Nyffenegger
|
||||
* stb_image.h : v2.08 - public domain image loader - [Github link](https://github.com/nothings/stb/blob/master/stb_image.h)
|
||||
* stb_image_write.h : v1.09 - public domain image writer - [Github link](https://github.com/nothings/stb/blob/master/stb_image_write.h)
|
||||
* stb_image.h : v2.08 - public domain image loader - http://nothings.org/stb_image.h
|
||||
|
||||
|
||||
## Build and example
|
||||
|
||||
Copy `stb_image.h`, `stb_image_write.h`, and `tiny_gltf.h` to your project.
|
||||
json11 code is embeded into `tiny_gltf.h`
|
||||
Copy `stb_image.h`, `picojson.h` 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 STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
// Optional. Uncomment these if you want external json11.cpp instead of embedded json11.cpp code
|
||||
//#define TINYGLTF_USE_EXTERNAL_JSON11_CPP
|
||||
//#include "json11.hpp"
|
||||
#include "tiny_gltf.h"
|
||||
|
||||
using namespace tinygltf;
|
||||
@@ -112,15 +78,9 @@ using namespace tinygltf;
|
||||
Model model;
|
||||
TinyGLTF loader;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
bool ret = loader.LoadASCIIFromFile(&model, &err, &warn, argv[1]);
|
||||
//bool ret = loader.LoadBinaryFromFile(&model, &err, &warn, argv[1]); // for binary glTF(.glb)
|
||||
|
||||
if (!warn.empty()) {
|
||||
printf("Warn: %s\n", warn.c_str());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
@@ -131,53 +91,19 @@ if (!ret) {
|
||||
}
|
||||
```
|
||||
|
||||
## Compile options
|
||||
|
||||
* `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.
|
||||
* `TINYGLTF_NO_STB_IMAGE_WRITE` : Do not write images with stb_image_write. Instead use `TinyGLTF::SetImageWriter(WriteimageDataFunction WriteImageData, void *user_data)` to set a callback for writing images.
|
||||
* `TINYGLTF_NO_EXTERNAL_IMAGE` : Do not try to load external image file. This option woulde be helpful if you do not want load image file during glTF parsing.
|
||||
* `TINYGLTF_ANDROID_LOAD_FROM_ASSETS`: Load all files from packaged app assets instead of the regular file system. **Note:** You must pass a valid asset manager from your android app to `tinygltf::asset_manager` beforehand.
|
||||
* `TINYGLTF_ENABLE_DRACO`: Enable Draco compression. User must provide include path and link correspnding libraries in your project file.
|
||||
* `TINYGLTF_USE_EXTERNAL_JSON11_CPP` : Use externally supplied json11.cpp/json11.hpp. This option may be helpful if you use json11.cpp in other source codes of your project.
|
||||
|
||||
### Saving gltTF 2.0 model
|
||||
* [ ] Buffers.
|
||||
* [x] To file
|
||||
* [x] Embedded
|
||||
* [ ] Draco compressed?
|
||||
* [x] Images
|
||||
* [x] To file
|
||||
* [x] Embedded
|
||||
* [ ] Binary(.glb)
|
||||
|
||||
T.B.W.
|
||||
|
||||
## Running tests.
|
||||
|
||||
### glTF parsing test
|
||||
|
||||
#### Setup
|
||||
### Setup
|
||||
|
||||
Python 2.6 or 2.7 required.
|
||||
Git clone https://github.com/KhronosGroup/glTF-Sample-Models to your local dir.
|
||||
|
||||
#### Run parsing test
|
||||
### Run 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
|
||||
|
||||
* json11 : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
* stb_image : Public domain.
|
||||
* catch : Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. Distributed under the Boost Software License, Version 1.0.
|
||||
$ python test_runner.py
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
version: 0.9.{build}
|
||||
|
||||
image:
|
||||
- Visual Studio 2015
|
||||
|
||||
# scripts that runs after repo cloning.
|
||||
install:
|
||||
- vcsetup.bat
|
||||
@@ -13,6 +10,3 @@ configuration: Release
|
||||
build:
|
||||
parallel: true
|
||||
project: TinyGLTFSolution.sln
|
||||
|
||||
after_build:
|
||||
- examples.bat
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# -*- cmake -*-
|
||||
# - Find TinyGLTF
|
||||
|
||||
# TinyGLTF_INCLUDE_DIR TinyGLTF's include directory
|
||||
|
||||
|
||||
FIND_PACKAGE ( PackageHandleStandardArgs )
|
||||
|
||||
SET ( TinyGLTF_INCLUDE_DIR "${TinyGLTF_DIR}/../include" CACHE STRING "TinyGLTF include directory")
|
||||
|
||||
FIND_FILE ( TinyGLTF_HEADER tiny_gltf.h PATHS ${TinyGLTF_INCLUDE_DIR} )
|
||||
|
||||
IF (NOT TinyGLTF_HEADER)
|
||||
MESSAGE ( FATAL_ERROR "Unable to find tiny_gltf.h, TinyGLTF_INCLUDE_DIR = ${TinyGLTF_INCLUDE_DIR}")
|
||||
ENDIF ()
|
||||
@@ -1,3 +0,0 @@
|
||||
cd examples\raytrace
|
||||
..\..\tools\windows\premake5.exe vs2015
|
||||
msbuild NanoSGSolution.sln /property:Configuration=Release
|
||||
7
examples/basic/.gitignore
vendored
7
examples/basic/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
.vs
|
||||
Debug
|
||||
x64
|
||||
packages
|
||||
|
||||
!*.sln
|
||||
!*.vcxproj*
|
||||
@@ -1,21 +0,0 @@
|
||||
# Basic glTF viewer
|
||||
|
||||
## Requirements
|
||||
|
||||
* glew
|
||||
* glfw3
|
||||
* premake5(linux)
|
||||
* OpenGL 3.3+ GPU
|
||||
|
||||
## Build on Linux and macOS
|
||||
|
||||
```
|
||||
$ premake5 gmake
|
||||
$ make
|
||||
```
|
||||
|
||||
## Build on Visual Studio
|
||||
|
||||
Plese use solution file located at `basic` folder.
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28010.2050
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "basic", "basic.vcxproj", "{0589AC44-0CF3-40D8-8D89-68393CFD40F3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0589AC44-0CF3-40D8-8D89-68393CFD40F3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0589AC44-0CF3-40D8-8D89-68393CFD40F3}.Debug|x64.Build.0 = Debug|x64
|
||||
{0589AC44-0CF3-40D8-8D89-68393CFD40F3}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{0589AC44-0CF3-40D8-8D89-68393CFD40F3}.Debug|x86.Build.0 = Debug|Win32
|
||||
{0589AC44-0CF3-40D8-8D89-68393CFD40F3}.Release|x64.ActiveCfg = Release|x64
|
||||
{0589AC44-0CF3-40D8-8D89-68393CFD40F3}.Release|x64.Build.0 = Release|x64
|
||||
{0589AC44-0CF3-40D8-8D89-68393CFD40F3}.Release|x86.ActiveCfg = Release|Win32
|
||||
{0589AC44-0CF3-40D8-8D89-68393CFD40F3}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {055E97C4-43DC-41B4-8D61-4FDDBC7B1EF7}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,159 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{0589AC44-0CF3-40D8-8D89-68393CFD40F3}</ProjectGuid>
|
||||
<RootNamespace>basic</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);opengl32.lib</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);opengl32.lib</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);opengl32.lib</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);opengl32.lib</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\main.cpp" />
|
||||
<ClCompile Include="..\shaders.cpp" />
|
||||
<ClCompile Include="..\window.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\shaders.h" />
|
||||
<ClInclude Include="..\window.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets" Condition="Exists('packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets')" />
|
||||
<Import Project="packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets" Condition="Exists('packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets')" />
|
||||
<Import Project="packages\glm.0.9.9.200\build\native\glm.targets" Condition="Exists('packages\glm.0.9.9.200\build\native\glm.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets'))" />
|
||||
<Error Condition="!Exists('packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets'))" />
|
||||
<Error Condition="!Exists('packages\glm.0.9.9.200\build\native\glm.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\glm.0.9.9.200\build\native\glm.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\window.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\shaders.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\window.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\shaders.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="glm" version="0.9.9.200" targetFramework="native" />
|
||||
<package id="nupengl.core" version="0.1.0.1" targetFramework="native" />
|
||||
<package id="nupengl.core.redist" version="0.1.0.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -1,338 +0,0 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "shaders.h"
|
||||
#include "window.h"
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define TINYGLTF_NOEXCEPTION
|
||||
#define JSON_NOEXCEPTION
|
||||
#include "../../tiny_gltf.h"
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
|
||||
bool loadModel(tinygltf::Model &model, const char *filename) {
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
bool res = loader.LoadASCIIFromFile(&model, &err, &warn, filename);
|
||||
if (!warn.empty()) {
|
||||
std::cout << "WARN: " << warn << std::endl;
|
||||
}
|
||||
|
||||
if (!err.empty()) {
|
||||
std::cout << "ERR: " << err << std::endl;
|
||||
}
|
||||
|
||||
if (!res)
|
||||
std::cout << "Failed to load glTF: " << filename << std::endl;
|
||||
else
|
||||
std::cout << "Loaded glTF: " << filename << std::endl;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::map<int, GLuint> bindMesh(std::map<int, GLuint> vbos,
|
||||
tinygltf::Model &model, tinygltf::Mesh &mesh) {
|
||||
for (size_t i = 0; i < model.bufferViews.size(); ++i) {
|
||||
const tinygltf::BufferView &bufferView = model.bufferViews[i];
|
||||
if (bufferView.target == 0) { // TODO impl drawarrays
|
||||
std::cout << "WARN: bufferView.target is zero" << std::endl;
|
||||
continue; // Unsupported bufferView.
|
||||
/*
|
||||
From spec2.0 readme:
|
||||
https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
|
||||
... drawArrays function should be used with a count equal to
|
||||
the count property of any of the accessors referenced by the attributes
|
||||
property (they are all equal for a given primitive).
|
||||
*/
|
||||
}
|
||||
|
||||
tinygltf::Buffer buffer = model.buffers[bufferView.buffer];
|
||||
std::cout << "bufferview.target " << bufferView.target << std::endl;
|
||||
|
||||
GLuint vbo;
|
||||
glGenBuffers(1, &vbo);
|
||||
vbos[i] = vbo;
|
||||
glBindBuffer(bufferView.target, vbo);
|
||||
|
||||
std::cout << "buffer.data.size = " << buffer.data.size()
|
||||
<< ", bufferview.byteOffset = " << bufferView.byteOffset
|
||||
<< std::endl;
|
||||
|
||||
glBufferData(bufferView.target, bufferView.byteLength,
|
||||
&buffer.data.at(0) + bufferView.byteOffset, GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mesh.primitives.size(); ++i) {
|
||||
tinygltf::Primitive primitive = mesh.primitives[i];
|
||||
tinygltf::Accessor indexAccessor = model.accessors[primitive.indices];
|
||||
|
||||
for (auto &attrib : primitive.attributes) {
|
||||
tinygltf::Accessor accessor = model.accessors[attrib.second];
|
||||
int byteStride =
|
||||
accessor.ByteStride(model.bufferViews[accessor.bufferView]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbos[accessor.bufferView]);
|
||||
|
||||
int size = 1;
|
||||
if (accessor.type != TINYGLTF_TYPE_SCALAR) {
|
||||
size = accessor.type;
|
||||
}
|
||||
|
||||
int vaa = -1;
|
||||
if (attrib.first.compare("POSITION") == 0) vaa = 0;
|
||||
if (attrib.first.compare("NORMAL") == 0) vaa = 1;
|
||||
if (attrib.first.compare("TEXCOORD_0") == 0) vaa = 2;
|
||||
if (vaa > -1) {
|
||||
glEnableVertexAttribArray(vaa);
|
||||
glVertexAttribPointer(vaa, size, accessor.componentType,
|
||||
accessor.normalized ? GL_TRUE : GL_FALSE,
|
||||
byteStride, BUFFER_OFFSET(accessor.byteOffset));
|
||||
} else
|
||||
std::cout << "vaa missing: " << attrib.first << std::endl;
|
||||
}
|
||||
|
||||
GLuint texid;
|
||||
glGenTextures(1, &texid);
|
||||
|
||||
tinygltf::Texture &tex = model.textures[0];
|
||||
tinygltf::Image &image = model.images[tex.source];
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texid);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width, image.height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, &image.image.at(0));
|
||||
}
|
||||
|
||||
return vbos;
|
||||
}
|
||||
|
||||
// bind models
|
||||
void bindModelNodes(std::map<int, GLuint> vbos, tinygltf::Model &model,
|
||||
tinygltf::Node &node) {
|
||||
bindMesh(vbos, model, model.meshes[node.mesh]);
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
bindModelNodes(vbos, model, model.nodes[node.children[i]]);
|
||||
}
|
||||
}
|
||||
GLuint bindModel(tinygltf::Model &model) {
|
||||
std::map<int, GLuint> vbos;
|
||||
GLuint vao;
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
|
||||
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
|
||||
for (size_t i = 0; i < scene.nodes.size(); ++i) {
|
||||
bindModelNodes(vbos, model, model.nodes[scene.nodes[i]]);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
// cleanup vbos
|
||||
for (size_t i = 0; i < vbos.size(); ++i) {
|
||||
glDeleteBuffers(1, &vbos[i]);
|
||||
}
|
||||
|
||||
return vao;
|
||||
}
|
||||
|
||||
void drawMesh(tinygltf::Model &model, tinygltf::Mesh &mesh) {
|
||||
for (size_t i = 0; i < mesh.primitives.size(); ++i) {
|
||||
tinygltf::Primitive primitive = mesh.primitives[i];
|
||||
tinygltf::Accessor indexAccessor = model.accessors[primitive.indices];
|
||||
|
||||
glDrawElements(primitive.mode, indexAccessor.count,
|
||||
indexAccessor.componentType,
|
||||
BUFFER_OFFSET(indexAccessor.byteOffset));
|
||||
}
|
||||
}
|
||||
|
||||
// recursively draw node and children nodes of model
|
||||
void drawModelNodes(tinygltf::Model &model, tinygltf::Node &node) {
|
||||
drawMesh(model, model.meshes[node.mesh]);
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
drawModelNodes(model, model.nodes[node.children[i]]);
|
||||
}
|
||||
}
|
||||
void drawModel(GLuint vao, tinygltf::Model &model) {
|
||||
glBindVertexArray(vao);
|
||||
|
||||
const tinygltf::Scene &scene = model.scenes[model.defaultScene];
|
||||
for (size_t i = 0; i < scene.nodes.size(); ++i) {
|
||||
drawModelNodes(model, model.nodes[scene.nodes[i]]);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void dbgModel(tinygltf::Model &model) {
|
||||
for (auto &mesh : model.meshes) {
|
||||
std::cout << "mesh : " << mesh.name << std::endl;
|
||||
for (auto &primitive : mesh.primitives) {
|
||||
const tinygltf::Accessor &indexAccessor =
|
||||
model.accessors[primitive.indices];
|
||||
|
||||
std::cout << "indexaccessor: count " << indexAccessor.count << ", type "
|
||||
<< indexAccessor.componentType << std::endl;
|
||||
|
||||
tinygltf::Material &mat = model.materials[primitive.material];
|
||||
for (auto &mats : mat.values) {
|
||||
std::cout << "mat : " << mats.first.c_str() << std::endl;
|
||||
}
|
||||
|
||||
for (auto &image : model.images) {
|
||||
std::cout << "image name : " << image.uri << std::endl;
|
||||
std::cout << " size : " << image.image.size() << std::endl;
|
||||
std::cout << " w/h : " << image.width << "/" << image.height
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
std::cout << "indices : " << primitive.indices << std::endl;
|
||||
std::cout << "mode : "
|
||||
<< "(" << primitive.mode << ")" << std::endl;
|
||||
|
||||
for (auto &attrib : primitive.attributes) {
|
||||
std::cout << "attribute : " << attrib.first.c_str() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 genView(glm::vec3 pos, glm::vec3 lookat) {
|
||||
// Camera matrix
|
||||
glm::mat4 view = glm::lookAt(
|
||||
pos, // Camera in World Space
|
||||
lookat, // and looks at the origin
|
||||
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
|
||||
);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
glm::mat4 genMVP(glm::mat4 view_mat, glm::mat4 model_mat, float fov, int w,
|
||||
int h) {
|
||||
glm::mat4 Projection =
|
||||
glm::perspective(glm::radians(fov), (float)w / (float)h, 0.01f, 1000.0f);
|
||||
|
||||
// Or, for an ortho camera :
|
||||
// glm::mat4 Projection = glm::ortho(-10.0f,10.0f,-10.0f,10.0f,0.0f,100.0f);
|
||||
// // In world coordinates
|
||||
|
||||
glm::mat4 mvp = Projection * view_mat * model_mat;
|
||||
|
||||
return mvp;
|
||||
}
|
||||
|
||||
void displayLoop(Window &window, const std::string &filename) {
|
||||
Shaders shader = Shaders();
|
||||
glUseProgram(shader.pid);
|
||||
|
||||
// grab uniforms to modify
|
||||
GLuint MVP_u = glGetUniformLocation(shader.pid, "MVP");
|
||||
GLuint sun_position_u = glGetUniformLocation(shader.pid, "sun_position");
|
||||
GLuint sun_color_u = glGetUniformLocation(shader.pid, "sun_color");
|
||||
|
||||
tinygltf::Model model;
|
||||
if (!loadModel(model, filename.c_str())) return;
|
||||
|
||||
GLuint vao = bindModel(model);
|
||||
// dbgModel(model); return;
|
||||
|
||||
// Model matrix : an identity matrix (model will be at the origin)
|
||||
glm::mat4 model_mat = glm::mat4(1.0f);
|
||||
glm::mat4 model_rot = glm::mat4(1.0f);
|
||||
glm::vec3 model_pos = glm::vec3(-3, 0, -3);
|
||||
|
||||
// generate a camera view, based on eye-position and lookAt world-position
|
||||
glm::mat4 view_mat = genView(glm::vec3(2, 2, 2), model_pos);
|
||||
|
||||
glm::vec3 sun_position = glm::vec3(3.0, 10.0, -5.0);
|
||||
glm::vec3 sun_color = glm::vec3(1.0);
|
||||
|
||||
while (!window.Close()) {
|
||||
window.Resize();
|
||||
|
||||
glClearColor(0.2, 0.2, 0.2, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glm::mat4 trans =
|
||||
glm::translate(glm::mat4(1.0f), model_pos); // reposition model
|
||||
model_rot = glm::rotate(model_rot, glm::radians(0.8f),
|
||||
glm::vec3(0, 1, 0)); // rotate model on y axis
|
||||
model_mat = trans * model_rot;
|
||||
|
||||
// build a model-view-projection
|
||||
GLint w, h;
|
||||
glfwGetWindowSize(window.window, &w, &h);
|
||||
glm::mat4 mvp = genMVP(view_mat, model_mat, 45.0f, w, h);
|
||||
glUniformMatrix4fv(MVP_u, 1, GL_FALSE, &mvp[0][0]);
|
||||
|
||||
glUniform3fv(sun_position_u, 1, &sun_position[0]);
|
||||
glUniform3fv(sun_color_u, 1, &sun_color[0]);
|
||||
|
||||
drawModel(vao, model);
|
||||
glfwSwapBuffers(window.window);
|
||||
glfwPollEvents();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::string filename = "../../models/Cube/Cube.gltf";
|
||||
|
||||
if (argc > 1) {
|
||||
filename = argv[1];
|
||||
}
|
||||
|
||||
if (!glfwInit()) return -1;
|
||||
|
||||
// Force create 3.3 profile.
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
|
||||
Window window = Window(800, 600, "TinyGLTF basic example");
|
||||
glfwMakeContextCurrent(window.window);
|
||||
|
||||
#ifdef __APPLE__
|
||||
// https://stackoverflow.com/questions/50192625/openggl-segmentation-fault
|
||||
glewExperimental = GL_TRUE;
|
||||
#endif
|
||||
|
||||
if (glewInit() != GLEW_OK) {
|
||||
std::cerr << "glew init error." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << glGetString(GL_RENDERER) << ", " << glGetString(GL_VERSION)
|
||||
<< std::endl;
|
||||
|
||||
if (!GLEW_VERSION_3_3) {
|
||||
std::cerr << "OpenGL 3.3 is required to execute this app." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
displayLoop(window, filename);
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
solution "basic_viewer"
|
||||
-- location ( "build" )
|
||||
configurations { "Debug", "Release" }
|
||||
platforms {"native", "x64", "x32"}
|
||||
|
||||
project "basic_viewer"
|
||||
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
cppdialect "C++11"
|
||||
files { "main.cpp", "shaders.cpp", "window.cpp" }
|
||||
includedirs { "./" }
|
||||
includedirs { "../../" }
|
||||
includedirs { "../common/glm" }
|
||||
|
||||
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 { "glfw", "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"
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,114 +0,0 @@
|
||||
#include "shaders.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
std::string FragmentShaderCode =
|
||||
"#version 330 core\n\
|
||||
in vec3 normal;\n\
|
||||
in vec3 position;\n\
|
||||
in vec2 texcoord;\n\
|
||||
\n\
|
||||
uniform sampler2D tex;\n\
|
||||
uniform vec3 sun_position; \n\
|
||||
uniform vec3 sun_color; \n\
|
||||
\n\
|
||||
out vec4 color;\n\
|
||||
void main() {\n\
|
||||
float lum = max(dot(normal, normalize(sun_position)), 0.0);\n\
|
||||
color = texture(tex, texcoord) * vec4((0.3 + 0.7 * lum) * sun_color, 1.0);\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
std::string VertexShaderCode =
|
||||
"#version 330 core\n\
|
||||
layout(location = 0) in vec3 in_vertex;\n\
|
||||
layout(location = 1) in vec3 in_normal;\n\
|
||||
layout(location = 2) in vec2 in_texcoord;\n\
|
||||
\n\
|
||||
uniform mat4 MVP;\n\
|
||||
\n\
|
||||
out vec3 normal;\n\
|
||||
out vec3 position;\n\
|
||||
out vec2 texcoord;\n\
|
||||
\n\
|
||||
void main(){\n\
|
||||
gl_Position = MVP * vec4(in_vertex, 1);\n\
|
||||
position = gl_Position.xyz;\n\
|
||||
normal = normalize(mat3(MVP) * in_normal);\n\
|
||||
position = in_vertex;\n\
|
||||
texcoord = in_texcoord;\n\
|
||||
}";
|
||||
|
||||
|
||||
Shaders::Shaders()
|
||||
{
|
||||
|
||||
// Create the shaders
|
||||
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
|
||||
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
|
||||
GLint Result = GL_FALSE;
|
||||
int InfoLogLength;
|
||||
|
||||
// Compile Vertex Shader
|
||||
char const * VertexSourcePointer = VertexShaderCode.c_str();
|
||||
glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL);
|
||||
glCompileShader(VertexShaderID);
|
||||
|
||||
// Check Vertex Shader
|
||||
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
|
||||
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
|
||||
if (InfoLogLength > 0) {
|
||||
std::vector<char> VertexShaderErrorMessage(InfoLogLength + 1);
|
||||
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
|
||||
printf("%s\n", &VertexShaderErrorMessage[0]);
|
||||
}
|
||||
|
||||
// Compile Fragment Shader
|
||||
char const * FragmentSourcePointer = FragmentShaderCode.c_str();
|
||||
glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL);
|
||||
glCompileShader(FragmentShaderID);
|
||||
|
||||
// Check Fragment Shader
|
||||
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
|
||||
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
|
||||
if (InfoLogLength > 0) {
|
||||
std::vector<char> FragmentShaderErrorMessage(InfoLogLength + 1);
|
||||
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
|
||||
printf("%s\n", &FragmentShaderErrorMessage[0]);
|
||||
}
|
||||
|
||||
// Link the program
|
||||
printf("Linking program\n");
|
||||
GLuint ProgramID = glCreateProgram();
|
||||
glAttachShader(ProgramID, VertexShaderID);
|
||||
glAttachShader(ProgramID, FragmentShaderID);
|
||||
glLinkProgram(ProgramID);
|
||||
|
||||
// Check the program
|
||||
glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
|
||||
glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
|
||||
if (InfoLogLength > 0) {
|
||||
std::vector<char> ProgramErrorMessage(InfoLogLength + 1);
|
||||
glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
|
||||
printf("%s\n", &ProgramErrorMessage[0]);
|
||||
}
|
||||
|
||||
glDetachShader(ProgramID, VertexShaderID);
|
||||
glDetachShader(ProgramID, FragmentShaderID);
|
||||
|
||||
glDeleteShader(VertexShaderID);
|
||||
glDeleteShader(FragmentShaderID);
|
||||
|
||||
this->pid = ProgramID;
|
||||
}
|
||||
|
||||
|
||||
Shaders::~Shaders()
|
||||
{
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
class Shaders
|
||||
{
|
||||
public:
|
||||
GLuint pid;
|
||||
Shaders();
|
||||
~Shaders();
|
||||
};
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#include "window.h"
|
||||
|
||||
|
||||
|
||||
Window::Window(int x, int y, const char* title)
|
||||
{
|
||||
GLFWwindow* window = glfwCreateWindow(x, y, title, NULL, NULL);
|
||||
this->window = window;
|
||||
}
|
||||
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
glfwDestroyWindow(this->window);
|
||||
}
|
||||
|
||||
void Window::Resize()
|
||||
{
|
||||
GLint w, h;
|
||||
glfwGetWindowSize(this->window, &w, &h);
|
||||
glViewport(0, 0, w, h);
|
||||
}
|
||||
|
||||
int Window::Close()
|
||||
{
|
||||
return glfwWindowShouldClose(this->window);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
class Window
|
||||
{
|
||||
public:
|
||||
GLFWwindow* window;
|
||||
|
||||
Window(int x, int y, const char* title);
|
||||
~Window();
|
||||
void Resize();
|
||||
int Close();
|
||||
};
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include "imgui_internal.h"
|
||||
#include "ImGuizmo.h"
|
||||
|
||||
@@ -67,8 +65,6 @@ namespace ImGuizmo
|
||||
//template <typename T> T LERP(T x, T y, float z) { return (x + (y - x)*z); }
|
||||
template <typename T> T Clamp(T x, T y, T z) { return ((x<y) ? y : ((x>z) ? z : x)); }
|
||||
template <typename T> T max(T x, T y) { return (x > y) ? x : y; }
|
||||
template <typename T> T min(T x, T y) { return (x < y) ? x : y; }
|
||||
template <typename T> bool IsWithin(T x, T y, T z) { return (x>=y) && (x<=z); }
|
||||
|
||||
struct matrix_t;
|
||||
struct vec_t
|
||||
@@ -90,7 +86,7 @@ namespace ImGuizmo
|
||||
vec_t& operator -= (const vec_t& v) { x -= v.x; y -= v.y; z -= v.z; w -= v.w; return *this; }
|
||||
vec_t& operator += (const vec_t& v) { x += v.x; y += v.y; z += v.z; w += v.w; return *this; }
|
||||
vec_t& operator *= (const vec_t& v) { x *= v.x; y *= v.y; z *= v.z; w *= v.w; return *this; }
|
||||
vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; }
|
||||
vec_t& operator *= (float v) { x *= v; y *= v; z *= v; w *= v; return *this; }
|
||||
|
||||
vec_t operator * (float f) const;
|
||||
vec_t operator - () const;
|
||||
@@ -103,7 +99,7 @@ namespace ImGuizmo
|
||||
float LengthSq() const { return (x*x + y*y + z*z); };
|
||||
vec_t Normalize() { (*this) *= (1.f / Length()); return (*this); }
|
||||
vec_t Normalize(const vec_t& v) { this->Set(v.x, v.y, v.z, v.w); this->Normalize(); return (*this); }
|
||||
vec_t Abs() const;
|
||||
vec_t Abs() const;
|
||||
void Cross(const vec_t& v)
|
||||
{
|
||||
vec_t res;
|
||||
@@ -151,6 +147,7 @@ namespace ImGuizmo
|
||||
vec_t vec_t::operator + (const vec_t& v) const { return makeVect(x + v.x, y + v.y, z + v.z, w + v.w); }
|
||||
vec_t vec_t::operator * (const vec_t& v) const { return makeVect(x * v.x, y * v.y, z * v.z, w * v.w); }
|
||||
vec_t vec_t::Abs() const { return makeVect(fabsf(x), fabsf(y), fabsf(z)); }
|
||||
ImVec2 operator+ (const ImVec2& a, const ImVec2& b) { return ImVec2(a.x + b.x, a.y + b.y); }
|
||||
|
||||
vec_t Normalized(const vec_t& v) { vec_t res; res = v; res.Normalize(); return res; }
|
||||
vec_t Cross(const vec_t& v1, const vec_t& v2)
|
||||
@@ -191,7 +188,7 @@ namespace ImGuizmo
|
||||
{
|
||||
vec_t right, up, dir, position;
|
||||
} v;
|
||||
vec_t component[4];
|
||||
vec_t component[4];
|
||||
};
|
||||
|
||||
matrix_t(const matrix_t& other) { memcpy(&m16[0], &other.m16[0], sizeof(float) * 16); }
|
||||
@@ -494,7 +491,7 @@ namespace ImGuizmo
|
||||
SCALE_Y,
|
||||
SCALE_Z,
|
||||
SCALE_XYZ,
|
||||
BOUNDS
|
||||
BOUNDS
|
||||
};
|
||||
|
||||
struct Context
|
||||
@@ -523,7 +520,6 @@ namespace ImGuizmo
|
||||
vec_t mRayOrigin;
|
||||
vec_t mRayVector;
|
||||
|
||||
float mRadiusSquareCenter;
|
||||
ImVec2 mScreenSquareCenter;
|
||||
ImVec2 mScreenSquareMin;
|
||||
ImVec2 mScreenSquareMax;
|
||||
@@ -555,25 +551,23 @@ namespace ImGuizmo
|
||||
bool mBelowPlaneLimit[3];
|
||||
float mAxisFactor[3];
|
||||
|
||||
// bounds stretching
|
||||
vec_t mBoundsPivot;
|
||||
vec_t mBoundsAnchor;
|
||||
vec_t mBoundsPlan;
|
||||
vec_t mBoundsLocalPivot;
|
||||
int mBoundsBestAxis;
|
||||
int mBoundsAxis[2];
|
||||
bool mbUsingBounds;
|
||||
matrix_t mBoundsMatrix;
|
||||
// bounds stretching
|
||||
vec_t mBoundsPivot;
|
||||
vec_t mBoundsAnchor;
|
||||
vec_t mBoundsPlan;
|
||||
vec_t mBoundsLocalPivot;
|
||||
int mBoundsBestAxis;
|
||||
int mBoundsAxis[2];
|
||||
bool mbUsingBounds;
|
||||
matrix_t mBoundsMatrix;
|
||||
|
||||
//
|
||||
int mCurrentOperation;
|
||||
|
||||
float mX = 0.f;
|
||||
float mY = 0.f;
|
||||
float mWidth = 0.f;
|
||||
float mHeight = 0.f;
|
||||
float mXMax = 0.f;
|
||||
float mYMax = 0.f;
|
||||
float mX = 0.f;
|
||||
float mY = 0.f;
|
||||
float mWidth = 0.f;
|
||||
float mHeight = 0.f;
|
||||
};
|
||||
|
||||
static Context gContext;
|
||||
@@ -583,11 +577,7 @@ namespace ImGuizmo
|
||||
|
||||
static const vec_t directionUnary[3] = { makeVect(1.f, 0.f, 0.f), makeVect(0.f, 1.f, 0.f), makeVect(0.f, 0.f, 1.f) };
|
||||
static const ImU32 directionColor[3] = { 0xFF0000AA, 0xFF00AA00, 0xFFAA0000 };
|
||||
|
||||
// Alpha: 100%: FF, 87%: DE, 70%: B3, 54%: 8A, 50%: 80, 38%: 61, 12%: 1F
|
||||
static const ImU32 planeBorderColor[3] = { 0xFFAA0000, 0xFF0000AA, 0xFF00AA00 };
|
||||
static const ImU32 planeColor[3] = { 0x610000AA, 0x6100AA00, 0x61AA0000 };
|
||||
static const ImU32 selectionColor = 0x8A1080FF;
|
||||
static const ImU32 selectionColor = 0xFF1080FF;
|
||||
static const ImU32 inactiveColor = 0x99999999;
|
||||
static const ImU32 translationLineColor = 0xAAAAAAAA;
|
||||
static const char *translationInfoMask[] = { "X : %5.3f", "Y : %5.3f", "Z : %5.3f", "X : %5.3f Y : %5.3f", "Y : %5.3f Z : %5.3f", "X : %5.3f Z : %5.3f", "X : %5.3f Y : %5.3f Z : %5.3f" };
|
||||
@@ -608,16 +598,18 @@ namespace ImGuizmo
|
||||
|
||||
static ImVec2 worldToPos(const vec_t& worldPos, const matrix_t& mat)
|
||||
{
|
||||
//ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
vec_t trans;
|
||||
trans.TransformPoint(worldPos, mat);
|
||||
trans *= 0.5f / trans.w;
|
||||
trans += makeVect(0.5f, 0.5f);
|
||||
trans.y = 1.f - trans.y;
|
||||
trans.x *= gContext.mWidth;
|
||||
trans.y *= gContext.mHeight;
|
||||
trans.x += gContext.mX;
|
||||
trans.y += gContext.mY;
|
||||
return ImVec2(trans.x, trans.y);
|
||||
trans.x *= gContext.mWidth;
|
||||
trans.y *= gContext.mHeight;
|
||||
trans.x += gContext.mX;
|
||||
trans.y += gContext.mY;
|
||||
return ImVec2(trans.x, trans.y);
|
||||
}
|
||||
|
||||
static void ComputeCameraRay(vec_t &rayOrigin, vec_t &rayDir)
|
||||
@@ -627,9 +619,9 @@ namespace ImGuizmo
|
||||
matrix_t mViewProjInverse;
|
||||
mViewProjInverse.Inverse(gContext.mViewMat * gContext.mProjectionMat);
|
||||
|
||||
float mox = ((io.MousePos.x - gContext.mX) / gContext.mWidth) * 2.f - 1.f;
|
||||
float moy = (1.f - ((io.MousePos.y - gContext.mY) / gContext.mHeight)) * 2.f - 1.f;
|
||||
|
||||
float mox = ((io.MousePos.x - gContext.mX) / gContext.mWidth) * 2.f - 1.f;
|
||||
float moy = (1.f - ((io.MousePos.y - gContext.mY) / gContext.mHeight)) * 2.f - 1.f;
|
||||
|
||||
rayOrigin.Transform(makeVect(mox, moy, 0.f, 1.f), mViewProjInverse);
|
||||
rayOrigin *= 1.f / rayOrigin.w;
|
||||
vec_t rayEnd;
|
||||
@@ -649,38 +641,23 @@ namespace ImGuizmo
|
||||
return -(numer / denom);
|
||||
}
|
||||
|
||||
static bool IsInContextRect( ImVec2 p )
|
||||
{
|
||||
return IsWithin( p.x, gContext.mX, gContext.mXMax ) && IsWithin(p.y, gContext.mY, gContext.mYMax );
|
||||
}
|
||||
|
||||
void SetRect(float x, float y, float width, float height)
|
||||
{
|
||||
gContext.mX = x;
|
||||
gContext.mY = y;
|
||||
gContext.mWidth = width;
|
||||
gContext.mHeight = height;
|
||||
gContext.mXMax = gContext.mX + gContext.mWidth;
|
||||
gContext.mYMax = gContext.mY + gContext.mXMax;
|
||||
}
|
||||
|
||||
void SetDrawlist()
|
||||
{
|
||||
gContext.mDrawList = ImGui::GetWindowDrawList();
|
||||
gContext.mX = x;
|
||||
gContext.mY = y;
|
||||
gContext.mWidth = width;
|
||||
gContext.mHeight = height;
|
||||
}
|
||||
|
||||
void BeginFrame()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
const ImU32 flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
|
||||
ImGui::SetNextWindowSize(io.DisplaySize);
|
||||
ImGui::Begin("gizmo", NULL, io.DisplaySize, 0, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
|
||||
gContext.mDrawList = ImGui::GetWindowDrawList();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, 0);
|
||||
ImGui::Begin("gizmo", NULL, flags);
|
||||
gContext.mDrawList = ImGui::GetWindowDrawList();
|
||||
ImGui::End();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
bool IsUsing()
|
||||
@@ -696,11 +673,11 @@ namespace ImGuizmo
|
||||
void Enable(bool enable)
|
||||
{
|
||||
gContext.mbEnable = enable;
|
||||
if (!enable)
|
||||
{
|
||||
gContext.mbUsing = false;
|
||||
gContext.mbUsingBounds = false;
|
||||
}
|
||||
if (!enable)
|
||||
{
|
||||
gContext.mbUsing = false;
|
||||
gContext.mbUsingBounds = false;
|
||||
}
|
||||
}
|
||||
|
||||
static float GetUniform(const vec_t& position, const matrix_t& mat)
|
||||
@@ -761,8 +738,7 @@ namespace ImGuizmo
|
||||
{
|
||||
int colorPlaneIndex = (i + 2) % 3;
|
||||
colors[i + 1] = (type == (int)(MOVE_X + i)) ? selectionColor : directionColor[i];
|
||||
colors[i + 4] = (type == (int)(MOVE_XY + i)) ? selectionColor : planeColor[colorPlaneIndex];
|
||||
colors[i + 4] = (type == MOVE_SCREEN) ? selectionColor : colors[i + 4];
|
||||
colors[i + 4] = (type == (int)(MOVE_XY + i)) ? selectionColor : directionColor[colorPlaneIndex];
|
||||
}
|
||||
break;
|
||||
case ROTATE:
|
||||
@@ -775,6 +751,8 @@ namespace ImGuizmo
|
||||
for (int i = 0; i < 3; i++)
|
||||
colors[i + 1] = (type == (int)(SCALE_X + i)) ? selectionColor : directionColor[i];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -870,6 +848,7 @@ namespace ImGuizmo
|
||||
static void DrawRotationGizmo(int type)
|
||||
{
|
||||
ImDrawList* drawList = gContext.mDrawList;
|
||||
//ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// colors
|
||||
ImU32 colors[7];
|
||||
@@ -878,7 +857,6 @@ namespace ImGuizmo
|
||||
vec_t cameraToModelNormalized = Normalized(gContext.mModel.v.position - gContext.mCameraEye);
|
||||
cameraToModelNormalized.TransformVector(gContext.mModelInverse);
|
||||
|
||||
gContext.mRadiusSquareCenter = screenRotateSize * gContext.mHeight;
|
||||
for (int axis = 0; axis < 3; axis++)
|
||||
{
|
||||
ImVec2 circlePos[halfCircleSegmentCount];
|
||||
@@ -892,14 +870,9 @@ namespace ImGuizmo
|
||||
vec_t pos = makeVect(axisPos[axis], axisPos[(axis+1)%3], axisPos[(axis+2)%3]) * gContext.mScreenFactor;
|
||||
circlePos[i] = worldToPos(pos, gContext.mMVP);
|
||||
}
|
||||
|
||||
float radiusAxis = sqrtf( (ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0]) ));
|
||||
if(radiusAxis > gContext.mRadiusSquareCenter)
|
||||
gContext.mRadiusSquareCenter = radiusAxis;
|
||||
|
||||
drawList->AddPolyline(circlePos, halfCircleSegmentCount, colors[3 - axis], false, 2);
|
||||
drawList->AddPolyline(circlePos, halfCircleSegmentCount, colors[3 - axis], false, 2, true);
|
||||
}
|
||||
drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), gContext.mRadiusSquareCenter, colors[0], 64, 3.f);
|
||||
drawList->AddCircle(worldToPos(gContext.mModel.v.position, gContext.mViewProjection), screenRotateSize * gContext.mHeight, colors[0], 64);
|
||||
|
||||
if (gContext.mbUsing)
|
||||
{
|
||||
@@ -916,8 +889,8 @@ namespace ImGuizmo
|
||||
pos *= gContext.mScreenFactor;
|
||||
circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection);
|
||||
}
|
||||
drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, 0x801080FF);
|
||||
drawList->AddPolyline(circlePos, halfCircleSegmentCount, 0xFF1080FF, true, 2);
|
||||
drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount, 0x801080FF, true);
|
||||
drawList->AddPolyline(circlePos, halfCircleSegmentCount, 0xFF1080FF, true, 2, true);
|
||||
|
||||
ImVec2 destinationPosOnScreen = circlePos[1];
|
||||
char tmps[512];
|
||||
@@ -945,6 +918,9 @@ namespace ImGuizmo
|
||||
ImU32 colors[7];
|
||||
ComputeColors(colors, type, SCALE);
|
||||
|
||||
// draw screen cirle
|
||||
drawList->AddCircleFilled(gContext.mScreenSquareCenter, 12.f, colors[0], 32);
|
||||
|
||||
// draw
|
||||
vec_t scaleDisplay = { 1.f, 1.f, 1.f, 1.f };
|
||||
|
||||
@@ -966,20 +942,17 @@ namespace ImGuizmo
|
||||
|
||||
if (gContext.mbUsing)
|
||||
{
|
||||
drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, 0xFF404040, 3.f);
|
||||
drawList->AddCircleFilled(worldDirSSpaceNoScale, 6.f, 0xFF404040);
|
||||
drawList->AddLine(baseSSpace, worldDirSSpaceNoScale, 0xFF404040, 6.f);
|
||||
drawList->AddCircleFilled(worldDirSSpaceNoScale, 10.f, 0xFF404040);
|
||||
}
|
||||
|
||||
drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
|
||||
drawList->AddCircleFilled(worldDirSSpace, 6.f, colors[i + 1]);
|
||||
drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 6.f);
|
||||
drawList->AddCircleFilled(worldDirSSpace, 10.f, colors[i + 1]);
|
||||
|
||||
if (gContext.mAxisFactor[i] < 0.f)
|
||||
DrawHatchedAxis(dirPlaneX * scaleDisplay[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// draw screen cirle
|
||||
drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
|
||||
|
||||
if (gContext.mbUsing)
|
||||
{
|
||||
@@ -1005,21 +978,21 @@ namespace ImGuizmo
|
||||
static void DrawTranslationGizmo(int type)
|
||||
{
|
||||
ImDrawList* drawList = gContext.mDrawList;
|
||||
if (!drawList)
|
||||
return;
|
||||
if (!drawList)
|
||||
return;
|
||||
|
||||
// colors
|
||||
ImU32 colors[7];
|
||||
ComputeColors(colors, type, TRANSLATE);
|
||||
|
||||
const ImVec2 origin = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
|
||||
|
||||
// draw screen quad
|
||||
drawList->AddRectFilled(gContext.mScreenSquareMin, gContext.mScreenSquareMax, colors[0], 2.f);
|
||||
|
||||
// draw
|
||||
bool belowAxisLimit = false;
|
||||
bool belowPlaneLimit = false;
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
for (unsigned int i = 0; i < 3; i++)
|
||||
{
|
||||
vec_t dirPlaneX, dirPlaneY;
|
||||
bool belowAxisLimit, belowPlaneLimit;
|
||||
ComputeTripodAxisAndVisibility(i, dirPlaneX, dirPlaneY, belowAxisLimit, belowPlaneLimit);
|
||||
|
||||
// draw axis
|
||||
@@ -1028,20 +1001,8 @@ namespace ImGuizmo
|
||||
ImVec2 baseSSpace = worldToPos(dirPlaneX * 0.1f * gContext.mScreenFactor, gContext.mMVP);
|
||||
ImVec2 worldDirSSpace = worldToPos(dirPlaneX * gContext.mScreenFactor, gContext.mMVP);
|
||||
|
||||
drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 3.f);
|
||||
|
||||
// Arrow head begin
|
||||
ImVec2 dir(origin - worldDirSSpace);
|
||||
|
||||
float d = sqrtf(ImLengthSqr(dir));
|
||||
dir /= d; // Normalize
|
||||
dir *= 6.0f;
|
||||
|
||||
ImVec2 ortogonalDir(dir.y, -dir.x); // Perpendicular vector
|
||||
ImVec2 a(worldDirSSpace + dir);
|
||||
drawList->AddTriangleFilled(worldDirSSpace - dir, a + ortogonalDir, a - ortogonalDir, colors[i + 1]);
|
||||
// Arrow head end
|
||||
|
||||
drawList->AddLine(baseSSpace, worldDirSSpace, colors[i + 1], 6.f);
|
||||
|
||||
if (gContext.mAxisFactor[i] < 0.f)
|
||||
DrawHatchedAxis(dirPlaneX);
|
||||
}
|
||||
@@ -1050,18 +1011,15 @@ namespace ImGuizmo
|
||||
if (belowPlaneLimit)
|
||||
{
|
||||
ImVec2 screenQuadPts[4];
|
||||
for (int j = 0; j < 4; ++j)
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor;
|
||||
screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP);
|
||||
}
|
||||
drawList->AddPolyline(screenQuadPts, 4, planeBorderColor[i], true, 1.0f);
|
||||
drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]);
|
||||
drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4], true);
|
||||
}
|
||||
}
|
||||
|
||||
drawList->AddCircleFilled(gContext.mScreenSquareCenter, 6.f, colors[0], 32);
|
||||
|
||||
if (gContext.mbUsing)
|
||||
{
|
||||
ImVec2 sourcePosOnScreen = worldToPos(gContext.mMatrixOrigin, gContext.mViewProjection);
|
||||
@@ -1082,233 +1040,176 @@ namespace ImGuizmo
|
||||
}
|
||||
}
|
||||
|
||||
static bool CanActivate()
|
||||
{
|
||||
if (ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemActive())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void HandleAndDrawLocalBounds(float *bounds, matrix_t *matrix, float *snapValues)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImDrawList* drawList = gContext.mDrawList;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImDrawList* drawList = gContext.mDrawList;
|
||||
|
||||
// compute best projection axis
|
||||
vec_t axesWorldDirections[3];
|
||||
vec_t bestAxisWorldDirection = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
int axes[3];
|
||||
unsigned int numAxes = 1;
|
||||
axes[0] = gContext.mBoundsBestAxis;
|
||||
int bestAxis = axes[0];
|
||||
if (!gContext.mbUsingBounds)
|
||||
{
|
||||
numAxes = 0;
|
||||
float bestDot = 0.f;
|
||||
for (unsigned int i = 0; i < 3; i++)
|
||||
{
|
||||
vec_t dirPlaneNormalWorld;
|
||||
dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource);
|
||||
dirPlaneNormalWorld.Normalize();
|
||||
// compute best projection axis
|
||||
vec_t bestAxisWorldDirection;
|
||||
int bestAxis = gContext.mBoundsBestAxis;
|
||||
if (!gContext.mbUsingBounds)
|
||||
{
|
||||
|
||||
float bestDot = 0.f;
|
||||
for (unsigned int i = 0; i < 3; i++)
|
||||
{
|
||||
vec_t dirPlaneNormalWorld;
|
||||
dirPlaneNormalWorld.TransformVector(directionUnary[i], gContext.mModelSource);
|
||||
dirPlaneNormalWorld.Normalize();
|
||||
|
||||
float dt = fabsf( Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld) );
|
||||
if ( dt >= bestDot )
|
||||
{
|
||||
bestDot = dt;
|
||||
bestAxis = i;
|
||||
bestAxisWorldDirection = dirPlaneNormalWorld;
|
||||
}
|
||||
float dt = Dot(Normalized(gContext.mCameraEye - gContext.mModelSource.v.position), dirPlaneNormalWorld);
|
||||
if (fabsf(dt) >= bestDot)
|
||||
{
|
||||
bestDot = fabsf(dt);
|
||||
bestAxis = i;
|
||||
bestAxisWorldDirection = dirPlaneNormalWorld;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( dt >= 0.1f )
|
||||
{
|
||||
axes[numAxes] = i;
|
||||
axesWorldDirections[numAxes] = dirPlaneNormalWorld;
|
||||
++numAxes;
|
||||
}
|
||||
}
|
||||
}
|
||||
// corners
|
||||
vec_t aabb[4];
|
||||
|
||||
if( numAxes == 0 )
|
||||
{
|
||||
axes[0] = bestAxis;
|
||||
axesWorldDirections[0] = bestAxisWorldDirection;
|
||||
numAxes = 1;
|
||||
}
|
||||
else if( bestAxis != axes[0] )
|
||||
{
|
||||
unsigned int bestIndex = 0;
|
||||
for (unsigned int i = 0; i < numAxes; i++)
|
||||
{
|
||||
if( axes[i] == bestAxis )
|
||||
{
|
||||
bestIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int tempAxis = axes[0];
|
||||
axes[0] = axes[bestIndex];
|
||||
axes[bestIndex] = tempAxis;
|
||||
vec_t tempDirection = axesWorldDirections[0];
|
||||
axesWorldDirections[0] = axesWorldDirections[bestIndex];
|
||||
axesWorldDirections[bestIndex] = tempDirection;
|
||||
}
|
||||
int secondAxis = (bestAxis + 1) % 3;
|
||||
int thirdAxis = (bestAxis + 2) % 3;
|
||||
|
||||
for (unsigned int axisIndex = 0; axisIndex < numAxes; ++axisIndex)
|
||||
{
|
||||
bestAxis = axes[axisIndex];
|
||||
bestAxisWorldDirection = axesWorldDirections[axisIndex];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
aabb[i][3] = aabb[i][bestAxis] = 0.f;
|
||||
aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)];
|
||||
aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))];
|
||||
}
|
||||
|
||||
// corners
|
||||
vec_t aabb[4];
|
||||
// draw bounds
|
||||
unsigned int anchorAlpha = gContext.mbEnable ? 0xFF000000 : 0x80000000;
|
||||
|
||||
int secondAxis = (bestAxis + 1) % 3;
|
||||
int thirdAxis = (bestAxis + 2) % 3;
|
||||
matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection;
|
||||
for (int i = 0; i < 4;i++)
|
||||
{
|
||||
ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP);
|
||||
ImVec2 worldBound2 = worldToPos(aabb[(i+1)%4], boundsMVP);
|
||||
float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2));
|
||||
int stepCount = (int)(boundDistance / 10.f);
|
||||
float stepLength = 1.f / (float)stepCount;
|
||||
for (int j = 0; j < stepCount; j++)
|
||||
{
|
||||
float t1 = (float)j * stepLength;
|
||||
float t2 = (float)j * stepLength + stepLength * 0.5f;
|
||||
ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1));
|
||||
ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2));
|
||||
drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 3.f);
|
||||
}
|
||||
vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f;
|
||||
ImVec2 midBound = worldToPos(midPoint, boundsMVP);
|
||||
static const float AnchorBigRadius = 8.f;
|
||||
static const float AnchorSmallRadius = 6.f;
|
||||
bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
|
||||
bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
aabb[i][3] = aabb[i][bestAxis] = 0.f;
|
||||
aabb[i][secondAxis] = bounds[secondAxis + 3 * (i >> 1)];
|
||||
aabb[i][thirdAxis] = bounds[thirdAxis + 3 * ((i >> 1) ^ (i & 1))];
|
||||
}
|
||||
|
||||
unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
|
||||
unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
|
||||
|
||||
drawList->AddCircleFilled(worldBound1, AnchorBigRadius, bigAnchorColor);
|
||||
drawList->AddCircleFilled(midBound, AnchorSmallRadius, smallAnchorColor);
|
||||
int oppositeIndex = (i + 2) % 4;
|
||||
// big anchor on corners
|
||||
if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && io.MouseDown[0])
|
||||
{
|
||||
gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource);
|
||||
gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource);
|
||||
gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
|
||||
gContext.mBoundsBestAxis = bestAxis;
|
||||
gContext.mBoundsAxis[0] = secondAxis;
|
||||
gContext.mBoundsAxis[1] = thirdAxis;
|
||||
|
||||
// draw bounds
|
||||
unsigned int anchorAlpha = gContext.mbEnable ? 0xFF000000 : 0x80000000;
|
||||
gContext.mBoundsLocalPivot.Set(0.f);
|
||||
gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis];
|
||||
gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis];
|
||||
|
||||
matrix_t boundsMVP = gContext.mModelSource * gContext.mViewProjection;
|
||||
for (int i = 0; i < 4;i++)
|
||||
{
|
||||
ImVec2 worldBound1 = worldToPos(aabb[i], boundsMVP);
|
||||
ImVec2 worldBound2 = worldToPos(aabb[(i+1)%4], boundsMVP);
|
||||
if( !IsInContextRect( worldBound1 ) || !IsInContextRect( worldBound2 ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
float boundDistance = sqrtf(ImLengthSqr(worldBound1 - worldBound2));
|
||||
int stepCount = (int)(boundDistance / 10.f);
|
||||
stepCount = min( stepCount, 1000 );
|
||||
float stepLength = 1.f / (float)stepCount;
|
||||
for (int j = 0; j < stepCount; j++)
|
||||
{
|
||||
float t1 = (float)j * stepLength;
|
||||
float t2 = (float)j * stepLength + stepLength * 0.5f;
|
||||
ImVec2 worldBoundSS1 = ImLerp(worldBound1, worldBound2, ImVec2(t1, t1));
|
||||
ImVec2 worldBoundSS2 = ImLerp(worldBound1, worldBound2, ImVec2(t2, t2));
|
||||
drawList->AddLine(worldBoundSS1, worldBoundSS2, 0xAAAAAA + anchorAlpha, 3.f);
|
||||
}
|
||||
vec_t midPoint = (aabb[i] + aabb[(i + 1) % 4] ) * 0.5f;
|
||||
ImVec2 midBound = worldToPos(midPoint, boundsMVP);
|
||||
static const float AnchorBigRadius = 8.f;
|
||||
static const float AnchorSmallRadius = 6.f;
|
||||
bool overBigAnchor = ImLengthSqr(worldBound1 - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
|
||||
bool overSmallAnchor = ImLengthSqr(midBound - io.MousePos) <= (AnchorBigRadius*AnchorBigRadius);
|
||||
gContext.mbUsingBounds = true;
|
||||
gContext.mBoundsMatrix = gContext.mModelSource;
|
||||
}
|
||||
// small anchor on middle of segment
|
||||
if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && io.MouseDown[0])
|
||||
{
|
||||
vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f;
|
||||
gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource);
|
||||
gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource);
|
||||
gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
|
||||
gContext.mBoundsBestAxis = bestAxis;
|
||||
int indices[] = { secondAxis , thirdAxis };
|
||||
gContext.mBoundsAxis[0] = indices[i%2];
|
||||
gContext.mBoundsAxis[1] = -1;
|
||||
|
||||
|
||||
unsigned int bigAnchorColor = overBigAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
|
||||
unsigned int smallAnchorColor = overSmallAnchor ? selectionColor : (0xAAAAAA + anchorAlpha);
|
||||
|
||||
drawList->AddCircleFilled(worldBound1, AnchorBigRadius, bigAnchorColor);
|
||||
drawList->AddCircleFilled(midBound, AnchorSmallRadius, smallAnchorColor);
|
||||
int oppositeIndex = (i + 2) % 4;
|
||||
// big anchor on corners
|
||||
if (!gContext.mbUsingBounds && gContext.mbEnable && overBigAnchor && CanActivate())
|
||||
{
|
||||
gContext.mBoundsPivot.TransformPoint(aabb[(i + 2) % 4], gContext.mModelSource);
|
||||
gContext.mBoundsAnchor.TransformPoint(aabb[i], gContext.mModelSource);
|
||||
gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
|
||||
gContext.mBoundsBestAxis = bestAxis;
|
||||
gContext.mBoundsAxis[0] = secondAxis;
|
||||
gContext.mBoundsAxis[1] = thirdAxis;
|
||||
gContext.mBoundsLocalPivot.Set(0.f);
|
||||
gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f);
|
||||
|
||||
gContext.mBoundsLocalPivot.Set(0.f);
|
||||
gContext.mBoundsLocalPivot[secondAxis] = aabb[oppositeIndex][secondAxis];
|
||||
gContext.mBoundsLocalPivot[thirdAxis] = aabb[oppositeIndex][thirdAxis];
|
||||
gContext.mbUsingBounds = true;
|
||||
gContext.mBoundsMatrix = gContext.mModelSource;
|
||||
}
|
||||
}
|
||||
|
||||
if (gContext.mbUsingBounds)
|
||||
{
|
||||
matrix_t scale;
|
||||
scale.SetToIdentity();
|
||||
|
||||
gContext.mbUsingBounds = true;
|
||||
gContext.mBoundsMatrix = gContext.mModelSource;
|
||||
}
|
||||
// small anchor on middle of segment
|
||||
if (!gContext.mbUsingBounds && gContext.mbEnable && overSmallAnchor && CanActivate())
|
||||
{
|
||||
vec_t midPointOpposite = (aabb[(i + 2) % 4] + aabb[(i + 3) % 4]) * 0.5f;
|
||||
gContext.mBoundsPivot.TransformPoint(midPointOpposite, gContext.mModelSource);
|
||||
gContext.mBoundsAnchor.TransformPoint(midPoint, gContext.mModelSource);
|
||||
gContext.mBoundsPlan = BuildPlan(gContext.mBoundsAnchor, bestAxisWorldDirection);
|
||||
gContext.mBoundsBestAxis = bestAxis;
|
||||
int indices[] = { secondAxis , thirdAxis };
|
||||
gContext.mBoundsAxis[0] = indices[i%2];
|
||||
gContext.mBoundsAxis[1] = -1;
|
||||
// compute projected mouse position on plan
|
||||
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan);
|
||||
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
|
||||
|
||||
gContext.mBoundsLocalPivot.Set(0.f);
|
||||
gContext.mBoundsLocalPivot[gContext.mBoundsAxis[0]] = aabb[oppositeIndex][indices[i % 2]];// bounds[gContext.mBoundsAxis[0]] * (((i + 1) & 2) ? 1.f : -1.f);
|
||||
// compute a reference and delta vectors base on mouse move
|
||||
vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs();
|
||||
vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs();
|
||||
|
||||
gContext.mbUsingBounds = true;
|
||||
gContext.mBoundsMatrix = gContext.mModelSource;
|
||||
}
|
||||
}
|
||||
|
||||
if (gContext.mbUsingBounds)
|
||||
{
|
||||
matrix_t scale;
|
||||
scale.SetToIdentity();
|
||||
// for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int axisIndex = gContext.mBoundsAxis[i];
|
||||
if (axisIndex == -1)
|
||||
continue;
|
||||
|
||||
// compute projected mouse position on plan
|
||||
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mBoundsPlan);
|
||||
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
|
||||
float ratioAxis = 1.f;
|
||||
vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex].Abs();
|
||||
|
||||
// compute a reference and delta vectors base on mouse move
|
||||
vec_t deltaVector = (newPos - gContext.mBoundsPivot).Abs();
|
||||
vec_t referenceVector = (gContext.mBoundsAnchor - gContext.mBoundsPivot).Abs();
|
||||
float dtAxis = axisDir.Dot(referenceVector);
|
||||
float boundSize = bounds[axisIndex + 3] - bounds[axisIndex];
|
||||
if (dtAxis > FLT_EPSILON)
|
||||
ratioAxis = axisDir.Dot(deltaVector) / dtAxis;
|
||||
|
||||
// for 1 or 2 axes, compute a ratio that's used for scale and snap it based on resulting length
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int axisIndex1 = gContext.mBoundsAxis[i];
|
||||
if (axisIndex1 == -1)
|
||||
continue;
|
||||
if (snapValues)
|
||||
{
|
||||
float length = boundSize * ratioAxis;
|
||||
ComputeSnap(&length, snapValues[axisIndex]);
|
||||
if (boundSize > FLT_EPSILON)
|
||||
ratioAxis = length / boundSize;
|
||||
}
|
||||
scale.component[axisIndex] *= ratioAxis;
|
||||
}
|
||||
|
||||
float ratioAxis = 1.f;
|
||||
vec_t axisDir = gContext.mBoundsMatrix.component[axisIndex1].Abs();
|
||||
// transform matrix
|
||||
matrix_t preScale, postScale;
|
||||
preScale.Translation(-gContext.mBoundsLocalPivot);
|
||||
postScale.Translation(gContext.mBoundsLocalPivot);
|
||||
matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix;
|
||||
*matrix = res;
|
||||
|
||||
float dtAxis = axisDir.Dot(referenceVector);
|
||||
float boundSize = bounds[axisIndex1 + 3] - bounds[axisIndex1];
|
||||
if (dtAxis > FLT_EPSILON)
|
||||
ratioAxis = axisDir.Dot(deltaVector) / dtAxis;
|
||||
// info text
|
||||
char tmps[512];
|
||||
ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
|
||||
ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f"
|
||||
, (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length()
|
||||
, (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length()
|
||||
, (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length()
|
||||
);
|
||||
drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps);
|
||||
drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps);
|
||||
}
|
||||
|
||||
if (snapValues)
|
||||
{
|
||||
float length = boundSize * ratioAxis;
|
||||
ComputeSnap(&length, snapValues[axisIndex1]);
|
||||
if (boundSize > FLT_EPSILON)
|
||||
ratioAxis = length / boundSize;
|
||||
}
|
||||
scale.component[axisIndex1] *= ratioAxis;
|
||||
}
|
||||
|
||||
// transform matrix
|
||||
matrix_t preScale, postScale;
|
||||
preScale.Translation(-gContext.mBoundsLocalPivot);
|
||||
postScale.Translation(gContext.mBoundsLocalPivot);
|
||||
matrix_t res = preScale * scale * postScale * gContext.mBoundsMatrix;
|
||||
*matrix = res;
|
||||
|
||||
// info text
|
||||
char tmps[512];
|
||||
ImVec2 destinationPosOnScreen = worldToPos(gContext.mModel.v.position, gContext.mViewProjection);
|
||||
ImFormatString(tmps, sizeof(tmps), "X: %.2f Y: %.2f Z:%.2f"
|
||||
, (bounds[3] - bounds[0]) * gContext.mBoundsMatrix.component[0].Length() * scale.component[0].Length()
|
||||
, (bounds[4] - bounds[1]) * gContext.mBoundsMatrix.component[1].Length() * scale.component[1].Length()
|
||||
, (bounds[5] - bounds[2]) * gContext.mBoundsMatrix.component[2].Length() * scale.component[2].Length()
|
||||
);
|
||||
drawList->AddText(ImVec2(destinationPosOnScreen.x + 15, destinationPosOnScreen.y + 15), 0xFF000000, tmps);
|
||||
drawList->AddText(ImVec2(destinationPosOnScreen.x + 14, destinationPosOnScreen.y + 14), 0xFFFFFFFF, tmps);
|
||||
}
|
||||
|
||||
if (!io.MouseDown[0])
|
||||
gContext.mbUsingBounds = false;
|
||||
|
||||
if( gContext.mbUsingBounds )
|
||||
break;
|
||||
}
|
||||
if (!io.MouseDown[0])
|
||||
gContext.mbUsingBounds = false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1354,7 +1255,7 @@ namespace ImGuizmo
|
||||
|
||||
vec_t deltaScreen = { io.MousePos.x - gContext.mScreenSquareCenter.x, io.MousePos.y - gContext.mScreenSquareCenter.y, 0.f, 0.f };
|
||||
float dist = deltaScreen.Length();
|
||||
if (dist >= (gContext.mRadiusSquareCenter - 1.0f) && dist < (gContext.mRadiusSquareCenter + 1.0f))
|
||||
if (dist >= (screenRotateSize - 0.002f) * gContext.mHeight && dist < (screenRotateSize + 0.002f) * gContext.mHeight)
|
||||
type = ROTATE_SCREEN;
|
||||
|
||||
const vec_t planNormals[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir};
|
||||
@@ -1426,7 +1327,6 @@ namespace ImGuizmo
|
||||
// move
|
||||
if (gContext.mbUsing)
|
||||
{
|
||||
ImGui::CaptureMouseFromApp();
|
||||
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
|
||||
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
|
||||
|
||||
@@ -1485,9 +1385,8 @@ namespace ImGuizmo
|
||||
// find new possible way to move
|
||||
vec_t gizmoHitProportion;
|
||||
type = GetMoveType(&gizmoHitProportion);
|
||||
if (CanActivate() && type != NONE)
|
||||
if (io.MouseDown[0] && type != NONE)
|
||||
{
|
||||
ImGui::CaptureMouseFromApp();
|
||||
gContext.mbUsing = true;
|
||||
gContext.mCurrentOperation = type;
|
||||
const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.up, -gContext.mCameraDir };
|
||||
@@ -1510,9 +1409,8 @@ namespace ImGuizmo
|
||||
{
|
||||
// find new possible way to scale
|
||||
type = GetScaleType();
|
||||
if (CanActivate() && type != NONE)
|
||||
if (io.MouseDown[0] && type != NONE)
|
||||
{
|
||||
ImGui::CaptureMouseFromApp();
|
||||
gContext.mbUsing = true;
|
||||
gContext.mCurrentOperation = type;
|
||||
const vec_t movePlanNormal[] = { gContext.mModel.v.up, gContext.mModel.v.dir, gContext.mModel.v.right, gContext.mModel.v.dir, gContext.mModel.v.up, gContext.mModel.v.right, -gContext.mCameraDir };
|
||||
@@ -1531,7 +1429,6 @@ namespace ImGuizmo
|
||||
// scale
|
||||
if (gContext.mbUsing)
|
||||
{
|
||||
ImGui::CaptureMouseFromApp();
|
||||
const float len = IntersectRayPlane(gContext.mRayOrigin, gContext.mRayVector, gContext.mTranslationPlan);
|
||||
vec_t newPos = gContext.mRayOrigin + gContext.mRayVector * len;
|
||||
vec_t newOrigin = newPos - gContext.mRelativeOrigin * gContext.mScreenFactor;
|
||||
@@ -1551,7 +1448,7 @@ namespace ImGuizmo
|
||||
gContext.mScale[axisIndex] = max(ratio, 0.001f);
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
float scaleDelta = (io.MousePos.x - gContext.mSaveMousePosx) * 0.01f;
|
||||
gContext.mScale.Set(max(1.f + scaleDelta, 0.001f));
|
||||
}
|
||||
@@ -1595,15 +1492,14 @@ namespace ImGuizmo
|
||||
if (!gContext.mbUsing)
|
||||
{
|
||||
type = GetRotateType();
|
||||
|
||||
|
||||
if (type == ROTATE_SCREEN)
|
||||
{
|
||||
applyRotationLocaly = true;
|
||||
}
|
||||
|
||||
if (CanActivate() && type != NONE)
|
||||
if (io.MouseDown[0] && type != NONE)
|
||||
{
|
||||
ImGui::CaptureMouseFromApp();
|
||||
gContext.mbUsing = true;
|
||||
gContext.mCurrentOperation = type;
|
||||
const vec_t rotatePlanNormal[] = { gContext.mModel.v.right, gContext.mModel.v.up, gContext.mModel.v.dir, -gContext.mCameraDir };
|
||||
@@ -1627,7 +1523,6 @@ namespace ImGuizmo
|
||||
// rotation
|
||||
if (gContext.mbUsing)
|
||||
{
|
||||
ImGui::CaptureMouseFromApp();
|
||||
gContext.mRotationAngle = ComputeAngleOnPlan();
|
||||
if (snap)
|
||||
{
|
||||
@@ -1732,41 +1627,41 @@ namespace ImGuizmo
|
||||
int type = NONE;
|
||||
if (gContext.mbEnable)
|
||||
{
|
||||
if (!gContext.mbUsingBounds)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case ROTATE:
|
||||
HandleRotation(matrix, deltaMatrix, type, snap);
|
||||
break;
|
||||
case TRANSLATE:
|
||||
HandleTranslation(matrix, deltaMatrix, type, snap);
|
||||
break;
|
||||
case SCALE:
|
||||
HandleScale(matrix, deltaMatrix, type, snap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!gContext.mbUsingBounds)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case ROTATE:
|
||||
HandleRotation(matrix, deltaMatrix, type, snap);
|
||||
break;
|
||||
case TRANSLATE:
|
||||
HandleTranslation(matrix, deltaMatrix, type, snap);
|
||||
break;
|
||||
case SCALE:
|
||||
HandleScale(matrix, deltaMatrix, type, snap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (localBounds && !gContext.mbUsing)
|
||||
HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap);
|
||||
if (localBounds && !gContext.mbUsing)
|
||||
HandleAndDrawLocalBounds(localBounds, (matrix_t*)matrix, boundsSnap);
|
||||
|
||||
if (!gContext.mbUsingBounds)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case ROTATE:
|
||||
DrawRotationGizmo(type);
|
||||
break;
|
||||
case TRANSLATE:
|
||||
DrawTranslationGizmo(type);
|
||||
break;
|
||||
case SCALE:
|
||||
DrawScaleGizmo(type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!gContext.mbUsingBounds)
|
||||
{
|
||||
switch (operation)
|
||||
{
|
||||
case ROTATE:
|
||||
DrawRotationGizmo(type);
|
||||
break;
|
||||
case TRANSLATE:
|
||||
DrawTranslationGizmo(type);
|
||||
break;
|
||||
case SCALE:
|
||||
DrawScaleGizmo(type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCube(const float *view, const float *projection, float *matrix)
|
||||
@@ -1818,7 +1713,7 @@ namespace ImGuizmo
|
||||
continue;
|
||||
|
||||
// draw face with lighter color
|
||||
gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080);
|
||||
gContext.mDrawList->AddConvexPolyFilled(faceCoordsScreen, 4, directionColor[normalIndex] | 0x808080, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,9 +112,6 @@ void EditTransform(const Camera& camera, matrix_t& matrix)
|
||||
|
||||
namespace ImGuizmo
|
||||
{
|
||||
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
|
||||
IMGUI_API void SetDrawlist();
|
||||
|
||||
// call BeginFrame right after ImGui_XXXX_NewFrame();
|
||||
IMGUI_API void BeginFrame();
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,6 @@
|
||||
#include "OpenGLWindow/X11OpenGLWindow.h"
|
||||
#endif
|
||||
|
||||
#include <imgui_internal.h>
|
||||
#include <cstdio>
|
||||
|
||||
#define IMGUI_B3G_CONTROL (300)
|
||||
@@ -123,12 +122,12 @@ void ImGui_ImplBtGui_RenderDrawLists(ImDrawData* draw_data) {
|
||||
(GLsizei)last_viewport[3]);
|
||||
}
|
||||
|
||||
static const char* ImGui_ImplBtGui_GetClipboardText(void* user_ctx) {
|
||||
static const char* ImGui_ImplBtGui_GetClipboardText() {
|
||||
// @todo
|
||||
return NULL; // glfwGetClipboardString(g_Window);
|
||||
}
|
||||
|
||||
static void ImGui_ImplBtGui_SetClipboardText(void* user_ctx, const char* text) {
|
||||
static void ImGui_ImplBtGui_SetClipboardText(const char* text) {
|
||||
// @todo
|
||||
return;
|
||||
// glfwSetClipboardString(g_Window, text);
|
||||
@@ -260,7 +259,7 @@ bool ImGui_ImplBtGui_Init(b3gDefaultOpenGLWindow* window) {
|
||||
io.SetClipboardTextFn = ImGui_ImplBtGui_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplBtGui_GetClipboardText;
|
||||
#ifdef _WIN32
|
||||
// io.ImeWindowHandle = glfwGetWin32Window(g_Window);
|
||||
//io.ImeWindowHandle = glfwGetWin32Window(g_Window);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
@@ -278,7 +277,7 @@ bool ImGui_ImplBtGui_Init(b3gDefaultOpenGLWindow* window) {
|
||||
|
||||
void ImGui_ImplBtGui_Shutdown() {
|
||||
ImGui_ImplBtGui_InvalidateDeviceObjects();
|
||||
ImGui::Shutdown(ImGui::GetCurrentContext());
|
||||
ImGui::Shutdown();
|
||||
}
|
||||
|
||||
void ImGui_ImplBtGui_NewFrame(int mouse_x, int mouse_y) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
project(gltfutil)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
include_directories(../../)
|
||||
|
||||
file(GLOB gltfutil_sources *.cc *.h)
|
||||
add_executable(gltfutil ${gltfutil_sources})
|
||||
|
||||
install ( TARGETS
|
||||
gltfutil
|
||||
DESTINATION
|
||||
bin
|
||||
)
|
||||
@@ -1,60 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "texture_dumper.h"
|
||||
|
||||
namespace gltfutil {
|
||||
|
||||
enum class ui_mode { cli, interactive };
|
||||
enum class cli_action { not_set, help, dump };
|
||||
enum class FileType { Ascii, Binary, Unknown };
|
||||
|
||||
/// Probe inside the file, or check the extension to determine if we have to
|
||||
/// load a text file, or a binary file
|
||||
FileType detectType(const std::string& path) {
|
||||
// Quickly open the file as binary and chekc if there's the gltf binary magic
|
||||
// number
|
||||
{
|
||||
auto probe = std::ifstream(path, std::ios_base::binary);
|
||||
if (!probe) throw std::runtime_error("Could not open " + path);
|
||||
|
||||
std::array<char, 5> buffer;
|
||||
for (size_t i{0}; i < 4; ++i) probe >> buffer[i];
|
||||
buffer[4] = 0;
|
||||
|
||||
if (std::string("glTF") == std::string(buffer.data())) {
|
||||
std::cout
|
||||
<< "Detected binary file thanks to the magic number at the start!\n";
|
||||
return FileType::Binary;
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have any better, check the file extension.
|
||||
auto extension = path.substr(path.find_last_of('.') + 1);
|
||||
std::transform(std::begin(extension), std::end(extension),
|
||||
std::begin(extension),
|
||||
[](char c) { return char(::tolower(int(c))); });
|
||||
if (extension == "gltf") return FileType::Ascii;
|
||||
if (extension == "glb") return FileType::Binary;
|
||||
|
||||
return FileType::Unknown;
|
||||
}
|
||||
|
||||
struct configuration {
|
||||
std::string input_path, output_dir;
|
||||
ui_mode mode;
|
||||
cli_action action = cli_action::not_set;
|
||||
texture_dumper::texture_output_format requested_format =
|
||||
texture_dumper::texture_output_format::not_specified;
|
||||
|
||||
bool has_output_dir;
|
||||
bool is_valid() {
|
||||
// TODO impl check
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gltfutil
|
||||
@@ -1,124 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gltfuilconfig.h"
|
||||
#include "texture_dumper.h"
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <tiny_gltf.h>
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
namespace gltfutil {
|
||||
int usage(int ret = 0) {
|
||||
using std::cout;
|
||||
cout << "gltfutil: tool for manipulating gltf files\n"
|
||||
<< " usage information:\n\n"
|
||||
<< "\t gltfutil (-d|-h|) (-f [png|bmp|tga]) [path to .gltf/glb] (-o "
|
||||
"[path to output directory])\n\n"
|
||||
//<< "\t\t -i: start in interactive mode\n"
|
||||
<< "\t\t -d: dump enclosed content (image assets)\n"
|
||||
<< "\t\t -f: file format for image output"
|
||||
<< "\t\t -o: ouptput directory path"
|
||||
<< "\t\t -h: print this help\n";
|
||||
return ret;
|
||||
}
|
||||
|
||||
int arg_error() {
|
||||
(void)usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int parse_args(int argc, char** argv) {
|
||||
gltfutil::configuration config;
|
||||
|
||||
for (size_t i = 1; i < size_t(argc); ++i) {
|
||||
char* arg = argv[i];
|
||||
if (arg[0] == '-') switch (arg[1]) {
|
||||
case 'h':
|
||||
return usage(0);
|
||||
break;
|
||||
case 'd':
|
||||
config.mode = ui_mode::cli;
|
||||
config.action = cli_action::dump;
|
||||
break;
|
||||
case 'i':
|
||||
config.mode = ui_mode::interactive;
|
||||
break;
|
||||
case 'o':
|
||||
i++;
|
||||
config.output_dir = argv[i];
|
||||
break;
|
||||
case 'f':
|
||||
i++;
|
||||
config.requested_format =
|
||||
texture_dumper::get_fromat_from_string(argv[i]);
|
||||
break;
|
||||
default:
|
||||
return arg_error();
|
||||
}
|
||||
else {
|
||||
// set the input path
|
||||
config.input_path = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (config.is_valid()) {
|
||||
tinygltf::TinyGLTF loader;
|
||||
tinygltf::Model model;
|
||||
std::string error;
|
||||
std::string warning;
|
||||
bool state;
|
||||
switch (detectType(config.input_path)) {
|
||||
case FileType::Ascii:
|
||||
state = loader.LoadASCIIFromFile(&model, &error, &warning, config.input_path);
|
||||
break;
|
||||
case FileType::Binary:
|
||||
state = loader.LoadBinaryFromFile(&model, &error, &warning, config.input_path);
|
||||
break;
|
||||
case FileType::Unknown:
|
||||
default:
|
||||
return arg_error();
|
||||
break;
|
||||
}
|
||||
|
||||
std::cerr << "state is " << state << '\n';
|
||||
|
||||
if (config.mode == ui_mode::interactive) {
|
||||
// interactive usage now;
|
||||
// TODO impl
|
||||
} else {
|
||||
switch (config.action) {
|
||||
case cli_action::help:
|
||||
return usage();
|
||||
break;
|
||||
|
||||
case cli_action::dump: {
|
||||
texture_dumper dumper(model);
|
||||
if (config.requested_format !=
|
||||
texture_dumper::texture_output_format::not_specified)
|
||||
dumper.set_output_format(config.requested_format);
|
||||
|
||||
if (config.output_dir.empty())
|
||||
dumper.dump_to_folder();
|
||||
else
|
||||
dumper.dump_to_folder(config.output_dir);
|
||||
|
||||
} break;
|
||||
default:
|
||||
return arg_error();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return arg_error();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace gltfutil
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc == 1) return gltfutil::usage();
|
||||
if (argc > 1) return gltfutil::parse_args(argc, argv);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,68 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "stb_image_write.h"
|
||||
#include "texture_dumper.h"
|
||||
|
||||
#include <tiny_gltf.h>
|
||||
|
||||
using namespace gltfutil;
|
||||
using namespace tinygltf;
|
||||
using std::cout;
|
||||
|
||||
texture_dumper::texture_dumper(const Model& input)
|
||||
: model(input), configured_format(texture_output_format::png) {
|
||||
cout << "Texture dumper\n";
|
||||
}
|
||||
|
||||
void texture_dumper::dump_to_folder(const std::string& path) {
|
||||
cout << "dumping to folder " << path << '\n';
|
||||
cout << "model file has " << model.textures.size() << " textures.\n";
|
||||
size_t index = 0;
|
||||
|
||||
for (const auto& texture : model.textures) {
|
||||
index++;
|
||||
const auto& image = model.images[texture.source];
|
||||
cout << "image name is: \"" << image.name << "\"\n";
|
||||
cout << "image size is: " << image.width << 'x' << image.height << '\n';
|
||||
cout << "pixel channel count :" << image.component << '\n';
|
||||
std::string name = image.name.empty() ? std::to_string(index) : image.name;
|
||||
|
||||
switch (configured_format) {
|
||||
case texture_output_format::png:
|
||||
name = path + "/" + name + ".png";
|
||||
std::cout << "Image will be written to " << name << '\n';
|
||||
stbi_write_png(name.c_str(), image.width, image.height, image.component,
|
||||
image.image.data(), 0);
|
||||
break;
|
||||
case texture_output_format::bmp:
|
||||
std::cout << "Image will be written to " << name << '\n';
|
||||
name = path + "/" + name + ".bmp";
|
||||
stbi_write_bmp(name.c_str(), image.width, image.height, image.component,
|
||||
image.image.data());
|
||||
break;
|
||||
case texture_output_format::tga:
|
||||
std::cout << "Image will be written to " << name << '\n';
|
||||
name = path + "/" + name + ".tga";
|
||||
stbi_write_tga(name.c_str(), image.width, image.height, image.component,
|
||||
image.image.data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void texture_dumper::set_output_format(texture_output_format format) {
|
||||
configured_format = format;
|
||||
}
|
||||
|
||||
texture_dumper::texture_output_format texture_dumper::get_fromat_from_string(
|
||||
const std::string& str) {
|
||||
std::string type = str;
|
||||
std::transform(str.begin(), str.end(), type.begin(), ::tolower);
|
||||
|
||||
if (type == "png") return texture_output_format::png;
|
||||
if (type == "bmp") return texture_output_format::bmp;
|
||||
if (type == "tga") return texture_output_format::tga;
|
||||
|
||||
return texture_output_format::not_specified;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <tiny_gltf.h>
|
||||
|
||||
namespace gltfutil {
|
||||
class texture_dumper {
|
||||
public:
|
||||
enum class texture_output_format { png, bmp, tga, not_specified };
|
||||
|
||||
private:
|
||||
const tinygltf::Model& model;
|
||||
texture_output_format configured_format;
|
||||
|
||||
public:
|
||||
texture_dumper(const tinygltf::Model& inputModel);
|
||||
void dump_to_folder(const std::string& path = "./");
|
||||
void set_output_format(texture_output_format format);
|
||||
|
||||
static texture_output_format get_fromat_from_string(const std::string& str);
|
||||
};
|
||||
} // namespace gltfutil
|
||||
7
examples/glview/.gitignore
vendored
7
examples/glview/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
.vs
|
||||
Debug
|
||||
x64
|
||||
packages
|
||||
|
||||
!*.sln
|
||||
!*.vcxproj*
|
||||
@@ -1,69 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(glview)
|
||||
|
||||
set ( CMAKE_PREFIX_PATH cmake )
|
||||
|
||||
set ( DRACO_DIR "" CACHE STRING "Path to draco" )
|
||||
|
||||
find_package ( GLEW REQUIRED )
|
||||
find_package ( GLFW3 REQUIRED )
|
||||
find_package ( OpenGL REQUIRED )
|
||||
|
||||
if (APPLE)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
find_library(COREVIDEO_LIBRARY CoreVideo)
|
||||
find_library(IOKIT_LIBRARY IOKit)
|
||||
else ()
|
||||
if (NOT WIN32)
|
||||
# This means it is Unices
|
||||
set ( GLFW3_UNIX_LINK_LIBRARIES X11 Xxf86vm Xrandr Xi Xinerama Xcursor )
|
||||
find_package (Threads)
|
||||
endif()
|
||||
endif (APPLE)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
if (DEFINED DRACO_DIR)
|
||||
if (DRACO_DIR STREQUAL "")
|
||||
else ()
|
||||
# TODO(syoyo): better CMake script for draco
|
||||
add_definitions(-DTINYGLTF_ENABLE_DRACO)
|
||||
include_directories(${DRACO_DIR}/include)
|
||||
|
||||
link_directories(${DRACO_DIR}/lib)
|
||||
set(DRACO_LIBRARY draco)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
../../
|
||||
../common
|
||||
# ${OPENGL_INCLUDE_DIR}
|
||||
${GLEW_INCLUDE_DIR}
|
||||
${GLFW3_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
add_executable(glview
|
||||
glview.cc
|
||||
../common/trackball.cc
|
||||
)
|
||||
|
||||
target_link_libraries ( glview
|
||||
${DRACO_LIBRARY}
|
||||
${GLFW3_UNIX_LINK_LIBRARIES}
|
||||
${GLEW_LIBRARY}
|
||||
${GLFW3_glfw_LIBRARY}
|
||||
${OPENGL_gl_LIBRARY}
|
||||
${OPENGL_glu_LIBRARY}
|
||||
${COCOA_LIBRARY}
|
||||
${COREVIDEO_LIBRARY}
|
||||
${IOKIT_LIBRARY}
|
||||
${CMAKE_DL_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
install ( TARGETS
|
||||
glview
|
||||
DESTINATION
|
||||
bin
|
||||
)
|
||||
@@ -2,11 +2,9 @@ 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.
|
||||
* premake4 : Requires recent `premake4` for macosx and linux, `premake5` for windows.
|
||||
* GLEW
|
||||
* Ubuntu 16.04: sudo apt install libglew-dev
|
||||
* glfw3
|
||||
* Ubuntu 16.04: sudo apt install libglfw3-dev
|
||||
|
||||
### MacOSX and Linux
|
||||
|
||||
@@ -19,7 +17,7 @@ Simple OpenGL viewer for glTF geometry.
|
||||
|
||||
### Windows(not tested well)
|
||||
|
||||
Edit glew and glfw path in `premake5.lua`, then
|
||||
Edit glew and glfw path in `premake4.lua`, then
|
||||
|
||||
> premake5.exe vs2013
|
||||
|
||||
@@ -27,17 +25,6 @@ Open .sln in Visual Studio 2013
|
||||
|
||||
When running .exe, glew and glfw dll must exist in the working directory.
|
||||
|
||||
#### Build with Draco(optional)
|
||||
|
||||
Assume CMake build.
|
||||
|
||||
```
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -DDRACO_DIR=/path/to/draco ../
|
||||
$ make
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
* [ ] PBR Material
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#-*-cmake-*-
|
||||
#
|
||||
# yue.nicholas@gmail.com
|
||||
#
|
||||
# This auxiliary CMake file helps in find the glfw3 headers and libraries
|
||||
#
|
||||
# GLFW3_FOUND set if glfw3 is found.
|
||||
# GLFW3_INCLUDE_DIR glfw3's include directory
|
||||
# GLFW3_LIBRARY_DIR glfw3's library directory
|
||||
# GLFW3_LIBRARIES all glfw3 libraries
|
||||
|
||||
FIND_PACKAGE (Threads)
|
||||
|
||||
FIND_PACKAGE ( PackageHandleStandardArgs )
|
||||
|
||||
FIND_PATH( GLFW3_LOCATION include/GLFW/glfw3.h
|
||||
"$ENV{GLFW3_HOME}"
|
||||
)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS ( GLFW3
|
||||
REQUIRED_VARS GLFW3_LOCATION
|
||||
)
|
||||
|
||||
IF (GLFW3_FOUND)
|
||||
SET( GLFW3_INCLUDE_DIR "${GLFW3_LOCATION}/include" CACHE STRING "GLFW3 include path")
|
||||
IF (GLFW3_USE_STATIC_LIBS)
|
||||
FIND_LIBRARY ( GLFW3_glfw_LIBRARY libglfw3.a ${GLFW3_LOCATION}/lib
|
||||
)
|
||||
ELSE (GLFW3_USE_STATIC_LIBS)
|
||||
# On windows build, we need to look for glfw3
|
||||
IF (WIN32)
|
||||
SET ( GLFW3_LIBRARY_NAME glfw3 )
|
||||
ELSE ()
|
||||
SET ( GLFW3_LIBRARY_NAME glfw )
|
||||
ENDIF()
|
||||
FIND_LIBRARY ( GLFW3_glfw_LIBRARY ${GLFW3_LIBRARY_NAME} ${GLFW3_LOCATION}/lib
|
||||
)
|
||||
ENDIF (GLFW3_USE_STATIC_LIBS)
|
||||
|
||||
IF (APPLE)
|
||||
FIND_LIBRARY ( COCOA_LIBRARY Cocoa )
|
||||
FIND_LIBRARY ( IOKIT_LIBRARY IOKit )
|
||||
FIND_LIBRARY ( COREVIDEO_LIBRARY CoreVideo )
|
||||
ELSEIF (UNIX AND NOT APPLE)
|
||||
SET ( GLFW3_REQUIRED_X11_LIBRARIES
|
||||
X11
|
||||
Xi
|
||||
Xrandr
|
||||
Xinerama
|
||||
Xcursor
|
||||
Xxf86vm
|
||||
m
|
||||
${CMAKE_DL_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
ENDIF ()
|
||||
|
||||
SET ( GLFW3_LIBRARIES
|
||||
${OPENGL_gl_LIBRARY}
|
||||
${OPENGL_glu_LIBRARY}
|
||||
${GLFW3_glfw_LIBRARY}
|
||||
# UNIX
|
||||
${GLFW3_REQUIRED_X11_LIBRARIES}
|
||||
# APPLE
|
||||
${COCOA_LIBRARY}
|
||||
${IOKIT_LIBRARY}
|
||||
${COREVIDEO_LIBRARY}
|
||||
CACHE STRING "GLFW3 required libraries"
|
||||
)
|
||||
|
||||
ENDIF ()
|
||||
@@ -12,22 +12,11 @@
|
||||
#define GLFW_INCLUDE_GLU
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "../common/trackball.h"
|
||||
#else
|
||||
#include "trackball.h"
|
||||
#endif
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "../../tiny_gltf.h"
|
||||
#else
|
||||
#include "tiny_gltf.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
|
||||
|
||||
@@ -557,12 +546,9 @@ static void DrawMesh(tinygltf::Model &model, const tinygltf::Mesh &mesh) {
|
||||
(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,
|
||||
model.bufferViews[accessor.bufferView].byteStride,
|
||||
BUFFER_OFFSET(accessor.byteOffset));
|
||||
CheckErrors("vertex attrib pointer");
|
||||
glEnableVertexAttribArray(gGLProgramState.attribs[it->first]);
|
||||
@@ -677,14 +663,10 @@ static void DrawNode(tinygltf::Model &model, const tinygltf::Node &node) {
|
||||
// std::cout << it->first << std::endl;
|
||||
// FIXME(syoyo): Refactor.
|
||||
// DrawCurves(scene, it->second);
|
||||
if (node.mesh > -1) {
|
||||
assert(node.mesh < int(model.meshes.size()));
|
||||
DrawMesh(model, model.meshes[node.mesh]);
|
||||
}
|
||||
DrawMesh(model, model.meshes[node.mesh]);
|
||||
|
||||
// Draw child nodes.
|
||||
for (size_t i = 0; i < node.children.size(); i++) {
|
||||
assert(node.children[i] < int(model.nodes.size()));
|
||||
DrawNode(model, model.nodes[node.children[i]]);
|
||||
}
|
||||
|
||||
@@ -701,11 +683,10 @@ static void DrawModel(tinygltf::Model &model) {
|
||||
DrawCurves(scene, it->second);
|
||||
}
|
||||
#else
|
||||
//If the glTF asset has at least one scene, and doesn't define a default one
|
||||
//just show the first one we can find
|
||||
assert(model.scenes.size() > 0);
|
||||
int scene_to_display = model.defaultScene > -1 ? model.defaultScene : 0;
|
||||
const tinygltf::Scene &scene = model.scenes[scene_to_display];
|
||||
|
||||
// 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]]);
|
||||
}
|
||||
@@ -736,8 +717,8 @@ static void PrintNodes(const tinygltf::Scene &scene) {
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "glview input.gltf <scale>" << std::endl;
|
||||
std::cout << "defaulting to example cube model" << std::endl;
|
||||
std::cout << "glview input.gltf <scale>\n" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
float scale = 1.0f;
|
||||
@@ -748,29 +729,16 @@ int main(int argc, char **argv) {
|
||||
tinygltf::Model model;
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string err;
|
||||
std::string warn;
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _DEBUG
|
||||
std::string input_filename(argv[1] ? argv[1] : "../../../models/Cube/Cube.gltf");
|
||||
#endif
|
||||
#else
|
||||
std::string input_filename(argv[1] ? argv[1] : "../../models/Cube/Cube.gltf");
|
||||
#endif
|
||||
|
||||
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, &warn, input_filename.c_str());
|
||||
ret = loader.LoadBinaryFromFile(&model, &err, input_filename.c_str());
|
||||
} else {
|
||||
// assume ascii glTF.
|
||||
ret = loader.LoadASCIIFromFile(&model, &err, &warn, input_filename.c_str());
|
||||
}
|
||||
|
||||
if (!warn.empty()) {
|
||||
printf("Warn: %s\n", warn.c_str());
|
||||
ret = loader.LoadASCIIFromFile(&model, &err, input_filename.c_str());
|
||||
}
|
||||
|
||||
if (!err.empty()) {
|
||||
@@ -784,19 +752,17 @@ int main(int argc, char **argv) {
|
||||
Init();
|
||||
|
||||
// DBG
|
||||
PrintNodes(model.scenes[model.defaultScene > -1 ? model.defaultScene : 0]);
|
||||
PrintNodes(model.scenes[model.defaultScene]);
|
||||
|
||||
if (!glfwInit()) {
|
||||
std::cerr << "Failed to initialize GLFW." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "Simple glTF viewer: " << input_filename;
|
||||
char title[1024];
|
||||
sprintf(title, "Simple glTF viewer: %s", input_filename.c_str());
|
||||
|
||||
std::string title = ss.str();
|
||||
|
||||
window = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);
|
||||
window = glfwCreateWindow(width, height, title, NULL, NULL);
|
||||
if (window == NULL) {
|
||||
std::cerr << "Failed to open GLFW window. " << std::endl;
|
||||
glfwTerminate();
|
||||
@@ -822,24 +788,12 @@ int main(int argc, char **argv) {
|
||||
reshapeFunc(window, width, height);
|
||||
|
||||
GLuint vertId = 0, fragId = 0, progId = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _DEBUG
|
||||
const char *shader_frag_filename = "../shader.frag";
|
||||
const char *shader_vert_filename = "../shader.vert";
|
||||
#endif
|
||||
#else
|
||||
const char *shader_frag_filename = "shader.frag";
|
||||
const char *shader_vert_filename = "shader.vert";
|
||||
#endif
|
||||
|
||||
|
||||
if (false == LoadShader(GL_VERTEX_SHADER, vertId, shader_vert_filename)) {
|
||||
if (false == LoadShader(GL_VERTEX_SHADER, vertId, "shader.vert")) {
|
||||
return -1;
|
||||
}
|
||||
CheckErrors("load vert shader");
|
||||
|
||||
if (false == LoadShader(GL_FRAGMENT_SHADER, fragId, shader_frag_filename)) {
|
||||
if (false == LoadShader(GL_FRAGMENT_SHADER, fragId, "shader.frag")) {
|
||||
return -1;
|
||||
}
|
||||
CheckErrors("load frag shader");
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28010.2050
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glview", "glview\glview.vcxproj", "{D078BB24-377E-497B-8045-03AF2A0547ED}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D078BB24-377E-497B-8045-03AF2A0547ED}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D078BB24-377E-497B-8045-03AF2A0547ED}.Debug|x64.Build.0 = Debug|x64
|
||||
{D078BB24-377E-497B-8045-03AF2A0547ED}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{D078BB24-377E-497B-8045-03AF2A0547ED}.Debug|x86.Build.0 = Debug|Win32
|
||||
{D078BB24-377E-497B-8045-03AF2A0547ED}.Release|x64.ActiveCfg = Release|x64
|
||||
{D078BB24-377E-497B-8045-03AF2A0547ED}.Release|x64.Build.0 = Release|x64
|
||||
{D078BB24-377E-497B-8045-03AF2A0547ED}.Release|x86.ActiveCfg = Release|Win32
|
||||
{D078BB24-377E-497B-8045-03AF2A0547ED}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DB7C2F91-06C9-4BD8-95BF-04814E0C62DA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,152 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{D078BB24-377E-497B-8045-03AF2A0547ED}</ProjectGuid>
|
||||
<RootNamespace>glview</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);opengl32.lib;glu32.lib</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);opengl32.lib;glu32.lib</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);opengl32.lib;glu32.lib</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);opengl32.lib;glu32.lib</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\common\trackball.cc" />
|
||||
<ClCompile Include="..\glview.cc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets" Condition="Exists('..\packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets')" />
|
||||
<Import Project="..\packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets" Condition="Exists('..\packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\nupengl.core.redist.0.1.0.1\build\native\nupengl.core.redist.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\nupengl.core.0.1.0.1\build\native\nupengl.core.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\glview.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\common\trackball.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="nupengl.core" version="0.1.0.1" targetFramework="native" />
|
||||
<package id="nupengl.core.redist" version="0.1.0.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -7,11 +7,9 @@ solution "glview"
|
||||
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
cppdialect "C++11"
|
||||
files { "glview.cc", "../../json11.cpp", "../common/trackball.cc" }
|
||||
files { "glview.cc", "trackball.cc" }
|
||||
includedirs { "./" }
|
||||
includedirs { "../../" }
|
||||
includedirs { "../common/" }
|
||||
|
||||
configuration { "linux" }
|
||||
linkoptions { "`pkg-config --libs glfw3`" }
|
||||
@@ -35,10 +33,9 @@ solution "glview"
|
||||
|
||||
configuration "Debug"
|
||||
defines { "DEBUG" }
|
||||
symbols "On"
|
||||
warnings "Extra"
|
||||
flags { "Symbols", "ExtraWarnings"}
|
||||
|
||||
configuration "Release"
|
||||
defines { "NDEBUG" }
|
||||
optimize "On"
|
||||
warnings "Extra"
|
||||
flags { "Optimize", "ExtraWarnings"}
|
||||
|
||||
292
examples/glview/trackball.cc
Normal file
292
examples/glview/trackball.cc
Normal file
@@ -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;
|
||||
}
|
||||
75
examples/glview/trackball.h
Normal file
75
examples/glview/trackball.h
Normal file
@@ -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]);
|
||||
@@ -1,128 +1,28 @@
|
||||
# NanoSG
|
||||
# Raytrace example
|
||||
|
||||
Simple, minimal and header-only scene graph library for NanoRT.
|
||||
Simple raytracing example with OpenGL preview
|
||||
|
||||
NanoSG itself shoud be compiled with C++-03 compiler, but demo code uses C++11 features.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Build
|
||||
|
||||
### Linux or macOS
|
||||
|
||||
```bash
|
||||
premake5 gmake
|
||||
make
|
||||
```
|
||||
$ premake5 gmake
|
||||
$ make
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```bash
|
||||
premake5 vs2015
|
||||
```
|
||||
$ 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
|
||||
## Third party libraries and its icenses.
|
||||
|
||||
* picojson : BSD license.
|
||||
* bt3gui : zlib 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
|
||||
|
||||
@@ -1,23 +1,9 @@
|
||||
{
|
||||
"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
|
||||
{ "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
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,11 @@
|
||||
#include "gltf-loader.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory> // c++11
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define TINYGLTF_NO_STB_IMAGE_WRITE
|
||||
#include <tiny_gltf.h>
|
||||
#define STB_IMAGE_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);
|
||||
@@ -16,30 +15,24 @@ static std::string GetFilePathExtension(const std::string &FileName) {
|
||||
///
|
||||
/// 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) {
|
||||
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;
|
||||
std::string warn;
|
||||
const std::string ext = GetFilePathExtension(filename);
|
||||
std::string ext = GetFilePathExtension(filename);
|
||||
|
||||
bool ret = false;
|
||||
if (ext.compare("glb") == 0) {
|
||||
// assume binary glTF.
|
||||
ret = loader.LoadBinaryFromFile(&model, &err, &warn, filename.c_str());
|
||||
ret = loader.LoadBinaryFromFile(&model, &err, filename.c_str());
|
||||
} else {
|
||||
// assume ascii glTF.
|
||||
ret = loader.LoadASCIIFromFile(&model, &err, &warn, filename.c_str());
|
||||
}
|
||||
|
||||
if (!warn.empty()) {
|
||||
std::cout << "glTF parse warning: " << warn << std::endl;
|
||||
ret = loader.LoadASCIIFromFile(&model, &err, filename.c_str());
|
||||
}
|
||||
|
||||
if (!err.empty()) {
|
||||
@@ -50,448 +43,7 @@ bool LoadGLTF(const std::string &filename, float scale,
|
||||
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(n1.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(n1.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;
|
||||
return false;
|
||||
}
|
||||
} // namespace example
|
||||
|
||||
} // namespace example
|
||||
|
||||
@@ -1,166 +1,19 @@
|
||||
#ifndef EXAMPLE_GLTF_LOADER_H_
|
||||
#define EXAMPLE_GLTF_LOADER_H_
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "material.h"
|
||||
#include "mesh.h"
|
||||
#include "material.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);
|
||||
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_
|
||||
#endif // EXAMPLE_GLTF_LOADER_H_
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 122 KiB |
@@ -53,13 +53,12 @@ THE SOFTWARE.
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <atomic> // C++11
|
||||
#include <chrono> // C++11
|
||||
@@ -73,23 +72,22 @@ THE SOFTWARE.
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4201)
|
||||
#pragma warning(disable: 4201)
|
||||
#endif
|
||||
|
||||
#include "glm/gtc/matrix_transform.hpp"
|
||||
#include "glm/gtc/quaternion.hpp"
|
||||
#include "glm/gtc/type_ptr.hpp"
|
||||
#include "glm/mat4x4.hpp"
|
||||
#include "glm/gtc/quaternion.hpp"
|
||||
#include "glm/gtc/matrix_transform.hpp"
|
||||
#include "glm/gtc/type_ptr.hpp"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "gltf-loader.h"
|
||||
#include "nanosg.h"
|
||||
#include "obj-loader.h"
|
||||
#include "render-config.h"
|
||||
#include "render.h"
|
||||
#include "gltf-loader.h"
|
||||
#include "trackball.h"
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -98,17 +96,19 @@ THE SOFTWARE.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
b3gDefaultOpenGLWindow *window = 0;
|
||||
#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)
|
||||
|
||||
b3gDefaultOpenGLWindow* window = 0;
|
||||
int gWidth = 512;
|
||||
int gHeight = 512;
|
||||
int gMousePosX = -1, gMousePosY = -1;
|
||||
bool gMouseLeftDown = false;
|
||||
|
||||
// FIX issue when max passes is done - no modes is switched. pass must be set to
|
||||
// 0 when mode is changed
|
||||
int gShowBufferMode_prv = SHOW_BUFFER_COLOR;
|
||||
int gShowBufferMode = SHOW_BUFFER_COLOR;
|
||||
|
||||
bool gTabPressed = false;
|
||||
bool gShiftPressed = false;
|
||||
float gShowPositionScale = 1.0f;
|
||||
@@ -128,7 +128,8 @@ std::atomic<bool> gSceneDirty;
|
||||
example::RenderConfig gRenderConfig;
|
||||
std::mutex gMutex;
|
||||
|
||||
struct RenderLayer {
|
||||
struct RenderLayer
|
||||
{
|
||||
std::vector<float> displayRGBA; // Accumurated image.
|
||||
std::vector<float> rgba;
|
||||
std::vector<float> auxRGBA; // Auxiliary buffer
|
||||
@@ -142,12 +143,14 @@ struct RenderLayer {
|
||||
|
||||
RenderLayer gRenderLayer;
|
||||
|
||||
struct Camera {
|
||||
struct Camera
|
||||
{
|
||||
glm::mat4 view;
|
||||
glm::mat4 projection;
|
||||
};
|
||||
|
||||
struct ManipConfig {
|
||||
struct ManipConfig
|
||||
{
|
||||
glm::vec3 snapTranslation;
|
||||
glm::vec3 snapRotation;
|
||||
glm::vec3 snapScale;
|
||||
@@ -198,12 +201,9 @@ void RenderThread() {
|
||||
// gRenderCancel may be set to true in main loop.
|
||||
// Render() will repeatedly check this flag inside the rendering loop.
|
||||
|
||||
bool ret = example::Renderer::Render(
|
||||
&gRenderLayer.rgba.at(0), &gRenderLayer.auxRGBA.at(0),
|
||||
&gRenderLayer.sampleCounts.at(0), gCurrQuat, gScene, gAsset,
|
||||
gRenderConfig, gRenderCancel,
|
||||
gShowBufferMode // added mode passing
|
||||
);
|
||||
bool ret =
|
||||
example::Renderer::Render(&gRenderLayer.rgba.at(0), &gRenderLayer.auxRGBA.at(0), &gRenderLayer.sampleCounts.at(0),
|
||||
gCurrQuat, gScene, gAsset, gRenderConfig, gRenderCancel);
|
||||
|
||||
if (ret) {
|
||||
std::lock_guard<std::mutex> guard(gMutex);
|
||||
@@ -219,18 +219,16 @@ void RenderThread() {
|
||||
}
|
||||
}
|
||||
|
||||
void InitRender(example::RenderConfig *rc) {
|
||||
void InitRender(example::RenderConfig* rc) {
|
||||
rc->pass = 0;
|
||||
|
||||
rc->max_passes = 128;
|
||||
|
||||
gRenderLayer.sampleCounts.resize(rc->width * rc->height);
|
||||
std::fill(gRenderLayer.sampleCounts.begin(), gRenderLayer.sampleCounts.end(),
|
||||
0.0);
|
||||
std::fill(gRenderLayer.sampleCounts.begin(), gRenderLayer.sampleCounts.end(), 0.0);
|
||||
|
||||
gRenderLayer.displayRGBA.resize(rc->width * rc->height * 4);
|
||||
std::fill(gRenderLayer.displayRGBA.begin(), gRenderLayer.displayRGBA.end(),
|
||||
0.0);
|
||||
std::fill(gRenderLayer.displayRGBA.begin(), gRenderLayer.displayRGBA.end(), 0.0);
|
||||
|
||||
gRenderLayer.rgba.resize(rc->width * rc->height * 4);
|
||||
std::fill(gRenderLayer.rgba.begin(), gRenderLayer.rgba.end(), 0.0);
|
||||
@@ -239,23 +237,19 @@ void InitRender(example::RenderConfig *rc) {
|
||||
std::fill(gRenderLayer.auxRGBA.begin(), gRenderLayer.auxRGBA.end(), 0.0);
|
||||
|
||||
gRenderLayer.normalRGBA.resize(rc->width * rc->height * 4);
|
||||
std::fill(gRenderLayer.normalRGBA.begin(), gRenderLayer.normalRGBA.end(),
|
||||
0.0);
|
||||
std::fill(gRenderLayer.normalRGBA.begin(), gRenderLayer.normalRGBA.end(), 0.0);
|
||||
|
||||
gRenderLayer.positionRGBA.resize(rc->width * rc->height * 4);
|
||||
std::fill(gRenderLayer.positionRGBA.begin(), gRenderLayer.positionRGBA.end(),
|
||||
0.0);
|
||||
std::fill(gRenderLayer.positionRGBA.begin(), gRenderLayer.positionRGBA.end(), 0.0);
|
||||
|
||||
gRenderLayer.depthRGBA.resize(rc->width * rc->height * 4);
|
||||
std::fill(gRenderLayer.depthRGBA.begin(), gRenderLayer.depthRGBA.end(), 0.0);
|
||||
|
||||
gRenderLayer.texCoordRGBA.resize(rc->width * rc->height * 4);
|
||||
std::fill(gRenderLayer.texCoordRGBA.begin(), gRenderLayer.texCoordRGBA.end(),
|
||||
0.0);
|
||||
std::fill(gRenderLayer.texCoordRGBA.begin(), gRenderLayer.texCoordRGBA.end(), 0.0);
|
||||
|
||||
gRenderLayer.varyCoordRGBA.resize(rc->width * rc->height * 4);
|
||||
std::fill(gRenderLayer.varyCoordRGBA.begin(),
|
||||
gRenderLayer.varyCoordRGBA.end(), 0.0);
|
||||
std::fill(gRenderLayer.varyCoordRGBA.begin(), gRenderLayer.varyCoordRGBA.end(), 0.0);
|
||||
|
||||
rc->normalImage = &gRenderLayer.normalRGBA.at(0);
|
||||
rc->positionImage = &gRenderLayer.positionRGBA.at(0);
|
||||
@@ -274,18 +268,19 @@ void checkErrors(std::string desc) {
|
||||
}
|
||||
}
|
||||
|
||||
static int CreateDisplayTextureGL(const float *data, int width, int height,
|
||||
int components) {
|
||||
GLuint id;
|
||||
glGenTextures(1, &id);
|
||||
|
||||
static int CreateDisplayTextureGL(const float *data, int width,
|
||||
int height, int components) {
|
||||
GLuint id;
|
||||
glGenTextures(1, &id);
|
||||
|
||||
GLint last_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
|
||||
GLenum format = GL_RGBA;
|
||||
if (components == 1) {
|
||||
format = GL_LUMINANCE;
|
||||
@@ -295,15 +290,15 @@ static int CreateDisplayTextureGL(const float *data, int width, int height,
|
||||
format = GL_RGB;
|
||||
} else if (components == 4) {
|
||||
format = GL_RGBA;
|
||||
} else {
|
||||
assert(0); // "Invalid components"
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, GL_FLOAT,
|
||||
data);
|
||||
|
||||
} else {
|
||||
assert(0); // "Invalid components"
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format,
|
||||
GL_FLOAT, data);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
|
||||
|
||||
return static_cast<int>(id);
|
||||
}
|
||||
|
||||
@@ -317,10 +312,8 @@ void keyboardCallback(int keycode, int state) {
|
||||
// reset.
|
||||
trackball(gCurrQuat, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
// clear buffer.
|
||||
memset(gRenderLayer.rgba.data(), 0,
|
||||
sizeof(float) * gRenderConfig.width * gRenderConfig.height * 4);
|
||||
memset(gRenderLayer.sampleCounts.data(), 0,
|
||||
sizeof(int) * gRenderConfig.width * gRenderConfig.height);
|
||||
memset(gRenderLayer.rgba.data(), 0, sizeof(float) * gRenderConfig.width * gRenderConfig.height * 4);
|
||||
memset(gRenderLayer.sampleCounts.data(), 0, sizeof(int) * gRenderConfig.width * gRenderConfig.height);
|
||||
} else if (keycode == 9) {
|
||||
gTabPressed = (state == 1);
|
||||
} else if (keycode == B3G_SHIFT) {
|
||||
@@ -337,10 +330,9 @@ void keyboardCallback(int keycode, int state) {
|
||||
}
|
||||
|
||||
void mouseMoveCallback(float x, float y) {
|
||||
|
||||
if (gMouseLeftDown) {
|
||||
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
|
||||
gSceneDirty = true;
|
||||
// RequestRender();
|
||||
} else {
|
||||
float w = static_cast<float>(gRenderConfig.width);
|
||||
float h = static_cast<float>(gRenderConfig.height);
|
||||
@@ -379,34 +371,31 @@ void mouseButtonCallback(int button, int state, float x, float y) {
|
||||
(void)y;
|
||||
ImGui_ImplBtGui_SetMouseButtonState(button, (state == 1));
|
||||
|
||||
if (button == 0 && !state)
|
||||
gMouseLeftDown = false; // prevent sticky trackball after using gizmo
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.WantCaptureMouse || io.WantCaptureKeyboard) {
|
||||
if (button == 0 && !state) {
|
||||
if (ImGuizmo::IsUsing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// left button
|
||||
if (button == 0) {
|
||||
if (state) {
|
||||
gMouseLeftDown = true;
|
||||
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
|
||||
} else {
|
||||
trackball(gPrevQuat, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
} else {
|
||||
gMouseLeftDown = false;
|
||||
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
|
||||
gSceneDirty = true;
|
||||
RequestRender();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// left button
|
||||
if (button == 0) {
|
||||
if (state) {
|
||||
gMouseLeftDown = true;
|
||||
if (ImGuizmo::IsOver() || ImGuizmo::IsUsing()) {
|
||||
} else {
|
||||
trackball(gPrevQuat, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resizeCallback(float width, float height) {
|
||||
// GLfloat h = (GLfloat)height / (GLfloat)width;
|
||||
//GLfloat h = (GLfloat)height / (GLfloat)width;
|
||||
GLfloat xmax, znear, zfar;
|
||||
|
||||
znear = 1.0f;
|
||||
@@ -417,7 +406,7 @@ void resizeCallback(float width, float height) {
|
||||
gHeight = static_cast<int>(height);
|
||||
}
|
||||
|
||||
inline float pseudoColor(float v, int ch) {
|
||||
inline float pesudoColor(float v, int ch) {
|
||||
if (ch == 0) { // red
|
||||
if (v <= 0.5f)
|
||||
return 0.f;
|
||||
@@ -482,7 +471,7 @@ void UpdateDisplayTextureGL(GLint tex_id, int width, int height) {
|
||||
for (size_t i = 0; i < buf.size(); i++) {
|
||||
float v = (gRenderLayer.depthRGBA[i] - d_min) / d_diff;
|
||||
if (gShowDepthPeseudoColor) {
|
||||
buf[i] = pseudoColor(v, i % 4);
|
||||
buf[i] = pesudoColor(v, i % 4);
|
||||
} else {
|
||||
buf[i] = v;
|
||||
}
|
||||
@@ -502,31 +491,31 @@ void UpdateDisplayTextureGL(GLint tex_id, int width, int height) {
|
||||
disp.resize(width * height * 4);
|
||||
{
|
||||
for (size_t y = 0; y < height; y++) {
|
||||
memcpy(&disp[4 * (y * width)], &buf[4 * ((height - y - 1) * width)],
|
||||
sizeof(float) * 4 * width);
|
||||
memcpy(&disp[4 * (y * width)], &buf[4 * ((height - y - 1) * width)], sizeof(float) * 4 * width);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT,
|
||||
disp.data());
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_FLOAT, disp.data());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// glRasterPos2i(-1, -1);
|
||||
// glDrawPixels(width, height, GL_RGBA, GL_FLOAT,
|
||||
//glRasterPos2i(-1, -1);
|
||||
//glDrawPixels(width, height, GL_RGBA, GL_FLOAT,
|
||||
// static_cast<const GLvoid*>(&buf.at(0)));
|
||||
}
|
||||
|
||||
void EditTransform(const ManipConfig &config, const Camera &camera,
|
||||
glm::mat4 &matrix) {
|
||||
void EditTransform(const ManipConfig &config, const Camera& camera, glm::mat4& matrix)
|
||||
{
|
||||
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
|
||||
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
|
||||
if (ImGui::IsKeyPressed(90)) mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
if (ImGui::IsKeyPressed(69)) mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||
if (ImGui::IsKeyPressed(82)) // r Key
|
||||
if (ImGui::IsKeyPressed(90))
|
||||
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
if (ImGui::IsKeyPressed(69))
|
||||
mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||
if (ImGui::IsKeyPressed(82)) // r Key
|
||||
mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||
if (ImGui::RadioButton("Translate",
|
||||
mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
|
||||
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
|
||||
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
|
||||
@@ -535,15 +524,14 @@ void EditTransform(const ManipConfig &config, const Camera &camera,
|
||||
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
|
||||
mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
|
||||
ImGuizmo::DecomposeMatrixToComponents(&matrix[0][0], matrixTranslation,
|
||||
matrixRotation, matrixScale);
|
||||
ImGuizmo::DecomposeMatrixToComponents(&matrix[0][0], matrixTranslation, matrixRotation, matrixScale);
|
||||
ImGui::InputFloat3("Tr", matrixTranslation, 3);
|
||||
ImGui::InputFloat3("Rt", matrixRotation, 3);
|
||||
ImGui::InputFloat3("Sc", matrixScale, 3);
|
||||
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation,
|
||||
matrixScale, &matrix[0][0]);
|
||||
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, &matrix[0][0]);
|
||||
|
||||
if (mCurrentGizmoOperation != ImGuizmo::SCALE) {
|
||||
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
|
||||
{
|
||||
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
|
||||
mCurrentGizmoMode = ImGuizmo::LOCAL;
|
||||
ImGui::SameLine();
|
||||
@@ -551,32 +539,33 @@ void EditTransform(const ManipConfig &config, const Camera &camera,
|
||||
mCurrentGizmoMode = ImGuizmo::WORLD;
|
||||
}
|
||||
static bool useSnap(false);
|
||||
if (ImGui::IsKeyPressed(83)) useSnap = !useSnap;
|
||||
if (ImGui::IsKeyPressed(83))
|
||||
useSnap = !useSnap;
|
||||
ImGui::Checkbox("", &useSnap);
|
||||
ImGui::SameLine();
|
||||
glm::vec3 snap;
|
||||
switch (mCurrentGizmoOperation) {
|
||||
case ImGuizmo::TRANSLATE:
|
||||
snap = config.snapTranslation;
|
||||
ImGui::InputFloat3("Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::ROTATE:
|
||||
snap = config.snapRotation;
|
||||
ImGui::InputFloat("Angle Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::SCALE:
|
||||
snap = config.snapScale;
|
||||
ImGui::InputFloat("Scale Snap", &snap.x);
|
||||
break;
|
||||
switch (mCurrentGizmoOperation)
|
||||
{
|
||||
case ImGuizmo::TRANSLATE:
|
||||
snap = config.snapTranslation;
|
||||
ImGui::InputFloat3("Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::ROTATE:
|
||||
snap = config.snapRotation;
|
||||
ImGui::InputFloat("Angle Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::SCALE:
|
||||
snap = config.snapScale;
|
||||
ImGui::InputFloat("Scale Snap", &snap.x);
|
||||
break;
|
||||
}
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
|
||||
ImGuizmo::Manipulate(&camera.view[0][0], &camera.projection[0][0],
|
||||
mCurrentGizmoOperation, mCurrentGizmoMode, &matrix[0][0],
|
||||
NULL, useSnap ? &snap.x : NULL);
|
||||
ImGuizmo::Manipulate(&camera.view[0][0], &camera.projection[0][0], mCurrentGizmoOperation, mCurrentGizmoMode, &matrix[0][0], NULL, useSnap ? &snap.x : NULL);
|
||||
}
|
||||
|
||||
void DrawMesh(const example::Mesh<float> *mesh) {
|
||||
void DrawMesh(const example::Mesh<float> *mesh)
|
||||
{
|
||||
// TODO(LTE): Use vertex array or use display list.
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
@@ -590,17 +579,20 @@ void DrawMesh(const example::Mesh<float> *mesh) {
|
||||
glNormal3f(mesh->facevarying_normals[9 * i + 0],
|
||||
mesh->facevarying_normals[9 * i + 1],
|
||||
mesh->facevarying_normals[9 * i + 2]);
|
||||
glVertex3f(mesh->vertices[3 * f0 + 0], mesh->vertices[3 * f0 + 1],
|
||||
glVertex3f(mesh->vertices[3 * f0 + 0],
|
||||
mesh->vertices[3 * f0 + 1],
|
||||
mesh->vertices[3 * f0 + 2]);
|
||||
glNormal3f(mesh->facevarying_normals[9 * i + 3],
|
||||
mesh->facevarying_normals[9 * i + 4],
|
||||
mesh->facevarying_normals[9 * i + 5]);
|
||||
glVertex3f(mesh->vertices[3 * f1 + 0], mesh->vertices[3 * f1 + 1],
|
||||
glVertex3f(mesh->vertices[3 * f1 + 0],
|
||||
mesh->vertices[3 * f1 + 1],
|
||||
mesh->vertices[3 * f1 + 2]);
|
||||
glNormal3f(mesh->facevarying_normals[9 * i + 6],
|
||||
mesh->facevarying_normals[9 * i + 7],
|
||||
mesh->facevarying_normals[9 * i + 8]);
|
||||
glVertex3f(mesh->vertices[3 * f2 + 0], mesh->vertices[3 * f2 + 1],
|
||||
glVertex3f(mesh->vertices[3 * f2 + 0],
|
||||
mesh->vertices[3 * f2 + 1],
|
||||
mesh->vertices[3 * f2 + 2]);
|
||||
}
|
||||
|
||||
@@ -610,19 +602,23 @@ void DrawMesh(const example::Mesh<float> *mesh) {
|
||||
unsigned int f1 = mesh->faces[3 * i + 1];
|
||||
unsigned int f2 = mesh->faces[3 * i + 2];
|
||||
|
||||
glVertex3f(mesh->vertices[3 * f0 + 0], mesh->vertices[3 * f0 + 1],
|
||||
glVertex3f(mesh->vertices[3 * f0 + 0],
|
||||
mesh->vertices[3 * f0 + 1],
|
||||
mesh->vertices[3 * f0 + 2]);
|
||||
glVertex3f(mesh->vertices[3 * f1 + 0], mesh->vertices[3 * f1 + 1],
|
||||
glVertex3f(mesh->vertices[3 * f1 + 0],
|
||||
mesh->vertices[3 * f1 + 1],
|
||||
mesh->vertices[3 * f1 + 2]);
|
||||
glVertex3f(mesh->vertices[3 * f2 + 0], mesh->vertices[3 * f2 + 1],
|
||||
glVertex3f(mesh->vertices[3 * f2 + 0],
|
||||
mesh->vertices[3 * f2 + 1],
|
||||
mesh->vertices[3 * f2 + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
glEnd();
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void DrawNode(const nanosg::Node<float, example::Mesh<float> > &node) {
|
||||
void DrawNode(const nanosg::Node<float, example::Mesh<float> > &node)
|
||||
{
|
||||
glPushMatrix();
|
||||
glMultMatrixf(node.GetLocalXformPtr());
|
||||
|
||||
@@ -638,8 +634,8 @@ void DrawNode(const nanosg::Node<float, example::Mesh<float> > &node) {
|
||||
}
|
||||
|
||||
// Draw scene with OpenGL
|
||||
void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene,
|
||||
const Camera &camera) {
|
||||
void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene, const Camera &camera)
|
||||
{
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
@@ -653,12 +649,11 @@ void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene,
|
||||
const float light_diffuse[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, &light0_pos[0]);
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, &light_diffuse[0]);
|
||||
glLightfv(GL_LIGHT0, GL_DIFFUSE, &light_diffuse[0]);
|
||||
glLightfv(GL_LIGHT1, GL_POSITION, &light1_pos[0]);
|
||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, &light_diffuse[0]);
|
||||
glLightfv(GL_LIGHT1, GL_DIFFUSE, &light_diffuse[0]);
|
||||
|
||||
const std::vector<nanosg::Node<float, example::Mesh<float> > > &root_nodes =
|
||||
scene.GetNodes();
|
||||
const std::vector<nanosg::Node<float, example::Mesh<float> > > &root_nodes = scene.GetNodes();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
@@ -680,12 +675,15 @@ void DrawScene(const nanosg::Scene<float, example::Mesh<float> > &scene,
|
||||
glDisable(GL_LIGHT1);
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
}
|
||||
|
||||
void BuildSceneItems(std::vector<std::string> *display_names,
|
||||
std::vector<std::string> *names,
|
||||
const nanosg::Node<float, example::Mesh<float> > &node,
|
||||
int indent) {
|
||||
void BuildSceneItems(
|
||||
std::vector<std::string> *display_names,
|
||||
std::vector<std::string> *names,
|
||||
const nanosg::Node<float, example::Mesh<float> > &node,
|
||||
int indent)
|
||||
{
|
||||
if (node.GetName().empty()) {
|
||||
// Skip a node with empty name.
|
||||
return;
|
||||
@@ -701,16 +699,14 @@ void BuildSceneItems(std::vector<std::string> *display_names,
|
||||
|
||||
display_names->push_back(display_name);
|
||||
names->push_back(node.GetName());
|
||||
|
||||
|
||||
for (size_t i = 0; i < node.GetChildren().size(); i++) {
|
||||
BuildSceneItems(display_names, names, node.GetChildren()[i], indent + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// tigra: add default material
|
||||
example::Material default_material;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(int argc, char** argv) {
|
||||
std::string config_filename = "config.json";
|
||||
|
||||
if (argc > 1) {
|
||||
@@ -733,47 +729,13 @@ int main(int argc, char **argv) {
|
||||
std::vector<example::Material> materials;
|
||||
std::vector<example::Texture> textures;
|
||||
|
||||
// tigra: set default material to 95% white diffuse
|
||||
default_material.diffuse[0] = 0.95f;
|
||||
default_material.diffuse[1] = 0.95f;
|
||||
default_material.diffuse[2] = 0.95f;
|
||||
|
||||
default_material.specular[0] = 0;
|
||||
default_material.specular[1] = 0;
|
||||
default_material.specular[2] = 0;
|
||||
|
||||
// Material pushed as first material on the list
|
||||
materials.push_back(default_material);
|
||||
|
||||
if (!gRenderConfig.obj_filename.empty()) {
|
||||
bool ret = LoadObj(gRenderConfig.obj_filename, gRenderConfig.scene_scale,
|
||||
&meshes, &materials, &textures);
|
||||
if (!ret) {
|
||||
std::cerr << "Failed to load .obj [ " << gRenderConfig.obj_filename
|
||||
<< " ]" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gRenderConfig.gltf_filename.empty()) {
|
||||
std::cout << "Found gltf file : " << gRenderConfig.gltf_filename << "\n";
|
||||
|
||||
bool ret =
|
||||
LoadGLTF(gRenderConfig.gltf_filename, gRenderConfig.scene_scale,
|
||||
&meshes, &materials, &textures);
|
||||
if (!ret) {
|
||||
std::cerr << "Failed to load glTF file [ "
|
||||
<< gRenderConfig.gltf_filename << " ]" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (textures.size() > 0) {
|
||||
materials[0].diffuse_texid = 0;
|
||||
bool ret = LoadGLTF(gRenderConfig.gltf_filename, gRenderConfig.scene_scale, &meshes, &materials, &textures);
|
||||
if (!ret) {
|
||||
std::cerr << "Failed to load glTF [ " << gRenderConfig.gltf_filename << " ]" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
gAsset.materials = materials;
|
||||
gAsset.default_material = default_material;
|
||||
gAsset.textures = textures;
|
||||
|
||||
for (size_t n = 0; n < meshes.size(); n++) {
|
||||
@@ -782,20 +744,12 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < gAsset.meshes.size(); n++) {
|
||||
|
||||
nanosg::Node<float, example::Mesh<float> > node(&gAsset.meshes[n]);
|
||||
|
||||
// case where the name of a mesh isn't defined in the loaded file
|
||||
if (gAsset.meshes[n].name.empty()) {
|
||||
std::string generatedName = "unnamed_" + std::to_string(n);
|
||||
gAsset.meshes[n].name = generatedName;
|
||||
meshes[n].name = generatedName;
|
||||
}
|
||||
|
||||
node.SetName(meshes[n].name);
|
||||
node.SetLocalXform(meshes[n].pivot_xform); // Use mesh's pivot transform
|
||||
// as node's local transform.
|
||||
node.SetLocalXform(meshes[n].pivot_xform); // Use mesh's pivot transform as node's local transform.
|
||||
gNodes.push_back(node);
|
||||
|
||||
|
||||
gScene.AddNode(node);
|
||||
}
|
||||
|
||||
@@ -807,12 +761,11 @@ int main(int argc, char **argv) {
|
||||
float bmin[3], bmax[3];
|
||||
gScene.GetBoundingBox(bmin, bmax);
|
||||
printf(" # of nodes : %d\n", int(gNodes.size()));
|
||||
printf(" Scene Bmin : %f, %f, %f\n", bmin[0], bmin[1],
|
||||
bmin[2]);
|
||||
printf(" Scene Bmax : %f, %f, %f\n", bmax[0], bmax[1],
|
||||
bmax[2]);
|
||||
}
|
||||
printf(" Scene Bmin : %f, %f, %f\n", bmin[0], bmin[1], bmin[2]);
|
||||
printf(" Scene Bmax : %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);
|
||||
|
||||
}
|
||||
|
||||
std::vector<const char *> imgui_node_names;
|
||||
std::vector<std::string> display_node_names;
|
||||
std::vector<std::string> node_names;
|
||||
@@ -820,14 +773,13 @@ int main(int argc, char **argv) {
|
||||
|
||||
{
|
||||
for (size_t i = 0; i < gScene.GetNodes().size(); i++) {
|
||||
BuildSceneItems(&display_node_names, &node_names, gScene.GetNodes()[i],
|
||||
/* indent */ 0);
|
||||
BuildSceneItems(&display_node_names, &node_names, gScene.GetNodes()[i], /* indent */0);
|
||||
}
|
||||
|
||||
// List of strings for imgui.
|
||||
// Assume nodes in the scene does not change.
|
||||
for (size_t i = 0; i < display_node_names.size(); i++) {
|
||||
// std::cout << "name : " << display_node_names[i] << std::endl;
|
||||
//std::cout << "name : " << display_node_names[i] << std::endl;
|
||||
imgui_node_names.push_back(display_node_names[i].c_str());
|
||||
}
|
||||
|
||||
@@ -836,13 +788,13 @@ int main(int argc, char **argv) {
|
||||
nanosg::Node<float, example::Mesh<float> > *node;
|
||||
|
||||
if (gScene.FindNode(node_names[i], &node)) {
|
||||
// std::cout << "id : " << i << ", name : " << node_names[i] <<
|
||||
// std::endl;
|
||||
//std::cout << "id : " << i << ", name : " << node_names[i] << std::endl;
|
||||
node_map[i] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
window = new b3gDefaultOpenGLWindow;
|
||||
b3gWindowConstructionInfo ci;
|
||||
#ifdef USE_OPENGL2
|
||||
@@ -882,12 +834,11 @@ int main(int argc, char **argv) {
|
||||
window->setResizeCallback(resizeCallback);
|
||||
checkErrors("resize");
|
||||
|
||||
ImGui::CreateContext();
|
||||
ImGui_ImplBtGui_Init(window);
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontDefault();
|
||||
// io.Fonts->AddFontFromFileTTF("./DroidSans.ttf", 15.0f);
|
||||
//io.Fonts->AddFontFromFileTTF("./DroidSans.ttf", 15.0f);
|
||||
|
||||
glm::mat4 projection(1.0f);
|
||||
glm::mat4 view(1.0f);
|
||||
@@ -910,7 +861,7 @@ int main(int argc, char **argv) {
|
||||
ImGuizmo::BeginFrame();
|
||||
ImGuizmo::Enable(true);
|
||||
|
||||
// ImGuiIO &io = ImGui::GetIO();
|
||||
//ImGuiIO &io = ImGui::GetIO();
|
||||
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
|
||||
|
||||
ImGui::Begin("UI");
|
||||
@@ -946,7 +897,7 @@ int main(int argc, char **argv) {
|
||||
ImGui::InputFloat("show pos scale", &gShowPositionScale);
|
||||
|
||||
ImGui::InputFloat2("show depth range", gShowDepthRange);
|
||||
ImGui::Checkbox("show depth pseudo color", &gShowDepthPeseudoColor);
|
||||
ImGui::Checkbox("show depth pesudo color", &gShowDepthPeseudoColor);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
@@ -957,47 +908,38 @@ int main(int argc, char **argv) {
|
||||
|
||||
checkErrors("clear");
|
||||
|
||||
// fix max passes issue
|
||||
if (gShowBufferMode_prv != gShowBufferMode) {
|
||||
gRenderConfig.pass = 0;
|
||||
gShowBufferMode_prv = gShowBufferMode;
|
||||
}
|
||||
|
||||
// Render display window
|
||||
{
|
||||
static GLint gl_texid = -1;
|
||||
if (gl_texid < 0) {
|
||||
gl_texid = CreateDisplayTextureGL(NULL, gRenderConfig.width,
|
||||
gRenderConfig.height, 4);
|
||||
gl_texid = CreateDisplayTextureGL(NULL, gRenderConfig.width, gRenderConfig.height, 4);
|
||||
}
|
||||
|
||||
// Refresh texture until rendering finishes.
|
||||
if (gRenderConfig.pass < gRenderConfig.max_passes) {
|
||||
// FIXME(LTE): Do not update GL texture frequently.
|
||||
UpdateDisplayTextureGL(gl_texid, gRenderConfig.width,
|
||||
gRenderConfig.height);
|
||||
UpdateDisplayTextureGL(gl_texid, gRenderConfig.width, gRenderConfig.height);
|
||||
}
|
||||
|
||||
ImGui::Begin("Render");
|
||||
ImTextureID tex_id =
|
||||
reinterpret_cast<void *>(static_cast<intptr_t>(gl_texid));
|
||||
ImTextureID tex_id = reinterpret_cast<void *>(
|
||||
static_cast<intptr_t>(gl_texid));
|
||||
ImGui::Image(tex_id, ImVec2(256, 256), ImVec2(0, 0),
|
||||
ImVec2(1, 1)); // Setup camera and draw imguizomo
|
||||
ImVec2(1, 1));// Setup camera and draw imguizomo
|
||||
|
||||
ImGui::End();
|
||||
|
||||
}
|
||||
|
||||
// scene graph tree
|
||||
glm::mat4 node_matrix;
|
||||
static int node_selected_index = 0;
|
||||
static int node_selected = 0;
|
||||
{
|
||||
ImGui::Begin("Scene");
|
||||
|
||||
ImGui::ListBox("Node list", &node_selected_index, imgui_node_names.data(),
|
||||
imgui_node_names.size(), 16);
|
||||
|
||||
auto node_selected = node_map.at(node_selected_index);
|
||||
node_matrix = glm::make_mat4(node_selected->GetLocalXformPtr());
|
||||
ImGui::ListBox("Node list", &node_selected, imgui_node_names.data(), imgui_node_names.size(), 16);
|
||||
node_matrix = glm::make_mat4(node_map[node_selected]->GetLocalXformPtr());
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -1022,14 +964,11 @@ int main(int argc, char **argv) {
|
||||
up[2] = gRenderConfig.up[2];
|
||||
|
||||
// NOTE(LTE): w, then (x,y,z) for glm::quat.
|
||||
glm::quat rot =
|
||||
glm::quat(gCurrQuat[3], gCurrQuat[0], gCurrQuat[1], gCurrQuat[2]);
|
||||
glm::quat rot = glm::quat(gCurrQuat[3], gCurrQuat[0], gCurrQuat[1], gCurrQuat[2]);
|
||||
glm::mat4 rm = glm::mat4_cast(rot);
|
||||
|
||||
view = glm::lookAt(eye, lookat, up) * glm::inverse(glm::mat4_cast(rot));
|
||||
projection = glm::perspective(
|
||||
45.0f, float(window->getWidth()) / float(window->getHeight()), 0.01f,
|
||||
1000.0f);
|
||||
projection = glm::perspective (45.0f, float(window->getWidth()) / float(window->getHeight()), 0.01f, 1000.0f);
|
||||
|
||||
camera.view = view;
|
||||
camera.projection = projection;
|
||||
@@ -1039,10 +978,10 @@ int main(int argc, char **argv) {
|
||||
|
||||
float mat[4][4];
|
||||
memcpy(mat, &node_matrix[0][0], sizeof(float) * 16);
|
||||
node_map[node_selected_index]->SetLocalXform(mat);
|
||||
node_map[node_selected]->SetLocalXform(mat);
|
||||
|
||||
checkErrors("edit_transform");
|
||||
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,10 @@
|
||||
|
||||
#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 {
|
||||
|
||||
// TODO(syoyo): Support PBR material.
|
||||
|
||||
struct Material {
|
||||
// float ambient[3];
|
||||
float diffuse[3];
|
||||
@@ -58,7 +53,6 @@ struct Texture {
|
||||
int width;
|
||||
int height;
|
||||
int components;
|
||||
int _pad_;
|
||||
unsigned char* image;
|
||||
|
||||
Texture() {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace example {
|
||||
|
||||
@@ -70,13 +69,9 @@ inline void calculate_normal(T Nn[3], const T v0[3], const T v1[3], const T v2[3
|
||||
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> vertices; /// [xyz] * 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
|
||||
@@ -87,7 +82,6 @@ class Mesh {
|
||||
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. ---
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#include "nanort.h"
|
||||
@@ -44,13 +44,6 @@ THE SOFTWARE.
|
||||
|
||||
namespace nanort {
|
||||
|
||||
#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
|
||||
|
||||
// Parallelized BVH build is not yet fully tested,
|
||||
// thus turn off if you face a problem when building BVH.
|
||||
#define NANORT_ENABLE_PARALLEL_BUILD (1)
|
||||
@@ -308,7 +301,9 @@ class real3 {
|
||||
real3 operator/(const real3 &f2) const {
|
||||
return real3(x() / f2.x(), y() / f2.y(), z() / f2.z());
|
||||
}
|
||||
real3 operator-() const { return real3(-x(), -y(), -z()); }
|
||||
real3 operator-() const {
|
||||
return real3(-x(), -y(), -z());
|
||||
}
|
||||
T operator[](int i) const { return v[i]; }
|
||||
T &operator[](int i) { return v[i]; }
|
||||
|
||||
@@ -335,8 +330,8 @@ template <typename T>
|
||||
inline real3<T> vnormalize(const real3<T> &rhs) {
|
||||
real3<T> v = rhs;
|
||||
T len = vlength(rhs);
|
||||
if (std::fabs(len) > static_cast<T>(1.0e-6)) {
|
||||
T inv_len = static_cast<T>(1.0) / len;
|
||||
if (fabs(len) > 1.0e-6f) {
|
||||
float inv_len = 1.0f / len;
|
||||
v.v[0] *= inv_len;
|
||||
v.v[1] *= inv_len;
|
||||
v.v[2] *= inv_len;
|
||||
@@ -368,7 +363,9 @@ inline const real *get_vertex_addr(const real *p, const size_t idx,
|
||||
template <typename T = float>
|
||||
class Ray {
|
||||
public:
|
||||
Ray() : min_t(static_cast<T>(0.0)), max_t(std::numeric_limits<T>::max()) {
|
||||
Ray()
|
||||
: min_t(static_cast<T>(0.0))
|
||||
, max_t(std::numeric_limits<T>::max()) {
|
||||
org[0] = static_cast<T>(0.0);
|
||||
org[1] = static_cast<T>(0.0);
|
||||
org[2] = static_cast<T>(0.0);
|
||||
@@ -377,11 +374,11 @@ class Ray {
|
||||
dir[2] = static_cast<T>(-1.0);
|
||||
}
|
||||
|
||||
T org[3]; // must set
|
||||
T dir[3]; // must set
|
||||
T min_t; // minimum ray hit distance.
|
||||
T max_t; // maximum ray hit distance.
|
||||
T inv_dir[3]; // filled internally
|
||||
T org[3]; // must set
|
||||
T dir[3]; // must set
|
||||
T min_t; // minium ray hit distance.
|
||||
T max_t; // maximum ray hit distance.
|
||||
T inv_dir[3]; // filled internally
|
||||
int dir_sign[3]; // filled internally
|
||||
};
|
||||
|
||||
@@ -389,38 +386,6 @@ template <typename T = float>
|
||||
class BVHNode {
|
||||
public:
|
||||
BVHNode() {}
|
||||
BVHNode(const BVHNode &rhs) {
|
||||
bmin[0] = rhs.bmin[0];
|
||||
bmin[1] = rhs.bmin[1];
|
||||
bmin[2] = rhs.bmin[2];
|
||||
flag = rhs.flag;
|
||||
|
||||
bmax[0] = rhs.bmax[0];
|
||||
bmax[1] = rhs.bmax[1];
|
||||
bmax[2] = rhs.bmax[2];
|
||||
axis = rhs.axis;
|
||||
|
||||
data[0] = rhs.data[0];
|
||||
data[1] = rhs.data[1];
|
||||
}
|
||||
|
||||
BVHNode &operator=(const BVHNode &rhs) {
|
||||
bmin[0] = rhs.bmin[0];
|
||||
bmin[1] = rhs.bmin[1];
|
||||
bmin[2] = rhs.bmin[2];
|
||||
flag = rhs.flag;
|
||||
|
||||
bmax[0] = rhs.bmax[0];
|
||||
bmax[1] = rhs.bmax[1];
|
||||
bmax[2] = rhs.bmax[2];
|
||||
axis = rhs.axis;
|
||||
|
||||
data[0] = rhs.data[0];
|
||||
data[1] = rhs.data[1];
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
~BVHNode() {}
|
||||
|
||||
T bmin[3];
|
||||
@@ -515,26 +480,14 @@ class BBox {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class NodeHit {
|
||||
template<typename T>
|
||||
class NodeHit
|
||||
{
|
||||
public:
|
||||
NodeHit()
|
||||
: t_min(std::numeric_limits<T>::max()),
|
||||
t_max(-std::numeric_limits<T>::max()),
|
||||
node_id(static_cast<unsigned int>(-1)) {}
|
||||
|
||||
NodeHit(const NodeHit<T> &rhs) {
|
||||
t_min = rhs.t_min;
|
||||
t_max = rhs.t_max;
|
||||
node_id = rhs.node_id;
|
||||
}
|
||||
|
||||
NodeHit &operator=(const NodeHit<T> &rhs) {
|
||||
t_min = rhs.t_min;
|
||||
t_max = rhs.t_max;
|
||||
node_id = rhs.node_id;
|
||||
|
||||
return (*this);
|
||||
: t_min(std::numeric_limits<T>::max())
|
||||
, t_max(-std::numeric_limits<T>::max())
|
||||
, node_id(static_cast<unsigned int>(-1)) {
|
||||
}
|
||||
|
||||
~NodeHit() {}
|
||||
@@ -544,8 +497,9 @@ class NodeHit {
|
||||
unsigned int node_id;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class NodeHitComparator {
|
||||
template<typename T>
|
||||
class NodeHitComparator
|
||||
{
|
||||
public:
|
||||
inline bool operator()(const NodeHit<T> &a, const NodeHit<T> &b) {
|
||||
return a.t_min < b.t_min;
|
||||
@@ -558,37 +512,29 @@ class BVHAccel {
|
||||
BVHAccel() : pad0_(0) { (void)pad0_; }
|
||||
~BVHAccel() {}
|
||||
|
||||
///
|
||||
/// Build BVH for input primitives.
|
||||
///
|
||||
template <class P, class Pred>
|
||||
bool Build(const unsigned int num_primitives, const P &p, const Pred &pred,
|
||||
const BVHBuildOptions<T> &options = BVHBuildOptions<T>());
|
||||
template<class P, class Pred>
|
||||
bool Build(const unsigned int num_primitives,
|
||||
const P &p, const Pred &pred, const BVHBuildOptions<T> &options = BVHBuildOptions<T>());
|
||||
|
||||
///
|
||||
/// Get statistics of built BVH tree. Valid after Build()
|
||||
///
|
||||
BVHBuildStatistics GetStatistics() const { return stats_; }
|
||||
|
||||
///
|
||||
/// Dump built BVH to the file.
|
||||
///
|
||||
bool Dump(const char *filename);
|
||||
|
||||
///
|
||||
/// Load BVH binary
|
||||
///
|
||||
bool Load(const char *filename);
|
||||
|
||||
void Debug();
|
||||
|
||||
///
|
||||
/// Traverse into BVH along ray and find closest hit point & primitive if
|
||||
/// found
|
||||
///
|
||||
template <class I, class H>
|
||||
bool Traverse(const Ray<T> &ray, const I &intersector, H *isect,
|
||||
const BVHTraceOptions &options = BVHTraceOptions()) const;
|
||||
template<class I, class H>
|
||||
bool Traverse(const Ray<T> &ray,
|
||||
const I &intersector,
|
||||
H *isect,
|
||||
const BVHTraceOptions& options = BVHTraceOptions()) const;
|
||||
|
||||
#if 0
|
||||
/// Multi-hit ray traversal
|
||||
@@ -605,17 +551,16 @@ class BVHAccel {
|
||||
/// List up nodes which intersects along the ray.
|
||||
/// This function is useful for two-level BVH traversal.
|
||||
///
|
||||
template <class I>
|
||||
bool ListNodeIntersections(const Ray<T> &ray, int max_intersections,
|
||||
template<class I>
|
||||
bool ListNodeIntersections(const Ray<T> &ray,
|
||||
int max_intersections,
|
||||
const I &intersector,
|
||||
StackVector<NodeHit<T>, 128> *hits) const;
|
||||
|
||||
const std::vector<BVHNode<T> > &GetNodes() const { return nodes_; }
|
||||
const std::vector<unsigned int> &GetIndices() const { return indices_; }
|
||||
|
||||
///
|
||||
/// Returns bounding box of built BVH.
|
||||
///
|
||||
void BoundingBox(T bmin[3], T bmax[3]) const {
|
||||
if (nodes_.empty()) {
|
||||
bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<T>::max();
|
||||
@@ -644,7 +589,7 @@ class BVHAccel {
|
||||
std::vector<ShallowNodeInfo> shallow_node_infos_;
|
||||
|
||||
/// Builds shallow BVH tree recursively.
|
||||
template <class P, class Pred>
|
||||
template<class P, class Pred>
|
||||
unsigned int BuildShallowTree(std::vector<BVHNode<T> > *out_nodes,
|
||||
unsigned int left_idx, unsigned int right_idx,
|
||||
unsigned int depth,
|
||||
@@ -653,22 +598,22 @@ class BVHAccel {
|
||||
#endif
|
||||
|
||||
/// Builds BVH tree recursively.
|
||||
template <class P, class Pred>
|
||||
template<class P, class Pred>
|
||||
unsigned int BuildTree(BVHBuildStatistics *out_stat,
|
||||
std::vector<BVHNode<T> > *out_nodes,
|
||||
unsigned int left_idx, unsigned int right_idx,
|
||||
unsigned int depth, const P &p, const Pred &pred);
|
||||
|
||||
template <class I>
|
||||
template<class I>
|
||||
bool TestLeafNode(const BVHNode<T> &node, const Ray<T> &ray,
|
||||
const I &intersector) const;
|
||||
|
||||
template <class I>
|
||||
bool TestLeafNodeIntersections(
|
||||
const BVHNode<T> &node, const Ray<T> &ray, const int max_intersections,
|
||||
const I &intersector,
|
||||
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >,
|
||||
NodeHitComparator<T> > *isect_pq) const;
|
||||
template<class I>
|
||||
bool TestLeafNodeIntersections(const BVHNode<T> &node,
|
||||
const Ray<T> &ray,
|
||||
const int max_intersections,
|
||||
const I &intersector,
|
||||
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >, NodeHitComparator<T> > *isect_pq) const;
|
||||
|
||||
#if 0
|
||||
template<class I, class H, class Comp>
|
||||
@@ -718,7 +663,7 @@ class TriangleSAHPred {
|
||||
|
||||
T center = p0[axis] + p1[axis] + p2[axis];
|
||||
|
||||
return (center < pos * static_cast<T>(3.0));
|
||||
return (center < pos * 3.0);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -817,7 +762,7 @@ class TriangleIntersector {
|
||||
/// distance `t`,
|
||||
/// varycentric coordinate `u` and `v`.
|
||||
/// Returns true if there's intersection.
|
||||
bool Intersect(T *t_inout, const unsigned int prim_index) const {
|
||||
bool Intersect(T *t_inout, unsigned int prim_index) const {
|
||||
if ((prim_index < trace_options_.prim_ids_range[0]) ||
|
||||
(prim_index >= trace_options_.prim_ids_range[1])) {
|
||||
return false;
|
||||
@@ -846,13 +791,8 @@ class TriangleIntersector {
|
||||
T V = Ax * Cy - Ay * Cx;
|
||||
T W = Bx * Ay - By * Ax;
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
|
||||
// Fall back to test against edges using double precision.
|
||||
if (U == static_cast<T>(0.0) || V == static_cast<T>(0.0) || W == static_cast<T>(0.0)) {
|
||||
if (U == 0.0 || V == 0.0 || W == 0.0) {
|
||||
double CxBy = static_cast<double>(Cx) * static_cast<double>(By);
|
||||
double CyBx = static_cast<double>(Cy) * static_cast<double>(Bx);
|
||||
U = static_cast<T>(CxBy - CyBx);
|
||||
@@ -867,19 +807,15 @@ class TriangleIntersector {
|
||||
}
|
||||
|
||||
if (trace_options_.cull_back_face) {
|
||||
if (U < static_cast<T>(0.0) || V < static_cast<T>(0.0) || W < static_cast<T>(0.0)) return false;
|
||||
if (U < 0.0 || V < 0.0 || W < 0.0) return false;
|
||||
} else {
|
||||
if ((U < static_cast<T>(0.0) || V < static_cast<T>(0.0) || W < static_cast<T>(0.0)) && (U > static_cast<T>(0.0) || V > static_cast<T>(0.0) || W > static_cast<T>(0.0))) {
|
||||
if ((U < 0.0 || V < 0.0 || W < 0.0) && (U > 0.0 || V > 0.0 || W > 0.0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
T det = U + V + W;
|
||||
if (det == static_cast<T>(0.0)) return false;
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
if (det == 0.0) return false;
|
||||
|
||||
const T Az = ray_coeff_.Sz * A[ray_coeff_.kz];
|
||||
const T Bz = ray_coeff_.Sz * B[ray_coeff_.kz];
|
||||
@@ -893,10 +829,6 @@ class TriangleIntersector {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tt < t_min_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(*t_inout) = tt;
|
||||
// Use Thomas-Mueller style barycentric coord.
|
||||
// U + V + W = 1.0 and interp(p) = U * p0 + V * p1 + W * p2
|
||||
@@ -952,8 +884,6 @@ class TriangleIntersector {
|
||||
|
||||
trace_options_ = trace_options;
|
||||
|
||||
t_min_ = ray.min_t;
|
||||
|
||||
u_ = 0.0f;
|
||||
v_ = 0.0f;
|
||||
}
|
||||
@@ -971,20 +901,19 @@ class TriangleIntersector {
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const T *vertices_;
|
||||
const unsigned int *faces_;
|
||||
const size_t vertex_stride_bytes_;
|
||||
|
||||
|
||||
mutable real3<T> ray_org_;
|
||||
mutable RayCoeff ray_coeff_;
|
||||
mutable BVHTraceOptions trace_options_;
|
||||
mutable T t_min_;
|
||||
|
||||
mutable T t_;
|
||||
mutable T u_;
|
||||
mutable T v_;
|
||||
mutable T t_;
|
||||
mutable T u_;
|
||||
mutable T v_;
|
||||
mutable unsigned int prim_id_;
|
||||
int _pad_;
|
||||
};
|
||||
|
||||
//
|
||||
@@ -1021,8 +950,7 @@ struct BinBuffer {
|
||||
template <typename T>
|
||||
inline T CalculateSurfaceArea(const real3<T> &min, const real3<T> &max) {
|
||||
real3<T> box = max - min;
|
||||
return static_cast<T>(2.0) *
|
||||
(box[0] * box[1] + box[1] * box[2] + box[2] * box[0]);
|
||||
return static_cast<T>(2.0) * (box[0] * box[1] + box[1] * box[2] + box[2] * box[0]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -1066,12 +994,12 @@ inline void ContributeBinBuffer(BinBuffer *bins, // [out]
|
||||
real3<T> scene_size, scene_inv_size;
|
||||
scene_size = scene_max - scene_min;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
assert(scene_size[i] >= static_cast<T>(0.0));
|
||||
assert(scene_size[i] >= 0.0);
|
||||
|
||||
if (scene_size[i] > static_cast<T>(0.0)) {
|
||||
if (scene_size[i] > 0.0) {
|
||||
scene_inv_size[i] = bin_size / scene_size[i];
|
||||
} else {
|
||||
scene_inv_size[i] = static_cast<T>(0.0);
|
||||
scene_inv_size[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1129,8 +1057,7 @@ inline T SAH(size_t ns1, T leftArea, size_t ns2, T rightArea, T invS, T Taabb,
|
||||
T Ttri) {
|
||||
T sah;
|
||||
|
||||
sah = static_cast<T>(2.0) * Taabb +
|
||||
(leftArea * invS) * static_cast<T>(ns1) * Ttri +
|
||||
sah = static_cast<T>(2.0) * Taabb + (leftArea * invS) * static_cast<T>(ns1) * Ttri +
|
||||
(rightArea * invS) * static_cast<T>(ns2) * Ttri;
|
||||
|
||||
return sah;
|
||||
@@ -1152,22 +1079,22 @@ inline bool FindCutFromBinBuffer(T *cut_pos, // [out] xyz
|
||||
T pos;
|
||||
T minCost[3];
|
||||
|
||||
T costTtri = static_cast<T>(1.0) - costTaabb;
|
||||
T costTtri = 1.0f - costTaabb;
|
||||
|
||||
(*minCostAxis) = 0;
|
||||
|
||||
bsize = bmax - bmin;
|
||||
bstep = bsize * (static_cast<T>(1.0) / bins->bin_size);
|
||||
bstep = bsize * (1.0f / bins->bin_size);
|
||||
saTotal = CalculateSurfaceArea(bmin, bmax);
|
||||
|
||||
T invSaTotal = static_cast<T>(0.0);
|
||||
T invSaTotal = 0.0f;
|
||||
if (saTotal > kEPS) {
|
||||
invSaTotal = static_cast<T>(1.0) / saTotal;
|
||||
invSaTotal = 1.0f / saTotal;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
//
|
||||
// Compute SAH cost for the right side of each cell of the bbox.
|
||||
// Compute SAH cost for right side of each cell of the bbox.
|
||||
// Exclude both extreme side of the bbox.
|
||||
//
|
||||
// i: 0 1 2 3
|
||||
@@ -1176,7 +1103,7 @@ inline bool FindCutFromBinBuffer(T *cut_pos, // [out] xyz
|
||||
// +----+----+----+----+----+
|
||||
//
|
||||
|
||||
T minCostPos = bmin[j] + static_cast<T>(1.0) * bstep[j];
|
||||
T minCostPos = bmin[j] + 0.5f * bstep[j];
|
||||
minCost[j] = std::numeric_limits<T>::max();
|
||||
|
||||
left = 0;
|
||||
@@ -1200,7 +1127,7 @@ inline bool FindCutFromBinBuffer(T *cut_pos, // [out] xyz
|
||||
// +1 for i since we want a position on right side of the cell.
|
||||
//
|
||||
|
||||
pos = bmin[j] + (i + static_cast<T>(1.0)) * bstep[j];
|
||||
pos = bmin[j] + (i + 0.5f) * bstep[j];
|
||||
bmaxLeft[j] = pos;
|
||||
bminRight[j] = pos;
|
||||
|
||||
@@ -1353,14 +1280,11 @@ inline void GetBoundingBox(real3<T> *bmin, real3<T> *bmax,
|
||||
//
|
||||
|
||||
#if NANORT_ENABLE_PARALLEL_BUILD
|
||||
template <typename T>
|
||||
template <class P, class Pred>
|
||||
unsigned int BVHAccel<T>::BuildShallowTree(std::vector<BVHNode<T> > *out_nodes,
|
||||
unsigned int left_idx,
|
||||
unsigned int right_idx,
|
||||
unsigned int depth,
|
||||
unsigned int max_shallow_depth,
|
||||
const P &p, const Pred &pred) {
|
||||
template <typename T> template<class P, class Pred>
|
||||
unsigned int BVHAccel<T>::BuildShallowTree(
|
||||
std::vector<BVHNode<T> > *out_nodes, unsigned int left_idx,
|
||||
unsigned int right_idx, unsigned int depth, unsigned int max_shallow_depth,
|
||||
const P &p, const Pred &pred) {
|
||||
assert(left_idx <= right_idx);
|
||||
|
||||
unsigned int offset = static_cast<unsigned int>(out_nodes->size());
|
||||
@@ -1500,13 +1424,11 @@ unsigned int BVHAccel<T>::BuildShallowTree(std::vector<BVHNode<T> > *out_nodes,
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
template <class P, class Pred>
|
||||
unsigned int BVHAccel<T>::BuildTree(BVHBuildStatistics *out_stat,
|
||||
std::vector<BVHNode<T> > *out_nodes,
|
||||
unsigned int left_idx,
|
||||
unsigned int right_idx, unsigned int depth,
|
||||
const P &p, const Pred &pred) {
|
||||
template <typename T> template<class P, class Pred>
|
||||
unsigned int BVHAccel<T>::BuildTree(
|
||||
BVHBuildStatistics *out_stat, std::vector<BVHNode<T> > *out_nodes,
|
||||
unsigned int left_idx, unsigned int right_idx, unsigned int depth,
|
||||
const P &p, const Pred &pred) {
|
||||
assert(left_idx <= right_idx);
|
||||
|
||||
unsigned int offset = static_cast<unsigned int>(out_nodes->size());
|
||||
@@ -1632,10 +1554,10 @@ unsigned int BVHAccel<T>::BuildTree(BVHBuildStatistics *out_stat,
|
||||
return offset;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <class P, class Pred>
|
||||
bool BVHAccel<T>::Build(unsigned int num_primitives, const P &p,
|
||||
const Pred &pred, const BVHBuildOptions<T> &options) {
|
||||
template <typename T> template<class P, class Pred>
|
||||
bool BVHAccel<T>::Build(unsigned int num_primitives,
|
||||
const P &p, const Pred &pred,
|
||||
const BVHBuildOptions<T> &options) {
|
||||
options_ = options;
|
||||
stats_ = BVHBuildStatistics();
|
||||
|
||||
@@ -1644,10 +1566,6 @@ bool BVHAccel<T>::Build(unsigned int num_primitives, const P &p,
|
||||
|
||||
assert(options_.bin_size > 1);
|
||||
|
||||
if (num_primitives == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int n = num_primitives;
|
||||
|
||||
//
|
||||
@@ -1779,9 +1697,15 @@ void BVHAccel<T>::Debug() {
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nodes_.size(); i++) {
|
||||
printf("node[%d] : bmin %f, %f, %f, bmax %f, %f, %f\n", int(i),
|
||||
nodes_[i].bmin[0], nodes_[i].bmin[1], nodes_[i].bmin[1],
|
||||
nodes_[i].bmax[0], nodes_[i].bmax[1], nodes_[i].bmax[1]);
|
||||
printf("node[%d] : bmin %f, %f, %f, bmax %f, %f, %f\n",
|
||||
int(i),
|
||||
nodes_[i].bmin[0],
|
||||
nodes_[i].bmin[1],
|
||||
nodes_[i].bmin[1],
|
||||
nodes_[i].bmax[0],
|
||||
nodes_[i].bmax[1],
|
||||
nodes_[i].bmax[1]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1789,7 +1713,7 @@ template <typename T>
|
||||
bool BVHAccel<T>::Dump(const char *filename) {
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
if (!fp) {
|
||||
// fprintf(stderr, "[BVHAccel] Cannot write a file: %s\n", filename);
|
||||
//fprintf(stderr, "[BVHAccel] Cannot write a file: %s\n", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1820,7 +1744,7 @@ template <typename T>
|
||||
bool BVHAccel<T>::Load(const char *filename) {
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
if (!fp) {
|
||||
// fprintf(stderr, "Cannot open file: %s\n", filename);
|
||||
//fprintf(stderr, "Cannot open file: %s\n", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1891,10 +1815,10 @@ inline bool IntersectRayAABB(T *tminOut, // [out]
|
||||
return false; // no hit
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <class I>
|
||||
inline bool BVHAccel<T>::TestLeafNode(const BVHNode<T> &node, const Ray<T> &ray,
|
||||
const I &intersector) const {
|
||||
template <typename T> template<class I>
|
||||
inline bool BVHAccel<T>::TestLeafNode(const BVHNode<T> &node,
|
||||
const Ray<T> &ray,
|
||||
const I &intersector) const {
|
||||
bool hit = false;
|
||||
|
||||
unsigned int num_primitives = node.data[0];
|
||||
@@ -1917,18 +1841,20 @@ inline bool BVHAccel<T>::TestLeafNode(const BVHNode<T> &node, const Ray<T> &ray,
|
||||
|
||||
T local_t = t;
|
||||
if (intersector.Intersect(&local_t, prim_idx)) {
|
||||
// Update isect state
|
||||
t = local_t;
|
||||
if (local_t > ray.min_t) {
|
||||
// Update isect state
|
||||
t = local_t;
|
||||
|
||||
intersector.Update(t, prim_idx);
|
||||
hit = true;
|
||||
intersector.Update(t, prim_idx);
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
#if 0 // TODO(LTE): Implement
|
||||
#if 0 // TODO(LTE): Implement
|
||||
template <typename T> template<class I, class H, class Comp>
|
||||
bool BVHAccel<T>::MultiHitTestLeafNode(
|
||||
std::priority_queue<H, std::vector<H>, Comp> *isect_pq,
|
||||
@@ -2002,10 +1928,11 @@ bool BVHAccel<T>::MultiHitTestLeafNode(
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
template <class I, class H>
|
||||
bool BVHAccel<T>::Traverse(const Ray<T> &ray, const I &intersector, H *isect,
|
||||
const BVHTraceOptions &options) const {
|
||||
template <typename T> template<class I, class H>
|
||||
bool BVHAccel<T>::Traverse(const Ray<T> &ray,
|
||||
const I &intersector,
|
||||
H *isect,
|
||||
const BVHTraceOptions &options) const {
|
||||
const int kMaxStackDepth = 512;
|
||||
|
||||
T hit_t = ray.max_t;
|
||||
@@ -2072,14 +1999,13 @@ bool BVHAccel<T>::Traverse(const Ray<T> &ray, const I &intersector, H *isect,
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <class I>
|
||||
inline bool BVHAccel<T>::TestLeafNodeIntersections(
|
||||
const BVHNode<T> &node, const Ray<T> &ray, const int max_intersections,
|
||||
const I &intersector,
|
||||
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >,
|
||||
NodeHitComparator<T> > *isect_pq) const {
|
||||
|
||||
template <typename T> template<class I>
|
||||
inline bool BVHAccel<T>::TestLeafNodeIntersections(const BVHNode<T> &node,
|
||||
const Ray<T> &ray,
|
||||
const int max_intersections,
|
||||
const I &intersector,
|
||||
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >, NodeHitComparator<T> > *isect_pq) const {
|
||||
bool hit = false;
|
||||
|
||||
unsigned int num_primitives = node.data[0];
|
||||
@@ -2117,6 +2043,7 @@ inline bool BVHAccel<T>::TestLeafNodeIntersections(
|
||||
isect_pq->pop();
|
||||
|
||||
isect_pq->push(isect);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2125,11 +2052,11 @@ inline bool BVHAccel<T>::TestLeafNodeIntersections(
|
||||
return hit;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <class I>
|
||||
bool BVHAccel<T>::ListNodeIntersections(
|
||||
const Ray<T> &ray, int max_intersections, const I &intersector,
|
||||
StackVector<NodeHit<T>, 128> *hits) const {
|
||||
template <typename T> template<class I>
|
||||
bool BVHAccel<T>::ListNodeIntersections(const Ray<T> &ray,
|
||||
int max_intersections,
|
||||
const I &intersector,
|
||||
StackVector<NodeHit<T>, 128> *hits) const {
|
||||
const int kMaxStackDepth = 512;
|
||||
|
||||
T hit_t = ray.max_t;
|
||||
@@ -2139,19 +2066,14 @@ bool BVHAccel<T>::ListNodeIntersections(
|
||||
node_stack[0] = 0;
|
||||
|
||||
// Stores furthest intersection at top
|
||||
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >,
|
||||
NodeHitComparator<T> >
|
||||
isect_pq;
|
||||
std::priority_queue<NodeHit<T>, std::vector<NodeHit<T> >, NodeHitComparator<T> > isect_pq;
|
||||
|
||||
(*hits)->clear();
|
||||
|
||||
int dir_sign[3];
|
||||
dir_sign[0] =
|
||||
ray.dir[0] < static_cast<T>(0.0) ? 1 : 0;
|
||||
dir_sign[1] =
|
||||
ray.dir[1] < static_cast<T>(0.0) ? 1 : 0;
|
||||
dir_sign[2] =
|
||||
ray.dir[2] < static_cast<T>(0.0) ? 1 : 0;
|
||||
dir_sign[0] = ray.dir[0] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
|
||||
dir_sign[1] = ray.dir[1] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
|
||||
dir_sign[2] = ray.dir[2] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
|
||||
|
||||
// @fixme { Check edge case; i.e., 1/0 }
|
||||
real3<T> ray_inv_dir;
|
||||
@@ -2186,8 +2108,7 @@ bool BVHAccel<T>::ListNodeIntersections(
|
||||
|
||||
} else { // leaf node
|
||||
if (hit) {
|
||||
TestLeafNodeIntersections(node, ray, max_intersections, intersector,
|
||||
&isect_pq);
|
||||
TestLeafNodeIntersections(node, ray, max_intersections, intersector, &isect_pq);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2211,7 +2132,7 @@ bool BVHAccel<T>::ListNodeIntersections(
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0 // TODO(LTE): Implement
|
||||
#if 0 // TODO(LTE): Implement
|
||||
template <typename T> template<class I, class H, class Comp>
|
||||
bool BVHAccel<T>::MultiHitTraverse(const Ray<T> &ray,
|
||||
int max_intersections,
|
||||
@@ -2304,10 +2225,6 @@ bool BVHAccel<T>::MultiHitTraverse(const Ray<T> &ray,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace nanort
|
||||
|
||||
#endif // NANORT_H_
|
||||
|
||||
@@ -31,36 +31,35 @@ THE SOFTWARE.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include "nanort.h"
|
||||
|
||||
namespace nanosg {
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
class PrimitiveInterface;
|
||||
|
||||
template <class T>
|
||||
class PrimitiveInterface {
|
||||
public:
|
||||
void print() { static_cast<T &>(this)->print(); }
|
||||
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; }
|
||||
public:
|
||||
void print(){ std::cout << "Sphere" << std::endl; }
|
||||
};
|
||||
|
||||
// 4x4 matrix
|
||||
template <typename T>
|
||||
class Matrix {
|
||||
public:
|
||||
template <typename T> class Matrix {
|
||||
public:
|
||||
Matrix();
|
||||
~Matrix();
|
||||
|
||||
static void Print(const T m[4][4]) {
|
||||
static void Print(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]);
|
||||
}
|
||||
@@ -233,15 +232,18 @@ class Matrix {
|
||||
dst[1] = tmp[1];
|
||||
dst[2] = tmp[2];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// typedef Matrix<float> Matrixf;
|
||||
// typedef Matrix<double> Matrixd;
|
||||
//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]) {
|
||||
|
||||
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];
|
||||
|
||||
@@ -284,6 +286,7 @@ static void XformBoundingBox(T xbmin[3], // out
|
||||
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]);
|
||||
@@ -294,45 +297,48 @@ static void XformBoundingBox(T xbmin[3], // out
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Intersection {
|
||||
template<typename T>
|
||||
struct Intersection
|
||||
{
|
||||
// required fields.
|
||||
T t; // hit distance
|
||||
unsigned int prim_id; // primitive ID of the hit
|
||||
float u;
|
||||
float v;
|
||||
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
|
||||
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 {
|
||||
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();
|
||||
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();
|
||||
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);
|
||||
}
|
||||
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() {}
|
||||
}
|
||||
|
||||
~Node() {}
|
||||
|
||||
void Copy(const type &rhs) {
|
||||
Matrix<T>::Copy(local_xform_, rhs.local_xform_);
|
||||
@@ -363,44 +369,53 @@ class Node {
|
||||
children_ = rhs.children_;
|
||||
}
|
||||
|
||||
Node(const type &rhs) { Copy(rhs); }
|
||||
Node(const type &rhs) {
|
||||
Copy(rhs);
|
||||
}
|
||||
|
||||
const type &operator=(const type &rhs) {
|
||||
Copy(rhs);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
void SetName(const std::string &name) { name_ = name; }
|
||||
void SetName(const std::string &name) {
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
const std::string &GetName() const { return name_; }
|
||||
const std::string &GetName() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
///
|
||||
/// Add child node.
|
||||
///
|
||||
void AddChild(const type &child) { children_.push_back(child); }
|
||||
void AddChild(const type &child) {
|
||||
children_.push_back(child);
|
||||
}
|
||||
|
||||
///
|
||||
/// Get chidren
|
||||
///
|
||||
const std::vector<type> &GetChildren() const { return children_; }
|
||||
const std::vector<type> &GetChildren() const {
|
||||
return children_;
|
||||
}
|
||||
|
||||
std::vector<type> &GetChildren() { 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)) {
|
||||
|
||||
///
|
||||
/// 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);
|
||||
nanort::TriangleMesh<float> triangle_mesh(mesh_->vertices.data(), mesh_->faces.data(), sizeof(float) * 3);
|
||||
nanort::TriangleSAHPred<float> triangle_pred(mesh_->vertices.data(), mesh_->faces.data(), sizeof(float) * 3);
|
||||
|
||||
bool ret =
|
||||
accel_.Build(static_cast<unsigned int>(mesh_->faces.size()) / 3,
|
||||
triangle_mesh, triangle_pred);
|
||||
bool ret = accel_.Build(int(mesh_->faces.size()) / 3, triangle_mesh, triangle_pred);
|
||||
|
||||
// Update local bbox.
|
||||
if (ret) {
|
||||
@@ -418,7 +433,7 @@ class Node {
|
||||
Matrix<T>::Copy(inv_xform_, xform_);
|
||||
Matrix<T>::Inverse(inv_xform_);
|
||||
|
||||
// Clear translation, then inverse(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);
|
||||
@@ -433,61 +448,67 @@ class Node {
|
||||
for (size_t i = 0; i < children_.size(); i++) {
|
||||
children_[i].Update(xform_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Set local transformation.
|
||||
///
|
||||
///
|
||||
/// 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];
|
||||
const T *GetLocalXformPtr() const {
|
||||
return &local_xform_[0][0];
|
||||
}
|
||||
|
||||
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];
|
||||
const T *GetXformPtr() const {
|
||||
return &xform_[0][0];
|
||||
}
|
||||
|
||||
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)
|
||||
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];
|
||||
// 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_;
|
||||
@@ -495,16 +516,16 @@ class Node {
|
||||
const M *mesh_;
|
||||
|
||||
std::vector<type> children_;
|
||||
|
||||
};
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
// Predefined SAH predicator for cube.
|
||||
template <typename T, class M>
|
||||
template<typename T, class M>
|
||||
class NodeBBoxPred {
|
||||
public:
|
||||
NodeBBoxPred(const std::vector<Node<T, M> > *nodes)
|
||||
: axis_(0), pos_(0.0f), nodes_(nodes) {}
|
||||
NodeBBoxPred(const std::vector<Node<T, M> >* nodes) : axis_(0), pos_(0.0f), nodes_(nodes) {}
|
||||
|
||||
void Set(int axis, float pos) const {
|
||||
axis_ = axis;
|
||||
@@ -517,8 +538,8 @@ class NodeBBoxPred {
|
||||
|
||||
T bmin[3], bmax[3];
|
||||
|
||||
(*nodes_)[i].GetWorldBoundingBox(bmin, bmax);
|
||||
|
||||
(*nodes_)[i].GetWorldBoundingBox(bmin, bmax);
|
||||
|
||||
T center = bmax[axis] - bmin[axis];
|
||||
|
||||
return (center < pos);
|
||||
@@ -530,15 +551,15 @@ class NodeBBoxPred {
|
||||
const std::vector<Node<T, M> > *nodes_;
|
||||
};
|
||||
|
||||
template <typename T, class M>
|
||||
template<typename T, class M>
|
||||
class NodeBBoxGeometry {
|
||||
public:
|
||||
NodeBBoxGeometry(const std::vector<Node<T, M> > *nodes) : nodes_(nodes) {}
|
||||
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 {
|
||||
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];
|
||||
@@ -549,11 +570,10 @@ class NodeBBoxGeometry {
|
||||
(*bmax)[2] = b[2];
|
||||
}
|
||||
|
||||
const std::vector<Node<T, M> > *nodes_;
|
||||
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 {
|
||||
@@ -567,13 +587,14 @@ class NodeBBoxIntersection {
|
||||
unsigned int prim_id;
|
||||
};
|
||||
|
||||
template <typename T, class M>
|
||||
template<typename T, class M>
|
||||
class NodeBBoxIntersector {
|
||||
public:
|
||||
NodeBBoxIntersector(const std::vector<Node<T, M> > *nodes) : nodes_(nodes) {}
|
||||
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 {
|
||||
|
||||
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);
|
||||
@@ -613,7 +634,7 @@ class NodeBBoxIntersector {
|
||||
|
||||
/// 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 {
|
||||
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];
|
||||
@@ -627,37 +648,40 @@ class NodeBBoxIntersector {
|
||||
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;
|
||||
ray_dir_sign_[0] = ray.dir[0] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
|
||||
ray_dir_sign_[1] = ray.dir[1] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
|
||||
ray_dir_sign_[2] = ray.dir[2] < static_cast<T>(0.0) ? static_cast<T>(1) : static_cast<T>(0);
|
||||
}
|
||||
|
||||
const std::vector<Node<T, M> > *nodes_;
|
||||
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 {
|
||||
template<typename T, class M>
|
||||
class Scene
|
||||
{
|
||||
public:
|
||||
Scene() {
|
||||
Scene() {
|
||||
bmin_[0] = bmin_[1] = bmin_[2] = std::numeric_limits<T>::max();
|
||||
bmax_[0] = bmax_[1] = bmax_[2] = -std::numeric_limits<T>::max();
|
||||
}
|
||||
|
||||
~Scene() {}
|
||||
~Scene() {};
|
||||
|
||||
///
|
||||
/// Add intersectable node to the scene.
|
||||
///
|
||||
bool AddNode(const Node<T, M> &node) {
|
||||
bool AddNode(const Node<T, M> &node) {
|
||||
nodes_.push_back(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<Node<T, M> > &GetNodes() const { return nodes_; }
|
||||
const std::vector<Node<T, M> > &GetNodes() const {
|
||||
return nodes_;
|
||||
}
|
||||
|
||||
bool FindNode(const std::string &name, Node<T, M> **found_node) {
|
||||
if (!found_node) {
|
||||
@@ -678,15 +702,11 @@ class Scene {
|
||||
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++) {
|
||||
@@ -699,26 +719,21 @@ class Scene {
|
||||
// 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.
|
||||
|
||||
// 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);
|
||||
|
||||
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();
|
||||
//toplevel_accel_.Debug();
|
||||
|
||||
if (ret) {
|
||||
toplevel_accel_.BoundingBox(bmin_, bmax_);
|
||||
toplevel_accel_.BoundingBox(bmin_, bmax_);
|
||||
} else {
|
||||
// Set invalid bbox value.
|
||||
bmin_[0] = std::numeric_limits<T>::max();
|
||||
@@ -751,54 +766,51 @@ class 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 {
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
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.
|
||||
// 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);
|
||||
//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
|
||||
// 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);
|
||||
nanort::TriangleIntersector<T, H> triangle_intersector(node.GetMesh()->vertices.data(), node.GetMesh()->faces.data(), sizeof(T) * 3);
|
||||
H local_isect;
|
||||
|
||||
bool hit = node.GetAccel().Traverse(local_ray, triangle_intersector,
|
||||
&local_isect);
|
||||
bool hit = node.GetAccel().Traverse(local_ray, triangle_intersector, &local_isect);
|
||||
|
||||
if (hit) {
|
||||
// Calulcate hit distance in world coordiante.
|
||||
@@ -816,7 +828,6 @@ class Scene {
|
||||
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;
|
||||
@@ -828,30 +839,36 @@ class Scene {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
///
|
||||
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;
|
||||
@@ -865,18 +882,19 @@ class Scene {
|
||||
}
|
||||
|
||||
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_;
|
||||
std::vector<Node<T, M> > nodes_;
|
||||
};
|
||||
|
||||
} // namespace nanosg
|
||||
} // namespace nanosg
|
||||
|
||||
#endif // NANOSG_H_
|
||||
#endif // NANOSG_H_
|
||||
|
||||
@@ -1,458 +0,0 @@
|
||||
#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
|
||||
@@ -1,19 +0,0 @@
|
||||
#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_
|
||||
@@ -1,19 +1,12 @@
|
||||
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",
|
||||
@@ -23,7 +16,7 @@ sources = {
|
||||
"../common/imgui/ImGuizmo.cpp",
|
||||
}
|
||||
|
||||
solution "NanoSGSolution"
|
||||
solution "RaytraceSolution"
|
||||
configurations { "Release", "Debug" }
|
||||
|
||||
if os.is("Windows") then
|
||||
@@ -60,6 +53,7 @@ solution "NanoSGSolution"
|
||||
end
|
||||
|
||||
if os.is("Windows") then
|
||||
flags { "FatalCompileWarnings" }
|
||||
warnings "Extra" -- /W4
|
||||
|
||||
defines { "NOMINMAX" }
|
||||
|
||||
@@ -29,24 +29,12 @@ bool LoadRenderConfig(example::RenderConfig* config, const char* filename) {
|
||||
|
||||
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>()) {
|
||||
@@ -119,4 +107,4 @@ bool LoadRenderConfig(example::RenderConfig* config, const char* filename) {
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace example
|
||||
}
|
||||
|
||||
@@ -28,9 +28,7 @@ typedef struct {
|
||||
float *varycoordImage;
|
||||
|
||||
// Scene input info
|
||||
std::string obj_filename;
|
||||
std::string gltf_filename;
|
||||
std::string eson_filename;
|
||||
float scene_scale;
|
||||
|
||||
} RenderConfig;
|
||||
|
||||
@@ -200,7 +200,6 @@ void BuildCameraFrame(float3* origin, float3* corner, float3* u, float3* v,
|
||||
}
|
||||
}
|
||||
|
||||
#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) {
|
||||
@@ -218,12 +217,9 @@ nanort::Ray<float> GenerateRay(const float3& origin, const float3& corner,
|
||||
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;
|
||||
@@ -239,15 +235,10 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
|
||||
const nanosg::Scene<float, example::Mesh<float>> &scene,
|
||||
const example::Asset &asset,
|
||||
const RenderConfig& config,
|
||||
std::atomic<bool>& cancelFlag,
|
||||
int &_showBufferMode
|
||||
) {
|
||||
std::atomic<bool>& cancelFlag) {
|
||||
//if (!gAccel.IsValid()) {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
int width = config.width;
|
||||
int height = config.height;
|
||||
@@ -309,19 +300,6 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
|
||||
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);
|
||||
@@ -342,9 +320,6 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
|
||||
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] =
|
||||
@@ -435,49 +410,26 @@ bool Renderer::Render(float* rgba, float* aux_rgba, int* sample_counts,
|
||||
// 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];
|
||||
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];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
||||
// Simple shading
|
||||
float NdotV = fabsf(vdot(N, dir));
|
||||
|
||||
@@ -3,15 +3,6 @@
|
||||
|
||||
#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"
|
||||
@@ -22,9 +13,6 @@ namespace example {
|
||||
struct Asset {
|
||||
std::vector<Mesh<float> > meshes;
|
||||
std::vector<Material> materials;
|
||||
|
||||
//tigra: add default material
|
||||
Material default_material;
|
||||
std::vector<Texture> textures;
|
||||
};
|
||||
|
||||
@@ -35,10 +23,7 @@ class 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
|
||||
);
|
||||
const nanosg::Scene<float, Mesh<float>> &scene, const Asset &asset, const RenderConfig& config, std::atomic<bool>& cancel_flag);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
@@ -1,357 +0,0 @@
|
||||
# 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
|
||||
@@ -1,2 +0,0 @@
|
||||
all:
|
||||
clang++ -std=c++11 -I../../ -g -O1 -o saver main.cc
|
||||
@@ -1 +0,0 @@
|
||||
# Simple serialization API sample.
|
||||
@@ -1,53 +0,0 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
|
||||
//#define TINYGLTF_NO_STB_IMAGE_WRITE
|
||||
|
||||
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
// If using a modern Microsoft Compiler, this define supress compilation
|
||||
// warnings in stb_image_write
|
||||
//#define STBI_MSC_SECURE_CRT
|
||||
|
||||
#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 warn;
|
||||
std::string input_filename(argv[1]);
|
||||
std::string output_filename(argv[2]);
|
||||
std::string embedded_filename =
|
||||
output_filename.substr(0, output_filename.size() - 5) + "-Embedded.gltf";
|
||||
|
||||
// assume ascii glTF.
|
||||
bool ret = loader.LoadASCIIFromFile(&model, &err, &warn, input_filename.c_str());
|
||||
if (!warn.empty()) {
|
||||
std::cout << "warn : " << warn << std::endl;
|
||||
}
|
||||
if (!ret) {
|
||||
if (!err.empty()) {
|
||||
std::cerr << err << std::endl;
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
loader.WriteGltfSceneToFile(&model, output_filename);
|
||||
|
||||
// Embedd buffers and images
|
||||
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
|
||||
loader.WriteGltfSceneToFile(&model, embedded_filename, true, true);
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -2,20 +2,21 @@ project(tinygltf-validator CXX)
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
# Use C++11
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
# exe
|
||||
add_executable(tinygltf-validator
|
||||
tinygltf-validate.cc
|
||||
json11.cpp)
|
||||
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}/valijson/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/
|
||||
)
|
||||
${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
|
||||
@@ -44,10 +45,3 @@ endif()
|
||||
|
||||
# test-zone
|
||||
# enable_testing()
|
||||
|
||||
install ( TARGETS
|
||||
tinygltf-validator
|
||||
DESTINATION
|
||||
bin
|
||||
)
|
||||
|
||||
|
||||
22
examples/validator/LICENSE.json-schema-validator.MIT
Normal file
22
examples/validator/LICENSE.json-schema-validator.MIT
Normal file
@@ -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.
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2013 Dropbox, 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.
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Syoyo Fujita, Aurélien Chatelain and many contributors
|
||||
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
|
||||
@@ -1,4 +0,0 @@
|
||||
EXTRA_CXXFLAGS=-fsanitize=address
|
||||
|
||||
all:
|
||||
clang++ $(EXTRA_CXXFLAGS) -g -O1 -I. -Ivalijson/include tinygltf-validate.cc json11.cpp -o tinygltf-validate
|
||||
@@ -1,15 +1,42 @@
|
||||
Currently it causes stack-overflow in runtime.
|
||||
# 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 3.2 or later
|
||||
|
||||
## How to build
|
||||
|
||||
```
|
||||
$ ./tinygltf-validate schema_dir ../../models/Cube/Cube.gltf
|
||||
|
||||
...
|
||||
|
||||
==17033==ERROR: AddressSanitizer: stack-overflow on address 0x7fff55415d68 (pc 0x0000004c62de bp 0x7fff554165c0 sp 0x7fff55415d70 T0)
|
||||
#0 0x4c62dd in __asan_memset /tmp/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:27:3
|
||||
#1 0x53a7a7 in std::_Vector_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::_Vector_impl::_Vector_impl(std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&) /usr/lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:91:37
|
||||
#2 0x53a6f1 in std::_Vector_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::_Vector_base(unsigned long, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&) /usr/lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:135:9
|
||||
#3 0x53a9e2 in std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::vector(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&) /usr/lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:319:9
|
||||
#4 0x584313 in valijson::ValidationVisitor<valijson::adapters::Json11Adapter>::ValidationVisitor(valijson::ValidationVisitor<valijson::adapters::Json11Adapter> const&) /home/syoyo/work/tinygltf/examples/validator/valijson/include/valijson/validation_visitor.hpp:26:7
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
$ make
|
||||
```
|
||||
|
||||
## How to run
|
||||
|
||||
First clone glTF repo somewhere to get glTF schema JSON files.
|
||||
|
||||
```
|
||||
$ cd ~/work # Choose your favorite dir.
|
||||
$ git clone https://github.com/KhronosGroup/glTF
|
||||
$ export GLTF_DIR=~/work/glTF
|
||||
```
|
||||
|
||||
Then, specify the directory of glTF 2.0 schema JSONs and a glTF file.
|
||||
|
||||
```
|
||||
$ ./tinygltf-validator $GLTF_DIR/specification/2.0/schema/ ../../models/Cube/Cube.gltf
|
||||
```
|
||||
|
||||
## Third party licenses
|
||||
|
||||
* json.hpp https://github.com/nlohmann/json : MIT
|
||||
* json-schema-validator https://github.com/pboettch/json-schema-validator : MIT
|
||||
|
||||
140
examples/validator/app/tinygltf-validate.cc
Normal file
140
examples/validator/app/tinygltf-validate.cc
Normal file
@@ -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;
|
||||
}
|
||||
@@ -1,788 +0,0 @@
|
||||
/* Copyright (c) 2013 Dropbox, 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.
|
||||
*/
|
||||
|
||||
#include "json11.hpp"
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
namespace json11 {
|
||||
|
||||
static const int max_depth = 200;
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::map;
|
||||
using std::make_shared;
|
||||
using std::initializer_list;
|
||||
using std::move;
|
||||
|
||||
/* Helper for representing null - just a do-nothing struct, plus comparison
|
||||
* operators so the helpers in JsonValue work. We can't use nullptr_t because
|
||||
* it may not be orderable.
|
||||
*/
|
||||
struct NullStruct {
|
||||
bool operator==(NullStruct) const { return true; }
|
||||
bool operator<(NullStruct) const { return false; }
|
||||
};
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Serialization
|
||||
*/
|
||||
|
||||
static void dump(NullStruct, string &out) {
|
||||
out += "null";
|
||||
}
|
||||
|
||||
static void dump(double value, string &out) {
|
||||
if (std::isfinite(value)) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%.17g", value);
|
||||
out += buf;
|
||||
} else {
|
||||
out += "null";
|
||||
}
|
||||
}
|
||||
|
||||
static void dump(int value, string &out) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof buf, "%d", value);
|
||||
out += buf;
|
||||
}
|
||||
|
||||
static void dump(bool value, string &out) {
|
||||
out += value ? "true" : "false";
|
||||
}
|
||||
|
||||
static void dump(const string &value, string &out) {
|
||||
out += '"';
|
||||
for (size_t i = 0; i < value.length(); i++) {
|
||||
const char ch = value[i];
|
||||
if (ch == '\\') {
|
||||
out += "\\\\";
|
||||
} else if (ch == '"') {
|
||||
out += "\\\"";
|
||||
} else if (ch == '\b') {
|
||||
out += "\\b";
|
||||
} else if (ch == '\f') {
|
||||
out += "\\f";
|
||||
} else if (ch == '\n') {
|
||||
out += "\\n";
|
||||
} else if (ch == '\r') {
|
||||
out += "\\r";
|
||||
} else if (ch == '\t') {
|
||||
out += "\\t";
|
||||
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof buf, "\\u%04x", ch);
|
||||
out += buf;
|
||||
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
|
||||
out += "\\u2028";
|
||||
i += 2;
|
||||
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
|
||||
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
|
||||
out += "\\u2029";
|
||||
i += 2;
|
||||
} else {
|
||||
out += ch;
|
||||
}
|
||||
}
|
||||
out += '"';
|
||||
}
|
||||
|
||||
static void dump(const Json::array &values, string &out) {
|
||||
bool first = true;
|
||||
out += "[";
|
||||
for (const auto &value : values) {
|
||||
if (!first)
|
||||
out += ", ";
|
||||
value.dump(out);
|
||||
first = false;
|
||||
}
|
||||
out += "]";
|
||||
}
|
||||
|
||||
static void dump(const Json::object &values, string &out) {
|
||||
bool first = true;
|
||||
out += "{";
|
||||
for (const auto &kv : values) {
|
||||
if (!first)
|
||||
out += ", ";
|
||||
dump(kv.first, out);
|
||||
out += ": ";
|
||||
kv.second.dump(out);
|
||||
first = false;
|
||||
}
|
||||
out += "}";
|
||||
}
|
||||
|
||||
void Json::dump(string &out) const {
|
||||
m_ptr->dump(out);
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Value wrappers
|
||||
*/
|
||||
|
||||
template <Json::Type tag, typename T>
|
||||
class Value : public JsonValue {
|
||||
protected:
|
||||
|
||||
// Constructors
|
||||
explicit Value(const T &value) : m_value(value) {}
|
||||
explicit Value(T &&value) : m_value(move(value)) {}
|
||||
|
||||
// Get type tag
|
||||
Json::Type type() const override {
|
||||
return tag;
|
||||
}
|
||||
|
||||
// Comparisons
|
||||
bool equals(const JsonValue * other) const override {
|
||||
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
|
||||
}
|
||||
bool less(const JsonValue * other) const override {
|
||||
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
|
||||
}
|
||||
|
||||
const T m_value;
|
||||
void dump(string &out) const override { json11::dump(m_value, out); }
|
||||
};
|
||||
|
||||
class JsonDouble final : public Value<Json::NUMBER, double> {
|
||||
double number_value() const override { return m_value; }
|
||||
int int_value() const override { return static_cast<int>(m_value); }
|
||||
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||
public:
|
||||
explicit JsonDouble(double value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonInt final : public Value<Json::NUMBER, int> {
|
||||
double number_value() const override { return m_value; }
|
||||
int int_value() const override { return m_value; }
|
||||
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
|
||||
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
|
||||
public:
|
||||
explicit JsonInt(int value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonBoolean final : public Value<Json::BOOL, bool> {
|
||||
bool bool_value() const override { return m_value; }
|
||||
public:
|
||||
explicit JsonBoolean(bool value) : Value(value) {}
|
||||
};
|
||||
|
||||
class JsonString final : public Value<Json::STRING, string> {
|
||||
const string &string_value() const override { return m_value; }
|
||||
public:
|
||||
explicit JsonString(const string &value) : Value(value) {}
|
||||
explicit JsonString(string &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonArray final : public Value<Json::ARRAY, Json::array> {
|
||||
const Json::array &array_items() const override { return m_value; }
|
||||
const Json & operator[](size_t i) const override;
|
||||
public:
|
||||
explicit JsonArray(const Json::array &value) : Value(value) {}
|
||||
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonObject final : public Value<Json::OBJECT, Json::object> {
|
||||
const Json::object &object_items() const override { return m_value; }
|
||||
const Json & operator[](const string &key) const override;
|
||||
public:
|
||||
explicit JsonObject(const Json::object &value) : Value(value) {}
|
||||
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
|
||||
};
|
||||
|
||||
class JsonNull final : public Value<Json::NUL, NullStruct> {
|
||||
public:
|
||||
JsonNull() : Value({}) {}
|
||||
};
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Static globals - static-init-safe
|
||||
*/
|
||||
struct Statics {
|
||||
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
|
||||
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
|
||||
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
|
||||
const string empty_string;
|
||||
const vector<Json> empty_vector;
|
||||
const map<string, Json> empty_map;
|
||||
Statics() {}
|
||||
};
|
||||
|
||||
static const Statics & statics() {
|
||||
static const Statics s {};
|
||||
return s;
|
||||
}
|
||||
|
||||
static const Json & static_null() {
|
||||
// This has to be separate, not in Statics, because Json() accesses statics().null.
|
||||
static const Json json_null;
|
||||
return json_null;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Constructors
|
||||
*/
|
||||
|
||||
Json::Json() noexcept : m_ptr(statics().null) {}
|
||||
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
|
||||
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
|
||||
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
|
||||
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
|
||||
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
|
||||
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
|
||||
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
|
||||
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
|
||||
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
|
||||
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Accessors
|
||||
*/
|
||||
|
||||
Json::Type Json::type() const { return m_ptr->type(); }
|
||||
double Json::number_value() const { return m_ptr->number_value(); }
|
||||
int Json::int_value() const { return m_ptr->int_value(); }
|
||||
bool Json::bool_value() const { return m_ptr->bool_value(); }
|
||||
const string & Json::string_value() const { return m_ptr->string_value(); }
|
||||
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
|
||||
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
|
||||
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
|
||||
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
|
||||
|
||||
double JsonValue::number_value() const { return 0; }
|
||||
int JsonValue::int_value() const { return 0; }
|
||||
bool JsonValue::bool_value() const { return false; }
|
||||
const string & JsonValue::string_value() const { return statics().empty_string; }
|
||||
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
|
||||
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
|
||||
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
|
||||
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
|
||||
|
||||
const Json & JsonObject::operator[] (const string &key) const {
|
||||
auto iter = m_value.find(key);
|
||||
return (iter == m_value.end()) ? static_null() : iter->second;
|
||||
}
|
||||
const Json & JsonArray::operator[] (size_t i) const {
|
||||
if (i >= m_value.size()) return static_null();
|
||||
else return m_value[i];
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Comparison
|
||||
*/
|
||||
|
||||
bool Json::operator== (const Json &other) const {
|
||||
if (m_ptr == other.m_ptr)
|
||||
return true;
|
||||
if (m_ptr->type() != other.m_ptr->type())
|
||||
return false;
|
||||
|
||||
return m_ptr->equals(other.m_ptr.get());
|
||||
}
|
||||
|
||||
bool Json::operator< (const Json &other) const {
|
||||
if (m_ptr == other.m_ptr)
|
||||
return false;
|
||||
if (m_ptr->type() != other.m_ptr->type())
|
||||
return m_ptr->type() < other.m_ptr->type();
|
||||
|
||||
return m_ptr->less(other.m_ptr.get());
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Parsing
|
||||
*/
|
||||
|
||||
/* esc(c)
|
||||
*
|
||||
* Format char c suitable for printing in an error message.
|
||||
*/
|
||||
static inline string esc(char c) {
|
||||
char buf[12];
|
||||
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
|
||||
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
|
||||
} else {
|
||||
snprintf(buf, sizeof buf, "(%d)", c);
|
||||
}
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
static inline bool in_range(long x, long lower, long upper) {
|
||||
return (x >= lower && x <= upper);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/* JsonParser
|
||||
*
|
||||
* Object that tracks all state of an in-progress parse.
|
||||
*/
|
||||
struct JsonParser final {
|
||||
|
||||
/* State
|
||||
*/
|
||||
const string &str;
|
||||
size_t i;
|
||||
string &err;
|
||||
bool failed;
|
||||
const JsonParse strategy;
|
||||
|
||||
/* fail(msg, err_ret = Json())
|
||||
*
|
||||
* Mark this parse as failed.
|
||||
*/
|
||||
Json fail(string &&msg) {
|
||||
return fail(move(msg), Json());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T fail(string &&msg, const T err_ret) {
|
||||
if (!failed)
|
||||
err = std::move(msg);
|
||||
failed = true;
|
||||
return err_ret;
|
||||
}
|
||||
|
||||
/* consume_whitespace()
|
||||
*
|
||||
* Advance until the current character is non-whitespace.
|
||||
*/
|
||||
void consume_whitespace() {
|
||||
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
|
||||
i++;
|
||||
}
|
||||
|
||||
/* consume_comment()
|
||||
*
|
||||
* Advance comments (c-style inline and multiline).
|
||||
*/
|
||||
bool consume_comment() {
|
||||
bool comment_found = false;
|
||||
if (str[i] == '/') {
|
||||
i++;
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input after start of comment", false);
|
||||
if (str[i] == '/') { // inline comment
|
||||
i++;
|
||||
// advance until next line, or end of input
|
||||
while (i < str.size() && str[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
comment_found = true;
|
||||
}
|
||||
else if (str[i] == '*') { // multiline comment
|
||||
i++;
|
||||
if (i > str.size()-2)
|
||||
return fail("unexpected end of input inside multi-line comment", false);
|
||||
// advance until closing tokens
|
||||
while (!(str[i] == '*' && str[i+1] == '/')) {
|
||||
i++;
|
||||
if (i > str.size()-2)
|
||||
return fail(
|
||||
"unexpected end of input inside multi-line comment", false);
|
||||
}
|
||||
i += 2;
|
||||
comment_found = true;
|
||||
}
|
||||
else
|
||||
return fail("malformed comment", false);
|
||||
}
|
||||
return comment_found;
|
||||
}
|
||||
|
||||
/* consume_garbage()
|
||||
*
|
||||
* Advance until the current character is non-whitespace and non-comment.
|
||||
*/
|
||||
void consume_garbage() {
|
||||
consume_whitespace();
|
||||
if(strategy == JsonParse::COMMENTS) {
|
||||
bool comment_found = false;
|
||||
do {
|
||||
comment_found = consume_comment();
|
||||
if (failed) return;
|
||||
consume_whitespace();
|
||||
}
|
||||
while(comment_found);
|
||||
}
|
||||
}
|
||||
|
||||
/* get_next_token()
|
||||
*
|
||||
* Return the next non-whitespace character. If the end of the input is reached,
|
||||
* flag an error and return 0.
|
||||
*/
|
||||
char get_next_token() {
|
||||
consume_garbage();
|
||||
if (failed) return (char)0;
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input", (char)0);
|
||||
|
||||
return str[i++];
|
||||
}
|
||||
|
||||
/* encode_utf8(pt, out)
|
||||
*
|
||||
* Encode pt as UTF-8 and add it to out.
|
||||
*/
|
||||
void encode_utf8(long pt, string & out) {
|
||||
if (pt < 0)
|
||||
return;
|
||||
|
||||
if (pt < 0x80) {
|
||||
out += static_cast<char>(pt);
|
||||
} else if (pt < 0x800) {
|
||||
out += static_cast<char>((pt >> 6) | 0xC0);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
} else if (pt < 0x10000) {
|
||||
out += static_cast<char>((pt >> 12) | 0xE0);
|
||||
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
} else {
|
||||
out += static_cast<char>((pt >> 18) | 0xF0);
|
||||
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
|
||||
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
|
||||
out += static_cast<char>((pt & 0x3F) | 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_string()
|
||||
*
|
||||
* Parse a string, starting at the current position.
|
||||
*/
|
||||
string parse_string() {
|
||||
string out;
|
||||
long last_escaped_codepoint = -1;
|
||||
while (true) {
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input in string", "");
|
||||
|
||||
char ch = str[i++];
|
||||
|
||||
if (ch == '"') {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (in_range(ch, 0, 0x1f))
|
||||
return fail("unescaped " + esc(ch) + " in string", "");
|
||||
|
||||
// The usual case: non-escaped characters
|
||||
if (ch != '\\') {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = -1;
|
||||
out += ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle escapes
|
||||
if (i == str.size())
|
||||
return fail("unexpected end of input in string", "");
|
||||
|
||||
ch = str[i++];
|
||||
|
||||
if (ch == 'u') {
|
||||
// Extract 4-byte escape sequence
|
||||
string esc = str.substr(i, 4);
|
||||
// Explicitly check length of the substring. The following loop
|
||||
// relies on std::string returning the terminating NUL when
|
||||
// accessing str[length]. Checking here reduces brittleness.
|
||||
if (esc.length() < 4) {
|
||||
return fail("bad \\u escape: " + esc, "");
|
||||
}
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
|
||||
&& !in_range(esc[j], '0', '9'))
|
||||
return fail("bad \\u escape: " + esc, "");
|
||||
}
|
||||
|
||||
long codepoint = strtol(esc.data(), nullptr, 16);
|
||||
|
||||
// JSON specifies that characters outside the BMP shall be encoded as a pair
|
||||
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
|
||||
// whether we're in the middle of such a beast: the previous codepoint was an
|
||||
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
|
||||
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
|
||||
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
|
||||
// Reassemble the two surrogate pairs into one astral-plane character, per
|
||||
// the UTF-16 algorithm.
|
||||
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
|
||||
| (codepoint - 0xDC00)) + 0x10000, out);
|
||||
last_escaped_codepoint = -1;
|
||||
} else {
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = codepoint;
|
||||
}
|
||||
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
encode_utf8(last_escaped_codepoint, out);
|
||||
last_escaped_codepoint = -1;
|
||||
|
||||
if (ch == 'b') {
|
||||
out += '\b';
|
||||
} else if (ch == 'f') {
|
||||
out += '\f';
|
||||
} else if (ch == 'n') {
|
||||
out += '\n';
|
||||
} else if (ch == 'r') {
|
||||
out += '\r';
|
||||
} else if (ch == 't') {
|
||||
out += '\t';
|
||||
} else if (ch == '"' || ch == '\\' || ch == '/') {
|
||||
out += ch;
|
||||
} else {
|
||||
return fail("invalid escape character " + esc(ch), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_number()
|
||||
*
|
||||
* Parse a double.
|
||||
*/
|
||||
Json parse_number() {
|
||||
size_t start_pos = i;
|
||||
|
||||
if (str[i] == '-')
|
||||
i++;
|
||||
|
||||
// Integer part
|
||||
if (str[i] == '0') {
|
||||
i++;
|
||||
if (in_range(str[i], '0', '9'))
|
||||
return fail("leading 0s not permitted in numbers");
|
||||
} else if (in_range(str[i], '1', '9')) {
|
||||
i++;
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
} else {
|
||||
return fail("invalid " + esc(str[i]) + " in number");
|
||||
}
|
||||
|
||||
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
|
||||
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
|
||||
return std::atoi(str.c_str() + start_pos);
|
||||
}
|
||||
|
||||
// Decimal part
|
||||
if (str[i] == '.') {
|
||||
i++;
|
||||
if (!in_range(str[i], '0', '9'))
|
||||
return fail("at least one digit required in fractional part");
|
||||
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
}
|
||||
|
||||
// Exponent part
|
||||
if (str[i] == 'e' || str[i] == 'E') {
|
||||
i++;
|
||||
|
||||
if (str[i] == '+' || str[i] == '-')
|
||||
i++;
|
||||
|
||||
if (!in_range(str[i], '0', '9'))
|
||||
return fail("at least one digit required in exponent");
|
||||
|
||||
while (in_range(str[i], '0', '9'))
|
||||
i++;
|
||||
}
|
||||
|
||||
return std::strtod(str.c_str() + start_pos, nullptr);
|
||||
}
|
||||
|
||||
/* expect(str, res)
|
||||
*
|
||||
* Expect that 'str' starts at the character that was just read. If it does, advance
|
||||
* the input and return res. If not, flag an error.
|
||||
*/
|
||||
Json expect(const string &expected, Json res) {
|
||||
assert(i != 0);
|
||||
i--;
|
||||
if (str.compare(i, expected.length(), expected) == 0) {
|
||||
i += expected.length();
|
||||
return res;
|
||||
} else {
|
||||
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
|
||||
}
|
||||
}
|
||||
|
||||
/* parse_json()
|
||||
*
|
||||
* Parse a JSON object.
|
||||
*/
|
||||
Json parse_json(int depth) {
|
||||
if (depth > max_depth) {
|
||||
return fail("exceeded maximum nesting depth");
|
||||
}
|
||||
|
||||
char ch = get_next_token();
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
if (ch == '-' || (ch >= '0' && ch <= '9')) {
|
||||
i--;
|
||||
return parse_number();
|
||||
}
|
||||
|
||||
if (ch == 't')
|
||||
return expect("true", true);
|
||||
|
||||
if (ch == 'f')
|
||||
return expect("false", false);
|
||||
|
||||
if (ch == 'n')
|
||||
return expect("null", Json());
|
||||
|
||||
if (ch == '"')
|
||||
return parse_string();
|
||||
|
||||
if (ch == '{') {
|
||||
map<string, Json> data;
|
||||
ch = get_next_token();
|
||||
if (ch == '}')
|
||||
return data;
|
||||
|
||||
while (1) {
|
||||
if (ch != '"')
|
||||
return fail("expected '\"' in object, got " + esc(ch));
|
||||
|
||||
string key = parse_string();
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch != ':')
|
||||
return fail("expected ':' in object, got " + esc(ch));
|
||||
|
||||
data[std::move(key)] = parse_json(depth + 1);
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch == '}')
|
||||
break;
|
||||
if (ch != ',')
|
||||
return fail("expected ',' in object, got " + esc(ch));
|
||||
|
||||
ch = get_next_token();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
if (ch == '[') {
|
||||
vector<Json> data;
|
||||
ch = get_next_token();
|
||||
if (ch == ']')
|
||||
return data;
|
||||
|
||||
while (1) {
|
||||
i--;
|
||||
data.push_back(parse_json(depth + 1));
|
||||
if (failed)
|
||||
return Json();
|
||||
|
||||
ch = get_next_token();
|
||||
if (ch == ']')
|
||||
break;
|
||||
if (ch != ',')
|
||||
return fail("expected ',' in list, got " + esc(ch));
|
||||
|
||||
ch = get_next_token();
|
||||
(void)ch;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
return fail("expected value, got " + esc(ch));
|
||||
}
|
||||
};
|
||||
}//namespace {
|
||||
|
||||
Json Json::parse(const string &in, string &err, JsonParse strategy) {
|
||||
JsonParser parser { in, 0, err, false, strategy };
|
||||
Json result = parser.parse_json(0);
|
||||
|
||||
// Check for any trailing garbage
|
||||
parser.consume_garbage();
|
||||
if (parser.failed)
|
||||
return Json();
|
||||
if (parser.i != in.size())
|
||||
return parser.fail("unexpected trailing " + esc(in[parser.i]));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Documented in json11.hpp
|
||||
vector<Json> Json::parse_multi(const string &in,
|
||||
std::string::size_type &parser_stop_pos,
|
||||
string &err,
|
||||
JsonParse strategy) {
|
||||
JsonParser parser { in, 0, err, false, strategy };
|
||||
parser_stop_pos = 0;
|
||||
vector<Json> json_vec;
|
||||
while (parser.i != in.size() && !parser.failed) {
|
||||
json_vec.push_back(parser.parse_json(0));
|
||||
if (parser.failed)
|
||||
break;
|
||||
|
||||
// Check for another object
|
||||
parser.consume_garbage();
|
||||
if (parser.failed)
|
||||
break;
|
||||
parser_stop_pos = parser.i;
|
||||
}
|
||||
return json_vec;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * *
|
||||
* Shape-checking
|
||||
*/
|
||||
|
||||
bool Json::has_shape(const shape & types, string & err) const {
|
||||
if (!is_object()) {
|
||||
err = "expected JSON object, got " + dump();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto & item : types) {
|
||||
if ((*this)[item.first].type() != item.second) {
|
||||
err = "bad type for " + item.first + " in " + dump();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace json11
|
||||
@@ -1,232 +0,0 @@
|
||||
/* json11
|
||||
*
|
||||
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
|
||||
*
|
||||
* The core object provided by the library is json11::Json. A Json object represents any JSON
|
||||
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
|
||||
* object (std::map).
|
||||
*
|
||||
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
|
||||
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
|
||||
* Json::parse (static) to parse a std::string as a Json object.
|
||||
*
|
||||
* Internally, the various types of Json object are represented by the JsonValue class
|
||||
* hierarchy.
|
||||
*
|
||||
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
|
||||
* so some JSON implementations distinguish between integers and floating-point numbers, while
|
||||
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
|
||||
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
|
||||
* to JSON that will be *silently* changed by a round-trip through those implementations.
|
||||
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
|
||||
* provides integer helpers.
|
||||
*
|
||||
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
|
||||
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
|
||||
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
|
||||
* will be exact for +/- 275 years.)
|
||||
*/
|
||||
|
||||
/* Copyright (c) 2013 Dropbox, 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <initializer_list>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER <= 1800 // VS 2013
|
||||
#ifndef noexcept
|
||||
#define noexcept throw()
|
||||
#endif
|
||||
|
||||
#ifndef snprintf
|
||||
#define snprintf _snprintf_s
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace json11 {
|
||||
|
||||
enum JsonParse {
|
||||
STANDARD, COMMENTS
|
||||
};
|
||||
|
||||
class JsonValue;
|
||||
|
||||
class Json final {
|
||||
public:
|
||||
// Types
|
||||
enum Type {
|
||||
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
|
||||
};
|
||||
|
||||
// Array and object typedefs
|
||||
typedef std::vector<Json> array;
|
||||
typedef std::map<std::string, Json> object;
|
||||
|
||||
// Constructors for the various types of JSON value.
|
||||
Json() noexcept; // NUL
|
||||
Json(std::nullptr_t) noexcept; // NUL
|
||||
Json(double value); // NUMBER
|
||||
Json(int value); // NUMBER
|
||||
Json(bool value); // BOOL
|
||||
Json(const std::string &value); // STRING
|
||||
Json(std::string &&value); // STRING
|
||||
Json(const char * value); // STRING
|
||||
Json(const array &values); // ARRAY
|
||||
Json(array &&values); // ARRAY
|
||||
Json(const object &values); // OBJECT
|
||||
Json(object &&values); // OBJECT
|
||||
|
||||
// Implicit constructor: anything with a to_json() function.
|
||||
template <class T, class = decltype(&T::to_json)>
|
||||
Json(const T & t) : Json(t.to_json()) {}
|
||||
|
||||
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
|
||||
template <class M, typename std::enable_if<
|
||||
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
|
||||
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
|
||||
int>::type = 0>
|
||||
Json(const M & m) : Json(object(m.begin(), m.end())) {}
|
||||
|
||||
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
|
||||
template <class V, typename std::enable_if<
|
||||
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
|
||||
int>::type = 0>
|
||||
Json(const V & v) : Json(array(v.begin(), v.end())) {}
|
||||
|
||||
// This prevents Json(some_pointer) from accidentally producing a bool. Use
|
||||
// Json(bool(some_pointer)) if that behavior is desired.
|
||||
Json(void *) = delete;
|
||||
|
||||
// Accessors
|
||||
Type type() const;
|
||||
|
||||
bool is_null() const { return type() == NUL; }
|
||||
bool is_number() const { return type() == NUMBER; }
|
||||
bool is_bool() const { return type() == BOOL; }
|
||||
bool is_string() const { return type() == STRING; }
|
||||
bool is_array() const { return type() == ARRAY; }
|
||||
bool is_object() const { return type() == OBJECT; }
|
||||
|
||||
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
|
||||
// distinguish between integer and non-integer numbers - number_value() and int_value()
|
||||
// can both be applied to a NUMBER-typed object.
|
||||
double number_value() const;
|
||||
int int_value() const;
|
||||
|
||||
// Return the enclosed value if this is a boolean, false otherwise.
|
||||
bool bool_value() const;
|
||||
// Return the enclosed string if this is a string, "" otherwise.
|
||||
const std::string &string_value() const;
|
||||
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
|
||||
const array &array_items() const;
|
||||
// Return the enclosed std::map if this is an object, or an empty map otherwise.
|
||||
const object &object_items() const;
|
||||
|
||||
// Return a reference to arr[i] if this is an array, Json() otherwise.
|
||||
const Json & operator[](size_t i) const;
|
||||
// Return a reference to obj[key] if this is an object, Json() otherwise.
|
||||
const Json & operator[](const std::string &key) const;
|
||||
|
||||
// Serialize.
|
||||
void dump(std::string &out) const;
|
||||
std::string dump() const {
|
||||
std::string out;
|
||||
dump(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Parse. If parse fails, return Json() and assign an error message to err.
|
||||
static Json parse(const std::string & in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD);
|
||||
static Json parse(const char * in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD) {
|
||||
if (in) {
|
||||
return parse(std::string(in), err, strategy);
|
||||
} else {
|
||||
err = "null input";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
// Parse multiple objects, concatenated or separated by whitespace
|
||||
static std::vector<Json> parse_multi(
|
||||
const std::string & in,
|
||||
std::string::size_type & parser_stop_pos,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD);
|
||||
|
||||
static inline std::vector<Json> parse_multi(
|
||||
const std::string & in,
|
||||
std::string & err,
|
||||
JsonParse strategy = JsonParse::STANDARD) {
|
||||
std::string::size_type parser_stop_pos;
|
||||
return parse_multi(in, parser_stop_pos, err, strategy);
|
||||
}
|
||||
|
||||
bool operator== (const Json &rhs) const;
|
||||
bool operator< (const Json &rhs) const;
|
||||
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
|
||||
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
|
||||
bool operator> (const Json &rhs) const { return (rhs < *this); }
|
||||
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
|
||||
|
||||
/* has_shape(types, err)
|
||||
*
|
||||
* Return true if this is a JSON object and, for each item in types, has a field of
|
||||
* the given type. If not, return false and set err to a descriptive message.
|
||||
*/
|
||||
typedef std::initializer_list<std::pair<std::string, Type>> shape;
|
||||
bool has_shape(const shape & types, std::string & err) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<JsonValue> m_ptr;
|
||||
};
|
||||
|
||||
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
|
||||
class JsonValue {
|
||||
protected:
|
||||
friend class Json;
|
||||
friend class JsonInt;
|
||||
friend class JsonDouble;
|
||||
virtual Json::Type type() const = 0;
|
||||
virtual bool equals(const JsonValue * other) const = 0;
|
||||
virtual bool less(const JsonValue * other) const = 0;
|
||||
virtual void dump(std::string &out) const = 0;
|
||||
virtual double number_value() const;
|
||||
virtual int int_value() const;
|
||||
virtual bool bool_value() const;
|
||||
virtual const std::string &string_value() const;
|
||||
virtual const Json::array &array_items() const;
|
||||
virtual const Json &operator[](size_t i) const;
|
||||
virtual const Json::object &object_items() const;
|
||||
virtual const Json &operator[](const std::string &key) const;
|
||||
virtual ~JsonValue() {}
|
||||
};
|
||||
|
||||
} // namespace json11
|
||||
160
examples/validator/src/json-schema-draft4.json.cpp
Normal file
160
examples/validator/src/json-schema-draft4.json.cpp
Normal file
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
201
examples/validator/src/json-schema.hpp
Normal file
201
examples/validator/src/json-schema.hpp
Normal file
@@ -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__ */
|
||||
190
examples/validator/src/json-uri.cpp
Normal file
190
examples/validator/src/json-uri.cpp
Normal file
@@ -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
|
||||
738
examples/validator/src/json-validator.cpp
Normal file
738
examples/validator/src/json-validator.cpp
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13003
examples/validator/src/json.hpp
Normal file
13003
examples/validator/src/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,170 +0,0 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "json11.hpp"
|
||||
|
||||
#include "valijson/adapters/json11_adapter.hpp"
|
||||
#include "valijson/utils/json11_utils.hpp"
|
||||
|
||||
#include "valijson/schema.hpp"
|
||||
#include "valijson/schema_parser.hpp"
|
||||
#include "valijson/validator.hpp"
|
||||
|
||||
static void usage(const char *name) {
|
||||
std::cerr << "Usage: " << name << " <gltf schema dir> <gltf file>\n";
|
||||
std::cerr << " schema dir is usually : $glTF/specification/2.0/schema\n";
|
||||
std::cerr << " where $glTF is a directory of https://github.com/KhronosGroup/glTF\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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
bool validate(const std::string &schema_dir, const std::string &filename) {
|
||||
std::string gltf_schema = schema_dir + "/glTF.schema.json";
|
||||
|
||||
// 1) Read the schema for the document you want to validate
|
||||
json11::Json schema_doc;
|
||||
bool ret = valijson::utils::loadDocument(gltf_schema, schema_doc);
|
||||
if (!ret) {
|
||||
std::cerr << "Failed to load schema file : " << gltf_schema << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) Parse JSON schema content using valijson
|
||||
valijson::Schema mySchema;
|
||||
valijson::SchemaParser parser;
|
||||
valijson::adapters::Json11Adapter mySchemaAdapter(schema_doc);
|
||||
parser.populateSchema(mySchemaAdapter, mySchema);
|
||||
|
||||
// 3) Load a document to validate
|
||||
json11::Json target_doc;
|
||||
if (!valijson::utils::loadDocument(filename, target_doc)) {
|
||||
std::cerr << "Failed to load JSON file to validate : " << filename << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
valijson::Validator validator;
|
||||
valijson::ValidationResults results;
|
||||
valijson::adapters::Json11Adapter myTargetAdapter(target_doc);
|
||||
if (!validator.validate(mySchema, myTargetAdapter, &results)) {
|
||||
std::cerr << "Validation failed.\n";
|
||||
|
||||
valijson::ValidationResults::Error error;
|
||||
unsigned int errorNum = 1;
|
||||
while (results.popError(error)) {
|
||||
std::cerr << "Error #" << errorNum << std::endl;
|
||||
std::cerr << " ";
|
||||
for (const std::string &contextElement : error.context) {
|
||||
std::cerr << contextElement << " ";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
std::cerr << " - " << error.description << std::endl;
|
||||
++errorNum;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Valid glTF file!\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;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
Copyright (c) 2016, Tristan Penman
|
||||
Copyright (c) 2016, Akamai Technolgies, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,476 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_ADAPTER_HPP
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class FrozenValue;
|
||||
|
||||
/**
|
||||
* @brief An interface that encapsulates access to the JSON values provided
|
||||
* by a JSON parser implementation.
|
||||
*
|
||||
* This interface allows JSON processing code to be parser-agnostic. It provides
|
||||
* functions to access the plain old datatypes (PODs) that are described in the
|
||||
* JSON specification, and callback-based access to the contents of arrays and
|
||||
* objects.
|
||||
*
|
||||
* The interface also defines a set of functions that allow for type-casting and
|
||||
* type-comparison based on value rather than on type.
|
||||
*/
|
||||
class Adapter
|
||||
{
|
||||
public:
|
||||
|
||||
/// Typedef for callback function supplied to applyToArray.
|
||||
typedef std::function<bool (const Adapter &)>
|
||||
ArrayValueCallback;
|
||||
|
||||
/// Typedef for callback function supplied to applyToObject.
|
||||
typedef std::function<bool (const std::string &, const Adapter &)>
|
||||
ObjectMemberCallback;
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor defined to ensure deletion via base-class
|
||||
* pointers is safe.
|
||||
*/
|
||||
virtual ~Adapter() { };
|
||||
|
||||
/**
|
||||
* @brief Apply a callback function to each value in an array.
|
||||
*
|
||||
* The callback function is invoked for each element in the array, until
|
||||
* it has been applied to all values, or it returns false.
|
||||
*
|
||||
* @param fn Callback function to invoke
|
||||
*
|
||||
* @returns true if Adapter contains an array and all values are equal,
|
||||
* false otherwise.
|
||||
*/
|
||||
virtual bool applyToArray(ArrayValueCallback fn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Apply a callback function to each member in an object.
|
||||
*
|
||||
* The callback function shall be invoked for each member in the object,
|
||||
* until it has been applied to all values, or it returns false.
|
||||
*
|
||||
* @param fn Callback function to invoke
|
||||
*
|
||||
* @returns true if Adapter contains an object, and callback function
|
||||
* returns true for each member in the object, false otherwise.
|
||||
*/
|
||||
virtual bool applyToObject(ObjectMemberCallback fn) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the boolean representation of the contained value.
|
||||
*
|
||||
* This function shall return a boolean value if the Adapter contains either
|
||||
* an actual boolean value, or one of the strings 'true' or 'false'.
|
||||
* The string comparison is case sensitive.
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a boolean.
|
||||
*
|
||||
* @returns Boolean representation of contained value.
|
||||
*/
|
||||
virtual bool asBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the boolean representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a boolean value if the Adapter contains
|
||||
* either an actual boolean value, or one of the strings 'true' or 'false'.
|
||||
* The string comparison is case sensitive.
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a bool to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asBool(bool &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the double representation of the contained value.
|
||||
*
|
||||
* This function shall return a double value if the Adapter contains either
|
||||
* an actual double, an integer, or a string that contains a valid
|
||||
* representation of a numeric value (according to the C++ Std Library).
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a double.
|
||||
*
|
||||
* @returns Double representation of contained value.
|
||||
*/
|
||||
virtual double asDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the double representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a double value if the Adapter contains either
|
||||
* an actual double, an integer, or a string that contains a valid
|
||||
* representation of a numeric value (according to the C++ Std Library).
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a double to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asDouble(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the int64_t representation of the contained value.
|
||||
*
|
||||
* This function shall return an int64_t value if the Adapter contains either
|
||||
* an actual integer, or a string that contains a valid representation of an
|
||||
* integer value (according to the C++ Std Library).
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to an int64_t.
|
||||
*
|
||||
* @returns int64_t representation of contained value.
|
||||
*/
|
||||
virtual int64_t asInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the int64_t representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve an int64_t value if the Adapter contains
|
||||
* either an actual integer, or a string that contains a valid
|
||||
* representation of an integer value (according to the C++ Std Library).
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a int64_t to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asInteger(int64_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the string representation of the contained value.
|
||||
*
|
||||
* This function shall return a string value if the Adapter contains either
|
||||
* an actual string, a literal value of another POD type, an empty array,
|
||||
* an empty object, or null.
|
||||
*
|
||||
* An exception shall be thrown if the value cannot be cast to a string.
|
||||
*
|
||||
* @returns string representation of contained value.
|
||||
*/
|
||||
virtual std::string asString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the string representation of the contained value.
|
||||
*
|
||||
* This function shall retrieve a string value if the Adapter contains either
|
||||
* an actual string, a literal value of another POD type, an empty array,
|
||||
* an empty object, or null.
|
||||
*
|
||||
* The retrieved value is returned via reference.
|
||||
*
|
||||
* @param result reference to a string to set with retrieved value.
|
||||
*
|
||||
* @returns true if the value could be retrieved, false otherwise
|
||||
*/
|
||||
virtual bool asString(std::string &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Compare the value held by this Adapter instance with the value
|
||||
* held by another Adapter instance.
|
||||
*
|
||||
* @param other the other adapter instance
|
||||
* @param strict flag to use strict type comparison
|
||||
*
|
||||
* @returns true if values are equal, false otherwise
|
||||
*/
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Create a new FrozenValue instance that is equivalent to the
|
||||
* value contained by the Adapter.
|
||||
*
|
||||
* @returns pointer to a new FrozenValue instance, belonging to the caller.
|
||||
*/
|
||||
virtual FrozenValue* freeze() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the number of elements in the array.
|
||||
*
|
||||
* Throws an exception if the value is not an array.
|
||||
*
|
||||
* @return number of elements if value is an array
|
||||
*/
|
||||
virtual size_t getArraySize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array.
|
||||
*
|
||||
* This function shall return true or false to indicate whether or not the
|
||||
* result value was set. If the contained value is not an array, the
|
||||
* result value shall not be set. This applies even if the value could be
|
||||
* cast to an empty array. The calling code is expected to handles those
|
||||
* cases manually.
|
||||
*
|
||||
* @param result reference to size_t variable to set with result.
|
||||
*
|
||||
* @return true if value retrieved successfully, false otherwise.
|
||||
*/
|
||||
virtual bool getArraySize(size_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained boolean value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* boolean.
|
||||
*
|
||||
* @returns contained boolean value.
|
||||
*/
|
||||
virtual bool getBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained boolean value.
|
||||
*
|
||||
* This function shall retrieve the boolean value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to boolean variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getBool(bool &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained double value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* double.
|
||||
*
|
||||
* @returns contained double value.
|
||||
*/
|
||||
virtual double getDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained double value.
|
||||
*
|
||||
* This function shall retrieve the double value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to double variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getDouble(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained integer value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* integer.
|
||||
*
|
||||
* @returns contained integer value.
|
||||
*/
|
||||
virtual int64_t getInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained integer value.
|
||||
*
|
||||
* This function shall retrieve the integer value contained by this Adapter,
|
||||
* and store it in the result variable that was passed by reference.
|
||||
*
|
||||
* @param result reference to integer variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getInteger(int64_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained numeric value as a double.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* integer or a double.
|
||||
*
|
||||
* @returns contained double or integral value.
|
||||
*/
|
||||
virtual double getNumber() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained numeric value as a double.
|
||||
*
|
||||
* This function shall retrieve the double or integral value contained by
|
||||
* this Adapter, and store it in the result variable that was passed by
|
||||
* reference.
|
||||
*
|
||||
* @param result reference to double variable to set with result.
|
||||
*
|
||||
* @returns true if the value was retrieved, false otherwise.
|
||||
*/
|
||||
virtual bool getNumber(double &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the number of members in the object.
|
||||
*
|
||||
* Throws an exception if the value is not an object.
|
||||
*
|
||||
* @return number of members if value is an object
|
||||
*/
|
||||
virtual size_t getObjectSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object.
|
||||
*
|
||||
* This function shall return true or false to indicate whether or not the
|
||||
* result value was set. If the contained value is not an object, the
|
||||
* result value shall not be set. This applies even if the value could be
|
||||
* cast to an empty object. The calling code is expected to handles those
|
||||
* cases manually.
|
||||
*
|
||||
* @param result reference to size_t variable to set with result.
|
||||
*
|
||||
* @return true if value retrieved successfully, false otherwise.
|
||||
*/
|
||||
virtual bool getObjectSize(size_t &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return the contained string value.
|
||||
*
|
||||
* This function shall throw an exception if the contained value is not a
|
||||
* string - even if the value could be cast to a string. The asString()
|
||||
* function should be used when casting is allowed.
|
||||
*
|
||||
* @returns string contained by this Adapter
|
||||
*/
|
||||
virtual std::string getString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the contained string value.
|
||||
*
|
||||
* This function shall retrieve the string value contained by this Adapter,
|
||||
* and store it in result variable that is passed by reference.
|
||||
*
|
||||
* @param result reference to string to set with result
|
||||
*
|
||||
* @returns true if string was retrieved, false otherwise
|
||||
*/
|
||||
virtual bool getString(std::string &result) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not this Adapter supports strict types.
|
||||
*
|
||||
* This function shall return true if the Adapter implementation supports
|
||||
* strict types, or false if the Adapter fails to store any part of the
|
||||
* type information supported by the Adapter interface.
|
||||
*
|
||||
* For example, the PropertyTreeAdapter implementation stores POD values as
|
||||
* strings, effectively discarding any other type information. If you were
|
||||
* to call isDouble() on a double stored by this Adapter, the result would
|
||||
* be false. The maybeDouble(), asDouble() and various related functions
|
||||
* are provided to perform type checking based on value rather than on type.
|
||||
*
|
||||
* The BasicAdapter template class provides implementations for the type-
|
||||
* casting functions so that Adapter implementations are semantically
|
||||
* equivalent in their type-casting behaviour.
|
||||
*
|
||||
* @returns true if Adapter supports strict types, false otherwise
|
||||
*/
|
||||
virtual bool hasStrictTypes() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an array.
|
||||
virtual bool isArray() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a boolean.
|
||||
virtual bool isBool() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a double.
|
||||
virtual bool isDouble() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an integer.
|
||||
virtual bool isInteger() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a null.
|
||||
virtual bool isNull() const = 0;
|
||||
|
||||
/// Returns true if the contained value is either a double or an integer.
|
||||
virtual bool isNumber() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely an object.
|
||||
virtual bool isObject() const = 0;
|
||||
|
||||
/// Returns true if the contained value is definitely a string.
|
||||
virtual bool isString() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an array.
|
||||
*
|
||||
* @returns true if the contained value is an array, an empty string, or an
|
||||
* empty object.
|
||||
*/
|
||||
virtual bool maybeArray() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a boolean.
|
||||
*
|
||||
* @returns true if the contained value is a boolean, or one of the strings
|
||||
* 'true' or 'false'. Note that numeric values are not to be cast
|
||||
* to boolean values.
|
||||
*/
|
||||
virtual bool maybeBool() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a double.
|
||||
*
|
||||
* @returns true if the contained value is a double, an integer, or a string
|
||||
* containing a double or integral value.
|
||||
*/
|
||||
virtual bool maybeDouble() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an integer.
|
||||
*
|
||||
* @returns true if the contained value is an integer, or a string
|
||||
* containing an integral value.
|
||||
*/
|
||||
virtual bool maybeInteger() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a null.
|
||||
*
|
||||
* @returns true if the contained value is null or an empty string.
|
||||
*/
|
||||
virtual bool maybeNull() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to an object.
|
||||
*
|
||||
* @returns true if the contained value is an object, an empty array or
|
||||
* an empty string.
|
||||
*/
|
||||
virtual bool maybeObject() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns true if the contained value can be cast to a string.
|
||||
*
|
||||
* @returns true if the contained value is a non-null POD type, an empty
|
||||
* array, or an empty object.
|
||||
*/
|
||||
virtual bool maybeString() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Template struct that should be specialised for each concrete Adapter
|
||||
* class.
|
||||
*
|
||||
* @deprecated This is a bit of a hack, and I'd like to remove it.
|
||||
*/
|
||||
template<typename T>
|
||||
struct AdapterTraits
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -1,868 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_BASIC_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_BASIC_ADAPTER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sstream>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/internal/optional.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
/**
|
||||
* @brief A helper for the array and object member iterators.
|
||||
*
|
||||
* See http://www.stlsoft.org/doc-1.9/group__group____pattern____dereference__proxy.html
|
||||
* for motivation
|
||||
*
|
||||
* @tparam Value Name of the value type
|
||||
*/
|
||||
template<class Value>
|
||||
struct DerefProxy
|
||||
{
|
||||
explicit DerefProxy(const Value& x)
|
||||
: m_ref(x) { }
|
||||
|
||||
Value* operator->()
|
||||
{
|
||||
return std::addressof(m_ref);
|
||||
}
|
||||
|
||||
operator Value*()
|
||||
{
|
||||
return std::addressof(m_ref);
|
||||
}
|
||||
|
||||
private:
|
||||
Value m_ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Template class that implements the expected semantics of an Adapter.
|
||||
*
|
||||
* Implementing all of the type-casting functionality for each Adapter is error
|
||||
* prone and tedious, so this template class aims to minimise the duplication
|
||||
* of code between various Adapter implementations. This template doesn't quite
|
||||
* succeed in removing all duplication, but it has greatly simplified the
|
||||
* implementation of a new Adapter by encapsulating the type-casting semantics
|
||||
* and a lot of the trivial functionality associated with the Adapter interface.
|
||||
*
|
||||
* By inheriting from this template class, Adapter implementations will inherit
|
||||
* the exception throwing behaviour that is expected by other parts of the
|
||||
* Valijson library.
|
||||
*
|
||||
* @tparam AdapterType Self-referential name of the Adapter being
|
||||
* specialised.
|
||||
* @tparam ArrayType Name of the type that will be returned by the
|
||||
* getArray() function. Instances of this type should
|
||||
* provide begin(), end() and size() functions so
|
||||
* that it is possible to iterate over the values in
|
||||
* the array.
|
||||
* @tparam ObjectMemberType Name of the type exposed when iterating over the
|
||||
* contents of an object returned by getObject().
|
||||
* @tparam ObjectType Name of the type that will be returned by the
|
||||
* getObject() function. Instances of this type
|
||||
* should provide begin(), end(), find() and size()
|
||||
* functions so that it is possible to iterate over
|
||||
* the members of the object.
|
||||
* @tparam ValueType Name of the type that provides a consistent
|
||||
* interface to a JSON value for a parser. For
|
||||
* example, this type should provide the getDouble()
|
||||
* and isDouble() functions. But it does not need to
|
||||
* know how to cast values from one type to another -
|
||||
* that functionality is provided by this template
|
||||
* class.
|
||||
*/
|
||||
template<
|
||||
typename AdapterType,
|
||||
typename ArrayType,
|
||||
typename ObjectMemberType,
|
||||
typename ObjectType,
|
||||
typename ValueType>
|
||||
class BasicAdapter: public Adapter
|
||||
{
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief Functor for comparing two arrays.
|
||||
*
|
||||
* This functor is used to compare the elements in an array of the type
|
||||
* ArrayType with individual values provided as generic Adapter objects.
|
||||
* Comparison is performed by the () operator.
|
||||
*
|
||||
* The functor works by maintaining an iterator for the current position
|
||||
* in an array. Each time the () operator is called, the value at this
|
||||
* position is compared with the value passed as an argument to ().
|
||||
* Immediately after the comparison, the iterator will be incremented.
|
||||
*
|
||||
* This functor is designed to be passed to the applyToArray() function
|
||||
* of an Adapter object.
|
||||
*/
|
||||
class ArrayComparisonFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an ArrayComparisonFunctor for an array.
|
||||
*
|
||||
* @param array Array to compare values against
|
||||
* @param strict Flag to use strict type comparison
|
||||
*/
|
||||
ArrayComparisonFunctor(const ArrayType &array, bool strict)
|
||||
: itr(array.begin()),
|
||||
end(array.end()),
|
||||
strict(strict) { }
|
||||
|
||||
/**
|
||||
* @brief Compare a value against the current element in the array.
|
||||
*
|
||||
* @param adapter Value to be compared with current element
|
||||
*
|
||||
* @returns true if values are equal, false otherwise.
|
||||
*/
|
||||
bool operator()(const Adapter &adapter)
|
||||
{
|
||||
if (itr == end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return AdapterType(*itr++).equalTo(adapter, strict);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iterator for current element in the array
|
||||
typename ArrayType::const_iterator itr;
|
||||
|
||||
/// Iterator for one-past the last element of the array
|
||||
typename ArrayType::const_iterator end;
|
||||
|
||||
/// Flag to use strict type comparison
|
||||
const bool strict;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Functor for comparing two objects
|
||||
*
|
||||
* This functor is used to compare the members of an object of the type
|
||||
* ObjectType with key-value pairs belonging to another object.
|
||||
*
|
||||
* The functor works by maintaining a reference to an object provided via
|
||||
* the constructor. When time the () operator is called with a key-value
|
||||
* pair as arguments, the function will attempt to find the key in the
|
||||
* base object. If found, the associated value will be compared with the
|
||||
* value provided to the () operator.
|
||||
*
|
||||
* This functor is designed to be passed to the applyToObject() function
|
||||
* of an Adapter object.
|
||||
*/
|
||||
class ObjectComparisonFunctor
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new ObjectComparisonFunctor for an object.
|
||||
*
|
||||
* @param object object to use as comparison baseline
|
||||
* @param strict flag to use strict type-checking
|
||||
*/
|
||||
ObjectComparisonFunctor(
|
||||
const ObjectType &object, bool strict)
|
||||
: object(object),
|
||||
strict(strict) { }
|
||||
|
||||
/**
|
||||
* @brief Find a key in the object and compare its value.
|
||||
*
|
||||
* @param key Key to find
|
||||
* @param value Value to be compared against
|
||||
*
|
||||
* @returns true if key is found and values are equal, false otherwise.
|
||||
*/
|
||||
bool operator()(const std::string &key, const Adapter &value)
|
||||
{
|
||||
const typename ObjectType::const_iterator itr = object.find(key);
|
||||
if (itr == object.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (*itr).second.equalTo(value, strict);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Object to be used as a comparison baseline
|
||||
const ObjectType &object;
|
||||
|
||||
/// Flag to use strict type-checking
|
||||
bool strict;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// Alias for ArrayType template parameter
|
||||
typedef ArrayType Array;
|
||||
|
||||
/// Alias for ObjectMemberType template parameter
|
||||
typedef ObjectMemberType ObjectMember;
|
||||
|
||||
/// Alias for ObjectType template parameter
|
||||
typedef ObjectType Object;
|
||||
|
||||
/**
|
||||
* @brief Construct an Adapter using the default value.
|
||||
*
|
||||
* This constructor relies on the default constructor of the ValueType
|
||||
* class provided as a template argument.
|
||||
*/
|
||||
BasicAdapter() { }
|
||||
|
||||
/**
|
||||
* @brief Construct an Adapter using a specified ValueType object.
|
||||
*
|
||||
* This constructor relies on the copy constructor of the ValueType
|
||||
* class provided as template argument.
|
||||
*/
|
||||
BasicAdapter(const ValueType &value)
|
||||
: value(value) { }
|
||||
|
||||
virtual bool applyToArray(ArrayValueCallback fn) const
|
||||
{
|
||||
if (!maybeArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Due to the fact that the only way a value can be 'maybe an array' is
|
||||
// if it is an empty string or empty object, we only need to go to
|
||||
// effort of constructing an ArrayType instance if the value is
|
||||
// definitely an array.
|
||||
if (value.isArray()) {
|
||||
const opt::optional<Array> array = value.getArrayOptional();
|
||||
for (const AdapterType element : *array) {
|
||||
if (!fn(element)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool applyToObject(ObjectMemberCallback fn) const
|
||||
{
|
||||
if (!maybeObject()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value.isObject()) {
|
||||
const opt::optional<Object> object = value.getObjectOptional();
|
||||
for (const ObjectMemberType member : *object) {
|
||||
if (!fn(member.first, AdapterType(member.second))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ArrayType instance containing an array representation
|
||||
* of the value held by this Adapter.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the elements in an array. The ArrayType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained value is either an empty object, or an empty string,
|
||||
* then this function will cast the value to an empty array.
|
||||
*
|
||||
* @returns ArrayType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ArrayType asArray() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return *value.getArrayOptional();
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return ArrayType();
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue) && stringValue.empty()) {
|
||||
return ArrayType();
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to an array.");
|
||||
}
|
||||
|
||||
virtual bool asBool() const
|
||||
{
|
||||
bool result;
|
||||
if (asBool(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a boolean.");
|
||||
}
|
||||
|
||||
virtual bool asBool(bool &result) const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
return value.getBool(result);
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
if (s.compare("true") == 0) {
|
||||
result = true;
|
||||
return true;
|
||||
} else if (s.compare("false") == 0) {
|
||||
result = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual double asDouble() const
|
||||
{
|
||||
double result;
|
||||
if (asDouble(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a double.");
|
||||
}
|
||||
|
||||
virtual bool asDouble(double &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
return value.getDouble(result);
|
||||
} else if (value.isInteger()) {
|
||||
int64_t i;
|
||||
if (value.getInteger(i)) {
|
||||
result = double(i);
|
||||
return true;
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
const char *b = s.c_str();
|
||||
char *e = NULL;
|
||||
double x = strtod(b, &e);
|
||||
if (e == b || e != b + s.length()) {
|
||||
return false;
|
||||
}
|
||||
result = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual int64_t asInteger() const
|
||||
{
|
||||
int64_t result;
|
||||
if (asInteger(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast as an integer.");
|
||||
}
|
||||
|
||||
virtual bool asInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isInteger()) {
|
||||
return value.getInteger(result);
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
std::istringstream i(s);
|
||||
int64_t x;
|
||||
char c;
|
||||
if (!(!(i >> x) || i.get(c))) {
|
||||
result = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ObjectType instance containing an array representation
|
||||
* of the value held by this Adapter.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the members of the object. The ObjectType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* @returns ObjectType instance containing an object representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ObjectType asObject() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return *value.getObjectOptional();
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return ObjectType();
|
||||
}
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue) && stringValue.empty()) {
|
||||
return ObjectType();
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to an object.");
|
||||
}
|
||||
|
||||
virtual std::string asString() const
|
||||
{
|
||||
std::string result;
|
||||
if (asString(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value cannot be cast to a string.");
|
||||
}
|
||||
|
||||
virtual bool asString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
return value.getString(result);
|
||||
} else if (value.isNull()) {
|
||||
result.clear();
|
||||
return true;
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
result.clear();
|
||||
return true;
|
||||
}
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
result.clear();
|
||||
return true;
|
||||
}
|
||||
} else if (value.isBool()) {
|
||||
bool boolValue;
|
||||
if (value.getBool(boolValue)) {
|
||||
result = boolValue ? "true" : "false";
|
||||
return true;
|
||||
}
|
||||
} else if (value.isInteger()) {
|
||||
int64_t integerValue;
|
||||
if (value.getInteger(integerValue)) {
|
||||
result = std::to_string(integerValue);
|
||||
return true;
|
||||
}
|
||||
} else if (value.isDouble()) {
|
||||
double doubleValue;
|
||||
if (value.getDouble(doubleValue)) {
|
||||
result = std::to_string(doubleValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
if (isNull() || (!strict && maybeNull())) {
|
||||
return other.isNull() || (!strict && other.maybeNull());
|
||||
} else if (isBool() || (!strict && maybeBool())) {
|
||||
return (other.isBool() || (!strict && other.maybeBool())) &&
|
||||
other.asBool() == asBool();
|
||||
} else if (isNumber() && strict) {
|
||||
return other.isNumber() && other.getNumber() == getNumber();
|
||||
} else if (!strict && maybeDouble()) {
|
||||
return (other.maybeDouble() &&
|
||||
other.asDouble() == asDouble());
|
||||
} else if (!strict && maybeInteger()) {
|
||||
return (other.maybeInteger() &&
|
||||
other.asInteger() == asInteger());
|
||||
} else if (isString() || (!strict && maybeString())) {
|
||||
return (other.isString() || (!strict && other.maybeString())) &&
|
||||
other.asString() == asString();
|
||||
} else if (isArray()) {
|
||||
if (other.isArray() && getArraySize() == other.getArraySize()) {
|
||||
const opt::optional<ArrayType> array = value.getArrayOptional();
|
||||
if (array) {
|
||||
ArrayComparisonFunctor fn(*array, strict);
|
||||
return other.applyToArray(fn);
|
||||
}
|
||||
} else if (!strict && other.maybeArray() && getArraySize() == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (isObject()) {
|
||||
if (other.isObject() && other.getObjectSize() == getObjectSize()) {
|
||||
const opt::optional<ObjectType> object = value.getObjectOptional();
|
||||
if (object) {
|
||||
ObjectComparisonFunctor fn(*object, strict);
|
||||
return other.applyToObject(fn);
|
||||
}
|
||||
} else if (!strict && other.maybeObject() && getObjectSize() == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ArrayType instance representing the array contained
|
||||
* by this Adapter instance.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the elements in an array. The ArrayType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained is not an array, this function will throw an exception.
|
||||
*
|
||||
* @returns ArrayType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ArrayType getArray() const
|
||||
{
|
||||
opt::optional<ArrayType> arrayValue = value.getArrayOptional();
|
||||
if (arrayValue) {
|
||||
return *arrayValue;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an array.");
|
||||
}
|
||||
|
||||
virtual size_t getArraySize() const
|
||||
{
|
||||
size_t result;
|
||||
if (value.getArraySize(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an array.");
|
||||
}
|
||||
|
||||
virtual bool getArraySize(size_t &result) const
|
||||
{
|
||||
return value.getArraySize(result);
|
||||
}
|
||||
|
||||
virtual bool getBool() const
|
||||
{
|
||||
bool result;
|
||||
if (getBool(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a boolean.");
|
||||
}
|
||||
|
||||
virtual bool getBool(bool &result) const
|
||||
{
|
||||
return value.getBool(result);
|
||||
}
|
||||
|
||||
virtual double getDouble() const
|
||||
{
|
||||
double result;
|
||||
if (getDouble(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a double.");
|
||||
}
|
||||
|
||||
virtual bool getDouble(double &result) const
|
||||
{
|
||||
return value.getDouble(result);
|
||||
}
|
||||
|
||||
virtual int64_t getInteger() const
|
||||
{
|
||||
int64_t result;
|
||||
if (getInteger(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an integer.");
|
||||
}
|
||||
|
||||
virtual bool getInteger(int64_t &result) const
|
||||
{
|
||||
return value.getInteger(result);
|
||||
}
|
||||
|
||||
virtual double getNumber() const
|
||||
{
|
||||
double result;
|
||||
if (getNumber(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a number.");
|
||||
}
|
||||
|
||||
virtual bool getNumber(double &result) const
|
||||
{
|
||||
if (isDouble()) {
|
||||
return getDouble(result);
|
||||
} else if (isInteger()) {
|
||||
int64_t integerResult;
|
||||
if (getInteger(integerResult)) {
|
||||
result = static_cast<double>(integerResult);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an ObjectType instance representing the object contained
|
||||
* by this Adapter instance.
|
||||
*
|
||||
* This is a convenience function that is not actually declared in the
|
||||
* Adapter interface, but allows for useful techniques such as procedural
|
||||
* iteration over the members of an object. The ObjectType instance that is
|
||||
* returned by this function is compatible with the BOOST_FOREACH macro.
|
||||
*
|
||||
* If the contained is not an object, this function will throw an exception.
|
||||
*
|
||||
* @returns ObjectType instance containing an array representation of the
|
||||
* value held by this Adapter.
|
||||
*/
|
||||
ObjectType getObject() const
|
||||
{
|
||||
opt::optional<ObjectType> objectValue = value.getObjectOptional();
|
||||
if (objectValue) {
|
||||
return *objectValue;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an object.");
|
||||
}
|
||||
|
||||
virtual size_t getObjectSize() const
|
||||
{
|
||||
size_t result;
|
||||
if (getObjectSize(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not an object.");
|
||||
}
|
||||
|
||||
virtual bool getObjectSize(size_t &result) const
|
||||
{
|
||||
return value.getObjectSize(result);
|
||||
}
|
||||
|
||||
virtual std::string getString() const
|
||||
{
|
||||
std::string result;
|
||||
if (getString(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw std::runtime_error("JSON value is not a string.");
|
||||
}
|
||||
|
||||
virtual bool getString(std::string &result) const
|
||||
{
|
||||
return value.getString(result);
|
||||
}
|
||||
|
||||
virtual FrozenValue * freeze() const
|
||||
{
|
||||
return value.freeze();
|
||||
}
|
||||
|
||||
virtual bool hasStrictTypes() const
|
||||
{
|
||||
return ValueType::hasStrictTypes();
|
||||
}
|
||||
|
||||
virtual bool isArray() const
|
||||
{
|
||||
return value.isArray();
|
||||
}
|
||||
|
||||
virtual bool isBool() const
|
||||
{
|
||||
return value.isBool();
|
||||
}
|
||||
|
||||
virtual bool isDouble() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
virtual bool isInteger() const
|
||||
{
|
||||
return value.isInteger();
|
||||
}
|
||||
|
||||
virtual bool isNull() const
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
|
||||
virtual bool isNumber() const
|
||||
{
|
||||
return value.isInteger() || value.isDouble();
|
||||
}
|
||||
|
||||
virtual bool isObject() const
|
||||
{
|
||||
return value.isObject();
|
||||
}
|
||||
|
||||
virtual bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
virtual bool maybeArray() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return true;
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeBool() const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue)) {
|
||||
if (stringValue.compare("true") == 0 || stringValue.compare("false") == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeDouble() const
|
||||
{
|
||||
if (value.isNumber()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
const char *b = s.c_str();
|
||||
char *e = NULL;
|
||||
strtod(b, &e);
|
||||
return e != b && e == b + s.length();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeInteger() const
|
||||
{
|
||||
if (value.isInteger()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string s;
|
||||
if (value.getString(s)) {
|
||||
std::istringstream i(s);
|
||||
int64_t x;
|
||||
char c;
|
||||
if (!(i >> x) || i.get(c)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeNull() const
|
||||
{
|
||||
if (value.isNull()) {
|
||||
return true;
|
||||
} else if (value.isString()) {
|
||||
std::string stringValue;
|
||||
if (value.getString(stringValue)) {
|
||||
if (stringValue.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeObject() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return true;
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool maybeString() const
|
||||
{
|
||||
if (value.isString() || value.isBool() || value.isInteger() ||
|
||||
value.isDouble()) {
|
||||
return true;
|
||||
} else if (value.isObject()) {
|
||||
size_t objectSize;
|
||||
if (value.getObjectSize(objectSize) && objectSize == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
size_t arraySize;
|
||||
if (value.getArraySize(arraySize) && arraySize == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const ValueType value;
|
||||
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_FROZEN_VALUE_HPP
|
||||
#define __VALIJSON_ADAPTERS_FROZEN_VALUE_HPP
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
/**
|
||||
* @brief An interface that provides minimal access to a stored JSON value.
|
||||
*
|
||||
* The main reason that this interface exists is to support the 'enum'
|
||||
* constraint. Each Adapter type is expected to provide an implementation of
|
||||
* this interface. That class should be able to maintain its own copy of a
|
||||
* JSON value, independent of the original document.
|
||||
*
|
||||
* This interface currently provides just the clone and equalTo functions, but
|
||||
* could be expanded to include other functions declared in the Adapter
|
||||
* interface.
|
||||
*
|
||||
* @todo it would be nice to better integrate this with the Adapter interface
|
||||
*/
|
||||
class FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor defined to ensure deletion via base-class
|
||||
* pointers is safe.
|
||||
*/
|
||||
virtual ~FrozenValue() { }
|
||||
|
||||
/**
|
||||
* @brief Clone the stored value and return a pointer to a new FrozenValue
|
||||
* object containing the value.
|
||||
*/
|
||||
virtual FrozenValue *clone() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return true if the stored value is equal to the value contained
|
||||
* by an Adapter instance.
|
||||
*
|
||||
* @param adapter Adapter to compare value against
|
||||
* @param strict Flag to use strict type comparison
|
||||
*
|
||||
* @returns true if values are equal, false otherwise
|
||||
*/
|
||||
virtual bool equalTo(const Adapter &adapter, bool strict) const = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -1,713 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the json11 parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for json11.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - Json11Adapter
|
||||
* - Json11Array
|
||||
* - Json11ArrayValueIterator
|
||||
* - Json11FrozenValue
|
||||
* - Json11Object
|
||||
* - Json11ObjectMember
|
||||
* - Json11ObjectMemberIterator
|
||||
* - Json11Value
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is Json11Adapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_JSON11_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_JSON11_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <json11.hpp>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class Json11Adapter;
|
||||
class Json11ArrayValueIterator;
|
||||
class Json11ObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, Json11Adapter> Json11ObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Json11 array value.
|
||||
*
|
||||
* This class is light weight wrapper for a Json11 array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* Json11 value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class Json11Array
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Json11ArrayValueIterator const_iterator;
|
||||
typedef Json11ArrayValueIterator iterator;
|
||||
|
||||
/// Construct a Json11Array referencing an empty array.
|
||||
Json11Array()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a Json11Array referencing a specific Json11
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a Json11 value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
Json11Array(const json11::Json &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is_array()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying Json11 implementation.
|
||||
*/
|
||||
Json11ArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying Json11 implementation.
|
||||
*/
|
||||
Json11ArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.array_items().size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a Json11 value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const json11::Json & emptyArray()
|
||||
{
|
||||
static const json11::Json array((json11::Json::array()));
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const json11::Json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Json11 object.
|
||||
*
|
||||
* This class is light weight wrapper for a Json11 object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* Json11 value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class Json11Object
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Json11ObjectMemberIterator const_iterator;
|
||||
typedef Json11ObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a Json11Object referencing an empty object singleton.
|
||||
Json11Object()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a Json11Object referencing a specific Json11
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a Json11 value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
Json11Object(const json11::Json &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is_object()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying Json11 implementation.
|
||||
*/
|
||||
Json11ObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying Json11 implementation.
|
||||
*/
|
||||
Json11ObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
Json11ObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.object_items().size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a Json11 value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const json11::Json & emptyObject()
|
||||
{
|
||||
static const json11::Json object((json11::Json::object()));
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const json11::Json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a Json11 value.
|
||||
*
|
||||
* This class allows a Json11 value to be stored independent of its original
|
||||
* document. Json11 makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class Json11FrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a Json11 value
|
||||
*
|
||||
* @param source the Json11 value to be copied
|
||||
*/
|
||||
explicit Json11FrozenValue(const json11::Json &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new Json11FrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored Json11 value
|
||||
json11::Json value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Json11 value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a Json11 value. This class is responsible
|
||||
* for the mechanics of actually reading a Json11 value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class Json11Value
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
Json11Value()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific Json11 value
|
||||
Json11Value(const json11::Json &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new Json11FrozenValue instance that contains the
|
||||
* value referenced by this Json11Value instance.
|
||||
*
|
||||
* @returns pointer to a new Json11FrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new Json11FrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a Json11Array instance.
|
||||
*
|
||||
* If the referenced Json11 value is an array, this function will return
|
||||
* a std::optional containing a Json11Array instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<Json11Array> getArrayOptional() const
|
||||
{
|
||||
if (value.is_array()) {
|
||||
return opt::make_optional(Json11Array(value));
|
||||
}
|
||||
|
||||
return opt::optional<Json11Array>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced Json11 value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.is_array()) {
|
||||
result = value.array_items().size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.is_bool()) {
|
||||
result = value.bool_value();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.is_number()) {
|
||||
result = value.number_value();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if(isInteger()) {
|
||||
result = value.int_value();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a Json11Object instance.
|
||||
*
|
||||
* If the referenced Json11 value is an object, this function will return a
|
||||
* std::optional containing a Json11Object instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<Json11Object> getObjectOptional() const
|
||||
{
|
||||
if (value.is_object()) {
|
||||
return opt::make_optional(Json11Object(value));
|
||||
}
|
||||
|
||||
return opt::optional<Json11Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced Json11 value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.is_object()) {
|
||||
result = value.object_items().size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.is_string()) {
|
||||
result = value.string_value();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.is_array();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.is_bool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.is_number();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.is_number()
|
||||
&& value.int_value() == value.number_value();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.is_null();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.is_number();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.is_object();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.is_string();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const json11::Json & emptyObject()
|
||||
{
|
||||
static const json11::Json object((json11::Json::object()));
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained Json11 value.
|
||||
const json11::Json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting Json11.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class Json11Adapter:
|
||||
public BasicAdapter<Json11Adapter,
|
||||
Json11Array,
|
||||
Json11ObjectMember,
|
||||
Json11Object,
|
||||
Json11Value>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a Json11Adapter that contains an empty object
|
||||
Json11Adapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a Json11Adapter containing a specific Json11 value
|
||||
Json11Adapter(const json11::Json &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* Json11Adapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see Json11Array
|
||||
*/
|
||||
class Json11ArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
Json11Adapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new Json11ArrayValueIterator using an existing
|
||||
* Json11 iterator.
|
||||
*
|
||||
* @param itr Json11 iterator to store
|
||||
*/
|
||||
Json11ArrayValueIterator(
|
||||
const json11::Json::array::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a Json11Adapter that contains the value of the current
|
||||
/// element.
|
||||
Json11Adapter operator*() const
|
||||
{
|
||||
return Json11Adapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<Json11Adapter> operator->() const
|
||||
{
|
||||
return DerefProxy<Json11Adapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const Json11ArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const Json11ArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const Json11ArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Json11ArrayValueIterator operator++(int)
|
||||
{
|
||||
Json11ArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const Json11ArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
json11::Json::array::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of Json11ObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see Json11Object
|
||||
* @see Json11ObjectMember
|
||||
*/
|
||||
class Json11ObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
Json11ObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a Json11 iterator.
|
||||
*
|
||||
* @param itr Json11 iterator to store
|
||||
*/
|
||||
Json11ObjectMemberIterator(
|
||||
const json11::Json::object::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a Json11ObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
Json11ObjectMember operator*() const
|
||||
{
|
||||
return Json11ObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<Json11ObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<Json11ObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const Json11ObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const Json11ObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const Json11ObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Json11ObjectMemberIterator operator++(int)
|
||||
{
|
||||
Json11ObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const Json11ObjectMemberIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original Json11 iterator
|
||||
json11::Json::object::const_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for Json11Adapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::Json11Adapter>
|
||||
{
|
||||
typedef json11::Json DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "Json11Adapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool Json11FrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return Json11Adapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline Json11ArrayValueIterator Json11Array::begin() const
|
||||
{
|
||||
return value.array_items().begin();
|
||||
}
|
||||
|
||||
inline Json11ArrayValueIterator Json11Array::end() const
|
||||
{
|
||||
return value.array_items().end();
|
||||
}
|
||||
|
||||
inline Json11ObjectMemberIterator Json11Object::begin() const
|
||||
{
|
||||
return value.object_items().begin();
|
||||
}
|
||||
|
||||
inline Json11ObjectMemberIterator Json11Object::end() const
|
||||
{
|
||||
return value.object_items().end();
|
||||
}
|
||||
|
||||
inline Json11ObjectMemberIterator Json11Object::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
return value.object_items().find(propertyName);
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -1,724 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the JsonCpp parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for JsonCpp.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - JsonCppAdapter
|
||||
* - JsonCppArray
|
||||
* - JsonCppArrayValueIterator
|
||||
* - JsonCppFrozenValue
|
||||
* - JsonCppObject
|
||||
* - JsonCppObjectMember
|
||||
* - JsonCppObjectMemberIterator
|
||||
* - JsonCppValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is JsonCppAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_JSONCPP_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_JSONCPP_ADAPTER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class JsonCppAdapter;
|
||||
class JsonCppArrayValueIterator;
|
||||
class JsonCppObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, JsonCppAdapter> JsonCppObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp array value.
|
||||
*
|
||||
* This class is light weight wrapper for a JsonCpp array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* JsonCpp value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class JsonCppArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef JsonCppArrayValueIterator const_iterator;
|
||||
typedef JsonCppArrayValueIterator iterator;
|
||||
|
||||
/// Construct a JsonCppArray referencing an empty array.
|
||||
JsonCppArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a JsonCppArray referencing a specific JsonCpp value.
|
||||
*
|
||||
* @param value reference to a JsonCpp value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
JsonCppArray(const Json::Value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.isArray()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array.
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a JsonCpp value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const Json::Value & emptyArray()
|
||||
{
|
||||
static const Json::Value array(Json::arrayValue);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained array
|
||||
const Json::Value &value;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp object.
|
||||
*
|
||||
* This class is light weight wrapper for a JsonCpp object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* JsonCpp object, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class JsonCppObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef JsonCppObjectMemberIterator const_iterator;
|
||||
typedef JsonCppObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a JsonCppObject referencing an empty object singleton.
|
||||
JsonCppObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a JsonCppObject referencing a specific JsonCpp value.
|
||||
*
|
||||
* @param value reference to a JsonCpp value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
JsonCppObject(const Json::Value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.isObject()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying JsonCpp implementation.
|
||||
*/
|
||||
JsonCppObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for a member/property with the given name
|
||||
*
|
||||
* @param propertyName Property name
|
||||
*
|
||||
* @returns a valid iterator if found, or an invalid iterator if not found
|
||||
*/
|
||||
JsonCppObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Return the number of members in the object
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty JsonCpp object
|
||||
static const Json::Value & emptyObject()
|
||||
{
|
||||
static const Json::Value object(Json::objectValue);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const Json::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a JsonCpp value.
|
||||
*
|
||||
* This class allows a JsonCpp value to be stored independent of its original
|
||||
* document. JsonCpp makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class JsonCppFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a JsonCpp value
|
||||
*
|
||||
* @param source the JsonCpp value to be copied
|
||||
*/
|
||||
explicit JsonCppFrozenValue(const Json::Value &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new JsonCppFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored JsonCpp value
|
||||
Json::Value value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a JsonCpp value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a JsonCpp value. This class is responsible
|
||||
* for the mechanics of actually reading a JsonCpp value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class JsonCppValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
JsonCppValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific JsonCpp value
|
||||
JsonCppValue(const Json::Value &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new JsonCppFrozenValue instance that contains the
|
||||
* value referenced by this JsonCppValue instance.
|
||||
*
|
||||
* @returns pointer to a new JsonCppFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new JsonCppFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a JsonCppArray instance.
|
||||
*
|
||||
* If the referenced JsonCpp value is an array, this function will return a
|
||||
* std::optional containing a JsonCppArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<JsonCppArray> getArrayOptional() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return opt::make_optional(JsonCppArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<JsonCppArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced JsonCpp value is an array, this function will retrieve
|
||||
* the number of elements in the array and store it in the output variable
|
||||
* provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
result = value.asBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
result = value.asDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isIntegral()) {
|
||||
result = static_cast<int64_t>(value.asInt());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a JsonCppObject instance.
|
||||
*
|
||||
* If the referenced JsonCpp value is an object, this function will return a
|
||||
* std::optional containing a JsonCppObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<JsonCppObject> getObjectOptional() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return opt::make_optional(JsonCppObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<JsonCppObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced JsonCpp value is an object, this function will retrieve
|
||||
* the number of members in the object and store it in the output variable
|
||||
* provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
result = value.asString();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.isArray() && !value.isNull();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.isBool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.isIntegral() && !value.isBool();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.isNumeric() && !value.isBool();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.isObject() && !value.isNull();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton.
|
||||
static const Json::Value &emptyObject()
|
||||
{
|
||||
static Json::Value object(Json::objectValue);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained JsonCpp value
|
||||
const Json::Value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting JsonCpp.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class JsonCppAdapter:
|
||||
public BasicAdapter<JsonCppAdapter,
|
||||
JsonCppArray,
|
||||
JsonCppObjectMember,
|
||||
JsonCppObject,
|
||||
JsonCppValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a JsonCppAdapter that contains an empty object
|
||||
JsonCppAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a JsonCppAdapter containing a specific JsonCpp value
|
||||
JsonCppAdapter(const Json::Value &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* JsonCppAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see JsonCppArray
|
||||
*/
|
||||
class JsonCppArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
JsonCppAdapter> // value type
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new JsonCppArrayValueIterator using an existing
|
||||
* JsonCpp iterator.
|
||||
*
|
||||
* @param itr JsonCpp iterator to store
|
||||
*/
|
||||
JsonCppArrayValueIterator(const Json::Value::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a JsonCppAdapter that contains the value of the current element.
|
||||
JsonCppAdapter operator*() const
|
||||
{
|
||||
return JsonCppAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<JsonCppAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<JsonCppAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const JsonCppArrayValueIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const JsonCppArrayValueIterator &rhs) const
|
||||
{
|
||||
return !(itr == rhs.itr);
|
||||
}
|
||||
|
||||
JsonCppArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonCppArrayValueIterator operator++(int)
|
||||
{
|
||||
JsonCppArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
JsonCppArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
if (n > 0) {
|
||||
while (n-- > 0) {
|
||||
itr++;
|
||||
}
|
||||
} else {
|
||||
while (n++ < 0) {
|
||||
itr--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Json::Value::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of JsonCppObjectMember representing one of the members of the object. It has
|
||||
* been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see JsonCppObject
|
||||
* @see JsonCppObjectMember
|
||||
*/
|
||||
class JsonCppObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
JsonCppObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a JsonCpp iterator.
|
||||
*
|
||||
* @param itr JsonCpp iterator to store
|
||||
*/
|
||||
JsonCppObjectMemberIterator(const Json::ValueConstIterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a JsonCppObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
JsonCppObjectMember operator*() const
|
||||
{
|
||||
return JsonCppObjectMember(itr.key().asString(), *itr);
|
||||
}
|
||||
|
||||
DerefProxy<JsonCppObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<JsonCppObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const JsonCppObjectMemberIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const JsonCppObjectMemberIterator &rhs) const
|
||||
{
|
||||
return !(itr == rhs.itr);
|
||||
}
|
||||
|
||||
const JsonCppObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonCppObjectMemberIterator operator++(int)
|
||||
{
|
||||
JsonCppObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
JsonCppObjectMemberIterator operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original JsonCpp iterator
|
||||
Json::ValueConstIterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for JsonCppAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::JsonCppAdapter>
|
||||
{
|
||||
typedef Json::Value DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "JsonCppAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool JsonCppFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return JsonCppAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline JsonCppArrayValueIterator JsonCppArray::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline JsonCppArrayValueIterator JsonCppArray::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline JsonCppObjectMemberIterator JsonCppObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
if (value.isMember(propertyName)) {
|
||||
Json::ValueConstIterator itr;
|
||||
for ( itr = value.begin(); itr != value.end(); ++itr) {
|
||||
if (itr.key() == propertyName) {
|
||||
return itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value.end();
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -1,712 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the nlohmann json parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for nlohmann json.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - NlohmannJsonAdapter
|
||||
* - NlohmannJsonArray
|
||||
* - NlohmannJsonValueIterator
|
||||
* - NlohmannJsonFrozenValue
|
||||
* - NlohmannJsonObject
|
||||
* - NlohmannJsonObjectMember
|
||||
* - NlohmannJsonObjectMemberIterator
|
||||
* - NlohmannJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is NlohmannJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_NLOHMANN_JSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_NLOHMANN_JSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <json.hpp>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class NlohmannJsonAdapter;
|
||||
class NlohmannJsonArrayValueIterator;
|
||||
class NlohmannJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, NlohmannJsonAdapter> NlohmannJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a NlohmannJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a NlohmannJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* NlohmannJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class NlohmannJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef NlohmannJsonArrayValueIterator const_iterator;
|
||||
typedef NlohmannJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a NlohmannJsonArray referencing an empty array.
|
||||
NlohmannJsonArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a NlohmannJsonArray referencing a specific NlohmannJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a NlohmannJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
NlohmannJsonArray(const nlohmann::json &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is_array()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying NlohmannJson implementation.
|
||||
*/
|
||||
NlohmannJsonArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying NlohmannJson implementation.
|
||||
*/
|
||||
NlohmannJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a NlohmannJson value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const nlohmann::json & emptyArray()
|
||||
{
|
||||
static const nlohmann::json array = nlohmann::json::array();
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const nlohmann::json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a NlohmannJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a NlohmannJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* NlohmannJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class NlohmannJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef NlohmannJsonObjectMemberIterator const_iterator;
|
||||
typedef NlohmannJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a NlohmannJsonObject referencing an empty object singleton.
|
||||
NlohmannJsonObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a NlohmannJsonObject referencing a specific NlohmannJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a NlohmannJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
NlohmannJsonObject(const nlohmann::json &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is_object()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying NlohmannJson implementation.
|
||||
*/
|
||||
NlohmannJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying NlohmannJson implementation.
|
||||
*/
|
||||
NlohmannJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
NlohmannJsonObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a NlohmannJson value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const nlohmann::json & emptyObject()
|
||||
{
|
||||
static const nlohmann::json object = nlohmann::json::object();
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const nlohmann::json &value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a NlohmannJson value.
|
||||
*
|
||||
* This class allows a NlohmannJson value to be stored independent of its original
|
||||
* document. NlohmannJson makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class NlohmannJsonFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a NlohmannJson value
|
||||
*
|
||||
* @param source the NlohmannJson value to be copied
|
||||
*/
|
||||
explicit NlohmannJsonFrozenValue(const nlohmann::json &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new NlohmannJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored NlohmannJson value
|
||||
nlohmann::json value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a NlohmannJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a NlohmannJson value. This class is responsible
|
||||
* for the mechanics of actually reading a NlohmannJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class NlohmannJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
NlohmannJsonValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific NlohmannJson value
|
||||
NlohmannJsonValue(const nlohmann::json &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new NlohmannJsonFrozenValue instance that contains the
|
||||
* value referenced by this NlohmannJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new NlohmannJsonFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new NlohmannJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a NlohmannJsonArray instance.
|
||||
*
|
||||
* If the referenced NlohmannJson value is an array, this function will return
|
||||
* a std::optional containing a NlohmannJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<NlohmannJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.is_array()) {
|
||||
return opt::make_optional(NlohmannJsonArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<NlohmannJsonArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced NlohmannJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.is_array()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.is_boolean()) {
|
||||
result = value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.is_number_float()) {
|
||||
result = value.get<double>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if(value.is_number_integer()) {
|
||||
result = value.get<int64_t>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a NlohmannJsonObject instance.
|
||||
*
|
||||
* If the referenced NlohmannJson value is an object, this function will return a
|
||||
* std::optional containing a NlohmannJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<NlohmannJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.is_object()) {
|
||||
return opt::make_optional(NlohmannJsonObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<NlohmannJsonObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced NlohmannJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.is_object()) {
|
||||
result = value.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.is_string()) {
|
||||
result = value.get<std::string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.is_array();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.is_boolean();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.is_number_float();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.is_number_integer();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.is_null();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.is_number();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.is_object();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.is_string();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const nlohmann::json & emptyObject()
|
||||
{
|
||||
static const nlohmann::json object = nlohmann::json::object();
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained NlohmannJson value.
|
||||
const nlohmann::json &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting NlohmannJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class NlohmannJsonAdapter:
|
||||
public BasicAdapter<NlohmannJsonAdapter,
|
||||
NlohmannJsonArray,
|
||||
NlohmannJsonObjectMember,
|
||||
NlohmannJsonObject,
|
||||
NlohmannJsonValue>
|
||||
{
|
||||
public:
|
||||
/// Construct a NlohmannJsonAdapter that contains an empty object
|
||||
NlohmannJsonAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a NlohmannJsonAdapter containing a specific Nlohmann Json object
|
||||
NlohmannJsonAdapter(const nlohmann::json &value)
|
||||
: BasicAdapter(NlohmannJsonValue{value}) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* NlohmannJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see NlohmannJsonArray
|
||||
*/
|
||||
class NlohmannJsonArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
NlohmannJsonAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new NlohmannJsonArrayValueIterator using an existing
|
||||
* NlohmannJson iterator.
|
||||
*
|
||||
* @param itr NlohmannJson iterator to store
|
||||
*/
|
||||
NlohmannJsonArrayValueIterator(const nlohmann::json::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a NlohmannJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
NlohmannJsonAdapter operator*() const
|
||||
{
|
||||
return NlohmannJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<NlohmannJsonAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<NlohmannJsonAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const NlohmannJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const NlohmannJsonArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const NlohmannJsonArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NlohmannJsonArrayValueIterator operator++(int)
|
||||
{
|
||||
NlohmannJsonArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const NlohmannJsonArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
nlohmann::json::const_iterator itr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of NlohmannJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see NlohmannJsonObject
|
||||
* @see NlohmannJsonObjectMember
|
||||
*/
|
||||
class NlohmannJsonObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
NlohmannJsonObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a NlohmannJson iterator.
|
||||
*
|
||||
* @param itr NlohmannJson iterator to store
|
||||
*/
|
||||
NlohmannJsonObjectMemberIterator(const nlohmann::json::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a NlohmannJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
NlohmannJsonObjectMember operator*() const
|
||||
{
|
||||
return NlohmannJsonObjectMember(itr.key(), itr.value());
|
||||
}
|
||||
|
||||
DerefProxy<NlohmannJsonObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<NlohmannJsonObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const NlohmannJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const NlohmannJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const NlohmannJsonObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NlohmannJsonObjectMemberIterator operator++(int)
|
||||
{
|
||||
NlohmannJsonObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const NlohmannJsonObjectMemberIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original NlohmannJson iterator
|
||||
nlohmann::json::const_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for NlohmannJsonAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::NlohmannJsonAdapter>
|
||||
{
|
||||
typedef nlohmann::json DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "NlohmannJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool NlohmannJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return NlohmannJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline NlohmannJsonArrayValueIterator NlohmannJsonArray::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline NlohmannJsonArrayValueIterator NlohmannJsonArray::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline NlohmannJsonObjectMemberIterator NlohmannJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
return value.find(propertyName);
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,727 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the PicoJson parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for PicoJson.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - PicoJsonAdapter
|
||||
* - PicoJsonArray
|
||||
* - PicoJsonArrayValueIterator
|
||||
* - PicoJsonFrozenValue
|
||||
* - PicoJsonObject
|
||||
* - PicoJsonObjectMember
|
||||
* - PicoJsonObjectMemberIterator
|
||||
* - PicoJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is PicoJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_PICOJSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_PICOJSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class PicoJsonAdapter;
|
||||
class PicoJsonArrayValueIterator;
|
||||
class PicoJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, PicoJsonAdapter> PicoJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PicoJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a PicoJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* PicoJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PicoJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PicoJsonArrayValueIterator const_iterator;
|
||||
typedef PicoJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a PicoJsonArray referencing an empty array.
|
||||
PicoJsonArray()
|
||||
: value(emptyArray()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PicoJsonArray referencing a specific PicoJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a PicoJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
explicit PicoJsonArray(const picojson::value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is<picojson::array>()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying PicoJson implementation.
|
||||
*/
|
||||
PicoJsonArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying PicoJson implementation.
|
||||
*/
|
||||
PicoJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
const picojson::array &array = value.get<picojson::array>();
|
||||
return array.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a PicoJson value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const picojson::value & emptyArray()
|
||||
{
|
||||
static const picojson::value array(picojson::array_type, false);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const picojson::value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PicoJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a PicoJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* PicoJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PicoJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PicoJsonObjectMemberIterator const_iterator;
|
||||
typedef PicoJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a PicoJsonObject referencing an empty object singleton.
|
||||
PicoJsonObject()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PicoJsonObject referencing a specific PicoJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a PicoJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
PicoJsonObject(const picojson::value &value)
|
||||
: value(value)
|
||||
{
|
||||
if (!value.is<picojson::object>()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying PicoJson implementation.
|
||||
*/
|
||||
PicoJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying PicoJson implementation.
|
||||
*/
|
||||
PicoJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
PicoJsonObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
return object.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a PicoJson value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const picojson::value & emptyObject()
|
||||
{
|
||||
static const picojson::value object(picojson::object_type, false);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const picojson::value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a PicoJson value.
|
||||
*
|
||||
* This class allows a PicoJson value to be stored independent of its original
|
||||
* document. PicoJson makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class PicoJsonFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a PicoJson value
|
||||
*
|
||||
* @param source the PicoJson value to be copied
|
||||
*/
|
||||
explicit PicoJsonFrozenValue(const picojson::value &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new PicoJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored PicoJson value
|
||||
picojson::value value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PicoJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a PicoJson value. This class is responsible
|
||||
* for the mechanics of actually reading a PicoJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PicoJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
PicoJsonValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific PicoJson value
|
||||
PicoJsonValue(const picojson::value &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new PicoJsonFrozenValue instance that contains the
|
||||
* value referenced by this PicoJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new PicoJsonFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new PicoJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PicoJsonArray instance.
|
||||
*
|
||||
* If the referenced PicoJson value is an array, this function will return
|
||||
* a std::optional containing a PicoJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PicoJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.is<picojson::array>()) {
|
||||
return opt::make_optional(PicoJsonArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<PicoJsonArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced PicoJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.is<picojson::array>()) {
|
||||
const picojson::array& array = value.get<picojson::array>();
|
||||
result = array.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.is<bool>()) {
|
||||
result = value.get<bool>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.is<double>()) {
|
||||
result = value.get<double>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.is<int64_t>()) {
|
||||
result = value.get<int64_t>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PicoJsonObject instance.
|
||||
*
|
||||
* If the referenced PicoJson value is an object, this function will return a
|
||||
* std::optional containing a PicoJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PicoJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.is<picojson::object>()) {
|
||||
return opt::make_optional(PicoJsonObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<PicoJsonObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced PicoJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.is<picojson::object>()) {
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
result = object.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.is<std::string>()) {
|
||||
result = value.get<std::string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.is<picojson::array>();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.is<bool>();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
if (value.is<int64_t>()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return value.is<double>();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return value.is<int64_t>();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.is<picojson::null>();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.is<double>();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.is<picojson::object>();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.is<std::string>();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const picojson::value & emptyObject()
|
||||
{
|
||||
static const picojson::value object(picojson::object_type, false);
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained PicoJson value.
|
||||
const picojson::value &value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting PicoJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PicoJsonAdapter:
|
||||
public BasicAdapter<PicoJsonAdapter,
|
||||
PicoJsonArray,
|
||||
PicoJsonObjectMember,
|
||||
PicoJsonObject,
|
||||
PicoJsonValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a PicoJsonAdapter that contains an empty object
|
||||
PicoJsonAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a PicoJsonAdapter containing a specific PicoJson value
|
||||
PicoJsonAdapter(const picojson::value &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* PicoJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the std::iterator template.
|
||||
*
|
||||
* @see PicoJsonArray
|
||||
*/
|
||||
class PicoJsonArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PicoJsonAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new PicoJsonArrayValueIterator using an existing
|
||||
* PicoJson iterator.
|
||||
*
|
||||
* @param itr PicoJson iterator to store
|
||||
*/
|
||||
PicoJsonArrayValueIterator(
|
||||
const picojson::array::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a PicoJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
PicoJsonAdapter operator*() const
|
||||
{
|
||||
return PicoJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<PicoJsonAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<PicoJsonAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const PicoJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PicoJsonArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const PicoJsonArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PicoJsonArrayValueIterator operator++(int)
|
||||
{
|
||||
PicoJsonArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PicoJsonArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
picojson::array::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of PicoJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PicoJsonObject
|
||||
* @see PicoJsonObjectMember
|
||||
*/
|
||||
class PicoJsonObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PicoJsonObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a PicoJson iterator.
|
||||
*
|
||||
* @param itr PicoJson iterator to store
|
||||
*/
|
||||
PicoJsonObjectMemberIterator(
|
||||
const picojson::object::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a PicoJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
PicoJsonObjectMember operator*() const
|
||||
{
|
||||
return PicoJsonObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<PicoJsonObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<PicoJsonObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const PicoJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PicoJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const PicoJsonObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PicoJsonObjectMemberIterator operator++(int)
|
||||
{
|
||||
PicoJsonObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PicoJsonObjectMemberIterator& operator--(int)
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original PicoJson iterator
|
||||
picojson::object::const_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for PicoJsonAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::PicoJsonAdapter>
|
||||
{
|
||||
typedef picojson::value DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "PicoJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool PicoJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return PicoJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline PicoJsonArrayValueIterator PicoJsonArray::begin() const
|
||||
{
|
||||
const picojson::array &array = value.get<picojson::array>();
|
||||
return array.begin();
|
||||
}
|
||||
|
||||
inline PicoJsonArrayValueIterator PicoJsonArray::end() const
|
||||
{
|
||||
const picojson::array &array = value.get<picojson::array>();
|
||||
return array.end();
|
||||
}
|
||||
|
||||
inline PicoJsonObjectMemberIterator PicoJsonObject::begin() const
|
||||
{
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
return object.begin();
|
||||
}
|
||||
|
||||
inline PicoJsonObjectMemberIterator PicoJsonObject::end() const
|
||||
{
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
return object.end();
|
||||
}
|
||||
|
||||
inline PicoJsonObjectMemberIterator PicoJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
const picojson::object &object = value.get<picojson::object>();
|
||||
return object.find(propertyName);
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -1,728 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the Poco json parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for Poco json.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - PocoJsonAdapter
|
||||
* - PocoJsonArray
|
||||
* - PocoJsonValueIterator
|
||||
* - PocoJsonFrozenValue
|
||||
* - PocoJsonObject
|
||||
* - PocoJsonObjectMember
|
||||
* - PocoJsonObjectMemberIterator
|
||||
* - PocoJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is PocoJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_POCO_JSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_POCO_JSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <Poco/JSON/Object.h>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson
|
||||
{
|
||||
namespace adapters
|
||||
{
|
||||
|
||||
class PocoJsonAdapter;
|
||||
class PocoJsonArrayValueIterator;
|
||||
class PocoJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, PocoJsonAdapter> PocoJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PocoJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a PocoJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* PocoJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PocoJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PocoJsonArrayValueIterator const_iterator;
|
||||
typedef PocoJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a PocoJsonArray referencing an empty array.
|
||||
PocoJsonArray()
|
||||
: value(emptyArray())
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Construct a PocoJsonArray referencing a specific PocoJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a PocoJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
PocoJsonArray(const Poco::Dynamic::Var &value)
|
||||
: value(value)
|
||||
{
|
||||
if (value.type() != typeid(Poco::JSON::Array::Ptr)) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying PocoJson implementation.
|
||||
*/
|
||||
PocoJsonArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying PocoJson implementation.
|
||||
*/
|
||||
PocoJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Array::Ptr>()->size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a PocoJson value that is an empty array.
|
||||
*/
|
||||
static Poco::Dynamic::Var emptyArray()
|
||||
{
|
||||
Poco::Dynamic::Var array = Poco::JSON::Array::Ptr();
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Contained value
|
||||
Poco::Dynamic::Var value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PocoJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a PocoJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* PocoJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PocoJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PocoJsonObjectMemberIterator const_iterator;
|
||||
typedef PocoJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a PocoJsonObject an empty object.
|
||||
PocoJsonObject()
|
||||
: value(emptyObject())
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Construct a PocoJsonObject referencing a specific PocoJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a PocoJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
PocoJsonObject(const Poco::Dynamic::Var &value)
|
||||
: value(value)
|
||||
{
|
||||
if (value.type() != typeid(Poco::JSON::Object::Ptr)) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying PocoJson implementation.
|
||||
*/
|
||||
PocoJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying PocoJson implementation.
|
||||
*/
|
||||
PocoJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
PocoJsonObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Object::Ptr>()->size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a PocoJson value that is empty object.
|
||||
*/
|
||||
static Poco::Dynamic::Var emptyObject()
|
||||
{
|
||||
Poco::Dynamic::Var object = Poco::JSON::Object::Ptr();
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Contained value
|
||||
Poco::Dynamic::Var value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a PocoJson value.
|
||||
*
|
||||
* This class allows a PocoJson value to be stored independent of its original
|
||||
* document. PocoJson makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class PocoJsonFrozenValue : public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a PocoJson value
|
||||
*
|
||||
* @param source the PocoJson value to be copied
|
||||
*/
|
||||
explicit PocoJsonFrozenValue(const Poco::Dynamic::Var &source)
|
||||
: value(source)
|
||||
{ }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new PocoJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored PocoJson value
|
||||
Poco::Dynamic::Var value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a PocoJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a PocoJson value. This class is responsible
|
||||
* for the mechanics of actually reading a PocoJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PocoJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object
|
||||
PocoJsonValue()
|
||||
: value(emptyObject())
|
||||
{ }
|
||||
|
||||
/// Construct a wrapper for a specific PocoJson value
|
||||
PocoJsonValue(const Poco::Dynamic::Var& value)
|
||||
: value(value)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Create a new PocoJsonFrozenValue instance that contains the
|
||||
* value referenced by this PocoJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new PocoJsonFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new PocoJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PocoJsonArray instance.
|
||||
*
|
||||
* If the referenced PocoJson value is an array, this function will return
|
||||
* a std::optional containing a PocoJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PocoJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.type() == typeid(Poco::JSON::Array::Ptr)) {
|
||||
return opt::make_optional(PocoJsonArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<PocoJsonArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced PocoJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.type() == typeid(Poco::JSON::Array::Ptr)) {
|
||||
result = value.extract<Poco::JSON::Array::Ptr>()->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.isBoolean()) {
|
||||
result = value.convert<bool>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.isNumeric() && !value.isInteger()) {
|
||||
result = value.convert<double>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isInteger()) {
|
||||
result = value.convert<int>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PocoJsonObject instance.
|
||||
*
|
||||
* If the referenced PocoJson value is an object, this function will return a
|
||||
* std::optional containing a PocoJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PocoJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.type() == typeid(Poco::JSON::Object::Ptr)) {
|
||||
return opt::make_optional(PocoJsonObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<PocoJsonObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced PocoJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.type() == typeid(Poco::JSON::Object::Ptr)) {
|
||||
result = value.extract<Poco::JSON::Object::Ptr>()->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
result = value.convert<std::string>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.type() == typeid(Poco::JSON::Array::Ptr);
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.isBoolean();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.isNumeric() && !value.isInteger();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return !isBool() && value.isInteger();
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.isEmpty();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.isNumeric();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.type() == typeid(Poco::JSON::Object::Ptr);
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return an empty object
|
||||
static Poco::Dynamic::Var emptyObject()
|
||||
{
|
||||
Poco::Dynamic::Var object = Poco::JSON::Object::Ptr();
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Contained value
|
||||
Poco::Dynamic::Var value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting PocoJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PocoJsonAdapter :
|
||||
public BasicAdapter<PocoJsonAdapter,
|
||||
PocoJsonArray,
|
||||
PocoJsonObjectMember,
|
||||
PocoJsonObject,
|
||||
PocoJsonValue>
|
||||
{
|
||||
public:
|
||||
/// Construct a PocoJsonAdapter that contains an empty object
|
||||
PocoJsonAdapter()
|
||||
: BasicAdapter()
|
||||
{ }
|
||||
|
||||
/// Construct a PocoJsonAdapter containing a specific Poco Json object
|
||||
PocoJsonAdapter(const Poco::Dynamic::Var &value)
|
||||
: BasicAdapter(PocoJsonValue {value})
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* PocoJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PocoJsonArray
|
||||
*/
|
||||
class PocoJsonArrayValueIterator :
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PocoJsonAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new PocoJsonArrayValueIterator using an existing
|
||||
* PocoJson iterator.
|
||||
*
|
||||
* @param itr PocoJson iterator to store
|
||||
*/
|
||||
PocoJsonArrayValueIterator(const Poco::JSON::Array::ConstIterator &itr)
|
||||
: itr(itr)
|
||||
{ }
|
||||
|
||||
/// Returns a PocoJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
PocoJsonAdapter operator*() const
|
||||
{
|
||||
return PocoJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<PocoJsonAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<PocoJsonAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const PocoJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PocoJsonArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const PocoJsonArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PocoJsonArrayValueIterator operator++(int)
|
||||
{
|
||||
PocoJsonArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PocoJsonArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
Poco::JSON::Array::ConstIterator itr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of PocoJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PocoJsonObject
|
||||
* @see PocoJsonObjectMember
|
||||
*/
|
||||
class PocoJsonObjectMemberIterator :
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PocoJsonObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a PocoJson iterator.
|
||||
*
|
||||
* @param itr PocoJson iterator to store
|
||||
*/
|
||||
PocoJsonObjectMemberIterator(const Poco::JSON::Object::ConstIterator &itr)
|
||||
: itr(itr)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* @brief Returns a PocoJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
PocoJsonObjectMember operator*() const
|
||||
{
|
||||
return PocoJsonObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<PocoJsonObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<PocoJsonObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const PocoJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PocoJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const PocoJsonObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PocoJsonObjectMemberIterator operator++(int)
|
||||
{
|
||||
PocoJsonObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PocoJsonObjectMemberIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original PocoJson iterator
|
||||
Poco::JSON::Object::ConstIterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for PocoJsonAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::PocoJsonAdapter>
|
||||
{
|
||||
typedef Poco::Dynamic::Var DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "PocoJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline PocoJsonArrayValueIterator PocoJsonArray::begin() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Array::Ptr>()->begin();
|
||||
}
|
||||
|
||||
inline PocoJsonArrayValueIterator PocoJsonArray::end() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Array::Ptr>()->end();
|
||||
}
|
||||
|
||||
inline PocoJsonObjectMemberIterator PocoJsonObject::begin() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Object::Ptr>()->begin();
|
||||
}
|
||||
|
||||
inline PocoJsonObjectMemberIterator PocoJsonObject::end() const
|
||||
{
|
||||
return value.extract<Poco::JSON::Object::Ptr>()->end();
|
||||
}
|
||||
|
||||
inline PocoJsonObjectMemberIterator PocoJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
auto& ptr = value.extract<Poco::JSON::Object::Ptr>();
|
||||
|
||||
auto it = std::find_if(ptr->begin(), ptr->end(),
|
||||
[&propertyName](const Poco::JSON::Object::ValueType& p)
|
||||
{
|
||||
return p.first == propertyName;
|
||||
});
|
||||
return it;
|
||||
}
|
||||
|
||||
inline bool PocoJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return PocoJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif // __VALIJSON_ADAPTERS_POCO_JSON_ADAPTER_HPP
|
||||
|
||||
@@ -1,757 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the Boost property tree library.
|
||||
*
|
||||
* Include this file in your program to enable support for boost property trees.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - PropertyTreeAdapter
|
||||
* - PropertyTreeArray
|
||||
* - PropertyTreeArrayValueIterator
|
||||
* - PropertyTreeFrozenValue
|
||||
* - PropertyTreeObject
|
||||
* - PropertyTreeObjectMember
|
||||
* - PropertyTreeObjectMemberIterator
|
||||
* - PropertyTreeValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is PropertyTreeAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_PROPERTY_TREE_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_PROPERTY_TREE_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class PropertyTreeAdapter;
|
||||
class PropertyTreeArrayValueIterator;
|
||||
class PropertyTreeObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, PropertyTreeAdapter> PropertyTreeObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree that contains
|
||||
* array-like data.
|
||||
*
|
||||
* This class is light weight wrapper for a Boost property tree. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to a Boost property
|
||||
* tree that is assumed to contain unnamed key-value pairs. There is very little
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class PropertyTreeArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PropertyTreeArrayValueIterator const_iterator;
|
||||
typedef PropertyTreeArrayValueIterator iterator;
|
||||
|
||||
/// Construct a PropertyTreeArra7 referencing an empty property tree
|
||||
/// singleton.
|
||||
PropertyTreeArray()
|
||||
: array(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct PropertyTreeArray referencing a specific Boost
|
||||
* property tree.
|
||||
*
|
||||
* @param array reference to a property tree containing an array
|
||||
*
|
||||
* It is assumed that this value contains array-like data, but this is not
|
||||
* checked due to runtime cost.
|
||||
*/
|
||||
explicit PropertyTreeArray(const boost::property_tree::ptree &array)
|
||||
: array(array) { }
|
||||
|
||||
/// Return an iterator for the first element in the array.
|
||||
PropertyTreeArrayValueIterator begin() const;
|
||||
|
||||
/// Return an iterator for one-past the last element of the array.
|
||||
PropertyTreeArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return array.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a property tree that looks like an
|
||||
* empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const boost::property_tree::ptree &array;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree that contains
|
||||
* object-like data.
|
||||
*
|
||||
* This class is light weight wrapper for a Boost property tree. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* property tree value, assumed to be object-like, so there is very little
|
||||
* overhead associated with copy construction and passing by value.
|
||||
*/
|
||||
class PropertyTreeObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef PropertyTreeObjectMemberIterator const_iterator;
|
||||
typedef PropertyTreeObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a PropertyTreeObject referencing an empty property tree.
|
||||
PropertyTreeObject()
|
||||
: object(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PropertyTreeObject referencing a specific property
|
||||
* tree.
|
||||
*
|
||||
* @param object reference to a property tree containing an object
|
||||
*
|
||||
* Note that the value of the property tree is not checked, due to the
|
||||
* runtime cost of doing so.
|
||||
*/
|
||||
PropertyTreeObject(const boost::property_tree::ptree &object)
|
||||
: object(object) { }
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying property tree
|
||||
* implementation.
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the pointer value returned by the underlying property tree
|
||||
* implementation.
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param property property name to search for
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator find(const std::string &property) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return object.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to an empty property tree.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const boost::property_tree::ptree &object;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a Boost property tree.
|
||||
*
|
||||
* This class allows a property tree value to be stored independent of its
|
||||
* original 'document'. Boost property trees make this easy to do, as they do
|
||||
* not perform any custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class PropertyTreeFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a Boost property tree POD value
|
||||
*
|
||||
* @param source string containing the POD vlaue
|
||||
*/
|
||||
explicit PropertyTreeFrozenValue(
|
||||
const boost::property_tree::ptree::data_type &source)
|
||||
: value(source) { }
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a Boost property tree object or array value
|
||||
*
|
||||
* @param source the property tree to be copied
|
||||
*/
|
||||
explicit PropertyTreeFrozenValue(
|
||||
const boost::property_tree::ptree &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new PropertyTreeFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored value
|
||||
boost::property_tree::ptree value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a Boost property tree.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a Boost property tree value. This class
|
||||
* is responsible for the mechanics of actually reading a property tree, whereas
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PropertyTreeValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for an empty property tree
|
||||
PropertyTreeValue()
|
||||
: object(emptyTree()) { }
|
||||
|
||||
/**
|
||||
* @brief Construct a PropertyTreeValue from a tree object
|
||||
*
|
||||
* This function will determine whether the tree object represents an array
|
||||
* or an object by scanning the key names for any non-empty strings. In the
|
||||
* case of an empty tree object, it is not possible to determine whether it
|
||||
* is an array or an object, so it will be treated as an array by default.
|
||||
* Empty arrays are considered equal to empty objects when compared using
|
||||
* non-strict type comparison. Empty strings will also be stored as empty
|
||||
* arrays.
|
||||
*
|
||||
* @param tree Tree object to be wrapped
|
||||
*/
|
||||
PropertyTreeValue(const boost::property_tree::ptree &tree)
|
||||
{
|
||||
if (tree.data().empty()) { // No string content
|
||||
if (tree.size() == 0) { // No children
|
||||
array.emplace(tree); // Treat as empty array
|
||||
} else {
|
||||
bool isArray = true;
|
||||
boost::property_tree::ptree::const_iterator itr;
|
||||
for (itr = tree.begin(); itr != tree.end(); itr++) {
|
||||
if (!itr->first.empty()) {
|
||||
isArray = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isArray) {
|
||||
array.emplace(tree);
|
||||
} else {
|
||||
object.emplace(tree);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = tree.data();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new PropertyTreeFrozenValue instance that contains the
|
||||
* value referenced by this PropertyTreeValue instance.
|
||||
*
|
||||
* @returns pointer to a new PropertyTreeFrozenValue instance, belonging to
|
||||
* the caller.
|
||||
*/
|
||||
FrozenValue* freeze() const
|
||||
{
|
||||
if (array) {
|
||||
return new PropertyTreeFrozenValue(*array);
|
||||
} else if (object) {
|
||||
return new PropertyTreeFrozenValue(*object);
|
||||
} else {
|
||||
return new PropertyTreeFrozenValue(*value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an instance of PropertyTreeArrayAdapter.
|
||||
*
|
||||
* If the referenced property tree value is an array, this function will
|
||||
* return a std::optional containing a PropertyTreeArray instance
|
||||
* referencing the array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PropertyTreeArray> getArrayOptional() const
|
||||
{
|
||||
if (array) {
|
||||
return opt::make_optional(PropertyTreeArray(*array));
|
||||
}
|
||||
|
||||
return opt::optional<PropertyTreeArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced property tree value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (array) {
|
||||
result = array->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a PropertyTreeObject instance.
|
||||
*
|
||||
* If the referenced property tree is an object, this function will return a
|
||||
* std::optional containing a PropertyTreeObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<PropertyTreeObject> getObjectOptional() const
|
||||
{
|
||||
if (object) {
|
||||
return opt::make_optional(PropertyTreeObject(*object));
|
||||
}
|
||||
|
||||
return opt::optional<PropertyTreeObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced property tree value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (object) {
|
||||
result = object->size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value) {
|
||||
result = *value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return static_cast<bool>(array);
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return static_cast<bool>(object);
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return static_cast<bool>(value);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static const boost::property_tree::ptree & emptyTree()
|
||||
{
|
||||
static const boost::property_tree::ptree tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// Reference used if the value is known to be an array
|
||||
opt::optional<const boost::property_tree::ptree &> array;
|
||||
|
||||
/// Reference used if the value is known to be an object
|
||||
opt::optional<const boost::property_tree::ptree &> object;
|
||||
|
||||
/// Reference used if the value is known to be a POD type
|
||||
opt::optional<std::string> value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting the Boost
|
||||
* property tree library.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class PropertyTreeAdapter:
|
||||
public BasicAdapter<PropertyTreeAdapter,
|
||||
PropertyTreeArray,
|
||||
PropertyTreeObjectMember,
|
||||
PropertyTreeObject,
|
||||
PropertyTreeValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a PropertyTreeAdapter for an empty property tree
|
||||
PropertyTreeAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a PropertyTreeAdapter using a specific property tree
|
||||
PropertyTreeAdapter(const boost::property_tree::ptree &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* PropertyTreeAdapter representing a value stored in the array. It has been
|
||||
* implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PropertyTreeArray
|
||||
*/
|
||||
class PropertyTreeArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PropertyTreeAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new PropertyTreeArrayValueIterator using an existing
|
||||
* property tree iterator.
|
||||
*
|
||||
* @param itr property tree iterator to store
|
||||
*/
|
||||
PropertyTreeArrayValueIterator(
|
||||
const boost::property_tree::ptree::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a PropertyTreeAdapter that contains the value of the current
|
||||
/// element.
|
||||
PropertyTreeAdapter operator*() const
|
||||
{
|
||||
return PropertyTreeAdapter(itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<PropertyTreeAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<PropertyTreeAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const PropertyTreeArrayValueIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PropertyTreeArrayValueIterator &rhs) const
|
||||
{
|
||||
return !(itr == rhs.itr);
|
||||
}
|
||||
|
||||
const PropertyTreeArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyTreeArrayValueIterator operator++(int)
|
||||
{
|
||||
PropertyTreeArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PropertyTreeArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
if (n > 0) {
|
||||
while (n-- > 0) {
|
||||
itr++;
|
||||
}
|
||||
} else {
|
||||
while (n++ < 0) {
|
||||
itr--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
boost::property_tree::ptree::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of PropertyTreeObjectMember representing one of the members of the object.
|
||||
* It has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see PropertyTreeObject
|
||||
* @see PropertyTreeObjectMember
|
||||
*/
|
||||
class PropertyTreeObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
PropertyTreeObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a PropertyTree iterator.
|
||||
*
|
||||
* @param itr PropertyTree iterator to store
|
||||
*/
|
||||
PropertyTreeObjectMemberIterator(
|
||||
boost::property_tree::ptree::const_assoc_iterator itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a PropertyTreeObjectMember that contains the key and
|
||||
* value belonging to the object member identified by the iterator.
|
||||
*/
|
||||
PropertyTreeObjectMember operator*() const
|
||||
{
|
||||
return PropertyTreeObjectMember(itr->first, itr->second);
|
||||
}
|
||||
|
||||
DerefProxy<PropertyTreeObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<PropertyTreeObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param rhs Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const PropertyTreeObjectMemberIterator &rhs) const
|
||||
{
|
||||
return itr == rhs.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const PropertyTreeObjectMemberIterator &rhs) const
|
||||
{
|
||||
return !(itr == rhs.itr);
|
||||
}
|
||||
|
||||
const PropertyTreeObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyTreeObjectMemberIterator operator++(int)
|
||||
{
|
||||
PropertyTreeObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const PropertyTreeObjectMemberIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
boost::property_tree::ptree::const_assoc_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for PropertyTreeAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::PropertyTreeAdapter>
|
||||
{
|
||||
typedef boost::property_tree::ptree DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "PropertyTreeAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool PropertyTreeFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return PropertyTreeAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline PropertyTreeArrayValueIterator PropertyTreeArray::begin() const
|
||||
{
|
||||
return array.begin();
|
||||
}
|
||||
|
||||
inline PropertyTreeArrayValueIterator PropertyTreeArray::end() const
|
||||
{
|
||||
return array.end();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::begin() const
|
||||
{
|
||||
return object.ordered_begin();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::end() const
|
||||
{
|
||||
return object.not_found();
|
||||
}
|
||||
|
||||
inline PropertyTreeObjectMemberIterator PropertyTreeObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
const boost::property_tree::ptree::const_assoc_iterator
|
||||
itr = object.find(propertyName);
|
||||
|
||||
if (itr != object.not_found()) {
|
||||
return itr;
|
||||
}
|
||||
|
||||
return object.not_found();
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
@@ -1,725 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @brief Adapter implementation for the QtJson parser library.
|
||||
*
|
||||
* Include this file in your program to enable support for QtJson.
|
||||
*
|
||||
* This file defines the following classes (not in this order):
|
||||
* - QtJsonAdapter
|
||||
* - QtJsonArray
|
||||
* - QtJsonArrayValueIterator
|
||||
* - QtJsonFrozenValue
|
||||
* - QtJsonObject
|
||||
* - QtJsonObjectMember
|
||||
* - QtJsonObjectMemberIterator
|
||||
* - QtJsonValue
|
||||
*
|
||||
* Due to the dependencies that exist between these classes, the ordering of
|
||||
* class declarations and definitions may be a bit confusing. The best place to
|
||||
* start is QtJsonAdapter. This class definition is actually very small,
|
||||
* since most of the functionality is inherited from the BasicAdapter class.
|
||||
* Most of the classes in this file are provided as template arguments to the
|
||||
* inherited BasicAdapter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __VALIJSON_ADAPTERS_QTJSON_ADAPTER_HPP
|
||||
#define __VALIJSON_ADAPTERS_QTJSON_ADAPTER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonArray>
|
||||
|
||||
|
||||
#include <valijson/adapters/adapter.hpp>
|
||||
#include <valijson/adapters/basic_adapter.hpp>
|
||||
#include <valijson/adapters/frozen_value.hpp>
|
||||
|
||||
namespace valijson {
|
||||
namespace adapters {
|
||||
|
||||
class QtJsonAdapter;
|
||||
class QtJsonArrayValueIterator;
|
||||
class QtJsonObjectMemberIterator;
|
||||
|
||||
typedef std::pair<std::string, QtJsonAdapter> QtJsonObjectMember;
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a QtJson array value.
|
||||
*
|
||||
* This class is light weight wrapper for a QtJson array. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* QtJson value, assumed to be an array, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class QtJsonArray
|
||||
{
|
||||
public:
|
||||
|
||||
typedef QtJsonArrayValueIterator const_iterator;
|
||||
typedef QtJsonArrayValueIterator iterator;
|
||||
|
||||
/// Construct a QtJsonArray referencing an empty array.
|
||||
QtJsonArray()
|
||||
: value(emptyArray())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a QtJsonArray referencing a specific QtJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a QtJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an array.
|
||||
*/
|
||||
explicit QtJsonArray(const QJsonValue &value)
|
||||
: value(value.toArray())
|
||||
{
|
||||
if (!value.isArray()) {
|
||||
throw std::runtime_error("Value is not an array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the first element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying QtJson implementation.
|
||||
*/
|
||||
QtJsonArrayValueIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for one-past the last element of the array.
|
||||
*
|
||||
* The iterator return by this function is effectively the iterator
|
||||
* returned by the underlying QtJson implementation.
|
||||
*/
|
||||
QtJsonArrayValueIterator end() const;
|
||||
|
||||
/// Return the number of elements in the array
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a QtJson value that is an empty array.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const QJsonArray emptyArray()
|
||||
{
|
||||
static const QJsonArray array;
|
||||
return array;
|
||||
}
|
||||
|
||||
/// Reference to the contained value
|
||||
const QJsonArray value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a QtJson object.
|
||||
*
|
||||
* This class is light weight wrapper for a QtJson object. It provides a
|
||||
* minimum set of container functions and typedefs that allow it to be used as
|
||||
* an iterable container.
|
||||
*
|
||||
* An instance of this class contains a single reference to the underlying
|
||||
* QtJson value, assumed to be an object, so there is very little overhead
|
||||
* associated with copy construction and passing by value.
|
||||
*/
|
||||
class QtJsonObject
|
||||
{
|
||||
public:
|
||||
|
||||
typedef QtJsonObjectMemberIterator const_iterator;
|
||||
typedef QtJsonObjectMemberIterator iterator;
|
||||
|
||||
/// Construct a QtJsonObject referencing an empty object singleton.
|
||||
QtJsonObject()
|
||||
: value(emptyObject())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct a QtJsonObject referencing a specific QtJson
|
||||
* value.
|
||||
*
|
||||
* @param value reference to a QtJson value
|
||||
*
|
||||
* Note that this constructor will throw an exception if the value is not
|
||||
* an object.
|
||||
*/
|
||||
QtJsonObject(const QJsonValue &value)
|
||||
: value(value.toObject())
|
||||
{
|
||||
if (!value.isObject()) {
|
||||
throw std::runtime_error("Value is not an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for this first object member
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying QtJson implementation.
|
||||
*/
|
||||
QtJsonObjectMemberIterator begin() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for an invalid object member that indicates
|
||||
* the end of the collection.
|
||||
*
|
||||
* The iterator return by this function is effectively a wrapper around
|
||||
* the iterator value returned by the underlying QtJson implementation.
|
||||
*/
|
||||
QtJsonObjectMemberIterator end() const;
|
||||
|
||||
/**
|
||||
* @brief Return an iterator for the object member with the specified
|
||||
* property name.
|
||||
*
|
||||
* If an object member with the specified name does not exist, the iterator
|
||||
* returned will be the same as the iterator returned by the end() function.
|
||||
*
|
||||
* @param propertyName property name to search for
|
||||
*/
|
||||
QtJsonObjectMemberIterator find(const std::string &propertyName) const;
|
||||
|
||||
/// Returns the number of members belonging to this object.
|
||||
size_t size() const
|
||||
{
|
||||
return value.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Return a reference to a QtJson value that is empty object.
|
||||
*
|
||||
* Note that the value returned by this function is a singleton.
|
||||
*/
|
||||
static const QJsonObject emptyObject()
|
||||
{
|
||||
static const QJsonObject object;
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained object
|
||||
const QJsonObject value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stores an independent copy of a QtJson value.
|
||||
*
|
||||
* This class allows a QtJson value to be stored independent of its original
|
||||
* document. QtJson makes this easy to do, as it does not perform any
|
||||
* custom memory management.
|
||||
*
|
||||
* @see FrozenValue
|
||||
*/
|
||||
class QtJsonFrozenValue: public FrozenValue
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Make a copy of a QtJson value
|
||||
*
|
||||
* @param source the QtJson value to be copied
|
||||
*/
|
||||
explicit QtJsonFrozenValue(const QJsonValue &source)
|
||||
: value(source) { }
|
||||
|
||||
virtual FrozenValue * clone() const
|
||||
{
|
||||
return new QtJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
virtual bool equalTo(const Adapter &other, bool strict) const;
|
||||
|
||||
private:
|
||||
|
||||
/// Stored QtJson value
|
||||
QJsonValue value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Light weight wrapper for a QtJson value.
|
||||
*
|
||||
* This class is passed as an argument to the BasicAdapter template class,
|
||||
* and is used to provide access to a QtJson value. This class is responsible
|
||||
* for the mechanics of actually reading a QtJson value, whereas the
|
||||
* BasicAdapter class is responsible for the semantics of type comparisons
|
||||
* and conversions.
|
||||
*
|
||||
* The functions that need to be provided by this class are defined implicitly
|
||||
* by the implementation of the BasicAdapter template class.
|
||||
*
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class QtJsonValue
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a wrapper for the empty object singleton
|
||||
QtJsonValue()
|
||||
: value(emptyObject()) { }
|
||||
|
||||
/// Construct a wrapper for a specific QtJson value
|
||||
QtJsonValue(const QJsonValue &value)
|
||||
: value(value) { }
|
||||
|
||||
/**
|
||||
* @brief Create a new QtJsonFrozenValue instance that contains the
|
||||
* value referenced by this QtJsonValue instance.
|
||||
*
|
||||
* @returns pointer to a new QtJsonFrozenValue instance, belonging to the
|
||||
* caller.
|
||||
*/
|
||||
FrozenValue * freeze() const
|
||||
{
|
||||
return new QtJsonFrozenValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a QtJsonArray instance.
|
||||
*
|
||||
* If the referenced QtJson value is an array, this function will return
|
||||
* a std::optional containing a QtJsonArray instance referencing the
|
||||
* array.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<QtJsonArray> getArrayOptional() const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
return opt::make_optional(QtJsonArray(value));
|
||||
}
|
||||
|
||||
return opt::optional<QtJsonArray>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of elements in the array
|
||||
*
|
||||
* If the referenced QtJson value is an array, this function will
|
||||
* retrieve the number of elements in the array and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of elements was retrieved, false otherwise.
|
||||
*/
|
||||
bool getArraySize(size_t &result) const
|
||||
{
|
||||
if (value.isArray()) {
|
||||
const QJsonArray array = value.toArray();
|
||||
result = array.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getBool(bool &result) const
|
||||
{
|
||||
if (value.isBool()) {
|
||||
result = value.toBool();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getDouble(double &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
result = value.toDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getInteger(int64_t &result) const
|
||||
{
|
||||
if (value.isDouble()) {
|
||||
result = value.toInt();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optionally return a QtJsonObject instance.
|
||||
*
|
||||
* If the referenced QtJson value is an object, this function will return a
|
||||
* std::optional containing a QtJsonObject instance referencing the
|
||||
* object.
|
||||
*
|
||||
* Otherwise it will return an empty optional.
|
||||
*/
|
||||
opt::optional<QtJsonObject> getObjectOptional() const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
return opt::make_optional(QtJsonObject(value));
|
||||
}
|
||||
|
||||
return opt::optional<QtJsonObject>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Retrieve the number of members in the object
|
||||
*
|
||||
* If the referenced QtJson value is an object, this function will
|
||||
* retrieve the number of members in the object and store it in the output
|
||||
* variable provided.
|
||||
*
|
||||
* @param result reference to size_t to set with result
|
||||
*
|
||||
* @returns true if the number of members was retrieved, false otherwise.
|
||||
*/
|
||||
bool getObjectSize(size_t &result) const
|
||||
{
|
||||
if (value.isObject()) {
|
||||
const QJsonObject &object = value.toObject();
|
||||
result = object.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getString(std::string &result) const
|
||||
{
|
||||
if (value.isString()) {
|
||||
result = value.toString().toStdString();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasStrictTypes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isArray() const
|
||||
{
|
||||
return value.isArray();
|
||||
}
|
||||
|
||||
bool isBool() const
|
||||
{
|
||||
return value.isBool();
|
||||
}
|
||||
|
||||
bool isDouble() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
bool isInteger() const
|
||||
{
|
||||
//toInt returns the default value (0, 1) if the value is not a whole number
|
||||
return value.isDouble() && (value.toInt(0) == value.toInt(1));
|
||||
}
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
|
||||
bool isNumber() const
|
||||
{
|
||||
return value.isDouble();
|
||||
}
|
||||
|
||||
bool isObject() const
|
||||
{
|
||||
return value.isObject();
|
||||
}
|
||||
|
||||
bool isString() const
|
||||
{
|
||||
return value.isString();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Return a reference to an empty object singleton
|
||||
static const QJsonValue emptyObject()
|
||||
{
|
||||
static const QJsonValue object;
|
||||
return object;
|
||||
}
|
||||
|
||||
/// Reference to the contained QtJson value.
|
||||
const QJsonValue value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An implementation of the Adapter interface supporting QtJson.
|
||||
*
|
||||
* This class is defined in terms of the BasicAdapter template class, which
|
||||
* helps to ensure that all of the Adapter implementations behave consistently.
|
||||
*
|
||||
* @see Adapter
|
||||
* @see BasicAdapter
|
||||
*/
|
||||
class QtJsonAdapter:
|
||||
public BasicAdapter<QtJsonAdapter,
|
||||
QtJsonArray,
|
||||
QtJsonObjectMember,
|
||||
QtJsonObject,
|
||||
QtJsonValue>
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct a QtJsonAdapter that contains an empty object
|
||||
QtJsonAdapter()
|
||||
: BasicAdapter() { }
|
||||
|
||||
/// Construct a QtJsonAdapter containing a specific QtJson value
|
||||
QtJsonAdapter(const QJsonValue &value)
|
||||
: BasicAdapter(value) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over values held in a JSON array.
|
||||
*
|
||||
* This class provides a JSON array iterator that dereferences as an instance of
|
||||
* QtJsonAdapter representing a value stored in the array. It has been
|
||||
* implemented using the std::iterator template.
|
||||
*
|
||||
* @see QtJsonArray
|
||||
*/
|
||||
class QtJsonArrayValueIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
QtJsonAdapter> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new QtJsonArrayValueIterator using an existing
|
||||
* QtJson iterator.
|
||||
*
|
||||
* @param itr QtJson iterator to store
|
||||
*/
|
||||
QtJsonArrayValueIterator(
|
||||
const QJsonArray::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/// Returns a QtJsonAdapter that contains the value of the current
|
||||
/// element.
|
||||
QtJsonAdapter operator*() const
|
||||
{
|
||||
return QtJsonAdapter(*itr);
|
||||
}
|
||||
|
||||
DerefProxy<QtJsonAdapter> operator->() const
|
||||
{
|
||||
return DerefProxy<QtJsonAdapter>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator against another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other iterator to compare against
|
||||
*
|
||||
* @returns true if the iterators are equal, false otherwise.
|
||||
*/
|
||||
bool operator==(const QtJsonArrayValueIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const QtJsonArrayValueIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const QtJsonArrayValueIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
QtJsonArrayValueIterator operator++(int)
|
||||
{
|
||||
QtJsonArrayValueIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const QtJsonArrayValueIterator& operator--()
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
itr += n;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
QJsonArray::const_iterator itr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class for iterating over the members belonging to a JSON object.
|
||||
*
|
||||
* This class provides a JSON object iterator that dereferences as an instance
|
||||
* of QtJsonObjectMember representing one of the members of the object. It
|
||||
* has been implemented using the boost iterator_facade template.
|
||||
*
|
||||
* @see QtJsonObject
|
||||
* @see QtJsonObjectMember
|
||||
*/
|
||||
class QtJsonObjectMemberIterator:
|
||||
public std::iterator<
|
||||
std::bidirectional_iterator_tag, // bi-directional iterator
|
||||
QtJsonObjectMember> // value type
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct an iterator from a QtJson iterator.
|
||||
*
|
||||
* @param itr QtJson iterator to store
|
||||
*/
|
||||
QtJsonObjectMemberIterator(
|
||||
const QJsonObject::const_iterator &itr)
|
||||
: itr(itr) { }
|
||||
|
||||
/**
|
||||
* @brief Returns a QtJsonObjectMember that contains the key and value
|
||||
* belonging to the object member identified by the iterator.
|
||||
*/
|
||||
QtJsonObjectMember operator*() const
|
||||
{
|
||||
std::string key = itr.key().toStdString();
|
||||
return QtJsonObjectMember(key, itr.value());
|
||||
}
|
||||
|
||||
DerefProxy<QtJsonObjectMember> operator->() const
|
||||
{
|
||||
return DerefProxy<QtJsonObjectMember>(**this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare this iterator with another iterator.
|
||||
*
|
||||
* Note that this directly compares the iterators, not the underlying
|
||||
* values, and assumes that two identical iterators will point to the same
|
||||
* underlying object.
|
||||
*
|
||||
* @param other Iterator to compare with
|
||||
*
|
||||
* @returns true if the underlying iterators are equal, false otherwise
|
||||
*/
|
||||
bool operator==(const QtJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return itr == other.itr;
|
||||
}
|
||||
|
||||
bool operator!=(const QtJsonObjectMemberIterator &other) const
|
||||
{
|
||||
return !(itr == other.itr);
|
||||
}
|
||||
|
||||
const QtJsonObjectMemberIterator& operator++()
|
||||
{
|
||||
itr++;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
QtJsonObjectMemberIterator operator++(int)
|
||||
{
|
||||
QtJsonObjectMemberIterator iterator_pre(itr);
|
||||
++(*this);
|
||||
return iterator_pre;
|
||||
}
|
||||
|
||||
const QtJsonObjectMemberIterator& operator--(int)
|
||||
{
|
||||
itr--;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// Iternal copy of the original QtJson iterator
|
||||
QJsonObject::const_iterator itr;
|
||||
};
|
||||
|
||||
/// Specialisation of the AdapterTraits template struct for QtJsonAdapter.
|
||||
template<>
|
||||
struct AdapterTraits<valijson::adapters::QtJsonAdapter>
|
||||
{
|
||||
typedef QJsonValue DocumentType;
|
||||
|
||||
static std::string adapterName()
|
||||
{
|
||||
return "QtJsonAdapter";
|
||||
}
|
||||
};
|
||||
|
||||
inline bool QtJsonFrozenValue::equalTo(const Adapter &other, bool strict) const
|
||||
{
|
||||
return QtJsonAdapter(value).equalTo(other, strict);
|
||||
}
|
||||
|
||||
inline QtJsonArrayValueIterator QtJsonArray::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline QtJsonArrayValueIterator QtJsonArray::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline QtJsonObjectMemberIterator QtJsonObject::begin() const
|
||||
{
|
||||
return value.begin();
|
||||
}
|
||||
|
||||
inline QtJsonObjectMemberIterator QtJsonObject::end() const
|
||||
{
|
||||
return value.end();
|
||||
}
|
||||
|
||||
inline QtJsonObjectMemberIterator QtJsonObject::find(
|
||||
const std::string &propertyName) const
|
||||
{
|
||||
return value.find(QString::fromStdString(propertyName));
|
||||
}
|
||||
|
||||
} // namespace adapters
|
||||
} // namespace valijson
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user