From b2918a4cf64647f6c3b35bc63db9fdc3946200cc Mon Sep 17 00:00:00 2001 From: Terry Jia Date: Sun, 8 Jun 2025 04:20:56 -0400 Subject: [PATCH] Improve bg color image logic (#4095) --- src/extensions/core/load3d/Load3d.ts | 22 +-- src/extensions/core/load3d/Load3dAnimation.ts | 2 +- src/extensions/core/load3d/PreviewManager.ts | 164 +++++++++++------- src/extensions/core/load3d/SceneManager.ts | 152 ++++++++++------ src/extensions/core/load3d/interfaces.ts | 2 - 5 files changed, 213 insertions(+), 129 deletions(-) diff --git a/src/extensions/core/load3d/Load3d.ts b/src/extensions/core/load3d/Load3d.ts index ddd5d6145..0a14dcc31 100644 --- a/src/extensions/core/load3d/Load3d.ts +++ b/src/extensions/core/load3d/Load3d.ts @@ -163,7 +163,7 @@ class Load3d { this.renderMainScene() if (this.previewManager.showPreview) { - this.renderPreviewScene() + this.previewManager.renderPreview() } this.resetViewport() @@ -183,7 +183,6 @@ class Load3d { this.renderer.setScissor(0, 0, width, height) this.renderer.setScissorTest(true) - this.renderer.clear() this.sceneManager.renderBackground() this.renderer.render( this.sceneManager.scene, @@ -191,10 +190,6 @@ class Load3d { ) } - renderPreviewScene(): void { - this.previewManager.renderPreview() - } - resetViewport(): void { const width = this.renderer.domElement.clientWidth const height = this.renderer.domElement.clientHeight @@ -231,7 +226,7 @@ class Load3d { this.renderMainScene() if (this.previewManager.showPreview) { - this.renderPreviewScene() + this.previewManager.renderPreview() } this.resetViewport() @@ -321,6 +316,9 @@ class Load3d { setBackgroundColor(color: string): void { this.sceneManager.setBackgroundColor(color) + + this.previewManager.setPreviewBackgroundColor(color) + this.forceRender() } @@ -337,9 +335,9 @@ class Load3d { removeBackgroundImage(): void { this.sceneManager.removeBackgroundImage() - if (this.previewManager.previewCamera) { - this.previewManager.updateBackgroundTexture(null) - } + this.previewManager.setPreviewBackgroundColor( + this.sceneManager.currentBackgroundColor + ) this.forceRender() } @@ -366,10 +364,6 @@ class Load3d { setCameraState(state: CameraState): void { this.cameraManager.setCameraState(state) - if (this.previewManager.showPreview) { - this.previewManager.syncWithMainCamera() - } - this.forceRender() } diff --git a/src/extensions/core/load3d/Load3dAnimation.ts b/src/extensions/core/load3d/Load3dAnimation.ts index 597867588..207b27497 100644 --- a/src/extensions/core/load3d/Load3dAnimation.ts +++ b/src/extensions/core/load3d/Load3dAnimation.ts @@ -53,7 +53,7 @@ class Load3dAnimation extends Load3d { this.renderMainScene() if (this.previewManager.showPreview) { - this.renderPreviewScene() + this.previewManager.renderPreview() } this.resetViewport() diff --git a/src/extensions/core/load3d/PreviewManager.ts b/src/extensions/core/load3d/PreviewManager.ts index 3f23dac61..a8d954817 100644 --- a/src/extensions/core/load3d/PreviewManager.ts +++ b/src/extensions/core/load3d/PreviewManager.ts @@ -23,7 +23,8 @@ export class PreviewManager implements PreviewManagerInterface { private previewBackgroundMesh: THREE.Mesh | null = null private previewBackgroundTexture: THREE.Texture | null = null - private previewBackgroundColor: THREE.Color = new THREE.Color(0x282828) + private previewBackgroundColorMaterial: THREE.MeshBasicMaterial | null = null + private currentBackgroundColor: THREE.Color = new THREE.Color(0x282828) constructor( scene: THREE.Scene, @@ -45,15 +46,24 @@ export class PreviewManager implements PreviewManagerInterface { this.previewBackgroundScene = backgroundScene.clone() this.previewBackgroundCamera = backgroundCamera.clone() + this.initPreviewBackgroundScene() + } + + private initPreviewBackgroundScene(): void { const planeGeometry = new THREE.PlaneGeometry(2, 2) - const planeMaterial = new THREE.MeshBasicMaterial({ - transparent: true, + + this.previewBackgroundColorMaterial = new THREE.MeshBasicMaterial({ + color: this.currentBackgroundColor.clone(), + transparent: false, depthWrite: false, depthTest: false, side: THREE.DoubleSide }) - this.previewBackgroundMesh = new THREE.Mesh(planeGeometry, planeMaterial) + this.previewBackgroundMesh = new THREE.Mesh( + planeGeometry, + this.previewBackgroundColorMaterial + ) this.previewBackgroundMesh.position.set(0, 0, 0) this.previewBackgroundScene.add(this.previewBackgroundMesh) } @@ -65,9 +75,15 @@ export class PreviewManager implements PreviewManagerInterface { this.previewBackgroundTexture.dispose() } + if (this.previewBackgroundColorMaterial) { + this.previewBackgroundColorMaterial.dispose() + } + if (this.previewBackgroundMesh) { this.previewBackgroundMesh.geometry.dispose() - ;(this.previewBackgroundMesh.material as THREE.Material).dispose() + if (this.previewBackgroundMesh.material instanceof THREE.Material) { + this.previewBackgroundMesh.material.dispose() + } } } @@ -174,10 +190,24 @@ export class PreviewManager implements PreviewManagerInterface { return { left, bottom, width, height } } - syncWithMainCamera(): void { - if (!this.showPreview) return + renderPreview(): void { + const viewport = this.getPreviewViewport() + if (!viewport) return - this.previewCamera = this.getActiveCamera().clone() + const renderer = this.getRenderer() + + const originalClearColor = renderer.getClearColor(new THREE.Color()) + const originalClearAlpha = renderer.getClearAlpha() + + if ( + !this.previewCamera || + (this.getActiveCamera() instanceof THREE.PerspectiveCamera && + !(this.previewCamera instanceof THREE.PerspectiveCamera)) || + (this.getActiveCamera() instanceof THREE.OrthographicCamera && + !(this.previewCamera instanceof THREE.OrthographicCamera)) + ) { + this.previewCamera = this.getActiveCamera().clone() + } this.previewCamera.position.copy(this.getActiveCamera().position) this.previewCamera.rotation.copy(this.getActiveCamera().rotation) @@ -188,16 +218,16 @@ export class PreviewManager implements PreviewManagerInterface { const activeOrtho = this.getActiveCamera() as THREE.OrthographicCamera const previewOrtho = this.previewCamera as THREE.OrthographicCamera - previewOrtho.zoom = activeOrtho.zoom - const frustumHeight = (activeOrtho.top - activeOrtho.bottom) / activeOrtho.zoom + const frustumWidth = frustumHeight * aspect previewOrtho.top = frustumHeight / 2 previewOrtho.left = -frustumWidth / 2 previewOrtho.right = frustumWidth / 2 previewOrtho.bottom = -frustumHeight / 2 + previewOrtho.zoom = 1 previewOrtho.updateProjectionMatrix() } else { @@ -213,18 +243,6 @@ export class PreviewManager implements PreviewManagerInterface { } this.previewCamera.lookAt(this.getControls().target) - } - - renderPreview(): void { - const viewport = this.getPreviewViewport() - if (!viewport) return - - const renderer = this.getRenderer() - - const originalClearColor = renderer.getClearColor(new THREE.Color()) - const originalClearAlpha = renderer.getClearAlpha() - - this.syncWithMainCamera() renderer.setViewport( viewport.left, @@ -239,42 +257,46 @@ export class PreviewManager implements PreviewManagerInterface { viewport.height ) - renderer.setClearColor(this.previewBackgroundColor, 1.0) + renderer.setClearColor(0x000000, 0) renderer.clear() - if (this.previewBackgroundMesh && this.previewBackgroundTexture) { - const material = this.previewBackgroundMesh - .material as THREE.MeshBasicMaterial - if (material.map) { - const currentToneMapping = renderer.toneMapping - const currentExposure = renderer.toneMappingExposure - - renderer.toneMapping = THREE.NoToneMapping - renderer.render( - this.previewBackgroundScene, - this.previewBackgroundCamera - ) - - renderer.toneMapping = currentToneMapping - renderer.toneMappingExposure = currentExposure - } - } + this.renderPreviewBackground(renderer) renderer.render(this.scene, this.previewCamera) renderer.setClearColor(originalClearColor, originalClearAlpha) } - setPreviewBackgroundColor(color: string | number): void { - this.previewBackgroundColor.set(color) + private renderPreviewBackground(renderer: THREE.WebGLRenderer): void { + if (this.previewBackgroundMesh) { + const currentToneMapping = renderer.toneMapping + const currentExposure = renderer.toneMappingExposure + + renderer.toneMapping = THREE.NoToneMapping + renderer.render(this.previewBackgroundScene, this.previewBackgroundCamera) + + renderer.toneMapping = currentToneMapping + renderer.toneMappingExposure = currentExposure + } } - getPreviewBackgroundColor(): THREE.Color { - return this.previewBackgroundColor.clone() - } + setPreviewBackgroundColor(color: string | number | THREE.Color): void { + this.currentBackgroundColor.set(color) - updatePreviewRender(): void { - this.syncWithMainCamera() + if (!this.previewBackgroundMesh || !this.previewBackgroundColorMaterial) { + this.initPreviewBackgroundScene() + } + + this.previewBackgroundColorMaterial!.color.copy(this.currentBackgroundColor) + + if (this.previewBackgroundMesh) { + this.previewBackgroundMesh.material = this.previewBackgroundColorMaterial! + } + + if (this.previewBackgroundTexture) { + this.previewBackgroundTexture.dispose() + this.previewBackgroundTexture = null + } } togglePreview(showPreview: boolean): void { @@ -323,26 +345,42 @@ export class PreviewManager implements PreviewManagerInterface { } updateBackgroundTexture(texture: THREE.Texture | null): void { - if (this.previewBackgroundTexture) { - this.previewBackgroundTexture.dispose() - } + if (texture) { + if (this.previewBackgroundTexture) { + this.previewBackgroundTexture.dispose() + } - this.previewBackgroundTexture = texture + this.previewBackgroundTexture = texture - if (texture && this.previewBackgroundMesh) { - const material2 = this.previewBackgroundMesh - .material as THREE.MeshBasicMaterial - material2.map = texture - material2.needsUpdate = true + if (this.previewBackgroundMesh) { + const imageMaterial = new THREE.MeshBasicMaterial({ + map: texture, + transparent: true, + depthWrite: false, + depthTest: false, + side: THREE.DoubleSide + }) - this.previewBackgroundMesh.position.set(0, 0, 0) + if ( + this.previewBackgroundMesh.material instanceof THREE.Material && + this.previewBackgroundMesh.material !== + this.previewBackgroundColorMaterial + ) { + this.previewBackgroundMesh.material.dispose() + } - this.updateBackgroundSize( - this.previewBackgroundTexture, - this.previewBackgroundMesh, - this.targetWidth, - this.targetHeight - ) + this.previewBackgroundMesh.material = imageMaterial + this.previewBackgroundMesh.position.set(0, 0, 0) + + this.updateBackgroundSize( + this.previewBackgroundTexture, + this.previewBackgroundMesh, + this.targetWidth, + this.targetHeight + ) + } + } else { + this.setPreviewBackgroundColor(this.currentBackgroundColor) } } diff --git a/src/extensions/core/load3d/SceneManager.ts b/src/extensions/core/load3d/SceneManager.ts index cf6c71410..4f46dda4b 100644 --- a/src/extensions/core/load3d/SceneManager.ts +++ b/src/extensions/core/load3d/SceneManager.ts @@ -13,6 +13,10 @@ export class SceneManager implements SceneManagerInterface { backgroundMesh: THREE.Mesh | null = null backgroundTexture: THREE.Texture | null = null + backgroundColorMaterial: THREE.MeshBasicMaterial | null = null + currentBackgroundType: 'color' | 'image' = 'color' + currentBackgroundColor: string = '#282828' + private eventManager: EventManagerInterface private renderer: THREE.WebGLRenderer @@ -40,17 +44,28 @@ export class SceneManager implements SceneManagerInterface { this.backgroundScene = new THREE.Scene() this.backgroundCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, -1, 1) + this.initBackgroundScene() + } + + private initBackgroundScene(): void { const planeGeometry = new THREE.PlaneGeometry(2, 2) - const planeMaterial = new THREE.MeshBasicMaterial({ - transparent: true, + + this.backgroundColorMaterial = new THREE.MeshBasicMaterial({ + color: new THREE.Color(this.currentBackgroundColor), + transparent: false, depthWrite: false, depthTest: false, side: THREE.DoubleSide }) - this.backgroundMesh = new THREE.Mesh(planeGeometry, planeMaterial) + this.backgroundMesh = new THREE.Mesh( + planeGeometry, + this.backgroundColorMaterial + ) this.backgroundMesh.position.set(0, 0, 0) this.backgroundScene.add(this.backgroundMesh) + + this.renderer.setClearColor(0x000000, 0) } init(): void {} @@ -60,9 +75,15 @@ export class SceneManager implements SceneManagerInterface { this.backgroundTexture.dispose() } + if (this.backgroundColorMaterial) { + this.backgroundColorMaterial.dispose() + } + if (this.backgroundMesh) { this.backgroundMesh.geometry.dispose() - ;(this.backgroundMesh.material as THREE.Material).dispose() + if (this.backgroundMesh.material instanceof THREE.Material) { + this.backgroundMesh.material.dispose() + } } this.scene.clear() @@ -77,18 +98,39 @@ export class SceneManager implements SceneManagerInterface { } setBackgroundColor(color: string): void { - this.renderer.setClearColor(new THREE.Color(color)) + this.currentBackgroundColor = color + this.currentBackgroundType = 'color' + + if (!this.backgroundMesh || !this.backgroundColorMaterial) { + this.initBackgroundScene() + } + + this.backgroundColorMaterial!.color.set(color) + this.backgroundColorMaterial!.map = null + this.backgroundColorMaterial!.transparent = false + this.backgroundColorMaterial!.needsUpdate = true + + if (this.backgroundMesh) { + this.backgroundMesh.material = this.backgroundColorMaterial! + } + + if (this.backgroundTexture) { + this.backgroundTexture.dispose() + this.backgroundTexture = null + } + this.eventManager.emitEvent('backgroundColorChange', color) } async setBackgroundImage(uploadPath: string): Promise { - this.eventManager.emitEvent('backgroundImageLoadingStart', null) - if (uploadPath === '') { - this.removeBackgroundImage() + this.setBackgroundColor(this.currentBackgroundColor) + return } + this.eventManager.emitEvent('backgroundImageLoadingStart', null) + let imageUrl = Load3dUtils.getResourceURL( ...Load3dUtils.splitFilePath(uploadPath) ) @@ -110,12 +152,31 @@ export class SceneManager implements SceneManagerInterface { texture.colorSpace = THREE.SRGBColorSpace this.backgroundTexture = texture + this.currentBackgroundType = 'image' - const material = this.backgroundMesh?.material as THREE.MeshBasicMaterial - material.map = texture - material.needsUpdate = true + if (!this.backgroundMesh) { + this.initBackgroundScene() + } - this.backgroundMesh?.position.set(0, 0, 0) + const imageMaterial = new THREE.MeshBasicMaterial({ + map: texture, + transparent: true, + depthWrite: false, + depthTest: false, + side: THREE.DoubleSide + }) + + if (this.backgroundMesh) { + if ( + this.backgroundMesh.material !== this.backgroundColorMaterial && + this.backgroundMesh.material instanceof THREE.Material + ) { + this.backgroundMesh.material.dispose() + } + + this.backgroundMesh.material = imageMaterial + this.backgroundMesh.position.set(0, 0, 0) + } this.updateBackgroundSize( this.backgroundTexture, @@ -129,20 +190,12 @@ export class SceneManager implements SceneManagerInterface { } catch (error) { this.eventManager.emitEvent('backgroundImageLoadingEnd', null) console.error('Error loading background image:', error) + this.setBackgroundColor(this.currentBackgroundColor) } } removeBackgroundImage(): void { - if (this.backgroundMesh) { - const material = this.backgroundMesh.material as THREE.MeshBasicMaterial - material.map = null - material.needsUpdate = true - } - - if (this.backgroundTexture) { - this.backgroundTexture.dispose() - this.backgroundTexture = null - } + this.setBackgroundColor(this.currentBackgroundColor) this.eventManager.emitEvent('backgroundImageLoadingEnd', null) } @@ -172,7 +225,11 @@ export class SceneManager implements SceneManagerInterface { } handleResize(width: number, height: number): void { - if (this.backgroundTexture && this.backgroundMesh) { + if ( + this.backgroundTexture && + this.backgroundMesh && + this.currentBackgroundType === 'image' + ) { this.updateBackgroundSize( this.backgroundTexture, this.backgroundMesh, @@ -183,18 +240,25 @@ export class SceneManager implements SceneManagerInterface { } renderBackground(): void { - if (this.backgroundMesh && this.backgroundTexture) { - const material = this.backgroundMesh.material as THREE.MeshBasicMaterial - if (material.map) { - const currentToneMapping = this.renderer.toneMapping - const currentExposure = this.renderer.toneMappingExposure + if (this.backgroundMesh) { + const currentToneMapping = this.renderer.toneMapping + const currentExposure = this.renderer.toneMappingExposure - this.renderer.toneMapping = THREE.NoToneMapping - this.renderer.render(this.backgroundScene, this.backgroundCamera) + this.renderer.toneMapping = THREE.NoToneMapping + this.renderer.render(this.backgroundScene, this.backgroundCamera) - this.renderer.toneMapping = currentToneMapping - this.renderer.toneMappingExposure = currentExposure - } + this.renderer.toneMapping = currentToneMapping + this.renderer.toneMappingExposure = currentExposure + } + } + + getCurrentBackgroundInfo(): { type: 'color' | 'image'; value: string } { + return { + type: this.currentBackgroundType, + value: + this.currentBackgroundType === 'color' + ? this.currentBackgroundColor + : '' } } @@ -210,8 +274,6 @@ export class SceneManager implements SceneManagerInterface { new THREE.Color() ) const originalClearAlpha = this.renderer.getClearAlpha() - const originalToneMapping = this.renderer.toneMapping - const originalExposure = this.renderer.toneMappingExposure const originalOutputColorSpace = this.renderer.outputColorSpace this.renderer.setSize(width, height) @@ -237,7 +299,11 @@ export class SceneManager implements SceneManagerInterface { orthographicCamera.updateProjectionMatrix() } - if (this.backgroundTexture && this.backgroundMesh) { + if ( + this.backgroundTexture && + this.backgroundMesh && + this.currentBackgroundType === 'image' + ) { this.updateBackgroundSize( this.backgroundTexture, this.backgroundMesh, @@ -252,19 +318,7 @@ export class SceneManager implements SceneManagerInterface { >() this.renderer.clear() - - if (this.backgroundMesh && this.backgroundTexture) { - const material = this.backgroundMesh - .material as THREE.MeshBasicMaterial - - if (material.map) { - this.renderer.toneMapping = THREE.NoToneMapping - this.renderer.render(this.backgroundScene, this.backgroundCamera) - this.renderer.toneMapping = originalToneMapping - this.renderer.toneMappingExposure = originalExposure - } - } - + this.renderBackground() this.renderer.render(this.scene, this.getActiveCamera()) const sceneData = this.renderer.domElement.toDataURL('image/png') diff --git a/src/extensions/core/load3d/interfaces.ts b/src/extensions/core/load3d/interfaces.ts index 70281af84..603740518 100644 --- a/src/extensions/core/load3d/interfaces.ts +++ b/src/extensions/core/load3d/interfaces.ts @@ -106,7 +106,6 @@ export interface PreviewManagerInterface extends BaseManager { previewWidth: number createCapturePreview(container: Element | HTMLElement): void updatePreviewSize(): void - updatePreviewRender(): void togglePreview(showPreview: boolean): void setTargetSize(width: number, height: number): void handleResize(): void @@ -118,7 +117,6 @@ export interface PreviewManagerInterface extends BaseManager { height: number } | null renderPreview(): void - syncWithMainCamera(): void } export interface EventManagerInterface {