92
src/App.tsx
92
src/App.tsx
@ -22,6 +22,7 @@ import { RenderViewerArtifacts } from './components/RenderViewerArtifacts'
|
|||||||
import { PanelHeader } from './components/PanelHeader'
|
import { PanelHeader } from './components/PanelHeader'
|
||||||
import { MemoryPanel } from './components/MemoryPanel'
|
import { MemoryPanel } from './components/MemoryPanel'
|
||||||
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
||||||
|
import { Stream } from './components/Stream'
|
||||||
|
|
||||||
const OrrthographicCamera = OrthographicCamera as any
|
const OrrthographicCamera = OrthographicCamera as any
|
||||||
|
|
||||||
@ -164,52 +165,55 @@ function App() {
|
|||||||
<MemoryPanel />
|
<MemoryPanel />
|
||||||
<Logs />
|
<Logs />
|
||||||
</Allotment>
|
</Allotment>
|
||||||
<div className="h-full">
|
<Allotment vertical defaultSizes={[4, 1]} minSize={20}>
|
||||||
<PanelHeader title="Drafting Board" />
|
<div className="h-full">
|
||||||
<Toolbar />
|
<PanelHeader title="Drafting Board" />
|
||||||
<div className="border h-full border-gray-300 relative">
|
<Toolbar />
|
||||||
<div className="absolute inset-0">
|
<div className="border h-full border-gray-300 relative">
|
||||||
<Canvas>
|
<div className="absolute inset-0">
|
||||||
<OrbitControls
|
<Canvas>
|
||||||
enableDamping={false}
|
<OrbitControls
|
||||||
enablePan
|
enableDamping={false}
|
||||||
enableRotate={
|
enablePan
|
||||||
!(
|
enableRotate={
|
||||||
guiMode.mode === 'canEditSketch' ||
|
!(
|
||||||
guiMode.mode === 'sketch'
|
guiMode.mode === 'canEditSketch' ||
|
||||||
)
|
guiMode.mode === 'sketch'
|
||||||
}
|
)
|
||||||
enableZoom
|
}
|
||||||
reverseOrbit={false}
|
enableZoom
|
||||||
/>
|
reverseOrbit={false}
|
||||||
<OrrthographicCamera
|
/>
|
||||||
ref={cam}
|
<OrrthographicCamera
|
||||||
makeDefault
|
ref={cam}
|
||||||
position={[0, 0, 1000]}
|
makeDefault
|
||||||
zoom={100}
|
position={[0, 0, 1000]}
|
||||||
rotation={[0, 0, 0]}
|
zoom={100}
|
||||||
far={2000}
|
rotation={[0, 0, 0]}
|
||||||
/>
|
far={2000}
|
||||||
<ambientLight />
|
/>
|
||||||
<pointLight position={[10, 10, 10]} />
|
<ambientLight />
|
||||||
<RenderViewerArtifacts artifacts={geoArray} />
|
<pointLight position={[10, 10, 10]} />
|
||||||
<BasePlanes />
|
<RenderViewerArtifacts artifacts={geoArray} />
|
||||||
<SketchPlane />
|
<BasePlanes />
|
||||||
<AxisIndicator />
|
<SketchPlane />
|
||||||
</Canvas>
|
<AxisIndicator />
|
||||||
</div>
|
</Canvas>
|
||||||
{errorState.isError && (
|
|
||||||
<div className="absolute inset-0 bg-gray-700/20">
|
|
||||||
<pre>
|
|
||||||
{'last first: \n\n' +
|
|
||||||
JSON.stringify(lastGuiMode, null, 2) +
|
|
||||||
'\n\n' +
|
|
||||||
JSON.stringify(guiMode)}
|
|
||||||
</pre>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
{errorState.isError && (
|
||||||
|
<div className="absolute inset-0 bg-gray-700/20">
|
||||||
|
<pre>
|
||||||
|
{'last first: \n\n' +
|
||||||
|
JSON.stringify(lastGuiMode, null, 2) +
|
||||||
|
'\n\n' +
|
||||||
|
JSON.stringify(guiMode)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<Stream />
|
||||||
|
</Allotment>
|
||||||
</Allotment>
|
</Allotment>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,7 @@ import withBaseUrl from './lib/withBaseURL'
|
|||||||
import App from './App'
|
import App from './App'
|
||||||
|
|
||||||
export const Auth = () => {
|
export const Auth = () => {
|
||||||
const { data: user, error } = useSWR(withBaseUrl('/user'), fetcher) as any
|
const { data: user } = useSWR(withBaseUrl('/user'), fetcher) as any
|
||||||
const isLocalHost =
|
const isLocalHost =
|
||||||
typeof window !== 'undefined' && window.location.hostname === 'localhost'
|
typeof window !== 'undefined' && window.location.hostname === 'localhost'
|
||||||
|
|
||||||
|
91
src/components/Stream.tsx
Normal file
91
src/components/Stream.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
import { PanelHeader } from '../components/PanelHeader'
|
||||||
|
|
||||||
|
export const Stream = () => {
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const url = 'wss://dev.api.kittycad.io/ws/channel'
|
||||||
|
const [pc, socket] = [new RTCPeerConnection(), new WebSocket(url)]
|
||||||
|
// Connection opened
|
||||||
|
socket.addEventListener('open', (event) => {
|
||||||
|
console.log('Connected to websocket, waiting for ICE servers')
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.addEventListener('close', (event) => {
|
||||||
|
console.log('websocket connection closed')
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.addEventListener('error', (event) => {
|
||||||
|
console.log('websocket connection error')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for messages
|
||||||
|
socket.addEventListener('message', (event) => {
|
||||||
|
//console.log('Message from server ', event.data);
|
||||||
|
if (event.data instanceof Blob) {
|
||||||
|
const reader = new FileReader()
|
||||||
|
|
||||||
|
reader.onload = () => {
|
||||||
|
//console.log("Result: " + reader.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.readAsText(event.data)
|
||||||
|
} else {
|
||||||
|
const message = JSON.parse(event.data)
|
||||||
|
if (message.type === 'SDPAnswer') {
|
||||||
|
pc.setRemoteDescription(new RTCSessionDescription(message.answer))
|
||||||
|
} else if (message.type === 'IceServerInfo') {
|
||||||
|
console.log('received IceServerInfo')
|
||||||
|
pc.setConfiguration({
|
||||||
|
iceServers: message.ice_servers,
|
||||||
|
})
|
||||||
|
pc.ontrack = function (event) {
|
||||||
|
const el = document.createElement(
|
||||||
|
event.track.kind
|
||||||
|
) as HTMLVideoElement
|
||||||
|
|
||||||
|
if (videoRef.current) {
|
||||||
|
videoRef.current.srcObject = event.streams[0]
|
||||||
|
videoRef.current.autoplay = true
|
||||||
|
videoRef.current.controls = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pc.oniceconnectionstatechange = (e) =>
|
||||||
|
console.log(pc.iceConnectionState)
|
||||||
|
pc.onicecandidate = (event) => {
|
||||||
|
if (event.candidate === null) {
|
||||||
|
console.log('sent SDPOffer')
|
||||||
|
socket.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'SDPOffer',
|
||||||
|
offer: pc.localDescription,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offer to receive 1 video track
|
||||||
|
pc.addTransceiver('video', {
|
||||||
|
direction: 'sendrecv',
|
||||||
|
})
|
||||||
|
pc.createOffer()
|
||||||
|
.then((d) => pc.setLocalDescription(d))
|
||||||
|
.catch(console.log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.close()
|
||||||
|
pc.close()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PanelHeader title="Stream" />
|
||||||
|
<video ref={videoRef} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
Reference in New Issue
Block a user