mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-05 15:40:10 +00:00
## Summary This PR refactors the Load3d 3D rendering system to remove its direct dependency on LGraphNode, making it a more decoupled and reusable component. The core rendering engine is now framework-agnostic and can be used in any context, not just within LiteGraph nodes. ## Changes 1. Decoupled Load3d from LGraphNode - Before: Load3d directly accessed node.widgets and node.properties - After: Load3d accepts optional parameters and callbacks, delegating node integration to the calling code 2. Event-Driven State Management - Removed internal storage from Load3d core components - Camera, controls, and view helper managers now emit cameraChanged events instead of directly storing state - External code (e.g., useLoad3d) listens to events and handles persistence to node.properties 3. Reactive Dimension Updates - Introduced getDimensions callback to support reactive dimension updates - Fixes the issue where dimension changes in vueNodes mode required a refresh - The callback is invoked on every render to get fresh width/height values 4. Improved Configuration System - Load3DConfiguration now accepts properties: Dictionary<NodeProperty | undefined> instead of custom storage interface - Uses official LiteGraph type definitions (Dictionary, NodeProperty) - More semantic parameter naming: storage → properties ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6707-refactor-remove-node-as-dependency-in-3d-node-2ab6d73d365081ffac1cdce354781ce8) by [Unito](https://www.unito.io)
121 lines
3.4 KiB
TypeScript
121 lines
3.4 KiB
TypeScript
import * as THREE from 'three'
|
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
|
import { ViewHelper } from 'three/examples/jsm/helpers/ViewHelper'
|
|
|
|
import {
|
|
type EventManagerInterface,
|
|
type ViewHelperManagerInterface
|
|
} from './interfaces'
|
|
|
|
export class ViewHelperManager implements ViewHelperManagerInterface {
|
|
viewHelper: ViewHelper = {} as ViewHelper
|
|
viewHelperContainer: HTMLDivElement = {} as HTMLDivElement
|
|
|
|
private getActiveCamera: () => THREE.Camera
|
|
private getControls: () => OrbitControls
|
|
private eventManager: EventManagerInterface
|
|
// @ts-expect-error unused variable
|
|
private renderer: THREE.WebGLRenderer
|
|
|
|
constructor(
|
|
renderer: THREE.WebGLRenderer,
|
|
getActiveCamera: () => THREE.Camera,
|
|
getControls: () => OrbitControls,
|
|
eventManager: EventManagerInterface
|
|
) {
|
|
this.renderer = renderer
|
|
this.getActiveCamera = getActiveCamera
|
|
this.getControls = getControls
|
|
this.eventManager = eventManager
|
|
}
|
|
|
|
init(): void {}
|
|
|
|
dispose(): void {
|
|
if (this.viewHelper) {
|
|
this.viewHelper.dispose()
|
|
}
|
|
|
|
if (this.viewHelperContainer && this.viewHelperContainer.parentNode) {
|
|
this.viewHelperContainer.parentNode.removeChild(this.viewHelperContainer)
|
|
}
|
|
}
|
|
|
|
createViewHelper(container: Element | HTMLElement): void {
|
|
this.viewHelperContainer = document.createElement('div')
|
|
|
|
this.viewHelperContainer.style.position = 'absolute'
|
|
this.viewHelperContainer.style.bottom = '0'
|
|
this.viewHelperContainer.style.left = '0'
|
|
this.viewHelperContainer.style.width = '128px'
|
|
this.viewHelperContainer.style.height = '128px'
|
|
|
|
this.viewHelperContainer.addEventListener('pointerup', (event) => {
|
|
event.stopPropagation()
|
|
this.viewHelper.handleClick(event)
|
|
})
|
|
|
|
this.viewHelperContainer.addEventListener('pointerdown', (event) => {
|
|
event.stopPropagation()
|
|
})
|
|
|
|
container.appendChild(this.viewHelperContainer)
|
|
|
|
this.viewHelper = new ViewHelper(
|
|
this.getActiveCamera(),
|
|
this.viewHelperContainer
|
|
)
|
|
|
|
this.viewHelper.center = this.getControls().target
|
|
}
|
|
|
|
update(delta: number): void {
|
|
if (this.viewHelper.animating) {
|
|
this.viewHelper.update(delta)
|
|
|
|
if (!this.viewHelper.animating) {
|
|
const cameraState = {
|
|
position: this.getActiveCamera().position.clone(),
|
|
target: this.getControls().target.clone(),
|
|
zoom:
|
|
this.getActiveCamera() instanceof THREE.OrthographicCamera
|
|
? (this.getActiveCamera() as THREE.OrthographicCamera).zoom
|
|
: (this.getActiveCamera() as THREE.PerspectiveCamera).zoom,
|
|
cameraType:
|
|
this.getActiveCamera() instanceof THREE.PerspectiveCamera
|
|
? 'perspective'
|
|
: 'orthographic'
|
|
}
|
|
|
|
this.eventManager.emitEvent('cameraChanged', cameraState)
|
|
}
|
|
}
|
|
}
|
|
|
|
handleResize(): void {}
|
|
|
|
visibleViewHelper(visible: boolean) {
|
|
if (visible) {
|
|
this.viewHelper.visible = true
|
|
this.viewHelperContainer.style.display = 'block'
|
|
} else {
|
|
this.viewHelper.visible = false
|
|
this.viewHelperContainer.style.display = 'none'
|
|
}
|
|
}
|
|
|
|
recreateViewHelper(): void {
|
|
if (this.viewHelper) {
|
|
this.viewHelper.dispose()
|
|
}
|
|
|
|
this.viewHelper = new ViewHelper(
|
|
this.getActiveCamera(),
|
|
this.viewHelperContainer
|
|
)
|
|
this.viewHelper.center = this.getControls().target
|
|
}
|
|
|
|
reset(): void {}
|
|
}
|