I do not know how to visualize some simple glb-model. I see that my scene is rendered, and that the model is loaded from the server, but the scene is grey. The question is how to zoom to some arbitrary glb-model? The demo of my problem can be seen here. The source code is simple and looks like so:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>3D Model Viewer</title>
<style>
* {margin:0;padding:0;}
body {overflow: hidden;}
.container {width: 100%; height: 100vh;}
</style>
</head>
<body>
<div class="container"></div>
<script type="importmap">
{
"imports": {
"three": "./node_modules/three/build/three.module.js",
"DRACOLoader": "./node_modules/three/examples/jsm/loaders/DRACOLoader.js",
"GLTFLoader": "./node_modules/three/examples/jsm/loaders/GLTFLoader.js",
"OBJLoader": "./node_modules/three/examples/jsm/loaders/OBJLoader.js",
"RectAreaLightHelper": "./node_modules/three/examples/jsm/helpers/RectAreaLightHelper.js",
"RectAreaLightUniformsLib": "./node_modules/three/examples/jsm/lights/RectAreaLightUniformsLib.js",
"OrbitControls": "./node_modules/three/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { DRACOLoader } from 'DRACOLoader';
import { GLTFLoader } from 'GLTFLoader';
import { OBJLoader } from 'OBJLoader';
import { OrbitControls } from 'OrbitControls';
import { RectAreaLightHelper } from 'RectAreaLightHelper'
import { RectAreaLightUniformsLib } from 'RectAreaLightUniformsLib';
function init() {
let container = document.querySelector('.container');
//Scene
const scene = new THREE.Scene()
scene.background = new THREE.Color("#E2DFE1");
//Camera
const camera = new THREE.PerspectiveCamera(100 / Math.PI, 2000 / 2000, 0.01, 1000);
camera.position.set(1, 1, 1)
//render
const renderer = new THREE.WebGLRenderer({antialias: true})
renderer.setSize(window.innerWidth, window.innerHeight)
container.appendChild(renderer.domElement)
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const guid = urlParams.get("guid");
const year = urlParams.get("year");
const month = urlParams.get("month");
const extension = urlParams.get("extension");
// GLB Model
if (extension == 'glb' || extension == 'gltf') {
const loader = new GLTFLoader();
loader.load(`/files/digitaltwineditor/files/${year}/${month}/${guid}.${extension}`, gltf => {
let model = gltf.scene;
scene.add(model);
},
function (error) {
console.log('Error: ' + error)
}
)
}
renderer.render(scene, camera)
}
init()
</script>
</body>
</html>
I’m totally new to threejs, but I made some investigations and found no simple answer. I see different manipulations with PerspectiveCamera
here and there, but do not know where all this numbers like in this call come from
new THREE.PerspectiveCamera(100 / Math.PI, 2000 / 2000, 0.01, 1000)
What I want to achieve is totally simple - just to load some arbitrary GLB model and zoom to this model. That is it. Many thanks in advance!
there’s a few issues with the setup, the model you’re currently trying to load is 80MB, this is a truly excessive file size for a model to be loaded over the web, you’d likely want to look into tools to reduce this file size starting with blender to see if you can manually reduce the model and then go on to more advanced tools such as glTF-Transform.
A simple draco compression from blender will bring the file size down to around 16.2MB but you’d want to look into manually reducing the mesh beforehand, some of the banisters and other parts in the model are quite heavily populated with excessive amounts of geometry…
in terms of not being able to see the model rendered (after it has eventually loaded) see the following reference image, your camera is at the position 1,1,1 (shown by the red arrow) and the model is outside of the camera’s view currently… you can move the camera to the correct location to bring the model into view or reposition the model so it’s centered at 0,0,0 and move the camera back far enough to see the model…
@Lawrence3DPK Thank you, Lawrence! I will check it up and return with the result! As for the size of glb, I actually have Draco compressed model of the very same object, but for a start I decided to use the initial glb - just for the sake of simplicity
And as for PerspectiveCamera
, how should I configure it for some arbitrary glb-model of any size?
there are a few threads online on how to achieve this, you effectively want the camera to “contain” the entire view of the model…
function zoomExtents(obj) {
let vFoV = camera.getEffectiveFOV();
let hFoV = camera.fov * camera.aspect;
let FoV = Math.min(vFoV, hFoV);
let FoV2 = FoV / 2;
let dir = new THREE.Vector3();
camera.getWorldDirection(dir);
let bb = obj.geometry.boundingBox;
let bs = obj.geometry.boundingSphere;
let bsWorld = bs.center.clone();
obj.localToWorld(bsWorld);
let th = FoV2 * Math.PI / 180.0;
let sina = Math.sin(th);
let R = bs.radius;
let FL = R / sina;
let cameraDir = new THREE.Vector3();
camera.getWorldDirection(cameraDir);
let cameraOffs = cameraDir.clone();
cameraOffs.multiplyScalar(-FL);
let newCameraPos = bsWorld.clone().add(cameraOffs);
camera.position.copy(newCameraPos);
camera.lookAt(bsWorld);
orbit.target.copy(bsWorld);
orbit.update();
}
This is a slightly modified function to zoom to a given model (obj) input from this SO answer
Should I call this function right after scene.add(model)
? Like so:
const loader = new GLTFLoader();
loader.load(`/files/digitaltwineditor/files/${year}/${month}/${guid}.${extension}`, gltf => {
let model = gltf.scene;
scene.add(model);
zoomExtents(model);
},
function (error) {
console.log('Error: ' + error)
}
)
yes, correct, as you havn’t yet setup your orbit controls you can comment out the last two lines here…
//orbit.target.copy(bsWorld);
//orbit.update();
I tried it. The current demo is updated. Now I get this error message:
TypeError: obj.geometry is undefined
right yeah my mistake, your model has multiple parts with nested objects and geometries, here’s a modified version that should work…
function zoomExtents(obj) {
let vFoV = camera.getEffectiveFOV();
let hFoV = camera.fov * camera.aspect;
let FoV = Math.min(vFoV, hFoV);
let FoV2 = FoV / 2;
let dir = new THREE.Vector3();
camera.getWorldDirection(dir);
let bb = new THREE.Box3().setFromObject(obj);
const center = new THREE.Vector3();
bb.getCenter(center);
let bs = bb.getBoundingSphere(new THREE.Sphere(center));
//let bb = obj.geometry.boundingBox;
//let bs = obj.geometry.boundingSphere;
let bsWorld = bs.center.clone();
obj.localToWorld(bsWorld);
let th = FoV2 * Math.PI / 180.0;
let sina = Math.sin(th);
let R = bs.radius;
let FL = R / sina;
let cameraDir = new THREE.Vector3();
camera.getWorldDirection(cameraDir);
let cameraOffs = cameraDir.clone();
cameraOffs.multiplyScalar(-FL);
let newCameraPos = bsWorld.clone().add(cameraOffs);
camera.position.copy(newCameraPos);
camera.lookAt(bsWorld);
//orbit.target.copy(bsWorld);
//orbit.update();
}
this is psudo code on my part so would need testing in your environment, setting up a codepen or sanbox would help a lot if you continue to run in to issues…
1 Like
I tried it. No errors now, but still do not see any model. The current source code looks like so:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>3D Model Viewer</title>
<style>
* {margin:0;padding:0;}
body {overflow: hidden;}
.container {width: 100%; height: 100vh;}
</style>
</head>
<body>
<div class="container"></div>
<script type="importmap">
{
"imports": {
"three": "./node_modules/three/build/three.module.js",
"DRACOLoader": "./node_modules/three/examples/jsm/loaders/DRACOLoader.js",
"GLTFLoader": "./node_modules/three/examples/jsm/loaders/GLTFLoader.js",
"OBJLoader": "./node_modules/three/examples/jsm/loaders/OBJLoader.js",
"RectAreaLightHelper": "./node_modules/three/examples/jsm/helpers/RectAreaLightHelper.js",
"RectAreaLightUniformsLib": "./node_modules/three/examples/jsm/lights/RectAreaLightUniformsLib.js",
"OrbitControls": "./node_modules/three/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { DRACOLoader } from 'DRACOLoader';
import { GLTFLoader } from 'GLTFLoader';
import { OBJLoader } from 'OBJLoader';
import { OrbitControls } from 'OrbitControls';
import { RectAreaLightHelper } from 'RectAreaLightHelper'
import { RectAreaLightUniformsLib } from 'RectAreaLightUniformsLib';
function init() {
let container = document.querySelector('.container');
//Scene
const scene = new THREE.Scene()
scene.background = new THREE.Color("#E2DFE1");
//Camera
const camera = new THREE.PerspectiveCamera(100 / Math.PI, 2000 / 2000, 0.01, 1000);
camera.position.set(0, 0, 0)
//render
const renderer = new THREE.WebGLRenderer({antialias: true})
renderer.setSize(window.innerWidth, window.innerHeight)
container.appendChild(renderer.domElement)
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const guid = urlParams.get("guid");
const year = urlParams.get("year");
const month = urlParams.get("month");
const extension = urlParams.get("extension");
// GLB Model
if (extension == 'glb' || extension == 'gltf') {
const loader = new GLTFLoader();
loader.load(`/files/digitaltwineditor/files/${year}/${month}/${guid}.${extension}`, gltf => {
let model = gltf.scene;
scene.add(model);
zoomExtents(model);
},
function (error) {
console.log('Error: ' + error)
}
)
}
function zoomExtents(obj) {
let vFoV = camera.getEffectiveFOV();
let hFoV = camera.fov * camera.aspect;
let FoV = Math.min(vFoV, hFoV);
let FoV2 = FoV / 2;
let dir = new THREE.Vector3();
camera.getWorldDirection(dir);
let bb = new THREE.Box3().setFromObject(obj);
const center = new THREE.Vector3();
bb.getCenter(center);
let bs = bb.getBoundingSphere(new THREE.Sphere(center));
//let bb = obj.geometry.boundingBox;
//let bs = obj.geometry.boundingSphere;
let bsWorld = bs.center.clone();
obj.localToWorld(bsWorld);
let th = FoV2 * Math.PI / 180.0;
let sina = Math.sin(th);
let R = bs.radius;
let FL = R / sina;
let cameraDir = new THREE.Vector3();
camera.getWorldDirection(cameraDir);
let cameraOffs = cameraDir.clone();
cameraOffs.multiplyScalar(-FL);
let newCameraPos = bsWorld.clone().add(cameraOffs);
camera.position.copy(newCameraPos);
camera.lookAt(bsWorld);
//orbit.target.copy(bsWorld);
//orbit.update();
}
renderer.render(scene, camera)
}
init()
</script>
</body>
</html>
It looks very painful for such a simple task 
@Lawrence3DPK Lawrence, you provided a screen with a model, which is visible. Can you please share your code? Probably, I miss something in mine.
that’s just a screenshot from blender, it’s best if you provide a live editable version of your setup on codepen or codesandbox…
have a look at the following, it seems to be working…
There were a few things to fix, there was no light in the scene & renderer.render was being called only once before the model was even loaded, hope this helps.
It works! Thank you, Lawrence!
1 Like