[3d] support output normal and lineart at once (#3122)

This commit is contained in:
Terry Jia
2025-03-18 10:51:53 -04:00
committed by GitHub
parent e8997a7653
commit 52bad3d0d1
8 changed files with 194 additions and 13 deletions

View File

@@ -1,6 +1,11 @@
<template>
<div class="relative w-full h-full">
<div
class="relative w-full h-full"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<Load3DScene
ref="load3DSceneRef"
:node="node"
:type="type"
:backgroundColor="backgroundColor"
@@ -90,11 +95,24 @@ const backgroundImage = ref('')
const upDirection = ref<UpDirection>('original')
const materialMode = ref<MaterialMode>('original')
const edgeThreshold = ref(85)
const load3DSceneRef = ref(null)
const showPreviewButton = computed(() => {
return !type.includes('Preview')
})
const handleMouseEnter = () => {
if (load3DSceneRef.value?.load3d) {
load3DSceneRef.value.load3d.updateStatusMouseOnScene(true)
}
}
const handleMouseLeave = () => {
if (load3DSceneRef.value?.load3d) {
load3DSceneRef.value.load3d.updateStatusMouseOnScene(false)
}
}
const switchCamera = () => {
cameraType.value =
cameraType.value === 'perspective' ? 'orthographic' : 'perspective'

View File

@@ -1,6 +1,11 @@
<template>
<div class="relative w-full h-full">
<div
class="relative w-full h-full"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<Load3DAnimationScene
ref="load3DAnimationSceneRef"
:node="node"
:type="type"
:backgroundColor="backgroundColor"
@@ -110,6 +115,22 @@ const showPreviewButton = computed(() => {
return !type.includes('Preview')
})
const load3DAnimationSceneRef = ref(null)
const handleMouseEnter = () => {
const sceneRef = load3DAnimationSceneRef.value?.load3DSceneRef
if (sceneRef?.load3d) {
sceneRef.load3d.updateStatusMouseOnScene(true)
}
}
const handleMouseLeave = () => {
const sceneRef = load3DAnimationSceneRef.value?.load3DSceneRef
if (sceneRef?.load3d) {
sceneRef.load3d.updateStatusMouseOnScene(false)
}
}
const switchCamera = () => {
cameraType.value =
cameraType.value === 'perspective' ? 'orthographic' : 'perspective'

View File

@@ -183,4 +183,8 @@ const animationListeners = {
emit('animationListChange', newValue)
}
}
defineExpose({
load3DSceneRef
})
</script>

View File

@@ -124,21 +124,30 @@ app.registerExtension({
sceneWidget.serializeValue = async () => {
node.properties['Camera Info'] = load3d.getCameraState()
const { scene: imageData, mask: maskData } = await load3d.captureScene(
const {
scene: imageData,
mask: maskData,
normal: normalData,
lineart: lineartData
} = await load3d.captureScene(
width.value as number,
height.value as number
)
const [data, dataMask] = await Promise.all([
const [data, dataMask, dataNormal, dataLineart] = await Promise.all([
Load3dUtils.uploadTempImage(imageData, 'scene'),
Load3dUtils.uploadTempImage(maskData, 'scene_mask')
Load3dUtils.uploadTempImage(maskData, 'scene_mask'),
Load3dUtils.uploadTempImage(normalData, 'scene_normal'),
Load3dUtils.uploadTempImage(lineartData, 'scene_lineart')
])
load3d.handleResize()
return {
image: `threed/${data.name} [temp]`,
mask: `threed/${dataMask.name} [temp]`
mask: `threed/${dataMask.name} [temp]`,
normal: `threed/${dataNormal.name} [temp]`,
lineart: `threed/${dataLineart.name} [temp]`
}
}
}
@@ -252,21 +261,27 @@ app.registerExtension({
load3d.toggleAnimation(false)
const { scene: imageData, mask: maskData } = await load3d.captureScene(
const {
scene: imageData,
mask: maskData,
normal: normalData
} = await load3d.captureScene(
width.value as number,
height.value as number
)
const [data, dataMask] = await Promise.all([
const [data, dataMask, dataNormal] = await Promise.all([
Load3dUtils.uploadTempImage(imageData, 'scene'),
Load3dUtils.uploadTempImage(maskData, 'scene_mask')
Load3dUtils.uploadTempImage(maskData, 'scene_mask'),
Load3dUtils.uploadTempImage(normalData, 'scene_normal')
])
load3d.handleResize()
return {
image: `threed/${data.name} [temp]`,
mask: `threed/${dataMask.name} [temp]`
mask: `threed/${dataMask.name} [temp]`,
normal: `threed/${dataNormal.name} [temp]`
}
}
}

View File

@@ -36,6 +36,9 @@ class Load3d {
protected loaderManager: LoaderManager
protected modelManager: ModelManager
STATUS_MOUSE_ON_NODE: boolean
STATUS_MOUSE_ON_SCENE: boolean
constructor(
container: Element | HTMLElement,
options: Load3DOptions = {
@@ -124,6 +127,9 @@ class Load3d {
this.previewManager.init()
}
this.STATUS_MOUSE_ON_NODE = false
this.STATUS_MOUSE_ON_SCENE = false
this.handleResize()
this.startAnimation()
}
@@ -167,6 +173,18 @@ class Load3d {
animate()
}
updateStatusMouseOnNode(onNode: boolean): void {
this.STATUS_MOUSE_ON_NODE = onNode
}
updateStatusMouseOnScene(onScene: boolean): void {
this.STATUS_MOUSE_ON_SCENE = onScene
}
isActive(): boolean {
return this.STATUS_MOUSE_ON_NODE || this.STATUS_MOUSE_ON_SCENE
}
setBackgroundColor(color: string): void {
this.sceneManager.setBackgroundColor(color)
this.renderer.render(

View File

@@ -196,7 +196,7 @@ export class SceneManager implements SceneManagerInterface {
captureScene(
width: number,
height: number
): Promise<{ scene: string; mask: string }> {
): Promise<{ scene: string; mask: string; normal: string; lineart: string }> {
return new Promise(async (resolve, reject) => {
try {
const originalWidth = this.renderer.domElement.width
@@ -207,6 +207,7 @@ export class SceneManager implements SceneManagerInterface {
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)
@@ -240,6 +241,11 @@ export class SceneManager implements SceneManagerInterface {
)
}
const originalMaterials = new Map<
THREE.Mesh,
THREE.Material | THREE.Material[]
>()
this.renderer.clear()
if (this.backgroundMesh && this.backgroundTexture) {
@@ -262,12 +268,103 @@ export class SceneManager implements SceneManagerInterface {
this.renderer.render(this.scene, this.getActiveCamera())
const maskData = this.renderer.domElement.toDataURL('image/png')
this.scene.traverse((child) => {
if (child instanceof THREE.Mesh) {
originalMaterials.set(child, child.material)
child.material = new THREE.MeshNormalMaterial({
flatShading: false,
side: THREE.DoubleSide,
normalScale: new THREE.Vector2(1, 1)
})
}
})
const gridVisible = this.gridHelper.visible
this.gridHelper.visible = false
this.renderer.setClearColor(0x000000, 1)
this.renderer.clear()
this.renderer.render(this.scene, this.getActiveCamera())
const normalData = this.renderer.domElement.toDataURL('image/png')
this.scene.traverse((child) => {
if (child instanceof THREE.Mesh) {
const originalMaterial = originalMaterials.get(child)
if (originalMaterial) {
child.material = originalMaterial
}
}
})
let lineartModel: THREE.Group | null = null
const originalSceneVisible: Map<THREE.Object3D, boolean> = new Map()
this.scene.traverse((child) => {
if (child instanceof THREE.Group && child.name === 'lineartModel') {
lineartModel = child as THREE.Group
}
if (
child instanceof THREE.Mesh &&
!(child.parent?.name === 'lineartModel')
) {
originalSceneVisible.set(child, child.visible)
child.visible = false
}
})
this.renderer.setClearColor(0xffffff, 1)
this.renderer.clear()
if (lineartModel !== null) {
lineartModel = lineartModel as THREE.Group
const originalLineartVisibleMap: Map<THREE.Object3D, boolean> =
new Map()
lineartModel.traverse((child: THREE.Object3D) => {
if (child instanceof THREE.Mesh) {
originalLineartVisibleMap.set(child, child.visible)
child.visible = true
}
})
const originalLineartVisible = lineartModel.visible
lineartModel.visible = true
this.renderer.render(this.scene, this.getActiveCamera())
lineartModel.visible = originalLineartVisible
originalLineartVisibleMap.forEach((visible, object) => {
object.visible = visible
})
}
const lineartData = this.renderer.domElement.toDataURL('image/png')
originalSceneVisible.forEach((visible, object) => {
object.visible = visible
})
this.gridHelper.visible = gridVisible
this.renderer.setClearColor(originalClearColor, originalClearAlpha)
this.renderer.setSize(originalWidth, originalHeight)
this.renderer.outputColorSpace = originalOutputColorSpace
this.handleResize(width, height)
this.handleResize(originalWidth, originalHeight)
resolve({ scene: sceneData, mask: maskData })
resolve({
scene: sceneData,
mask: maskData,
normal: normalData,
lineart: lineartData
})
} catch (error) {
reject(error)
}

View File

@@ -40,6 +40,8 @@ export interface Load3DOptions {
export interface CaptureResult {
scene: string
mask: string
normal: string
lineart: string
}
export interface BaseManager {

View File

@@ -41,6 +41,12 @@ export class Load3dService {
rawNode.onMouseEnter = function () {
instance.refreshViewport()
instance.updateStatusMouseOnNode(true)
}
rawNode.onMouseLeave = function () {
instance.updateStatusMouseOnNode(false)
}
rawNode.onResize = function () {