Zoom on mouse pointer in three.js - javascript

Using thingiview.js, Three.js and the trackballControls, I've put together a system in which I can upload an STL file, and then render it on the canvas. trackballControls are pretty great with some adjustment, but I'm having an issue:
I would like to zoom in at the point of the mouse cursor as opposed to the center of the grid/plane.
I've done a simple captureEvent to get the on screen coordinates of the mouse and track it, but I'm having issue on figuring out where to tap into the control scheme to do it.
I checked out the _zoomStart / _zoomEnd stuff (which confuses me a little as it goes off of "y", I assumed it would be "z"). But when trying to add a _zoomStart.x, it basically ignores it.
Now I may not be a guru, but I'm comfortable banging around usually.
I'd also like to make sure that when I pan, the zoom and rotate still bases off the center of the object, as opposed to the center of the grid/plane.
Have been searching for days through posts and examples, but not really finding any answers.
I'm sure I'm not looking in the right place/heading in the right direction. A helpful nudge (or better yet a swift kick) in the right direction would be truly appreciated.
EDIT
this.zoomCamera = function () {
var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
if ( factor !== 1.0 && factor > 0.0 ) {
_eye.multiplyScalar( factor );
if ( _this.staticMoving ) {
_zoomStart.copy( _zoomEnd );
} else {
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
};
I assume the above is where I would go in for the zoom alter. What I don't understand is it being set to _zoomStart.y. But taking it as it is, how would I implement x?
I mean, if _zoomStart and _zoomEnd are a Vector2, where in the above code does it define x?
confuzzled

Zooming in Trackballcontrols is not actually zooming (that would be setting the camera fov). Two objects are just getting moved around in the controls..., the other would be the camera (this.object), the other the point it's looking at (this.target). I have not played much with trackballcontrols, but I would hazard a guess it won't touch the target at all (so all movement and zooming will revolve around that).
You could try changing the target at onclick, something like:
mycontrols.target = new THREE.Vector3(newx, newy, newz);
You might need to update/reset some other Trackballcontrols internal variables, but it might also work just like that.
For getting the 3D x/y/z coordinates from the 2D x/y mouse coordinates, I suggest searching around for ray casting or object picking, should find plenty of examples.

The trick is that _zoomStart and _zoomEnd is created for touch zooming, and when you zoom using the mouse wheel you have to pass only one variable expressing: "how much to zoom". The programmer didn't create a new variable for it but used the _zoom###.y component.
So _zoomStart and _zoomEnd doesn't provide information about how the zooming will be executed, these variables contain the "instruction" only. Then the software converts it to the "zoompan" vector expressing the required movement of the camera in 3D.

Related

Move up and down using pointer lock controls THREE.js

I need to move up and down using three.js's pointerlockcontrols. I am looking for the same kind of movement as three.js's flycontrols, where the amount you move up and down is proportional to the direction you are looking in (ie. if your looking north-east, you move in the Y and Z directions at the same rate).
I tried:
if(keys[38] || keys[87]){
Controls.moveForward(playerSpeed);
Camera.position.y += Math.cos(Camera.rotation.y) * playerSpeed;
}
Where keys is just a object of all the keys and whether they are down, however this didn't give the appropriate behavior, as looking straight down also moved you forward a lot.
Has anyone ever accomplished or knows how to do this?

GMap Bearing rotation in smooth motion (avoid jerky effect when changing bearing values)

I want to rotate GMap by changing the bearing angle value, so the camera rotates around the center point (360-Degree one full round ).
When we change the bearing, there is a easing effect at camera start and end points. How can I control/change that in order to make the rotation smooth when change Bearing values (in order to rotate map in 360 Degree, smooth animation)?
Required this for all languages as it appears the easing effect is different in different language libraries. e.g. Swift, Android, PHP, JS, Node.js, React.
Swift Example (running OK in Linear Animation):
Note that initially the animation did had jerks in iOS as well, but when we make use of CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear along its CATransaction properties then the GMap animation turned into smooth animation. so now if you see the code below, the change in Bearing value does not create jerky effect (due to the easing effect in GMap animation). I am looking for appropriate solution for Android and Web as well.
//Move the map around current location, first loop
let timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
CATransaction.begin()
CATransaction.setValue(3.0, forKey: kCATransactionAnimationDuration)
CATransaction.setAnimationTimingFunction(timingFunction)
CATransaction.setCompletionBlock({
//Move the map around current location, second loop
let timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
CATransaction.begin()
CATransaction.setValue(3.0, forKey: kCATransactionAnimationDuration)
CATransaction.setAnimationTimingFunction(timingFunction)
CATransaction.setCompletionBlock({
//Move the map around current location, third loop
let timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
CATransaction.begin()
CATransaction.setValue(3.0, forKey: kCATransactionAnimationDuration)
CATransaction.setAnimationTimingFunction(timingFunction)
CATransaction.setCompletionBlock({
UIView.animate(withDuration: 0.5, animations: {
self.findingYourLocation.alpha = 0.0
})
//TODO: Set nearest branch
// Zoom in one zoom level
let zoomCamera = GMSCameraUpdate.zoomIn()
self.mapView.animate(with: zoomCamera)
// Center the camera on UBL Branch when animation finished
//let nearestBranch = CLLocationCoordinate2D(latitude: 24.850751, longitude: 67.016589)
let nearestBranch = CLLocationCoordinate2D.init(latitude: 24.806849, longitude: 67.038734)
let nearestBranchCam = GMSCameraUpdate.setTarget(nearestBranch)
CATransaction.begin()
let timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
CATransaction.setValue(3.0, forKey: kCATransactionAnimationDuration)
CATransaction.setAnimationTimingFunction(timingFunction)
CATransaction.setCompletionBlock({
self.nextButton.alpha = 1.0
})
self.mapView.animate(with: nearestBranchCam)
self.mapView.animate(toZoom: 15)
self.mapView.animate(toBearing: 0)
self.mapView.animate(toViewingAngle: 0)
CATransaction.commit()
})
self.mapView.animate(toBearing: self.mapView.camera.bearing + 120)
CATransaction.commit()
})
self.mapView.animate(toBearing: self.mapView.camera.bearing + 120)
CATransaction.commit()
})
self.mapView.animate(toBearing: self.mapView.camera.bearing + 120)
CATransaction.commit()
The Android example code (has problem):
The Android example/sample code can be found here: https://issuetracker.google.com/issues/71738889
Which also includes an .apk file, an .mp4 video of sample app output. Which clearly shows jerky effects when Bearing value changes while rotating the map in 360-Degree.
Giving this as an answer as a comment would be rather hard to read; this is taken from the google documentation.
Consider this code:
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(MOUNTAIN_VIEW) // Sets the center of the map to Mountain View
.zoom(17) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(30) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
This code creates a new camera position, and that's exactly what you're trying to mutate: the bearing of the camera. So if you create a new camera position like this:
CameraPosition cameraPosition = new CameraPosition.Builder()
.bearing(50)
.build();
and then animate the camera to that position:
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
That should do the trick. To give some more information on what you will need to use as bearing:
a bearing of 90 degrees results in a map where the upwards direction points due east.
Good luck.
I'm going to write this as another answer as I'd like to take the time to write a wall of text, whereas I'd like to keep the other answer as short as possible since it might still help other people with a similar problem.
The problem
So if I understand correctly, what you're trying to do is build an application with Google maps for different platforms. You're running into an issue with Google maps (the jerky movement) and you're trying to find a fix for all the platforms.
My proposed solutions
I'll divide this into a few sections, because I see different ways to go forward.
Find a solution for all the platforms.
This one seems like the most straightforward, but it could be akin to the XY problem. I've tried to introduce you to some ways of animating views, and you've solved the problem in your iOS app, but at the core what you're dealing with is a flaw in the Google maps animation when changing the bearing. I am not sure if there is a way to tackle this problem on every platform, as I haven't tried.
Use a different map
This sounds like a big step, and depending on your usage something you don't want to do. However, I've successfully used Leaflet (a JS map) with a WKWebView on iOS and Android and that all worked pretty well (this obviously also works fine in the browser). Keep in mind that some other maps might also have the jerky animation.
Wrapping it up
I hope I've given some insight. I'll add to this answer as we find out new things about the problem. Could you try to provide a minimal reproducible example? That way I can give it a better try. Good luck!
After going through all possible cases, I came to know that GMap is not built with required feature of Rotating map in 360-Degree in one go with custom animation. Don't know if this appears in next GMap api version.
For now, the possible solution is to change the animation logic, and instead of rotating 360-Degree we can rotate to e.g. 180-Degree with reduced animation speed (animation duration time).
This allows us to bring required map surrounding search experience to the user.
(For now I am posting this temporary answer, and allow others to update or post latest answer in future).
I have submitted this animation control issue on GMap issue tracker, please START this issue in order to show your interest and feedback, so Google team can take this feature into their final consideration.
https://issuetracker.google.com/u/1/issues/71738889
Here is my approach:
tilt = 45f
zoom = 18
target = currentLocation
bearing = lastKnownLocation.bearingTo(currentLocation)
Use map.animateCamera(); // instead of move camera
CameraPosition cameraPosition;
if (currentLocation.distanceTo(lastKnownLocation) > 50) {
cameraPosition = new CameraPosition.Builder()
.target(currentLatLng).zoom(20).tilt(45f).bearing(bearingValue).build();
} else {
cameraPosition = new CameraPosition.Builder()
.target(currentLatLng).zoom(20).tilt(45f).build();
}
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
What else you have to do?
You must call this method from LocationCallback (where you are getting location updates) and then we can have a smooth experience.
So if the user moved (walked or driving) and the distance between lastKnownLocation and currentLocation is greater than 50 meters then only we will set bearing otherwise we keep changing the targetLocation only.
In this way, we can show heading direction like in google maps.

Three.js - Rotation not respecting local orientation

I'm using a large array of objects built around a center point in a scene, and need to manipulate them all around their local axis. They are all facing the origin using a blank object and lookAt(), then I used this method to align the other axes correctly. Getting the initial rotation this way worked great, unfortunately when I try to rotate these objects on the fly with object.rotation.x = <amount>, it does not respect the local axis of the object.
The confusing part is, it's not even using the global axis, the axis it's using almost seems entirely arbitrary. I set up a JSFiddle to demonstrate this here. As you can see on line 129, looker.rotation.z works correctly, it rotates along the Z axis properly, but if it's changed to X or Y, it doesn't rotate along local or global axes. If anyone could demystify what is happening to cause this, that would be great.
What is happening is that you want to add some rotation to the current orientation, and setting the variable looker.rotation.z means other thing.
At the end, to calculate the rotation matrix of the looker, there will be something like (pseudocode: the functions are not these, but you get the idea):
this.matrix.multiply( makeXRotationMatrix(this.rotation.x) )
this.matrix.multiply( makeYRotationMatrix(this.rotation.y) )
this.matrix.multiply( makeZRotationMatrix(this.rotation.z) )
DrawGeometry(this.geom, this.matrix)
and composition of rotations are not intuitive. This is why it doesn't seem to follow any axis system.
If you want to apply a rotation in some axis to the existing matrix, it can be made with the functions rotateX (angle), rotateY (angle), rotateZ (angle), and rotateOnAxis (axis, angle). axis can be a THREE.Vector3.
Changing directly looker.rotation.z works because it is the nearest rotation to the geometry, and it will not be affected by the other rotations (remember that transformation matrices apply in inverse order, e.g. T*R*G is Rotating the Geometry, and then, Translating it).
Summary
In this case I suggest not to use the line:
looker.rotation.z += 0.05;
Use
looker.rotateZ (0.05);
or
looker.rotateX (0.05);
instead. Hope this helps :)

combine rotation and translation with three.js

i'm using Three.js (without shaders, only with existing objects methods) in order to realize animations, but my question is very simple : i'm sure it's possible, but can you tell me (or help me) how should i combine several animations on a shape ? For example, rotating and translating a sphere.
When i'm doing :
three.sphere.rotation.y += 0.1;
three.sphere.translateZ += 1;
the sphere rotates but the translation vector is also rotating, so the translation has no effect.
I know a bit openGL and i already have used glPushMatrix and glPopMatrix functions, so do them exist in this framework ?
Cheers
Each three.js object3D has a position, rotation and scale; the rotation (always relative to its origin or "center") defines its own local axis coordinates (say, what the object sees as its own "front,up, right" directions) and when you call translateZ, the object is moved according to those local directions (not along the world -or parent- Z axis). If you want the later, do three.sphere.position.z += 1 instead.
The order of transformation is important. You get a different result if you translate first and then rotate than if you rotate first and then translate. Of course with a sphere it will be hard to see the rotation.

First person simulation with three.js using keyboard arrows

For my source, visit http://jsfiddle.net/fYtwf/
Background
I have a simple 3d simulation using three.js where the camera is surrounded in 3 dimensions by cubes. These cubes are to help visualise where the camera is looking until the view controls are coded and tested. I want to create a simple 3D application, where the camera is controlled via up, down, left and right keys. Just like moving your head
Issues
In my current application, when facing forward, and starting to look up, we are successful. However when we turn left 90 degrees, and we press the up arrow... The wrong thing happens. the camera increments the x axis, but because we're facing another direction, modifying the x axis ALONE is WRONG...
Now I'm assuming this is because some trigonometry is required to calculate the correct values for the z axis. However, my trig isn't brilliant.
Current
To get a better understanding of what i mean, please visit my jsfiddle : http://jsfiddle.net/fYtwf/
UP key ONLY increments X
DOWN key ONLY decrements X
LEFT key ONLY increments Y
RIGHT key ONLY decrements Y
Q key ONLY increments Z
W key ONLY decrements Z
( Q and W were only coded to try and help me understand. )
From my current understanding, when I press the UP key, X must increment and the Z axis must be modified based on what the current Y axis is. However I don't know the algorithm :(
So X and Z must be modified in the KEYUP code ( I think, please correct me if I am wrong )
// setRotateX, getRotateX, setRotateY and getRotateY are extended
// camera functions I wrote so I could work with degrees. Solution
// IS NOT required to use them, they just helped me
switch( key )
{
case KEYUP:
if ( camera.getRotateX() < 90 ){ // restrict so they cannot look overhead
camera.setRotateX( camera.getRotateX() + VIEW_INCREMENT );
}
break;
case KEYDOWN:
if ( camera.getRotateX() > -90 ){ // restrict so they cannot look under feet
camera.setRotateX( camera.getRotateX() - VIEW_INCREMENT );
}
break;
case KEYLEFT:
camera.setRotateY( camera.getRotateY() + VIEW_INCREMENT );
break;
case KEYRIGHT:
camera.setRotateY( camera.getRotateY() - VIEW_INCREMENT );
break;
}
There are a number of solutions to this problem, but since you only want the camera to rotate up, down, left, and right, the answer in this case is easy.
You just need to set the camera Euler order to "YXZ" like so:
camera.rotation.order = "YXZ"; // three.js r.65
If you do that, everything becomes very intuitive.
Here is an updated fiddle: http://jsfiddle.net/fYtwf/3/ (this demo is using r.54, however)
Once you change camera.rotation.z from it's default value of zero, things will become very confusing. So don't do that. :-)
three.js r.65
While this does not directly fix your code, I thought I'd mention that Three.js provides two ready-made controllers to navigate in FPS mode. They both use mouse for looking and can move, but should be rather simple to adapt to keyboard look and remove movement if needed. They are:
FirstPersonControls
PointerLockControls
I'd recommend the latter as a starting point because it's rather simple and the former confusingly has the looking code twice, probably as an artifact from old features.

Categories