8

I am trying to make a 3D viewer for building models. We have the model loaded and are trying to make some sort of interaction with the model. Therefor we are using OrbiControls for rotating, panning and zooming the model.

We want to have the behaviour in the viewer that when a user clicks and drags (thus rotating), the rotation center is at the point of the building where the user clicks.

I thought I was wise by changing the target of the OrbitControl as such:

control.target.set(newX, newY, newZ);

However, what I found in the source of the OrbitControl.js file, is that when the control updates, the

camera.lookAt() 

function is called, which results the camera jumping to a new position.

Is there any way to get around this? I've been trying for a few hours now, and nothing seemed to be working.

Tried changing target0, then calling reset() on the control. Also tried changing the camera back to the old position (this might be how its done, but I might have tried it poorly.

Wouter Coebergh
  • 804
  • 1
  • 6
  • 20
  • 2
    have you solved this ? :) – Ciprian Tomoiagă Feb 14 '17 at 16:33
  • No I actually have not – Wouter Coebergh Jun 13 '17 at 14:32
  • I think I can solve this for you if you are still interested but I just want to understand your intended result. If a user clicks on the side of a building, you want the central point of the controls to become the nearest point on the mesh that was clicked? Or the central point of the mesh of the building that was clicked? Which one is it, or have I misunderstood? – Martin Joiner Sep 18 '17 at 15:08
  • Hi @MartinJoiner, thanks for your reply. The intended result is that the rotation center of the orbit controls is the intersection point with the first face on mouse down of the drag event. So the user clicks and drags, and that click intersection should become the rotation target without changing the target of the camera. Currently, the target of the camera is the rotation center – Wouter Coebergh Sep 19 '17 at 15:14

3 Answers3

4

Is there any way to get around this?

Short answer is "Yes, but it's not using a standard version OrbitControls.js".

Read on for my detailed reasoning....

I have just spent some time looking at the source code of OrbitControls.js (r87) considering the idea of implementing an enhancement that would allow you to provide an optional 2nd point that it would use as the camera target.

However after exploring the code I think this would be a bad feature to add to the standard public version. There are many features of OrbitControls such as the ability to limit viewing angle, min and max rotation and dolly distance that assume camera and orbit center are the same. If there was an option 2nd camera tagret these would all need to be modified to use either the center of orbit or the camera target or to have a configurable parameter that switches which one it uses. That would add hundreds of extra lines of code, making it harder to understand and all for a very niche feature.

So... The solution:

Because you are building a technical tool, I suspect you don't care about limited viewing angle, distance or rotation so if I were you I would copy OrbitControls.js into your project, rename it to OrbitControls_customised.js and make the changes you need:

Add 2 new parameters below this.target called this.cameraTarget and this.coupleCenters

// "target" sets the location of focus, where the object orbits around
this.target = new THREE.Vector3();

// "cameraTarget" is where the camera is looking (by default the same as target
this.cameraTarget = new THREE.Vector3();

// Whether the camera should be locked to the orbit center
this.coupleCenters = true;

And on the line where it instructs the camera to look at the center...

scope.object.lookAt( scope.target );

...change it so it only updates the camera when coupleCenters is true...

if( scope.coupleCenters ){
    scope.cameraTarget = scope.target;
}
scope.object.lookAt( scope.cameraTarget );

Now, with those changes made, you can put an onMouseDown event that uses RayCasting to find the point on your object, sets controls.decoupleCenters to false and sets controls.target to the intersecting/crossing point of the raycast. Then an onMouseUp event that sets the controls.target back to the controls.cameraTarget to allow it to behave as normal.

I hope that answers your question and gives you some rough road-map to work towards.

Martin Joiner
  • 3,529
  • 2
  • 23
  • 49
  • Martin, thanks for the great tutorial. I will look into this shortly and get back to you – Wouter Coebergh Sep 20 '17 at 13:14
  • Unless I didn't implement it correctly, it didn't seem to work for me on Three.js-v127 as camera still moves to center around cursor position in 3D. – Hakim Jan 14 '22 at 13:34
0

Try this:

First set the position of the control to:

control.center.set(0, 0, 0);

Then do this:

camera.position.copy(control.center).add(new THREE.Vector3(x, y, z+10));

where x, y, and z is the position of your building model.

Notice I've added +10 to z, so that the camera is in front of the model. Change +10 to some other value to get closer/farther to/from the model.

  • Hi, thanks for the feedback, but that was not really what I wanted. You are setting the camera position on the point that is clicked and the controls target to 0,0,0. Yet I want the camera position to change, and only the target of the controls to update! – Wouter Coebergh Mar 01 '16 at 17:17
  • How about, instead of control.target.set(newX, newY, newZ); do this: control.center.set(newX, newY, newZ); – TheWebDesignerPro Mar 02 '16 at 10:47
-1

The following is Martin Joiner's solution (THANKS!!!) implemented in this codepen:

Change line 45 from this:

controls.coupleCenters = false;

to this:

controls.coupleCenters = true;

and then press any key (or click left mouse) to see the original behavior described by Wouter Coebergh before Martin's recommended logic is added to the customized orbital controls.

UberMario
  • 81
  • 1
  • 8