Compare commits

...

2 Commits

Author SHA1 Message Date
Terry Jia
0fc8459682 test(load3d): cover Timer-driven frame delta in Viewport3d 2026-07-13 10:17:03 -04:00
Terry Jia
f0181b081b fix: replace deprecated THREE.Clock with THREE.Timer in Load3d viewport 2026-07-13 10:01:15 -04:00
3 changed files with 88 additions and 5 deletions

View File

@@ -545,7 +545,7 @@ describe('Load3d', () => {
STATUS_MOUSE_ON_SCENE: false,
STATUS_MOUSE_ON_VIEWER: false,
INITIAL_RENDER_DONE: false,
clock: new THREE.Clock(),
timer: new THREE.Timer(),
animationManager: {
update: animationUpdate,
isAnimationPlaying: false,

View File

@@ -1,6 +1,7 @@
import * as THREE from 'three'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { Viewport3dDeps } from '@/extensions/core/load3d/Viewport3d'
import { Viewport3d } from '@/extensions/core/load3d/Viewport3d'
type CameraStub = {
@@ -426,4 +427,86 @@ describe('Viewport3d', () => {
expect(ctx.forceRender).toHaveBeenCalledOnce()
})
})
describe('frame timing (constructed instance)', () => {
function makeConstructedViewport() {
const renderer = {
setViewport: vi.fn(),
setScissor: vi.fn(),
setScissorTest: vi.fn(),
setClearColor: vi.fn(),
clear: vi.fn(),
render: vi.fn()
}
const view = {
canvas: document.createElement('canvas'),
renderer,
width: 800,
height: 600,
state: { clearColor: new THREE.Color(0x000000), clearAlpha: 0 },
observeResize: vi.fn(),
beginRender: vi.fn(),
blit: vi.fn(),
setSize: vi.fn(),
dispose: vi.fn()
}
const controlsManager = { init: vi.fn(), update: vi.fn() }
const viewHelperManager = {
createViewHelper: vi.fn(),
init: vi.fn(),
update: vi.fn<(delta: number) => void>(),
render: vi.fn()
}
const deps = {
view,
eventManager: {
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
emitEvent: vi.fn()
},
sceneManager: {
init: vi.fn(),
scene: new THREE.Scene(),
renderBackground: vi.fn()
},
cameraManager: {
init: vi.fn(),
activeCamera: new THREE.PerspectiveCamera()
},
controlsManager,
lightingManager: { init: vi.fn() },
viewHelperManager
} as unknown as Viewport3dDeps
const viewport = new Viewport3d(document.createElement('div'), deps)
return { viewport, view, renderer, controlsManager, viewHelperManager }
}
beforeEach(() => {
vi.useFakeTimers({ toFake: ['performance'] })
})
afterEach(() => {
vi.useRealTimers()
})
it('forceRender feeds elapsed time between frames to per-frame updates and renders the view', () => {
const { viewport, view, renderer, controlsManager, viewHelperManager } =
makeConstructedViewport()
viewport.forceRender()
vi.advanceTimersByTime(100)
viewport.forceRender()
const deltas = viewHelperManager.update.mock.calls.map(([delta]) => delta)
expect(deltas).toHaveLength(2)
expect(deltas[1]).toBeCloseTo(0.1)
expect(controlsManager.update).toHaveBeenCalledTimes(2)
expect(view.beginRender).toHaveBeenCalledTimes(2)
expect(renderer.render).toHaveBeenCalledTimes(2)
expect(view.blit).toHaveBeenCalledTimes(2)
expect(viewport.INITIAL_RENDER_DONE).toBe(true)
})
})
})

View File

@@ -33,7 +33,7 @@ export type Viewport3dDeps = {
export class Viewport3d {
protected readonly view: RendererView
protected clock: THREE.Clock
protected timer: THREE.Timer
private renderLoop: RenderLoopHandle | null = null
private onContextMenuCallback?: (event: MouseEvent) => void
private getDimensionsCallback?: () => { width: number; height: number } | null
@@ -68,7 +68,7 @@ export class Viewport3d {
options: Load3DOptions = {}
) {
this.view = deps.view
this.clock = new THREE.Clock()
this.timer = new THREE.Timer()
this.isViewerMode = options.isViewerMode || false
this.onContextMenuCallback = options.onContextMenu
this.getDimensionsCallback = options.getDimensions
@@ -169,7 +169,7 @@ export class Viewport3d {
}
forceRender(): void {
const delta = this.clock.getDelta()
const delta = this.timer.update().getDelta()
this.tickPerFrame(delta)
this.renderView()
this.INITIAL_RENDER_DONE = true
@@ -279,7 +279,7 @@ export class Viewport3d {
protected startAnimation(): void {
this.renderLoop = startRenderLoop({
tick: () => {
const delta = this.clock.getDelta()
const delta = this.timer.update().getDelta()
this.tickPerFrame(delta)
this.renderView()
},