Camera Position - (Web)

From this interesting discussion I got curious about seeing if I could hook up my SpaceMouse to work in SketchUp Web.

Unfortunately I was unable to find a way to change the camera position (yet).

In another 3D web app that we use for quoting I was able to manipulate the camera, but I’m not quite sure how to calculate all the moves from the six axes.

So I can set the camera position and target like this, but I’m not sure how to calculate the values.

function updateController() {
  axes = navigator.getGamepads()[1].axes
  pan_lr_delta = axes[0];
  pan_ud_delta = axes[2];
  zoom_delta = axes[1];
  rotate_ud_delta = axes[3];
  rotate_lr_delta = axes[4];
  orbit_delta = axes[5];
  //Set the camera target and position
  //???
  camera.position.set(x, y, z,);
  controls.target.set(x, y, z);
  controls.update();
  requestAnimationFrame(updateController);
}

PS: @TheOnlyAaron Is there any chance that the same thing could be done in SketchUp Web? It seems like it would be trivial to implement. Obviously there is a camera control in there somewhere already.

The controls.target and camera.position are both a Vector3

https://threejs.org/docs/#api/en/math/Vector3

It could be trivial, I did the same with other web 3D engines, I have code somewhere that I tried for SketchUp. But for SketchUp I don’t know the API to set the camera position.

I was in talk with engineers at BaseCamp 2018 and the idea did not seem new to them at all. I guess there is a long way from finding out at a hackathon that something is possible to making it part of a supportable product.

OK I got the zoom and up/down panning working nicely (in SmartBuild), but I’m stuck on the rotation, orbit, and left/right pan.

I realize this isn’t really a SketchUp question.

function updateController() {
  if(document.hasFocus()) {
    axes = navigator.getGamepads()[1].axes
    pan_lr_delta = axes[0];
    pan_ud_delta = axes[2] * 10;
    zoom_delta = axes[1] * 25;
    rotate_ud_delta = axes[3];
    rotate_lr_delta = axes[4];
    orbit_delta = axes[5];

    //Set the camera target and position
        camera_direction = new THREE.Vector3();
    camera_direction.subVectors(camera.position, controls.target);
    distance = camera_direction.length();
    //zoom
    if (zoom_delta != 0){
      console.log(distance);
      distance_factor = (distance / 100) + 0.1
      camera_direction.setLength(0 - (distance_factor * zoom_delta));

      camera.position.add(camera_direction);
      if (distance < 10 && zoom_delta > 0) controls.target.add(camera_direction);
    }
    //up and down pan
    if (pan_ud_delta != 0) {
      controls.target.z += pan_ud_delta;
      camera.position.z += pan_ud_delta;
    }


    // camera.position.set(x, y, z,);
    // controls.target.set(x, y, z);
    controls.update();
  }
  requestAnimationFrame(updateController);
}

Not much I can add to this… I am not part of the web development team and, for that matter, have never created any extension code, myself!

I do love using a 3D mouse, though!

1 Like

I have some js code somewhere. Roughly a year ago i made a space mouse controlled relay board. The actual code interacting with the spacemouse was in JS.

I will set a note in my agenda for next monday to try to find it again.

2 Likes

Don’t bother I finally got it figured out. Perhaps not the easiest way but it is totally working. I used inject code extension to automatically insert the js into the website when it loads. Yeah! I’ve implemented SpaceMouse control for SmartBuild.

EDIT: Added code to turn off input if document is not focused.

Some code was borrowed from OrbitControls.js.

offset = new THREE.Vector3();
// so camera.up is the orbit axis
quat = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 0, 1), new THREE.Vector3(0, 1, 0));
quatInverse = quat.clone().invert();
lastPosition = new THREE.Vector3();
lastQuaternion = new THREE.Quaternion();
spherical = new THREE.Spherical();
sphericalDelta = new THREE.Spherical();

function rotate(angle, anglez) {
  angle = angle * Math.PI / 180
  anglez = anglez * Math.PI / 180
  var position = camera.position;
  offset.copy(position).sub(controls.target);
  // rotate offset to "y-axis-is-up" space
  offset.applyQuaternion(quat);
  // angle from z-axis around y-axis
  spherical.setFromVector3(offset);
  spherical.theta -= angle;
  spherical.phi -= anglez;
  // restrict theta to be between desired limits
  spherical.theta = Math.max(controls.minAzimuthAngle, Math.min(controls.maxAzimuthAngle, spherical.theta));
  // restrict phi to be between desired limits
  //spherical.phi = Math.max(controls.minPolarAngle, Math.min(controls.maxPolarAngle, spherical.phi));

  spherical.makeSafe();
  //scope.target.add(panOffset);
  offset.setFromSpherical(spherical);
  // rotate offset back to "camera-up-vector-is-up" space
  offset.applyQuaternion(quatInverse);
  camera.position.copy(controls.target).add(offset);
  controls.update();
}

