UNPKG

model2image

Version:

Use model2image to display thumbnails of 3D models on a web page without requiring plugins or external programs.

162 lines (136 loc) 5.31 kB
import * as THREE from "three"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; import { MeshoptDecoder } from "three/examples/jsm/libs/meshopt_decoder.module.js"; import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js"; export function model2image(modelUrl) { return new Promise((resolve, reject) => { try { let camera, scene, renderer; const { innerWidth, innerHeight, devicePixelRatio } = window; init(); render(); function init() { const container = document.createElement("div"); document.body.appendChild(container); renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true, antialias: devicePixelRatio < 2, failIfMajorPerformanceCaveat: true, }); renderer.setPixelRatio(devicePixelRatio); renderer.setSize(innerWidth, innerHeight); renderer.toneMapping = THREE.ACESFilmicToneMapping; container.appendChild(renderer.domElement); camera = new THREE.PerspectiveCamera( 45, innerWidth / innerHeight, 1, 20000 ); camera.position.set(-200, 100, 400); const environment = new RoomEnvironment(); const pmremGenerator = new THREE.PMREMGenerator(renderer); scene = new THREE.Scene(); const axesHelper = new THREE.AxesHelper(100); scene.add(axesHelper); scene.background = new THREE.Color(0xe8e8e8); scene.environment = pmremGenerator.fromScene(environment).texture; const grid = new THREE.GridHelper(500, 10, 0xffffff, 0xffffff); grid.material.opacity = 0.09; grid.material.depthWrite = false; grid.material.transparent = true; scene.add(grid); const ktx2Loader = new KTX2Loader() .setTranscoderPath("js/libs/basis/") .detectSupport(renderer); const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderConfig({ type: "js" }); dracoLoader.setDecoderPath( "https://www.gstatic.com/draco/versioned/decoders/1.5.6/" ); const loader = new GLTFLoader(); loader.setDRACOLoader(dracoLoader); loader.setKTX2Loader(ktx2Loader); loader.setMeshoptDecoder(MeshoptDecoder); loader.load( modelUrl, (gltf) => { const mesh = gltf.scene; const boundingBox = new THREE.Box3(); boundingBox.setFromObject(mesh); const vector = new THREE.Vector3(); const height = boundingBox.getSize(vector).y; const width = boundingBox.getSize(vector).x / 2; const length = boundingBox.getSize(vector).z / 2; let maxDimension; if (height > width && height > length) { maxDimension = height; } else if (width > length) { maxDimension = width; } else { maxDimension = length; } const requiredMaxDimension = 200; const requiredScale = requiredMaxDimension / maxDimension; mesh.scale.setScalar(requiredScale); const boundingBox2 = new THREE.Box3(); boundingBox2.setFromObject(mesh); const centeredObject = moveObjectToCenter(mesh, true); centeredObject.position.y = 8; scene.add(centeredObject); render(); const href = renderer.domElement .toDataURL("image/png") .replace("image/png", "image/octet-stream"); resolve(href); }, () => {}, (error) => { reject(error); throw new Error(error); } ); const controls = new OrbitControls(camera, renderer.domElement); controls.addEventListener("change", render); controls.minDistance = 400; controls.maxDistance = 1000; controls.target.set(10, 90, -16); controls.update(); window.addEventListener("resize", onWindowResize); } function moveObjectToCenter(inObject, YBottomBool = false) { const parent = new THREE.Object3D(); parent.add(inObject); const box = new THREE.Box3(); box.setFromObject(inObject); const center = new THREE.Vector3(); box.getCenter(center); center.negate(); inObject.position.copy(center); if (YBottomBool) { const box2 = new THREE.Box3(); box2.setFromObject(parent); const minY = box2.min.y; const translateY = -minY; inObject.translateY(translateY); } return parent; } function onWindowResize() { camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth, innerHeight); render(); } function render() { renderer.render(scene, camera); } } catch (error) { reject(error); throw new Error(error); } }); }