-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Description
Increasing Access
The default behavior of EasyCam is to always rotate in the direction the pointer moves, unlike orbitControl like p5.js, Three.js, vRoidHub.
2023-06-01.01-45-11.mp4
There are two types of orbitControl().
First, p5.js and Three.js orbitControl() rotates only in the basic vertical direction. It rotates horizontally as well, but it feels like it rotates around a circle made by slicing a sphere horizontally, so when you rotate ±90°, the entire screen rotates around the center. So it doesn't always rotate in the direction the pointer moves.
However, this orbitControl() always keeps the horizontal, so the screen never tilts sideways. Therefore, if the top and bottom are clear, it is useful. When viewing models, it may be difficult to view people or animals from various angles if the screen is tilted. very convenient.
On the other hand, if you have a model in the middle and just want to view it from different angles, the limited rotation can be inconvenient. In such a case, this EasyCam camera may be better.
Since the two orbitControls are used for different purposes, it is not possible to say which one is better. I think the current orbtiControl() should be the default, and would be in trouble if it wasn't, but I thought it would be nice to have other options.
To change it, use _orbitFree(dx, dy), which we'll see later, and rewrite the part that performs the rotation in orbitControl() as follows:
if (this._renderer.rotateVelocity.magSq() > 0.000001) {
this._renderer._curCamera._orbitFree(
-this._renderer.rotateVelocity.x,
this._renderer.rotateVelocity.y
);
// damping
this._renderer.rotateVelocity.mult(damping);
} else {
this._renderer.rotateVelocity.set(0, 0);
}Using this _orbitFree(dx, dy) always rotates the camera in the direction of (dx, dy). You can achieve the behavior like EasyCam.
2023-06-01.02-10-38.mp4
However, it is not directly rewritten. I want the default to be the current behavior. Therefore, we introduce a property called "free" to the option introduced when supporting touch, and set the default to false.
// If option.free is true, it will always rotate freely in the direction
// the pointer moves. default value is false (normal behavior)
const { free = false } = options;And only if this is true will _orbitFree() be used.
if (this._renderer.rotateVelocity.magSq() > 0.000001) {
// if free, it will always rotate freely in the direction the pointer moves
if (free) {
this._renderer._curCamera._orbitFree(
-this._renderer.rotateVelocity.x,
this._renderer.rotateVelocity.y
);
} else {
this._renderer._curCamera._orbit(
this._renderer.rotateVelocity.x,
this._renderer.rotateVelocity.y,
0
);
}
// damping
this._renderer.rotateVelocity.mult(damping);
} else {
this._renderer.rotateVelocity.set(0, 0);
}When we use it, I think we'll call it like this:
orbitControl(1, 1, 1, {free:true});Most appropriate sub-area of p5.js?
- Accessibility
- Color
- Core/Environment/Rendering
- Data
- DOM
- Events
- Image
- IO
- Math
- Typography
- Utilities
- WebGL
- Build Process
- Unit Testing
- Internalization
- Friendly Errors
- Other (specify if possible)
Feature request details
The implementation of _orbitFree(dx, dy) looks like this:
p5.Camera.prototype._orbitFree = function(dx, dy) {
// Calculate the vector and its magnitude from the center to the viewpoint
const diffX = this.eyeX - this.centerX;
const diffY = this.eyeY - this.centerY;
const diffZ = this.eyeZ - this.centerZ;
let camRadius = Math.hypot(diffX, diffY, diffZ);
// front vector. unit vector from center to eye.
const front = new p5.Vector(diffX, diffY, diffZ).normalize();
// up vector. camera's up vector.
const up = new p5.Vector(this.upX, this.upY, this.upZ);
// side vector. Right when viewed from the front. (like x-axis)
const side = new p5.Vector.cross(up, front).normalize();
// down vector. Bottom when viewed from the front. (like y-axis)
const down = new p5.Vector.cross(front, side);
// side vector and down vector are no longer used as-is.
// Create a vector representing the direction of rotation
// in the form cos(direction)*side + sin(direction)*down.
// Make the current side vector into this.
const directionAngle = Math.atan2(dy, dx);
down.mult(Math.sin(directionAngle));
side.mult(Math.cos(directionAngle)).add(down);
// The amount of rotation is the size of the vector (dx, dy).
const rotAngle = Math.sqrt(dx*dx + dy*dy);
// The vector that is orthogonal to both the front vector and
// the rotation direction vector is the rotation axis vector.
const axis = new p5.Vector.cross(front, side);
// If the axis vector is likened to the z-axis, the front vector is
// the x-axis and the side vector is the y-axis. Rotate the up and front
// vectors respectively by thinking of them as rotations around the z-axis.
// Calculate the components by taking the dot product and
// calculate a rotation based on that.
const c = Math.cos(rotAngle);
const s = Math.sin(rotAngle);
const dotFront = up.dot(front);
const dotSide = up.dot(side);
const ux = dotFront * c + dotSide * s;
const uy = -dotFront * s + dotSide * c;
const uz = up.dot(axis);
up.x = ux * front.x + uy * side.x + uz * axis.x;
up.y = ux * front.y + uy * side.y + uz * axis.y;
up.z = ux * front.z + uy * side.z + uz * axis.z;
// We won't be using the side vector and the front vector anymore,
// so let's make the front vector into the vector from the center to the new eye.
side.mult(-s);
front.mult(c).add(side).mult(camRadius);
// it's complete. let's update camera.
this.camera(
front.x + this.centerX,
front.y + this.centerY,
front.z + this.centerZ,
this.centerX, this.centerY, this.centerZ,
up.x, up.y, up.z
);
}First, dx and dy are vectors in the direction of rotation, and we write them as vectors orthogonal to front vector. The magnitude of this is the rotation angle. The vector pointing from the center to the eye and the up vector of the camera are each rotated in this direction.
For calculation, take the axis vector of rotation as a vector orthogonal to the direction of rotation, create a coordinate system, and calculate directly. I'm not familiar with quaternions, so I decided to do it directly with linear algebra (EasyCam uses quaternions, but it's hard to prepare quarternion class, so it's unreal).
I think the amount of calculation is not much different from _orbit().
I will add explanation of free-option to the orbitControl() reference. I'm also going to have a unit test to make sure it comes to the same camera after a full rotation.
Here is the demo.
p5.Editor
OpenProcessing
Default of free-options are true in these codes. It's just for testing, I'll set it to false in the actual implementation.
