From 0cfd6a487a9a6a550e96e6386ff91cee0b19fa19 Mon Sep 17 00:00:00 2001 From: Terry Jia Date: Thu, 13 Mar 2025 20:37:54 -0400 Subject: [PATCH] [3d] apply new vue desige (#3018) --- src/components/load3d/Load3D.vue | 38 +-- src/components/load3d/Load3DAnimation.vue | 36 ++- .../load3d/Load3DAnimationScene.vue | 3 +- src/components/load3d/Load3DScene.vue | 8 +- src/extensions/core/load3d.ts | 258 +++++------------- src/extensions/core/load3d/interfaces.ts | 4 + 6 files changed, 128 insertions(+), 219 deletions(-) diff --git a/src/components/load3d/Load3D.vue b/src/components/load3d/Load3D.vue index f2e5bc576..a41743c12 100644 --- a/src/components/load3d/Load3D.vue +++ b/src/components/load3d/Load3D.vue @@ -61,16 +61,22 @@ import Load3DScene from '@/components/load3d/Load3DScene.vue' import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' import { CameraType, + Load3DNodeType, MaterialMode, UpDirection } from '@/extensions/core/load3d/interfaces' +import type { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2' +import type { ComponentWidget } from '@/scripts/domWidget' -const props = defineProps<{ - node: any - type: 'Load3D' | 'Preview3D' +const { widget } = defineProps<{ + widget: ComponentWidget }>() -const node = ref(props.node) +const inputSpec = widget.inputSpec as CustomInputSpec + +const node = widget.node +const type = inputSpec.type as Load3DNodeType + const backgroundColor = ref('#000000') const showGrid = ref(true) const showPreview = ref(false) @@ -86,7 +92,7 @@ const materialMode = ref('original') const edgeThreshold = ref(85) const showPreviewButton = computed(() => { - return !props.type.includes('Preview') + return !type.includes('Preview') }) const switchCamera = () => { @@ -95,68 +101,68 @@ const switchCamera = () => { showFOVButton.value = cameraType.value === 'perspective' - node.value.properties['Camera Type'] = cameraType.value + node.properties['Camera Type'] = cameraType.value } const togglePreview = (value: boolean) => { showPreview.value = value - node.value.properties['Show Preview'] = showPreview.value + node.properties['Show Preview'] = showPreview.value } const toggleGrid = (value: boolean) => { showGrid.value = value - node.value.properties['Show Grid'] = showGrid.value + node.properties['Show Grid'] = showGrid.value } const handleUpdateLightIntensity = (value: number) => { lightIntensity.value = value - node.value.properties['Light Intensity'] = lightIntensity.value + node.properties['Light Intensity'] = lightIntensity.value } const handleBackgroundImageUpdate = async (file: File | null) => { if (!file) { hasBackgroundImage.value = false backgroundImage.value = '' - node.value.properties['Background Image'] = '' + node.properties['Background Image'] = '' return } backgroundImage.value = await Load3dUtils.uploadFile(file) - node.value.properties['Background Image'] = backgroundImage.value + node.properties['Background Image'] = backgroundImage.value } const handleUpdateFOV = (value: number) => { fov.value = value - node.value.properties['FOV'] = fov.value + node.properties['FOV'] = fov.value } const handleUpdateEdgeThreshold = (value: number) => { edgeThreshold.value = value - node.value.properties['Edge Threshold'] = edgeThreshold.value + node.properties['Edge Threshold'] = edgeThreshold.value } const handleBackgroundColorChange = (value: string) => { backgroundColor.value = value - node.value.properties['Background Color'] = value + node.properties['Background Color'] = value } const handleUpdateUpDirection = (value: UpDirection) => { upDirection.value = value - node.value.properties['Up Direction'] = value + node.properties['Up Direction'] = value } const handleUpdateMaterialMode = (value: MaterialMode) => { materialMode.value = value - node.value.properties['Material Mode'] = value + node.properties['Material Mode'] = value } const listenMaterialModeChange = (mode: MaterialMode) => { diff --git a/src/components/load3d/Load3DAnimation.vue b/src/components/load3d/Load3DAnimation.vue index 9e9e8423d..d181dfbf3 100644 --- a/src/components/load3d/Load3DAnimation.vue +++ b/src/components/load3d/Load3DAnimation.vue @@ -74,16 +74,22 @@ import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' import { AnimationItem, CameraType, + Load3DAnimationNodeType, MaterialMode, UpDirection } from '@/extensions/core/load3d/interfaces' +import type { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2' +import type { ComponentWidget } from '@/scripts/domWidget' -const props = defineProps<{ - node: any - type: 'Load3DAnimation' | 'Preview3DAnimation' +const { widget } = defineProps<{ + widget: ComponentWidget }>() -const node = ref(props.node) +const inputSpec = widget.inputSpec as CustomInputSpec + +const node = widget.node +const type = inputSpec.type as Load3DAnimationNodeType + const backgroundColor = ref('#000000') const showGrid = ref(true) const showPreview = ref(false) @@ -101,7 +107,7 @@ const selectedAnimation = ref(0) const backgroundImage = ref('') const showPreviewButton = computed(() => { - return !props.type.includes('Preview') + return !type.includes('Preview') }) const switchCamera = () => { @@ -110,44 +116,44 @@ const switchCamera = () => { showFOVButton.value = cameraType.value === 'perspective' - node.value.properties['Camera Type'] = cameraType.value + node.properties['Camera Type'] = cameraType.value } const togglePreview = (value: boolean) => { showPreview.value = value - node.value.properties['Show Preview'] = showPreview.value + node.properties['Show Preview'] = showPreview.value } const toggleGrid = (value: boolean) => { showGrid.value = value - node.value.properties['Show Grid'] = showGrid.value + node.properties['Show Grid'] = showGrid.value } const handleUpdateLightIntensity = (value: number) => { lightIntensity.value = value - node.value.properties['Light Intensity'] = lightIntensity.value + node.properties['Light Intensity'] = lightIntensity.value } const handleBackgroundImageUpdate = async (file: File | null) => { if (!file) { hasBackgroundImage.value = false backgroundImage.value = '' - node.value.properties['Background Image'] = '' + node.properties['Background Image'] = '' return } backgroundImage.value = await Load3dUtils.uploadFile(file) - node.value.properties['Background Image'] = backgroundImage.value + node.properties['Background Image'] = backgroundImage.value } const handleUpdateFOV = (value: number) => { fov.value = value - node.value.properties['FOV'] = fov.value + node.properties['FOV'] = fov.value } const materialMode = ref('original') @@ -156,19 +162,19 @@ const upDirection = ref('original') const handleUpdateUpDirection = (value: UpDirection) => { upDirection.value = value - node.value.properties['Up Direction'] = value + node.properties['Up Direction'] = value } const handleUpdateMaterialMode = (value: MaterialMode) => { materialMode.value = value - node.value.properties['Material Mode'] = value + node.properties['Material Mode'] = value } const handleBackgroundColorChange = (value: string) => { backgroundColor.value = value - node.value.properties['Background Color'] = value + node.properties['Background Color'] = value } const togglePlay = (value: boolean) => { diff --git a/src/components/load3d/Load3DAnimationScene.vue b/src/components/load3d/Load3DAnimationScene.vue index 5d85f58b8..cf1c711d4 100644 --- a/src/components/load3d/Load3DAnimationScene.vue +++ b/src/components/load3d/Load3DAnimationScene.vue @@ -28,13 +28,14 @@ import { ref, watch } from 'vue' import Load3DScene from '@/components/load3d/Load3DScene.vue' import { CameraType, + Load3DAnimationNodeType, MaterialMode, UpDirection } from '@/extensions/core/load3d/interfaces' const props = defineProps<{ node: any - type: 'Load3DAnimation' | 'Preview3DAnimation' + type: Load3DAnimationNodeType backgroundColor: string showGrid: boolean lightIntensity: number diff --git a/src/components/load3d/Load3DScene.vue b/src/components/load3d/Load3DScene.vue index 93eb11d7a..f97cc6db4 100644 --- a/src/components/load3d/Load3DScene.vue +++ b/src/components/load3d/Load3DScene.vue @@ -1,5 +1,5 @@ @@ -13,6 +13,8 @@ import Load3d from '@/extensions/core/load3d/Load3d' import Load3dAnimation from '@/extensions/core/load3d/Load3dAnimation' import { CameraType, + Load3DAnimationNodeType, + Load3DNodeType, MaterialMode, UpDirection } from '@/extensions/core/load3d/interfaces' @@ -20,8 +22,8 @@ import { t } from '@/i18n' import { useLoad3dService } from '@/services/load3dService' const props = defineProps<{ - node: any - type: 'Load3D' | 'Load3DAnimation' | 'Preview3D' | 'Preview3DAnimation' + node: LGraphNode + type: Load3DNodeType | Load3DAnimationNodeType backgroundColor: string showGrid: boolean lightIntensity: number diff --git a/src/extensions/core/load3d.ts b/src/extensions/core/load3d.ts index 3386ab3e4..caebb6a09 100644 --- a/src/extensions/core/load3d.ts +++ b/src/extensions/core/load3d.ts @@ -1,65 +1,27 @@ // @ts-strict-ignore import { IWidget } from '@comfyorg/litegraph' import { IStringWidget } from '@comfyorg/litegraph/dist/types/widgets' -import PrimeVue from 'primevue/config' -import { createApp, h, nextTick, render } from 'vue' +import { nextTick } from 'vue' import Load3D from '@/components/load3d/Load3D.vue' import Load3DAnimation from '@/components/load3d/Load3DAnimation.vue' import Load3DConfiguration from '@/extensions/core/load3d/Load3DConfiguration' import Load3dAnimation from '@/extensions/core/load3d/Load3dAnimation' import Load3dUtils from '@/extensions/core/load3d/Load3dUtils' +import { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2' import { api } from '@/scripts/api' import { app } from '@/scripts/app' +import { ComponentWidgetImpl, addWidget } from '@/scripts/domWidget' import { useLoad3dService } from '@/services/load3dService' import { useToastStore } from '@/stores/toastStore' +import { generateUUID } from '@/utils/formatUtil' app.registerExtension({ name: 'Comfy.Load3D', - getCustomWidgets(app) { + getCustomWidgets() { return { - LOAD_3D(node, inputName) { - node.addProperty('Camera Info', '') - - const container = document.createElement('div') - container.classList.add('comfy-load-3d') - - /* Hold off for now - const mountComponent = () => { - const vnode = h(Load3D, { - node: node, - type: 'Load3D' - }) - - render(vnode, container) - } - */ - - let controlsApp = createApp(Load3D, { - node: node, - type: 'Load3D' - }) - - controlsApp.mount(container) - - const origOnRemoved = node.onRemoved - - node.onRemoved = function () { - /* - render(null, container) - - container.remove() - */ - - if (controlsApp) { - controlsApp.unmount() - controlsApp = null - } - - origOnRemoved?.apply(this, []) - } - + LOAD_3D(node) { const fileInput = document.createElement('input') fileInput.type = 'file' fileInput.accept = '.gltf,.glb,.obj,.mtl,.fbx,.stl' @@ -112,11 +74,23 @@ app.registerExtension({ } }) - //mountComponent() - - return { - widget: node.addDOMWidget(inputName, 'LOAD_3D', container) + const inputSpec: CustomInputSpec = { + name: 'image', + type: 'Load3D' } + + const widget = new ComponentWidgetImpl({ + id: generateUUID(), + node, + name: inputSpec.name, + component: Load3D, + inputSpec, + options: {} + }) + + addWidget(node, widget) + + return { widget } } } }, @@ -130,8 +104,6 @@ app.registerExtension({ await nextTick() - const sceneWidget = node.widgets.find((w: IWidget) => w.name === 'image') - const load3d = useLoad3dService().getLoad3d(node) const modelWidget = node.widgets.find( @@ -147,6 +119,8 @@ app.registerExtension({ config.configure('input', modelWidget, cameraState, width, height) + const sceneWidget = node.widgets.find((w: IWidget) => w.name === 'image') + sceneWidget.serializeValue = async () => { node.properties['Camera Info'] = load3d.getCameraState() @@ -173,52 +147,9 @@ app.registerExtension({ app.registerExtension({ name: 'Comfy.Load3DAnimation', - getCustomWidgets(app) { + getCustomWidgets() { return { - LOAD_3D_ANIMATION(node, inputName) { - node.addProperty('Camera Info', '') - - const container = document.createElement('div') - - container.classList.add('comfy-load-3d-animation') - - /* - const mountComponent = () => { - const vnode = h(Load3DAnimation, { - node: node, - type: 'Load3DAnimation' - }) - - render(vnode, container) - } - */ - - let controlsApp = createApp(Load3DAnimation, { - node: node, - type: 'Load3DAnimation' - }) - - controlsApp.use(PrimeVue) - - controlsApp.mount(container) - - const origOnRemoved = node.onRemoved - - node.onRemoved = function () { - /* - render(null, container) - - container.remove() - */ - - if (controlsApp) { - controlsApp.unmount() - controlsApp = null - } - - origOnRemoved?.apply(this, []) - } - + LOAD_3D_ANIMATION(node) { const fileInput = document.createElement('input') fileInput.type = 'file' fileInput.accept = '.fbx,glb,gltf' @@ -269,11 +200,23 @@ app.registerExtension({ } }) - //mountComponent() - - return { - widget: node.addDOMWidget(inputName, 'LOAD_3D_ANIMATION', container) + const inputSpec: CustomInputSpec = { + name: 'image', + type: 'Load3DAnimation' } + + const widget = new ComponentWidgetImpl({ + id: generateUUID(), + node, + name: inputSpec.name, + component: Load3DAnimation, + inputSpec, + options: {} + }) + + addWidget(node, widget) + + return { widget } } } }, @@ -333,62 +276,32 @@ app.registerExtension({ name: 'Comfy.Preview3D', async beforeRegisterNodeDef(nodeType, nodeData) { - if ( - // @ts-expect-error ComfyNode - ['Preview3D'].includes(nodeType.comfyClass) - ) { + if ('Preview3D' === nodeData.name) { // @ts-expect-error InputSpec is not typed correctly nodeData.input.required.image = ['PREVIEW_3D'] } }, - getCustomWidgets(app) { + getCustomWidgets() { return { - PREVIEW_3D(node, inputName) { - const container = document.createElement('div') - - container.classList.add('comfy-preview-3d') - - /* - const mountComponent = () => { - const vnode = h(Load3D, { - node: node, - type: 'Preview3D' - }) - - render(vnode, container) - } - */ - - let controlsApp = createApp(Load3D, { - node: node, + PREVIEW_3D(node) { + const inputSpec: CustomInputSpec = { + name: 'image', type: 'Preview3D' + } + + const widget = new ComponentWidgetImpl({ + id: generateUUID(), + node, + name: inputSpec.name, + component: Load3D, + inputSpec, + options: {} }) - controlsApp.mount(container) + addWidget(node, widget) - const origOnRemoved = node.onRemoved - - node.onRemoved = function () { - /* - render(null, container) - - container.remove() - */ - - if (controlsApp) { - controlsApp.unmount() - controlsApp = null - } - - origOnRemoved?.apply(this, []) - } - - //mountComponent() - - return { - widget: node.addDOMWidget(inputName, 'PREVIEW_3D', container) - } + return { widget } } } }, @@ -436,55 +349,32 @@ app.registerExtension({ name: 'Comfy.Preview3DAnimation', async beforeRegisterNodeDef(nodeType, nodeData) { - if ( - // @ts-expect-error ComfyNode - ['Preview3DAnimation'].includes(nodeType.comfyClass) - ) { + if ('Preview3DAnimation' === nodeData.name) { // @ts-expect-error InputSpec is not typed correctly nodeData.input.required.image = ['PREVIEW_3D_ANIMATION'] } }, - getCustomWidgets(app) { + getCustomWidgets() { return { - PREVIEW_3D_ANIMATION(node, inputName) { - const container = document.createElement('div') - - container.classList.add('comfy-preview-3d-animation') - - let controlsApp = createApp(Load3DAnimation, { - node: node, + PREVIEW_3D_ANIMATION(node) { + const inputSpec: CustomInputSpec = { + name: 'image', type: 'Preview3DAnimation' + } + + const widget = new ComponentWidgetImpl({ + id: generateUUID(), + node, + name: inputSpec.name, + component: Load3DAnimation, + inputSpec, + options: {} }) - controlsApp.use(PrimeVue) + addWidget(node, widget) - controlsApp.mount(container) - - const origOnRemoved = node.onRemoved - - node.onRemoved = function () { - /* - render(null, container) - - container.remove() - */ - - if (controlsApp) { - controlsApp.unmount() - controlsApp = null - } - - origOnRemoved?.apply(this, []) - } - - return { - widget: node.addDOMWidget( - inputName, - 'PREVIEW_3D_ANIMATION', - container - ) - } + return { widget } } } }, diff --git a/src/extensions/core/load3d/interfaces.ts b/src/extensions/core/load3d/interfaces.ts index df7a7f434..cde866638 100644 --- a/src/extensions/core/load3d/interfaces.ts +++ b/src/extensions/core/load3d/interfaces.ts @@ -8,6 +8,10 @@ import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader' import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader' import { STLLoader } from 'three/examples/jsm/loaders/STLLoader' +export type Load3DNodeType = 'Load3D' | 'Preview3D' + +export type Load3DAnimationNodeType = 'Load3DAnimation' | 'Preview3DAnimation' + export type MaterialMode = | 'original' | 'normal'