Settings.js 5.7 KB

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