mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 06:20:11 +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)
177 lines
5.0 KiB
Vue
177 lines
5.0 KiB
Vue
<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">
|
|
<i class="pi pi-bars text-lg text-white" />
|
|
</Button>
|
|
|
|
<div
|
|
v-show="isMenuOpen"
|
|
class="absolute top-0 left-12 rounded-lg bg-black/50 shadow-lg"
|
|
>
|
|
<div class="flex flex-col">
|
|
<Button
|
|
v-for="category in availableCategories"
|
|
:key="category"
|
|
class="p-button-text flex w-full items-center justify-start"
|
|
:class="{ 'bg-smoke-600': activeCategory === category }"
|
|
@click="selectCategory(category)"
|
|
>
|
|
<i :class="getCategoryIcon(category)" />
|
|
<span class="whitespace-nowrap text-white">{{
|
|
$t(categoryLabels[category])
|
|
}}</span>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-show="activeCategory" class="rounded-lg bg-smoke-700/30">
|
|
<SceneControls
|
|
v-if="showSceneControls"
|
|
ref="sceneControlsRef"
|
|
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="showModelControls"
|
|
ref="modelControlsRef"
|
|
v-model:material-mode="modelConfig!.materialMode"
|
|
v-model:up-direction="modelConfig!.upDirection"
|
|
/>
|
|
|
|
<CameraControls
|
|
v-if="showCameraControls"
|
|
ref="cameraControlsRef"
|
|
v-model:camera-type="cameraConfig!.cameraType"
|
|
v-model:fov="cameraConfig!.fov"
|
|
/>
|
|
|
|
<LightControls
|
|
v-if="showLightControls"
|
|
ref="lightControlsRef"
|
|
v-model:light-intensity="lightConfig!.intensity"
|
|
v-model:material-mode="modelConfig!.materialMode"
|
|
/>
|
|
|
|
<ExportControls
|
|
v-if="showExportControls"
|
|
ref="exportControlsRef"
|
|
@export-model="handleExportModel"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import Button from 'primevue/button'
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
|
|
import CameraControls from '@/components/load3d/controls/CameraControls.vue'
|
|
import ExportControls from '@/components/load3d/controls/ExportControls.vue'
|
|
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 {
|
|
CameraConfig,
|
|
LightConfig,
|
|
ModelConfig,
|
|
SceneConfig
|
|
} from '@/extensions/core/load3d/interfaces'
|
|
|
|
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')
|
|
const categoryLabels: Record<string, string> = {
|
|
scene: 'load3d.scene',
|
|
model: 'load3d.model',
|
|
camera: 'load3d.camera',
|
|
light: 'load3d.light',
|
|
export: 'load3d.export'
|
|
}
|
|
|
|
const availableCategories = computed(() => {
|
|
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
|
|
}
|
|
|
|
const selectCategory = (category: string) => {
|
|
activeCategory.value = category
|
|
isMenuOpen.value = false
|
|
}
|
|
|
|
const getCategoryIcon = (category: string) => {
|
|
const icons = {
|
|
scene: 'pi pi-image',
|
|
model: 'pi pi-box',
|
|
camera: 'pi pi-camera',
|
|
light: 'pi pi-sun',
|
|
export: 'pi pi-download'
|
|
}
|
|
// @ts-expect-error fixme ts strict error
|
|
return `${icons[category]} text-white text-lg`
|
|
}
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'updateBackgroundImage', file: File | null): void
|
|
(e: 'exportModel', format: string): void
|
|
}>()
|
|
|
|
const handleBackgroundImageUpdate = (file: File | null) => {
|
|
emit('updateBackgroundImage', file)
|
|
}
|
|
|
|
const handleExportModel = (format: string) => {
|
|
emit('exportModel', format)
|
|
}
|
|
|
|
const closeSlider = (e: MouseEvent) => {
|
|
const target = e.target as HTMLElement
|
|
|
|
if (!target.closest('.show-menu')) {
|
|
isMenuOpen.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
document.addEventListener('click', closeSlider)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
document.removeEventListener('click', closeSlider)
|
|
})
|
|
</script>
|