mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-18 22:10:03 +00:00
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)
142 lines
4.0 KiB
Vue
142 lines
4.0 KiB
Vue
<template>
|
|
<div
|
|
class="widget-expands relative h-full w-full"
|
|
@mouseenter="handleMouseEnter"
|
|
@mouseleave="handleMouseLeave"
|
|
@pointerdown.stop
|
|
@pointermove.stop
|
|
@pointerup.stop
|
|
>
|
|
<Load3DScene
|
|
v-if="node"
|
|
ref="load3DSceneRef"
|
|
:initialize-load3d="initializeLoad3d"
|
|
:cleanup="cleanup"
|
|
:loading="loading"
|
|
:loading-message="loadingMessage"
|
|
:on-model-drop="isPreview ? undefined : handleModelDrop"
|
|
:is-preview="isPreview"
|
|
/>
|
|
<div class="pointer-events-none absolute top-0 left-0 h-full w-full">
|
|
<Load3DControls
|
|
v-model:scene-config="sceneConfig"
|
|
v-model:model-config="modelConfig"
|
|
v-model:camera-config="cameraConfig"
|
|
v-model:light-config="lightConfig"
|
|
@update-background-image="handleBackgroundImageUpdate"
|
|
@export-model="handleExportModel"
|
|
/>
|
|
<AnimationControls
|
|
v-if="animations && animations.length > 0"
|
|
v-model:animations="animations"
|
|
v-model:playing="playing"
|
|
v-model:selected-speed="selectedSpeed"
|
|
v-model:selected-animation="selectedAnimation"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-if="enable3DViewer && node"
|
|
class="pointer-events-auto absolute top-12 right-2 z-20"
|
|
>
|
|
<ViewerControls :node="node as LGraphNode" />
|
|
</div>
|
|
|
|
<div
|
|
v-if="!isPreview"
|
|
class="pointer-events-auto absolute right-2 z-20"
|
|
:class="{
|
|
'top-12': !enable3DViewer,
|
|
'top-24': enable3DViewer
|
|
}"
|
|
>
|
|
<RecordingControls
|
|
v-model:is-recording="isRecording"
|
|
v-model:has-recording="hasRecording"
|
|
v-model:recording-duration="recordingDuration"
|
|
@start-recording="handleStartRecording"
|
|
@stop-recording="handleStopRecording"
|
|
@export-recording="handleExportRecording"
|
|
@clear-recording="handleClearRecording"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, ref } from 'vue'
|
|
import type { Ref } from 'vue'
|
|
|
|
import Load3DControls from '@/components/load3d/Load3DControls.vue'
|
|
import Load3DScene from '@/components/load3d/Load3DScene.vue'
|
|
import AnimationControls from '@/components/load3d/controls/AnimationControls.vue'
|
|
import RecordingControls from '@/components/load3d/controls/RecordingControls.vue'
|
|
import ViewerControls from '@/components/load3d/controls/ViewerControls.vue'
|
|
import { useLoad3d } from '@/composables/useLoad3d'
|
|
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
|
|
import { useSettingStore } from '@/platform/settings/settingStore'
|
|
import type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'
|
|
import { app } from '@/scripts/app'
|
|
import type { ComponentWidget } from '@/scripts/domWidget'
|
|
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
|
|
|
|
const props = defineProps<{
|
|
widget: ComponentWidget<string[]> | SimplifiedWidget
|
|
nodeId?: NodeId
|
|
}>()
|
|
|
|
function isComponentWidget(
|
|
widget: ComponentWidget<string[]> | SimplifiedWidget
|
|
): widget is ComponentWidget<string[]> {
|
|
return 'node' in widget && widget.node !== undefined
|
|
}
|
|
|
|
const node = ref<LGraphNode | null>(null)
|
|
|
|
if (isComponentWidget(props.widget)) {
|
|
node.value = props.widget.node
|
|
} else if (props.nodeId) {
|
|
onMounted(() => {
|
|
node.value = app.rootGraph?.getNodeById(props.nodeId!) || null
|
|
})
|
|
}
|
|
|
|
const load3DSceneRef = ref<InstanceType<typeof Load3DScene> | null>(null)
|
|
|
|
const {
|
|
// configs
|
|
sceneConfig,
|
|
modelConfig,
|
|
cameraConfig,
|
|
lightConfig,
|
|
|
|
// other state
|
|
isRecording,
|
|
isPreview,
|
|
hasRecording,
|
|
recordingDuration,
|
|
animations,
|
|
playing,
|
|
selectedSpeed,
|
|
selectedAnimation,
|
|
loading,
|
|
loadingMessage,
|
|
|
|
// Methods
|
|
initializeLoad3d,
|
|
handleMouseEnter,
|
|
handleMouseLeave,
|
|
handleStartRecording,
|
|
handleStopRecording,
|
|
handleExportRecording,
|
|
handleClearRecording,
|
|
handleBackgroundImageUpdate,
|
|
handleExportModel,
|
|
handleModelDrop,
|
|
cleanup
|
|
} = useLoad3d(node as Ref<LGraphNode | null>)
|
|
|
|
const enable3DViewer = computed(() =>
|
|
useSettingStore().get('Comfy.Load3D.3DViewerEnable')
|
|
)
|
|
</script>
|