How calculate rotational increment in THREE.js - three.js

For drag function, I'm making objectB follow objectA like:
var positionDeltas = objectA.position.clone();
postitionDeltas.sub(objectAOldPosition);
var newPositon = objectB.position.clone();
objectB.position = newPosition.add(positionDeltas);
Can angle increments be done similarly? Can I subtract/add quaternions in a similar way?:
var rotationDeltas = objectA.quaternion.clone();
rotationDeltas.sub(objectAOldRotation);
var newRotation = objectB.quaternion.clone();
objectB.quaternion = newRotation.add(rotationDeltas);
Clarification: this is what I want to do; however, I do know that eulers can't be subtracted, and subtraction isn't defined for quaternions. So....how would one who knows what they're doing approach this three.js?

Try this:
const q = new THREE.Quaternion();
q.multiply( q2, q1.inverse() );
Quaternion q represents the rotation from quaternion q1 to q2. You can then apply this rotation to your 3D object via .applyQuaternion().
three.js R91

Related

ThreeJS : How to get the angle between2 object

I have an Avatar at a given Position (v0) and Rotation (r0)
I have an Object at a givent Position (v1)
I looking for the angle to rotate the avatar toward v1. I need the angle I don't want to use lookAt() function
// Get the Avatar Position
let v0 = new THREE.Vector3();
avatar.getWorldPosition(v0)
// Get the Object Position
let v1 = new THREE.Vector3();
obj.getWorldPosition(v0)
// Get the direction v0 to v1
let dir0 = new THREE.Vector3();
dir0.subVectors( v0, v1 ).normalize();
// Get the direction of avatar (where it look at)
let dir2 = new THREE.Vector3();
avatar.getWorldDirection(dir2)
// Get the angle between the 2 direction
let radians = dir0.angleTo(dir2)
It doesn't work !
The this.mesh.lookAt(v1.setY(0)) works and rotate correctly the mesh
But the angle computation didn't work because of avatar.getWorldDirection
BTW, since everything is on the same plane I don't need 3D (only 2D)
BTW, The avatar (Mixamo) seems to face backward
I need that angle to trigger some animation (if angle > 90 then trigger 90, if angle > 180 then turnback animation...)
Your code has a few mistakes. obj.getWorldPosition(v0) is going to overwrite the v0 values retrieved with avatar.getWorldPosition(v0), so when you subtract v0 - v1 you'll get a vector with a magnitude of 0.
Looking at the documentation of Vector3.angleTo(), it says all you need to do is input the positions, without any subtraction necessary:
let posAvatar = new THREE.Vector3();
avatar.getWorldPosition(posAvatar);
let posObj = new THREE.Vector3();
obj.getWorldPosition(posObj);
const angleRadians = posAvatar.angleTo(posObj);
// convert from radians to degrees
const angleDeg = THREE.MathUtils.radToDeg(angleRadians);
Keep in mind, both objects will need to be on the same plane for this 2D angle to be accurate.
Update:
This approach uses the Javascript Math.atan2() method to calculate the absolute y-axis rotation from your avatar's vantage point towards the obj. This also only uses the x,z positions so any height variations are ignored.
let posAvatar = new THREE.Vector3();
avatar.getWorldPosition(posAvatar);
let posObj = new THREE.Vector3();
obj.getWorldPosition(posObj);
const xDist = posObj.x - posAvatar.x;
const zDist = posObj.z - posAvatar.z;
const angle = Math.atan2(zDist, xDist) * 180 / Math.PI;
avatar.rotation.y = angle;
Your avatar might be facing a different axis, so you can just add 90 or 180 degrees to the final angle value.

Is internally .matrixWorld is used?

