I have implemented a simple 3D model viewer with three.js. The project contains a database of 3D models, divided into different categories. In one category, the models are tall (with a relatively small width, the height is much higher than the height of the other models), in another category, the models are small (relative to all products, their height and width are smaller than in others, these are small models), in another category, the models are large (their height and wider than many models from other categories.
The viewer has a fixed canvas width and height. For this reason, when loading a model in canvas, many models are immediately loaded at a small scale, which requires subsequent multiple zooming. There are also models, the upper part of which does not fit into the canvas, and the lower part does, at boot time. This also requires subsequent scaling.
It is necessary that the viewer first estimates the dimensions of the model, after that it automatically selects the scale for the model individually, after that it centers the model vertically and horizontally.
It is also necessary that the plane in which the model lies (the model has tangible width and height, the thickness is very small, all these models are close to flat) coincide with the plane of the screen. At boot time, many models have an offset for these planes. How do I implement this so that the viewer automatically expands the model?
Below I am attaching screenshots for models from different categories - where they are clearly recorded:
Below is the code of the viewer that is responsible for initializing the scene:
var objectUrl = $('#modelViewerModal').data('object-url');//'/storage/3d/qqq.obj';
var mesh, renderer, scene, camera, controls;
init();
animate();
function init() {
const screenshotPageWidth = $(document).width();
const screenshotPageHeight = $(document).height();
let modalBody = document.querySelector('#scene-container');
// renderer
renderer = new THREE.WebGLRenderer({modalBody});
var height = screenshotPageHeight / 2;
var width = screenshotPageWidth / 2;
if (screenshotPageHeight < screenshotPageWidth) { // landscape orientation
if (width > 3 * height / 2) {
width = 3 * height / 2;
} else if (width < 3 * height / 2) {
height = 2 * width / 3;
}
} else if (screenshotPageHeight > screenshotPageWidth) { // portrait orientation
if (height > 2 * width / 3) {
height = 2 * width / 3;
} else if (height < 2 * width / 3) {
width = 3 * height / 2;
}
}
// let limitHeight = screen.height - 137;
renderer.setSize(width, height);
modalBody.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x000000 );
// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 20, 20, 20 );
// controls
controls = new OrbitControls( camera, renderer.domElement );
// ambient
scene.add( new THREE.AmbientLight( 0x222222 ) );
// light
var light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 2000, 2000, 2000 );
scene.add( light );
var spotLight_01 = getSpotlight('rgb(145, 200, 255)', 1);
spotLight_01.name = 'spotLight_01';
var spotLight_02 = getSpotlight('rgb(255, 220, 180)', 1);
spotLight_02.name = 'spotLight_02';
scene.add(spotLight_01);
scene.add(spotLight_02);
// geometry
var geometry = new THREE.SphereGeometry( 5, 12, 8 );
// material
const material = new THREE.MeshPhongMaterial({
color: 0xeae8dc,
side: THREE.DoubleSide,
transparent: false,
flatShading: false,
opacity: 0
});
// mesh
var objLoader = new THREE.OBJLoader();
objLoader.load(objectUrl,
function ( obj ) {
mesh = obj;
mesh.scale.setScalar( 0.01 );
obj.traverse( function( child ) {
if ( child.isMesh ) child.material = material;
} );
// center the object
var aabb = new THREE.Box3().setFromObject( mesh );
var center = aabb.getCenter( new THREE.Vector3() );
mesh.position.x += (mesh.position.x - center.x);
mesh.position.y += (mesh.position.y - center.y);
mesh.position.z += (mesh.position.z - center.z);
scene.add( mesh );
animate();
} );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
renderer.render( scene, camera );
}
function getSpotlight(color, intensity) {
var light = new THREE.SpotLight(color, intensity);
light.castShadow = true;
light.shadow.mapSize.x = 4096;
light.shadow.mapSize.y = 4096;
return light;
}