function pan(deltax, deltay) {
  var element = controls.domElement === document ? controls.domElement.body : controls.domElement;
  var position = camera.position;
  offset.copy(position).sub(controls.target);
  var targetDistance = offset.length();
  // half of the fov is center to top of screen
  targetDistance *= Math.tan((camera.fov / 2) * Math.PI / 180.0);
  distancex = 2 * deltax * targetDistance / element.clientHeight
  var v = new THREE.Vector3();
  v.setFromMatrixColumn(camera.matrix, 0); // get x column of objectMatrix
  v.multiplyScalar(distancex);
  controls.target.add(v);
  camera.position.add(v);
  distancey = 2 * deltay * targetDistance / element.clientHeight
  v.setFromMatrixColumn(camera.matrix, 1); // get x column of objectMatrix
  v.multiplyScalar(distancey);
  controls.target.add(v);
  camera.position.add(v);
}

function updateController() {
  if(document.hasFocus()) {
    gamepad = navigator.getGamepads()[1]
    if (gamepad === null || typeof controls == 'undefined' || typeof camera == 'undefined') {
      requestAnimationFrame(updateController);
      return;
    }
    axes = gamepad.axes
    pan_lr_delta = axes[0] * 100;
    pan_ud_delta = axes[2] * 100;
    zoom_delta = axes[1] * 25;
    rotate_ud_delta = axes[3] * 10;
    rotate_lr_delta = axes[4] * 10;
    orbit_delta = axes[5] * 10;

    //Set the camera target and position
    camera_direction = new THREE.Vector3();
    camera_direction.subVectors(camera.position, controls.target);
    distance = camera_direction.length();
    distance_factor = (distance / 100) + 0.1
    //zoom
    if (zoom_delta !== 0){
      camera_direction.setLength(0 - (distance_factor * zoom_delta));
      camera.position.add(camera_direction);
      if (distance < 10 && zoom_delta > 0) controls.target.add(camera_direction);
    }
    //pan
    if (pan_lr_delta !== 0 || pan_ud_delta !== 0) pan(0 - pan_lr_delta, pan_ud_delta);
    //ORBIT
    if (orbit_delta !== 0 || rotate_ud_delta != 0) rotate(0 - orbit_delta, rotate_ud_delta);
    controls.update();
  }
  requestAnimationFrame(updateController);
}
updateController();

Now if SketchUp would expose their camera the same thing could be done in SketchUp Web.

1 Like

As cool as that is doesn’t that defeat the whole purpose of the variable input? :wink:

Sure :smile:
But my goal was to control a toy tower crane with the space mouse.

It was at the beginning of the pandemic when my son said that he was unable to play with his crane, since Kraanman (dutch for crane operator) was unable to come to work, since “all mommies and dads” needed to work from home… so I picked up some laying around parts and made the guy’s crane remote controlled…

In the end the relay board got hooked up to a raspberry pi running an asp.net core webservice + a video streaming service and a react.js frontend. So that the laptop could use that frontend to communicate with the backend services and the spacemouse. And, correct as you say, I did not take into account the variable input. Instead of using a relay board, feeding some mosfets with a pwm signal based on the variable input… would be better. Maybe next time :grin:

Now, other than that… we are waiting for ages now, for a SketchUp web API…

6 Likes

I would give you two likes if I could :slight_smile:

2 Likes

How did you do the calibration (e.g. find the factors to multiply with the gamepad axes signals)?

I suppose that it varies from device to device and that might be the reason why their hackathon project did not find its way into the product.

Also it would be code that depends on a product that is controlled by another company (3dconnexion) who might introduce changes or release new devices that work slightly differently so that it is hard for SketchUp to ensure a consistent experience.

I played around till it felt right. I imagine ‘real support’ would need to allow the user to set the sensitivity, as well as allow the option to reverse the direction, as I has to do for some of the axes.

Not really. Gamepad API is universal. If they made the axis customizable so the end user could select which axes controls what function, any gamepad device could be used, not just the SpaceMouse. Settings would of course need to be per device id, in case the user has multiple devices.

There are really only three things that could change.

  1. axes sensitivity.
  2. axes direction.
  3. order of axes.
2 Likes

True! Also, why I think SketchUp for Web could provide so much additional value by supporting 3 axis controllers (any controller!). It could even be a unique selling point which makes SketchUp Web (within the Studio subscription) more attractive than Pro. Isn’t that what everone says: “What do I want with all the extra stuff in Studio if Pro has all I need?”

I was thinking of setting reasonable defaults (without burdening users with too much GUI), which is hard when there are differences from device to device.

1 Like

That is true. I would expect some defaults for the most common devices.

Do you know who the right person is to ask about a camera api? It doesn’t have to be official.

For API you can always try @ChrisFullmer or @thomthom … if they don’t know they might be able to point you in the right direction

1 Like

@ChrisFullmer or @thomthom
Any input? (Pun intended)

Something like:

camera = window.someBuriedVariable.camera;
camera.position.set(x, y, z);
camera.target.set(x, y, z);
camera.refreshView();

I find it unlikely anyone will provide assistance for an internal API. Even if it is possible to interact with SketchUp for web using Javascript, doing so before there is a formal API is very fragile. An internal API could change at any time, and may also lack the safeguards a public API has to prevent corruption and data loss.

I’m not asking about an official api, although that would be welcome. The interest here instead is an unofficial ‘hack’, as a proof of concept.

I would love to do something like this for SketchUp Web (even though I don’t use SketchUp web myself).