mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-13 09:00:16 +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,6 +1,10 @@
|
||||
<template>
|
||||
<div
|
||||
class="pointer-events-auto absolute top-12 left-2 z-20 flex flex-col rounded-lg bg-smoke-700/30"
|
||||
@pointerdown.stop
|
||||
@pointermove.stop
|
||||
@pointerup.stop
|
||||
@wheel.stop
|
||||
>
|
||||
<div class="show-menu relative">
|
||||
<Button class="p-button-rounded p-button-text" @click="toggleMenu">
|
||||
@@ -20,7 +24,9 @@
|
||||
@click="selectCategory(category)"
|
||||
>
|
||||
<i :class="getCategoryIcon(category)" />
|
||||
<span class="text-white">{{ t(categoryLabels[category]) }}</span>
|
||||
<span class="whitespace-nowrap text-white">{{
|
||||
$t(categoryLabels[category])
|
||||
}}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,71 +34,47 @@
|
||||
|
||||
<div v-show="activeCategory" class="rounded-lg bg-smoke-700/30">
|
||||
<SceneControls
|
||||
v-if="activeCategory === 'scene'"
|
||||
v-if="showSceneControls"
|
||||
ref="sceneControlsRef"
|
||||
:background-color="backgroundColor"
|
||||
:show-grid="showGrid"
|
||||
:has-background-image="hasBackgroundImage"
|
||||
@toggle-grid="handleToggleGrid"
|
||||
@update-background-color="handleBackgroundColorChange"
|
||||
v-model:show-grid="sceneConfig!.showGrid"
|
||||
v-model:background-color="sceneConfig!.backgroundColor"
|
||||
v-model:background-image="sceneConfig!.backgroundImage"
|
||||
@update-background-image="handleBackgroundImageUpdate"
|
||||
/>
|
||||
|
||||
<ModelControls
|
||||
v-if="activeCategory === 'model'"
|
||||
v-if="showModelControls"
|
||||
ref="modelControlsRef"
|
||||
:input-spec="inputSpec"
|
||||
:up-direction="upDirection"
|
||||
:material-mode="materialMode"
|
||||
:edge-threshold="edgeThreshold"
|
||||
@update-up-direction="handleUpdateUpDirection"
|
||||
@update-material-mode="handleUpdateMaterialMode"
|
||||
@update-edge-threshold="handleUpdateEdgeThreshold"
|
||||
v-model:material-mode="modelConfig!.materialMode"
|
||||
v-model:up-direction="modelConfig!.upDirection"
|
||||
/>
|
||||
|
||||
<CameraControls
|
||||
v-if="activeCategory === 'camera'"
|
||||
v-if="showCameraControls"
|
||||
ref="cameraControlsRef"
|
||||
:camera-type="cameraType"
|
||||
:fov="fov"
|
||||
:show-f-o-v-button="showFOVButton"
|
||||
@switch-camera="switchCamera"
|
||||
@update-f-o-v="handleUpdateFOV"
|
||||
v-model:camera-type="cameraConfig!.cameraType"
|
||||
v-model:fov="cameraConfig!.fov"
|
||||
/>
|
||||
|
||||
<LightControls
|
||||
v-if="activeCategory === 'light'"
|
||||
v-if="showLightControls"
|
||||
ref="lightControlsRef"
|
||||
:light-intensity="lightIntensity"
|
||||
:show-light-intensity-button="showLightIntensityButton"
|
||||
@update-light-intensity="handleUpdateLightIntensity"
|
||||
v-model:light-intensity="lightConfig!.intensity"
|
||||
v-model:material-mode="modelConfig!.materialMode"
|
||||
/>
|
||||
|
||||
<ExportControls
|
||||
v-if="activeCategory === 'export'"
|
||||
v-if="showExportControls"
|
||||
ref="exportControlsRef"
|
||||
@export-model="handleExportModel"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="showPreviewButton">
|
||||
<Button class="p-button-rounded p-button-text" @click="togglePreview">
|
||||
<i
|
||||
v-tooltip.right="{ value: t('load3d.previewOutput'), showDelay: 300 }"
|
||||
:class="[
|
||||
'pi',
|
||||
showPreview ? 'pi-eye' : 'pi-eye-slash',
|
||||
'text-lg text-white'
|
||||
]"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Tooltip } from 'primevue'
|
||||
import Button from 'primevue/button'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import CameraControls from '@/components/load3d/controls/CameraControls.vue'
|
||||
import ExportControls from '@/components/load3d/controls/ExportControls.vue'
|
||||
@@ -100,31 +82,16 @@ import LightControls from '@/components/load3d/controls/LightControls.vue'
|
||||
import ModelControls from '@/components/load3d/controls/ModelControls.vue'
|
||||
import SceneControls from '@/components/load3d/controls/SceneControls.vue'
|
||||
import type {
|
||||
CameraType,
|
||||
MaterialMode,
|
||||
UpDirection
|
||||
CameraConfig,
|
||||
LightConfig,
|
||||
ModelConfig,
|
||||
SceneConfig
|
||||
} from '@/extensions/core/load3d/interfaces'
|
||||
import { t } from '@/i18n'
|
||||
import type { CustomInputSpec } from '@/schemas/nodeDef/nodeDefSchemaV2'
|
||||
|
||||
const vTooltip = Tooltip
|
||||
|
||||
const props = defineProps<{
|
||||
inputSpec: CustomInputSpec
|
||||
backgroundColor: string
|
||||
showGrid: boolean
|
||||
showPreview: boolean
|
||||
lightIntensity: number
|
||||
showLightIntensityButton: boolean
|
||||
fov: number
|
||||
showFOVButton: boolean
|
||||
showPreviewButton: boolean
|
||||
cameraType: CameraType
|
||||
hasBackgroundImage?: boolean
|
||||
upDirection: UpDirection
|
||||
materialMode: MaterialMode
|
||||
edgeThreshold?: number
|
||||
}>()
|
||||
const sceneConfig = defineModel<SceneConfig>('sceneConfig')
|
||||
const modelConfig = defineModel<ModelConfig>('modelConfig')
|
||||
const cameraConfig = defineModel<CameraConfig>('cameraConfig')
|
||||
const lightConfig = defineModel<LightConfig>('lightConfig')
|
||||
|
||||
const isMenuOpen = ref(false)
|
||||
const activeCategory = ref<string>('scene')
|
||||
@@ -137,15 +104,26 @@ const categoryLabels: Record<string, string> = {
|
||||
}
|
||||
|
||||
const availableCategories = computed(() => {
|
||||
const baseCategories = ['scene', 'model', 'camera', 'light']
|
||||
|
||||
if (!props.inputSpec.isAnimation) {
|
||||
return [...baseCategories, 'export']
|
||||
}
|
||||
|
||||
return baseCategories
|
||||
return ['scene', 'model', 'camera', 'light', 'export']
|
||||
})
|
||||
|
||||
const showSceneControls = computed(
|
||||
() => activeCategory.value === 'scene' && !!sceneConfig.value
|
||||
)
|
||||
const showModelControls = computed(
|
||||
() => activeCategory.value === 'model' && !!modelConfig.value
|
||||
)
|
||||
const showCameraControls = computed(
|
||||
() => activeCategory.value === 'camera' && !!cameraConfig.value
|
||||
)
|
||||
const showLightControls = computed(
|
||||
() =>
|
||||
activeCategory.value === 'light' &&
|
||||
!!lightConfig.value &&
|
||||
!!modelConfig.value
|
||||
)
|
||||
const showExportControls = computed(() => activeCategory.value === 'export')
|
||||
|
||||
const toggleMenu = () => {
|
||||
isMenuOpen.value = !isMenuOpen.value
|
||||
}
|
||||
@@ -168,73 +146,14 @@ const getCategoryIcon = (category: string) => {
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'switchCamera'): void
|
||||
(e: 'toggleGrid', value: boolean): void
|
||||
(e: 'updateBackgroundColor', color: string): void
|
||||
(e: 'updateLightIntensity', value: number): void
|
||||
(e: 'updateFOV', value: number): void
|
||||
(e: 'togglePreview', value: boolean): void
|
||||
(e: 'updateBackgroundImage', file: File | null): void
|
||||
(e: 'updateUpDirection', direction: UpDirection): void
|
||||
(e: 'updateMaterialMode', mode: MaterialMode): void
|
||||
(e: 'updateEdgeThreshold', value: number): void
|
||||
(e: 'exportModel', format: string): void
|
||||
}>()
|
||||
|
||||
const backgroundColor = ref(props.backgroundColor)
|
||||
const showGrid = ref(props.showGrid)
|
||||
const showPreview = ref(props.showPreview)
|
||||
const lightIntensity = ref(props.lightIntensity)
|
||||
const upDirection = ref(props.upDirection || 'original')
|
||||
const materialMode = ref(props.materialMode || 'original')
|
||||
const showLightIntensityButton = ref(props.showLightIntensityButton)
|
||||
const fov = ref(props.fov)
|
||||
const showFOVButton = ref(props.showFOVButton)
|
||||
const showPreviewButton = ref(props.showPreviewButton)
|
||||
const hasBackgroundImage = ref(props.hasBackgroundImage)
|
||||
const edgeThreshold = ref(props.edgeThreshold)
|
||||
|
||||
const switchCamera = () => {
|
||||
emit('switchCamera')
|
||||
}
|
||||
|
||||
const togglePreview = () => {
|
||||
showPreview.value = !showPreview.value
|
||||
emit('togglePreview', showPreview.value)
|
||||
}
|
||||
|
||||
const handleToggleGrid = (value: boolean) => {
|
||||
emit('toggleGrid', value)
|
||||
}
|
||||
|
||||
const handleBackgroundColorChange = (value: string) => {
|
||||
emit('updateBackgroundColor', value)
|
||||
}
|
||||
|
||||
const handleBackgroundImageUpdate = (file: File | null) => {
|
||||
emit('updateBackgroundImage', file)
|
||||
}
|
||||
|
||||
const handleUpdateUpDirection = (direction: UpDirection) => {
|
||||
emit('updateUpDirection', direction)
|
||||
}
|
||||
|
||||
const handleUpdateMaterialMode = (mode: MaterialMode) => {
|
||||
emit('updateMaterialMode', mode)
|
||||
}
|
||||
|
||||
const handleUpdateEdgeThreshold = (value: number) => {
|
||||
emit('updateEdgeThreshold', value)
|
||||
}
|
||||
|
||||
const handleUpdateLightIntensity = (value: number) => {
|
||||
emit('updateLightIntensity', value)
|
||||
}
|
||||
|
||||
const handleUpdateFOV = (value: number) => {
|
||||
emit('updateFOV', value)
|
||||
}
|
||||
|
||||
const handleExportModel = (format: string) => {
|
||||
emit('exportModel', format)
|
||||
}
|
||||
@@ -247,101 +166,6 @@ const closeSlider = (e: MouseEvent) => {
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.upDirection,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
upDirection.value = newValue
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.backgroundColor,
|
||||
(newValue) => {
|
||||
backgroundColor.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.fov,
|
||||
(newValue) => {
|
||||
fov.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.lightIntensity,
|
||||
(newValue) => {
|
||||
lightIntensity.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.showFOVButton,
|
||||
(newValue) => {
|
||||
showFOVButton.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.showLightIntensityButton,
|
||||
(newValue) => {
|
||||
showLightIntensityButton.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.upDirection,
|
||||
(newValue) => {
|
||||
upDirection.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.materialMode,
|
||||
(newValue) => {
|
||||
materialMode.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.showPreviewButton,
|
||||
(newValue) => {
|
||||
showPreviewButton.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.showPreview,
|
||||
(newValue) => {
|
||||
showPreview.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.hasBackgroundImage,
|
||||
(newValue) => {
|
||||
hasBackgroundImage.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.materialMode,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
materialMode.value = newValue
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.edgeThreshold,
|
||||
(newValue) => {
|
||||
edgeThreshold.value = newValue
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', closeSlider)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user