Compare commits

..

1 Commits

Author SHA1 Message Date
Syoyo Fujita
2026e03fab Add how to compile doc. 2017-11-27 01:01:06 +09:00
144 changed files with 22168 additions and 70222 deletions

70
.gitignore vendored
View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -1,3 +0,0 @@
cd examples\raytrace
..\..\tools\windows\premake5.exe vs2015
msbuild NanoSGSolution.sln /property:Configuration=Release

View File

@@ -1,7 +0,0 @@
.vs
Debug
x64
packages
!*.sln
!*.vcxproj*

View File

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

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

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

View File

@@ -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

View File

@@ -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()
{
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>
class Shaders
{
public:
GLuint pid;
Shaders();
~Shaders();
};

View File

@@ -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);
}

View File

@@ -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();
};

View File

@@ -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);
}
}
};

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -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

View File

@@ -1,7 +0,0 @@
.vs
Debug
x64
packages
!*.sln
!*.vcxproj*

View File

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

View File

@@ -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

View File

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

View File

@@ -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");

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"}

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

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

View File

@@ -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.
![screenshot of the demo program](images/nanosg-demo.png)
![Animation showing node manipulation](https://media.giphy.com/media/l3JDO29fMFndyObHW/giphy.gif)
## Build
### Linux or macOS
```bash
premake5 gmake
make
```
$ 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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() {

View File

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

View File

@@ -1 +0,0 @@
#include "nanort.h"

View File

@@ -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_

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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" }

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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));

View File

@@ -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);
};
};

View File

@@ -1,3 +0,0 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

View File

@@ -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

View File

@@ -1,2 +0,0 @@
all:
clang++ -std=c++11 -I../../ -g -O1 -o saver main.cc

View File

@@ -1 +0,0 @@
# Simple serialization API sample.

View File

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

View File

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

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

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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

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

View File

@@ -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

View File

@@ -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

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

View 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__ */

View 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

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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