diff --git a/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp b/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp index 30157a4700..dee338aec9 100644 --- a/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp +++ b/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp @@ -120,6 +120,33 @@ Java_com_google_android_filament_utils_AutomationEngine_nApplySettings(JNIEnv* e } } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_utils_AutomationEngine_nGetViewerOptions(JNIEnv* env, jclass, + jlong nativeObject, jobject result) { + AutomationEngine* automation = (AutomationEngine*) nativeObject; + auto options = automation->getViewerOptions(); + + const jclass klass = env->GetObjectClass(result); + + const jfieldID cameraAperture = env->GetFieldID(klass, "cameraAperture", "F"); + const jfieldID cameraSpeed = env->GetFieldID(klass, "cameraSpeed", "F"); + const jfieldID cameraISO = env->GetFieldID(klass, "cameraISO", "F"); + const jfieldID groundShadowStrength = env->GetFieldID(klass, "groundShadowStrength", "F"); + const jfieldID groundPlaneEnabled = env->GetFieldID(klass, "groundPlaneEnabled", "Z"); + const jfieldID skyboxEnabled = env->GetFieldID(klass, "skyboxEnabled", "Z"); + const jfieldID cameraFocalLength = env->GetFieldID(klass, "cameraFocalLength", "F"); + const jfieldID cameraFocusDistance = env->GetFieldID(klass, "cameraFocusDistance", "F"); + + env->SetFloatField(result, cameraAperture, options.cameraAperture); + env->SetFloatField(result, cameraSpeed, options.cameraSpeed); + env->SetFloatField(result, cameraISO, options.cameraISO); + env->SetFloatField(result, groundShadowStrength, options.groundShadowStrength); + env->SetBooleanField(result, groundPlaneEnabled, options.groundPlaneEnabled); + env->SetBooleanField(result, skyboxEnabled, options.skyboxEnabled); + env->SetFloatField(result, cameraFocalLength, options.cameraFocalLength); + env->SetFloatField(result, cameraFocusDistance, options.cameraFocusDistance); +} + extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_utils_AutomationEngine_nGetColorGrading(JNIEnv*, jclass, jlong nativeObject, jlong nativeEngine) { 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 7d094892c6..847519e9f2 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 @@ -62,18 +62,32 @@ public class AutomationEngine { * Minimum time that automation waits between applying a settings object and advancing * to the next test case. Specified in seconds. */ - float sleepDuration = 0.2f; + public float sleepDuration = 0.2f; /** * Similar to sleepDuration, but expressed as a frame count. Both the minimum sleep time * and the minimum frame count must be elapsed before automation advances to the next test. */ - int minFrameCount = 2; + public int minFrameCount = 2; /** * If true, test progress is dumped to the utils Log (info priority). */ - boolean verbose = true; + public boolean verbose = true; + } + + /** + * Allows remote control for the viewer. + */ + public static class ViewerOptions { + public float cameraAperture = 16.0f; + public float cameraSpeed = 125.0f; + public float cameraISO = 100.0f; + public float groundShadowStrength = 0.75f; + public boolean groundPlaneEnabled = false; + public boolean skyboxEnabled = true; + public float cameraFocalLength = 28.0f; + public float cameraFocusDistance = 0.0f; } /** @@ -173,12 +187,25 @@ public class AutomationEngine { nativeIbl, light, nativeLm, nativeScene, nativeRenderer); } + /** + * Gets the current viewer options. + * + * NOTE: Focal length here might be different from the user-specified value, due to DoF options. + */ + @NonNull + public ViewerOptions getViewerOptions() { + ViewerOptions result = new ViewerOptions(); + nGetViewerOptions(mNativeObject, result); + return result; + } + /** * Gets a color grading object that corresponds to the latest settings. * * This method either returns a cached instance, or it destroys the cached instance and creates * a new one. */ + @NonNull public ColorGrading getColorGrading(@NonNull Engine engine) { // The native layer automatically destroys the old color grading instance, // so there is no need to call Engine#destroyColorGrading here. @@ -220,6 +247,7 @@ public class AutomationEngine { float deltaTime); private static native void nApplySettings(long nativeObject, String jsonSettings, long view, long[] materials, long ibl, int light, long lightManager, long scene, long renderer); + private static native void nGetViewerOptions(long nativeObject, Object result); 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 aeb35c7524..ac15823d18 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 @@ -29,7 +29,6 @@ import java.nio.Buffer private const val kNearPlane = 0.5 private const val kFarPlane = 10000.0 -private const val kFovDegrees = 45.0 private const val kAperture = 16f private const val kShutterSpeed = 1f / 125f private const val kSensitivity = 100f @@ -73,6 +72,12 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener { var normalizeSkinningWeights = true var recomputeBoundingBoxes = false + var cameraFocalLength = 28f + set(value) { + field = value + updateCameraProjection() + } + val scene: Scene val view: View val camera: Camera @@ -323,6 +328,13 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener { } } + private fun updateCameraProjection() { + val width = view.viewport.width + val height = view.viewport.height + val aspect = width.toDouble() / height.toDouble() + camera.setLensProjection(cameraFocalLength.toDouble(), aspect, kNearPlane, kFarPlane) + } + inner class SurfaceCallback : UiHelper.RendererCallback { override fun onNativeWindowChanged(surface: Surface) { swapChain?.let { engine.destroySwapChain(it) } @@ -342,9 +354,8 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener { override fun onResized(width: Int, height: Int) { view.viewport = Viewport(0, 0, width, height) - val aspect = width.toDouble() / height.toDouble() - camera.setProjection(kFovDegrees, aspect, kNearPlane, kFarPlane, Camera.Fov.VERTICAL) cameraManipulator.setViewport(width, height) + updateCameraProjection() } } 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 cac95dbe77..4acffacfe4 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 @@ -242,6 +242,7 @@ class MainActivity : Activity() { modelViewer.scene.indirectLight, modelViewer.light, modelViewer.engine.lightManager, modelViewer.scene, modelViewer.renderer) modelViewer.view.colorGrading = automation.getColorGrading((modelViewer.engine)) + modelViewer.cameraFocalLength = automation.viewerOptions.cameraFocalLength } inner class FrameCallback : Choreographer.FrameCallback { diff --git a/filament/include/filament/Camera.h b/filament/include/filament/Camera.h index 1689a19fc1..12b40ee70c 100644 --- a/filament/include/filament/Camera.h +++ b/filament/include/filament/Camera.h @@ -439,7 +439,7 @@ public: /** * Sets the camera focus distance. This is used by the Depth-of-field PostProcessing effect. - * @param distance Distnace from the camera to the plane of focus in world units. + * @param distance Distance from the camera to the plane of focus in world units. * Must be positive and larger than the near clipping plane. */ void setFocusDistance(float distance) noexcept; diff --git a/libs/viewer/include/viewer/AutomationEngine.h b/libs/viewer/include/viewer/AutomationEngine.h index 1237893cf2..ae6293c3df 100644 --- a/libs/viewer/include/viewer/AutomationEngine.h +++ b/libs/viewer/include/viewer/AutomationEngine.h @@ -169,6 +169,13 @@ public: */ ColorGrading* getColorGrading(Engine* engine); + /** + * Gets the current viewer options. + * + * NOTE: Focal length here might be different from the user-specified value, due to DoF options. + */ + ViewerOptions getViewerOptions() const; + /** * Signals that batch mode can begin. Call this after all meshes and textures finish loading. */ diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index 2e7c3882e1..73bd32e476 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -173,7 +173,7 @@ struct ViewerOptions { bool skyboxEnabled = true; sRGBColor backgroundColor = { 0.0f }; float cameraFocalLength = 28.0f; - float cameraFocusDistance = { 0.0f }; + float cameraFocusDistance = 10.0f; }; struct Settings { diff --git a/libs/viewer/src/AutomationEngine.cpp b/libs/viewer/src/AutomationEngine.cpp index da117a2e51..fa5cec0556 100644 --- a/libs/viewer/src/AutomationEngine.cpp +++ b/libs/viewer/src/AutomationEngine.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -167,6 +168,18 @@ ColorGrading* AutomationEngine::getColorGrading(Engine* engine) { return mColorGrading; } +ViewerOptions AutomationEngine::getViewerOptions() const { + ViewerOptions options = mSettings->viewer; + const auto dofOptions = mSettings->view.dof; + if (dofOptions.enabled) { + options.cameraFocalLength = Camera::computeEffectiveFocalLength( + options.cameraFocalLength / 1000.0, + std::max(0.1f, options.cameraFocusDistance)) * 1000.0; + + } + return mSettings->viewer; +} + void AutomationEngine::tick(View* view, MaterialInstance* const* materials, size_t materialCount, Renderer* renderer, float deltaTime) { const auto activateTest = [this, view, materials, materialCount]() { diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp index b82091f19c..312a74fd19 100644 --- a/libs/viewer/src/SimpleViewer.cpp +++ b/libs/viewer/src/SimpleViewer.cpp @@ -723,11 +723,8 @@ void SimpleViewer::updateUserInterface() { 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); - } + ImGui::SliderFloat("Focal length (mm)", &mSettings.viewer.cameraFocalLength, + 16.0f, 90.0f); bool dofMedian = mSettings.view.dof.filter == View::DepthOfFieldOptions::Filter::MEDIAN; int dofRingCount = mSettings.view.dof.fastGatherRingCount; @@ -739,7 +736,7 @@ void SimpleViewer::updateUserInterface() { 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("Focus distance", &mSettings.viewer.cameraFocusDistance, 0.0f, 30.0f); ImGui::SliderFloat("Blur scale", &mSettings.view.dof.cocScale, 0.1f, 10.0f); ImGui::SliderInt("Ring count", &dofRingCount, 1, 17); ImGui::SliderInt("Max CoC", &dofMaxCoC, 1, 32); @@ -763,6 +760,8 @@ void SimpleViewer::updateUserInterface() { ImGui::ColorEdit3("Color##vignetteColor", &mSettings.view.vignette.color.r); } + // We do not yet support camera selection in the remote UI. To support this feature, we + // would need to send a message from DebugServer to the WebSockets client. if (mAsset != nullptr) { const utils::Entity* cameras = mAsset->getCameraEntities(); @@ -826,6 +825,8 @@ void SimpleViewer::updateUserInterface() { ImGui::Unindent(); } + // We do not yet support animation selection in the remote UI. To support this feature, we + // would need to send a message from DebugServer to the WebSockets client. if (mAnimator && mAnimator->getAnimationCount() > 0 && ImGui::CollapsingHeader("Animation")) { ImGui::Indent(); diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 207751ffb0..265cf38d8f 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -633,18 +633,14 @@ 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; + const auto viewerOptions = app.automationEngine->getViewerOptions(); const auto& dofOptions = app.viewer->getSettings().view.dof; rcm.setLayerMask(instance, 0xff, viewerOptions.groundPlaneEnabled ? 0xff : 0x00); - float fe = viewerOptions.cameraFocalLength; - if (dofOptions.enabled) { - fe = Camera::computeEffectiveFocalLength(fe / 1000.0, - std::max(0.1f, dofOptions.focusDistance)) * 1000.0; - } - - FilamentApp::get().getCameraFocalLength() = fe; + // Note that this focal length might be different from the slider value because the + // automation engine applies Camera::computeEffectiveFocalLength when DoF is enabled. + FilamentApp::get().getCameraFocalLength() = viewerOptions.cameraFocalLength; const size_t cameraCount = app.asset->getCameraEntityCount(); view->setCamera(app.mainCamera);