66#include " core/event_bridge/localization_manager.hpp"
77#include " core/logger.hpp"
88#include " core/path_utils.hpp"
9+ #include " core/splat_data_transform.hpp"
910#include " gui/bounds_gizmo.hpp"
1011#include " gui/gui_focus_state.hpp"
1112#include " gui/gui_manager.hpp"
@@ -49,11 +50,22 @@ namespace lfs::vis {
4950 constexpr double kCameraFrustumClickThreshold = 5.0 ;
5051 namespace string_keys = lichtfeld::Strings;
5152
52- // Expand [world_min, world_max] by a node's local AABB transformed to world space.
53+ // Expand [world_min, world_max] by a node's local AABB transformed to world
54+ // space. With use_percentile, the splat/point-cloud box is the trimmed
55+ // (1st/99th percentile) AABB so far-flung floaters don't inflate it; meshes
56+ // and other geometry fall back to the full node bounds.
5357 void expandNodeWorldBounds (const core::Scene& scene, const core::SceneNode& node,
54- glm::vec3& world_min, glm::vec3& world_max) {
58+ glm::vec3& world_min, glm::vec3& world_max,
59+ bool use_percentile = false ) {
5560 glm::vec3 local_min, local_max;
56- if (!scene.getNodeBounds (node.id , local_min, local_max))
61+ bool have_local = false ;
62+ if (use_percentile) {
63+ if (node.model && node.model ->size () > 0 )
64+ have_local = core::compute_bounds (*node.model , local_min, local_max, 0 .0f , true );
65+ else if (node.point_cloud && node.point_cloud ->size () > 0 )
66+ have_local = core::compute_bounds (*node.point_cloud , local_min, local_max, 0 .0f , true );
67+ }
68+ if (!have_local && !scene.getNodeBounds (node.id , local_min, local_max))
5769 return ;
5870
5971 const glm::mat4 world_xform = scene_coords::nodeVisualizerWorldTransform (scene, node.id );
@@ -343,12 +355,14 @@ namespace lfs::vis {
343355 clearViewportDragState ();
344356 clearWasdMomentumViewport ();
345357 scene_extent_ = 0 .0f ;
358+ depth_range_initialized_ = false ;
346359 focusSplitPanel (SplitViewPanelId::Left);
347360 });
348361 scene_loaded_handler_id_ = state::SceneLoaded::when ([this ](const auto &) {
349362 clearViewportDragState ();
350363 clearWasdMomentumViewport ();
351364 scene_extent_ = 0 .0f ;
365+ depth_range_initialized_ = false ;
352366 focusSplitPanel (SplitViewPanelId::Left);
353367 });
354368
@@ -1816,6 +1830,8 @@ namespace lfs::vis {
18161830 }
18171831
18181832 void InputController::update (float delta_time) {
1833+ maybeInitializeDepthViewRange ();
1834+
18191835 if (input_router_) {
18201836 const bool any_mouse_buttons_pressed = SDL_GetMouseState (nullptr , nullptr ) != 0 ;
18211837 input_router_->syncPressedMouseButtons (any_mouse_buttons_pressed);
@@ -2350,7 +2366,9 @@ namespace lfs::vis {
23502366 }
23512367
23522368 // Whole-scene world AABB, skipping container nodes that carry no geometry.
2353- bool InputController::computeWholeSceneBounds (glm::vec3& out_min, glm::vec3& out_max) const {
2369+ // use_percentile yields the trimmed (outlier-excluding) box.
2370+ bool InputController::computeWholeSceneBounds (glm::vec3& out_min, glm::vec3& out_max,
2371+ const bool use_percentile) const {
23542372 if (!tool_context_)
23552373 return false ;
23562374 auto * const sm = tool_context_->getSceneManager ();
@@ -2366,27 +2384,54 @@ namespace lfs::vis {
23662384 node->type == core::NodeType::CAMERA_GROUP ||
23672385 node->type == core::NodeType::IMAGE_GROUP )
23682386 continue ;
2369- expandNodeWorldBounds (scene, *node, out_min, out_max);
2387+ expandNodeWorldBounds (scene, *node, out_min, out_max, use_percentile );
23702388 }
23712389
23722390 return out_min.x <= out_max.x ;
23732391 }
23742392
23752393 // Cached whole-scene radius used to scale WASD speed and pan distance with
2376- // splat size. Lazily computed after load (bounds may not exist the instant
2377- // SceneLoaded fires) and invalidated on scene load/clear. Returns 0 while no
2378- // geometry is present.
2394+ // splat size. Uses the trimmed (1st/99th percentile) bounds so a few far-flung
2395+ // floaters can't blow up the extent and make navigation far too fast. Lazily
2396+ // computed after load (bounds may not exist the instant SceneLoaded fires) and
2397+ // invalidated on scene load/clear. Returns 0 while no geometry is present.
23792398 float InputController::sceneExtent () {
23802399 if (scene_extent_ > 0 .0f )
23812400 return scene_extent_;
23822401
23832402 glm::vec3 min, max;
2384- if (computeWholeSceneBounds (min, max))
2403+ if (computeWholeSceneBounds (min, max, /* use_percentile= */ true ))
23852404 scene_extent_ = glm::length (max - min) * 0 .5f ;
23862405
23872406 return scene_extent_;
23882407 }
23892408
2409+ // Seed the depth-map range from the trimmed scene scale once the extent is
2410+ // known after a load, so the palette spans the actual content instead of the
2411+ // fixed 0.1..100 default that is useless on large RAD scenes. Runs once per
2412+ // scene; the user owns the range after that.
2413+ void InputController::maybeInitializeDepthViewRange () {
2414+ if (depth_range_initialized_)
2415+ return ;
2416+
2417+ const float radius = sceneExtent ();
2418+ if (radius <= 0 .0f )
2419+ return ;
2420+
2421+ auto * const rendering = services ().renderingOrNull ();
2422+ if (!rendering)
2423+ return ;
2424+
2425+ constexpr float kDepthViewInitNear = 1 .0f ;
2426+ auto settings = rendering->getSettings ();
2427+ settings.depth_view_min = kDepthViewInitNear ;
2428+ settings.depth_view_max = radius;
2429+ sanitizeDepthViewSettings (settings);
2430+ rendering->updateSettings (settings);
2431+
2432+ depth_range_initialized_ = true ;
2433+ }
2434+
23902435 bool InputController::focusSelection () {
23912436 return handleFocusSelection (activeKeyboardViewport ());
23922437 }
0 commit comments