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