[3d] add record video support for load3d animation node (#3798)

This commit is contained in:
Terry Jia
2025-05-07 10:12:33 -04:00
committed by GitHub
parent 43365b4318
commit db81b62274
5 changed files with 109 additions and 5 deletions

View File

@@ -32,6 +32,7 @@
@background-image-change="listenBackgroundImageChange"
@animation-list-change="animationListChange"
@up-direction-change="listenUpDirectionChange"
@recording-status-change="listenRecordingStatusChange"
/>
<div class="absolute top-0 left-0 w-full h-full pointer-events-none">
<Load3DControls
@@ -66,6 +67,21 @@
@animation-change="animationChange"
/>
</div>
<div
v-if="showRecordingControls"
class="absolute top-12 right-2 z-20 pointer-events-auto"
>
<RecordingControls
:node="node"
:is-recording="isRecording"
:has-recording="hasRecording"
:recording-duration="recordingDuration"
@start-recording="handleStartRecording"
@stop-recording="handleStopRecording"
@export-recording="handleExportRecording"
@clear-recording="handleClearRecording"
/>
</div>
</div>
</template>
@@ -75,6 +91,7 @@ import { computed, ref } from 'vue'
import Load3DAnimationControls from '@/components/load3d/Load3DAnimationControls.vue'
import Load3DAnimationScene from '@/components/load3d/Load3DAnimationScene.vue'
import Load3DControls from '@/components/load3d/Load3DControls.vue'
import RecordingControls from '@/components/load3d/controls/RecordingControls.vue'
import Load3dUtils from '@/extensions/core/load3d/Load3dUtils'
import {
AnimationItem,
@@ -111,6 +128,11 @@ const selectedSpeed = ref(1)
const selectedAnimation = ref(0)
const backgroundImage = ref('')
const isRecording = ref(false)
const hasRecording = ref(false)
const recordingDuration = ref(0)
const showRecordingControls = ref(!inputSpec.isPreview)
const showPreviewButton = computed(() => {
return !type.includes('Preview')
})
@@ -133,6 +155,54 @@ const handleMouseLeave = () => {
}
}
const handleStartRecording = async () => {
const sceneRef = load3DAnimationSceneRef.value?.load3DSceneRef
if (sceneRef?.load3d) {
await sceneRef.load3d.startRecording()
isRecording.value = true
}
}
const handleStopRecording = () => {
const sceneRef = load3DAnimationSceneRef.value?.load3DSceneRef
if (sceneRef?.load3d) {
sceneRef.load3d.stopRecording()
isRecording.value = false
hasRecording.value = true
recordingDuration.value = sceneRef.load3d.getRecordingDuration()
}
}
const handleExportRecording = () => {
const sceneRef = load3DAnimationSceneRef.value?.load3DSceneRef
if (sceneRef?.load3d) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
const filename = `${timestamp}-animation-recording.mp4`
sceneRef.load3d.exportRecording(filename)
}
}
const handleClearRecording = () => {
const sceneRef = load3DAnimationSceneRef.value?.load3DSceneRef
if (sceneRef?.load3d) {
sceneRef.load3d.clearRecording()
hasRecording.value = false
recordingDuration.value = 0
}
}
const listenRecordingStatusChange = (value: boolean) => {
isRecording.value = value
if (!value) {
const sceneRef = load3DAnimationSceneRef.value?.load3DSceneRef
if (sceneRef?.load3d) {
hasRecording.value = true
recordingDuration.value = sceneRef.load3d.getRecordingDuration()
}
}
}
const switchCamera = () => {
cameraType.value =
cameraType.value === 'perspective' ? 'orthographic' : 'perspective'

View File

@@ -20,6 +20,7 @@
@camera-type-change="listenCameraTypeChange"
@show-grid-change="listenShowGridChange"
@show-preview-change="listenShowPreviewChange"
@recording-status-change="listenRecordingStatusChange"
/>
</template>
<script setup lang="ts">
@@ -148,6 +149,15 @@ watch(
const emit = defineEmits<{
(e: 'animationListChange', animationList: string): void
(e: 'materialModeChange', materialMode: string): void
(e: 'backgroundColorChange', color: string): void
(e: 'lightIntensityChange', lightIntensity: number): void
(e: 'fovChange', fov: number): void
(e: 'cameraTypeChange', cameraType: string): void
(e: 'showGridChange', showGrid: boolean): void
(e: 'showPreviewChange', showPreview: boolean): void
(e: 'upDirectionChange', direction: string): void
(e: 'recording-status-change', status: boolean): void
}>()
const listenMaterialModeChange = (mode: MaterialMode) => {
@@ -182,6 +192,10 @@ const listenShowPreviewChange = (value: boolean) => {
showPreview.value = value
}
const listenRecordingStatusChange = (value: boolean) => {
emit('recording-status-change', value)
}
const animationListeners = {
animationListChange: (newValue: any) => {
emit('animationListChange', newValue)

View File

@@ -365,7 +365,7 @@ useExtensionService().registerExtension({
const width = node.widgets?.find((w: IWidget) => w.name === 'width')
const height = node.widgets?.find((w: IWidget) => w.name === 'height')
if (modelWidget && width && height && sceneWidget) {
if (modelWidget && width && height && sceneWidget && load3d) {
const config = new Load3DConfiguration(load3d)
config.configure('input', modelWidget, cameraState, width, height)
@@ -375,6 +375,10 @@ useExtensionService().registerExtension({
load3d.toggleAnimation(false)
if (load3d.isRecording()) {
load3d.stopRecording()
}
const {
scene: imageData,
mask: maskData,
@@ -392,12 +396,23 @@ useExtensionService().registerExtension({
load3d.handleResize()
return {
const returnVal = {
image: `threed/${data.name} [temp]`,
mask: `threed/${dataMask.name} [temp]`,
normal: `threed/${dataNormal.name} [temp]`,
camera_info: node.properties['Camera Info']
camera_info: node.properties['Camera Info'],
recording: ''
}
const recordingData = load3d.getRecordingData()
if (recordingData) {
const [recording] = await Promise.all([
Load3dUtils.uploadTempImage(recordingData, 'recording', 'mp4')
])
returnVal['recording'] = `threed/${recording.name} [temp]`
}
return returnVal
}
}
}

View File

@@ -229,6 +229,7 @@ class Load3d {
return (
this.STATUS_MOUSE_ON_NODE ||
this.STATUS_MOUSE_ON_SCENE ||
this.isRecording() ||
!this.INITIAL_RENDER_DONE
)
}
@@ -461,7 +462,7 @@ class Load3d {
}
public isRecording(): boolean {
return this.recordingManager.hasRecording()
return this.recordingManager.getIsRecording()
}
public getRecordingDuration(): number {

View File

@@ -106,7 +106,7 @@ export class RecordingManager {
return
}
this.recordingDuration = (Date.now() - this.recordingStartTime) / 1000 // In seconds
this.recordingDuration = (Date.now() - this.recordingStartTime) / 1000
this.mediaRecorder.stop()
if (this.recordingStream) {
@@ -114,6 +114,10 @@ export class RecordingManager {
}
}
public getIsRecording(): boolean {
return this.isRecording
}
public hasRecording(): boolean {
return this.recordedChunks.length > 0
}