Add 'fit to scene' feature to examples/viewer#425
Conversation
- Press 'F' key to reset camera to view entire model - Initial camera position set to fit the scene on load - Enable centering translation to center objects at origin - Store bounding box data in globals for FitToScene() access - Update help text to document 'F' key Agent-Logs-Url: https://github.com/tinyobjloader/tinyobjloader/sessions/3b05e7bf-a2b5-4b2d-b308-718537dfc75a Co-authored-by: syoyo <18676+syoyo@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a “fit to scene” camera reset to the OpenGL OBJ viewer example, and fixes initial framing by centering/scaling the loaded model consistently.
Changes:
- Introduces
FitToScene()and binds it to theFkey; also calls it after loading to set a sensible initial view. - Persists scene bounds (
scene_bmin/scene_bmax) andscene_maxExtentat file scope for use during rendering and fit/reset. - Enables the previously-disabled centering translation and uses the persisted bounds/extent in the render loop.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Reset camera to fit the entire scene in view. | ||
| static void FitToScene() { | ||
| trackball(curr_quat, 0, 0, 0, 0); | ||
|
|
||
| g_angleX = 0.0f; | ||
| g_angleY = 0.0f; | ||
|
|
||
| eye[0] = 0.0f; | ||
| eye[1] = 0.0f; | ||
| eye[2] = 3.0f; | ||
|
|
||
| lookat[0] = 0.0f; | ||
| lookat[1] = 0.0f; | ||
| lookat[2] = 0.0f; | ||
|
|
||
| up[0] = 0.0f; | ||
| up[1] = 1.0f; | ||
| up[2] = 0.0f; | ||
| } |
There was a problem hiding this comment.
FitToScene() duplicates the camera reset logic already in Init() (same quaternion + eye/lookat/up defaults) with only small differences. To avoid the two getting out of sync over time, consider having Init() call FitToScene() (or vice versa) and keep the reset logic in one place.
| scene_bmin[0] = bmin[0]; scene_bmin[1] = bmin[1]; scene_bmin[2] = bmin[2]; | ||
| scene_bmax[0] = bmax[0]; scene_bmax[1] = bmax[1]; scene_bmax[2] = bmax[2]; |
There was a problem hiding this comment.
The chained assignments for scene_bmin/scene_bmax on single lines reduce readability and make diffs harder to review. Consider formatting these as one assignment per line (or using std::copy) for consistency with the surrounding style.
| scene_bmin[0] = bmin[0]; scene_bmin[1] = bmin[1]; scene_bmin[2] = bmin[2]; | |
| scene_bmax[0] = bmax[0]; scene_bmax[1] = bmax[1]; scene_bmax[2] = bmax[2]; | |
| scene_bmin[0] = bmin[0]; | |
| scene_bmin[1] = bmin[1]; | |
| scene_bmin[2] = bmin[2]; | |
| scene_bmax[0] = bmax[0]; | |
| scene_bmax[1] = bmax[1]; | |
| scene_bmax[2] = bmax[2]; |
| glScalef(1.0f / scene_maxExtent, 1.0f / scene_maxExtent, | ||
| 1.0f / scene_maxExtent); |
There was a problem hiding this comment.
scene_maxExtent can become 0 (e.g., degenerate geometry where bmin==bmax on all axes), which would make glScalef(1.0f / scene_maxExtent, ...) divide by zero and propagate INF/NaN through the modelview matrix. Consider clamping scene_maxExtent to a small positive epsilon (or falling back to 1.0f) after computing it, and/or early-out when bounds are invalid.
| glScalef(1.0f / scene_maxExtent, 1.0f / scene_maxExtent, | |
| 1.0f / scene_maxExtent); | |
| float scale = 1.0f; | |
| if (scene_maxExtent > 0.0f) { | |
| scale = 1.0f / scene_maxExtent; | |
| } | |
| glScalef(scale, scale, scale); |
Press
Fto reset camera to view the entire mesh. Initial camera is now properly positioned on load.#if 0'd out. Without this, off-origin models render outside the viewport.FitToScene()function: Resets trackball quaternion, turntable angles, and camera to default pose(0, 0, 3)looking at origin. Works because the render loop already normalizes geometry to ±1 viaglScalef(1/maxExtent).bmin/bmax/maxExtentto file scope sokeyboardFunccan triggerFitToScene()and the render loop can reference them after the centering fix.Fkey binding inkeyboardFunc, called once afterLoadObjAndConvertfor initial fit.🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.