From b841709729d900897d1878140ef0393f263d70bf Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Mon, 28 Jun 2021 12:30:02 -0700 Subject: [PATCH] gltf_viewer: make auto-scale into a UI setting. --- .../src/main/cpp/AutomationEngine.cpp | 2 ++ .../filament/utils/AutomationEngine.java | 1 + .../android/filament/utils/ModelViewer.kt | 10 +++++++ .../android/filament/gltf/MainActivity.kt | 19 +++++++++---- libs/viewer/include/viewer/Settings.h | 1 + libs/viewer/include/viewer/SimpleViewer.h | 12 ++++---- libs/viewer/src/Settings.cpp | 7 +++-- libs/viewer/src/SimpleViewer.cpp | 28 +++++++++++++------ samples/gltf_instances.cpp | 3 +- samples/gltf_viewer.cpp | 6 +++- 10 files changed, 66 insertions(+), 23 deletions(-) diff --git a/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp b/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp index dee338aec9..7514d15452 100644 --- a/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp +++ b/android/filament-utils-android/src/main/cpp/AutomationEngine.cpp @@ -136,6 +136,7 @@ Java_com_google_android_filament_utils_AutomationEngine_nGetViewerOptions(JNIEnv const jfieldID skyboxEnabled = env->GetFieldID(klass, "skyboxEnabled", "Z"); const jfieldID cameraFocalLength = env->GetFieldID(klass, "cameraFocalLength", "F"); const jfieldID cameraFocusDistance = env->GetFieldID(klass, "cameraFocusDistance", "F"); + const jfieldID autoScaleEnabled = env->GetFieldID(klass, "autoScaleEnabled", "Z"); env->SetFloatField(result, cameraAperture, options.cameraAperture); env->SetFloatField(result, cameraSpeed, options.cameraSpeed); @@ -145,6 +146,7 @@ Java_com_google_android_filament_utils_AutomationEngine_nGetViewerOptions(JNIEnv env->SetBooleanField(result, skyboxEnabled, options.skyboxEnabled); env->SetFloatField(result, cameraFocalLength, options.cameraFocalLength); env->SetFloatField(result, cameraFocusDistance, options.cameraFocusDistance); + env->SetBooleanField(result, autoScaleEnabled, options.autoScaleEnabled); } extern "C" JNIEXPORT jlong JNICALL 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 847519e9f2..8908c6049d 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 @@ -88,6 +88,7 @@ public class AutomationEngine { public boolean skyboxEnabled = true; public float cameraFocalLength = 28.0f; public float cameraFocusDistance = 0.0f; + public boolean autoScaleEnabled = true; } /** 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 d2f8960b33..ccd1b917ff 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 @@ -225,6 +225,16 @@ class ModelViewer(val engine: Engine) : android.view.View.OnTouchListener { } } + /** + * Removes the transformation that was set up via transformToUnitCube. + */ + fun clearRootTransform() { + asset?.let { + val tm = engine.transformManager + tm.setTransform(tm.getInstance(it.root), Mat4().toFloatArray()) + } + } + /** * Frees all entities associated with the most recently-loaded model. */ 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 bed5ef9f07..fdd4f54587 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 @@ -112,7 +112,7 @@ class MainActivity : Activity() { } modelViewer.loadModelGltfAsync(buffer) { uri -> readCompressedAsset("models/$uri") } - modelViewer.transformToUnitCube() + updateRootTransform() } private fun createIndirectLight() { @@ -157,7 +157,7 @@ class MainActivity : Activity() { withContext(Dispatchers.Main) { modelViewer.destroyModel() modelViewer.loadModelGlb(message.buffer) - modelViewer.transformToUnitCube() + updateRootTransform() loadStartTime = System.nanoTime() loadStartFence = modelViewer.engine.createFence() } @@ -277,7 +277,7 @@ class MainActivity : Activity() { pathToBufferMapping[path] } } - modelViewer.transformToUnitCube() + updateRootTransform() loadStartTime = System.nanoTime() loadStartFence = modelViewer.engine.createFence() } @@ -319,8 +319,17 @@ class MainActivity : Activity() { automation.applySettings(json, modelViewer.view, null, modelViewer.scene.indirectLight, modelViewer.light, modelViewer.engine.lightManager, modelViewer.scene, modelViewer.renderer) - modelViewer.view.colorGrading = automation.getColorGrading((modelViewer.engine)) + modelViewer.view.colorGrading = automation.getColorGrading(modelViewer.engine) modelViewer.cameraFocalLength = automation.viewerOptions.cameraFocalLength + updateRootTransform() + } + + private fun updateRootTransform() { + if (automation.viewerOptions.autoScaleEnabled) { + modelViewer.transformToUnitCube() + } else { + modelViewer.clearRootTransform() + } } inner class FrameCallback : Choreographer.FrameCallback { @@ -332,7 +341,7 @@ class MainActivity : Activity() { if (it.wait(Fence.Mode.FLUSH, 0) == Fence.FenceStatus.CONDITION_SATISFIED) { val end = System.nanoTime() val total = (end - loadStartTime) / 1_000_000 - Log.i(TAG, "The Filament backend took ${total} ms to load the model geometry.") + Log.i(TAG, "The Filament backend took $total ms to load the model geometry.") modelViewer.engine.destroyFence(it) loadStartFence = null } diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index 73bd32e476..06451610c3 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -174,6 +174,7 @@ struct ViewerOptions { sRGBColor backgroundColor = { 0.0f }; float cameraFocalLength = 28.0f; float cameraFocusDistance = 10.0f; + bool autoScaleEnabled = true; }; struct Settings { diff --git a/libs/viewer/include/viewer/SimpleViewer.h b/libs/viewer/include/viewer/SimpleViewer.h index a8c534f675..15015bcfd3 100644 --- a/libs/viewer/include/viewer/SimpleViewer.h +++ b/libs/viewer/include/viewer/SimpleViewer.h @@ -73,18 +73,15 @@ public: ~SimpleViewer(); /** - * Adds the asset's ready-to-render entities into the scene and optionally transforms the root - * node to make it fit into a unit cube at the origin. + * Adds the asset's ready-to-render entities into the scene. * * The viewer does not claim ownership over the asset or its entities. Clients should use * AssetLoader and ResourceLoader to load an asset before passing it in. * * @param asset The asset to view. - * @param scale Adds a transform to the root to fit the asset into a unit cube at the origin. * @param instanceToAnimate Optional instance from which to get the animator. */ - void populateScene(FilamentAsset* asset, bool scale, - FilamentInstance* instanceToAnimate = nullptr); + void populateScene(FilamentAsset* asset, FilamentInstance* instanceToAnimate = nullptr); /** * Removes the current asset from the viewer. @@ -200,6 +197,11 @@ public: */ void setIBLIntensity(float brightness) { mSettings.lighting.iblIntensity = brightness; } + /** + * Updates the transform at the root node according to the autoScaleEnabled setting. + */ + void updateRootTransform(); + /** * Gets a modifiable reference to stashed state. */ diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index ac4346ea63..ae7b6ecfa0 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -834,7 +834,9 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewerOp i = parse(tokens, i + 1, jsonChunk, &out->cameraFocalLength); } else if (compare(tok, jsonChunk, "cameraFocusDistance") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->cameraFocusDistance); - } else { + } 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); } @@ -1245,7 +1247,8 @@ static std::ostream& operator<<(std::ostream& out, const ViewerOptions& in) { << "\"skyboxEnabled\": " << to_string(in.skyboxEnabled) << ",\n" << "\"backgroundColor\": " << (in.backgroundColor) << ",\n" << "\"cameraFocalLength\": " << (in.cameraFocalLength) << ",\n" - << "\"cameraFocusDistance\": " << (in.cameraFocusDistance) << "\n" + << "\"cameraFocusDistance\": " << (in.cameraFocusDistance) << ",\n" + << "\"autoScaleEnabled\": " << to_string(in.autoScaleEnabled) << "\n" << "}"; } diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp index fa45661324..62996aaf5f 100644 --- a/libs/viewer/src/SimpleViewer.cpp +++ b/libs/viewer/src/SimpleViewer.cpp @@ -334,8 +334,7 @@ SimpleViewer::~SimpleViewer() { delete mImGuiHelper; } -void SimpleViewer::populateScene(FilamentAsset* asset, bool scale, - FilamentInstance* instanceToAnimate) { +void SimpleViewer::populateScene(FilamentAsset* asset, FilamentInstance* instanceToAnimate) { if (mAsset != asset) { removeAsset(); mAsset = asset; @@ -345,13 +344,7 @@ void SimpleViewer::populateScene(FilamentAsset* asset, bool scale, return; } mAnimator = instanceToAnimate ? instanceToAnimate->getAnimator() : asset->getAnimator(); - if (scale) { - auto& tcm = mEngine->getTransformManager(); - auto root = tcm.getInstance(mAsset->getRoot()); - filament::math::mat4f transform = fitIntoUnitCube(mAsset->getBoundingBox(), 4); - tcm.setTransform(root, transform); - } - + updateRootTransform(); mScene->addEntities(asset->getLightEntities(), asset->getLightEntityCount()); } @@ -393,6 +386,19 @@ void SimpleViewer::setIndirectLight(filament::IndirectLight* ibl, } } +void SimpleViewer::updateRootTransform() { + if (mAsset == nullptr) { + return; + } + auto& tcm = mEngine->getTransformManager(); + auto root = tcm.getInstance(mAsset->getRoot()); + filament::math::mat4f transform; + if (mSettings.viewer.autoScaleEnabled) { + transform = fitIntoUnitCube(mAsset->getBoundingBox(), 4); + } + tcm.setTransform(root, transform); +} + void SimpleViewer::updateIndirectLight() { using namespace filament::math; if (mIndirectLight) { @@ -723,6 +729,10 @@ void SimpleViewer::updateUserInterface() { if (ImGui::CollapsingHeader("Scene")) { ImGui::Indent(); + + ImGui::Checkbox("Scale to unit cube", &mSettings.viewer.autoScaleEnabled); + updateRootTransform(); + ImGui::Checkbox("Show skybox", &mSettings.viewer.skyboxEnabled); ImGui::ColorEdit3("Background color", &mSettings.viewer.backgroundColor.r); diff --git a/samples/gltf_instances.cpp b/samples/gltf_instances.cpp index 2ae65847b3..866726b4e2 100644 --- a/samples/gltf_instances.cpp +++ b/samples/gltf_instances.cpp @@ -275,7 +275,8 @@ int main(int argc, char** argv) { if (app.instanceToAnimate > -1) { instance = app.instances[app.instanceToAnimate]; } - app.viewer->populateScene(app.asset, true, instance); + app.viewer->updateRootTransform(); + app.viewer->populateScene(app.asset, instance); app.viewer->applyAnimation(now); // Add a new instance every second until reaching 100 instances. diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 6534e4e088..21b97a5057 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -439,6 +439,7 @@ int main(int argc, char** argv) { app.engine = engine; app.names = new NameComponentManager(EntityManager::get()); app.viewer = new SimpleViewer(engine, scene, view, 410); + app.viewer->getSettings().viewer.autoScaleEnabled = !app.actualSize; const bool batchMode = !app.batchFile.empty(); @@ -612,8 +613,11 @@ int main(int argc, char** argv) { auto animate = [&app](Engine* engine, View* view, double now) { app.resourceLoader->asyncUpdateLoad(); + // Optionally fit the model into a unit cube at the origin. + app.viewer->updateRootTransform(); + // Add renderables to the scene as they become ready. - app.viewer->populateScene(app.asset, !app.actualSize); + app.viewer->populateScene(app.asset); app.viewer->applyAnimation(now); };