Browse Source

Refresh the scene graph once a second.

Mr.doob 10 months ago
parent
commit
425407c0cf
4 changed files with 79 additions and 71 deletions
  1. 1 4
      devtools/background.js
  2. 30 45
      devtools/bridge.js
  3. 2 2
      devtools/content-script.js
  4. 46 20
      devtools/panel/panel.js

+ 1 - 4
devtools/background.js

@@ -10,10 +10,7 @@ chrome.runtime.onConnect.addListener(port => {
 		if (message.name === 'init') {
 			tabId = message.tabId;
 			connections.set(tabId, port);
-		} else if ((message.name === 'traverse' || message.name === 'reload-scene') && tabId) {
-			// Forward traverse or reload request to content script
-			chrome.tabs.sendMessage(tabId, message);
-		} else if (message.name === 'request-initial-state' && tabId) {
+		} else if (message.name === 'request-state' && tabId) {
 			chrome.tabs.sendMessage(tabId, message);
 		} else if (tabId === undefined) {
 			console.warn('Background: Message received from panel before init:', message);

+ 30 - 45
devtools/bridge.js

@@ -69,6 +69,7 @@ if (!window.__THREE_DEVTOOLS__) {
 	// Declare arrays for tracking observed objects
 	const observedScenes = [];
 	const observedRenderers = [];
+	const sceneObjectCountCache = new Map(); // Cache for object counts per scene
 
 	// Function to get renderer data
 	function getRendererData(renderer) {
@@ -347,41 +348,24 @@ if (!window.__THREE_DEVTOOLS__) {
 		if (!message || message.id !== 'three-devtools') return;
 
 		// Handle request for initial state from panel
-		if ( message.name === 'request-initial-state' ) {
-			for (const observedRenderer of observedRenderers) {
-				const data = getObjectData(observedRenderer);
-				if (data) {
-					data.properties = getRendererProperties(observedRenderer);
-					dispatchEvent('renderer', data);
-				}
-			}
-			for (const observedScene of observedScenes) {
-				reloadSceneObjects(observedScene);
-			}
+		if ( message.name === 'request-state' ) {
+			sendState();
 		}
 	});
 
-	// Helper function to find a Three.js object by UUID
-	function findObjectByUUID(uuid) {
-		console.log('DevTools: Finding object by UUID:', uuid);
-		
-		// Check for scenes we've observed
-		const sceneData = Array.from(devTools.objects.values())
-			.find(obj => obj.uuid === uuid && obj.isScene);
-		
-		if (sceneData) {
-			// For scenes accessed through observe events, they are already available
-			// through the scene object reference passed to the observe handler
-			for (const observedScene of observedScenes) {
-				if (observedScene && observedScene.uuid === uuid) {
-					console.log('DevTools: Found scene in observed scenes');
-					return observedScene;
-				}
+	function sendState() {
+		// Send current renderers
+		for (const observedRenderer of observedRenderers) {
+			const data = getObjectData(observedRenderer);
+			if (data) {
+				data.properties = getRendererProperties(observedRenderer);
+				dispatchEvent('renderer', data);
 			}
 		}
-		
-		console.warn('DevTools: Could not find object with UUID:', uuid);
-		return null;
+		// Send current scenes
+		for (const observedScene of observedScenes) {
+			reloadSceneObjects(observedScene);
+		}
 	}
 
 	function dispatchEvent(type, detail) {
@@ -420,9 +404,8 @@ if (!window.__THREE_DEVTOOLS__) {
 	}
 
 	// Function to manually reload scene objects
-	function reloadSceneObjects(scene) {
-		// console.log('DevTools: Manually reloading scene objects for scene:', scene.uuid);
-				
+	function reloadSceneObjects(scene) {	
+					
 		const batchObjects = [];
 		
 		// Recursively observe all objects, collect data, update local cache
@@ -448,18 +431,20 @@ if (!window.__THREE_DEVTOOLS__) {
 		
 		// Start traversal from the scene itself
 		observeAndBatchObject(scene);
-		
-		// Dispatch the batch update for the panel as 'scene'
-		dispatchEvent('scene', { sceneUuid: scene.uuid, objects: batchObjects });
 
-		// TODO: Optionally, detect and dispatch 'remove' events here?
-		// For now, panel handles removal implicitly based on batch content.
-		
-		// console.log('DevTools: Scene reload batch dispatched. Processed', processedUUIDs.size, 'objects');
+		// --- Caching Logic ---
+		const currentObjectCount = batchObjects.length;
+		const previousObjectCount = sceneObjectCountCache.get(scene.uuid);
+
+		if (currentObjectCount !== previousObjectCount) {
+			console.log(`DevTools: Scene ${scene.uuid} count changed (${previousObjectCount} -> ${currentObjectCount}), dispatching update.`);
+			// Dispatch the batch update for the panel as 'scene'
+			dispatchEvent('scene', { sceneUuid: scene.uuid, objects: batchObjects });
+			// Update the cache
+			sceneObjectCountCache.set(scene.uuid, currentObjectCount);
+		} else {
+			console.log(`DevTools: Scene ${scene.uuid} count unchanged (${currentObjectCount}), skipping dispatch.`);
+		}
 	}
 
-} else {
-
-	// console.log('DevTools: Bridge already initialized');
-
-} 
+}

+ 2 - 2
devtools/content-script.js

@@ -174,8 +174,8 @@ function handleIframeMessage( event ) {
 function handleBackgroundMessage( message, sender, sendResponse ) {
 
 	// Check if the message is one we need to forward to the bridge
-	// Only forward request-initial-state now
-	if ( message.name === 'request-initial-state' ) {
+	// Only forward request-state now
+	if ( message.name === 'request-state' ) {
 
 		// console.log( 'Content script: Forwarding message to bridge:', message.name );
 		// Ensure the message has the correct ID before forwarding to the page

+ 46 - 20
devtools/panel/panel.js

@@ -21,10 +21,23 @@ backgroundPageConnection.postMessage({
 
 // Request the initial state from the bridge script
 backgroundPageConnection.postMessage({
-	name: 'request-initial-state',
-	tabId: chrome.devtools.inspectedWindow.tabId // Include tabId for routing
+	name: 'request-state',
+	tabId: chrome.devtools.inspectedWindow.tabId
 });
 
+let intervalId = setInterval(() => {
+	backgroundPageConnection.postMessage({
+		name: 'request-state',
+		tabId: chrome.devtools.inspectedWindow.tabId
+	});
+}, 1000);
+
+backgroundPageConnection.onDisconnect.addListener(() => {
+	console.log('Panel: Connection to background page lost');
+	clearInterval(intervalId);
+	clearState();
+});	
+
 // console.log('Connected to background page with tab ID:', chrome.devtools.inspectedWindow.tabId);
 
 // Store renderer collapse states
@@ -44,7 +57,6 @@ function clearState() {
 
 // Listen for messages from the background page
 backgroundPageConnection.onMessage.addListener(function (message) {
-	// console.log('Panel received message:', message);
 	if (message.id === 'three-devtools') {
 		handleThreeEvent(message);
 	}
@@ -83,32 +95,46 @@ function handleThreeEvent(message) {
 			const { sceneUuid, objects: batchObjects } = message.detail;
 			console.log('Panel: Received scene batch for', sceneUuid, 'with', batchObjects.length, 'objects');
 
-			// Clear existing objects belonging to this scene (or previously known descendants)
-			// This is a simplified removal, assuming objects don't move between scenes
-			const objectsToRemove = [];
+			// 1. Identify UUIDs in the new batch
+			const newObjectUuids = new Set(batchObjects.map(obj => obj.uuid));
+
+			// 2. Identify current object UUIDs associated with this scene that are NOT renderers
+			const currentSceneObjectUuids = new Set();
 			state.objects.forEach((obj, uuid) => {
-				if (!obj.isRenderer && obj.uuid !== sceneUuid) { // Keep renderers and the scene root itself initially
-					// Basic check: remove if parent was the scene OR if it's a known descendant (heuristic)
-					// A more robust approach might involve storing/checking full ancestor paths
-					if (obj.parent === sceneUuid || state.scenes.has(obj.parent)) { 
-						objectsToRemove.push(uuid);
-					}
+				// Use the _sceneUuid property we'll add below, or check if it's the scene root itself
+				if (!obj.isRenderer && (obj._sceneUuid === sceneUuid || uuid === sceneUuid)) {
+					currentSceneObjectUuids.add(uuid);
 				}
 			});
-			objectsToRemove.forEach(uuid => {
+
+			// 3. Find UUIDs to remove (in current state for this scene, but not in the new batch)
+			const uuidsToRemove = new Set();
+			currentSceneObjectUuids.forEach(uuid => {
+				if (!newObjectUuids.has(uuid)) {
+					uuidsToRemove.add(uuid);
+				}
+			});
+
+			// 4. Remove stale objects from state
+			uuidsToRemove.forEach(uuid => {
 				state.objects.delete(uuid);
-				// Also remove from scenes/renderers maps if necessary, although unlikely for non-roots
-				state.scenes.delete(uuid);
-				// state.renderers.delete(uuid); // Renderers shouldn't be removed here
+				// If a scene object itself was somehow removed (unlikely for root), clean up scenes map too
+				if (state.scenes.has(uuid)) {
+					state.scenes.delete(uuid);
+				}
 			});
 
-			// Process the new batch
+			// 5. Process the new batch: Add/Update objects and mark their scene association
 			batchObjects.forEach(objData => {
+				// Add a private property to track which scene this object belongs to
+				objData._sceneUuid = sceneUuid;
 				state.objects.set(objData.uuid, objData);
-				if (objData.isScene) {
-					state.scenes.set(objData.uuid, objData); // Ensure scene is in the scenes map
+
+				// Ensure the scene root is in the scenes map
+				if (objData.isScene && objData.uuid === sceneUuid) {
+					state.scenes.set(objData.uuid, objData);
 				}
-				// Renderers are handled by separate 'renderer' events
+				// Note: Renderers are handled separately by 'renderer' events and shouldn't appear in scene batches.
 			});
 
 			// Update UI once after processing the entire batch

粤ICP备19079148号