mirror of
https://github.com/lighttransport/tinyusdz.git
synced 2026-01-18 01:11:17 +01:00
287 lines
7.8 KiB
JavaScript
287 lines
7.8 KiB
JavaScript
import * as THREE from 'three';
|
|
import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js';
|
|
|
|
import { GUI } from 'https://cdn.jsdelivr.net/npm/dat.gui@0.7.9/build/dat.gui.module.js';
|
|
|
|
import { TinyUSDZLoader } from 'tinyusdz/TinyUSDZLoader.js'
|
|
import { TinyUSDZLoaderUtils } from 'tinyusdz/TinyUSDZLoaderUtils.js'
|
|
|
|
function checkMemory64Support() {
|
|
try {
|
|
// Try creating a 64-bit memory
|
|
const memory = new WebAssembly.Memory({
|
|
initial: 1,
|
|
maximum: 65536,
|
|
index: 'i64' // This specifies 64-bit indexing
|
|
});
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// Loading bar elements
|
|
const loadingContainer = document.createElement('div');
|
|
loadingContainer.id = 'loading-container';
|
|
loadingContainer.style.cssText = `
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 1000;
|
|
font-family: Arial, sans-serif;
|
|
color: white;
|
|
`;
|
|
|
|
const loadingText = document.createElement('div');
|
|
loadingText.id = 'loading-text';
|
|
loadingText.textContent = 'Loading...';
|
|
loadingText.style.cssText = `
|
|
font-size: 24px;
|
|
margin-bottom: 20px;
|
|
`;
|
|
|
|
const progressBarContainer = document.createElement('div');
|
|
progressBarContainer.style.cssText = `
|
|
width: 300px;
|
|
height: 20px;
|
|
background: #333;
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
margin-bottom: 10px;
|
|
`;
|
|
|
|
const progressBar = document.createElement('div');
|
|
progressBar.id = 'progress-bar';
|
|
progressBar.style.cssText = `
|
|
width: 0%;
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #4CAF50, #8BC34A);
|
|
border-radius: 10px;
|
|
transition: width 0.3s ease;
|
|
`;
|
|
|
|
const progressText = document.createElement('div');
|
|
progressText.id = 'progress-text';
|
|
progressText.textContent = '0%';
|
|
progressText.style.cssText = `
|
|
font-size: 14px;
|
|
color: #ccc;
|
|
`;
|
|
|
|
progressBarContainer.appendChild(progressBar);
|
|
loadingContainer.appendChild(loadingText);
|
|
loadingContainer.appendChild(progressBarContainer);
|
|
loadingContainer.appendChild(progressText);
|
|
document.body.appendChild(loadingContainer);
|
|
|
|
// Function to update loading progress
|
|
function updateLoadingProgress(progress, message = 'Loading...') {
|
|
loadingText.textContent = message;
|
|
progressBar.style.width = `${progress}%`;
|
|
progressText.textContent = `${Math.round(progress)}%`;
|
|
}
|
|
|
|
// Function to hide loading screen
|
|
function hideLoadingScreen() {
|
|
loadingContainer.style.display = 'none';
|
|
}
|
|
|
|
|
|
|
|
const gui = new GUI();
|
|
|
|
let ui_state = {}
|
|
ui_state['rot_scale'] = 1.0;
|
|
ui_state['defaultMtl'] = TinyUSDZLoaderUtils.createDefaultMaterial();
|
|
|
|
ui_state['envMapIntensity'] = 3.14; // pi is good for pisaHDR;
|
|
ui_state['ambient'] = 0.4;
|
|
let ambientLight = new THREE.AmbientLight(0x404040, ui_state['ambient']);
|
|
ui_state['camera_z'] = 3.14; // TODO: Compute best fit from scene's bbox.
|
|
ui_state['needsMtlUpdate'] = false;
|
|
ui_state['renderer'] = null;
|
|
|
|
|
|
// Create a parameters object
|
|
const params = {
|
|
envMapIntensity: ui_state['envMapIntensity'],
|
|
rotationSpeed: ui_state['rot_scale'],
|
|
camera_z: ui_state['camera_z'],
|
|
take_screenshot: takeScreenshot
|
|
};
|
|
|
|
// Add controls
|
|
gui.add(params, 'envMapIntensity', 0, 20, 0.1).name('envMapIntensity').onChange((value) => {
|
|
ui_state['envMapIntensity'] = value;
|
|
ui_state['needsMtlUpdate'] = true;
|
|
|
|
});
|
|
gui.add(params, 'camera_z', 0, 20).name('Camera Z').onChange((value) => {
|
|
ui_state['camera_z'] = value;
|
|
});
|
|
gui.add(params, 'rotationSpeed', 0, 10).name('Rotation Speed').onChange((value) => {
|
|
ui_state['rot_scale'] = value;
|
|
});
|
|
gui.add(params, 'take_screenshot').name('Take Screenshot');
|
|
|
|
|
|
function takeScreenshot() {
|
|
|
|
const renderer = ui_state['renderer'];
|
|
const quality = 0.92; // JPEG quality, if you want to use JPEG format
|
|
|
|
const img = renderer.domElement.toDataURL('image/jpeg', quality)
|
|
console.log('Screenshot taken:', img);
|
|
|
|
return img;
|
|
}
|
|
|
|
async function loadScenes() {
|
|
|
|
updateLoadingProgress(20, 'Initializing TinyUSDZLoader...');
|
|
|
|
// Create loader with optional memory limit
|
|
// Default: 2GB for WASM32, 8GB for WASM64
|
|
// const loader = new TinyUSDZLoader(null, { maxMemoryLimitMB: 512 }); // Set 512MB limit
|
|
const loader = new TinyUSDZLoader();
|
|
|
|
// it is recommended to call init() before loadAsync()
|
|
// (wait loading/compiling wasm module in the early stage))
|
|
//await loader.init();
|
|
|
|
const useMemory64 = checkMemory64Support();
|
|
console.log('64-bit memory support:', useMemory64);
|
|
await loader.init({useMemory64});
|
|
|
|
// You can set memory limit for USD loading.
|
|
// The limit is only effective to USD loading.
|
|
// No limit for asset data(e.g. textures) and Three.js data, etc.
|
|
loader.setMaxMemoryLimitMB(250);
|
|
|
|
|
|
// Use zstd compressed tinyusdz.wasm to save the bandwidth.
|
|
//await loader.init({useZstdCompressedWasm: true});
|
|
|
|
const suzanne_filename = "./assets/suzanne-pbr.usda";
|
|
const texcat_filename = "./assets/texture-cat-plane.usdz";
|
|
const cookie_filename = "./assets/UsdCookie.usdz";
|
|
//const usd_filename = "./assets/suzanne-pbr.usda";
|
|
const usd_filename = "./assets/suzanne-subd-lv6.usdc";
|
|
|
|
var threeScenes = []
|
|
|
|
const usd_scenes = await Promise.all([
|
|
//loader.loadAsync(texcat_filename),
|
|
loader.loadAsync(usd_filename),
|
|
//loader.loadAsync(suzanne_filename),
|
|
]);
|
|
|
|
hideLoadingScreen();
|
|
|
|
const defaultMtl = ui_state['defaultMtl'];
|
|
|
|
const options = {
|
|
overrideMaterial: false, // override USD material with defaultMtl(default 'false')
|
|
envMap: defaultMtl.envMap, // reuse envmap from defaultMtl
|
|
envMapIntensity: ui_state['envMapIntensity'], // default envmap intensity
|
|
}
|
|
|
|
var offset = -(usd_scenes.length - 1) * 1.5;
|
|
for (const usd_scene of usd_scenes) {
|
|
|
|
const usdRootNode = usd_scene.getDefaultRootNode();
|
|
|
|
const threeNode = TinyUSDZLoaderUtils.buildThreeNode(usdRootNode, defaultMtl, usd_scene, options);
|
|
|
|
if (usd_scene.getURI().includes('UsdCookie')) {
|
|
// Add exra scaling
|
|
threeNode.scale.x *= 2.5;
|
|
threeNode.scale.y *= 2.5;
|
|
threeNode.scale.z *= 2.5;
|
|
}
|
|
|
|
threeNode.position.x += offset;
|
|
offset += 3.0;
|
|
|
|
threeScenes.push(threeNode);
|
|
}
|
|
|
|
return threeScenes;
|
|
|
|
}
|
|
|
|
|
|
|
|
const scene = new THREE.Scene();
|
|
|
|
async function initScene() {
|
|
|
|
const envmap = await new HDRCubeTextureLoader()
|
|
.setPath('assets/textures/cube/pisaHDR/')
|
|
.loadAsync(['px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr'])
|
|
scene.background = envmap;
|
|
scene.environment = envmap;
|
|
|
|
// Assign envmap to material
|
|
// Otherwise some material parameters like clarcoat will not work properly.
|
|
ui_state['defaultMtl'].envMap = envmap;
|
|
|
|
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|
camera.position.z = ui_state['camera_z'];
|
|
|
|
const renderer = new THREE.WebGLRenderer({
|
|
preserveDrawingBuffer: true, // for screenshot
|
|
alpha: true, // Enable transparency
|
|
antialias: true
|
|
});
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
ui_state['renderer'] = renderer; // Store renderer in ui_state
|
|
document.body.appendChild(renderer.domElement);
|
|
|
|
const rootNodes = await loadScenes();
|
|
|
|
for (const rootNode of rootNodes) {
|
|
scene.add(rootNode);
|
|
}
|
|
|
|
function animate() {
|
|
|
|
for (const rootNode of rootNodes) {
|
|
rootNode.rotation.y += 0.01 * ui_state['rot_scale'];
|
|
rootNode.rotation.x += 0.02 * ui_state['rot_scale'];
|
|
}
|
|
|
|
camera.position.z = ui_state['camera_z'];
|
|
|
|
if (ui_state['needsMtlUpdate']) {
|
|
|
|
// TODO: Cache materials in the scene.
|
|
scene.traverse((object) => {
|
|
if (object.material) {
|
|
if (Object.prototype.hasOwnProperty.call(object.material, 'envMapIntensity')) {
|
|
object.material.envMapIntensity = ui_state['envMapIntensity'];
|
|
object.material.needsUpdate = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
ui_state['needsMtlUpdate'] = false;
|
|
}
|
|
|
|
renderer.render(scene, camera);
|
|
|
|
}
|
|
|
|
renderer.setAnimationLoop(animate);
|
|
}
|
|
|
|
initScene();
|