Move engineStreamMachine as a global actor; tons of more work

This commit is contained in:
lee-at-zoo-corp
2025-03-18 20:06:36 -04:00
parent deb83cf62c
commit 2a60efc5ab
11 changed files with 92 additions and 536 deletions

View File

@ -41,7 +41,6 @@ import { onboardingPaths } from '@src/routes/Onboarding/paths'
maybeWriteToDisk()
.then(() => {})
.catch(() => {})
import EngineStreamContext from 'hooks/useEngineStreamContext'
import { EngineStream } from 'components/EngineStream'
export function App() {
@ -67,8 +66,6 @@ export function App() {
const ref = useRef<HTMLDivElement>(null)
// Stream related refs and data
const videoRef = useRef<HTMLVideoElement>(null)
const canvasRef = useRef<HTMLCanvasElement>(null)
let [searchParams] = useSearchParams()
const pool = searchParams.get('pool')
@ -86,7 +83,7 @@ export function App() {
useHotKeyListener()
const settings = useSettings()
const token = useToken()
const authToken = useToken()
const coreDumpManager = useMemo(
() =>
@ -94,7 +91,7 @@ export function App() {
engineCommandManager,
codeManager,
rustContext,
token
authToken
),
[]
)
@ -157,26 +154,13 @@ export function App() {
/>
<ModalContainer />
<ModelingSidebar paneOpacity={paneOpacity} />
<EngineStreamContext.Provider
options={{
input: {
videoRef,
canvasRef,
mediaStream: null,
authToken: token,
pool,
zoomToFit: true,
},
}}
>
<EngineStream />
{/* <CamToggle /> */}
<LowerRightControls coreDumpManager={coreDumpManager}>
<UnitsMenu />
<Gizmo />
<CameraProjectionToggle />
</LowerRightControls>
</EngineStreamContext.Provider>
<EngineStream pool={pool} authToken={authToken} />
{/* <CamToggle /> */}
<LowerRightControls coreDumpManager={coreDumpManager}>
<UnitsMenu />
<Gizmo />
<CameraProjectionToggle />
</LowerRightControls>
</div>
)
}

View File

@ -480,12 +480,13 @@ export class CameraControls {
if (this.syncDirection === 'engineToClient') {
const newCmdId = uuidv4()
const { videoRef } = engineStreamActor.getSnapshot().context
// Nonsense to do anything until the video stream is established.
if (!this.engineCommandManager.elVideo) return
if (!videoRef.current) return
const { x, y } = getNormalisedCoordinates(
event,
this.engineCommandManager.elVideo,
videoRef.current,
this.engineCommandManager.streamDimensions
)
this.throttledEngCmd({

View File

@ -1,6 +1,5 @@
import { MouseEventHandler, useEffect, useRef } from 'react'
import { useAppState } from 'AppState'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useModelingContext } from 'hooks/useModelingContext'
import { useNetworkContext } from 'hooks/useNetworkContext'
import { NetworkHealthState } from 'hooks/useNetworkStatus'
@ -14,31 +13,34 @@ import { PATHS } from 'lib/paths'
import { IndexLoaderData } from 'lib/types'
import { err, reportRejection, trap } from 'lib/trap'
import { getArtifactOfTypes } from 'lang/std/artifactGraph'
import { clearSceneAndBustCache } from 'lang/wasm'
import { ViewControlContextMenu } from './ViewControlMenu'
import { useSettings, engineStreamActor } from 'machines/appMachine'
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
import { EngineStreamState, EngineStreamTransition } from 'machines/engineStreamMachine'
import { useSelector } from '@xstate/react'
import useEngineStreamContext, {
EngineStreamState,
EngineStreamTransition,
} from 'hooks/useEngineStreamContext'
import { REASONABLE_TIME_TO_REFRESH_STREAM_SIZE } from 'lib/timings'
export const EngineStream = () => {
export const EngineStream = (props: {
pool: string | null,
authToken: string | undefined,
}) => {
const { setAppState } = useAppState()
const { overallState } = useNetworkContext()
const { settings } = useSettingsAuthContext()
const settings = useSettings()
const engineStreamState = useSelector(engineStreamActor, (state) => state)
const { file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const last = useRef<number>(Date.now())
const videoWrapperRef = useRef<HTMLDivElement>(null)
const settingsEngine = {
theme: settings.context.app.theme.current,
enableSSAO: settings.context.app.enableSSAO.current,
highlightEdges: settings.context.modeling.highlightEdges.current,
showScaleGrid: settings.context.modeling.showScaleGrid.current,
cameraProjection: settings.context.modeling.cameraProjection.current,
theme: settings.app.theme.current,
enableSSAO: settings.modeling.enableSSAO.current,
highlightEdges: settings.modeling.highlightEdges.current,
showScaleGrid: settings.modeling.showScaleGrid.current,
cameraProjection: settings.modeling.cameraProjection.current,
}
const { state: modelingMachineState, send: modelingMachineActorSend } =
@ -46,10 +48,7 @@ export const EngineStream = () => {
const commandBarState = useCommandBarState()
const engineStreamActor = useEngineStreamContext.useActorRef()
const engineStreamState = engineStreamActor.getSnapshot()
const streamIdleMode = settings.context.app.streamIdleMode.current
const streamIdleMode = settings.app.streamIdleMode.current
const startOrReconfigureEngine = () => {
engineStreamActor.send({
@ -80,6 +79,15 @@ export const EngineStream = () => {
play
)
engineStreamActor.send({
type: EngineStreamTransition.SetPool,
data: { pool: props.pool },
})
engineStreamActor.send({
type: EngineStreamTransition.SetAuthToken,
data: { authToken: props.authToken },
})
return () => {
engineCommandManager.tearDown()
engineCommandManager.removeEventListener(
@ -325,7 +333,7 @@ export const EngineStream = () => {
No canvas support
</canvas>
<ClientSideScene
cameraControls={settings.context.modeling.mouseControls.current}
cameraControls={settings.modeling.mouseControls.current}
/>
<ViewControlContextMenu
event="mouseup"

View File

@ -6,8 +6,7 @@ export const ModelStateIndicator = () => {
const [commands] = useEngineCommands()
const [isDone, setIsDone] = useState<boolean>(false)
const engineStreamActor = useEngineStreamContext.useActorRef()
const engineStreamState = engineStreamActor.getSnapshot()
const engineStreamState = useSelector(engineStreamActor, (state) => state)
const lastCommandType = commands[commands.length - 1]?.type

View File

@ -147,7 +147,6 @@ export const ModelingMachineProvider = ({
showScaleGrid,
cameraOrbit,
enableSSAO,
cameraProjection,
},
} = useSettings()
const navigate = useNavigate()

View File

@ -1,414 +0,0 @@
import { useAppStream } from '@src/AppState'
import type { MouseEventHandler } from 'react'
import { useEffect, useRef, useState } from 'react'
import { useRouteLoaderData } from 'react-router-dom'
import { ClientSideScene } from '@src/clientSideScene/ClientSideSceneComp'
import Loading from '@src/components/Loading'
import { ViewControlContextMenu } from '@src/components/ViewControlMenu'
import { useModelingContext } from '@src/hooks/useModelingContext'
import { useNetworkContext } from '@src/hooks/useNetworkContext'
import { NetworkHealthState } from '@src/hooks/useNetworkStatus'
import { getArtifactOfTypes } from '@src/lang/std/artifactGraph'
import {
DisconnectingType,
EngineCommandManagerEvents,
EngineConnectionStateType,
} from '@src/lang/std/engineConnection'
import { btnName } from '@src/lib/cameraControls'
import { PATHS } from '@src/lib/paths'
import { sendSelectEventToEngine } from '@src/lib/selections'
import {
engineCommandManager,
kclManager,
sceneInfra,
} from '@src/lib/singletons'
import { err, reportRejection } from '@src/lib/trap'
import type { IndexLoaderData } from '@src/lib/types'
import { uuidv4 } from '@src/lib/utils'
import { useSettings } from '@src/machines/appMachine'
import { useCommandBarState } from '@src/machines/commandBarMachine'
enum StreamState {
Playing = 'playing',
Paused = 'paused',
Resuming = 'resuming',
Unset = 'unset',
}
export const Stream = () => {
const [isLoading, setIsLoading] = useState(true)
const videoWrapperRef = useRef<HTMLDivElement>(null)
const videoRef = useRef<HTMLVideoElement>(null)
const settings = useSettings()
const { state, send } = useModelingContext()
const commandBarState = useCommandBarState()
const { mediaStream } = useAppStream()
const { overallState, immediateState } = useNetworkContext()
const [streamState, setStreamState] = useState(StreamState.Unset)
const { file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const IDLE = settings.app.streamIdleMode.current
const isNetworkOkay =
overallState === NetworkHealthState.Ok ||
overallState === NetworkHealthState.Weak
engineCommandManager.elVideo = videoRef.current
/**
* Execute code and show a "building scene message"
* in Stream.tsx in the meantime.
*
* I would like for this to live somewhere more central,
* but it seems to me that we need the video element ref
* to be able to play the video after the code has been
* executed. If we can find a way to do this from a more
* central place, we can move this code there.
*/
function executeCodeAndPlayStream() {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
kclManager.executeCode().then(async () => {
await videoRef.current?.play().catch((e) => {
console.warn('Video playing was prevented', e, videoRef.current)
})
setStreamState(StreamState.Playing)
// Only call zoom_to_fit once when the stream starts to center the scene.
await engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'zoom_to_fit',
object_ids: [], // leave empty to zoom to all objects
padding: 0.1, // padding around the objects
animated: false, // don't animate the zoom for now
},
})
})
}
/**
* Subscribe to execute code when the file changes
* but only if the scene is already ready.
* See onSceneReady for the initial scene setup.
*/
useEffect(() => {
if (engineCommandManager.engineConnection?.isReady() && file?.path) {
console.log('execute on file change')
executeCodeAndPlayStream()
}
}, [file?.path, engineCommandManager.engineConnection])
useEffect(() => {
if (
immediateState.type === EngineConnectionStateType.Disconnecting &&
immediateState.value.type === DisconnectingType.Pause
) {
setStreamState(StreamState.Paused)
}
}, [immediateState])
// Linux has a default behavior to paste text on middle mouse up
// This adds a listener to block that pasting if the click target
// is not a text input, so users can move in the 3D scene with
// middle mouse drag with a text input focused without pasting.
useEffect(() => {
const handlePaste = (e: ClipboardEvent) => {
const isHtmlElement = e.target && e.target instanceof HTMLElement
const isEditable =
(isHtmlElement && !('explicitOriginalTarget' in e)) ||
('explicitOriginalTarget' in e &&
((e.explicitOriginalTarget as HTMLElement).contentEditable ===
'true' ||
['INPUT', 'TEXTAREA'].some(
(tagName) =>
tagName === (e.explicitOriginalTarget as HTMLElement).tagName
)))
if (!isEditable) {
e.preventDefault()
e.stopPropagation()
e.stopImmediatePropagation()
}
}
globalThis?.window?.document?.addEventListener('paste', handlePaste, {
capture: true,
})
const IDLE_TIME_MS = 1000 * 60 * 2
let timeoutIdIdleA: ReturnType<typeof setTimeout> | undefined = undefined
const teardown = () => {
// Already paused
if (streamState === StreamState.Paused) return
videoRef.current?.pause()
setStreamState(StreamState.Paused)
sceneInfra.modelingSend({ type: 'Cancel' })
// Give video time to pause
window.requestAnimationFrame(() => {
engineCommandManager.tearDown({ idleMode: true })
})
}
const onVisibilityChange = () => {
if (globalThis.window.document.visibilityState === 'hidden') {
clearTimeout(timeoutIdIdleA)
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
} else if (!engineCommandManager.engineConnection?.isReady()) {
clearTimeout(timeoutIdIdleA)
setStreamState(StreamState.Resuming)
}
}
// Teardown everything if we go hidden or reconnect
if (IDLE) {
globalThis?.window?.document?.addEventListener(
'visibilitychange',
onVisibilityChange
)
}
let timeoutIdIdleB: ReturnType<typeof setTimeout> | undefined = undefined
const onAnyInput = () => {
if (streamState === StreamState.Playing) {
// Clear both timers
clearTimeout(timeoutIdIdleA)
clearTimeout(timeoutIdIdleB)
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
}
if (streamState === StreamState.Paused) {
setStreamState(StreamState.Resuming)
}
}
if (IDLE) {
globalThis?.window?.document?.addEventListener('keydown', onAnyInput)
globalThis?.window?.document?.addEventListener('mousemove', onAnyInput)
globalThis?.window?.document?.addEventListener('mousedown', onAnyInput)
globalThis?.window?.document?.addEventListener('scroll', onAnyInput)
globalThis?.window?.document?.addEventListener('touchstart', onAnyInput)
}
if (IDLE) {
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
}
/**
* Add a listener to execute code and play the stream
* on initial stream setup.
*/
engineCommandManager.addEventListener(
EngineCommandManagerEvents.SceneReady,
executeCodeAndPlayStream
)
return () => {
engineCommandManager.removeEventListener(
EngineCommandManagerEvents.SceneReady,
executeCodeAndPlayStream
)
globalThis?.window?.document?.removeEventListener('paste', handlePaste, {
capture: true,
})
if (IDLE) {
clearTimeout(timeoutIdIdleA)
clearTimeout(timeoutIdIdleB)
globalThis?.window?.document?.removeEventListener(
'visibilitychange',
onVisibilityChange
)
globalThis?.window?.document?.removeEventListener('keydown', onAnyInput)
globalThis?.window?.document?.removeEventListener(
'mousemove',
onAnyInput
)
globalThis?.window?.document?.removeEventListener(
'mousedown',
onAnyInput
)
globalThis?.window?.document?.removeEventListener('scroll', onAnyInput)
globalThis?.window?.document?.removeEventListener(
'touchstart',
onAnyInput
)
}
}
}, [IDLE, streamState])
useEffect(() => {
if (
typeof window === 'undefined' ||
typeof RTCPeerConnection === 'undefined'
)
return
if (!videoRef.current) return
if (!mediaStream) return
// The browser complains if we try to load a new stream without pausing first.
// Do not immediately play the stream!
// we instead use a setTimeout to play the stream in the next event loop
try {
videoRef.current.srcObject = mediaStream
videoRef.current.pause()
setTimeout(() => {
videoRef.current?.play().catch((e) => {
console.warn('Video playing was prevented', e, videoRef.current)
})
})
} catch (e) {
console.warn('Attempted to pause stream while play was still loading', e)
}
send({
type: 'Set context',
data: {
videoElement: videoRef.current,
},
})
setIsLoading(false)
}, [mediaStream])
const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {
// If we've got no stream or connection, don't do anything
if (!isNetworkOkay) return
if (!videoRef.current) return
// If we're in sketch mode, don't send a engine-side select event
if (state.matches('Sketch')) return
// Only respect default plane selection if we're on a selection command argument
if (
state.matches({ idle: 'showPlanes' }) &&
!(
commandBarState.matches('Gathering arguments') &&
commandBarState.context.currentArgument?.inputType === 'selection'
)
)
return
// If we're mousing up from a camera drag, don't send a select event
if (sceneInfra.camControls.wasDragging === true) return
if (btnName(e.nativeEvent).left) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
sendSelectEventToEngine(e)
}
}
/**
* On double-click of sketch entities we automatically enter sketch mode with the selected sketch,
* allowing for quick editing of sketches. TODO: This should be moved to a more central place.
*/
const enterSketchModeIfSelectingSketch: MouseEventHandler<HTMLDivElement> = (
e
) => {
if (
!isNetworkOkay ||
!videoRef.current ||
state.matches('Sketch') ||
state.matches({ idle: 'showPlanes' }) ||
sceneInfra.camControls.wasDragging === true ||
!btnName(e.nativeEvent).left
) {
return
}
sendSelectEventToEngine(e)
.then(({ entity_id }) => {
if (!entity_id) {
// No entity selected. This is benign
return
}
const path = getArtifactOfTypes(
{ key: entity_id, types: ['path', 'solid2d', 'segment', 'helix'] },
kclManager.artifactGraph
)
if (err(path)) {
return path
}
sceneInfra.modelingSend({ type: 'Enter sketch' })
})
.catch(reportRejection)
}
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div
ref={videoWrapperRef}
className="absolute inset-0 z-0"
id="stream"
data-testid="stream"
onClick={handleClick}
onDoubleClick={enterSketchModeIfSelectingSketch}
onContextMenu={(e) => e.preventDefault()}
onContextMenuCapture={(e) => e.preventDefault()}
>
<video
ref={videoRef}
muted
autoPlay
controls={false}
onPlay={() => setIsLoading(false)}
className="w-full cursor-pointer h-full"
disablePictureInPicture
id="video-stream"
/>
<ClientSideScene
cameraControls={settings.modeling.mouseControls.current}
/>
{(streamState === StreamState.Paused ||
streamState === StreamState.Resuming) && (
<div className="text-center absolute inset-0">
<div
className="flex flex-col items-center justify-center h-screen"
data-testid="paused"
>
<div className="border-primary border p-2 rounded-sm">
<svg
width="8"
height="12"
viewBox="0 0 8 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M2 12V0H0V12H2ZM8 12V0H6V12H8Z"
fill="var(--primary)"
/>
</svg>
</div>
<p className="text-base mt-2 text-primary bold">
{streamState === StreamState.Paused && 'Paused'}
{streamState === StreamState.Resuming && 'Resuming'}
</p>
</div>
</div>
)}
{(!isNetworkOkay || isLoading) && (
<div className="text-center absolute inset-0">
<Loading>
{!isNetworkOkay && !isLoading ? (
<span data-testid="loading-stream">Stream disconnected...</span>
) : (
!isLoading && (
<span data-testid="loading-stream">Loading stream...</span>
)
)}
</Loading>
</div>
)}
<ViewControlContextMenu
event="mouseup"
guard={(e) =>
sceneInfra.camControls.wasDragging === false &&
btnName(e).right === true
}
menuTargetElement={videoWrapperRef}
/>
</div>
)
}

View File

@ -841,7 +841,9 @@ class EngineConnection extends EventTarget {
// Bust the cache before anything
;(async () => {
await clearSceneAndBustCache(kclManager.engineCommandManager)
await rustContext.clearSceneAndBustCache(
kclManager.engineCommandManager.settings
)
})().catch(reportRejection)
this.dispatchEvent(
@ -1395,8 +1397,6 @@ export class EngineCommandManager extends EventTarget {
height: 1337,
}
elVideo: HTMLVideoElement | null = null
_commandLogCallBack: (command: CommandLog[]) => void = () => {}
subscriptions: {

View File

@ -649,12 +649,13 @@ export async function sendSelectEventToEngine(
e: React.MouseEvent<HTMLDivElement, MouseEvent>
) {
// No video stream to normalise against, return immediately
if (!engineCommandManager.elVideo)
const engineStreamState = engineStreamActor.getSnapshot().context
if (!engineStreamState.videoRef.current)
return Promise.reject('video element not ready')
const { x, y } = getNormalisedCoordinates(
e,
engineCommandManager.elVideo,
engineStreamState.videoRef.current,
engineCommandManager.streamDimensions
)
const res = await engineCommandManager.sendSceneCommand({

View File

@ -6,10 +6,11 @@ import { authMachine } from '@src/machines/authMachine'
import { ACTOR_IDS } from '@src/machines/machineConstants'
import { settingsMachine } from '@src/machines/settingsMachine'
const { AUTH, SETTINGS } = ACTOR_IDS
const { AUTH, SETTINGS, ENGINE_STREAM } = ACTOR_IDS
const appMachineActors = {
[AUTH]: authMachine,
[SETTINGS]: settingsMachine,
[ENGINE_STREAM]: engineStreamMachine,
} as const
const appMachine = setup({
@ -29,6 +30,11 @@ const appMachine = setup({
systemId: SETTINGS,
input: createSettings(),
}),
spawnChild(ENGINE_STREAM, {
id: ENGINE_STREAM,
systemId: ENGINE_STREAM,
input: engineStreamContextCreate(),
}),
],
})
@ -61,3 +67,7 @@ export const useSettings = () =>
const { currentProject, ...settings } = state.context
return settings
})
export const engineStreamActor = appActor.system.get(ENGINE_STREAM) as ActorRefFrom<
typeof engineStreamMachine
>

View File

@ -1,9 +1,7 @@
import { uuidv4 } from 'lib/utils'
import { makeDefaultPlanes, clearSceneAndBustCache } from 'lang/wasm'
import { MutableRefObject } from 'react'
import { setup, assign, fromPromise } from 'xstate'
import { createActorContext } from '@xstate/react'
import { kclManager, sceneInfra, engineCommandManager } from 'lib/singletons'
import { rustContext, kclManager, sceneInfra, engineCommandManager } from 'lib/singletons'
import { trap } from 'lib/trap'
import { Vector3, Vector4 } from 'three'
@ -19,7 +17,9 @@ export enum EngineStreamState {
}
export enum EngineStreamTransition {
SetMediaStream = 'set-context',
SetMediaStream = 'set-media-stream',
SetPool = 'set-pool',
SetAuthToken = 'set-auth-token',
Play = 'play',
Resume = 'resume',
Pause = 'pause',
@ -35,6 +35,15 @@ export interface EngineStreamContext {
zoomToFit: boolean
}
export const engineStreamContextCreate = (): EngineStreamContext => ({
pool: null,
authToken: undefined,
mediaStream: null,
videoRef: { current: null },
canvasRef: { current: null },
zoomToFit: true,
})
export function getDimensions(streamWidth: number, streamHeight: number) {
const factorOf = 4
const maxResolution = 2160
@ -47,11 +56,11 @@ export function getDimensions(streamWidth: number, streamHeight: number) {
return { width: quadWidth, height: quadHeight }
}
export function holdOntoVideoFrameInCanvas(
export async function holdOntoVideoFrameInCanvas(
video: HTMLVideoElement,
canvas: HTMLCanvasElement
) {
video.pause()
await video.pause()
canvas.width = video.videoWidth
canvas.height = video.videoHeight
canvas.style.width = video.videoWidth + 'px'
@ -64,7 +73,7 @@ export function holdOntoVideoFrameInCanvas(
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
}
const engineStreamMachine = setup({
export const engineStreamMachine = setup({
types: {
context: {} as EngineStreamContext,
input: {} as EngineStreamContext,
@ -88,7 +97,7 @@ const engineStreamMachine = setup({
video.style.display = 'block'
canvas.style.display = 'none'
await sceneInfra.camControls.restoreCameraPosition()
// await sceneInfra.camControls.restoreCameraPosition()
video.srcObject = mediaStream
await video.play()
@ -105,12 +114,12 @@ const engineStreamMachine = setup({
const video = context.videoRef.current
if (!video) return
video.pause()
await video.pause()
const canvas = context.canvasRef.current
if (!canvas) return
holdOntoVideoFrameInCanvas(video, canvas)
await holdOntoVideoFrameInCanvas(video, canvas)
// Make sure we're on the next frame for no flickering between canvas
// and the video elements.
@ -126,57 +135,6 @@ const engineStreamMachine = setup({
context.mediaStream?.getVideoTracks()[0].stop()
video.srcObject = null
const reqDefaultCameraGetSettings =
await engineCommandManager.sendSceneCommand({
// CameraControls subscribes to default_camera_get_settings response events
// firing this at connection ensure the camera's are synced initially
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
})
if (
reqDefaultCameraGetSettings === null ||
!reqDefaultCameraGetSettings.success
)
return
if (
!('modeling_response' in reqDefaultCameraGetSettings.resp.data)
)
return
if (
reqDefaultCameraGetSettings.resp.data.modeling_response.type ===
'empty'
)
return
if (
!(
'settings' in
reqDefaultCameraGetSettings.resp.data.modeling_response.data
)
)
return
const center =
reqDefaultCameraGetSettings.resp.data.modeling_response.data
.settings.center
const pos =
reqDefaultCameraGetSettings.resp.data.modeling_response.data
.settings.pos
sceneInfra.camControls.old = {
camera: sceneInfra.camControls.camera.clone(),
target: new Vector3(center.x, center.y, center.z),
}
sceneInfra.camControls.old.camera.position.set(
pos.x,
pos.y,
pos.z
)
engineCommandManager.tearDown({ idleMode: true })
})()
)
@ -215,7 +173,7 @@ const engineStreamMachine = setup({
// If we don't pause there could be a really bad flicker
// on reconfiguration (resize, for example)
holdOntoVideoFrameInCanvas(video, canvas)
await holdOntoVideoFrameInCanvas(video, canvas)
canvas.style.display = 'block'
window.requestAnimationFrame(() => {
@ -231,9 +189,6 @@ const engineStreamMachine = setup({
height,
token: context.authToken,
settings: settingsNext,
makeDefaultPlanes: () => {
return makeDefaultPlanes(kclManager.engineCommandManager)
},
})
event.modelingMachineActorSend({
@ -250,11 +205,24 @@ const engineStreamMachine = setup({
),
},
}).createMachine({
context: (initial) => initial.input,
initial: EngineStreamState.Off,
context: (initial) => initial.input,
states: {
[EngineStreamState.Off]: {
reenter: true,
on: {
[EngineStreamTransition.SetPool]: {
target: EngineStreamState.Setup,
actions: [
assign({ pool: ({ context, event }) => event.data.pool }),
],
},
[EngineStreamTransition.SetAuthToken]: {
target: EngineStreamState.Setup,
actions: [
assign({ authToken: ({ context, event }) => event.data.authToken }),
],
},
[EngineStreamTransition.StartOrReconfigureEngine]: {
target: EngineStreamState.On,
},
@ -267,6 +235,7 @@ const engineStreamMachine = setup({
input: (args) => args,
},
on: {
// Transition requested by engineConnection
[EngineStreamTransition.SetMediaStream]: {
target: EngineStreamState.On,
actions: [
@ -335,5 +304,3 @@ const engineStreamMachine = setup({
},
},
})
export default createActorContext(engineStreamMachine)

View File

@ -1,4 +1,5 @@
export const ACTOR_IDS = {
AUTH: 'auth',
SETTINGS: 'settings',
ENGINE_STREAM: 'engine_stream',
} as const