You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
earthquake_3d_viewer_front/three/editor/js/Sidebar.Project.Image.js

226 lines
5.8 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import * as THREE from 'three';
import { UIBreak, UIButton, UIInteger, UIPanel, UIRow, UISelect, UIText } from './libs/ui.js';
import { ViewportPathtracer } from './Viewport.Pathtracer.js';
function SidebarProjectImage( editor ) {
const strings = editor.strings;
const container = new UIPanel();
container.setId( 'render' );
// Image
container.add( new UIText( strings.getKey( 'sidebar/project/image' ) ).setTextTransform( 'uppercase' ) );
container.add( new UIBreak(), new UIBreak() );
// Shading
const shadingRow = new UIRow();
container.add( shadingRow );
shadingRow.add( new UIText( strings.getKey( 'sidebar/project/shading' ) ).setClass( 'Label' ) );
const shadingTypeSelect = new UISelect().setOptions( {
'solid': 'SOLID',
'realistic': 'REALISTIC'
} ).setWidth( '170px' ).onChange( refreshShadingRow ).setValue( 'solid' );
shadingRow.add( shadingTypeSelect );
const pathTracerMinSamples = 3;
const pathTracerMaxSamples = 65536;
const samplesNumber = new UIInteger( 16 ).setRange( pathTracerMinSamples, pathTracerMaxSamples );
const samplesRow = new UIRow();
samplesRow.add( new UIText( strings.getKey( 'sidebar/project/image/samples' ) ).setClass( 'Label' ) );
samplesRow.add( samplesNumber );
container.add( samplesRow );
function refreshShadingRow() {
samplesRow.setHidden( shadingTypeSelect.getValue() !== 'realistic' );
}
refreshShadingRow();
// Resolution
const resolutionRow = new UIRow();
container.add( resolutionRow );
resolutionRow.add( new UIText( strings.getKey( 'sidebar/project/resolution' ) ).setClass( 'Label' ) );
const imageWidth = new UIInteger( 1024 ).setTextAlign( 'center' ).setWidth( '28px' );
resolutionRow.add( imageWidth );
resolutionRow.add( new UIText( '×' ).setTextAlign( 'center' ).setFontSize( '12px' ).setWidth( '12px' ) );
const imageHeight = new UIInteger( 1024 ).setTextAlign( 'center' ).setWidth( '28px' );
resolutionRow.add( imageHeight );
// Render
const renderButton = new UIButton( strings.getKey( 'sidebar/project/render' ) );
renderButton.setWidth( '170px' );
renderButton.setMarginLeft( '120px' );
renderButton.onClick( async () => {
if ( shadingTypeSelect.getValue() === 'realistic' ) {
let isMaterialsValid = true;
editor.scene.traverseVisible( ( object ) => {
if ( object.isMesh ) {
const materials = Array.isArray( object.material ) ? object.material : [ object.material ];
for ( let i = 0; i < materials.length; i ++ ) {
const material = materials[ i ];
if ( ! material.isMeshStandardMaterial ) {
isMaterialsValid = false;
return;
}
}
}
} );
if ( isMaterialsValid === false ) {
alert( strings.getKey( 'prompt/rendering/realistic/unsupportedMaterial' ) );
return;
}
}
//
const json = editor.toJSON();
const project = json.project;
//
const loader = new THREE.ObjectLoader();
const camera = loader.parse( json.camera );
camera.aspect = imageWidth.getValue() / imageHeight.getValue();
camera.updateProjectionMatrix();
camera.updateMatrixWorld();
const scene = loader.parse( json.scene );
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( imageWidth.getValue(), imageHeight.getValue() );
if ( project.shadows !== undefined ) renderer.shadowMap.enabled = project.shadows;
if ( project.shadowType !== undefined ) renderer.shadowMap.type = project.shadowType;
if ( project.toneMapping !== undefined ) renderer.toneMapping = project.toneMapping;
if ( project.toneMappingExposure !== undefined ) renderer.toneMappingExposure = project.toneMappingExposure;
// popup
const width = imageWidth.getValue() / window.devicePixelRatio;
const height = imageHeight.getValue() / window.devicePixelRatio;
const left = ( screen.width - width ) / 2;
const top = ( screen.height - height ) / 2;
const output = window.open( '', '_blank', `location=no,left=${left},top=${top},width=${width},height=${height}` );
const meta = document.createElement( 'meta' );
meta.name = 'viewport';
meta.content = 'width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0';
output.document.head.appendChild( meta );
output.document.body.style.background = '#000';
output.document.body.style.margin = '0px';
output.document.body.style.overflow = 'hidden';
const canvas = renderer.domElement;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
output.document.body.appendChild( canvas );
//
switch ( shadingTypeSelect.getValue() ) {
case 'solid':
renderer.render( scene, camera );
renderer.dispose();
break;
case 'realistic':
const status = document.createElement( 'div' );
status.style.position = 'absolute';
status.style.top = '10px';
status.style.left = '10px';
status.style.color = 'white';
status.style.fontFamily = 'system-ui';
status.style.fontSize = '12px';
output.document.body.appendChild( status );
const pathTracer = new ViewportPathtracer( renderer );
pathTracer.init( scene, camera );
pathTracer.setSize( imageWidth.getValue(), imageHeight.getValue() );
const maxSamples = Math.max( pathTracerMinSamples, Math.min( pathTracerMaxSamples, samplesNumber.getValue() ) );
function animate() {
if ( output.closed === true ) return;
const samples = Math.floor( pathTracer.getSamples() ) + 1;
if ( samples < maxSamples ) {
requestAnimationFrame( animate );
}
pathTracer.update();
const progress = Math.floor( samples / maxSamples * 100 );
status.textContent = `${ samples } / ${ maxSamples } ( ${ progress }% )`;
if ( progress === 100 ) {
status.textContent += ' ✓';
}
}
animate();
break;
}
} );
container.add( renderButton );
//
return container;
}
export { SidebarProjectImage };