How do I enable vtk wasm, trame with space mouse support in the browser?
I am not sure how the events from the space mouse are presented to the browser.
But it is very likely that the mouse binding from wasm are incomplete and therefore do not currently handle the space mouse. I’m not sure is someone at Kitware has a space mouse.
@jaswantp do you know where the mouse binding is happening in JS for WASM?
@Sebastien_Jourdain , @jaswantp — I have this code snippet that uses a gamepad to control an acrball camera. How do add custom java script to the render loop for vtk wasm and then udpate the camera based on this code?
var prevTx = 0.0;
var prevTy = 0.0;
var prevTz = 0.0;
var prevRx = 0.0;
var prevRy = 0.0;
var prevRz = 0.0;
function handleGamepadInput() {
if (!isWindowActive) {
// If window is not active, don't process input
return;
}
if (!camera)
{
return;
}
var gamepads = navigator.getGamepads();
var gamepad = null;
for (var i = 0; i < gamepads.length; i++) {
if (gamepads[i] && gamepads[i].id.toLowerCase().includes('spacemouse')) {
gamepad = gamepads[i];
break;
}
}
if (gamepad) {
if (gamepad.buttons[0].pressed) {
camera.fitView();
setHudText("Fit View");
somethingchanged = true;
return;
}
var Tx = gamepad.axes[0];
var Ty = gamepad.axes[1];
var Tz = gamepad.axes[2];
var Rx = gamepad.axes[3];
var Ry = gamepad.axes[4];
var Rz = gamepad.axes[5];
var ROTATIONAL_SCALE = 50.0;
var TRANSLATIONAL_SCALE = 0.1;
var RzoomPenalty = Math.exp(-1.0/(camera.getZoom()*.1));
var TzoomPenalty = Math.exp(-1.0/(camera.getZoom()*.1));
Rx *= ROTATIONAL_SCALE*RzoomPenalty;
Ry *= ROTATIONAL_SCALE*RzoomPenalty;
Rz *= -ROTATIONAL_SCALE*RzoomPenalty;
Tx *= TRANSLATIONAL_SCALE*TzoomPenalty;
Ty *= 1.0;//TRANSLATIONAL_SCALE*TzoomPenalty;
Tz *= -TRANSLATIONAL_SCALE*TzoomPenalty;
var SMOOTHING_FACTOR = 0.1;
function smoothValue(prev, current) {
return prev + (current - prev) * SMOOTHING_FACTOR;
}
Tx = smoothValue(prevTx, Tx);
Ty = smoothValue(prevTy, Ty);
Tz = smoothValue(prevTz, Tz);
Rx = smoothValue(prevRx, Rx);
Ry = smoothValue(prevRy, Ry);
Rz = smoothValue(prevRz, Rz);
function shouldMove(prev, current) {
var delta = Math.abs(prev - current);
if (delta < 1e-6)
return false;
delta/=current;
if (delta < 0.01) {
return false;
}
return true;
}
if (shouldMove(prevTx, Tx) || shouldMove(prevTy, Ty) || shouldMove(prevTz, Tz) || shouldMove(prevRx, Rx) || shouldMove(prevRy, Ry) || shouldMove(prevRz, Rz))
{
setPZRing();
// Create a rotation matrix from the Rx, Ry, Rz values
// Create a quaternion for each axis rotation
let quatX = quat.create();
let quatY = quat.create();
let quatZ = quat.create();
// Set the quaternions to represent the desired rotations
quat.rotateX(quatX, quatX, glMatrix.toRadian(Rx));
quat.rotateY(quatY, quatY, glMatrix.toRadian(Rz));
quat.rotateZ(quatZ, quatZ, glMatrix.toRadian(Ry));
// Combine the rotations by multiplying the quaternions
let rotQuat = quat.create();
quat.multiply(rotQuat, quatZ, quatY);
quat.multiply(rotQuat, rotQuat, quatX);
// Translate to the point (0.5, 0.5, 0.5)
let translationTo = vec3.set(vec3.create(), -0.5, -0.5, -0.5);
let translationMatrixTo = mat4.fromTranslation(mat4.create(), translationTo);
// Translate back from the point (0.5, 0.5, 0.5)
let translationFrom = vec3.set(vec3.create(), 0.5, 0.5, 0.5);
let translationMatrixFrom = mat4.fromTranslation(mat4.create(), translationFrom);
// Apply the translations and rotation
mat4.multiply(camera.translation, translationMatrixFrom, camera.translation);
quat.multiply(camera.rotation, rotQuat, camera.rotation);
mat4.multiply(camera.translation, translationMatrixTo, camera.translation);
// Apply the Tx, Ty, Tz translations
var vt = vec3.set(vec3.create(), Tx, Tz, Ty);
var t = mat4.fromTranslation(mat4.create(), vt);
camera.translation = mat4.mul(camera.translation, t, camera.translation);
if (camera.translation[14] >= -0.2) {
camera.translation[14] = -0.2;
}
var delta = vec4.set(vec4.create(), Tx * camera.invScreen[0] * Math.abs(camera.translation[14]),
Tz * camera.invScreen[1] * Math.abs(camera.translation[14]), 0, 0);
var worldDelta = vec4.transformMat4(vec4.create(), delta, camera.invCamera);
var translation = mat4.fromTranslation(mat4.create(), worldDelta);
camera.centerTranslation = mat4.mul(camera.centerTranslation, translation, camera.centerTranslation);
// Update the camera matrix
camera.updateCameraMatrix();
prevTx = Tx;
prevTy = Ty;
prevTz = Tz;
prevRx = Rx;
prevRy = Ry;
prevRz = Rz;
somethingchanged = true;
}
}
}
Hi @Sebastien_Jourdain , @jaswantp I got this wired up. I can see the gamepad but it breaks the sync with the trame camera and the app stops working any ideas?
#### code to set the view update
with vtklocal.LocalView(
self.render_window,
ctx_name="view",
updated="utils.get('setVtkReady')()",
) as view:
#wasm_id = view.register_vtk_object(self.factory.axes)
wasm_id = view.register_vtk_object(self.factory.scalar_bar)
self.interactor_wasm_id = view.get_wasm_id(self.render_window_interactor)
self.renderer_id = view.get_wasm_id(self.renderer)
### Code to add the script
client.Script(f"""
var vtk_ready = false;
function setVtkReady() {{
vtk_ready = true;
}}
window.setVtkReady = setVtkReady;
var prevTx = 0.0;
var prevTy = 0.0;
var prevTz = 0.0;
var prevRx = 0.0;
var prevRy = 0.0;
var prevRz = 0.0;
var renderer = null;
var renderWindow = null;
var camera = null;
console.log("Entering Custom JS");
function handleGamepadInput() {{
if (!vtk_ready) {{
return;
}}
if (!renderer) {{
renderer = trame.refs.{self.ctx.view.ref_name}.getVtkObject({self.renderer_id});
}}
if (!renderWindow) {{
renderWindow = trame.refs.{self.ctx.view.ref_name}.getVtkObject({self.ctx.view.get_wasm_id(self.render_window)});
}}
camera = renderer.activeCamera;
var gamepad = null
if (!gamepad)
{{
var gamepads = navigator.getGamepads();
for (var i = 0; i < gamepads.length; i++) {{
if (gamepads[i] && gamepads[i].id.toLowerCase().includes('spacemouse')) {{
gamepad = gamepads[i];
console.log("Gamepad connected: " + gamepad.id);
break;
}}
}}
}}
if (gamepad) {{
var Tx = gamepad.axes[0];
var Ty = gamepad.axes[1];
var Tz = gamepad.axes[2];
var Rx = gamepad.axes[3];
var Ry = gamepad.axes[4];
var Rz = gamepad.axes[5];
//console.log(`Raw Input - Tx: ${{Tx.toFixed(4)}}, Ty: ${{Ty.toFixed(4)}}, Tz: ${{Tz.toFixed(4)}}, Rx: ${{Rx.toFixed(4)}}, Ry: ${{Ry.toFixed(4)}}, Rz: ${{Rz.toFixed(4)}}`);
/*
var ROTATIONAL_SCALE = 50.0;
var TRANSLATIONAL_SCALE = 0.1;
var RzoomPenalty = 1.0;//Math.exp(-1.0/(camera.getZoom()*.1));
var TzoomPenalty = 1.0;//Math.exp(-1.0/(camera.getZoom()*.1));
Rx *= ROTATIONAL_SCALE*RzoomPenalty;
Ry *= ROTATIONAL_SCALE*RzoomPenalty;
Rz *= -ROTATIONAL_SCALE*RzoomPenalty;
Tx *= TRANSLATIONAL_SCALE*TzoomPenalty;
Ty *= 1.0;//TRANSLATIONAL_SCALE*TzoomPenalty;
Tz *= -TRANSLATIONAL_SCALE*TzoomPenalty;
*/
var SMOOTHING_FACTOR = 0.1;
function smoothValue(prev, current) {{
return prev + (current - prev) * SMOOTHING_FACTOR;
}}
Tx = smoothValue(prevTx, Tx);
Ty = smoothValue(prevTy, Ty);
Tz = smoothValue(prevTz, Tz);
Rx = smoothValue(prevRx, Rx);
Ry = smoothValue(prevRy, Ry);
Rz = smoothValue(prevRz, Rz);
function shouldMove(prev, current) {{
var delta = Math.abs(prev - current);
if (delta < 1e-6) {{
return false;
}}
delta/=current;
if (delta < 0.01) {{
return false;
}}
return true;
}}
// asdf
if (shouldMove(prevTx, Tx) || shouldMove(prevTy, Ty) || shouldMove(prevTz, Tz) || shouldMove(prevRx, Rx) || shouldMove(prevRy, Ry) || shouldMove(prevRz, Rz))
{{
let position = camera.position;
let focalPoint = camera.focalPoint;
let viewUp = camera.viewUp;
// Rotation
let rotQuat = quat.create();
quat.rotateX(rotQuat, rotQuat, glMatrix.toRadian(Rx));
quat.rotateY(rotQuat, rotQuat, glMatrix.toRadian(Ry));
quat.rotateZ(rotQuat, rotQuat, glMatrix.toRadian(Rz));
let rotMat = mat4.create();
mat4.fromQuat(rotMat, rotQuat);
let camDir = vec3.create();
vec3.sub(camDir, focalPoint, position);
vec3.transformMat4(camDir, camDir, rotMat);
let newFocalPoint = vec3.create();
vec3.add(newFocalPoint, position, camDir);
// Pan
let pan = vec3.fromValues(Tx, Ty, 0); // Only pan in X/Y, adjust as needed
vec3.add(position, position, pan);
vec3.add(newFocalPoint, newFocalPoint, pan);
// Zoom (Dolly)
if (Math.abs(Tz) > 1e-6) {{
let zoomDir = vec3.create();
vec3.sub(zoomDir, newFocalPoint, position);
vec3.normalize(zoomDir, zoomDir);
vec3.scale(zoomDir, zoomDir, Tz * 0.1); // 0.1 is a zoom scale factor
vec3.add(position, position, zoomDir);
}}
// Update camera
camera.setPosition(...position);
camera.setFocalPoint(...newFocalPoint);
camera.setViewUp(...viewUp);
renderer.resetCameraClippingRange();
renderWindow.render();
}}
}}
}}
function gamepadRenderLoop() {{
handleGamepadInput();
requestAnimationFrame( gamepadRenderLoop);
}}
console.log("Entering Render Loop");
gamepadRenderLoop();
window.gamepadRenderLoop = gamepadRenderLoop;
"""
)
It would be hard to provide meaning input without spending time trying things ourselves.
But at a high level it should be able to work. Normally the camera get synched the first time and skipped afterwards. So I’m not sure what is happening here…
Keep in mind our support options if you want us to dig deeper and help you with your exploration of trame and VTK.wasm.