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

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