mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-20 23:04:06 +00:00
feat: support open 3d viewer in media asset panel (#6703)
## Summary Add support for previewing 3D assets directly in the Media Asset Panel. ## Changes - **3D Asset Preview**: Clicking on 3D assets (`.glb`, `.gltf`, etc.) in the Media Asset Panel now opens the full 3D viewer ## Screenshots https://github.com/user-attachments/assets/38808712-acc8-42aa-9f11-8d8bf2387b20 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6703-feat-support-open-3d-viewer-in-media-asset-panel-2ab6d73d3650811dbff9ecb570a0a878) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -27,7 +27,11 @@ interface Load3dViewerState {
|
||||
materialMode: MaterialMode
|
||||
}
|
||||
|
||||
export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
/**
|
||||
* @param node Optional node - if provided, viewer works in node mode with apply/restore
|
||||
* If not provided, viewer works in standalone mode for asset preview
|
||||
*/
|
||||
export const useLoad3dViewer = (node?: LGraphNode) => {
|
||||
const backgroundColor = ref('')
|
||||
const showGrid = ref(true)
|
||||
const cameraType = ref<CameraType>('perspective')
|
||||
@@ -40,6 +44,7 @@ export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
const materialMode = ref<MaterialMode>('original')
|
||||
const needApplyChanges = ref(true)
|
||||
const isPreview = ref(false)
|
||||
const isStandaloneMode = ref(false)
|
||||
|
||||
let load3d: Load3d | null = null
|
||||
let sourceLoad3d: Load3d | null = null
|
||||
@@ -166,11 +171,14 @@ export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Initialize viewer in node mode (with source Load3d)
|
||||
*/
|
||||
const initializeViewer = async (
|
||||
containerRef: HTMLElement,
|
||||
source: Load3d
|
||||
) => {
|
||||
if (!containerRef) return
|
||||
if (!containerRef || !node) return
|
||||
|
||||
sourceLoad3d = source
|
||||
|
||||
@@ -263,6 +271,52 @@ export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize viewer in standalone mode (for asset preview)
|
||||
*/
|
||||
const initializeStandaloneViewer = async (
|
||||
containerRef: HTMLElement,
|
||||
modelUrl: string
|
||||
) => {
|
||||
if (!containerRef) return
|
||||
|
||||
try {
|
||||
isStandaloneMode.value = true
|
||||
|
||||
const mockNode = {
|
||||
widgets: [
|
||||
{ name: 'width', value: 800 },
|
||||
{ name: 'height', value: 600 }
|
||||
],
|
||||
properties: {},
|
||||
graph: null,
|
||||
type: 'AssetPreview'
|
||||
} as unknown as LGraphNode
|
||||
|
||||
load3d = new Load3d(containerRef, {
|
||||
node: mockNode,
|
||||
disablePreview: true,
|
||||
isViewerMode: true
|
||||
})
|
||||
|
||||
await load3d.loadModel(modelUrl)
|
||||
|
||||
backgroundColor.value = '#282828'
|
||||
showGrid.value = true
|
||||
cameraType.value = 'perspective'
|
||||
fov.value = 75
|
||||
lightIntensity.value = 1
|
||||
backgroundRenderMode.value = 'tiled'
|
||||
upDirection.value = 'original'
|
||||
materialMode.value = 'original'
|
||||
|
||||
isPreview.value = true
|
||||
} catch (error) {
|
||||
console.error('Error initializing standalone 3D viewer:', error)
|
||||
useToastStore().addAlert('Failed to load 3D model')
|
||||
}
|
||||
}
|
||||
|
||||
const exportModel = async (format: string) => {
|
||||
if (!load3d) return
|
||||
|
||||
@@ -289,6 +343,8 @@ export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
}
|
||||
|
||||
const restoreInitialState = () => {
|
||||
if (!node) return
|
||||
|
||||
const nodeValue = node
|
||||
|
||||
needApplyChanges.value = false
|
||||
@@ -324,7 +380,7 @@ export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
}
|
||||
|
||||
const applyChanges = async () => {
|
||||
if (!sourceLoad3d || !load3d) return false
|
||||
if (!node || !sourceLoad3d || !load3d) return false
|
||||
|
||||
const viewerCameraState = load3d.getCameraState()
|
||||
const nodeValue = node
|
||||
@@ -378,6 +434,10 @@ export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
return
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const resourceFolder =
|
||||
(node.properties['Resource Folder'] as string) || ''
|
||||
@@ -403,6 +463,10 @@ export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
return
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const resourceFolder =
|
||||
(node.properties['Resource Folder'] as string) || ''
|
||||
@@ -460,9 +524,11 @@ export const useLoad3dViewer = (node: LGraphNode) => {
|
||||
materialMode,
|
||||
needApplyChanges,
|
||||
isPreview,
|
||||
isStandaloneMode,
|
||||
|
||||
// Methods
|
||||
initializeViewer,
|
||||
initializeStandaloneViewer,
|
||||
exportModel,
|
||||
handleResize,
|
||||
handleMouseEnter,
|
||||
|
||||
Reference in New Issue
Block a user