Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
bd15c72
Add cscore + cameraserver
ryanzhangofficial Aug 1, 2025
3cf39fe
Add json processing
ryanzhangofficial Aug 1, 2025
bade902
Add opencv
ryanzhangofficial Aug 1, 2025
5ed3124
Add Camera
ryanzhangofficial Aug 1, 2025
c0822d4
Add Camera extra functions
ryanzhangofficial Aug 1, 2025
4c59da2
Add SimInput
ryanzhangofficial Aug 1, 2025
f76957f
Add camera to wpilibbrain
ryanzhangofficial Aug 1, 2025
1f239ca
Add SimCamera
ryanzhangofficial Aug 1, 2025
b020665
Add camera frame sending
ryanzhangofficial Aug 1, 2025
64a0587
Update siminput camera frame sending
ryanzhangofficial Aug 1, 2025
626c9de
Add camera frame websocket
ryanzhangofficial Aug 1, 2025
8aed9ca
Add camera to wsviewpanel
ryanzhangofficial Aug 1, 2025
d88fc78
Merge branch 'dev' into ryan/1925/camera-implementation
ryanzhangofficial Aug 1, 2025
d207704
Fix: duplicate identifiers
ryanzhangofficial Aug 1, 2025
eab9414
Add java websocket implementation
ryanzhangofficial Aug 1, 2025
2c6090b
Add java-websocket implementation
ryanzhangofficial Aug 1, 2025
3c317e3
chore: formatting
ryanzhangofficial Aug 1, 2025
d45511c
Add SimCameraRenderer
ryanzhangofficial Aug 1, 2025
75634eb
Update captureFrame to jpeg
ryanzhangofficial Aug 1, 2025
c91bb80
Update renderFrame
ryanzhangofficial Aug 1, 2025
d6f1e85
Merge branch 'dev' into ryan/1925/camera-implementation
ryanzhangofficial Aug 4, 2025
48031cd
Add SynthesisWebSocketServer
ryanzhangofficial Aug 4, 2025
9e80ad2
Update SocketAddress to InetSocketAddress
ryanzhangofficial Aug 4, 2025
2a4dfba
Update onOpen & onClose
ryanzhangofficial Aug 4, 2025
94c4284
Add WebSocketMessageHandler
ryanzhangofficial Aug 4, 2025
d9ad7a5
Add simulateTestMessage
ryanzhangofficial Aug 4, 2025
8bea908
Add message handling error handling
ryanzhangofficial Aug 4, 2025
c9e20d4
Add CameraFrameHandler
ryanzhangofficial Aug 4, 2025
bc9449b
Add frame handler debug prints
ryanzhangofficial Aug 4, 2025
b60380d
Add test frame
ryanzhangofficial Aug 4, 2025
1fa3249
Update CameraFrameHandler
ryanzhangofficial Aug 4, 2025
93b0e36
Update SimInput
ryanzhangofficial Aug 4, 2025
e5af40e
Add camera disposing
ryanzhangofficial Aug 4, 2025
e573fd8
Update SimInput
ryanzhangofficial Aug 4, 2025
d8c5b0d
Add SimCameraVisualization
ryanzhangofficial Aug 4, 2025
42918a4
Add updateCameraTransform
ryanzhangofficial Aug 4, 2025
7bca458
Update SimCameraRenderer
ryanzhangofficial Aug 4, 2025
33287c5
Update JavaSample imports
ryanzhangofficial Aug 4, 2025
44dfb7f
chore: formatting
ryanzhangofficial Aug 4, 2025
1a6b417
Cleanup
ryanzhangofficial Aug 4, 2025
1981929
Merge remote-tracking branch 'origin/dev' into ryan/1925/camera-imple…
ryanzhangofficial Aug 6, 2025
62a3fa4
chore: biome
ryanzhangofficial Aug 6, 2025
ae8e477
Merge branch 'dev' into ryan/1925/camera-implementation
ryanzhangofficial Aug 8, 2025
ddb949e
fix: code connection indicator not showing up
ryanzhangofficial Aug 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions fission/src/systems/simulation/wpilib_brain/SimCameraRenderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import * as THREE from "three"
import type MirabufSceneObject from "@/mirabuf/MirabufSceneObject"
import World from "@/systems/World"

export class SimCameraRenderer {
private _camera: THREE.PerspectiveCamera
private _renderTarget: THREE.WebGLRenderTarget
private _canvas: OffscreenCanvas
private _ctx: OffscreenCanvasRenderingContext2D
private _robot: MirabufSceneObject
private _cameraPosition: THREE.Vector3
private _cameraQuaternion: THREE.Quaternion

constructor(robot: MirabufSceneObject, width: number = 640, height: number = 480) {
this._robot = robot

// Create camera for robot perspective
this._camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)

// Create render target for off-screen rendering
this._renderTarget = new THREE.WebGLRenderTarget(width, height, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
})

// Create canvas for frame capture
this._canvas = new OffscreenCanvas(width, height)
this._ctx = this._canvas.getContext("2d")!

// Camera position relative to robot
this._cameraPosition = new THREE.Vector3(0, 0.5, 0.2) // Mounted on robot
this._cameraQuaternion = new THREE.Quaternion()

this.updateCameraTransform()
}

