mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-14 01:20:03 +00:00
[refactor] refactor load3d (#5765)
Summary Fully Refactored the Load3D module to improve architecture and maintainability by consolidating functionality into a centralized composable pattern and simplifying component structure. and support VueNodes system Changes - Architecture: Introduced new useLoad3d composable to centralize 3D loading logic and state management - Component Simplification: Removed redundant components (Load3DAnimation.vue, Load3DAnimationScene.vue, PreviewManager.ts) - Support VueNodes - improve config store - remove lineart output due Animation doesnot support it, may add it back later - remove Preview screen and keep scene in fixed ratio in load3d (not affect preview3d) - improve record video feature which will already record video by same ratio as scene Need BE change https://github.com/comfyanonymous/ComfyUI/pull/10025 https://github.com/user-attachments/assets/9e038729-84a0-45ad-b0f2-11c57d7e0c9a ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5765-refactor-refactor-load3d-2796d73d365081728297cc486e2e9052) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -1,20 +1,15 @@
|
||||
import { toRaw } from 'vue'
|
||||
|
||||
import { nodeToLoad3dMap } from '@/composables/useLoad3d'
|
||||
import { useLoad3dViewer } from '@/composables/useLoad3dViewer'
|
||||
import Load3d from '@/extensions/core/load3d/Load3d'
|
||||
import Load3dAnimation from '@/extensions/core/load3d/Load3dAnimation'
|
||||
import type Load3d from '@/extensions/core/load3d/Load3d'
|
||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
|
||||
import type { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
|
||||
type Load3dReadyCallback = (load3d: Load3d | Load3dAnimation) => void
|
||||
|
||||
const viewerInstances = new Map<NodeId, any>()
|
||||
|
||||
export class Load3dService {
|
||||
private static instance: Load3dService
|
||||
private nodeToLoad3dMap = new Map<LGraphNode, Load3d | Load3dAnimation>()
|
||||
private pendingCallbacks = new Map<LGraphNode, Load3dReadyCallback[]>()
|
||||
|
||||
private constructor() {}
|
||||
|
||||
@@ -25,84 +20,14 @@ export class Load3dService {
|
||||
return Load3dService.instance
|
||||
}
|
||||
|
||||
registerLoad3d(
|
||||
node: LGraphNode,
|
||||
container: HTMLElement,
|
||||
inputSpec: CustomInputSpec
|
||||
) {
|
||||
getLoad3d(node: LGraphNode): Load3d | null {
|
||||
const rawNode = toRaw(node)
|
||||
|
||||
if (this.nodeToLoad3dMap.has(rawNode)) {
|
||||
this.removeLoad3d(rawNode)
|
||||
}
|
||||
|
||||
const type = inputSpec.type
|
||||
|
||||
const isAnimation = type.includes('Animation')
|
||||
|
||||
const Load3dClass = isAnimation ? Load3dAnimation : Load3d
|
||||
|
||||
const instance = new Load3dClass(container, {
|
||||
node: rawNode,
|
||||
inputSpec: inputSpec
|
||||
})
|
||||
|
||||
rawNode.onMouseEnter = function () {
|
||||
instance.refreshViewport()
|
||||
|
||||
instance.updateStatusMouseOnNode(true)
|
||||
}
|
||||
|
||||
rawNode.onMouseLeave = function () {
|
||||
instance.updateStatusMouseOnNode(false)
|
||||
}
|
||||
|
||||
rawNode.onResize = function () {
|
||||
instance.handleResize()
|
||||
}
|
||||
|
||||
rawNode.onDrawBackground = function () {
|
||||
instance.renderer.domElement.hidden = this.flags.collapsed ?? false
|
||||
}
|
||||
|
||||
this.nodeToLoad3dMap.set(rawNode, instance)
|
||||
|
||||
const callbacks = this.pendingCallbacks.get(rawNode)
|
||||
|
||||
if (callbacks) {
|
||||
callbacks.forEach((callback) => callback(instance))
|
||||
this.pendingCallbacks.delete(rawNode)
|
||||
}
|
||||
|
||||
return instance
|
||||
return nodeToLoad3dMap.get(rawNode) || null
|
||||
}
|
||||
|
||||
getLoad3d(node: LGraphNode): Load3d | Load3dAnimation | null {
|
||||
const rawNode = toRaw(node)
|
||||
|
||||
return this.nodeToLoad3dMap.get(rawNode) || null
|
||||
}
|
||||
|
||||
waitForLoad3d(node: LGraphNode, callback: Load3dReadyCallback): void {
|
||||
const rawNode = toRaw(node)
|
||||
|
||||
const existingInstance = this.nodeToLoad3dMap.get(rawNode)
|
||||
|
||||
if (existingInstance) {
|
||||
callback(existingInstance)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.pendingCallbacks.has(rawNode)) {
|
||||
this.pendingCallbacks.set(rawNode, [])
|
||||
}
|
||||
|
||||
this.pendingCallbacks.get(rawNode)!.push(callback)
|
||||
}
|
||||
|
||||
getNodeByLoad3d(load3d: Load3d | Load3dAnimation): LGraphNode | null {
|
||||
for (const [node, instance] of this.nodeToLoad3dMap) {
|
||||
getNodeByLoad3d(load3d: Load3d): LGraphNode | null {
|
||||
for (const [node, instance] of nodeToLoad3dMap) {
|
||||
if (instance === load3d) {
|
||||
return node
|
||||
}
|
||||
@@ -113,22 +38,19 @@ export class Load3dService {
|
||||
removeLoad3d(node: LGraphNode) {
|
||||
const rawNode = toRaw(node)
|
||||
|
||||
const instance = this.nodeToLoad3dMap.get(rawNode)
|
||||
const instance = nodeToLoad3dMap.get(rawNode)
|
||||
|
||||
if (instance) {
|
||||
instance.remove()
|
||||
|
||||
this.nodeToLoad3dMap.delete(rawNode)
|
||||
nodeToLoad3dMap.delete(rawNode)
|
||||
}
|
||||
|
||||
this.pendingCallbacks.delete(rawNode)
|
||||
}
|
||||
|
||||
clear() {
|
||||
for (const [node] of this.nodeToLoad3dMap) {
|
||||
for (const [node] of nodeToLoad3dMap) {
|
||||
this.removeLoad3d(node)
|
||||
}
|
||||
this.pendingCallbacks.clear()
|
||||
}
|
||||
|
||||
getOrCreateViewer(node: LGraphNode) {
|
||||
@@ -149,7 +71,7 @@ export class Load3dService {
|
||||
viewerInstances.delete(node.id)
|
||||
}
|
||||
|
||||
async copyLoad3dState(source: Load3d, target: Load3d | Load3dAnimation) {
|
||||
async copyLoad3dState(source: Load3d, target: Load3d) {
|
||||
const sourceModel = source.modelManager.currentModel
|
||||
|
||||
if (sourceModel) {
|
||||
@@ -188,12 +110,13 @@ export class Load3dService {
|
||||
.getCurrentBackgroundInfo()
|
||||
if (sourceBackgroundInfo.type === 'image') {
|
||||
const sourceNode = this.getNodeByLoad3d(source)
|
||||
const backgroundPath = sourceNode?.properties?.[
|
||||
'Background Image'
|
||||
] as string
|
||||
const sceneConfig = sourceNode?.properties?.['Scene Config'] as any
|
||||
const backgroundPath = sceneConfig?.backgroundImage
|
||||
if (backgroundPath) {
|
||||
await target.setBackgroundImage(backgroundPath)
|
||||
}
|
||||
} else {
|
||||
await target.setBackgroundImage('')
|
||||
}
|
||||
|
||||
target.setLightIntensity(
|
||||
@@ -203,11 +126,6 @@ export class Load3dService {
|
||||
if (sourceCameraType === 'perspective') {
|
||||
target.setFOV(source.getCameraManager().perspectiveCamera.fov)
|
||||
}
|
||||
|
||||
const sourceNode = this.getNodeByLoad3d(source)
|
||||
if (sourceNode?.properties?.['Edge Threshold']) {
|
||||
target.setEdgeThreshold(sourceNode.properties['Edge Threshold'] as number)
|
||||
}
|
||||
}
|
||||
|
||||
handleViewportRefresh(load3d: Load3d | null) {
|
||||
@@ -230,6 +148,11 @@ export class Load3dService {
|
||||
|
||||
if (viewer.needApplyChanges.value) {
|
||||
await viewer.applyChanges()
|
||||
|
||||
// Sync configuration back to the node's UI
|
||||
if ((node as any).syncLoad3dConfig) {
|
||||
;(node as any).syncLoad3dConfig()
|
||||
}
|
||||
}
|
||||
|
||||
useLoad3dService().removeViewer(node)
|
||||
|
||||
Reference in New Issue
Block a user