Introduce libs/viewer and serializable view settings.

This introduces the `viewer::Settings` struct, and a JSON reader /
writer.

This will be used for automated testing and for client / server
communication.

Note that `viewer::Settings` is closely associated with the
`filament::View` API; when updating the latter we will often need to
update the former, as well as some serialization code. This increases
the maintenance burden and I think we should consider using a parser
library like libclang or a macro-based reflection utility.

This PR also migrates SimpleViewer into libs/viewer and un-inlines its
implementation. It does not belong in gltfio because it has an imgui
dependency.
This commit is contained in:
Philip Rideout
2020-09-25 17:04:13 -07:00
parent cdb0f20c6c
commit 7997e4aa21
13 changed files with 1382 additions and 221 deletions

View File

@@ -593,7 +593,6 @@ set(FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)
if (WEBGL)
add_subdirectory(web/filament-js)
add_subdirectory(web/samples)
add_subdirectory(${EXTERNAL}/imgui/tnt)
endif()
if (IS_HOST_PLATFORM)
@@ -601,6 +600,7 @@ if (IS_HOST_PLATFORM)
add_subdirectory(${LIBRARIES}/filamentapp)
add_subdirectory(${LIBRARIES}/filagui)
add_subdirectory(${LIBRARIES}/imageio)
add_subdirectory(${LIBRARIES}/viewer)
add_subdirectory(${FILAMENT}/java/filamat)
add_subdirectory(${FILAMENT}/java/filament)

View File

@@ -29,7 +29,6 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/include/gltfio/AssetLoader.h
${GLTFIO_DIR}/include/gltfio/MaterialProvider.h
${GLTFIO_DIR}/include/gltfio/ResourceLoader.h
${GLTFIO_DIR}/include/gltfio/SimpleViewer.h
${GLTFIO_DIR}/include/gltfio/FilamentAsset.h
${GLTFIO_DIR}/include/gltfio/FilamentInstance.h

View File

@@ -1,3 +1,4 @@
libs/viewer/test_settings
filament/test/test_filament --gtest_filter=-FilamentTest.FroxelData:FilamentExposureWithEngineTest.SetExposure:FilamentExposureWithEngineTest.ComputeEV100:RenderingTest.*
filament/test/test_material_parser
libs/math/test_math

View File

@@ -12,7 +12,6 @@ set(PUBLIC_HDRS
include/gltfio/AssetLoader.h
include/gltfio/MaterialProvider.h
include/gltfio/ResourceLoader.h
include/gltfio/SimpleViewer.h
include/gltfio/FilamentAsset.h
include/gltfio/FilamentInstance.h
)

View File

@@ -0,0 +1,49 @@
cmake_minimum_required(VERSION 3.10)
project(viewer)
set(TARGET viewer)
set(PUBLIC_HDR_DIR include)
# ==================================================================================================
# Sources and headers
# ==================================================================================================
set(PUBLIC_HDRS
include/viewer/SimpleViewer.h
include/viewer/Settings.h
)
set(SRCS
src/SimpleViewer.cpp
src/Settings.cpp
)
# ==================================================================================================
# Include and target definitions
# ==================================================================================================
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
target_link_libraries(${TARGET} PUBLIC imgui cgltf filament gltfio filagui)
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
# ==================================================================================================
# Compiler flags
# ==================================================================================================
if (MSVC)
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:/fp:fast>)
else()
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:-ffast-math>)
target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register)
endif()
# ==================================================================================================
# Installation
# ==================================================================================================
install(TARGETS ${TARGET} ARCHIVE DESTINATION lib/${DIST_DIR})
install(DIRECTORY ${PUBLIC_HDR_DIR}/viewer DESTINATION include)
# ==================================================================================================
# Tests
# ==================================================================================================
if (NOT ANDROID AND NOT WEBGL AND NOT IOS)
add_executable(test_settings tests/test_settings.cpp)
target_link_libraries(test_settings PRIVATE ${TARGET} gtest)
endif()

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by mIcable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef VIEWER_SETTINGS_H
#define VIEWER_SETTINGS_H
#include <stddef.h>
#include <stdint.h>
#include <filament/ColorGrading.h>
#include <filament/View.h>
namespace filament {
namespace viewer {
struct ColorGradingSettings;
struct DynamicLightingSettings;
struct Settings;
struct ViewSettings;
using AmbientOcclusionOptions = filament::View::AmbientOcclusionOptions;
using AntiAliasing = filament::View::AntiAliasing;
using BloomOptions = filament::View::BloomOptions;
using DepthOfFieldOptions = filament::View::DepthOfFieldOptions;
using Dithering = filament::View::Dithering;
using FogOptions = filament::View::FogOptions;
using RenderQuality = filament::View::RenderQuality;
using ShadowType = filament::View::ShadowType;
using TemporalAntiAliasingOptions = filament::View::TemporalAntiAliasingOptions;
using ToneMapping = filament::ColorGrading::ToneMapping;
using VignetteOptions = filament::View::VignetteOptions;
// Reads the given JSON blob and updates the corresponding fields in the given Settings object.
//
// - The given JSON blob need not specify all settings.
// - Returns true if successful.
// - This function writes warnings and error messages into the utils log.
bool readJson(const char* jsonChunk, size_t size, Settings* out);
// Generates human-readable JSON strings from settings objects.
std::string writeJson(const AmbientOcclusionOptions& out);
std::string writeJson(const BloomOptions& out);
std::string writeJson(const ColorGradingSettings& out);
std::string writeJson(const DepthOfFieldOptions& out);
std::string writeJson(const DynamicLightingSettings& out);
std::string writeJson(const FogOptions& out);
std::string writeJson(const RenderQuality& out);
std::string writeJson(const Settings& out);
std::string writeJson(const TemporalAntiAliasingOptions& out);
std::string writeJson(const ViewSettings& out);
std::string writeJson(const VignetteOptions& out);
struct ColorGradingSettings {
bool enabled = true;
filament::ColorGrading::QualityLevel quality = filament::ColorGrading::QualityLevel::MEDIUM;
ToneMapping toneMapping = ToneMapping::ACES_LEGACY;
int temperature = 0;
int tint = 0;
math::float3 outRed{1.0f, 0.0f, 0.0f};
math::float3 outGreen{0.0f, 1.0f, 0.0f};
math::float3 outBlue{0.0f, 0.0f, 1.0f};
math::float4 shadows{1.0f, 1.0f, 1.0f, 0.0f};
math::float4 midtones{1.0f, 1.0f, 1.0f, 0.0f};
math::float4 highlights{1.0f, 1.0f, 1.0f, 0.0f};
math::float4 ranges{0.0f, 0.333f, 0.550f, 1.0f};
float contrast = 1.0f;
float vibrance = 1.0f;
float saturation = 1.0f;
math::float3 slope{1.0f};
math::float3 offset{0.0f};
math::float3 power{1.0f};
math::float3 gamma{1.0f};
math::float3 midPoint{1.0f};
math::float3 scale{1.0f};
bool linkedCurves = false;
};
struct DynamicLightingSettings {
float zLightNear = 5;
float zLightFar = 100;
};
// This defines fields in the same order as the setter methods in filament::View.
struct ViewSettings {
uint8_t sampleCount = 1;
AntiAliasing antiAliasing = AntiAliasing::FXAA;
TemporalAntiAliasingOptions taa;
ColorGradingSettings colorGrading;
AmbientOcclusionOptions ssao;
BloomOptions bloom;
FogOptions fog;
DepthOfFieldOptions dof;
VignetteOptions vignette;
Dithering dithering = Dithering::TEMPORAL;
RenderQuality renderQuality;
DynamicLightingSettings dynamicLighting;
ShadowType shadowType = ShadowType::PCF;
bool postProcessingEnabled = true;
};
struct Settings {
ViewSettings view;
};
} // namespace viewer
} // namespace filament
#endif // VIEWER_SETTINGS_H

