Settings.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import { Parameters } from './Parameters.js';
  2. import { WebGPURenderer, WebGLBackend, Node } from 'three/webgpu';
  3. import { getItem, setItem } from '../Inspector.js';
  4. const _EXTENSIONS_PATH = '../extensions/extensions.json';
  5. const _init = WebGPURenderer.prototype.init;
  6. function forceWebGL( enable ) {
  7. if ( enable ) {
  8. WebGPURenderer.prototype.init = async function () {
  9. if ( this.backend.isWebGLBackend !== true ) {
  10. const parameters = this.backend.parameters;
  11. this.backend = new WebGLBackend( parameters );
  12. }
  13. return _init.call( this );
  14. };
  15. } else {
  16. WebGPURenderer.prototype.init = _init;
  17. }
  18. }
  19. let _state = null;
  20. function _loadState() {
  21. if ( _state !== null ) return _state;
  22. const settings = getItem( 'settings' );
  23. _state = {
  24. forceWebGL: settings.forceWebGL !== undefined ? settings.forceWebGL : false,
  25. captureStackTrace: settings.captureStackTrace !== undefined ? settings.captureStackTrace : false,
  26. activeExtensions: settings.activeExtensions !== undefined ? settings.activeExtensions : {},
  27. storage: settings.storage !== undefined ? settings.storage : 'url'
  28. };
  29. if ( _state.forceWebGL ) {
  30. forceWebGL( true );
  31. }
  32. if ( _state.captureStackTrace ) {
  33. Node.captureStackTrace = true;
  34. }
  35. return _state;
  36. }
  37. function _saveState() {
  38. setItem( 'settings', {
  39. forceWebGL: _state.forceWebGL,
  40. captureStackTrace: _state.captureStackTrace,
  41. activeExtensions: _state.activeExtensions,
  42. storage: _state.storage
  43. } );
  44. }
  45. _loadState();
  46. //
  47. class Settings extends Parameters {
  48. constructor() {
  49. super( { name: 'Settings' } );
  50. this.extensions = {};
  51. const currentState = _loadState();
  52. // UI
  53. const rendererGroup = this.createGroup( 'Renderer' );
  54. rendererGroup.add( currentState, 'forceWebGL' ).name( 'Force WebGL' ).onChange( ( enable ) => {
  55. forceWebGL( enable );
  56. _saveState();
  57. location.reload();
  58. } );
  59. rendererGroup.add( currentState, 'captureStackTrace' ).name( 'Capture Stack Trace' ).onChange( ( enable ) => {
  60. Node.captureStackTrace = enable;
  61. _saveState();
  62. location.reload();
  63. } );
  64. }
  65. init() {
  66. const extensionsGroup = this.createGroup( 'Extensions' );
  67. const storageGroup = this.createGroup( 'Storage' );
  68. const currentState = _loadState();
  69. storageGroup.add( currentState, 'storage', { 'URL Session': 'url', 'Keep across Origin': 'origin' } )
  70. .name( 'Save Settings' )
  71. .onChange( () => {
  72. _saveState();
  73. } ).info( `
  74. Defines how the **Inspector** preferences and states are stored in the browser.
  75. **URL Session**
  76. Saves state based on the exact URL. It will reset the settings whenever the URL changes.
  77. **Keep across Origin**
  78. Shares the same state across any page within the current origin.` );
  79. storageGroup.add( {
  80. clear: () => {
  81. localStorage.removeItem( 'threejs-inspector' );
  82. location.reload();
  83. }
  84. }, 'clear' ).name( 'Clear Settings' );
  85. this._getExtensions().then( extensions => {
  86. for ( const extension of extensions ) {
  87. extension.active = false;
  88. extension.loaded = false;
  89. extension.tab = null;
  90. this.extensions[ extension.name ] = extension;
  91. extension.ui = extensionsGroup.add( { [ extension.name ]: false }, extension.name ).onChange( async ( value ) => {
  92. this.setActiveExtension( extension.name, value );
  93. // User preference
  94. if ( value ) {
  95. _state.activeExtensions[ extension.name ] = {
  96. name: extension.name,
  97. url: extension.url
  98. };
  99. } else {
  100. delete _state.activeExtensions[ extension.name ];
  101. }
  102. //
  103. this._updateExtensionUI( extension );
  104. _saveState();
  105. } );
  106. // Set user-defined state
  107. if ( _state.activeExtensions[ extension.name ] !== undefined ) {
  108. extension.ui.setValue( true );
  109. }
  110. }
  111. } );
  112. }
  113. async setActiveExtension( name, value ) {
  114. const extension = this.extensions[ name ];
  115. const inspector = this.inspector;
  116. if ( extension ) {
  117. if ( value ) {
  118. await this._loadExtension( inspector, extension );
  119. } else {
  120. await this._unloadExtension( inspector, extension );
  121. }
  122. }
  123. }
  124. _updateExtensionUI( extension ) {
  125. const forceActive = extension.active && _state.activeExtensions[ extension.name ] === undefined;
  126. if ( forceActive ) {
  127. extension.ui.checkbox.checked = true;
  128. extension.ui.domElement.style.setProperty( '--accent-color', 'var(--color-green)' );
  129. } else {
  130. extension.ui.domElement.style.removeProperty( '--accent-color' );
  131. }
  132. }
  133. async _unloadExtension( inspector, extension ) {
  134. if ( extension.active === false ) return;
  135. //
  136. inspector.removeTab( extension.tab );
  137. extension.active = false;
  138. extension.loaded = false;
  139. extension.tab = null;
  140. this._updateExtensionUI( extension );
  141. this.dispatchEvent( { type: 'extensionremoved', name: extension.name } );
  142. }
  143. async _loadExtension( inspector, extension ) {
  144. if ( extension.active === true ) return;
  145. //
  146. extension.active = true;
  147. const extUrl = new URL( extension.url, new URL( _EXTENSIONS_PATH, import.meta.url ) ).href;
  148. const module = await import( extUrl );
  149. const keys = Object.keys( module );
  150. const ExtensionClass = module[ keys[ 0 ] ];
  151. const extensionTab = new ExtensionClass();
  152. inspector.addTab( extensionTab );
  153. extension.loaded = true;
  154. extension.tab = extensionTab;
  155. this._updateExtensionUI( extension );
  156. this.dispatchEvent( { type: 'extensionadded', name: extension.name, tab: extensionTab } );
  157. }
  158. async _getExtensions() {
  159. const url = new URL( _EXTENSIONS_PATH, import.meta.url );
  160. const extensions = await fetch( url ).then( res => res.json() );
  161. return extensions;
  162. }
  163. }
  164. export { Settings };
粤ICP备19079148号