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