[3d] support using image as background (#2657)

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Terry Jia
2025-02-20 20:05:54 -05:00
committed by GitHub
parent 02d77002c9
commit c3c6ec627b
15 changed files with 475 additions and 72 deletions

View File

@@ -9,6 +9,7 @@
:fov="fov"
:cameraType="cameraType"
:showPreview="showPreview"
:backgroundImage="backgroundImage"
@materialModeChange="listenMaterialModeChange"
@backgroundColorChange="listenBackgroundColorChange"
@lightIntensityChange="listenLightIntensityChange"
@@ -16,6 +17,7 @@
@cameraTypeChange="listenCameraTypeChange"
@showGridChange="listenShowGridChange"
@showPreviewChange="listenShowPreviewChange"
@backgroundImageChange="listenBackgroundImageChange"
/>
<Load3DControls
:backgroundColor="backgroundColor"
@@ -27,6 +29,8 @@
:showFOVButton="showFOVButton"
:showPreviewButton="showPreviewButton"
:cameraType="cameraType"
:hasBackgroundImage="hasBackgroundImage"
@updateBackgroundImage="handleBackgroundImageUpdate"
@switchCamera="switchCamera"
@toggleGrid="toggleGrid"
@updateBackgroundColor="handleBackgroundColorChange"
@@ -42,6 +46,7 @@ import { computed, ref } from 'vue'
import Load3DControls from '@/components/load3d/Load3DControls.vue'
import Load3DScene from '@/components/load3d/Load3DScene.vue'
import Load3dUtils from '@/extensions/core/load3d/Load3dUtils'
const props = defineProps<{
node: any
@@ -57,6 +62,8 @@ const showLightIntensityButton = ref(true)
const fov = ref(75)
const showFOVButton = ref(true)
const cameraType = ref<'perspective' | 'orthographic'>('perspective')
const hasBackgroundImage = ref(false)
const backgroundImage = ref('')
const showPreviewButton = computed(() => {
return !props.type.includes('Preview')
@@ -89,6 +96,19 @@ const handleUpdateLightIntensity = (value: number) => {
node.value.properties['Light Intensity'] = lightIntensity.value
}
const handleBackgroundImageUpdate = async (file: File | null) => {
if (!file) {
hasBackgroundImage.value = false
backgroundImage.value = ''
node.value.properties['Background Image'] = ''
return
}
backgroundImage.value = await Load3dUtils.uploadFile(file)
node.value.properties['Background Image'] = backgroundImage.value
}
const handleUpdateFOV = (value: number) => {
fov.value = value
@@ -137,4 +157,12 @@ const listenShowGridChange = (value: boolean) => {
const listenShowPreviewChange = (value: boolean) => {
showPreview.value = value
}
const listenBackgroundImageChange = (value: string) => {
backgroundImage.value = value
if (backgroundImage.value && backgroundImage.value !== '') {
hasBackgroundImage.value = true
}
}
</script>

View File

@@ -15,6 +15,7 @@
:playing="playing"
:selectedSpeed="selectedSpeed"
:selectedAnimation="selectedAnimation"
:backgroundImage="backgroundImage"
@materialModeChange="listenMaterialModeChange"
@backgroundColorChange="listenBackgroundColorChange"
@lightIntensityChange="listenLightIntensityChange"
@@ -22,6 +23,7 @@
@cameraTypeChange="listenCameraTypeChange"
@showGridChange="listenShowGridChange"
@showPreviewChange="listenShowPreviewChange"
@backgroundImageChange="listenBackgroundImageChange"
@animationListChange="animationListChange"
/>
<div class="absolute top-0 left-0 w-full h-full pointer-events-none">
@@ -35,6 +37,8 @@
:showFOVButton="showFOVButton"
:showPreviewButton="showPreviewButton"
:cameraType="cameraType"
:hasBackgroundImage="hasBackgroundImage"
@updateBackgroundImage="handleBackgroundImageUpdate"
@switchCamera="switchCamera"
@toggleGrid="toggleGrid"
@updateBackgroundColor="handleBackgroundColorChange"
@@ -60,6 +64,7 @@ import Load3DAnimationControls from '@/components/load3d/Load3DAnimationControls
import Load3DAnimationScene from '@/components/load3d/Load3DAnimationScene.vue'
import Load3DControls from '@/components/load3d/Load3DControls.vue'
import type { AnimationItem } from '@/extensions/core/load3d/Load3dAnimation'
import Load3dUtils from '@/extensions/core/load3d/Load3dUtils'
const props = defineProps<{
node: any
@@ -75,11 +80,13 @@ const showLightIntensityButton = ref(true)
const fov = ref(75)
const showFOVButton = ref(true)
const cameraType = ref<'perspective' | 'orthographic'>('perspective')
const hasBackgroundImage = ref(false)
const animations = ref<AnimationItem[]>([])
const playing = ref(false)
const selectedSpeed = ref(1)
const selectedAnimation = ref(0)
const backgroundImage = ref('')
const showPreviewButton = computed(() => {
return !props.type.includes('Preview')
@@ -112,6 +119,19 @@ const handleUpdateLightIntensity = (value: number) => {
node.value.properties['Light Intensity'] = lightIntensity.value
}
const handleBackgroundImageUpdate = async (file: File | null) => {
if (!file) {
hasBackgroundImage.value = false
backgroundImage.value = ''
node.value.properties['Background Image'] = ''
return
}
backgroundImage.value = await Load3dUtils.uploadFile(file)
node.value.properties['Background Image'] = backgroundImage.value
}
const handleUpdateFOV = (value: number) => {
fov.value = value
@@ -177,4 +197,12 @@ const listenShowGridChange = (value: boolean) => {
const listenShowPreviewChange = (value: boolean) => {
showPreview.value = value
}
const listenBackgroundImageChange = (value: string) => {
backgroundImage.value = value
if (backgroundImage.value && backgroundImage.value !== '') {
hasBackgroundImage.value = true
}
}
</script>

View File

@@ -9,6 +9,7 @@
:cameraType="cameraType"
:showPreview="showPreview"
:extraListeners="animationListeners"
:backgroundImage="backgroundImage"
@materialModeChange="listenMaterialModeChange"
@backgroundColorChange="listenBackgroundColorChange"
@lightIntensityChange="listenLightIntensityChange"
@@ -39,6 +40,7 @@ const props = defineProps<{
playing: boolean
selectedSpeed: number
selectedAnimation: number
backgroundImage: string
}>()
const node = ref(props.node)

View File

@@ -1,6 +1,6 @@
<template>
<div
class="absolute top-2 left-2 flex flex-col gap-2 pointer-events-auto z-20"
class="absolute top-2 left-2 flex flex-col pointer-events-auto z-20 bg-gray-700 bg-opacity-30 rounded-lg"
>
<Button class="p-button-rounded p-button-text" @click="switchCamera">
<i
@@ -20,21 +20,60 @@
></i>
</Button>
<Button class="p-button-rounded p-button-text" @click="openColorPicker">
<i
class="pi pi-palette text-white text-lg"
v-tooltip.right="{ value: t('load3d.backgroundColor'), showDelay: 300 }"
></i>
<input
type="color"
ref="colorPickerRef"
:value="backgroundColor"
@input="
updateBackgroundColor(($event.target as HTMLInputElement).value)
"
class="absolute opacity-0 w-0 h-0 p-0 m-0 pointer-events-none"
/>
</Button>
<div v-if="!hasBackgroundImage">
<Button class="p-button-rounded p-button-text" @click="openColorPicker">
<i
class="pi pi-palette text-white text-lg"
v-tooltip.right="{
value: t('load3d.backgroundColor'),
showDelay: 300
}"
></i>
<input
type="color"
ref="colorPickerRef"
:value="backgroundColor"
@input="
updateBackgroundColor(($event.target as HTMLInputElement).value)
"
class="absolute opacity-0 w-0 h-0 p-0 m-0 pointer-events-none"
/>
</Button>
</div>
<div v-if="!hasBackgroundImage">
<Button class="p-button-rounded p-button-text" @click="openImagePicker">
<i
class="pi pi-image text-white text-lg"
v-tooltip.right="{
value: t('load3d.uploadBackgroundImage'),
showDelay: 300
}"
></i>
<input
type="file"
ref="imagePickerRef"
accept="image/*"
@change="uploadBackgroundImage"
class="absolute opacity-0 w-0 h-0 p-0 m-0 pointer-events-none"
/>
</Button>
</div>
<div v-if="hasBackgroundImage">
<Button
class="p-button-rounded p-button-text"
@click="removeBackgroundImage"
>
<i
class="pi pi-times text-white text-lg"
v-tooltip.right="{
value: t('load3d.removeBackgroundImage'),
showDelay: 300
}"
></i>
</Button>
</div>
<div class="relative show-light-intensity" v-if="showLightIntensityButton">
<Button
@@ -123,6 +162,7 @@ const props = defineProps<{
showFOVButton: boolean
showPreviewButton: boolean
cameraType: 'perspective' | 'orthographic'
hasBackgroundImage?: boolean
}>()
const emit = defineEmits<{
@@ -132,6 +172,7 @@ const emit = defineEmits<{
(e: 'updateLightIntensity', value: number): void
(e: 'updateFOV', value: number): void
(e: 'togglePreview', value: boolean): void
(e: 'updateBackgroundImage', file: File | null): void
}>()
const backgroundColor = ref(props.backgroundColor)
@@ -145,6 +186,8 @@ const fov = ref(props.fov)
const showFOV = ref(false)
const showFOVButton = ref(props.showFOVButton)
const showPreviewButton = ref(props.showPreviewButton)
const hasBackgroundImage = ref(props.hasBackgroundImage)
const imagePickerRef = ref<HTMLInputElement | null>(null)
const switchCamera = () => {
emit('switchCamera')
@@ -196,6 +239,26 @@ const closeSlider = (e: MouseEvent) => {
}
}
const openImagePicker = () => {
imagePickerRef.value?.click()
}
const uploadBackgroundImage = (event: Event) => {
const input = event.target as HTMLInputElement
hasBackgroundImage.value = true
if (input.files && input.files[0]) {
emit('updateBackgroundImage', input.files[0])
}
}
const removeBackgroundImage = () => {
hasBackgroundImage.value = false
emit('updateBackgroundImage', null)
}
watch(
() => props.backgroundColor,
(newValue) => {
@@ -245,6 +308,13 @@ watch(
}
)
watch(
() => props.hasBackgroundImage,
(newValue) => {
hasBackgroundImage.value = newValue
}
)
onMounted(() => {
document.addEventListener('click', closeSlider)
})

View File

@@ -19,6 +19,7 @@ const props = defineProps<{
fov: number
cameraType: 'perspective' | 'orthographic'
showPreview: boolean
backgroundImage: string
extraListeners?: Record<string, (value: any) => void>
}>()
@@ -34,7 +35,8 @@ const eventConfig = {
fovChange: (value: number) => emit('fovChange', value),
cameraTypeChange: (value: string) => emit('cameraTypeChange', value),
showGridChange: (value: boolean) => emit('showGridChange', value),
showPreviewChange: (value: boolean) => emit('showPreviewChange', value)
showPreviewChange: (value: boolean) => emit('showPreviewChange', value),
backgroundImageChange: (value: string) => emit('backgroundImageChange', value)
} as const
watchEffect(() => {
@@ -47,6 +49,7 @@ watchEffect(() => {
rawLoad3d.setFOV(props.fov)
rawLoad3d.toggleCamera(props.cameraType)
rawLoad3d.togglePreview(props.showPreview)
rawLoad3d.setBackgroundImage(props.backgroundImage)
}
})
@@ -58,6 +61,7 @@ const emit = defineEmits<{
(e: 'cameraTypeChange', cameraType: string): void
(e: 'showGridChange', showGrid: boolean): void
(e: 'showPreviewChange', showPreview: boolean): void
(e: 'backgroundImageChange', backgroundImage: string): void
}>()
const handleEvents = (action: 'add' | 'remove') => {