225 lines
7.3 KiB
JavaScript
225 lines
7.3 KiB
JavaScript
//
|
|
// Copyright Contributors to the MaterialX Project
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
import * as THREE from 'three';
|
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
|
|
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
|
|
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
|
|
|
|
import { GammaCorrectionShader } from 'three/examples/jsm/shaders/GammaCorrectionShader.js';
|
|
|
|
import { Viewer } from './viewer.js'
|
|
import { dropHandler, dragOverHandler, setLoadingCallback, setSceneLoadingCallback } from './dropHandling.js';
|
|
|
|
let renderer, composer, orbitControls;
|
|
|
|
// Turntable option. For now the step size is fixed.
|
|
let turntableEnabled = false;
|
|
let turntableSteps = 360;
|
|
let turntableStep = 0;
|
|
|
|
let captureRequested = false;
|
|
|
|
// Get URL options. Fallback to defaults if not specified.
|
|
let materialFilename = new URLSearchParams(document.location.search).get("file");
|
|
if (!materialFilename)
|
|
{
|
|
materialFilename = 'Materials/Examples/StandardSurface/standard_surface_default.mtlx';
|
|
}
|
|
|
|
let viewer = Viewer.create();
|
|
init();
|
|
viewer.getEditor().updateProperties(0.9);
|
|
|
|
// Capture the current frame and save an image file.
|
|
function captureFrame()
|
|
{
|
|
let canvas = document.getElementById('webglcanvas');
|
|
var url = canvas.toDataURL();
|
|
var link = document.createElement('a');
|
|
link.setAttribute('href', url);
|
|
link.setAttribute('target', '_blank');
|
|
link.setAttribute('download', 'screenshot.png');
|
|
link.click();
|
|
}
|
|
|
|
function init()
|
|
{
|
|
let canvas = document.getElementById('webglcanvas');
|
|
let context = canvas.getContext('webgl2');
|
|
|
|
// Handle material selection changes
|
|
let materialsSelect = document.getElementById('materials');
|
|
materialsSelect.value = materialFilename;
|
|
materialsSelect.addEventListener('change', (e) =>
|
|
{
|
|
materialFilename = e.target.value;
|
|
viewer.getEditor().initialize();
|
|
viewer.getMaterial().loadMaterials(viewer, materialFilename);
|
|
viewer.getEditor().updateProperties(0.9);
|
|
viewer.getScene().setUpdateTransforms();
|
|
});
|
|
|
|
// Handle geometry selection changes
|
|
const scene = viewer.getScene();
|
|
let geometrySelect = document.getElementById('geometry');
|
|
geometrySelect.value = scene.getGeometryURL();
|
|
geometrySelect.addEventListener('change', (e) =>
|
|
{
|
|
console.log('Change geometry to:', e.target.value);
|
|
scene.setGeometryURL(e.target.value);
|
|
scene.loadGeometry(viewer, orbitControls);
|
|
});
|
|
|
|
// Set up scene
|
|
scene.initialize();
|
|
|
|
// Set up renderer
|
|
renderer = new THREE.WebGLRenderer({ canvas, context });
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
// Disable introspection for shader debugging for deployment.
|
|
// - The code associated with getting program information can be very slow when
|
|
// dealing with shaders with lots of input uniforms (such as standard surface, openpbr shading models)
|
|
// as each call is blocking.
|
|
// - Adding this avoids the chess set scene from "hanging" the Chrome browser on Windows to a few second load.
|
|
// - Documentation for this flag: https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderer.debug
|
|
renderer.debug.checkShaderErrors = false;
|
|
|
|
composer = new EffectComposer(renderer);
|
|
const renderPass = new RenderPass(scene.getScene(), scene.getCamera());
|
|
composer.addPass(renderPass);
|
|
const gammaCorrectionPass = new ShaderPass(GammaCorrectionShader);
|
|
composer.addPass(gammaCorrectionPass);
|
|
|
|
window.addEventListener('resize', onWindowResize);
|
|
|
|
// Set up controls
|
|
orbitControls = new OrbitControls(scene.getCamera(), renderer.domElement);
|
|
orbitControls.addEventListener('change', () =>
|
|
{
|
|
viewer.getScene().setUpdateTransforms();
|
|
})
|
|
|
|
// Add hotkey 'f' to capture the current frame and save an image file.
|
|
// See check inside the render loop when a capture can be performed.
|
|
document.addEventListener('keydown', (event) =>
|
|
{
|
|
if (event.key === 'f')
|
|
{
|
|
captureRequested = true;
|
|
}
|
|
});
|
|
|
|
// Initialize editor
|
|
viewer.getEditor().initialize();
|
|
|
|
const hdrLoader = viewer.getHdrLoader();
|
|
const fileLoader = viewer.getFileLoader();
|
|
Promise.all([
|
|
new Promise(resolve => hdrLoader.load('Lights/san_giuseppe_bridge_split.hdr', resolve)),
|
|
new Promise(resolve => hdrLoader.load('Lights/irradiance/san_giuseppe_bridge_split.hdr', resolve)),
|
|
new Promise(resolve => fileLoader.load('Lights/san_giuseppe_bridge_split.mtlx', resolve)),
|
|
new Promise(function (resolve)
|
|
{
|
|
MaterialX().then((module) =>
|
|
{
|
|
resolve(module);
|
|
});
|
|
})
|
|
]).then(async ([radianceTexture, irradianceTexture, lightRigXml, mxIn]) =>
|
|
{
|
|
// Initialize viewer + lighting
|
|
await viewer.initialize(mxIn, renderer, radianceTexture, irradianceTexture, lightRigXml);
|
|
|
|
// Load geometry
|
|
let scene = viewer.getScene();
|
|
scene.loadGeometry(viewer, orbitControls);
|
|
|
|
// Load materials
|
|
viewer.getMaterial().loadMaterials(viewer, materialFilename);
|
|
|
|
// Update assignments
|
|
viewer.getMaterial().updateMaterialAssignments(viewer);
|
|
|
|
canvas.addEventListener("keydown", handleKeyEvents, true);
|
|
|
|
}).then(() =>
|
|
{
|
|
animate();
|
|
}).catch(err =>
|
|
{
|
|
console.error(Number.isInteger(err) ? this.getMx().getExceptionMessage(err) : err);
|
|
})
|
|
|
|
// allow dropping files and directories
|
|
document.addEventListener('drop', dropHandler, false);
|
|
document.addEventListener('dragover', dragOverHandler, false);
|
|
|
|
setLoadingCallback(file =>
|
|
{
|
|
materialFilename = file.fullPath || file.name;
|
|
viewer.getEditor().initialize();
|
|
viewer.getMaterial().loadMaterials(viewer, materialFilename);
|
|
viewer.getEditor().updateProperties(0.9);
|
|
viewer.getScene().setUpdateTransforms();
|
|
});
|
|
|
|
setSceneLoadingCallback(file =>
|
|
{
|
|
let glbFileName = file.fullPath || file.name;
|
|
console.log('Drop geometry to:', glbFileName);
|
|
scene.setGeometryURL(glbFileName);
|
|
scene.loadGeometry(viewer, orbitControls);
|
|
});
|
|
|
|
// enable three.js Cache so that dropped files can reference each other
|
|
THREE.Cache.enabled = true;
|
|
}
|
|
|
|
function onWindowResize()
|
|
{
|
|
viewer.getScene().updateCamera();
|
|
viewer.getScene().setUpdateTransforms();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
}
|
|
|
|
function animate()
|
|
{
|
|
requestAnimationFrame(animate);
|
|
|
|
if (turntableEnabled)
|
|
{
|
|
turntableStep = (turntableStep + 1) % 360;
|
|
var turntableAngle = turntableStep * (360.0 / turntableSteps) / 180.0 * Math.PI;
|
|
viewer.getScene()._scene.rotation.y = turntableAngle;
|
|
viewer.getScene().setUpdateTransforms();
|
|
}
|
|
|
|
composer.render();
|
|
viewer.getScene().updateTransforms();
|
|
|
|
if (captureRequested)
|
|
{
|
|
captureFrame();
|
|
captureRequested = false;
|
|
}
|
|
}
|
|
|
|
function handleKeyEvents(event)
|
|
{
|
|
const V_KEY = 86;
|
|
const P_KEY = 80;
|
|
|
|
if (event.keyCode == V_KEY)
|
|
{
|
|
viewer.getScene().toggleBackgroundTexture();
|
|
}
|
|
else if (event.keyCode == P_KEY)
|
|
{
|
|
turntableEnabled = !turntableEnabled;
|
|
}
|
|
}
|