Viewer.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. import { Tab } from '../ui/Tab.js';
  2. import { List } from '../ui/List.js';
  3. import { Item } from '../ui/Item.js';
  4. import { splitPath, splitCamelCase } from '../ui/utils.js';
  5. import { getItem, setItem } from '../Inspector.js';
  6. import { RendererUtils, NoToneMapping, LinearSRGBColorSpace, QuadMesh, NodeMaterial, CanvasTarget, Vector2 } from 'three/webgpu';
  7. import { renderOutput, vec2, vec3, vec4, Fn, screenUV, step, OnMaterialUpdate, uniform, float } from 'three/tsl';
  8. const _size = /*@__PURE__*/ new Vector2();
  9. const aspectRatioUV = /*@__PURE__*/ Fn( ( [ uv, textureNode, canvasAspect ] ) => {
  10. const textureAspect = uniform( 0 );
  11. OnMaterialUpdate( () => {
  12. const { width, height } = textureNode.value;
  13. textureAspect.value = width / height;
  14. } );
  15. const ratio = canvasAspect.div( textureAspect );
  16. const centered = uv.sub( 0.5 );
  17. // If canvasAspect > textureAspect:
  18. const uvWide = vec2( centered.x.mul( ratio ), centered.y ).add( 0.5 );
  19. // If canvasAspect <= textureAspect:
  20. const uvTall = vec2( centered.x, centered.y.div( ratio ) ).add( 0.5 );
  21. const finalUV = canvasAspect.greaterThan( textureAspect ).select( uvWide, uvTall );
  22. 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 ) );
  23. return vec3( finalUV, inBounds );
  24. } );
  25. class Viewer extends Tab {
  26. constructor( options = {} ) {
  27. super( 'Viewer', options );
  28. this.content.style.overflow = 'hidden';
  29. this.maximizedByFullscreenButton = false;
  30. // Toolbar
  31. const toolbar = document.createElement( 'div' );
  32. toolbar.className = 'toolbar';
  33. const backBtn = document.createElement( 'button' );
  34. backBtn.className = 'viewer-back-btn';
  35. backBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>';
  36. backBtn.title = 'Back to list';
  37. backBtn.style.display = 'none';
  38. toolbar.appendChild( backBtn );
  39. const label = document.createElement( 'span' );
  40. label.textContent = 'View:';
  41. toolbar.appendChild( label );
  42. const select = document.createElement( 'select' );
  43. select.className = 'select';
  44. select.style.width = '200px';
  45. const defaultOption = document.createElement( 'option' );
  46. defaultOption.value = 'list';
  47. defaultOption.textContent = 'List';
  48. select.appendChild( defaultOption );
  49. toolbar.appendChild( select );
  50. this.content.appendChild( toolbar );
  51. const nodeList = new List( 'Viewer', 'Name' );
  52. nodeList.setGridStyle( '150px minmax(200px, 2fr)' );
  53. nodeList.domElement.style.minWidth = '400px';
  54. const scrollWrapper = document.createElement( 'div' );
  55. scrollWrapper.className = 'list-scroll-wrapper';
  56. scrollWrapper.style.flexGrow = '1';
  57. scrollWrapper.style.overflowY = 'auto';
  58. scrollWrapper.style.minHeight = '0';
  59. scrollWrapper.appendChild( nodeList.domElement );
  60. this.content.appendChild( scrollWrapper );
  61. // Container for full screen view
  62. const fullViewerContainer = document.createElement( 'div' );
  63. fullViewerContainer.className = 'full-viewer-container';
  64. fullViewerContainer.style.touchAction = 'none';
  65. this.content.appendChild( fullViewerContainer );
  66. const nodes = new Item( 'User Defined' );
  67. nodes.setCollapsible( true );
  68. nodeList.add( nodes );
  69. //
  70. this.itemLibrary = new Map();
  71. this.folderLibrary = new Map();
  72. this.canvasNodes = new Map();
  73. this.currentDataList = [];
  74. this.nodeList = nodeList;
  75. this.nodes = nodes;
  76. this.scrollWrapper = scrollWrapper;
  77. this.fullViewerContainer = fullViewerContainer;
  78. this.select = select;
  79. this.backBtn = backBtn;
  80. this.activeFullNodeId = null;
  81. this.pendingRestoreView = true;
  82. backBtn.addEventListener( 'click', () => {
  83. select.value = 'list';
  84. this.showListView();
  85. if ( this.maximizedByFullscreenButton ) {
  86. if ( this.profiler && this.profiler.panel.classList.contains( 'maximized' ) ) {
  87. this.profiler.toggleMaximize();
  88. }
  89. this.maximizedByFullscreenButton = false;
  90. }
  91. this.saveLastView();
  92. } );
  93. select.addEventListener( 'change', () => {
  94. const val = select.value;
  95. if ( val === 'list' ) {
  96. this.showListView();
  97. } else {
  98. this.showNodeView( val );
  99. }
  100. this.saveLastView();
  101. } );
  102. // Event forwarding setup for OrbitControls
  103. this.isDraggingThumbnail = false;
  104. this.activeSourceCanvas = null;
  105. this.activePointerIds = new Set();
  106. const handleGlobalPointer = ( e ) => {
  107. if ( ! this.isDraggingThumbnail || ! this.activeSourceCanvas ) return;
  108. const renderer = this.inspector.getRenderer();
  109. if ( ! renderer || ! renderer.domElement ) return;
  110. if ( e.isForwarded ) return;
  111. // Block native event from reaching other document-level listeners (OrbitControls)
  112. e.stopImmediatePropagation();
  113. e.preventDefault();
  114. // Project and dispatch forwarded event
  115. this.forwardEvent( e, this.activeSourceCanvas, renderer.domElement );
  116. if ( e.type === 'pointerup' || e.type === 'pointercancel' ) {
  117. this.activePointerIds.delete( e.pointerId );
  118. if ( this.activePointerIds.size === 0 ) {
  119. this.isDraggingThumbnail = false;
  120. this.activeSourceCanvas = null;
  121. }
  122. }
  123. };
  124. window.addEventListener( 'pointermove', handleGlobalPointer, true );
  125. window.addEventListener( 'pointerup', handleGlobalPointer, true );
  126. window.addEventListener( 'pointercancel', handleGlobalPointer, true );
  127. }
  128. getFolder( name ) {
  129. let folder = this.folderLibrary.get( name );
  130. if ( folder === undefined ) {
  131. folder = new Item( name );
  132. folder.setCollapsible( true );
  133. this.folderLibrary.set( name, folder );
  134. this.nodeList.add( folder );
  135. }
  136. return folder;
  137. }
  138. hide() {
  139. super.hide();
  140. this.maximizedByFullscreenButton = false;
  141. this.isDraggingThumbnail = false;
  142. this.activeSourceCanvas = null;
  143. this.activePointerIds.clear();
  144. }
  145. addNodeItem( canvasData ) {
  146. let item = this.itemLibrary.get( canvasData.id );
  147. if ( item === undefined ) {
  148. const name = canvasData.name;
  149. const domElement = canvasData.canvasTarget.domElement;
  150. // Create wrapper
  151. const wrapper = document.createElement( 'div' );
  152. wrapper.className = 'node-canvas-wrapper';
  153. wrapper.style.position = 'relative';
  154. wrapper.style.display = 'inline-block';
  155. wrapper.style.width = '140px';
  156. wrapper.style.height = '140px';
  157. wrapper.style.touchAction = 'none';
  158. // View full screen button
  159. const viewBtn = document.createElement( 'button' );
  160. viewBtn.className = 'node-canvas-detach-btn';
  161. viewBtn.title = 'View full size';
  162. viewBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="7" y1="17" x2="17" y2="7"></line><polyline points="7 7 17 7 17 17"></polyline></svg>';
  163. viewBtn.onclick = ( e ) => {
  164. e.stopPropagation();
  165. this.select.value = canvasData.id;
  166. this.showNodeView( canvasData.id );
  167. this.saveLastView();
  168. };
  169. // Fullscreen and maximize button
  170. const fullscreenBtn = document.createElement( 'button' );
  171. fullscreenBtn.className = 'node-canvas-fullscreen-btn';
  172. fullscreenBtn.title = 'Fullscreen view';
  173. fullscreenBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>';
  174. fullscreenBtn.onclick = ( e ) => {
  175. e.stopPropagation();
  176. this.select.value = canvasData.id;
  177. this.showNodeView( canvasData.id );
  178. if ( this.profiler && ! this.profiler.panel.classList.contains( 'maximized' ) ) {
  179. this.profiler.toggleMaximize();
  180. this.maximizedByFullscreenButton = true;
  181. if ( ! this._maximizeListenerAdded && this.profiler.maximizeBtn ) {
  182. this.profiler.maximizeBtn.addEventListener( 'click', () => {
  183. this.maximizedByFullscreenButton = false;
  184. } );
  185. this._maximizeListenerAdded = true;
  186. }
  187. }
  188. this.saveLastView();
  189. };
  190. wrapper.appendChild( domElement );
  191. wrapper.appendChild( viewBtn );
  192. wrapper.appendChild( fullscreenBtn );
  193. this.setupEventForwarding( domElement );
  194. // Store elements in canvasData for access
  195. canvasData.domElement = domElement;
  196. canvasData.wrapperElement = wrapper;
  197. item = new Item( wrapper, name );
  198. item.itemRow.children[ 1 ].style[ 'justify-content' ] = 'flex-start';
  199. this.itemLibrary.set( canvasData.id, item );
  200. }
  201. return item;
  202. }
  203. setupEventForwarding( sourceCanvas ) {
  204. sourceCanvas.style.touchAction = 'none';
  205. const onPointerDown = ( e ) => {
  206. const renderer = this.inspector.getRenderer();
  207. if ( ! renderer || ! renderer.domElement ) return;
  208. const targetCanvas = renderer.domElement;
  209. this.isDraggingThumbnail = true;
  210. this.activeSourceCanvas = sourceCanvas;
  211. this.activePointerIds.add( e.pointerId );
  212. // Project and dispatch pointerdown
  213. this.forwardEvent( e, sourceCanvas, targetCanvas );
  214. };
  215. sourceCanvas.addEventListener( 'pointerdown', onPointerDown );
  216. // Wheel event support for zooming
  217. const onWheel = ( e ) => {
  218. const renderer = this.inspector.getRenderer();
  219. if ( ! renderer || ! renderer.domElement ) return;
  220. e.preventDefault();
  221. e.stopPropagation();
  222. this.forwardEvent( e, sourceCanvas, renderer.domElement );
  223. };
  224. sourceCanvas.addEventListener( 'wheel', onWheel, { passive: false } );
  225. // Click, dblclick, contextmenu
  226. const onMouseShortcut = ( e ) => {
  227. const renderer = this.inspector.getRenderer();
  228. if ( ! renderer || ! renderer.domElement ) return;
  229. e.stopPropagation();
  230. if ( e.type === 'contextmenu' ) {
  231. e.preventDefault();
  232. }
  233. this.forwardEvent( e, sourceCanvas, renderer.domElement );
  234. };
  235. sourceCanvas.addEventListener( 'click', onMouseShortcut );
  236. sourceCanvas.addEventListener( 'dblclick', onMouseShortcut );
  237. sourceCanvas.addEventListener( 'contextmenu', onMouseShortcut );
  238. }
  239. forwardEvent( event, sourceCanvas, targetCanvas ) {
  240. const sourceRect = sourceCanvas.getBoundingClientRect();
  241. const targetRect = targetCanvas.getBoundingClientRect();
  242. const localX = ( event.clientX - sourceRect.left ) / sourceRect.width;
  243. const localY = ( event.clientY - sourceRect.top ) / sourceRect.height;
  244. const targetClientX = targetRect.left + localX * targetRect.width;
  245. const targetClientY = targetRect.top + localY * targetRect.height;
  246. const targetPageX = targetClientX + window.scrollX;
  247. const targetPageY = targetClientY + window.scrollY;
  248. let newEvent;
  249. const eventInit = {
  250. bubbles: true,
  251. cancelable: true,
  252. view: window,
  253. clientX: targetClientX,
  254. clientY: targetClientY,
  255. screenX: targetClientX + window.screenX,
  256. screenY: targetClientY + window.screenY,
  257. pageX: targetPageX,
  258. pageY: targetPageY,
  259. ctrlKey: event.ctrlKey,
  260. shiftKey: event.shiftKey,
  261. altKey: event.altKey,
  262. metaKey: event.metaKey,
  263. buttons: event.buttons,
  264. button: event.button
  265. };
  266. if ( event instanceof WheelEvent ) {
  267. newEvent = new WheelEvent( event.type, {
  268. ...eventInit,
  269. deltaX: event.deltaX,
  270. deltaY: event.deltaY,
  271. deltaZ: event.deltaZ,
  272. deltaMode: event.deltaMode
  273. } );
  274. } else if ( window.PointerEvent && event instanceof PointerEvent ) {
  275. newEvent = new PointerEvent( event.type, {
  276. ...eventInit,
  277. pointerId: event.pointerId,
  278. width: event.width,
  279. height: event.height,
  280. pressure: event.pressure,
  281. tiltX: event.tiltX,
  282. tiltY: event.tiltY,
  283. pointerType: event.pointerType,
  284. isPrimary: event.isPrimary
  285. } );
  286. } else {
  287. newEvent = new MouseEvent( event.type, eventInit );
  288. }
  289. newEvent.isForwarded = true;
  290. targetCanvas.dispatchEvent( newEvent );
  291. }
  292. showListView() {
  293. if ( this.activeFullNodeId ) {
  294. const canvasData = Array.from( this.canvasNodes.values() ).find( data => String( data.id ) === String( this.activeFullNodeId ) );
  295. if ( canvasData ) {
  296. // Move canvas back to wrapper
  297. canvasData.wrapperElement.appendChild( canvasData.domElement );
  298. // Reset size
  299. canvasData.domElement.style.width = '';
  300. canvasData.domElement.style.height = '';
  301. canvasData.canvasTarget.setSize( 140, 140 );
  302. const renderer = this.inspector.getRenderer();
  303. renderer.backend.delete( canvasData.canvasTarget );
  304. }
  305. this.activeFullNodeId = null;
  306. }
  307. this.scrollWrapper.style.display = '';
  308. this.fullViewerContainer.style.display = 'none';
  309. this.backBtn.style.display = 'none';
  310. }
  311. showNodeView( nodeId ) {
  312. // First restore previous full screen node if any
  313. if ( this.activeFullNodeId && String( this.activeFullNodeId ) !== String( nodeId ) ) {
  314. this.showListView();
  315. }
  316. const canvasData = Array.from( this.canvasNodes.values() ).find( data => String( data.id ) === String( nodeId ) );
  317. if ( canvasData ) {
  318. this.addNodeItem( canvasData );
  319. this.activeFullNodeId = nodeId;
  320. this.backBtn.style.display = 'flex';
  321. // Hide list, show full screen container
  322. this.scrollWrapper.style.display = 'none';
  323. this.fullViewerContainer.style.display = 'flex';
  324. // Move canvas to the full viewer container
  325. this.fullViewerContainer.appendChild( canvasData.domElement );
  326. canvasData.domElement.style.width = '100%';
  327. canvasData.domElement.style.height = '100%';
  328. // Resize canvas to fit full viewer container
  329. const rect = this.fullViewerContainer.getBoundingClientRect();
  330. const contentWidth = rect.width || this.content.clientWidth;
  331. const contentHeight = rect.height || ( this.content.clientHeight - 38 ); // minus toolbar
  332. canvasData.canvasTarget.setSize( contentWidth, contentHeight );
  333. const renderer = this.inspector.getRenderer();
  334. renderer.backend.delete( canvasData.canvasTarget );
  335. }
  336. }
  337. getCanvasDataByNode( renderer, node ) {
  338. let canvasData = this.canvasNodes.get( node );
  339. if ( canvasData === undefined ) {
  340. const canvas = document.createElement( 'canvas' );
  341. const canvasTarget = new CanvasTarget( canvas );
  342. canvasTarget.setPixelRatio( window.devicePixelRatio );
  343. canvasTarget.setSize( 140, 140 );
  344. const id = node.id;
  345. const { path, name } = splitPath( splitCamelCase( node.getName() || '(unnamed)' ) );
  346. const canvasAspect = uniform( 1 );
  347. const mask = float( 1 );
  348. const target = node.context( { getUV: ( textureNode ) => {
  349. const uvData = aspectRatioUV( screenUV, textureNode, canvasAspect );
  350. const correctedUV = uvData.xy;
  351. mask.assign( uvData.z );
  352. return correctedUV;
  353. } } );
  354. let output = vec4( vec3( target ), 1 ).mul( mask );
  355. output = renderOutput( output, NoToneMapping, renderer.outputColorSpace );
  356. output = output.context( { inspector: true } );
  357. const material = new NodeMaterial();
  358. material.outputNode = output;
  359. const quad = new QuadMesh( material );
  360. quad.name = 'Viewer - ' + name;
  361. canvasData = {
  362. id,
  363. name,
  364. path,
  365. node,
  366. quad,
  367. canvasTarget,
  368. material,
  369. canvasAspect
  370. };
  371. this.canvasNodes.set( node, canvasData );
  372. }
  373. return canvasData;
  374. }
  375. update( inspector ) {
  376. const renderer = inspector.getRenderer();
  377. const nodes = inspector.getNodes();
  378. if ( nodes.length > 0 ) {
  379. if ( ! renderer.backend.isWebGPUBackend ) {
  380. inspector.resolveConsoleOnce( 'warn', 'Inspector: Viewer is only available with WebGPU.' );
  381. return;
  382. }
  383. if ( ! this.isVisible ) {
  384. this.show();
  385. }
  386. }
  387. if ( ! this.isActive ) return;
  388. const canvasDataList = nodes.map( node => this.getCanvasDataByNode( renderer, node ) );
  389. // Check if the list of nodes has changed
  390. let nodesChanged = canvasDataList.length !== this.currentDataList.length;
  391. if ( ! nodesChanged ) {
  392. for ( let i = 0; i < canvasDataList.length; i ++ ) {
  393. if ( canvasDataList[ i ].id !== this.currentDataList[ i ].id ) {
  394. nodesChanged = true;
  395. break;
  396. }
  397. }
  398. }
  399. if ( nodesChanged ) {
  400. const currentSelectedValue = this.select.value;
  401. // Clear options except the first one ('list')
  402. while ( this.select.options.length > 1 ) {
  403. this.select.remove( 1 );
  404. }
  405. // Add options for each node in canvasDataList
  406. for ( const canvasData of canvasDataList ) {
  407. const option = document.createElement( 'option' );
  408. option.value = canvasData.id;
  409. option.textContent = canvasData.path ? `${ canvasData.path } / ${ canvasData.name }` : canvasData.name;
  410. this.select.appendChild( option );
  411. }
  412. // Try to restore from saved view first on initial load
  413. let restored = false;
  414. if ( this.pendingRestoreView ) {
  415. const savedView = getItem( 'viewerLastView' );
  416. if ( savedView !== 'list' ) {
  417. for ( let i = 0; i < this.select.options.length; i ++ ) {
  418. if ( this.select.options[ i ].textContent === savedView ) {
  419. this.select.selectedIndex = i;
  420. const nodeId = this.select.options[ i ].value;
  421. this.showNodeView( nodeId );
  422. restored = true;
  423. this.pendingRestoreView = false;
  424. break;
  425. }
  426. }
  427. } else {
  428. this.pendingRestoreView = false;
  429. }
  430. }
  431. if ( ! restored ) {
  432. // Restore selection if still valid
  433. let hasSelectedValue = false;
  434. for ( let i = 0; i < this.select.options.length; i ++ ) {
  435. if ( this.select.options[ i ].value === currentSelectedValue ) {
  436. this.select.selectedIndex = i;
  437. hasSelectedValue = true;
  438. break;
  439. }
  440. }
  441. if ( ! hasSelectedValue ) {
  442. this.select.value = 'list';
  443. this.showListView();
  444. }
  445. }
  446. }
  447. // Real-time resize of active full-screen node canvas target
  448. if ( this.activeFullNodeId ) {
  449. const canvasData = canvasDataList.find( data => String( data.id ) === String( this.activeFullNodeId ) );
  450. if ( canvasData ) {
  451. const rect = this.fullViewerContainer.getBoundingClientRect();
  452. const contentWidth = rect.width || this.content.clientWidth;
  453. const contentHeight = rect.height || ( this.content.clientHeight - 38 );
  454. if ( canvasData.canvasTarget.domElement.width !== contentWidth || canvasData.canvasTarget.domElement.height !== contentHeight ) {
  455. canvasData.canvasTarget.setSize( contentWidth, contentHeight );
  456. renderer.backend.delete( canvasData.canvasTarget );
  457. }
  458. }
  459. }
  460. //
  461. const previousDataList = [ ...this.currentDataList ];
  462. // remove old
  463. for ( const canvasData of previousDataList ) {
  464. if ( this.itemLibrary.has( canvasData.id ) && canvasDataList.indexOf( canvasData ) === - 1 ) {
  465. const item = this.itemLibrary.get( canvasData.id );
  466. const parent = item.parent;
  467. parent.remove( item );
  468. if ( this.folderLibrary.has( parent.data[ 0 ] ) && parent.children.length === 0 ) {
  469. parent.parent.remove( parent );
  470. this.folderLibrary.delete( parent.data[ 0 ] );
  471. }
  472. this.itemLibrary.delete( canvasData.id );
  473. }
  474. }
  475. //
  476. const indexes = {};
  477. for ( const canvasData of canvasDataList ) {
  478. const item = this.addNodeItem( canvasData );
  479. const previousCanvasTarget = renderer.getCanvasTarget();
  480. const path = canvasData.path;
  481. if ( path ) {
  482. const folder = this.getFolder( path );
  483. if ( indexes[ path ] === undefined ) {
  484. indexes[ path ] = 0;
  485. }
  486. if ( folder.parent === null || item.parent !== folder || folder.children.indexOf( item ) !== indexes[ path ] ) {
  487. folder.add( item );
  488. }
  489. indexes[ path ] ++;
  490. } else {
  491. if ( ! item.parent ) {
  492. this.nodes.add( item );
  493. }
  494. }
  495. const rttNodes = [];
  496. const mainSize = previousCanvasTarget.getDrawingBufferSize( _size );
  497. canvasData.node.traverse( ( child ) => {
  498. if ( child.isRTTNode && child.autoResize === true ) {
  499. const oldWidth = child.width;
  500. const oldHeight = child.height;
  501. child.width = mainSize.width;
  502. child.height = mainSize.height;
  503. child.setSize( mainSize.width, mainSize.height );
  504. rttNodes.push( {
  505. node: child,
  506. oldWidth,
  507. oldHeight
  508. } );
  509. }
  510. } );
  511. const state = RendererUtils.resetRendererState( renderer );
  512. renderer.toneMapping = NoToneMapping;
  513. renderer.outputColorSpace = LinearSRGBColorSpace;
  514. renderer.setCanvasTarget( canvasData.canvasTarget );
  515. if ( canvasData.canvasAspect ) {
  516. canvasData.canvasAspect.value = canvasData.canvasTarget.domElement.width / canvasData.canvasTarget.domElement.height;
  517. }
  518. canvasData.quad.render( renderer );
  519. renderer.setCanvasTarget( previousCanvasTarget );
  520. RendererUtils.restoreRendererState( renderer, state );
  521. for ( const rtt of rttNodes ) {
  522. rtt.node.width = rtt.oldWidth;
  523. rtt.node.height = rtt.oldHeight;
  524. }
  525. }
  526. this.currentDataList = canvasDataList;
  527. }
  528. setActive( isActive ) {
  529. super.setActive( isActive );
  530. }
  531. saveLastView() {
  532. const selectedValue = this.select.value;
  533. if ( selectedValue === 'list' ) {
  534. setItem( 'viewerLastView', 'list' );
  535. } else {
  536. const selectedOption = this.select.options[ this.select.selectedIndex ];
  537. if ( selectedOption ) {
  538. setItem( 'viewerLastView', selectedOption.textContent );
  539. }
  540. }
  541. }
  542. }
  543. export { Viewer };
粤ICP备19079148号