Files
filament/libs/viewer/src/Settings.cpp
Philip Rideout 094e7169cc Introduce a configurable test case generator.
This lets us generate long lists of settings permutations by writing a
simple JSON spec. For example, the following spec would generate six
`Settings` objects, all of which have SSAO enabled. See the unit test
for a larger example.

```
{
    "name": "viewopts",
    "base": { "view.ssao.enabled": true }
    "permute": {
        "view.dithering": ["NONE", "TEMPORAL"],
        "view.sampleCount": [1, 4, 8]
    }
}
```
2020-09-30 16:17:23 -07:00

885 lines
36 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 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 "parse.h"
#include <assert.h>
#include <sstream>
#include <string>
using namespace utils;
namespace filament {
namespace viewer {
// Compares a JSON string token against a C string.
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.
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::Ssct* 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, "lightConeRad") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->lightConeRad);
} else if (compare(tok, jsonChunk, "startTraceDistance") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->startTraceDistance);
} else if (compare(tok, jsonChunk, "contactDistanceMax") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->contactDistanceMax);
} else if (compare(tok, jsonChunk, "intensity") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->intensity);
} else if (compare(tok, jsonChunk, "lightDirection") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->lightDirection);
} else if (compare(tok, jsonChunk, "depthBias") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->depthBias);
} else if (compare(tok, jsonChunk, "depthSlopeBias") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->depthSlopeBias);
} else if (compare(tok, jsonChunk, "sampleCount") == 0){
i = parse(tokens, i + 1, jsonChunk, &out->sampleCount);
} else {
slog.w << "Invalid SSCT key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid SSCT 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 if (compare(tok, jsonChunk, "ssct") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->ssct);
} 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;
}
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[i];
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::Ssct& in) {
std::ostringstream oss;
oss << "{\n"
<< "\"enabled\": " << writeJson(in.enabled) << ",\n"
<< "\"lightConeRad\": " << writeJson(in.lightConeRad) << ",\n"
<< "\"startTraceDistance\": " << writeJson(in.startTraceDistance) << ",\n"
<< "\"contactDistanceMax\": " << writeJson(in.contactDistanceMax) << ",\n"
<< "\"intensity\": " << writeJson(in.intensity) << ",\n"
<< "\"lightDirection\": " << writeJson(in.lightDirection) << ",\n"
<< "\"depthBias\": " << writeJson(in.depthBias) << ",\n"
<< "\"depthSlopeBias\": " << writeJson(in.depthSlopeBias) << ",\n"
<< "\"sampleCount\": " << writeJson(in.sampleCount) << "\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"
<< "\"ssct\": " << writeJson(in.ssct) << "\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