It’s a first-person experiment. The camera has a pivot as a parent, that controls the player’s movement and handles rotation around the current up direction with cameraPivot.rotateOnWorldAxis()
. The camera itself rotates up and down using camera.rotateX()
.
The game world is a large icosahedron. The player is able to move from face to face on the icosahedron, and gravity is intended to update to match the closest face’s normal direction. For now I just update the current normal direction with a button.
When the camera skewed correctly, everything seems to work fine. I update the current up direction, and both rotation around that axis, and movement perpendicular to it, seems to be correct.
The problem is that the camera is rarely skewed correctly. Every time I move onto a new face, the camera will be off-center and I have to manually use camera.rotateZ()
until it looks correct, or another jank solution I found in my demo (move to the center and use camera.lookAt()
the center position).
Rotations are still complex to me. Is there a simple solution I’m missing, or was my approach whack from the start?
Any help appreciated!
Demo here: icosahedron gravity by dilsency
EDIT: Thanks to some help on Reddit cameraPivot.quaternion.setFromUnitVectors(scene.up, face.normal)
correctly aligns the camera with the plane, though it first jarringly rotates the camera. Same helpful user suggested that I added a second pivot as a parent to the first one, so I’ll try that in conjunction next.
EDIT: Solved! camera.up = face.normal
followed by camera.lookAt()
a point directly in front of the camera, lets you skew the camera to align with any plane as a floor. By lerping camera.up
towards face.normal
, the transition can be made smoother.
I have created a lot of custom camera rotators in three.js.
However, have you tried Orbit Controls? If you are just orbiting around an object located at 0,0,0, that may be all you need.
If that doesn’t work for you, I have done what it sounds like you are doing, with the rotator placed at a specific location or attached to an object of interest. I then link the camera to that rotator, setting z distance to the desired radius and rotating the camera on the y-axis by 180 degrees (so it will look inwards towards the rotator). I analogize this to using a “selfie-stick”. (I am not at my PC, but I don’t recall having to create an intermediate mesh to hold the camera - but that may be needed.). I believe I also specify a YXZ rotation order for the rotator.
I can then move the camera to various locations by changing the x-rotation of the rotator (latitude) or the y-rotation (longitude). It sounds like you might want to create some kind of look-up table to position your camera - specifying the correct xyz values for your rotator.
I have never used cameraPivot.rateOnWorldAxis(). So that may be something I could use to improve my approach or it could be what is causing you problems. As I recall, the LookAt function prevents you from performing a Z-rotation and causes your camera to remain level with the horizon.
You can get a long way with these kinds of things by using camera.lookAt ,
and setting your own camera.up vector.
https://threejs.org/docs/#api/en/core/Object3D.up
For instance, setting camera.up to the normal of the face you want to be on, or, the normal from the center of the icosahedron to the camera.position, will make .lookAt behave more consistently.
3 Likes
Assuming that the icosahedron is at 0,0,0 and the camera is pointed towards 0,0,0, can you read the normal values of the face of the icosahedron that you are looking at? That would avoid the need for a look-up table.
I was absolutely sure that I had tried this, but evidently not, this solved it. Thank you!
camera.up = face.normal
followed by camera.lookAt()
with a point directly in front of the camera. But I also lerped camera.up
towards face.normal
to make the transition smoother.
2 Likes