269 lines
5.5 KiB
JavaScript
269 lines
5.5 KiB
JavaScript
import { Tab } from '../ui/Tab.js';
|
|
import { List } from '../ui/List.js';
|
|
import { Item } from '../ui/Item.js';
|
|
import { splitPath, splitCamelCase } from '../ui/utils.js';
|
|
|
|
import { RendererUtils, NoToneMapping, LinearSRGBColorSpace, QuadMesh, NodeMaterial, CanvasTarget } from 'three/webgpu';
|
|
import { renderOutput, vec2, vec3, vec4, Fn, screenUV, step, OnMaterialUpdate, uniform } from 'three/tsl';
|
|
|
|
const aspectRatioUV = /*@__PURE__*/ Fn( ( [ uv, textureNode ] ) => {
|
|
|
|
const aspect = uniform( 0 );
|
|
|
|
OnMaterialUpdate( () => {
|
|
|
|
const { width, height } = textureNode.value;
|
|
|
|
aspect.value = width / height;
|
|
|
|
} );
|
|
|
|
const centered = uv.sub( 0.5 );
|
|
const corrected = vec2( centered.x.div( aspect ), centered.y );
|
|
const finalUV = corrected.add( 0.5 );
|
|
|
|
const inBounds = step( 0.0, finalUV.x ).mul( step( finalUV.x, 1.0 ) ).mul( step( 0.0, finalUV.y ) ).mul( step( finalUV.y, 1.0 ) );
|
|
|
|
return vec3( finalUV, inBounds );
|
|
|
|
} );
|
|
|
|
class Viewer extends Tab {
|
|
|
|
constructor( options = {} ) {
|
|
|
|
super( 'Viewer', options );
|
|
|
|
const nodeList = new List( 'Viewer', 'Name' );
|
|
nodeList.setGridStyle( '150px minmax(200px, 2fr)' );
|
|
nodeList.domElement.style.minWidth = '400px';
|
|
|
|
const scrollWrapper = document.createElement( 'div' );
|
|
scrollWrapper.className = 'list-scroll-wrapper';
|
|
scrollWrapper.appendChild( nodeList.domElement );
|
|
this.content.appendChild( scrollWrapper );
|
|
|
|
const nodes = new Item( 'Nodes' );
|
|
nodeList.add( nodes );
|
|
|
|
//
|
|
|
|
this.itemLibrary = new Map();
|
|
this.folderLibrary = new Map();
|
|
this.canvasNodes = new Map();
|
|
this.currentDataList = [];
|
|
this.nodeList = nodeList;
|
|
this.nodes = nodes;
|
|
|
|
}
|
|
|
|
getFolder( name ) {
|
|
|
|
let folder = this.folderLibrary.get( name );
|
|
|
|
if ( folder === undefined ) {
|
|
|
|
folder = new Item( name );
|
|
|
|
this.folderLibrary.set( name, folder );
|
|
this.nodeList.add( folder );
|
|
|
|
}
|
|
|
|
return folder;
|
|
|
|
}
|
|
|
|
addNodeItem( canvasData ) {
|
|
|
|
let item = this.itemLibrary.get( canvasData.id );
|
|
|
|
if ( item === undefined ) {
|
|
|
|
const name = canvasData.name;
|
|
const domElement = canvasData.canvasTarget.domElement;
|
|
|
|
item = new Item( domElement, name );
|
|
item.itemRow.children[ 1 ].style[ 'justify-content' ] = 'flex-start';
|
|
this.itemLibrary.set( canvasData.id, item );
|
|
|
|
}
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
getCanvasDataByNode( renderer, node ) {
|
|
|
|
let canvasData = this.canvasNodes.get( node );
|
|
|
|
if ( canvasData === undefined ) {
|
|
|
|
const canvas = document.createElement( 'canvas' );
|
|
|
|
const canvasTarget = new CanvasTarget( canvas );
|
|
canvasTarget.setPixelRatio( window.devicePixelRatio );
|
|
canvasTarget.setSize( 140, 140 );
|
|
|
|
const id = node.id;
|
|
|
|
const { path, name } = splitPath( splitCamelCase( node.getName() || '(unnamed)' ) );
|
|
|
|
const target = node.context( { getUV: ( textureNode ) => {
|
|
|
|
const uvData = aspectRatioUV( screenUV, textureNode );
|
|
const correctedUV = uvData.xy;
|
|
const mask = uvData.z;
|
|
|
|
return correctedUV.mul( mask );
|
|
|
|
} } );
|
|
|
|
let output = vec4( vec3( target ), 1 );
|
|
output = renderOutput( output, NoToneMapping, renderer.outputColorSpace );
|
|
output = output.context( { inspector: true } );
|
|
|
|
const material = new NodeMaterial();
|
|
material.outputNode = output;
|
|
|
|
const quad = new QuadMesh( material );
|
|
quad.name = 'Viewer - ' + name;
|
|
|
|
canvasData = {
|
|
id,
|
|
name,
|
|
path,
|
|
node,
|
|
quad,
|
|
canvasTarget,
|
|
material
|
|
};
|
|
|
|
this.canvasNodes.set( node, canvasData );
|
|
|
|
}
|
|
|
|
return canvasData;
|
|
|
|
}
|
|
|
|
update( inspector ) {
|
|
|
|
const renderer = inspector.getRenderer();
|
|
const nodes = inspector.getNodes();
|
|
|
|
if ( nodes.length > 0 ) {
|
|
|
|
if ( ! renderer.backend.isWebGPUBackend ) {
|
|
|
|
inspector.resolveConsoleOnce( 'warn', 'Inspector: Viewer is only available with WebGPU.' );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( ! this.isVisible ) {
|
|
|
|
this.show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( ! this.isActive ) return;
|
|
|
|
const canvasDataList = nodes.map( node => this.getCanvasDataByNode( renderer, node ) );
|
|
|
|
//
|
|
|
|
const previousDataList = [ ...this.currentDataList ];
|
|
|
|
// remove old
|
|
|
|
for ( const canvasData of previousDataList ) {
|
|
|
|
if ( this.itemLibrary.has( canvasData.id ) && canvasDataList.indexOf( canvasData ) === - 1 ) {
|
|
|
|
const item = this.itemLibrary.get( canvasData.id );
|
|
const parent = item.parent;
|
|
|
|
parent.remove( item );
|
|
|
|
if ( this.folderLibrary.has( parent.data[ 0 ] ) && parent.children.length === 0 ) {
|
|
|
|
parent.parent.remove( parent );
|
|
|
|
this.folderLibrary.delete( parent.data[ 0 ] );
|
|
|
|
}
|
|
|
|
this.itemLibrary.delete( canvasData.id );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
|
|
const indexes = {};
|
|
|
|
for ( const canvasData of canvasDataList ) {
|
|
|
|
const item = this.addNodeItem( canvasData );
|
|
const previousCanvasTarget = renderer.getCanvasTarget();
|
|
|
|
const path = canvasData.path;
|
|
|
|
if ( path ) {
|
|
|
|
const folder = this.getFolder( path );
|
|
|
|
if ( indexes[ path ] === undefined ) {
|
|
|
|
indexes[ path ] = 0;
|
|
|
|
}
|
|
|
|
if ( folder.parent === null || item.parent !== folder || folder.children.indexOf( item ) !== indexes[ path ] ) {
|
|
|
|
folder.add( item );
|
|
|
|
}
|
|
|
|
indexes[ path ] ++;
|
|
|
|
} else {
|
|
|
|
if ( ! item.parent ) {
|
|
|
|
this.nodes.add( item );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.currentDataList = canvasDataList;
|
|
|
|
//
|
|
|
|
const state = RendererUtils.resetRendererState( renderer );
|
|
|
|
renderer.toneMapping = NoToneMapping;
|
|
renderer.outputColorSpace = LinearSRGBColorSpace;
|
|
|
|
renderer.setCanvasTarget( canvasData.canvasTarget );
|
|
|
|
canvasData.quad.render( renderer );
|
|
|
|
renderer.setCanvasTarget( previousCanvasTarget );
|
|
|
|
RendererUtils.restoreRendererState( renderer, state );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export { Viewer };
|