* prevent public classes from being created on the stack
- we used to to this by deleting operator delete, but this prevented
the internal "F" classes from being virtual; which can be useful
when using EntityManger::Listener.
now we just make the destructor protected in each class.
- EntityManger::Listener now has a virtual destructor so that
objects could be correctly destroyed from Listener*
* improve EntityManger and Component managers
- all component managers now have the same "base" API
- getComponentCount()
- empty()
- getEntity()
- getEntities()
- Scene now has getEntityCount()
- EntityManager now has getEntityCount()
- all component manager implement gc() the same way, by calling destroy()
- SingleInstanceComponentManager::gc() that calls removeComponent() has
been removed because it's dangerous. removeComponent() is often
not enough, some additional cleanup might be needed.
1009 lines
43 KiB
C++
1009 lines
43 KiB
C++
/*
|
|
* 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 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 "jsonParseUtils.h"
|
|
#include "Settings_generated.h"
|
|
|
|
#include <filament/Engine.h>
|
|
#include <filament/Camera.h>
|
|
#include <filament/Renderer.h>
|
|
#include <filament/Skybox.h>
|
|
|
|
#include <utils/Log.h>
|
|
|
|
#include <math/mat3.h>
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
using namespace utils;
|
|
|
|
namespace filament::viewer {
|
|
|
|
std::string_view to_string(color::ColorSpace const& colorspace) noexcept {
|
|
using namespace color;
|
|
if (colorspace == Rec709-Linear-D65) {
|
|
return "Rec709-Linear-D65";
|
|
}
|
|
if (colorspace == Rec709-sRGB-D65) {
|
|
return "Rec709-sRGB-D65";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
// Skips over an unused token.
|
|
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;
|
|
}
|
|
|
|
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, "AGX")) { *out = ToneMapping::AGX; }
|
|
else if (0 == compare(tokens[i], jsonChunk, "GENERIC")) { *out = ToneMapping::GENERIC; }
|
|
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, color::ColorSpace* out) {
|
|
using namespace filament::color;
|
|
if (0 == compare(tokens[i], jsonChunk, "Rec709-Linear-D65")) { *out = Rec709-Linear-D65; }
|
|
else if (0 == compare(tokens[i], jsonChunk, "Rec709-sRGB-D65")) { *out = Rec709-sRGB-D65; }
|
|
else {
|
|
slog.w << "Invalid ColorSpace: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
|
|
}
|
|
return i + 1;
|
|
}
|
|
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, GenericToneMapperSettings* 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, "contrast") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->contrast);
|
|
} else if (compare(tok, jsonChunk, "midGrayIn") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->midGrayIn);
|
|
} else if (compare(tok, jsonChunk, "midGrayOut") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->midGrayOut);
|
|
} else if (compare(tok, jsonChunk, "hdrMax") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->hdrMax);
|
|
} else {
|
|
slog.w << "Invalid generic tone mapper key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
i = parse(tokens, i + 1);
|
|
}
|
|
if (i < 0) {
|
|
slog.e << "Invalid generic tone mapper value: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
return i;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AgxToneMapper::AgxLook* out) {
|
|
if (0 == compare(tokens[i], jsonChunk, "NONE")) { *out = AgxToneMapper::AgxLook::NONE; }
|
|
else if (0 == compare(tokens[i], jsonChunk, "PUNCHY")) { *out = AgxToneMapper::AgxLook::PUNCHY; }
|
|
else if (0 == compare(tokens[i], jsonChunk, "GOLDEN")) { *out = AgxToneMapper::AgxLook::GOLDEN; }
|
|
else {
|
|
slog.w << "Invalid AgxLook: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
|
|
}
|
|
return i + 1;
|
|
}
|
|
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AgxToneMapperSettings* 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, "look") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->look);
|
|
} else {
|
|
slog.w << "Invalid AgX tone mapper key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
i = parse(tokens, i + 1);
|
|
}
|
|
if (i < 0) {
|
|
slog.e << "Invalid AgX tone mapper 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, "colorspace") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->colorspace);
|
|
} 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, "genericToneMapper") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->genericToneMapper);
|
|
} else if (compare(tok, jsonChunk, "agxToneMapper") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->agxToneMapper);
|
|
} else if (compare(tok, jsonChunk, "luminanceScaling") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->luminanceScaling);
|
|
} else if (compare(tok, jsonChunk, "gamutMapping") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->gamutMapping);
|
|
} else if (compare(tok, jsonChunk, "exposure") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->exposure);
|
|
} else if (compare(tok, jsonChunk, "nightAdaptation") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->nightAdaptation);
|
|
} 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,
|
|
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, "antiAliasing") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->antiAliasing);
|
|
} else if (compare(tok, jsonChunk, "msaa") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->msaa);
|
|
} else if (compare(tok, jsonChunk, "taa") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->taa);
|
|
} else if (compare(tok, jsonChunk, "dsr") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->dsr);
|
|
} 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, "screenSpaceReflections") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->screenSpaceReflections);
|
|
} 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, "guardBand") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->guardBand);
|
|
} else if (compare(tok, jsonChunk, "vsmShadowOptions") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->vsmShadowOptions);
|
|
} 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;
|
|
}
|
|
|
|
template <typename T>
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, MaterialProperty<T>* 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);
|
|
const std::string name = STR(tok, jsonChunk);
|
|
|
|
// Find the first unused slot, or the first slot that matches the given name.
|
|
size_t k = 0;
|
|
for (; k < MaterialSettings::MAX_COUNT; ++k) {
|
|
if (out[k].name == name || out[k].name.empty()) {
|
|
break;
|
|
}
|
|
}
|
|
if (k == MaterialSettings::MAX_COUNT) {
|
|
slog.e << "Too many material settings." << io::endl;
|
|
return i;
|
|
}
|
|
|
|
out[k].name = name;
|
|
i = parse(tokens, i + 1, jsonChunk, &out[k].value);
|
|
if (i < 0) {
|
|
slog.e << "Invalid materials value: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
return i;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, MaterialSettings* 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, "scalar") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, out->scalar);
|
|
} else if (compare(tok, jsonChunk, "float3") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, out->float3);
|
|
} else if (compare(tok, jsonChunk, "float4") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, out->float4);
|
|
} else {
|
|
slog.w << "Invalid materials key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
i = parse(tokens, i + 1);
|
|
}
|
|
if (i < 0) {
|
|
slog.e << "Invalid materials value: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
return i;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
|
|
LightManager::ShadowOptions::Vsm* 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, "elvsm") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->elvsm);
|
|
} else if (compare(tok, jsonChunk, "blurWidth") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->blurWidth);
|
|
} else {
|
|
slog.w << "Invalid shadow options VSM key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
i = parse(tokens, i + 1);
|
|
}
|
|
if (i < 0) {
|
|
slog.e << "Invalid shadow options VSM value: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
return i;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
|
|
LightManager::ShadowOptions* out) {
|
|
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
|
|
int const size = tokens[i++].size;
|
|
math::float3 splitsVector;
|
|
for (int j = 0; j < size; ++j) {
|
|
const jsmntok_t tok = tokens[i];
|
|
CHECK_KEY(tok);
|
|
if (compare(tok, jsonChunk, "mapSize") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->mapSize);
|
|
} else if (compare(tok, jsonChunk, "shadowCascades") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->shadowCascades);
|
|
} else if (compare(tok, jsonChunk, "cascadeSplitPositions") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &splitsVector);
|
|
out->cascadeSplitPositions[0] = splitsVector[0];
|
|
out->cascadeSplitPositions[1] = splitsVector[1];
|
|
out->cascadeSplitPositions[2] = splitsVector[2];
|
|
// TODO: constantBias
|
|
// TODO: normalBias
|
|
// TODO: shadowFar
|
|
// TODO: shadowNearHint
|
|
// TODO: shadowFarHint
|
|
} else if (compare(tok, jsonChunk, "stable") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->stable);
|
|
} else if (compare(tok, jsonChunk, "lispsm") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->lispsm);
|
|
// TODO: polygonOffsetConstant
|
|
// TODO: polygonOffsetSlope
|
|
} else if (compare(tok, jsonChunk, "screenSpaceContactShadows") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->screenSpaceContactShadows);
|
|
// TODO: stepCount
|
|
// TODO: maxShadowDistance
|
|
} else if (compare(tok, jsonChunk, "vsm") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->vsm);
|
|
} else if (compare(tok, jsonChunk, "shadowBulbRadius") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->shadowBulbRadius);
|
|
} else if (compare(tok, jsonChunk, "transform") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->transform.xyzw);
|
|
} else {
|
|
slog.w << "Invalid shadow options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
i = parse(tokens, i + 1);
|
|
}
|
|
if (i < 0) {
|
|
slog.e << "Invalid shadow options value: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
return i;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, LightSettings* 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, "enableShadows") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->enableShadows);
|
|
} else if (compare(tok, jsonChunk, "enableSunlight") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->enableSunlight);
|
|
} else if (compare(tok, jsonChunk, "shadowOptions") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->shadowOptions);
|
|
} else if (compare(tok, jsonChunk, "softShadowOptions") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->softShadowOptions);
|
|
} else if (compare(tok, jsonChunk, "sunlightIntensity") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->sunlightIntensity);
|
|
} else if (compare(tok, jsonChunk, "sunlightHaloSize") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->sunlightHaloSize);
|
|
} else if (compare(tok, jsonChunk, "sunlightHaloFalloff") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->sunlightHaloFalloff);
|
|
} else if (compare(tok, jsonChunk, "sunlightAngularRadius") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->sunlightAngularRadius);
|
|
} else if (compare(tok, jsonChunk, "sunlightDirection") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->sunlightDirection);
|
|
} else if (compare(tok, jsonChunk, "sunlightColor") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->sunlightColor);
|
|
} else if (compare(tok, jsonChunk, "iblIntensity") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->iblIntensity);
|
|
} else if (compare(tok, jsonChunk, "iblRotation") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->iblRotation);
|
|
} else {
|
|
slog.w << "Invalid light setting key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
i = parse(tokens, i + 1);
|
|
}
|
|
if (i < 0) {
|
|
slog.e << "Invalid light setting value: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
return i;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewerOptions* 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, "cameraAperture") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->cameraAperture);
|
|
} else if (compare(tok, jsonChunk, "cameraSpeed") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->cameraSpeed);
|
|
} else if (compare(tok, jsonChunk, "cameraISO") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->cameraISO);
|
|
} else if (compare(tok, jsonChunk, "cameraNear") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->cameraNear);
|
|
} else if (compare(tok, jsonChunk, "cameraFar") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->cameraFar);
|
|
} else if (compare(tok, jsonChunk, "groundShadowStrength") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->groundShadowStrength);
|
|
} else if (compare(tok, jsonChunk, "groundPlaneEnabled") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->groundPlaneEnabled);
|
|
} else if (compare(tok, jsonChunk, "skyboxEnabled") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->skyboxEnabled);
|
|
} else if (compare(tok, jsonChunk, "backgroundColor") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->backgroundColor);
|
|
} else if (compare(tok, jsonChunk, "cameraFocalLength") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->cameraFocalLength);
|
|
} else if (compare(tok, jsonChunk, "cameraFocusDistance") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->cameraFocusDistance);
|
|
} else if (compare(tok, jsonChunk, "autoInstancingEnabled") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->autoInstancingEnabled);
|
|
} else if (compare(tok, jsonChunk, "autoScaleEnabled") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->autoScaleEnabled);
|
|
} else {
|
|
slog.w << "Invalid viewer options key: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
i = parse(tokens, i + 1);
|
|
}
|
|
if (i < 0) {
|
|
slog.e << "Invalid viewer options value: '" << STR(tok, jsonChunk) << "'" << io::endl;
|
|
return i;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
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 if (compare(tok, jsonChunk, "material") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->material);
|
|
} else if (compare(tok, jsonChunk, "lighting") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->lighting);
|
|
} else if (compare(tok, jsonChunk, "viewer") == 0) {
|
|
i = parse(tokens, i + 1, jsonChunk, &out->viewer);
|
|
} 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;
|
|
}
|
|
|
|
void applySettings(Engine* engine, const ViewSettings& settings, View* dest) {
|
|
dest->setAntiAliasing(settings.antiAliasing);
|
|
dest->setTemporalAntiAliasingOptions(settings.taa);
|
|
dest->setMultiSampleAntiAliasingOptions(settings.msaa);
|
|
dest->setDynamicResolutionOptions(settings.dsr);
|
|
dest->setAmbientOcclusionOptions(settings.ssao);
|
|
dest->setScreenSpaceReflectionsOptions(settings.screenSpaceReflections);
|
|
dest->setBloomOptions(settings.bloom);
|
|
dest->setFogOptions(settings.fog);
|
|
dest->setDepthOfFieldOptions(settings.dof);
|
|
dest->setVignetteOptions(settings.vignette);
|
|
dest->setDithering(settings.dithering);
|
|
dest->setRenderQuality(settings.renderQuality);
|
|
dest->setDynamicLightingOptions(settings.dynamicLighting.zLightNear,
|
|
settings.dynamicLighting.zLightFar);
|
|
dest->setShadowType(settings.shadowType);
|
|
dest->setVsmShadowOptions(settings.vsmShadowOptions);
|
|
dest->setGuardBandOptions(settings.guardBand);
|
|
dest->setPostProcessingEnabled(settings.postProcessingEnabled);
|
|
}
|
|
|
|
template <typename T>
|
|
static void apply(MaterialProperty<T> prop, MaterialInstance* dest) {
|
|
if (!prop.name.empty()) {
|
|
dest->setParameter(prop.name.c_str(), prop.value);
|
|
}
|
|
}
|
|
|
|
void applySettings(Engine* engine, const MaterialSettings& settings, MaterialInstance* dest) {
|
|
for (const auto& prop : settings.scalar) { apply(prop, dest); }
|
|
for (const auto& prop : settings.float3) { apply(prop, dest); }
|
|
for (const auto& prop : settings.float4) { apply(prop, dest); }
|
|
}
|
|
|
|
void applySettings(Engine* engine, const LightSettings& settings, IndirectLight* ibl, utils::Entity sunlight,
|
|
const utils::Entity* sceneLights, size_t sceneLightCount, LightManager* lm, Scene* scene, View* view) {
|
|
auto light = lm->getInstance(sunlight);
|
|
if (light) {
|
|
if (settings.enableSunlight) {
|
|
scene->addEntity(sunlight);
|
|
} else {
|
|
scene->remove(sunlight);
|
|
}
|
|
lm->setIntensity(light, settings.sunlightIntensity);
|
|
lm->setSunHaloSize(light, settings.sunlightHaloSize);
|
|
lm->setSunHaloFalloff(light, settings.sunlightHaloFalloff);
|
|
lm->setSunAngularRadius(light, settings.sunlightAngularRadius);
|
|
lm->setDirection(light, normalize(settings.sunlightDirection));
|
|
lm->setColor(light, settings.sunlightColor);
|
|
lm->setShadowCaster(light, settings.enableShadows);
|
|
lm->setShadowOptions(light, settings.shadowOptions);
|
|
}
|
|
if (ibl) {
|
|
ibl->setIntensity(settings.iblIntensity);
|
|
ibl->setRotation(math::mat3f::rotation(settings.iblRotation, math::float3 { 0, 1, 0 }));
|
|
}
|
|
for (size_t i = 0; i < sceneLightCount; i++) {
|
|
auto const li = lm->getInstance(sceneLights[i]);
|
|
if (li) {
|
|
lm->setShadowCaster(li, settings.enableShadows);
|
|
lm->setShadowOptions(li, settings.shadowOptions);
|
|
}
|
|
}
|
|
view->setSoftShadowOptions(settings.softShadowOptions);
|
|
}
|
|
|
|
static LinearColor inverseTonemapSRGB(sRGBColor x) {
|
|
return (x * -0.155f) / (x - 1.019f);
|
|
}
|
|
|
|
void applySettings(Engine* engine, const ViewerOptions& settings, Camera* camera, Skybox* skybox,
|
|
Renderer* renderer) {
|
|
if (renderer) {
|
|
// we have to clear because the side-bar doesn't have a background, we cannot use
|
|
// a skybox on the ui scene, because the ui view is always full screen.
|
|
renderer->setClearOptions({
|
|
.clearColor = { inverseTonemapSRGB(settings.backgroundColor), 1.0f },
|
|
.clear = true
|
|
});
|
|
}
|
|
if (skybox) {
|
|
skybox->setLayerMask(0xff, settings.skyboxEnabled ? 0xff : 0x00);
|
|
}
|
|
if (camera) {
|
|
camera->setExposure(
|
|
settings.cameraAperture,
|
|
1.0f / settings.cameraSpeed,
|
|
settings.cameraISO);
|
|
|
|
camera->setFocusDistance(settings.cameraFocusDistance);
|
|
}
|
|
engine->setAutomaticInstancingEnabled(settings.autoInstancingEnabled);
|
|
}
|
|
|
|
constexpr ToneMapper* createToneMapper(const ColorGradingSettings& settings) noexcept {
|
|
switch (settings.toneMapping) {
|
|
case ToneMapping::LINEAR: return new LinearToneMapper;
|
|
case ToneMapping::ACES_LEGACY: return new ACESLegacyToneMapper;
|
|
case ToneMapping::ACES: return new ACESToneMapper;
|
|
case ToneMapping::FILMIC: return new FilmicToneMapper;
|
|
case ToneMapping::AGX: return new AgxToneMapper(settings.agxToneMapper.look);
|
|
case ToneMapping::GENERIC: return new GenericToneMapper(
|
|
settings.genericToneMapper.contrast,
|
|
settings.genericToneMapper.midGrayIn,
|
|
settings.genericToneMapper.midGrayOut,
|
|
settings.genericToneMapper.hdrMax
|
|
);
|
|
case ToneMapping::DISPLAY_RANGE: return new DisplayRangeToneMapper;
|
|
}
|
|
}
|
|
|
|
ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* engine) {
|
|
ToneMapper* toneMapper = createToneMapper(settings);
|
|
ColorGrading *colorGrading = ColorGrading::Builder()
|
|
.quality(settings.quality)
|
|
.exposure(settings.exposure)
|
|
.nightAdaptation(settings.nightAdaptation)
|
|
.whiteBalance(settings.temperature, settings.tint)
|
|
.channelMixer(settings.outRed, settings.outGreen, settings.outBlue)
|
|
.shadowsMidtonesHighlights(
|
|
Color::toLinear(settings.shadows),
|
|
Color::toLinear(settings.midtones),
|
|
Color::toLinear(settings.highlights),
|
|
settings.ranges
|
|
)
|
|
.slopeOffsetPower(settings.slope, settings.offset, settings.power)
|
|
.contrast(settings.contrast)
|
|
.vibrance(settings.vibrance)
|
|
.saturation(settings.saturation)
|
|
.curves(settings.gamma, settings.midPoint, settings.scale)
|
|
.toneMapper(toneMapper)
|
|
.luminanceScaling(settings.luminanceScaling)
|
|
.gamutMapping(settings.gamutMapping)
|
|
.outputColorSpace(settings.colorspace)
|
|
.build(*engine);
|
|
delete toneMapper;
|
|
return colorGrading;
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, CGQL in) {
|
|
switch (in) {
|
|
case CGQL::LOW: return out << "\"LOW\"";
|
|
case CGQL::MEDIUM: return out << "\"MEDIUM\"";
|
|
case CGQL::HIGH: return out << "\"HIGH\"";
|
|
case CGQL::ULTRA: return out << "\"ULTRA\"";
|
|
}
|
|
return out << "\"INVALID\"";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, ToneMapping in) {
|
|
switch (in) {
|
|
case ToneMapping::LINEAR: return out << "\"LINEAR\"";
|
|
case ToneMapping::ACES_LEGACY: return out << "\"ACES_LEGACY\"";
|
|
case ToneMapping::ACES: return out << "\"ACES\"";
|
|
case ToneMapping::FILMIC: return out << "\"FILMIC\"";
|
|
case ToneMapping::AGX: return out << "\"AGX\"";
|
|
case ToneMapping::GENERIC: return out << "\"GENERIC\"";
|
|
case ToneMapping::DISPLAY_RANGE: return out << "\"DISPLAY_RANGE\"";
|
|
}
|
|
return out << "\"INVALID\"";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const GenericToneMapperSettings& in) {
|
|
return out << "{\n"
|
|
<< "\"contrast\": " << (in.contrast) << ",\n"
|
|
<< "\"midGrayIn\": " << (in.midGrayIn) << ",\n"
|
|
<< "\"midGrayOut\": " << (in.midGrayOut) << ",\n"
|
|
<< "\"hdrMax\": " << (in.hdrMax) << "\n"
|
|
<< "}";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, AgxToneMapper::AgxLook in) {
|
|
switch (in) {
|
|
case AgxToneMapper::AgxLook::NONE: return out << "\"NONE\"";
|
|
case AgxToneMapper::AgxLook::PUNCHY: return out << "\"PUNCHY\"";
|
|
case AgxToneMapper::AgxLook::GOLDEN: return out << "\"GOLDEN\"";
|
|
}
|
|
return out << "\"INVALID\"";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const AgxToneMapperSettings& in) {
|
|
return out << "{\n"
|
|
<< "\"look\": " << (in.look) << ",\n"
|
|
<< "}";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const ColorGradingSettings& in) {
|
|
return out << "{\n"
|
|
<< "\"enabled\": " << to_string(in.enabled) << ",\n"
|
|
<< "\"colorspace\": " << to_string(in.colorspace) << ",\n"
|
|
<< "\"quality\": " << (in.quality) << ",\n"
|
|
<< "\"toneMapping\": " << (in.toneMapping) << ",\n"
|
|
<< "\"genericToneMapper\": " << (in.genericToneMapper) << ",\n"
|
|
<< "\"agxToneMapper\": " << (in.agxToneMapper) << ",\n"
|
|
<< "\"luminanceScaling\": " << to_string(in.luminanceScaling) << ",\n"
|
|
<< "\"gamutMapping\": " << to_string(in.gamutMapping) << ",\n"
|
|
<< "\"exposure\": " << (in.exposure) << ",\n"
|
|
<< "\"nightAdaptation\": " << (in.nightAdaptation) << ",\n"
|
|
<< "\"temperature\": " << (in.temperature) << ",\n"
|
|
<< "\"tint\": " << (in.tint) << ",\n"
|
|
<< "\"outRed\": " << (in.outRed) << ",\n"
|
|
<< "\"outGreen\": " << (in.outGreen) << ",\n"
|
|
<< "\"outBlue\": " << (in.outBlue) << ",\n"
|
|
<< "\"shadows\": " << (in.shadows) << ",\n"
|
|
<< "\"midtones\": " << (in.midtones) << ",\n"
|
|
<< "\"highlights\": " << (in.highlights) << ",\n"
|
|
<< "\"ranges\": " << (in.ranges) << ",\n"
|
|
<< "\"contrast\": " << (in.contrast) << ",\n"
|
|
<< "\"vibrance\": " << (in.vibrance) << ",\n"
|
|
<< "\"saturation\": " << (in.saturation) << ",\n"
|
|
<< "\"slope\": " << (in.slope) << ",\n"
|
|
<< "\"offset\": " << (in.offset) << ",\n"
|
|
<< "\"power\": " << (in.power) << ",\n"
|
|
<< "\"gamma\": " << (in.gamma) << ",\n"
|
|
<< "\"midPoint\": " << (in.midPoint) << ",\n"
|
|
<< "\"scale\": " << (in.scale) << ",\n"
|
|
<< "\"linkedCurves\": " << to_string(in.linkedCurves) << "\n"
|
|
<< "}";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const LightManager::ShadowOptions& in) {
|
|
const float* splits = in.cascadeSplitPositions;
|
|
math::float3 const splitsVector = { splits[0], splits[1], splits[2] };
|
|
return out << "{\n"
|
|
<< "\"vsm\": {\n"
|
|
<< "\"elvsm\": " << to_string(in.vsm.elvsm) << ",\n"
|
|
<< "\"blurWidth\": " << in.vsm.blurWidth << "\n"
|
|
<< "},\n"
|
|
<< "\"mapSize\": " << in.mapSize << ",\n"
|
|
<< "\"shadowCascades\": " << int(in.shadowCascades) << ",\n"
|
|
<< "\"cascadeSplitPositions\": " << (splitsVector) << "\n"
|
|
<< "\"stable\": " << to_string(in.stable) << ",\n"
|
|
<< "\"lispsm\": " << to_string(in.lispsm) << ",\n"
|
|
<< "\"screenSpaceContactShadows\": " << to_string(in.screenSpaceContactShadows) << ",\n"
|
|
<< "\"shadowBulbRadius\": " << in.shadowBulbRadius << ",\n"
|
|
<< "\"transform\": " << in.transform.xyzw << ",\n"
|
|
<< "}";
|
|
}
|
|
|
|
template <typename T>
|
|
void writeJson(MaterialProperty<T> prop, std::ostream& oss) {
|
|
if (!prop.name.empty()) {
|
|
oss << "\"" << prop.name << "\": " << prop.value << ",\n";
|
|
}
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const MaterialSettings& in) {
|
|
std::ostringstream oss;
|
|
oss << "{\n";
|
|
oss << "\"scalar\": {\n";
|
|
for (const auto& prop : in.scalar) { writeJson(prop, oss); }
|
|
oss << "},\n";
|
|
oss << "\"float3\": {\n";
|
|
for (const auto& prop : in.float3) { writeJson(prop, oss); }
|
|
oss << "},\n";
|
|
oss << "\"float4\": {\n";
|
|
for (const auto& prop : in.float4) { writeJson(prop, oss); }
|
|
oss << "},\n";
|
|
oss << "}";
|
|
std::string result = oss.str();
|
|
|
|
const auto replace = [&result](const std::string& s, const std::string& t) {
|
|
std::string::size_type n = 0;
|
|
while ((n = result.find(s, n )) != std::string::npos) {
|
|
result.replace(n, s.size(), t);
|
|
n += t.size();
|
|
}
|
|
};
|
|
|
|
// Remove empty objects and trailing commas.
|
|
replace("\"scalar\": {\n},\n", "");
|
|
replace("\"float3\": {\n},\n", "");
|
|
replace("\"float4\": {\n},\n", "");
|
|
replace(",\n}", "\n}");
|
|
replace("{\n}", "{}");
|
|
|
|
return out << result;
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const LightSettings& in) {
|
|
return out << "{\n"
|
|
<< "\"enableShadows\": " << to_string(in.enableShadows) << ",\n"
|
|
<< "\"enableSunlight\": " << to_string(in.enableSunlight) << ",\n"
|
|
<< "\"shadowOptions\": " << (in.shadowOptions) << ",\n"
|
|
<< "\"softShadowOptions\": " << (in.softShadowOptions) << ",\n"
|
|
<< "\"sunlightIntensity\": " << (in.sunlightIntensity) << ",\n"
|
|
<< "\"sunlightHaloSize\": " << (in.sunlightHaloSize) << ",\n"
|
|
<< "\"sunlightHaloFalloff\": " << (in.sunlightHaloFalloff) << ",\n"
|
|
<< "\"sunlightAngularRadius\": " << (in.sunlightAngularRadius) << ",\n"
|
|
<< "\"sunlightDirection\": " << (in.sunlightDirection) << ",\n"
|
|
<< "\"sunlightColor\": " << (in.sunlightColor) << ",\n"
|
|
<< "\"iblIntensity\": " << (in.iblIntensity) << ",\n"
|
|
<< "\"iblRotation\": " << (in.iblRotation) << "\n"
|
|
<< "}";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const ViewerOptions& in) {
|
|
return out << "{\n"
|
|
<< "\"cameraAperture\": " << (in.cameraAperture) << ",\n"
|
|
<< "\"cameraSpeed\": " << (in.cameraSpeed) << ",\n"
|
|
<< "\"cameraISO\": " << (in.cameraISO) << ",\n"
|
|
<< "\"cameraNear\": " << (in.cameraNear) << ",\n"
|
|
<< "\"cameraFar\": " << (in.cameraFar) << ",\n"
|
|
<< "\"groundShadowStrength\": " << (in.groundShadowStrength) << ",\n"
|
|
<< "\"groundPlaneEnabled\": " << to_string(in.groundPlaneEnabled) << ",\n"
|
|
<< "\"skyboxEnabled\": " << to_string(in.skyboxEnabled) << ",\n"
|
|
<< "\"backgroundColor\": " << (in.backgroundColor) << ",\n"
|
|
<< "\"cameraFocalLength\": " << (in.cameraFocalLength) << ",\n"
|
|
<< "\"cameraFocusDistance\": " << (in.cameraFocusDistance) << ",\n"
|
|
<< "\"autoInstancingEnabled\": " << to_string(in.autoInstancingEnabled) << ",\n"
|
|
<< "\"autoScaleEnabled\": " << to_string(in.autoScaleEnabled) << "\n"
|
|
<< "}";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const DynamicLightingSettings& in) {
|
|
return out << "{\n"
|
|
<< "\"zLightNear\": " << (in.zLightNear) << ",\n"
|
|
<< "\"zLightFar\": " << (in.zLightFar) << "\n"
|
|
<< "}";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const ViewSettings& in) {
|
|
return out << "{\n"
|
|
<< "\"antiAliasing\": " << in.antiAliasing << ",\n"
|
|
<< "\"msaa\": " << in.msaa << ",\n"
|
|
<< "\"taa\": " << in.taa << ",\n"
|
|
<< "\"dsr\": " << in.dsr << ",\n"
|
|
<< "\"colorGrading\": " << (in.colorGrading) << ",\n"
|
|
<< "\"ssao\": " << (in.ssao) << ",\n"
|
|
<< "\"screenSpaceReflections\": " << (in.screenSpaceReflections) << ",\n"
|
|
<< "\"bloom\": " << (in.bloom) << ",\n"
|
|
<< "\"fog\": " << (in.fog) << ",\n"
|
|
<< "\"dof\": " << (in.dof) << ",\n"
|
|
<< "\"vignette\": " << (in.vignette) << ",\n"
|
|
<< "\"dithering\": " << (in.dithering) << ",\n"
|
|
<< "\"renderQuality\": " << (in.renderQuality) << ",\n"
|
|
<< "\"dynamicLighting\": " << (in.dynamicLighting) << ",\n"
|
|
<< "\"shadowType\": " << (in.shadowType) << ",\n"
|
|
<< "\"vsmShadowOptions\": " << (in.vsmShadowOptions) << ",\n"
|
|
<< "\"guardBand\": " << (in.guardBand) << ",\n"
|
|
<< "\"postProcessingEnabled\": " << to_string(in.postProcessingEnabled) << "\n"
|
|
<< "}";
|
|
}
|
|
|
|
static std::ostream& operator<<(std::ostream& out, const Settings& in) {
|
|
return out << "{\n"
|
|
<< "\"view\": " << (in.view) << ",\n"
|
|
<< "\"material\": " << (in.material) << ",\n"
|
|
<< "\"lighting\": " << (in.lighting) << ",\n"
|
|
<< "\"viewer\": " << (in.viewer)
|
|
<< "}";
|
|
}
|
|
|
|
bool GenericToneMapperSettings::operator==(const GenericToneMapperSettings& rhs) const {
|
|
static_assert(sizeof(GenericToneMapperSettings) == 16, "Please update Settings.cpp");
|
|
return contrast == rhs.contrast &&
|
|
midGrayIn == rhs.midGrayIn &&
|
|
midGrayOut == rhs.midGrayOut &&
|
|
hdrMax == rhs.hdrMax;
|
|
}
|
|
|
|
bool AgxToneMapperSettings::operator==(const AgxToneMapperSettings& rhs) const {
|
|
static_assert(sizeof(AgxToneMapperSettings) == 1, "Please update Settings.cpp");
|
|
return look == rhs.look;
|
|
}
|
|
|
|
bool ColorGradingSettings::operator==(const ColorGradingSettings& rhs) const {
|
|
// If you had to fix the following codeline, then you likely also need to update the
|
|
// implementation of operator==.
|
|
static_assert(sizeof(ColorGradingSettings) == 312, "Please update Settings.cpp");
|
|
return enabled == rhs.enabled &&
|
|
colorspace == rhs.colorspace &&
|
|
quality == rhs.quality &&
|
|
toneMapping == rhs.toneMapping &&
|
|
genericToneMapper == rhs.genericToneMapper &&
|
|
agxToneMapper == rhs.agxToneMapper &&
|
|
luminanceScaling == rhs.luminanceScaling &&
|
|
gamutMapping == rhs.gamutMapping &&
|
|
exposure == rhs.exposure &&
|
|
nightAdaptation == rhs.nightAdaptation &&
|
|
temperature == rhs.temperature &&
|
|
tint == rhs.tint &&
|
|
outRed == rhs.outRed &&
|
|
outGreen == rhs.outGreen &&
|
|
outBlue == rhs.outBlue &&
|
|
shadows == rhs.shadows &&
|
|
midtones == rhs.midtones &&
|
|
highlights == rhs.highlights &&
|
|
ranges == rhs.ranges &&
|
|
contrast == rhs.contrast &&
|
|
vibrance == rhs.vibrance &&
|
|
saturation == rhs.saturation &&
|
|
slope == rhs.slope &&
|
|
offset == rhs.offset &&
|
|
power == rhs.power &&
|
|
gamma == rhs.gamma &&
|
|
midPoint == rhs.midPoint &&
|
|
linkedCurves == rhs.linkedCurves &&
|
|
scale == rhs.scale;
|
|
}
|
|
|
|
class JsonSerializer::Context {
|
|
public:
|
|
const std::string& writeJson(const Settings& in) {
|
|
mStringStream.str("");
|
|
mStringStream.clear();
|
|
mStringStream << in;
|
|
mResultBuffer = mStringStream.str();
|
|
return mResultBuffer;
|
|
}
|
|
private:
|
|
std::ostringstream mStringStream;
|
|
std::string mResultBuffer;
|
|
};
|
|
|
|
JsonSerializer::JsonSerializer() {
|
|
context = new JsonSerializer::Context();
|
|
}
|
|
|
|
JsonSerializer::~JsonSerializer() {
|
|
delete context;
|
|
}
|
|
|
|
const std::string& JsonSerializer::writeJson(const Settings& in) {
|
|
return context->writeJson(in);
|
|
}
|
|
|
|
bool JsonSerializer::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;
|
|
}
|
|
|
|
} // namespace filament::viewer
|