I have two functions which difference is using clone of originObj.matrixWorld to multiply with 'transform' argument.
the first one does not work but second one does.
as far as I know the 'matrix' and 'matrixWorld' properties of object3d are newly calculated in every frame, more specifically in renderer.render().
So I thought that assigning arbitrary matrix to .matrix or .matrixWorld is worthless, cause it will be overwrited in rederer.render().
If my thought is right, following two functions are working well either.
But only second one does work.
What is misunderstanding I am?
applyTransform(originObj, target, transform) {
const newTransform = originObj.matrixWorld.multiply(transform);
// decompose newTransformMatrix as position, rotation and scale
const position = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
const scale = new THREE.Vector3();
newTransform.decompose(position, quaternion, scale);
target.position.copy(position);
target.quaternion.copy(quaternion);
target.scale.copy(scale);
}
applyTransform(originObj, target, transform) {
const newTransform = originObj.matrixWorld.clone().multiply(transform);
// decompose newTransformMatrix as position, rotation and scale
const position = new THREE.Vector3();
const quaternion = new THREE.Quaternion();
const scale = new THREE.Vector3();
newTransform.decompose(position, quaternion, scale);
target.position.copy(position);
target.quaternion.copy(quaternion);
target.scale.copy(scale);
}
I'm using threejs r90
There is a build-in function to apply a matrix to an object. The Object3D.applyMatrix method does exactly the same what your second function is doing.
Applies the matrix transform to the object and updates the object's position, rotation and scale.
applyMatrix: function ( matrix ) {
this.matrix.multiplyMatrices( matrix, this.matrix );
this.matrix.decompose( this.position, this.quaternion, this.scale );
}
So, just call:
target.applyMatrix(originObj.matrixWorld);
EDIT: Oh, I forgot the transform;
var matrix = new THREE.Matrix4().multiplyMatrices(originObj.matrixWorld, transform);
target.applyMatrix(matrix);

THREE.js - Quaternion to lat, lon

