diff --git a/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp b/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp index e391570756..30157a4700 100644 --- a/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp +++ b/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp @@ -94,7 +94,7 @@ Java_com_google_android_filament_utils_AutomationEngine_nTick(JNIEnv* env, jclas extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_utils_AutomationEngine_nApplySettings(JNIEnv* env, jclass klass, jlong nativeAutomation, jstring json, jlong view, jlongArray materials, jlong nativeIbl, - jint lightEntity, jlong nativeLm, jlong scene) { + jint lightEntity, jlong nativeLm, jlong scene, jlong renderer) { using MaterialPointer = MaterialInstance*; jsize materialCount = 0; jlong* longMaterials = nullptr; @@ -112,7 +112,7 @@ Java_com_google_android_filament_utils_AutomationEngine_nApplySettings(JNIEnv* e size_t jsonLength = env->GetStringUTFLength(json); automation->applySettings(nativeJson, jsonLength, (View*) view, ptrMaterials, materialCount, (IndirectLight*) nativeIbl, (Entity&) lightEntity, (LightManager*) nativeLm, - (Scene*) scene); + (Scene*) scene, (Renderer*) renderer); env->ReleaseStringUTFChars(json, nativeJson); if (longMaterials) { env->ReleaseLongArrayElements(materials, longMaterials, 0); diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/AutomationEngine.java b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/AutomationEngine.java index 6fd3e5cb1b..7d094892c6 100644 --- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/AutomationEngine.java +++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/AutomationEngine.java @@ -156,7 +156,7 @@ public class AutomationEngine { */ public void applySettings(@NonNull String settingsJson, @NonNull View view, @Nullable MaterialInstance[] materials, @Nullable IndirectLight ibl, @Entity int light, - @NonNull LightManager lm, @NonNull Scene scene) { + @NonNull LightManager lm, @NonNull Scene scene, @NonNull Renderer renderer) { long[] nativeMaterialInstances = null; if (materials != null) { nativeMaterialInstances = new long[materials.length]; @@ -168,8 +168,9 @@ public class AutomationEngine { long nativeIbl = ibl == null ? 0 : ibl.getNativeObject(); long nativeLm = lm.getNativeObject(); long nativeScene = scene.getNativeObject(); + long nativeRenderer = renderer.getNativeObject(); nApplySettings(mNativeObject, settingsJson, nativeView, nativeMaterialInstances, - nativeIbl, light, nativeLm, nativeScene); + nativeIbl, light, nativeLm, nativeScene, nativeRenderer); } /** @@ -218,7 +219,7 @@ public class AutomationEngine { private static native void nTick(long nativeObject, long view, long[] materials, long renderer, float deltaTime); private static native void nApplySettings(long nativeObject, String jsonSettings, long view, - long[] materials, long ibl, int light, long lightManager, long scene); + long[] materials, long ibl, int light, long lightManager, long scene, long renderer); private static native long nGetColorGrading(long nativeObject, long nativeEngine); private static native void nSignalBatchMode(long nativeObject); private static native void nStopRunning(long nativeObject); diff --git a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt index 29c892313f..aeb35c7524 100644 --- a/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt +++ b/android/filament-utils-android/src/main/java/com/google/android/filament/utils/ModelViewer.kt @@ -76,6 +76,7 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener { val scene: Scene val view: View val camera: Camera + val renderer: Renderer @Entity val light: Int private val uiHelper: UiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK) @@ -87,7 +88,6 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener { private var fetchResourcesJob: Job? = null - private val renderer: Renderer private var swapChain: SwapChain? = null private var assetLoader: AssetLoader private var resourceLoader: ResourceLoader diff --git a/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt b/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt index 03c2c46e27..cac95dbe77 100644 --- a/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt +++ b/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt @@ -240,7 +240,7 @@ class MainActivity : Activity() { val json = StandardCharsets.UTF_8.decode(message.buffer).toString() automation.applySettings(json, modelViewer.view, null, modelViewer.scene.indirectLight, modelViewer.light, modelViewer.engine.lightManager, - modelViewer.scene) + modelViewer.scene, modelViewer.renderer) modelViewer.view.colorGrading = automation.getColorGrading((modelViewer.engine)) } diff --git a/libs/viewer/include/viewer/AutomationEngine.h b/libs/viewer/include/viewer/AutomationEngine.h index a6040b50f4..1237893cf2 100644 --- a/libs/viewer/include/viewer/AutomationEngine.h +++ b/libs/viewer/include/viewer/AutomationEngine.h @@ -159,7 +159,7 @@ public: */ void applySettings(const char* json, size_t jsonLength, View* view, MaterialInstance* const* materials, size_t materialCount, IndirectLight* ibl, - utils::Entity sunlight, LightManager* lm, Scene* scene); + utils::Entity sunlight, LightManager* lm, Scene* scene, Renderer* renderer); /** * Gets a color grading object that corresponds to the latest settings. diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index 97cdccdc7f..6579457610 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -33,6 +33,10 @@ #include namespace filament { + +class Skybox; +class Renderer; + namespace viewer { struct ColorGradingSettings; @@ -41,6 +45,7 @@ struct MaterialSettings; struct Settings; struct ViewSettings; struct LightSettings; +struct ViewerOptions; using AmbientOcclusionOptions = filament::View::AmbientOcclusionOptions; using AntiAliasing = filament::View::AntiAliasing; @@ -61,6 +66,8 @@ void applySettings(const ViewSettings& settings, View* dest); void applySettings(const MaterialSettings& settings, MaterialInstance* dest); void applySettings(const LightSettings& settings, IndirectLight* ibl, utils::Entity sunlight, LightManager* lm, Scene* scene); +void applySettings(const ViewerOptions& settings, Camera* camera, Skybox* skybox, + Renderer* renderer); // Creates a new ColorGrading object based on the given settings. ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* engine); @@ -157,10 +164,22 @@ struct LightSettings { float iblRotation = 0.0f; }; +struct ViewerOptions { + float cameraAperture = 16.0f; + float cameraSpeed = 125.0f; + float cameraISO = 100.0f; + float groundShadowStrength = 0.75f; + bool groundPlaneEnabled = false; + bool skyboxEnabled = true; + sRGBColor backgroundColor = { 0.0f }; + float cameraFocalLength = 28.0f; +}; + struct Settings { ViewSettings view; MaterialSettings material; LightSettings lighting; + ViewerOptions viewer; }; } // namespace viewer diff --git a/libs/viewer/include/viewer/SimpleViewer.h b/libs/viewer/include/viewer/SimpleViewer.h index c1ca119e6b..7f5e7648f2 100644 --- a/libs/viewer/include/viewer/SimpleViewer.h +++ b/libs/viewer/include/viewer/SimpleViewer.h @@ -196,6 +196,8 @@ public: void stopAnimation() { mCurrentAnimation = 0; } + int getCurrentCamera() const { return mCurrentCamera; } + private: void updateIndirectLight(); @@ -223,6 +225,9 @@ private: int mSidebarWidth; uint32_t mFlags; + // 0 is the default "free camera". Additional cameras come from the gltf file (1-based index). + int mCurrentCamera = 0; + // Color grading UI state. float mRangePlot[1024 * 3]; float mCurvePlot[1024 * 3]; diff --git a/libs/viewer/src/AutomationEngine.cpp b/libs/viewer/src/AutomationEngine.cpp index 9244058926..359b884eb2 100644 --- a/libs/viewer/src/AutomationEngine.cpp +++ b/libs/viewer/src/AutomationEngine.cpp @@ -138,7 +138,7 @@ void AutomationEngine::exportSettings(const Settings& settings, const char* file void AutomationEngine::applySettings(const char* json, size_t jsonLength, View* view, MaterialInstance* const* materials, size_t materialCount, IndirectLight* ibl, - utils::Entity sunlight, LightManager* lm, Scene* scene) { + utils::Entity sunlight, LightManager* lm, Scene* scene, Renderer* renderer) { JsonSerializer serializer; serializer.readJson(json, jsonLength, mSettings); viewer::applySettings(mSettings->view, view); @@ -146,6 +146,9 @@ void AutomationEngine::applySettings(const char* json, size_t jsonLength, View* viewer::applySettings(mSettings->material, materials[i]); } viewer::applySettings(mSettings->lighting, ibl, sunlight, lm, scene); + Camera* camera = &view->getCamera(); + Skybox* skybox = scene->getSkybox(); + viewer::applySettings(mSettings->viewer, camera, skybox, renderer); } ColorGrading* AutomationEngine::getColorGrading(Engine* engine) { diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index eebcf95080..60c734f378 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -18,6 +18,10 @@ #include "jsonParseUtils.h" +#include +#include +#include + #include #include @@ -749,6 +753,40 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, LightSet 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 { + 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; @@ -761,6 +799,8 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, Settings* out) 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); @@ -825,6 +865,31 @@ void applySettings(const LightSettings& settings, IndirectLight* ibl, utils::Ent } } +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); + } +} + ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* engine) { return ColorGrading::Builder() .quality(settings.quality) @@ -1096,6 +1161,19 @@ static std::ostream& operator<<(std::ostream& out, const LightSettings& in) { << "}"; } +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" + << "}"; +} + static std::ostream& operator<<(std::ostream& out, const DepthOfFieldOptions& in) { return out << "{\n" << "\"focusDistance\": " << (in.focusDistance) << ",\n" @@ -1159,7 +1237,8 @@ static std::ostream& operator<<(std::ostream& out, const Settings& in) { return out << "{\n" << "\"view\": " << (in.view) << ",\n" << "\"material\": " << (in.material) << ",\n" - << "\"lighting\": " << (in.lighting) + << "\"lighting\": " << (in.lighting) << ",\n" + << "\"viewer\": " << (in.viewer) << "}"; } diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp index 54c3bab10b..1233cec56e 100644 --- a/libs/viewer/src/SimpleViewer.cpp +++ b/libs/viewer/src/SimpleViewer.cpp @@ -339,6 +339,7 @@ void SimpleViewer::populateScene(FilamentAsset* asset, bool scale, if (mAsset != asset) { removeAsset(); mAsset = asset; + mCurrentCamera = 0; if (!asset) { mAnimator = nullptr; return; @@ -658,6 +659,84 @@ void SimpleViewer::updateUserInterface() { ImGui::Unindent(); } + if (ImGui::CollapsingHeader("Scene")) { + ImGui::Indent(); + ImGui::Checkbox("Show skybox", &mSettings.viewer.skyboxEnabled); + ImGui::ColorEdit3("Background color", &mSettings.viewer.backgroundColor.r); + + // We do not yet support ground shadow in remote mode (i.e. when mAsset is null) + if (mAsset) { + ImGui::Checkbox("Ground shadow", &mSettings.viewer.groundPlaneEnabled); + ImGui::Indent(); + ImGui::SliderFloat("Strength", &mSettings.viewer.groundShadowStrength, 0.0f, 1.0f); + ImGui::Unindent(); + } + + ImGui::Unindent(); + } + + if (ImGui::CollapsingHeader("Camera")) { + ImGui::Indent(); + + // We do not yet support focal length in remote mode (i.e. when mAsset is null) + if (mAsset) { + ImGui::SliderFloat("Focal length (mm)", &mSettings.viewer.cameraFocalLength, + 16.0f, 90.0f); + } + + bool median = mSettings.view.dof.filter == View::DepthOfFieldOptions::Filter::MEDIAN; + + ImGui::SliderFloat("Aperture", &mSettings.viewer.cameraAperture, 1.0f, 32.0f); + ImGui::SliderFloat("Speed (1/s)", &mSettings.viewer.cameraSpeed, 1000.0f, 1.0f); + ImGui::SliderFloat("ISO", &mSettings.viewer.cameraISO, 25.0f, 6400.0f); + ImGui::Checkbox("DoF", &mSettings.view.dof.enabled); + ImGui::SliderFloat("Focus distance", &mSettings.view.dof.focusDistance, 0.0f, 30.0f); + ImGui::SliderFloat("Blur scale", &mSettings.view.dof.cocScale, 0.1f, 10.0f); + ImGui::Checkbox("Median Filter", &median); + mSettings.view.dof.filter = median ? + View::DepthOfFieldOptions::Filter::MEDIAN : + View::DepthOfFieldOptions::Filter::NONE; + + if (ImGui::CollapsingHeader("Vignette")) { + ImGui::Checkbox("Enabled##vignetteEnabled", &mSettings.view.vignette.enabled); + ImGui::SliderFloat("Mid point", &mSettings.view.vignette.midPoint, 0.0f, 1.0f); + ImGui::SliderFloat("Roundness", &mSettings.view.vignette.roundness, 0.0f, 1.0f); + ImGui::SliderFloat("Feather", &mSettings.view.vignette.feather, 0.0f, 1.0f); + ImGui::ColorEdit3("Color##vignetteColor", &mSettings.view.vignette.color.r); + } + + if (mAsset != nullptr) { + + const utils::Entity* cameras = mAsset->getCameraEntities(); + const size_t cameraCount = mAsset->getCameraEntityCount(); + + std::vector names; + names.reserve(cameraCount + 1); + names.push_back("Free camera"); + int c = 0; + for (size_t i = 0; i < cameraCount; i++) { + const char* n = mAsset->getName(cameras[i]); + if (n) { + names.emplace_back(n); + } else { + char buf[32]; + sprintf(buf, "Unnamed camera %d", c++); + names.emplace_back(buf); + } + } + + std::vector cstrings; + cstrings.reserve(names.size()); + for (size_t i = 0; i < names.size(); i++) { + cstrings.push_back(names[i].c_str()); + } + + ImGui::ListBox("Cameras", &mCurrentCamera, cstrings.data(), cstrings.size()); + } + + ImGui::Unindent(); + } + colorGradingUI(mSettings, mRangePlot, mCurvePlot); // At this point, all View settings have been modified, diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index b1afd20ab1..2fd7c9e19f 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -83,16 +83,6 @@ struct App { bool actualSize = false; - struct ViewOptions { - float cameraAperture = 16.0f; - float cameraSpeed = 125.0f; - float cameraISO = 100.0f; - float groundShadowStrength = 0.75f; - bool groundPlaneEnabled = false; - bool skyboxEnabled = true; - sRGBColor backgroundColor = { 0.0f }; - } viewOptions; - struct Scene { Entity groundPlane; VertexBuffer* groundVertexBuffer; @@ -105,9 +95,6 @@ struct App { ColorGrading* colorGrading = nullptr; - // 0 is the default "free camera". Additional cameras come from the gltf file. - int currentCamera = 0; - std::string messageBoxText; std::string settingsFile; std::string batchFile; @@ -265,7 +252,8 @@ static void createGroundPlane(Engine* engine, Scene* scene, App& app) { Material* shadowMaterial = Material::Builder() .package(GLTF_VIEWER_GROUNDSHADOW_DATA, GLTF_VIEWER_GROUNDSHADOW_SIZE) .build(*engine); - shadowMaterial->setDefaultParameter("strength", app.viewOptions.groundShadowStrength); + auto& viewerOptions = app.viewer->getSettings().viewer; + shadowMaterial->setDefaultParameter("strength", viewerOptions.groundShadowStrength); const static uint32_t indices[] = { 0, 1, 2, 2, 3, 0 @@ -572,71 +560,6 @@ int main(int argc, char** argv) { ImGui::Unindent(); } - if (ImGui::CollapsingHeader("Scene")) { - ImGui::Indent(); - ImGui::Checkbox("Show skybox", &app.viewOptions.skyboxEnabled); - ImGui::ColorEdit3("Background color", &app.viewOptions.backgroundColor.r); - ImGui::Checkbox("Ground shadow", &app.viewOptions.groundPlaneEnabled); - ImGui::Indent(); - ImGui::SliderFloat("Strength", &app.viewOptions.groundShadowStrength, 0.0f, 1.0f); - ImGui::Unindent(); - ImGui::Unindent(); - } - - if (ImGui::CollapsingHeader("Camera")) { - ViewSettings& settings = app.viewer->getSettings().view; - - bool median = settings.dof.filter == View::DepthOfFieldOptions::Filter::MEDIAN; - - ImGui::Indent(); - ImGui::SliderFloat("Focal length (mm)", &FilamentApp::get().getCameraFocalLength(), 16.0f, 90.0f); - ImGui::SliderFloat("Aperture", &app.viewOptions.cameraAperture, 1.0f, 32.0f); - ImGui::SliderFloat("Speed (1/s)", &app.viewOptions.cameraSpeed, 1000.0f, 1.0f); - ImGui::SliderFloat("ISO", &app.viewOptions.cameraISO, 25.0f, 6400.0f); - ImGui::Checkbox("DoF", &settings.dof.enabled); - ImGui::SliderFloat("Focus distance", &settings.dof.focusDistance, 0.0f, 30.0f); - ImGui::SliderFloat("Blur scale", &settings.dof.cocScale, 0.1f, 10.0f); - ImGui::Checkbox("Median Filter", &median); - settings.dof.filter = median ? - View::DepthOfFieldOptions::Filter::MEDIAN : - View::DepthOfFieldOptions::Filter::NONE; - - if (ImGui::CollapsingHeader("Vignette")) { - ImGui::Checkbox("Enabled##vignetteEnabled", &settings.vignette.enabled); - ImGui::SliderFloat("Mid point", &settings.vignette.midPoint, 0.0f, 1.0f); - ImGui::SliderFloat("Roundness", &settings.vignette.roundness, 0.0f, 1.0f); - ImGui::SliderFloat("Feather", &settings.vignette.feather, 0.0f, 1.0f); - ImGui::ColorEdit3("Color##vignetteColor", &settings.vignette.color.r); - } - - const utils::Entity* cameras = app.asset->getCameraEntities(); - const size_t cameraCount = app.asset->getCameraEntityCount(); - - std::vector names; - names.reserve(cameraCount + 1); - names.push_back("Free camera"); - int c = 0; - for (size_t i = 0; i < cameraCount; i++) { - const char* n = app.asset->getName(cameras[i]); - if (n) { - names.emplace_back(n); - } else { - char buf[32]; - sprintf(buf, "Unnamed camera %d", c++); - names.emplace_back(buf); - } - } - - std::vector cstrings; - cstrings.reserve(names.size()); - for (size_t i = 0; i < names.size(); i++) { - cstrings.push_back(names[i].c_str()); - } - - ImGui::ListBox("Cameras", &app.currentCamera, cstrings.data(), cstrings.size()); - ImGui::Unindent(); - } - if (ImGui::CollapsingHeader("Debug")) { if (ImGui::Button("Capture frame")) { auto& debug = engine->getDebugRegistry(); @@ -705,51 +628,38 @@ int main(int argc, char** argv) { auto preRender = [&app](Engine* engine, View* view, Scene* scene, Renderer* renderer) { auto& rcm = engine->getRenderableManager(); auto instance = rcm.getInstance(app.scene.groundPlane); + const auto& viewerOptions = app.viewer->getSettings().viewer; rcm.setLayerMask(instance, - 0xff, app.viewOptions.groundPlaneEnabled ? 0xff : 0x00); + 0xff, viewerOptions.groundPlaneEnabled ? 0xff : 0x00); + + FilamentApp::get().getCameraFocalLength() = viewerOptions.cameraFocalLength; const size_t cameraCount = app.asset->getCameraEntityCount(); view->setCamera(app.mainCamera); - if (app.currentCamera > 0) { - const int gltfCamera = app.currentCamera - 1; - if (gltfCamera < cameraCount) { - const utils::Entity* cameras = app.asset->getCameraEntities(); - Camera* c = engine->getCameraComponent(cameras[gltfCamera]); - assert(c); - view->setCamera(c); - // Override the aspect ratio in the glTF file and adjust the aspect ratio of this - // camera to the viewport. - const Viewport& vp = view->getViewport(); - double aspectRatio = (double) vp.width / vp.height; - c->setScaling({1.0 / aspectRatio, 1.0}); - } else { - // gltfCamera is out of bounds. Reset camera selection to main camera. - app.currentCamera = 0; - } + const int currentCamera = app.viewer->getCurrentCamera(); + if (currentCamera > 0 && currentCamera <= cameraCount) { + const utils::Entity* cameras = app.asset->getCameraEntities(); + Camera* camera = engine->getCameraComponent(cameras[currentCamera - 1]); + assert_invariant(camera); + view->setCamera(camera); + + // Override the aspect ratio in the glTF file and adjust the aspect ratio of this + // camera to the viewport. + const Viewport& vp = view->getViewport(); + double aspectRatio = (double) vp.width / vp.height; + camera->setScaling({1.0 / aspectRatio, 1.0}); } - Camera& camera = view->getCamera(); - camera.setExposure( - app.viewOptions.cameraAperture, - 1.0f / app.viewOptions.cameraSpeed, - app.viewOptions.cameraISO); - app.scene.groundMaterial->setDefaultParameter( - "strength", app.viewOptions.groundShadowStrength); + "strength", viewerOptions.groundShadowStrength); - auto ibl = FilamentApp::get().getIBL(); - if (ibl) { - ibl->getSkybox()->setLayerMask(0xff, app.viewOptions.skyboxEnabled ? 0xff : 0x00); - } - - // 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(app.viewOptions.backgroundColor), 1.0f }, - .clear = true - }); + // This applies clear options, the skybox mask, and some camera settings. + Camera& camera = view->getCamera(); + Skybox* skybox = scene->getSkybox(); + applySettings(app.viewer->getSettings().viewer, &camera, skybox, renderer); + // Check if color grading has changed. ColorGradingSettings& options = app.viewer->getSettings().view.colorGrading; if (options.enabled) { if (options != app.lastColorGradingOptions) {