Rework zooming (#2798)
* Rework zooming * Adjust sketch mode zoom * Do not retry failures * typo * use sha as file upload id * again * again * again * again * Fix camera moving too * Use virtual fps instead of buffering for mouse --------- Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
This commit is contained in:
@ -15,8 +15,8 @@ export default defineConfig({
|
|||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
/* Retry on CI only */
|
/* Do not retry */
|
||||||
retries: process.env.CI ? 3 : 0,
|
retries: process.env.CI ? 0 : 0,
|
||||||
/* Different amount of parallelism on CI and local. */
|
/* Different amount of parallelism on CI and local. */
|
||||||
workers: process.env.CI ? 4 : 4,
|
workers: process.env.CI ? 4 : 4,
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
@ -74,6 +74,9 @@ export class CameraControls {
|
|||||||
enableRotate = true
|
enableRotate = true
|
||||||
enablePan = true
|
enablePan = true
|
||||||
enableZoom = true
|
enableZoom = true
|
||||||
|
zoomDataFromLastFrame?: number = undefined
|
||||||
|
// holds coordinates, and interaction
|
||||||
|
moveDataFromLastFrame?: [number, number, string] = undefined
|
||||||
lastPerspectiveFov: number = 45
|
lastPerspectiveFov: number = 45
|
||||||
pendingZoom: number | null = null
|
pendingZoom: number | null = null
|
||||||
pendingRotation: Vector2 | null = null
|
pendingRotation: Vector2 | null = null
|
||||||
@ -101,16 +104,12 @@ export class CameraControls {
|
|||||||
get isPerspective() {
|
get isPerspective() {
|
||||||
return this.camera instanceof PerspectiveCamera
|
return this.camera instanceof PerspectiveCamera
|
||||||
}
|
}
|
||||||
private debounceTimer = 0
|
|
||||||
|
|
||||||
handleStart = () => {
|
handleStart = () => {
|
||||||
if (this.debounceTimer) clearTimeout(this.debounceTimer)
|
|
||||||
this._isCamMovingCallback(true, false)
|
this._isCamMovingCallback(true, false)
|
||||||
}
|
}
|
||||||
handleEnd = () => {
|
handleEnd = () => {
|
||||||
this.debounceTimer = setTimeout(() => {
|
|
||||||
this._isCamMovingCallback(false, false)
|
this._isCamMovingCallback(false, false)
|
||||||
}, 400) as any as number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setCam = (camProps: ReactCameraProperties) => {
|
setCam = (camProps: ReactCameraProperties) => {
|
||||||
@ -230,6 +229,7 @@ export class CameraControls {
|
|||||||
camSettings.orientation.z,
|
camSettings.orientation.z,
|
||||||
camSettings.orientation.w
|
camSettings.orientation.w
|
||||||
).invert()
|
).invert()
|
||||||
|
|
||||||
this.camera.up.copy(new Vector3(0, 1, 0).applyQuaternion(quat))
|
this.camera.up.copy(new Vector3(0, 1, 0).applyQuaternion(quat))
|
||||||
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
||||||
this.useOrthographicCamera()
|
this.useOrthographicCamera()
|
||||||
@ -258,6 +258,48 @@ export class CameraControls {
|
|||||||
}
|
}
|
||||||
this.onCameraChange()
|
this.onCameraChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Our stream is never more than 60fps.
|
||||||
|
// We can get away with capping our "virtual fps" to 60 then.
|
||||||
|
const FPS_VIRTUAL = 60
|
||||||
|
|
||||||
|
const doZoom = () => {
|
||||||
|
if (this.zoomDataFromLastFrame !== undefined) {
|
||||||
|
this.handleStart()
|
||||||
|
this.engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_zoom',
|
||||||
|
magnitude:
|
||||||
|
(-1 * this.zoomDataFromLastFrame) / window.devicePixelRatio,
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
})
|
||||||
|
this.handleEnd()
|
||||||
|
}
|
||||||
|
this.zoomDataFromLastFrame = undefined
|
||||||
|
}
|
||||||
|
setInterval(doZoom, 1000 / FPS_VIRTUAL)
|
||||||
|
|
||||||
|
const doMove = () => {
|
||||||
|
if (this.moveDataFromLastFrame !== undefined) {
|
||||||
|
this.engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'camera_drag_move',
|
||||||
|
interaction: this.moveDataFromLastFrame[2] as any,
|
||||||
|
window: {
|
||||||
|
x: this.moveDataFromLastFrame[0],
|
||||||
|
y: this.moveDataFromLastFrame[1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.moveDataFromLastFrame = undefined
|
||||||
|
}
|
||||||
|
setInterval(doMove, 1000 / FPS_VIRTUAL)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.engineCommandManager.subscribeTo({
|
this.engineCommandManager.subscribeTo({
|
||||||
event: 'camera_drag_end',
|
event: 'camera_drag_end',
|
||||||
@ -342,15 +384,7 @@ export class CameraControls {
|
|||||||
if (interaction === 'none') return
|
if (interaction === 'none') return
|
||||||
|
|
||||||
if (this.syncDirection === 'engineToClient') {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
this.throttledEngCmd({
|
this.moveDataFromLastFrame = [event.clientX, event.clientY, interaction]
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'camera_drag_move',
|
|
||||||
interaction,
|
|
||||||
window: { x: event.clientX, y: event.clientY },
|
|
||||||
},
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,34 +432,19 @@ export class CameraControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMouseWheel = (event: WheelEvent) => {
|
onMouseWheel = (event: WheelEvent) => {
|
||||||
// Assume trackpad if the deltas are small and integers
|
|
||||||
this.handleStart()
|
|
||||||
|
|
||||||
if (this.syncDirection === 'engineToClient') {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
const interactions = this.interactionGuards.zoom.scrollCallback(
|
this.zoomDataFromLastFrame = event.deltaY
|
||||||
event as any
|
|
||||||
)
|
|
||||||
if (!interactions) {
|
|
||||||
this.handleEnd()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_zoom',
|
|
||||||
magnitude: -event.deltaY * 0.4,
|
|
||||||
},
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
})
|
|
||||||
this.handleEnd()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Else "clientToEngine" (Sketch Mode) or forceUpdate
|
// else "clientToEngine" (Sketch Mode) or forceUpdate
|
||||||
|
|
||||||
|
// We need to simulate similar behavior as when we send
|
||||||
|
// zoom commands to engine. This means dropping some zoom
|
||||||
|
// commands too.
|
||||||
// From onMouseMove zoom handling which seems to be really smooth
|
// From onMouseMove zoom handling which seems to be really smooth
|
||||||
this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1
|
this.handleStart()
|
||||||
this.pendingZoom *= 1 + event.deltaY * 0.01
|
this.pendingZoom = 1 + (event.deltaY / window.devicePixelRatio) * 0.001
|
||||||
this.handleEnd()
|
this.handleEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1737,6 +1737,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
if (
|
if (
|
||||||
(cmd.type === 'camera_drag_move' ||
|
(cmd.type === 'camera_drag_move' ||
|
||||||
cmd.type === 'handle_mouse_drag_move' ||
|
cmd.type === 'handle_mouse_drag_move' ||
|
||||||
|
cmd.type === 'default_camera_zoom' ||
|
||||||
cmd.type === ('default_camera_perspective_settings' as any)) &&
|
cmd.type === ('default_camera_perspective_settings' as any)) &&
|
||||||
this.engineConnection?.unreliableDataChannel &&
|
this.engineConnection?.unreliableDataChannel &&
|
||||||
!forceWebsocket
|
!forceWebsocket
|
||||||
|
Reference in New Issue
Block a user