bridge.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. /**
  2. * This script injected by the installed three.js developer
  3. * tools extension.
  4. */
  5. // Only initialize if not already initialized
  6. if (!window.__THREE_DEVTOOLS__) {
  7. // Create our custom EventTarget with logging
  8. class DevToolsEventTarget extends EventTarget {
  9. constructor() {
  10. super();
  11. this._ready = false;
  12. this._backlog = [];
  13. this.objects = new Map();
  14. }
  15. addEventListener(type, listener, options) {
  16. super.addEventListener(type, listener, options);
  17. // If this is the first listener for a type, and we have backlogged events,
  18. // check if we should process them
  19. if (type !== 'devtools-ready' && this._backlog.length > 0) {
  20. this.dispatchEvent(new CustomEvent('devtools-ready'));
  21. }
  22. }
  23. dispatchEvent(event) {
  24. if (this._ready || event.type === 'devtools-ready') {
  25. if (event.type === 'devtools-ready') {
  26. this._ready = true;
  27. const backlog = this._backlog;
  28. this._backlog = [];
  29. backlog.forEach(e => super.dispatchEvent(e));
  30. }
  31. return super.dispatchEvent(event);
  32. } else {
  33. this._backlog.push(event);
  34. return false; // Return false to indicate synchronous handling
  35. }
  36. }
  37. reset() {
  38. console.log('DevTools: Resetting state');
  39. // Clear all monitoring intervals
  40. this.objects.forEach((obj, uuid) => {
  41. if (obj.isRenderer || obj.isScene) {
  42. const interval = monitoringIntervals.get(obj);
  43. if (interval) {
  44. clearInterval(interval);
  45. monitoringIntervals.delete(obj);
  46. }
  47. }
  48. });
  49. // Clear objects map
  50. this.objects.clear();
  51. // Clear backlog
  52. this._backlog = [];
  53. // Reset ready state
  54. this._ready = false;
  55. // Clear observed arrays
  56. observedScenes.length = 0;
  57. observedRenderers.length = 0;
  58. }
  59. }
  60. // Create and expose the __THREE_DEVTOOLS__ object
  61. const devTools = new DevToolsEventTarget();
  62. devTools.isVisible = true; // Initialize visibility state
  63. Object.defineProperty(window, '__THREE_DEVTOOLS__', {
  64. value: devTools,
  65. configurable: false,
  66. enumerable: true,
  67. writable: false
  68. });
  69. // Store monitoring intervals without polluting objects
  70. const monitoringIntervals = new WeakMap();
  71. // Declare arrays for tracking observed objects
  72. const observedScenes = [];
  73. const observedRenderers = [];
  74. // Function to get renderer data
  75. function getRendererData(renderer) {
  76. try {
  77. const webglInfo = getWebGLInfo(renderer);
  78. const data = {
  79. uuid: renderer.uuid || generateUUID(),
  80. type: 'WebGLRenderer',
  81. name: '',
  82. visible: true,
  83. isScene: false,
  84. isObject3D: false,
  85. isCamera: false,
  86. isLight: false,
  87. isMesh: false,
  88. isRenderer: true,
  89. parent: null,
  90. children: [],
  91. properties: {
  92. width: renderer.domElement ? renderer.domElement.clientWidth : 0,
  93. height: renderer.domElement ? renderer.domElement.clientHeight : 0,
  94. drawingBufferWidth: renderer.domElement ? renderer.domElement.width : 0,
  95. drawingBufferHeight: renderer.domElement ? renderer.domElement.height : 0,
  96. alpha: renderer.alpha || false,
  97. antialias: renderer.antialias || false,
  98. autoClear: renderer.autoClear,
  99. autoClearColor: renderer.autoClearColor,
  100. autoClearDepth: renderer.autoClearDepth,
  101. autoClearStencil: renderer.autoClearStencil,
  102. localClippingEnabled: renderer.localClippingEnabled,
  103. physicallyCorrectLights: renderer.physicallyCorrectLights,
  104. outputColorSpace: renderer.outputColorSpace,
  105. toneMapping: renderer.toneMapping,
  106. toneMappingExposure: renderer.toneMappingExposure,
  107. shadowMapEnabled: renderer.shadowMap ? renderer.shadowMap.enabled : false,
  108. shadowMapType: renderer.shadowMap ? renderer.shadowMap.type : 'None',
  109. info: {
  110. render: {
  111. frame: renderer.info.render.frame,
  112. calls: renderer.info.render.calls,
  113. triangles: renderer.info.render.triangles,
  114. points: renderer.info.render.points,
  115. lines: renderer.info.render.lines,
  116. geometries: renderer.info.render.geometries,
  117. sprites: renderer.info.render.sprites
  118. },
  119. memory: {
  120. geometries: renderer.info.memory.geometries,
  121. textures: renderer.info.memory.textures,
  122. programs: renderer.info.programs ? renderer.info.programs.length : 0,
  123. renderLists: renderer.info.memory.renderLists,
  124. renderTargets: renderer.info.memory.renderTargets
  125. },
  126. webgl: webglInfo || {
  127. version: 'unknown',
  128. gpu: 'unknown',
  129. vendor: 'unknown',
  130. maxTextures: 'unknown',
  131. maxAttributes: 'unknown',
  132. maxTextureSize: 'unknown',
  133. maxCubemapSize: 'unknown'
  134. }
  135. }
  136. }
  137. };
  138. return data;
  139. } catch (error) {
  140. console.warn('DevTools: Error getting renderer data:', error);
  141. return null;
  142. }
  143. }
  144. // Function to get object hierarchy
  145. function getObjectData(obj) {
  146. try {
  147. // Special case for WebGLRenderer
  148. if (obj.isWebGLRenderer === true) {
  149. return getRendererData(obj);
  150. }
  151. // Get descriptive name for the object
  152. let name = obj.name || obj.type || obj.constructor.name;
  153. if (obj.isMesh) {
  154. const geoType = obj.geometry ? obj.geometry.type : 'Unknown';
  155. const matType = obj.material ?
  156. (Array.isArray(obj.material) ?
  157. obj.material.map(m => m.type).join(', ') :
  158. obj.material.type) :
  159. 'Unknown';
  160. name = `${name} <span class="object-details">${geoType} ${matType}</span>`;
  161. }
  162. const data = {
  163. uuid: obj.uuid,
  164. type: obj.type || obj.constructor.name,
  165. name: name,
  166. visible: obj.visible !== undefined ? obj.visible : true,
  167. isScene: obj.isScene === true,
  168. isObject3D: obj.isObject3D === true,
  169. isCamera: obj.isCamera === true,
  170. isLight: obj.isLight === true,
  171. isMesh: obj.isMesh === true,
  172. isRenderer: obj.isWebGLRenderer === true,
  173. parent: obj.parent ? obj.parent.uuid : null,
  174. children: obj.children ? obj.children.map(child => child.uuid) : []
  175. };
  176. return data;
  177. } catch (error) {
  178. console.warn('DevTools: Error getting object data:', error);
  179. return null;
  180. }
  181. }
  182. // Generate a UUID for objects that don't have one
  183. function generateUUID() {
  184. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  185. const r = Math.random() * 16 | 0;
  186. const v = c === 'x' ? r : (r & 0x3 | 0x8);
  187. return v.toString(16);
  188. });
  189. }
  190. // Listen for Three.js registration
  191. devTools.addEventListener('register', (event) => {
  192. console.log('DevTools: Three.js registered with revision:', event.detail.revision);
  193. dispatchEvent('register', event.detail);
  194. });
  195. // Listen for object observations
  196. devTools.addEventListener('observe', (event) => {
  197. const obj = event.detail;
  198. if (!obj) {
  199. console.warn('DevTools: Received observe event with null/undefined detail');
  200. return;
  201. }
  202. console.log('DevTools: Received object:', {
  203. type: obj.type || obj.constructor.name,
  204. isWebGLRenderer: obj.isWebGLRenderer === true,
  205. hasUUID: !!obj.uuid
  206. });
  207. // Generate UUID if needed (especially for WebGLRenderer)
  208. if (!obj.uuid) {
  209. obj.uuid = generateUUID();
  210. console.log('DevTools: Generated UUID for object:', obj.uuid);
  211. }
  212. // Skip if already registered
  213. if (devTools.objects.has(obj.uuid)) {
  214. console.log('DevTools: Object already registered:', obj.uuid);
  215. return;
  216. }
  217. console.log('DevTools: Found Three.js object:', obj.type || obj.constructor.name);
  218. // Get data for this object
  219. const data = getObjectData(obj);
  220. if (data) {
  221. console.log('DevTools: Got object data:', data);
  222. // If this is a renderer, start periodic updates
  223. if (obj.isWebGLRenderer) {
  224. console.log('DevTools: Starting periodic updates for renderer:', obj.uuid);
  225. data.properties = getRendererProperties(obj);
  226. observedRenderers.push(obj);
  227. startRendererMonitoring(obj);
  228. }
  229. // Store the object data
  230. devTools.objects.set(obj.uuid, data);
  231. dispatchEvent('observe', data);
  232. // If this is a scene, store the reference and traverse its children
  233. if (obj.isScene) {
  234. console.log('DevTools: Traversing scene children');
  235. // Store the scene reference locally
  236. observedScenes.push(obj);
  237. // First observe all existing children
  238. const processedObjects = new Set([obj.uuid]);
  239. function observeObject(object) {
  240. if (!processedObjects.has(object.uuid)) {
  241. processedObjects.add(object.uuid);
  242. const objectData = getObjectData(object);
  243. if (objectData) {
  244. devTools.objects.set(object.uuid, objectData);
  245. dispatchEvent('observe', objectData);
  246. }
  247. // Process children
  248. object.children.forEach(child => observeObject(child));
  249. }
  250. }
  251. // Process all children
  252. obj.children.forEach(child => observeObject(child));
  253. // Start monitoring for changes
  254. startSceneMonitoring(obj);
  255. }
  256. }
  257. });
  258. // Function to get renderer properties
  259. function getRendererProperties(renderer) {
  260. const webglInfo = getWebGLInfo(renderer);
  261. return {
  262. width: renderer.domElement ? renderer.domElement.clientWidth : 0,
  263. height: renderer.domElement ? renderer.domElement.clientHeight : 0,
  264. drawingBufferWidth: renderer.domElement ? renderer.domElement.width : 0,
  265. drawingBufferHeight: renderer.domElement ? renderer.domElement.height : 0,
  266. alpha: renderer.alpha || false,
  267. antialias: renderer.antialias || false,
  268. autoClear: renderer.autoClear,
  269. autoClearColor: renderer.autoClearColor,
  270. autoClearDepth: renderer.autoClearDepth,
  271. autoClearStencil: renderer.autoClearStencil,
  272. localClippingEnabled: renderer.localClippingEnabled,
  273. physicallyCorrectLights: renderer.physicallyCorrectLights,
  274. outputColorSpace: renderer.outputColorSpace,
  275. toneMapping: renderer.toneMapping,
  276. toneMappingExposure: renderer.toneMappingExposure,
  277. shadowMapEnabled: renderer.shadowMap ? renderer.shadowMap.enabled : false,
  278. shadowMapType: renderer.shadowMap ? renderer.shadowMap.type : 'None',
  279. info: {
  280. render: {
  281. frame: renderer.info.render.frame,
  282. calls: renderer.info.render.calls,
  283. triangles: renderer.info.render.triangles,
  284. points: renderer.info.render.points,
  285. lines: renderer.info.render.lines,
  286. geometries: renderer.info.render.geometries,
  287. sprites: renderer.info.render.sprites
  288. },
  289. memory: {
  290. geometries: renderer.info.memory.geometries,
  291. textures: renderer.info.memory.textures,
  292. programs: renderer.info.programs ? renderer.info.programs.length : 0,
  293. renderLists: renderer.info.memory.renderLists,
  294. renderTargets: renderer.info.memory.renderTargets
  295. },
  296. webgl: webglInfo || {
  297. version: 'unknown',
  298. gpu: 'unknown',
  299. vendor: 'unknown',
  300. maxTextures: 'unknown',
  301. maxAttributes: 'unknown',
  302. maxTextureSize: 'unknown',
  303. maxCubemapSize: 'unknown'
  304. }
  305. }
  306. };
  307. }
  308. // Function to start renderer monitoring
  309. function startRendererMonitoring(renderer) {
  310. // Clear any existing monitoring
  311. const existingInterval = monitoringIntervals.get(renderer);
  312. if (existingInterval) {
  313. clearInterval(existingInterval);
  314. }
  315. // Function to monitor renderer properties
  316. function monitorRendererProperties() {
  317. try {
  318. // Skip updates if devtools is not visible
  319. if ( ! devTools.isVisible ) {
  320. // console.log('DevTools: Panel not visible, skipping renderer update'); // Optional debug log
  321. return;
  322. }
  323. const data = devTools.objects.get( renderer.uuid );
  324. if ( ! data ) {
  325. clearInterval( intervalId );
  326. monitoringIntervals.delete( renderer );
  327. return;
  328. }
  329. const oldProperties = data.properties;
  330. const newProperties = getRendererProperties( renderer );
  331. // Compare relevant properties directly for changes
  332. const changed = (
  333. !oldProperties || // Update if old properties don't exist yet
  334. oldProperties.width !== newProperties.width ||
  335. oldProperties.height !== newProperties.height ||
  336. oldProperties.drawingBufferWidth !== newProperties.drawingBufferWidth ||
  337. oldProperties.drawingBufferHeight !== newProperties.drawingBufferHeight ||
  338. JSON.stringify(oldProperties.info?.render) !== JSON.stringify(newProperties.info?.render) || // Compare render stats
  339. JSON.stringify(oldProperties.info?.memory) !== JSON.stringify(newProperties.info?.memory) // Compare memory stats
  340. // Add other comparisons if needed, or use full stringify as fallback:
  341. // || JSON.stringify(oldProperties) !== JSON.stringify(newProperties)
  342. );
  343. if ( changed ) {
  344. console.log('DevTools: Renderer properties changed, dispatching update for', renderer.uuid); // Log dispatched updates
  345. data.properties = newProperties;
  346. dispatchEvent( 'update', data );
  347. } else {
  348. // console.log('DevTools: Renderer properties unchanged for', renderer.uuid); // Optional: for debugging
  349. }
  350. } catch ( error ) {
  351. // If we get an "Extension context invalidated" error, stop monitoring
  352. if ( error.message.includes( 'Extension context invalidated' ) ) {
  353. clearInterval( intervalId );
  354. monitoringIntervals.delete( renderer );
  355. devTools.reset();
  356. return;
  357. }
  358. console.warn( 'DevTools: Error in renderer monitoring:', error );
  359. }
  360. }
  361. const intervalId = setInterval(monitorRendererProperties, 1000);
  362. monitoringIntervals.set(renderer, intervalId);
  363. }
  364. // Start periodic renderer checks
  365. console.log('DevTools: Starting periodic renderer checks');
  366. // Function to check if bridge is available
  367. function checkBridgeAvailability() {
  368. const hasDevTools = window.hasOwnProperty('__THREE_DEVTOOLS__');
  369. const devToolsValue = window.__THREE_DEVTOOLS__;
  370. // If we have devtools and we're interactive or complete, trigger ready
  371. if (hasDevTools && devToolsValue && (document.readyState === 'interactive' || document.readyState === 'complete')) {
  372. devTools.dispatchEvent(new CustomEvent('devtools-ready'));
  373. }
  374. }
  375. // Watch for readyState changes
  376. document.addEventListener('readystatechange', () => {
  377. if (document.readyState === 'loading') {
  378. devTools.reset();
  379. }
  380. checkBridgeAvailability();
  381. });
  382. // Watch for page unload to reset state
  383. window.addEventListener('beforeunload', () => {
  384. devTools.reset();
  385. });
  386. // Listen for messages from the content script
  387. window.addEventListener('message', function(event) {
  388. // Only accept messages from the same frame
  389. if (event.source !== window) return;
  390. const message = event.data;
  391. if (!message || message.id !== 'three-devtools') return;
  392. // Handle traverse request
  393. if (message.name === 'traverse' && message.uuid) {
  394. const scene = Array.from(devTools.objects.values())
  395. .find(obj => obj.uuid === message.uuid && obj.isScene);
  396. if (scene) {
  397. console.log('DevTools: Re-traversing scene:', scene.uuid);
  398. // Find the actual scene object in the page
  399. const actualScene = findObjectByUUID(message.uuid);
  400. if (actualScene) {
  401. reloadSceneObjects(actualScene);
  402. }
  403. }
  404. }
  405. // Handle reload-scene request
  406. else if (message.name === 'reload-scene' && message.uuid) {
  407. console.log('DevTools: Received reload request for scene:', message.uuid);
  408. const actualScene = findObjectByUUID(message.uuid);
  409. if (actualScene) {
  410. reloadSceneObjects(actualScene);
  411. } else {
  412. console.warn('DevTools: Could not find scene for reload:', message.uuid);
  413. }
  414. }
  415. // Handle visibility toggle
  416. else if (message.name === 'visibility' && message.uuid !== undefined) {
  417. toggleVisibility(message.uuid, message.visible);
  418. }
  419. // Handle visibility update from panel (via content script)
  420. else if ( message.name === 'panel-visibility' ) {
  421. devTools.isVisible = message.value;
  422. // console.log( 'DevTools: Visibility set to', devTools.isVisible ); // Optional debug log
  423. }
  424. });
  425. // Helper function to find a Three.js object by UUID
  426. function findObjectByUUID(uuid) {
  427. console.log('DevTools: Finding object by UUID:', uuid);
  428. // Check for scenes we've observed
  429. const sceneData = Array.from(devTools.objects.values())
  430. .find(obj => obj.uuid === uuid && obj.isScene);
  431. if (sceneData) {
  432. // For scenes accessed through observe events, they are already available
  433. // through the scene object reference passed to the observe handler
  434. for (const observedScene of observedScenes) {
  435. if (observedScene && observedScene.uuid === uuid) {
  436. console.log('DevTools: Found scene in observed scenes');
  437. return observedScene;
  438. }
  439. }
  440. }
  441. console.warn('DevTools: Could not find object with UUID:', uuid);
  442. return null;
  443. }
  444. function dispatchEvent(type, detail) {
  445. try {
  446. window.postMessage({
  447. id: 'three-devtools',
  448. type: type,
  449. detail: detail
  450. }, '*');
  451. } catch (error) {
  452. // If we get an "Extension context invalidated" error, stop all monitoring
  453. if (error.message.includes('Extension context invalidated')) {
  454. console.log('DevTools: Extension context invalidated, stopping monitoring');
  455. devTools.reset();
  456. return;
  457. }
  458. console.warn('DevTools: Error dispatching event:', error);
  459. }
  460. }
  461. function getWebGLInfo(renderer) {
  462. if (!renderer || !renderer.domElement) return null;
  463. const gl = renderer.domElement.getContext('webgl2') || renderer.domElement.getContext('webgl');
  464. if (!gl) return null;
  465. return {
  466. version: gl.getParameter(gl.VERSION),
  467. gpu: gl.getParameter(gl.RENDERER),
  468. vendor: gl.getParameter(gl.VENDOR),
  469. maxTextures: gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS),
  470. maxAttributes: gl.getParameter(gl.MAX_VERTEX_ATTRIBS),
  471. maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
  472. maxCubemapSize: gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE)
  473. };
  474. }
  475. // Add visibility toggle function
  476. function toggleVisibility(uuid, visible) {
  477. // Update our local state
  478. const obj = devTools.objects.get(uuid);
  479. if (!obj) return;
  480. obj.visible = visible;
  481. console.log('DevTools: Setting visibility of', obj.type || obj.constructor.name, 'to', visible);
  482. // Find the actual Three.js object using our observed scenes
  483. if (observedScenes.length > 0) {
  484. for (const scene of observedScenes) {
  485. let found = false;
  486. scene.traverse((object) => {
  487. if (object.uuid === uuid) {
  488. object.visible = visible;
  489. // If it's a light, update its helper visibility too
  490. if (object.isLight && object.helper) {
  491. object.helper.visible = visible;
  492. }
  493. found = true;
  494. console.log('DevTools: Updated visibility in scene object');
  495. }
  496. });
  497. if (found) break;
  498. }
  499. } else {
  500. console.warn('DevTools: No observed scenes found for visibility toggle');
  501. }
  502. }
  503. // Function to start scene monitoring
  504. function startSceneMonitoring(scene) {
  505. // Clear any existing monitoring
  506. const existingInterval = monitoringIntervals.get(scene);
  507. if (existingInterval) {
  508. clearInterval(existingInterval);
  509. }
  510. // Set up monitoring interval
  511. const intervalId = setInterval(() => {
  512. try {
  513. // Clear existing objects except renderers and the scene itself
  514. devTools.objects.forEach((obj, uuid) => {
  515. if (!obj.isRenderer && uuid !== scene.uuid) {
  516. devTools.objects.delete(uuid);
  517. dispatchEvent('remove', { uuid });
  518. }
  519. });
  520. // Traverse and recreate the entire object list
  521. function traverseScene(object) {
  522. const objectData = getObjectData(object);
  523. if (objectData) {
  524. devTools.objects.set(object.uuid, objectData);
  525. dispatchEvent('observe', objectData);
  526. // Traverse children
  527. object.children.forEach(child => traverseScene(child));
  528. }
  529. }
  530. // Start traversal from scene root
  531. traverseScene(scene);
  532. } catch (error) {
  533. // If we get an "Extension context invalidated" error, stop monitoring
  534. if (error.message.includes('Extension context invalidated')) {
  535. clearInterval(intervalId);
  536. monitoringIntervals.delete(scene);
  537. devTools.reset();
  538. return;
  539. }
  540. console.warn('DevTools: Error in scene monitoring:', error);
  541. }
  542. }, 1000);
  543. monitoringIntervals.set(scene, intervalId);
  544. // Clean up monitoring when scene is disposed
  545. const originalDispose = scene.dispose;
  546. scene.dispose = function() {
  547. const intervalId = monitoringIntervals.get(this);
  548. if (intervalId) {
  549. clearInterval(intervalId);
  550. monitoringIntervals.delete(this);
  551. }
  552. if (originalDispose) {
  553. originalDispose.call(this);
  554. }
  555. };
  556. }
  557. // Function to manually reload scene objects
  558. function reloadSceneObjects(scene) {
  559. console.log('DevTools: Manually reloading scene objects for scene:', scene.uuid);
  560. // Track new objects to avoid duplicates
  561. const processedObjects = new Set();
  562. // Recursively observe all objects
  563. function observeObject(object) {
  564. if (!processedObjects.has(object.uuid)) {
  565. processedObjects.add(object.uuid);
  566. console.log('DevTools: Processing object during reload:', object.type || object.constructor.name, object.uuid);
  567. // Get object data
  568. const objectData = getObjectData(object);
  569. if (objectData) {
  570. if (devTools.objects.has(object.uuid)) {
  571. // Update existing object
  572. const existingData = devTools.objects.get(object.uuid);
  573. existingData.children = objectData.children;
  574. dispatchEvent('update', existingData);
  575. } else {
  576. // Add new object
  577. devTools.objects.set(object.uuid, objectData);
  578. dispatchEvent('observe', objectData);
  579. console.log('DevTools: New object observed during reload:', object.type || object.constructor.name);
  580. }
  581. }
  582. // Process children recursively
  583. if (object.children && object.children.length > 0) {
  584. console.log('DevTools: Processing', object.children.length, 'children of', object.type || object.constructor.name);
  585. object.children.forEach(child => observeObject(child));
  586. }
  587. }
  588. }
  589. // Start with the scene itself to ensure everything is traversed
  590. observeObject(scene);
  591. console.log('DevTools: Scene reload complete. Processed', processedObjects.size, 'objects');
  592. }
  593. } else {
  594. console.log('DevTools: Bridge already initialized');
  595. }
粤ICP备19079148号