Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.38.10/javascript/MaterialXView/source/index.js
2025-05-18 13:04:45 +08:00

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;
}
}