I am trying to convert vrDisplays rotation (comes in quaternions) to latitude and longitude, since an API I use needs the current rotation of the head mounted display in lat and lon values.
lordofducts answer in unity3D forums (https://forum.unity3d.com/threads/quaternions-and-euler-angles-to-longitude-latitude.154392/ )
gave me good hope that this is actually possible, but I am kind of stuck, as THREE.Quaternion doesn't seem to have ToAxisAngle() method and I am not that good with 3D geometry.
so does anybody have an idea how I can achieve this so I can do sth like that?
var quat = THREE.Quaternion();
quat.fromArray(vrDisplay.getPose().orientation);
var lat = //do sth with quat
var lon = //do seth with quat
api.rotateCamera({
lat: lat,
lon: lon
});
// I noticed that in my code I already have a conversion from Euler angles to lat lon. So I might be just converting the Quaternion to Euler angles and then convert it to lat lon values. Still, there has to be a more sophisticated method...
var look = new THREE.Vector3();
quatToSpherical = function(quat){
look.set(0,0,1);
look.applyQuaternion(quat);
look.normalize();
if(Math.abs(look.x) < 0.001) look.x = 0.001;
var angles = {};
var radius = 1;
angles.lat = Math.acos(look.z/r);
angles.lon = Math.atan2(look.y , look.x);
return angles;
}
The solution applies only to a standard cartesian coordinate system.
a right handed coordinate system with XY ground plane like I used in my setup needs offsets:
angles.lat = Math.acos(look.z/r) - Math.PI/2;
angles.lon = Math.atan2(look.y , look.x) - Math.PI;
https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates

three.js - Set the rotation of an object in relation to its own axes

I'm trying to make a static 3D prism out of point clouds with specific numbers of particles in each. I've got the the corner coordinates of each side of the prism based on the angle of turn, and tried spawning the particles in the area bound by these coordinates. Instead, the resulting point clouds have kept only the bottom left coordinate.
Screenshot: http://i.stack.imgur.com/uQ7Q8.png
I've tried to set the rotation of each cloud object such that their edges meet, but they will rotate only around the world centre. I gather this is something to do with rotation matrices and Euler angles, but, having been trying to work them out for 3 solid days, I've despaired. (I'm a sociologist, not a dev, and haven't touched graphics before this project.)
Please help? How do I set the rotation on each face of the prism? Or maybe there is a more sensible way to get the particles to spawn in the correct area in the first place?
The code:
// draw the particles
var n = 0;
do {
var geom = new THREE.Geometry();
var material = new THREE.PointCloudMaterial({size: 1, vertexColors: true, color: 0xffffff});
for (i = 0; i < group[n]; i++) {
if (geom.vertices.length < group[n]){
var particle = new THREE.Vector3(
Math.random() * screens[n].bottomrightback.x + screens[n].bottomleftfront.x,
Math.random() * screens[n].toprightback.y + screens[n].bottomleftfront.y,
Math.random() * screens[n].bottomrightfront.z + screens[n].bottomleftfront.z);
geom.vertices.push(particle);
geom.colors.push(new THREE.Color(Math.random() * 0x00ffff));
}
}
var system = new THREE.PointCloud(geom, material);
scene.add(system);
**// something something matrix Euler something?**
n++
}
while (n < numGroups);
I've tried to set the rotation of each cloud object such that their
edges meet, but they will rotate only around the world centre.
It is true they only rotate around 0,0,0. The simple solution then is to move the object to the center, rotate it, and then move it back to its original position.
For example (Code not tested so might take a bit of tweaking):
var m = new THREE.Matrix4();
var movetocenter = new THREE.Matrix4();
movetocenter.makeTranslation(-x, -y, -z);
var rotate = new THREE.Matrix4();
rotate.makeRotationFromEuler(); //Build your rotation here
var moveback = new THREE.Matrix4();
moveback .makeTranslation(x, y, z);
m.multiply(movetocenter);
m.multiply(rotate);
m.multiply(moveback);
//Now you can use geometry.applyMatrix(m)

Converting Sketchup Transformation to Three.js rotation+scale+position or Matrix

I'm writing an export script (ruby) in SketchUp, and I'm having trouble applying the same transformation in Three.js side, so that objects have the same rotation in Three.js as they appear in SketchUp.
I can read the rotation using the SketchUp Transformation class: http://www.sketchup.com/intl/en/developer/docs/ourdoc/transformation.php
I can get these kind of values from a rotated component that I pass to my Three.js code. All are Vectors in the form of X, Y, Z
xaxis: 0.0157771536190692,-0.0,-0.0199058138160762
yaxis: -0.0199058138160762,0.0,-0.0157771536190692
zaxis: 0.0,0.0254,-0.0
origin: 1.4975125146729,0.0,-1.25735397455338
Objects are positioned correctly if I just copy the values from origin to Object3D.position. But I have no idea how to apply the xaxis, yaxis and zaxis values to Object3D.rotation.
Three.js has various ways to rotate a model, via Matrix manipulation, quaternion, angles, radians and whatnot. But how to set object rotation using those axis values?
EDIT:
SketchUp Transformation provides also a .to_a (to array) method, which I think is supposed to return a 16 element matrix. I tried to use that in Three.js:
// tm is from SketchUp:Transformation to_a
var tm = "0.621147780278315,0.783693457325836,-0.0,0.0,-0.783693457325836,0.621147780278315,0.0,0.0,0.0,0.0,1.0,0.0,58.9571856170433,49.5021249824165,0.0,1.0";
tm = tm.split(",");
for (var i = 0; i < tm.length; i++) {
tm[i] = tm[i] * 1.0;
}
var matrix = new THREE.Matrix4(tm[0], tm[1], tm[2], tm[3], tm[4], tm[5], tm[6], tm[7], tm[8], tm[9], tm[10], tm[11], tm[12], tm[13], tm[14], tm[15]);
obj.applyMatrix(matrix);
This results in a total mess however, so there's still something wrong.
Based on information here: http://sketchucation.com/forums/viewtopic.php?f=180&t=46944&p=419606&hilit=matrix#p419606
I was able to construct a working Matrix4. I think the problem was both in unit scales (see the .to_m conversion in some of the elements) and the order of matrix array elements. In Sketchup:
tr = transformation.to_a
trc = [tr[0],tr[8],-(tr[4]),tr[12].to_m, tr[2],tr[10],-(tr[6]),tr[14].to_m, -(tr[1]),-(tr[9]),tr[5],-(tr[13].to_m), 0.0, 0.0, 0.0, 1.0] # the last 4 values are unused in Sketchup
el.attributes["tm"] = trc.join(",") # rotation and scale matrix
el.attributes["to"] = convertscale(transformation.origin) # position
In Three.js
var origin = this.parsevector3(node.getAttribute("to"));
obj.position = origin;
var tm = node.getAttribute("tm");
tm = tm.split(",");
for (var i = 0; i < tm.length; i++) {
tm[i] = tm[i] * 1.0;
}
var matrix = new THREE.Matrix4(tm[0], tm[1], tm[2], tm[3], tm[4], tm[5], tm[6], tm[7], tm[8], tm[9], tm[10], tm[11], tm[12], tm[13], tm[14], tm[15]);
obj.applyMatrix(matrix);
Sorry there is some application specific logic in the code, but I think the idea can be found regardless, if someone runs into similar problems.
SketchUp Transformation provides also a .to_a (to array) method, which I think is supposed to return a 16 element matrix.
It has been a while since you posted this, but here's a useful link for people who bump into this in the future: http://www.martinrinehart.com/models/tutorial/tutorial_t.html

Resources