View File

@@ -0,0 +1,217 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by mIcable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef VIEWER_SIMPLEVIEWER_H
#define VIEWER_SIMPLEVIEWER_H
#include <filament/Box.h>
#include <filament/DebugRegistry.h>
#include <filament/Engine.h>
#include <filament/IndirectLight.h>
#include <filament/Scene.h>
#include <filament/View.h>
#include <gltfio/Animator.h>
#include <gltfio/FilamentAsset.h>
#include <utils/Entity.h>
#include <math/vec3.h>
namespace filament {
namespace viewer {
/**
* \class SimpleViewer SimpleViewer.h gltfio/SimpleViewer.h
* \brief Manages the state for a simple glTF viewer with imgui controls and a tree view.
*
* This is a utility that can be used across multiple platforms, including web.
*
* \note If you don't need ImGui controls, there is no need to use this class, just use AssetLoader
* instead.
*/
class SimpleViewer {
public:
using Animator = gltfio::Animator;
using FilamentAsset = gltfio::FilamentAsset;
using FilamentInstance = gltfio::FilamentInstance;
static constexpr int DEFAULT_SIDEBAR_WIDTH = 350;
/**
* Constructs a SimpleViewer that has a fixed association with the given Filament objects.
*
* Upon construction, the simple viewer may create some additional Filament objects (such as
* light sources) that it owns.
*/
SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view,
int sidebarWidth = DEFAULT_SIDEBAR_WIDTH);
/**
* Destroys the SimpleViewer and any Filament entities that it owns.
*/
~SimpleViewer();
/**
* Adds the asset's ready-to-render entities into the scene and optionally transforms the root
* node to make it fit into a unit cube at the origin.
*
* The viewer does not claim ownership over the asset or its entities. Clients should use
* AssetLoader and ResourceLoader to load an asset before passing it in.
*
* @param asset The asset to view.
* @param scale Adds a transform to the root to fit the asset into a unit cube at the origin.
* @param instanceToAnimate Optional instance from which to get the animator.
*/
void populateScene(FilamentAsset* asset, bool scale,
FilamentInstance* instanceToAnimate = nullptr);
/**
* Removes the current asset from the viewer.
*
* This removes all the asset entities from the Scene, but does not destroy them.
*/
void removeAsset();
/**
* Sets or changes the current scene's IBL to allow the UI manipulate it.
*/
void setIndirectLight(filament::IndirectLight* ibl, filament::math::float3 const* sh3);
/**
* Applies the currently-selected glTF animation to the transformation hierarchy and updates
* the bone matrices on all renderables.
*/
void applyAnimation(double currentTime);
/**
* Constructs ImGui controls for the current frame and responds to everything that the user has
* changed since the previous frame.
*
* If desired this can be used in conjunction with the filagui library, which allows clients to
* render ImGui controls with Filament.
*/
void updateUserInterface();
/**
* Retrieves the current width of the ImGui "window" which we are using as a sidebar.
* Clients can monitor this value to adjust the size of the view.
*/
int getSidebarWidth() const { return mSidebarWidth; }
/**
* Allows clients to inject custom UI.
*/
void setUiCallback(std::function<void()> callback) { mCustomUI = callback; }
/**
* Draws the bounding box of each renderable.
* Defaults to false.
*/
void enableWireframe(bool b) { mEnableWireframe = b; }
/**
* Enables a built-in light source (useful for creating shadows).
* Defaults to true.
*/
void enableSunlight(bool b) { mEnableSunlight = b; }
/**
* Enables dithering on the view.
* Defaults to true.
*/
void enableDithering(bool b) { mEnableDithering = b; }
/**
* Enables FXAA antialiasing in the post-process pipeline.
* Defaults to true.
*/
void enableFxaa(bool b) { mEnableFxaa = b; }
/**
* Enables hardware-based MSAA antialiasing.
* Defaults to true.
*/
void enableMsaa(bool b) { mEnableMsaa = b; }
/**
* Enables screen-space ambient occlusion in the post-process pipeline.
* Defaults to true.
*/
void enableSSAO(bool b) { mSSAOOptions.enabled = b; }
/**
* Enables Bloom.
* Defaults to true.
*/
void enableBloom(bool bloom) {
mBloomOptions.enabled = bloom;
}
/**
* Adjusts the intensity of the IBL.
* See also filament::IndirectLight::setIntensity().
* Defaults to 30000.0.
*/
void setIBLIntensity(float brightness) { mIblIntensity = brightness; }
private:
void updateIndirectLight();
// Immutable properties set from the constructor.
filament::Engine* const mEngine;
filament::Scene* const mScene;
filament::View* const mView;
const utils::Entity mSunlight;
// Properties that can be changed from the application.
FilamentAsset* mAsset = nullptr;
Animator* mAnimator = nullptr;
filament::IndirectLight* mIndirectLight = nullptr;
std::function<void()> mCustomUI;
// Properties that can be changed from the UI.
int mCurrentAnimation = 1;
bool mResetAnimation = true;
float mIblIntensity = 30000.0f;
float mIblRotation = 0.0f;
float mSunlightIntensity = 100000.0f; // <-- This value is overridden when loading an IBL.
filament::math::float3 mSunlightColor = filament::Color::toLinear<filament::ACCURATE>({ 0.98, 0.92, 0.89});
filament::math::float3 mSunlightDirection = {0.6, -1.0, -0.8};
bool mEnableWireframe = false;
bool mEnableSunlight = true;
bool mEnableVsm = false;
bool mEnableShadows = true;
int mShadowCascades = 1;
bool mEnableContactShadows = false;
std::array<float, 3> mSplitPositions = {0.25f, 0.50f, 0.75f};
bool mEnableDithering = true;
bool mEnableFxaa = true;
bool mEnableMsaa = true;
filament::View::AmbientOcclusionOptions mSSAOOptions = { .enabled = true };
filament::View::BloomOptions mBloomOptions = { .enabled = true };
filament::View::FogOptions mFogOptions = {};
filament::View::TemporalAntiAliasingOptions mTAAOptions = {};
int mSidebarWidth;
uint32_t mFlags;
};
filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds);
} // namespace viewer
} // namespace filament
#endif // VIEWER_SIMPLEVIEWER_H

