/* * 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 #include "jsonParseUtils.h" #include #include #include #include #include #include #include #include #include using namespace utils; namespace filament { namespace viewer { static const char* to_string(bool b) { return b ? "true" : "false"; } // 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, uint16_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); if (0 == compare(tokens[i], jsonChunk, "true")) { *val = true; return i + 1; } if (0 == compare(tokens[i], jsonChunk, "false")) { *val = false; return i + 1; } return -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, "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, 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, VsmShadowOptions* 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, "anisotropy")) { i = parse(tokens, i + 1, jsonChunk, &out->anisotropy); } else if (0 == compare(tok, jsonChunk, "mipmapping")) { i = parse(tokens, i + 1, jsonChunk, &out->mipmapping); } else if (0 == compare(tok, jsonChunk, "exponent")) { i = parse(tokens, i + 1, jsonChunk, &out->exponent); } else if (0 == compare(tok, jsonChunk, "minVarianceScale")) { i = parse(tokens, i + 1, jsonChunk, &out->minVarianceScale); } else if (0 == compare(tok, jsonChunk, "lightBleedReduction")) { i = parse(tokens, i + 1, jsonChunk, &out->lightBleedReduction); } 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, 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, 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, "shoulder") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->shoulder); } 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, 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, "genericToneMapper") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->genericToneMapper); } else if (compare(tok, jsonChunk, "luminanceScaling") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->luminanceScaling); } else if (compare(tok, jsonChunk, "exposure") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->exposure); } 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, "shadowDistance") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->shadowDistance); } 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 if (compare(tok, jsonChunk, "rayCount") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->rayCount); } 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, "bilateralThreshold") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->bilateralThreshold); } else if (compare(tok, jsonChunk, "quality") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->quality); } else if (compare(tok, jsonChunk, "lowPassFilter") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->lowPassFilter); } 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, "bentNormals") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->bentNormals); } 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 if (compare(tok, jsonChunk, "lensFlare") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->lensFlare); } else if (compare(tok, jsonChunk, "starburst") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->starburst); } else if (compare(tok, jsonChunk, "chromaticAberration") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->chromaticAberration); } else if (compare(tok, jsonChunk, "ghostCount") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->ghostCount); } else if (compare(tok, jsonChunk, "ghostSpacing") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->ghostSpacing); } else if (compare(tok, jsonChunk, "ghostThreshold") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->ghostThreshold); } else if (compare(tok, jsonChunk, "haloThickness") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->haloThickness); } else if (compare(tok, jsonChunk, "haloRadius") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->haloRadius); } else if (compare(tok, jsonChunk, "haloThreshold") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->haloThreshold); } 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::Filter* out) { if (0 == compare(tokens[i], jsonChunk, "NONE")) { *out = DepthOfFieldOptions::Filter::NONE; } else if (0 == compare(tokens[i], jsonChunk, "MEDIAN")) { *out = DepthOfFieldOptions::Filter::MEDIAN; } else { slog.w << "Invalid DepthOfFieldOptions::Filter: '" << STR(tokens[i], jsonChunk) << "'" << io::endl; } return i + 1; } 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, "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 if (0 == compare(tok, jsonChunk, "filter")) { i = parse(tokens, i + 1, jsonChunk, &out->filter); } else if (0 == compare(tok, jsonChunk, "nativeResolution")) { i = parse(tokens, i + 1, jsonChunk, &out->nativeResolution); } else if (0 == compare(tok, jsonChunk, "foregroundRingCount")) { i = parse(tokens, i + 1, jsonChunk, &out->foregroundRingCount); } else if (0 == compare(tok, jsonChunk, "backgroundRingCount")) { i = parse(tokens, i + 1, jsonChunk, &out->backgroundRingCount); } else if (0 == compare(tok, jsonChunk, "fastGatherRingCount")) { i = parse(tokens, i + 1, jsonChunk, &out->fastGatherRingCount); } else if (0 == compare(tok, jsonChunk, "maxForegroundCOC")) { i = parse(tokens, i + 1, jsonChunk, &out->maxForegroundCOC); } else if (0 == compare(tok, jsonChunk, "maxBackgroundCOC")) { i = parse(tokens, i + 1, jsonChunk, &out->maxBackgroundCOC); } 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, "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 static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, MaterialProperty* 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, "msaaSamples") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->msaaSamples); } 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 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, "screenSpaceContactShadows") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->screenSpaceContactShadows); } 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]; } else if (compare(tok, jsonChunk, "vsm") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->vsm); } 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, "sunlightIntensity") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->sunlightIntensity); } 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, "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, "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(const ViewSettings& settings, View* dest) { dest->setSampleCount(settings.sampleCount); dest->setAntiAliasing(settings.antiAliasing); dest->setTemporalAntiAliasingOptions(settings.taa); dest->setAmbientOcclusionOptions(settings.ssao); 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->setPostProcessingEnabled(settings.postProcessingEnabled); } template static void apply(MaterialProperty prop, MaterialInstance* dest) { if (!prop.name.empty()) { dest->setParameter(prop.name.c_str(), prop.value); } } void applySettings(const MaterialSettings& settings, MaterialInstance* dest) { for (auto prop : settings.scalar) { apply(prop, dest); } for (auto prop : settings.float3) { apply(prop, dest); } for (auto prop : settings.float4) { apply(prop, dest); } } void applySettings(const LightSettings& settings, IndirectLight* ibl, utils::Entity sunlight, utils::Entity* sceneLights, size_t sceneLightCount, LightManager* lm, Scene* scene) { auto light = lm->getInstance(sunlight); if (light) { if (settings.enableSunlight) { scene->addEntity(sunlight); } else { scene->remove(sunlight); } lm->setIntensity(light, settings.sunlightIntensity); 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++) { light = lm->getInstance(sceneLights[i]); if (lm->isSpotLight(light)) { lm->setShadowCaster(light, settings.enableShadows); } lm->setShadowOptions(light, settings.shadowOptions); } } static LinearColor inverseTonemapSRGB(sRGBColor x) { return (x * -0.155) / (x - 1.019); } void applySettings(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); } } 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::GENERIC: return new GenericToneMapper( settings.genericToneMapper.contrast, settings.genericToneMapper.shoulder, 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) .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) .build(*engine); delete toneMapper; return colorGrading; } static std::ostream& operator<<(std::ostream& out, AntiAliasing in) { switch (in) { case AntiAliasing::NONE: return out << "\"NONE\""; case AntiAliasing::FXAA: return out << "\"FXAA\""; } return out << "\"INVALID\""; } static std::ostream& operator<<(std::ostream& out, Dithering in) { switch (in) { case Dithering::NONE: return out << "\"NONE\""; case Dithering::TEMPORAL: return out << "\"TEMPORAL\""; } return out << "\"INVALID\""; } static std::ostream& operator<<(std::ostream& out, ShadowType in) { switch (in) { case ShadowType::PCF: return out << "\"PCF\""; case ShadowType::VSM: return out << "\"VSM\""; } return out << "\"INVALID\""; } static std::ostream& operator<<(std::ostream& out, BlendMode in) { switch (in) { case BlendMode::ADD: return out << "\"ADD\""; case BlendMode::INTERPOLATE: return out << "\"INTERPOLATE\""; } return out << "\"INVALID\""; } static std::ostream& operator<<(std::ostream& out, VQL in) { switch (in) { case VQL::LOW: return out << "\"LOW\""; case VQL::MEDIUM: return out << "\"MEDIUM\""; case VQL::HIGH: return out << "\"HIGH\""; case VQL::ULTRA: return out << "\"ULTRA\""; } return out << "\"INVALID\""; } 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, DepthOfFieldOptions::Filter in) { switch (in) { case DepthOfFieldOptions::Filter::NONE: return out << "\"NONE\""; case DepthOfFieldOptions::Filter::MEDIAN: return out << "\"MEDIAN\""; } 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::GENERIC: return out << "\"GENERIC\""; case ToneMapping::DISPLAY_RANGE: return out << "\"DISPLAY_RANGE\""; } return out << "\"INVALID\""; } static std::ostream& writeJson(std::ostream& oss, const float* v, int count) { oss << "["; for (int i = 0; i < count; i++) { oss << v[i]; if (i < count - 1) { oss << ", "; } } oss << "]"; return oss; } static std::ostream& operator<<(std::ostream& out, math::float3 v) { return writeJson(out, &v.x, 3); } static std::ostream& operator<<(std::ostream& out, math::float4 v) { return writeJson(out, &v.x, 4); } static std::ostream& operator<<(std::ostream& out, const TemporalAntiAliasingOptions& in) { return out << "{\n" << "\"filterWidth\": " << (in.filterWidth) << ",\n" << "\"feedback\": " << (in.feedback) << ",\n" << "\"enabled\": " << to_string(in.enabled) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const GenericToneMapperSettings& in) { return out << "{\n" << "\"contrast\": " << (in.contrast) << ",\n" << "\"shoulder\": " << (in.shoulder) << ",\n" << "\"midGrayIn\": " << (in.midGrayIn) << ",\n" << "\"midGrayOut\": " << (in.midGrayOut) << ",\n" << "\"hdrMax\": " << (in.hdrMax) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const ColorGradingSettings& in) { return out << "{\n" << "\"enabled\": " << to_string(in.enabled) << ",\n" << "\"quality\": " << (in.quality) << ",\n" << "\"toneMapping\": " << (in.toneMapping) << ",\n" << "\"genericToneMapper\": " << (in.genericToneMapper) << ",\n" << "\"luminanceScaling\": " << to_string(in.luminanceScaling) << ",\n" << "\"exposure\": " << (in.exposure) << ",\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 AmbientOcclusionOptions::Ssct& in) { return out << "{\n" << "\"enabled\": " << to_string(in.enabled) << ",\n" << "\"lightConeRad\": " << (in.lightConeRad) << ",\n" << "\"shadowDistance\": " << (in.shadowDistance) << ",\n" << "\"contactDistanceMax\": " << (in.contactDistanceMax) << ",\n" << "\"intensity\": " << (in.intensity) << ",\n" << "\"lightDirection\": " << (in.lightDirection) << ",\n" << "\"depthBias\": " << (in.depthBias) << ",\n" << "\"depthSlopeBias\": " << (in.depthSlopeBias) << ",\n" << "\"sampleCount\": " << int(in.sampleCount) << ",\n" << "\"rayCount\": " << int(in.rayCount) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in) { return out << "{\n" << "\"radius\": " << (in.radius) << ",\n" << "\"power\": " << (in.power) << ",\n" << "\"bias\": " << (in.bias) << ",\n" << "\"resolution\": " << (in.resolution) << ",\n" << "\"intensity\": " << (in.intensity) << ",\n" << "\"bilateralThreshold\": " << (in.bilateralThreshold) << ",\n" << "\"quality\": " << (in.quality) << ",\n" << "\"lowPassFilter\": " << (in.lowPassFilter) << ",\n" << "\"upsampling\": " << (in.upsampling) << ",\n" << "\"enabled\": " << to_string(in.enabled) << ",\n" << "\"bentNormals\": " << to_string(in.bentNormals) << ",\n" << "\"minHorizonAngleRad\": " << (in.minHorizonAngleRad) << ",\n" << "\"ssct\": " << (in.ssct) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const BloomOptions& in) { return out << "{\n" << "\"strength\": " << (in.strength) << ",\n" << "\"resolution\": " << (in.resolution) << ",\n" << "\"anamorphism\": " << (in.anamorphism) << ",\n" << "\"levels\": " << int(in.levels) << ",\n" << "\"blendMode\": " << (in.blendMode) << ",\n" << "\"threshold\": " << to_string(in.threshold) << ",\n" << "\"enabled\": " << to_string(in.enabled) << ",\n" << "\"highlight\": " << (in.highlight) << ",\n" << "\"lensFlare\": " << to_string(in.lensFlare) << ",\n" << "\"starburst\": " << to_string(in.starburst) << ",\n" << "\"chromaticAberration\": " << (in.chromaticAberration) << ",\n" << "\"ghostCount\": " << int(in.ghostCount) << ",\n" << "\"ghostSpacing\": " << (in.ghostSpacing) << ",\n" << "\"ghostThreshold\": " << (in.ghostThreshold) << ",\n" << "\"haloThickness\": " << (in.haloThickness) << ",\n" << "\"haloRadius\": " << (in.haloRadius) << ",\n" << "\"haloThreshold\": " << (in.haloThreshold) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const FogOptions& in) { return out << "{\n" << "\"distance\": " << (in.distance) << ",\n" << "\"maximumOpacity\": " << (in.maximumOpacity) << ",\n" << "\"height\": " << (in.height) << ",\n" << "\"heightFalloff\": " << (in.heightFalloff) << ",\n" << "\"color\": " << (in.color) << ",\n" << "\"density\": " << (in.density) << ",\n" << "\"inScatteringStart\": " << (in.inScatteringStart) << ",\n" << "\"inScatteringSize\": " << (in.inScatteringSize) << ",\n" << "\"fogColorFromIbl\": " << to_string(in.fogColorFromIbl) << ",\n" << "\"enabled\": " << to_string(in.enabled) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const LightManager::ShadowOptions& in) { const float* splits = in.cascadeSplitPositions; math::float3 splitsVector = { splits[0], splits[1], splits[2] }; return out << "{\n" << "\"vsm\": {\n" << "\"msaaSamples\": " << int(in.vsm.msaaSamples) << ",\n" << "\"blurWidth\": " << in.vsm.blurWidth << "\n" << "},\n" << "\"mapSize\": " << in.mapSize << ",\n" << "\"screenSpaceContactShadows\": " << to_string(in.screenSpaceContactShadows) << ",\n" << "\"shadowCascades\": " << int(in.shadowCascades) << ",\n" << "\"cascadeSplitPositions\": " << (splitsVector) << "\n" << "}"; } template void writeJson(MaterialProperty 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 (auto prop : in.scalar) { writeJson(prop, oss); } oss << "},\n"; oss << "\"float3\": {\n"; for (auto prop : in.float3) { writeJson(prop, oss); } oss << "},\n"; oss << "\"float4\": {\n"; for (auto prop : in.float4) { writeJson(prop, oss); } oss << "},\n"; oss << "}"; std::string result = oss.str(); const auto replace = [&result](std::string s, 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" << "\"sunlightIntensity\": " << (in.sunlightIntensity) << ",\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" << "\"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" << "\"autoScaleEnabled\": " << to_string(in.autoScaleEnabled) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const DepthOfFieldOptions& in) { return out << "{\n" << "\"cocScale\": " << (in.cocScale) << ",\n" << "\"maxApertureDiameter\": " << (in.maxApertureDiameter) << ",\n" << "\"enabled\": " << to_string(in.enabled) << ",\n" << "\"filter\": " << (in.filter) << ",\n" << "\"nativeResolution\": " << to_string(in.nativeResolution) << ",\n" << "\"foregroundRingCount\": " << int(in.foregroundRingCount) << ",\n" << "\"backgroundRingCount\": " << int(in.backgroundRingCount) << ",\n" << "\"fastGatherRingCount\": " << int(in.fastGatherRingCount) << ",\n" << "\"maxForegroundCOC\": " << (in.maxForegroundCOC) << ",\n" << "\"maxBackgroundCOC\": " << (in.maxBackgroundCOC) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const VignetteOptions& in) { return out << "{\n" << "\"midPoint\": " << (in.midPoint) << ",\n" << "\"roundness\": " << (in.roundness) << ",\n" << "\"feather\": " << (in.feather) << ",\n" << "\"color\": " << (in.color) << ",\n" << "\"enabled\": " << to_string(in.enabled) << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const RenderQuality& in) { return out << "{\n" << "\"hdrColorBuffer\": " << (in.hdrColorBuffer) << "\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 VsmShadowOptions& in) { return out << "{\n" << "\"anisotropy\": " << int(in.anisotropy) << ",\n" << "\"mipmapping\": " << to_string(in.mipmapping) << ",\n" << "\"exponent\": " << in.exponent << ",\n" << "\"minVarianceScale\": " << in.minVarianceScale << ",\n" << "\"lightBleedReduction\": " << in.lightBleedReduction << "\n" << "}"; } static std::ostream& operator<<(std::ostream& out, const ViewSettings& in) { return out << "{\n" << "\"sampleCount\": " << int(in.sampleCount) << ",\n" << "\"antiAliasing\": " << in.antiAliasing << ",\n" << "\"taa\": " << in.taa << ",\n" << "\"colorGrading\": " << (in.colorGrading) << ",\n" << "\"ssao\": " << (in.ssao) << ",\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" << "\"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) == 20, "Please update Settings.cpp"); return contrast == rhs.contrast && shoulder == rhs.shoulder && midGrayIn == rhs.midGrayIn && midGrayOut == rhs.midGrayOut && hdrMax == rhs.hdrMax; } 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) == 228, "Please update Settings.cpp"); return enabled == rhs.enabled && quality == rhs.quality && toneMapping == rhs.toneMapping && genericToneMapper == rhs.genericToneMapper && luminanceScaling == rhs.luminanceScaling && exposure == rhs.exposure && 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 viewer } // namespace filament