private updateCameraTransform() {
if (!this._robot.mechanism.rootBody) return

// Get robot's transform
const robotBody = World.physicsSystem.getBody(
this._robot.mechanism.nodeToBody.get(this._robot.mechanism.rootBody)!
)

if (!robotBody) return

const robotPos = robotBody.GetPosition()
const robotRot = robotBody.GetRotation()

const robotPosition = new THREE.Vector3(robotPos.GetX(), robotPos.GetY(), robotPos.GetZ())
const robotQuaternion = new THREE.Quaternion(robotRot.GetX(), robotRot.GetY(), robotRot.GetZ(), robotRot.GetW())

const worldCameraPos = this._cameraPosition.clone()
worldCameraPos.applyQuaternion(robotQuaternion)
worldCameraPos.add(robotPosition)

const cameraRotation = new THREE.Quaternion()
cameraRotation.copy(robotQuaternion)

const forwardFix = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI)
const upFix = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI)

cameraRotation.multiply(forwardFix).multiply(upFix)

this._camera.position.copy(worldCameraPos)
this._camera.quaternion.copy(cameraRotation)
this._camera.updateMatrixWorld()
}

public renderFrame(): ImageData | null {
if (!World.sceneRenderer) return null

this.updateCameraTransform()

// Render scene from camera perspective
const renderer = (World.sceneRenderer as any)._renderer as THREE.WebGLRenderer
const scene = (World.sceneRenderer as any)._scene as THREE.Scene

// Store original render target
const originalTarget = renderer.getRenderTarget()

// Render to our target
renderer.setRenderTarget(this._renderTarget)
renderer.render(scene, this._camera)

// Read pixels
const pixels = new Uint8Array(this._renderTarget.width * this._renderTarget.height * 4)
renderer.readRenderTargetPixels(
this._renderTarget,
0,
0,
this._renderTarget.width,
this._renderTarget.height,
pixels
)

// Restore original target
renderer.setRenderTarget(originalTarget)

// Convert to ImageData
const imageData = new ImageData(
new Uint8ClampedArray(pixels),
this._renderTarget.width,
this._renderTarget.height
)

return imageData
}

public captureFrameAsJPEG(): Promise<Blob> {
const imageData = this.renderFrame()
if (!imageData) return Promise.reject("No frame data")

// Draw to canvas
this._ctx.putImageData(imageData, 0, 0)

// Convert to JPEG blob
return this._canvas.convertToBlob({ type: "image/jpeg", quality: 0.8 })
}

public setResolution(width: number, height: number) {
this._renderTarget.setSize(width, height)
this._canvas.width = width
this._canvas.height = height
this._camera.aspect = width / height
this._camera.updateProjectionMatrix()
}

public setCameraOffset(position: THREE.Vector3, rotation?: THREE.Quaternion) {
this._cameraPosition.copy(position)
if (rotation) {
this._cameraQuaternion.copy(rotation)
}
}

public dispose() {
this._renderTarget.dispose()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as THREE from "three"
import type MirabufSceneObject from "@/mirabuf/MirabufSceneObject"
import SceneObject from "@/systems/scene/SceneObject"
import World from "@/systems/World"

/**
* Visual representation of the WPILib camera in the 3D scene
* Shows where the camera is mounted and its field of view
*/
export class SimCameraVisualization extends SceneObject {
private _robot: MirabufSceneObject
private _cameraGroup: THREE.Group
private _cameraPosition: THREE.Vector3
private _isVisible: boolean = false

constructor(robot: MirabufSceneObject) {
super()
this._robot = robot

this._cameraPosition = new THREE.Vector3(0, 0.5, 0.2)

this._cameraGroup = new THREE.Group()
}

public setup(): void {
World.sceneRenderer.addObject(this._cameraGroup)
console.log("[VISUAL] SimCameraVisualization added to scene")
}

public update(): void {
if (!this._isVisible) return

this.updateCameraTransform()
}

private updateCameraTransform() {
if (!this._robot.mechanism.rootBody) return

const robotBody = World.physicsSystem.getBody(
this._robot.mechanism.nodeToBody.get(this._robot.mechanism.rootBody)!
)

if (!robotBody) return

const robotPos = robotBody.GetPosition()
const robotRot = robotBody.GetRotation()

const robotPosition = new THREE.Vector3(robotPos.GetX(), robotPos.GetY(), robotPos.GetZ())
const robotQuaternion = new THREE.Quaternion(robotRot.GetX(), robotRot.GetY(), robotRot.GetZ(), robotRot.GetW())

const worldCameraPos = this._cameraPosition.clone()
worldCameraPos.applyQuaternion(robotQuaternion)
worldCameraPos.add(robotPosition)

const cameraRotation = new THREE.Quaternion()
cameraRotation.copy(robotQuaternion)
const forwardFix = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI)
const upFix = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI)
cameraRotation.multiply(forwardFix).multiply(upFix)

this._cameraGroup.position.copy(worldCameraPos)
this._cameraGroup.quaternion.copy(cameraRotation)
}

public setVisible(visible: boolean) {
this._isVisible = visible
this._cameraGroup.visible = visible

// if (visible) {
// console.log("📹 [VISUAL] Camera visualization enabled - you should see a camera model on your robot")
// } else {
// console.log("📹 [VISUAL] Camera visualization disabled")
// }
}

public dispose(): void {
if (this._cameraGroup.parent) {
this._cameraGroup.parent.remove(this._cameraGroup)
}

this._cameraGroup.traverse(child => {
if (child instanceof THREE.Mesh) {
child.geometry.dispose()
if (Array.isArray(child.material)) {
child.material.forEach(material => material.dispose())
} else {
child.material.dispose()
}
}
})

console.log("[VISUAL] SimCameraVisualization disposed")
}
}
Loading
Loading