View File

@@ -0,0 +1,834 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by mIcable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <viewer/Settings.h>
#include <utils/Log.h>
#include <sstream>
#include <string>
#include <assert.h>
// NOTE: We are including cgltf only to get JSMN. We should consider adding jsmn to third_party.
#define CGLTF_IMPLEMENTATION
#include <cgltf.h>
#define CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return -1; }
#define CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return -1; }
#define STR(tok, jsonChunk) std::string(jsonChunk + tok.start, tok.end - tok.start)
using namespace utils;
namespace filament {
namespace viewer {
// Compares a JSON string token against a C string.
static int compare(jsmntok_t tok, const char* jsonChunk, const char* str) {
size_t slen = strlen(str);
size_t tlen = tok.end - tok.start;
return (slen == tlen) ? strncmp(jsonChunk + tok.start, str, slen) : 128;
}
// Skips over an unused token.
static int parse(jsmntok_t const* tokens, int i) {
int end = i + 1;
while (i < end) {
switch (tokens[i].type) {
case JSMN_OBJECT:
end += tokens[i].size * 2; break;
case JSMN_ARRAY:
end += tokens[i].size; break;
case JSMN_PRIMITIVE:
case JSMN_STRING:
break;
default: return -1;
}
i++;
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, uint8_t* val) {
CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
*val = strtol(jsonChunk + tokens[i].start, nullptr, 10);
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, uint32_t* val) {
CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
*val = strtol(jsonChunk + tokens[i].start, nullptr, 10);
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, int* val) {
CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
*val = strtol(jsonChunk + tokens[i].start, nullptr, 10);
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, float* val) {
CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
*val = strtod(jsonChunk + tokens[i].start, nullptr);
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, float* vals, int size) {
CHECK_TOKTYPE(tokens[i], JSMN_ARRAY);
if (tokens[i].size != size) {
slog.w << "Expected " << size << " floats, got " << tokens[i].size << io::endl;
return i + 1 + tokens[i].size;
}
++i;
for (int j = 0; j < size; ++j) {
i = parse(tokens, i, jsonChunk, &vals[j]);
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, bool* val) {
CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE);
*val = 0 == compare(tokens[i], jsonChunk, "true");
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, math::float3* val) {
float values[3];
i = parse(tokens, i, jsonChunk, values, 3);
*val = {values[0], values[1], values[2]};
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, math::float4* val) {
float values[4];
i = parse(tokens, i, jsonChunk, values, 4);
*val = {values[0], values[1], values[2], values[3]};
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AntiAliasing* out) {
if (0 == compare(tokens[i], jsonChunk, "NONE")) { *out = AntiAliasing::NONE; }
else if (0 == compare(tokens[i], jsonChunk, "FXAA")) { *out = AntiAliasing::FXAA; }
else {
slog.w << "Invalid AntiAliasing: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
using BlendMode = BloomOptions::BlendMode;
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, BlendMode* out) {
if (0 == compare(tokens[i], jsonChunk, "ADD")) { *out = BlendMode::ADD; }
else if (0 == compare(tokens[i], jsonChunk, "INTERPOLATE")) { *out = BlendMode::INTERPOLATE; }
else {
slog.w << "Invalid BlendMode: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
using VQL = filament::View::QualityLevel;
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, VQL* out) {
if (0 == compare(tokens[i], jsonChunk, "LOW")) { *out = VQL::LOW; }
else if (0 == compare(tokens[i], jsonChunk, "MEDIUM")) { *out = VQL::MEDIUM; }
else if (0 == compare(tokens[i], jsonChunk, "HIGH")) { *out = VQL::HIGH; }
else if (0 == compare(tokens[i], jsonChunk, "ULTRA")) { *out = VQL::ULTRA; }
else {
slog.w << "Invalid QualityLevel: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
using CGQL = filament::ColorGrading::QualityLevel;
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, CGQL* out) {
if (0 == compare(tokens[i], jsonChunk, "LOW")) { *out = CGQL::LOW; }
else if (0 == compare(tokens[i], jsonChunk, "MEDIUM")) { *out = CGQL::MEDIUM; }
else if (0 == compare(tokens[i], jsonChunk, "HIGH")) { *out = CGQL::HIGH; }
else if (0 == compare(tokens[i], jsonChunk, "ULTRA")) { *out = CGQL::ULTRA; }
else {
slog.w << "Invalid QualityLevel: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ToneMapping* out) {
if (0 == compare(tokens[i], jsonChunk, "LINEAR")) { *out = ToneMapping::LINEAR; }
else if (0 == compare(tokens[i], jsonChunk, "ACES_LEGACY")) { *out = ToneMapping::ACES_LEGACY; }
else if (0 == compare(tokens[i], jsonChunk, "ACES")) { *out = ToneMapping::ACES; }
else if (0 == compare(tokens[i], jsonChunk, "FILMIC")) { *out = ToneMapping::FILMIC; }
else if (0 == compare(tokens[i], jsonChunk, "UCHIMURA")) { *out = ToneMapping::UCHIMURA; }
else if (0 == compare(tokens[i], jsonChunk, "REINHARD")) { *out = ToneMapping::REINHARD; }
else if (0 == compare(tokens[i], jsonChunk, "DISPLAY_RANGE")) { *out = ToneMapping::DISPLAY_RANGE; }
else {
slog.w << "Invalid ToneMapping: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, Dithering* out) {
if (0 == compare(tokens[i], jsonChunk, "NONE")) { *out = Dithering::NONE; }
else if (0 == compare(tokens[i], jsonChunk, "TEMPORAL")) { *out = Dithering::TEMPORAL; }
else {
slog.w << "Invalid Dithering: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ShadowType* out) {
if (0 == compare(tokens[i], jsonChunk, "PCF")) { *out = ShadowType::PCF; }
else if (0 == compare(tokens[i], jsonChunk, "VSM")) { *out = ShadowType::VSM; }
else {
slog.w << "Invalid ShadowType: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
TemporalAntiAliasingOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "filterWidth") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->filterWidth);
} else if (compare(tok, jsonChunk, "feedback") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->feedback);
} else if (compare(tok, jsonChunk, "enabled") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else {
slog.w << "Invalid taa key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid taa value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ColorGradingSettings* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "enabled") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else if (compare(tok, jsonChunk, "quality") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->quality);
} else if (compare(tok, jsonChunk, "toneMapping") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->toneMapping);
} else if (compare(tok, jsonChunk, "temperature") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->temperature);
} else if (compare(tok, jsonChunk, "tint") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->tint);
} else if (compare(tok, jsonChunk, "outRed") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->outRed);
} else if (compare(tok, jsonChunk, "outGreen") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->outGreen);
} else if (compare(tok, jsonChunk, "outBlue") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->outBlue);
} else if (compare(tok, jsonChunk, "shadows") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->shadows);
} else if (compare(tok, jsonChunk, "midtones") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->midtones);
} else if (compare(tok, jsonChunk, "highlights") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->highlights);
} else if (compare(tok, jsonChunk, "ranges") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->ranges);
} else if (compare(tok, jsonChunk, "contrast") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->contrast);
} else if (compare(tok, jsonChunk, "vibrance") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->vibrance);
} else if (compare(tok, jsonChunk, "saturation") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->saturation);
} else if (compare(tok, jsonChunk, "slope") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->slope);
} else if (compare(tok, jsonChunk, "offset") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->offset);
} else if (compare(tok, jsonChunk, "power") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->power);
} else if (compare(tok, jsonChunk, "gamma") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->gamma);
} else if (compare(tok, jsonChunk, "midPoint") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->midPoint);
} else if (compare(tok, jsonChunk, "scale") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->scale);
} else if (compare(tok, jsonChunk, "linkedCurves") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->linkedCurves);
} else {
slog.w << "Invalid color grading key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid color grading value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
AmbientOcclusionOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "radius") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->radius);
} else if (compare(tok, jsonChunk, "power") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->power);
} else if (compare(tok, jsonChunk, "bias") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->bias);
} else if (compare(tok, jsonChunk, "resolution") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->resolution);
} else if (compare(tok, jsonChunk, "intensity") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->intensity);
} else if (compare(tok, jsonChunk, "quality") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->quality);
} else if (compare(tok, jsonChunk, "upsampling") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->upsampling);
} else if (compare(tok, jsonChunk, "enabled") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else if (compare(tok, jsonChunk, "minHorizonAngleRad") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->minHorizonAngleRad);
} else {
slog.w << "Invalid AO key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid AO value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, BloomOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "strength") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->strength);
} else if (compare(tok, jsonChunk, "resolution") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->resolution);
} else if (compare(tok, jsonChunk, "anamorphism") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->anamorphism);
} else if (compare(tok, jsonChunk, "levels") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->levels);
} else if (compare(tok, jsonChunk, "blendMode") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->blendMode);
} else if (compare(tok, jsonChunk, "threshold") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->threshold);
} else if (compare(tok, jsonChunk, "enabled") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else if (compare(tok, jsonChunk, "highlight") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->highlight);
} else {
slog.w << "Invalid bloom options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid bloom options value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, FogOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (0 == compare(tok, jsonChunk, "distance")) {
i = parse(tokens, i + 1, jsonChunk, &out->distance);
} else if (0 == compare(tok, jsonChunk, "maximumOpacity")) {
i = parse(tokens, i + 1, jsonChunk, &out->maximumOpacity);
} else if (0 == compare(tok, jsonChunk, "height")) {
i = parse(tokens, i + 1, jsonChunk, &out->height);
} else if (0 == compare(tok, jsonChunk, "heightFalloff")) {
i = parse(tokens, i + 1, jsonChunk, &out->heightFalloff);
} else if (0 == compare(tok, jsonChunk, "color")) {
i = parse(tokens, i + 1, jsonChunk, &out->color);
} else if (0 == compare(tok, jsonChunk, "density")) {
i = parse(tokens, i + 1, jsonChunk, &out->density);
} else if (0 == compare(tok, jsonChunk, "inScatteringStart")) {
i = parse(tokens, i + 1, jsonChunk, &out->inScatteringStart);
} else if (0 == compare(tok, jsonChunk, "inScatteringSize")) {
i = parse(tokens, i + 1, jsonChunk, &out->inScatteringSize);
} else if (0 == compare(tok, jsonChunk, "fogColorFromIbl")) {
i = parse(tokens, i + 1, jsonChunk, &out->fogColorFromIbl);
} else if (0 == compare(tok, jsonChunk, "enabled")) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else {
slog.w << "Invalid fog options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid fog options value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, DepthOfFieldOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (0 == compare(tok, jsonChunk, "focusDistance")) {
i = parse(tokens, i + 1, jsonChunk, &out->focusDistance);
} else if (0 == compare(tok, jsonChunk, "cocScale")) {
i = parse(tokens, i + 1, jsonChunk, &out->cocScale);
} else if (0 == compare(tok, jsonChunk, "maxApertureDiameter")) {
i = parse(tokens, i + 1, jsonChunk, &out->maxApertureDiameter);
} else if (0 == compare(tok, jsonChunk, "enabled")) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else {
slog.w << "Invalid dof options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid dof options value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, VignetteOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (0 == compare(tok, jsonChunk, "midPoint")) {
i = parse(tokens, i + 1, jsonChunk, &out->midPoint);
} else if (0 == compare(tok, jsonChunk, "roundness")) {
i = parse(tokens, i + 1, jsonChunk, &out->roundness);
} else if (0 == compare(tok, jsonChunk, "feather")) {
i = parse(tokens, i + 1, jsonChunk, &out->feather);
} else if (0 == compare(tok, jsonChunk, "color")) {
i = parse(tokens, i + 1, jsonChunk, &out->color);
} else if (0 == compare(tok, jsonChunk, "enabled")) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else {
slog.w << "Invalid vignette options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid vignette options value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, RenderQuality* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (0 == compare(tok, jsonChunk, "hdrColorBuffer")) {
i = parse(tokens, i + 1, jsonChunk, &out->hdrColorBuffer);
} else {
slog.w << "Invalid render quality key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid render quality value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
DynamicLightingSettings* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (0 == compare(tok, jsonChunk, "zLightNear")) {
i = parse(tokens, i + 1, jsonChunk, &out->zLightNear);
} else if (0 == compare(tok, jsonChunk, "zLightFar")) {
i = parse(tokens, i + 1, jsonChunk, &out->zLightFar);
} else {
slog.w << "Invalid dynamic lighting key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid dynamic lighting value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewSettings* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "sampleCount") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->sampleCount);
} else if (compare(tok, jsonChunk, "antiAliasing") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->antiAliasing);
} else if (compare(tok, jsonChunk, "taa") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->taa);
} else if (compare(tok, jsonChunk, "colorGrading") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->colorGrading);
} else if (compare(tok, jsonChunk, "ssao") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->ssao);
} else if (compare(tok, jsonChunk, "bloom") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->bloom);
} else if (compare(tok, jsonChunk, "fog") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->fog);
} else if (compare(tok, jsonChunk, "dof") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->dof);
} else if (compare(tok, jsonChunk, "vignette") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->vignette);
} else if (compare(tok, jsonChunk, "dithering") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->dithering);
} else if (compare(tok, jsonChunk, "renderQuality") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->renderQuality);
} else if (compare(tok, jsonChunk, "dynamicLighting") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->dynamicLighting);
} else if (compare(tok, jsonChunk, "shadowType") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->shadowType);
} else if (compare(tok, jsonChunk, "postProcessingEnabled") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->postProcessingEnabled);
} else {
slog.w << "Invalid view setting key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid view setting value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, Settings* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "view") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->view);
} else {
slog.w << "Invalid group key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid group value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
bool readJson(const char* jsonChunk, size_t size, Settings* out) {
jsmn_parser parser = { 0, 0, 0 };
int tokenCount = jsmn_parse(&parser, jsonChunk, size, nullptr, 0);
if (tokenCount <= 0) {
return false;
}
jsmntok_t* tokens = (jsmntok_t*) malloc(sizeof(jsmntok_t) * tokenCount);
assert(tokens);
jsmn_init(&parser);
tokenCount = jsmn_parse(&parser, jsonChunk, size, tokens, tokenCount);
if (tokenCount <= 0) {
free(tokens);
return false;
}
int i = parse(tokens, 0, jsonChunk, out);
free(tokens);
return i >= 0;
}
static std::string writeJson(bool v) { return v ? "true" : "false"; }
static std::string writeJson(float v) { return std::to_string(v); }
static std::string writeJson(int v) { return std::to_string(v); }
static std::string writeJson(uint32_t v) { return std::to_string(v); }
static std::string writeJson(uint8_t v) { return std::to_string((int)v); }
static const char* writeJson(AntiAliasing in) {
switch (in) {
case AntiAliasing::NONE: return "NONE";
case AntiAliasing::FXAA: return "FXAA";
}
return "INVALID";
}
static const char* writeJson(Dithering in) {
switch (in) {
case Dithering::NONE: return "NONE";
case Dithering::TEMPORAL: return "TEMPORAL";
}
return "INVALID";
}
static const char* writeJson(ShadowType in) {
switch (in) {
case ShadowType::PCF: return "PCF";
case ShadowType::VSM: return "VSM";
}
return "INVALID";
}
static const char* writeJson(BlendMode in) {
switch (in) {
case BlendMode::ADD: return "ADD";
case BlendMode::INTERPOLATE: return "INTERPOLATE";
}
return "INVALID";
}
static const char* writeJson(VQL in) {
switch (in) {
case VQL::LOW: return "LOW";
case VQL::MEDIUM: return "MEDIUM";
case VQL::HIGH: return "HIGH";
case VQL::ULTRA: return "ULTRA";
}
return "INVALID";
}
static const char* writeJson(CGQL in) {
switch (in) {
case CGQL::LOW: return "LOW";
case CGQL::MEDIUM: return "MEDIUM";
case CGQL::HIGH: return "HIGH";
case CGQL::ULTRA: return "ULTRA";
}
return "INVALID";
}
static const char* writeJson(ToneMapping in) {
switch (in) {
case ToneMapping::LINEAR: return "LINEAR";
case ToneMapping::ACES_LEGACY: return "ACES_LEGACY";
case ToneMapping::ACES: return "ACES";
case ToneMapping::FILMIC: return "FILMIC";
case ToneMapping::UCHIMURA: return "UCHIMURA";
case ToneMapping::REINHARD: return "REINHARD";
case ToneMapping::DISPLAY_RANGE: return "DISPLAY_RANGE";
}
return "INVALID";
}
static std::string writeJson(const float* v, int count) {
std::ostringstream oss;
oss << "[";
for (int i = 0; i < count; i++) {
oss << v;
if (i < count - 1) {
oss << ", ";
}
}
oss << "]";
return oss.str();
}
static std::string writeJson(math::float3 v) {
return writeJson(&v.x, 3);
}
static std::string writeJson(math::float4 v) {
return writeJson(&v.x, 4);
}
std::string writeJson(const Settings& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"view\": "
<< writeJson(in.view)
<< "}";
return oss.str();
}
std::string writeJson(const TemporalAntiAliasingOptions& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"filterWidth\": " << writeJson(in.filterWidth) << ",\n"
<< "\"feedback\": " << writeJson(in.feedback) << ",\n"
<< "\"enabled\": " << writeJson(in.enabled) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const ColorGradingSettings& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"enabled\": " << writeJson(in.enabled) << ",\n"
<< "\"quality\": " << writeJson(in.quality) << ",\n"
<< "\"toneMapping\": " << writeJson(in.toneMapping) << ",\n"
<< "\"temperature\": " << writeJson(in.temperature) << ",\n"
<< "\"tint\": " << writeJson(in.tint) << ",\n"
<< "\"outRed\": " << writeJson(in.outRed) << ",\n"
<< "\"outGreen\": " << writeJson(in.outGreen) << ",\n"
<< "\"outBlue\": " << writeJson(in.outBlue) << ",\n"
<< "\"shadows\": " << writeJson(in.shadows) << ",\n"
<< "\"midtones\": " << writeJson(in.midtones) << ",\n"
<< "\"highlights\": " << writeJson(in.highlights) << ",\n"
<< "\"ranges\": " << writeJson(in.ranges) << ",\n"
<< "\"contrast\": " << writeJson(in.contrast) << ",\n"
<< "\"vibrance\": " << writeJson(in.vibrance) << ",\n"
<< "\"saturation\": " << writeJson(in.saturation) << ",\n"
<< "\"slope\": " << writeJson(in.slope) << ",\n"
<< "\"offset\": " << writeJson(in.offset) << ",\n"
<< "\"power\": " << writeJson(in.power) << ",\n"
<< "\"gamma\": " << writeJson(in.gamma) << ",\n"
<< "\"midPoint\": " << writeJson(in.midPoint) << ",\n"
<< "\"scale\": " << writeJson(in.scale) << ",\n"
<< "\"linkedCurves\": " << writeJson(in.linkedCurves) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const AmbientOcclusionOptions& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"radius\": " << writeJson(in.radius) << ",\n"
<< "\"power\": " << writeJson(in.power) << ",\n"
<< "\"bias\": " << writeJson(in.bias) << ",\n"
<< "\"resolution\": " << writeJson(in.resolution) << ",\n"
<< "\"intensity\": " << writeJson(in.intensity) << ",\n"
<< "\"quality\": " << writeJson(in.quality) << ",\n"
<< "\"upsampling\": " << writeJson(in.upsampling) << ",\n"
<< "\"enabled\": " << writeJson(in.enabled) << ",\n"
<< "\"minHorizonAngleRad\": " << writeJson(in.minHorizonAngleRad) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const BloomOptions& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"strength\": " << writeJson(in.strength) << ",\n"
<< "\"resolution\": " << writeJson(in.resolution) << ",\n"
<< "\"anamorphism\": " << writeJson(in.anamorphism) << ",\n"
<< "\"levels\": " << writeJson(in.levels) << ",\n"
<< "\"blendMode\": " << writeJson(in.blendMode) << ",\n"
<< "\"threshold\": " << writeJson(in.threshold) << ",\n"
<< "\"enabled\": " << writeJson(in.enabled) << ",\n"
<< "\"highlight\": " << writeJson(in.highlight) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const FogOptions& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"distance\": " << writeJson(in.distance) << ",\n"
<< "\"maximumOpacity\": " << writeJson(in.maximumOpacity) << ",\n"
<< "\"height\": " << writeJson(in.height) << ",\n"
<< "\"heightFalloff\": " << writeJson(in.heightFalloff) << ",\n"
<< "\"color\": " << writeJson(in.color) << ",\n"
<< "\"density\": " << writeJson(in.density) << ",\n"
<< "\"inScatteringStart\": " << writeJson(in.inScatteringStart) << ",\n"
<< "\"inScatteringSize\": " << writeJson(in.inScatteringSize) << ",\n"
<< "\"fogColorFromIbl\": " << writeJson(in.fogColorFromIbl) << ",\n"
<< "\"enabled\": " << writeJson(in.enabled) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const DepthOfFieldOptions& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"focusDistance\": " << writeJson(in.focusDistance) << ",\n"
<< "\"cocScale\": " << writeJson(in.cocScale) << ",\n"
<< "\"maxApertureDiameter\": " << writeJson(in.maxApertureDiameter) << ",\n"
<< "\"enabled\": " << writeJson(in.enabled) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const VignetteOptions& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"midPoint\": " << writeJson(in.midPoint) << ",\n"
<< "\"roundness\": " << writeJson(in.roundness) << ",\n"
<< "\"feather\": " << writeJson(in.feather) << ",\n"
<< "\"color\": " << writeJson(in.color) << ",\n"
<< "\"enabled\": " << writeJson(in.enabled) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const RenderQuality& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"hdrColorBuffer\": " << writeJson(in.hdrColorBuffer) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const DynamicLightingSettings& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"zLightNear\": " << writeJson(in.zLightNear) << ",\n"
<< "\"zLightFar\": " << writeJson(in.zLightFar) << "\n"
<< "}";
return oss.str();
}
std::string writeJson(const ViewSettings& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"sampleCount\": " << writeJson(in.sampleCount) << ",\n"
<< "\"antiAliasing\": " << writeJson(in.antiAliasing) << ",\n"
<< "\"taa\": " << writeJson(in.taa) << ",\n"
<< "\"colorGrading\": " << writeJson(in.colorGrading) << ",\n"
<< "\"ssao\": " << writeJson(in.ssao) << ",\n"
<< "\"bloom\": " << writeJson(in.bloom) << ",\n"
<< "\"fog\": " << writeJson(in.fog) << ",\n"
<< "\"dof\": " << writeJson(in.dof) << ",\n"
<< "\"vignette\": " << writeJson(in.vignette) << ",\n"
<< "\"dithering\": " << writeJson(in.dithering) << ",\n"
<< "\"renderQuality\": " << writeJson(in.renderQuality) << ",\n"
<< "\"dynamicLighting\": " << writeJson(in.dynamicLighting) << ",\n"
<< "\"shadowType\": " << writeJson(in.shadowType) << ",\n"
<< "\"postProcessingEnabled\": " << writeJson(in.postProcessingEnabled) << "\n"
<< "}";
return oss.str();
}
} // namespace viewer
} // namespace filament

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 The Android Open Source Project
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,209 +14,7 @@
* limitations under the License.
*/
#ifndef GLTFIO_SIMPLEVIEWER_H
#define GLTFIO_SIMPLEVIEWER_H
// NOTE: This is an optional header-only utility to avoid a hard dependency on imgui. To use
// SimpleViewer, please add:
//
// #define GLTFIO_SIMPLEVIEWER_IMPLEMENTATION
//
// to one of your CPP source files to create the implementation. See gltf_viewer.cpp for an example.
#include <filament/Box.h>
#include <filament/DebugRegistry.h>
#include <filament/Engine.h>
#include <filament/IndirectLight.h>
#include <filament/Scene.h>
#include <filament/View.h>
#include <gltfio/Animator.h>
#include <gltfio/FilamentAsset.h>
#include <utils/Entity.h>
#include <math/vec3.h>
namespace gltfio {
/**
* \class SimpleViewer SimpleViewer.h gltfio/SimpleViewer.h
* \brief Manages the state for a simple glTF viewer with imgui controls and a tree view.
*
* This is a utility that can be used across multiple platforms, including web.
*
* \note If you don't need ImGui controls, there is no need to use this class, just use AssetLoader
* instead.
*/
class SimpleViewer {
public:
static constexpr int DEFAULT_SIDEBAR_WIDTH = 350;
/**
* Constructs a SimpleViewer that has a fixed association with the given Filament objects.
*
* Upon construction, the simple viewer may create some additional Filament objects (such as
* light sources) that it owns.
*/
SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view,
int sidebarWidth = DEFAULT_SIDEBAR_WIDTH);
/**
* Destroys the SimpleViewer and any Filament entities that it owns.
*/
~SimpleViewer();
/**
* Adds the asset's ready-to-render entities into the scene and optionally transforms the root
* node to make it fit into a unit cube at the origin.
*
* The viewer does not claim ownership over the asset or its entities. Clients should use
* AssetLoader and ResourceLoader to load an asset before passing it in.
*
* @param asset The asset to view.
* @param scale Adds a transform to the root to fit the asset into a unit cube at the origin.
* @param instanceToAnimate Optional instance from which to get the animator.
*/
void populateScene(FilamentAsset* asset, bool scale,
FilamentInstance* instanceToAnimate = nullptr);
/**
* Removes the current asset from the viewer.
*
* This removes all the asset entities from the Scene, but does not destroy them.
*/
void removeAsset();
/**
* Sets or changes the current scene's IBL to allow the UI manipulate it.
*/
void setIndirectLight(filament::IndirectLight* ibl, filament::math::float3 const* sh3);
/**
* Applies the currently-selected glTF animation to the transformation hierarchy and updates
* the bone matrices on all renderables.
*/
void applyAnimation(double currentTime);
/**
* Constructs ImGui controls for the current frame and responds to everything that the user has
* changed since the previous frame.
*
* If desired this can be used in conjunction with the filagui library, which allows clients to
* render ImGui controls with Filament.
*/
void updateUserInterface();
/**
* Retrieves the current width of the ImGui "window" which we are using as a sidebar.
* Clients can monitor this value to adjust the size of the view.
*/
int getSidebarWidth() const { return mSidebarWidth; }
/**
* Allows clients to inject custom UI.
*/
void setUiCallback(std::function<void()> callback) { mCustomUI = callback; }
/**
* Draws the bounding box of each renderable.
* Defaults to false.
*/
void enableWireframe(bool b) { mEnableWireframe = b; }
/**
* Enables a built-in light source (useful for creating shadows).
* Defaults to true.
*/
void enableSunlight(bool b) { mEnableSunlight = b; }
/**
* Enables dithering on the view.
* Defaults to true.
*/
void enableDithering(bool b) { mEnableDithering = b; }
/**
* Enables FXAA antialiasing in the post-process pipeline.
* Defaults to true.
*/
void enableFxaa(bool b) { mEnableFxaa = b; }
/**
* Enables hardware-based MSAA antialiasing.
* Defaults to true.
*/
void enableMsaa(bool b) { mEnableMsaa = b; }
/**
* Enables screen-space ambient occlusion in the post-process pipeline.
* Defaults to true.
*/
void enableSSAO(bool b) { mSSAOOptions.enabled = b; }
/**
* Enables Bloom.
* Defaults to true.
*/
void enableBloom(bool bloom) {
mBloomOptions.enabled = bloom;
}
/**
* Adjusts the intensity of the IBL.
* See also filament::IndirectLight::setIntensity().
* Defaults to 30000.0.
*/
void setIBLIntensity(float brightness) { mIblIntensity = brightness; }
private:
void updateIndirectLight();
// Immutable properties set from the constructor.
filament::Engine* const mEngine;
filament::Scene* const mScene;
filament::View* const mView;
const utils::Entity mSunlight;
// Properties that can be changed from the application.
FilamentAsset* mAsset = nullptr;
Animator* mAnimator = nullptr;
filament::IndirectLight* mIndirectLight = nullptr;
std::function<void()> mCustomUI;
// Properties that can be changed from the UI.
int mCurrentAnimation = 1;
bool mResetAnimation = true;
float mIblIntensity = 30000.0f;
float mIblRotation = 0.0f;
float mSunlightIntensity = 100000.0f; // <-- This value is overridden when loading an IBL.
filament::math::float3 mSunlightColor = filament::Color::toLinear<filament::ACCURATE>({ 0.98, 0.92, 0.89});
filament::math::float3 mSunlightDirection = {0.6, -1.0, -0.8};
bool mEnableWireframe = false;
bool mEnableSunlight = true;
bool mEnableVsm = false;
bool mEnableShadows = true;
int mShadowCascades = 1;
bool mEnableContactShadows = false;
std::array<float, 3> mSplitPositions = {0.25f, 0.50f, 0.75f};
bool mEnableDithering = true;
bool mEnableFxaa = true;
bool mEnableMsaa = true;
filament::View::AmbientOcclusionOptions mSSAOOptions = { .enabled = true };
filament::View::BloomOptions mBloomOptions = { .enabled = true };
filament::View::FogOptions mFogOptions = {};
filament::View::TemporalAntiAliasingOptions mTAAOptions = {};
int mSidebarWidth;
uint32_t mFlags;
};
filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds);
} // namespace gltfio
#ifdef GLTFIO_SIMPLEVIEWER_IMPLEMENTATION
#include <viewer/SimpleViewer.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>
@@ -231,9 +29,11 @@ filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds);
#include <imgui.h>
#include <filagui/ImGuiExtensions.h>
#include <string>
#include <vector>
namespace gltfio {
namespace filament {
namespace viewer {
filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds) {
using namespace filament::math;
@@ -603,8 +403,5 @@ void SimpleViewer::updateUserInterface() {
updateIndirectLight();
}
} // namespace gltfio
#endif // GLTFIO_SIMPLEVIEWER_IMPLEMENTATION
#endif // GLTFIO_SIMPLEVIEWER_H
} // namespace viewer
} // namespace filament

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <viewer/Settings.h>
#include <gtest/gtest.h>
using namespace filament::viewer;
class ViewSettingsTest : public testing::Test {};
static const char* JSON_TEST_DEFAULTS = R"TXT(
{
"view": {
"sampleCount": 1,
"antiAliasing": "FXAA",
"taa": {
"enabled": false,
"filterWidth": 1.0,
"feedback": 0.04
},
"colorGrading": {
"enabled": true,
"quality": "MEDIUM",
"toneMapping": "ACES_LEGACY",
"temperature": 0,
"tint": 0,
"outRed": [1.0, 0.0, 0.0],
"outGreen": [0.0, 1.0, 0.0],
"outBlue": [0.0, 0.0, 1.0],
"shadows": [1.0, 1.0, 1.0, 0.0],
"midtones": [1.0, 1.0, 1.0, 0.0],
"highlights": [1.0, 1.0, 1.0, 0.0],
"ranges": [0.0, 0.333, 0.550, 1.0],
"contrast": 1.0,
"vibrance": 1.0,
"saturation": 1.0,
"slope": [1.0, 1.0, 1.0],
"offset": [0.0, 0.0, 0.0],
"power": [1.0, 1.0, 1.0],
"gamma": [1.0, 1.0, 1.0],
"midPoint": [1.0, 1.0, 1.0],
"scale": [1.0, 1.0, 1.0],
"linkedCurves": false
},
"ssao": {
"enabled": false,
"radius": 0.3,
"power": 1.0,
"bias": 0.0005,
"resolution": 0.5,
"intensity": 1.0,
"quality": "LOW",
"upsampling": "LOW",
"minHorizonAngleRad": 0.0
},
"bloom": {
"enabled": false,
"strength": 0.10,
"resolution": 360,
"anamorphism": 1.0,
"levels": 6,
"blendMode": "ADD",
"threshold": true,
"highlight": 1000.0
},
"fog": {
"enabled": false,
"distance": 0.0,
"maximumOpacity": 1.0,
"height": 0.0,
"heightFalloff": 1.0,
"color": [0.5, 0.5, 0.5],
"density": 0.1,
"inScatteringStart": 0.0,
"inScatteringSize": -1.0,
"fogColorFromIbl": false
},
"dof": {
"enabled": false,
"focusDistance": 10.0,
"cocScale": 1.0,
"maxApertureDiameter": 0.01
},
"vignette": {
"enabled": false,
"midPoint": 0.5,
"roundness": 0.5,
"feather": 0.5,
"color": [0, 0, 0, 1]
},
"dithering": "TEMPORAL",
"renderQuality": {
"hdrColorBuffer": "HIGH"
},
"dynamicLighting": {
"zLightNear": 5,
"zLightFar": 100,
},
"shadowType": "PCF",
"postProcessingEnabled": true
}
}
)TXT";
TEST_F(ViewSettingsTest, JsonTestDefaults) {
Settings settings1 = {0};
ASSERT_TRUE(readJson(JSON_TEST_DEFAULTS, strlen(JSON_TEST_DEFAULTS), &settings1));
ASSERT_TRUE(settings1.view.bloom.threshold);
Settings settings2;
ASSERT_TRUE(readJson("{}", strlen("{}"), &settings2));
ASSERT_EQ(writeJson(settings1), writeJson(settings2));
Settings settings3;
ASSERT_EQ(writeJson(settings1), writeJson(settings3));
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -240,8 +240,8 @@ if (NOT ANDROID)
# Sample app specific
target_link_libraries(frame_generator PRIVATE imageio)
target_link_libraries(gltf_viewer PRIVATE gltf-resources gltfio)
target_link_libraries(gltf_instances PRIVATE gltf-resources gltfio)
target_link_libraries(gltf_viewer PRIVATE gltf-resources gltfio viewer)
target_link_libraries(gltf_instances PRIVATE gltf-resources gltfio viewer)
target_link_libraries(hellopbr PRIVATE filameshio suzanne-resources)
target_link_libraries(sample_cloth PRIVATE filameshio)
target_link_libraries(sample_normal_map PRIVATE filameshio)

View File

@@ -14,8 +14,6 @@
* limitations under the License.
*/
#define GLTFIO_SIMPLEVIEWER_IMPLEMENTATION
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
#include <filamentapp/IBL.h>
@@ -23,12 +21,14 @@
#include <filament/Engine.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
#include <filament/View.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <gltfio/SimpleViewer.h>
#include <viewer/SimpleViewer.h>
#include <camutils/Manipulator.h>
@@ -46,6 +46,8 @@
using namespace filament;
using namespace filament::math;
using namespace filament::viewer;
using namespace gltfio;
using namespace utils;

View File

@@ -14,8 +14,6 @@
* limitations under the License.
*/
#define GLTFIO_SIMPLEVIEWER_IMPLEMENTATION
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
#include <filamentapp/IBL.h>
@@ -24,8 +22,10 @@
#include <filament/ColorGrading.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <filament/Renderer.h>
@@ -33,7 +33,8 @@
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <gltfio/SimpleViewer.h>
#include <viewer/SimpleViewer.h>
#include <camutils/Manipulator.h>
@@ -46,6 +47,9 @@
#include <math/mat3.h>
#include <math/norm.h>
#include <imgui.h>
#include <filagui/ImGuiExtensions.h>
#include <fstream>
#include <iostream>
#include <string>
@@ -54,6 +58,8 @@
using namespace filament;
using namespace filament::math;
using namespace filament::viewer;
using namespace gltfio;
using namespace utils;