Issue aligning faces in Three.JS from two seperate objects - javascript

I'm having an issue in my three.js code.
I am trying to align two objects by selecting two faces and have the second face (and object) rotate to match the normal vector of the first object's selected face.
So far I have:
var normalMatrix = new THREE.Matrix3().getNormalMatrix( object.matrixWorld );
var worldNormal = face.normal.clone().applyMatrix3( normalMatrix ).normalize();
var normalMatrixRef = new THREE.Matrix3().getNormalMatrix( objectRef.matrixWorld );
var worldNormalRef = faceRef.normal.clone().applyMatrix3( normalMatrixRef).normalize();
where: object is my object that will be rotated, face is the selected face of that object, objectRef is the stationary object reference and faceRef is the normal of the object I want to match.
Prior to doing
object.lookAt(worldNormalRef);
, I am trying either
object.up = new THREE.Vector3(0,0,1);
object.up = worldNormal;
object.up = face.normal;
and neither work.
Either object.up cases are not working as they should. When I do the action, the target object (the one to be rotated) does rotate, but does not align the faces correctly. It is arbitrary but somehow is linked to objects current rotation (ie: sometimes it rotates along (0,1,0) or simliar, othertimes if I pre-rotate the object it just rotates a slight bit.
In theory lookAt uses a world vector which my faceRef is, and as long as I set the 'up' correctly, it should work but it dosen't.
Any ideas?

I found the issue in my code, I hope it can help somebody in the future.
The
object.lookAt(worldNormalRef);
needs instead to be modified to add the new face normals to the current object's postition for the lookAt to function correctly:
//create a point to lookAt
var newPoint = new THREE.Vector3(
object.position.x + worldNormalRef.x,
object.position.y + worldNormalRef.y,
object.position.z + worldNormalRef.z
);
object.up = face.normal;//Z axis up
object.lookAt(newPoint );
Also note that the object.up should be set as above to the face.normal (the face I want to align to the reference object's face), and not to the worldNormal set above (I will delete this part of my code).

Related

Apply quaternion is not a function

I need some light on this question I'm trying to set vertices of a geometry using a quaternion and
geometry.attributes.position.array[x].applyQuaternion(quaternion)
is not working as expected
I know this implementation as changed in the passed and I suspect that the problem is that
geometry.vertices[x]
is not available anymore
some one on this ?
The method .applyQuaternion() is only available in Vector3(), not in a simple number. You cannot perform 7.applyQuaternion() with a number. In this context, it doesn't make sense to apply a 3D rotation to a 1-dimensional value.
First, make sure you create a Vector3 with the XYZ coordinates of the vertex you're trying to manipulate by using the BufferAttribute.get... method:
// Create quaternion
const quat = new THREE.Quaternion();
quat.setFromAxisAngle( /* ... */ );
// Position attribute
const posAttrib = geometry.getAttribute('position');
// Let's get the XYZ components of the 7th vertex in the geometry
const twistVec = new THREE.Vector3(
posAttrib.getX(7),
posAttrib.getY(7),
posAttrib.getZ(7)
);
// Now that you've created a Vec3, you CAN apply a quaternion
twistVec.applyQuaternion(quat);

Attaching position to camera but rotating in world axis

Quick abstract: I don't know how to give to an object, attached to the camera, a local rotation so that it is rotated as if it is attached to the scene.
Background: I am the creator of this website which offers to players of the related game the ability to build levels on a 3D Builder, in a web browser. Everything work fine with it but some parts are not well optimized, specially the toolbar (or dashboard), where objects can be selected to be added to the main scene.
Those toolbar objects need to be sticked at the bottom of the screen and always above all layers but their rotation has to be relative to the main axis of the scene. That's where I face some weird behaviors on browsers other than Firefox and Chrome.
If first thought this other thread was saving my life on this matter by implementing the Object3D.onBeforeRender function, but I faced other issues.
Currently those objects are attached to the scene and their position is updated relatively to the camera with the onBeforeRender function of each object. On such way I noticed some flickering results with Edge and iPhone browser, despite everything ends up fine in the rendering. Not nice on the scene rotation...
So I tried something else by attaching each object to the camera and giving them their supposed world rotation. Now when testing on Edge there is no flickering and the results is much better but I can't get how to give them the same rotation than when they are attached to the scene.
Here is a visual:
and an animated gif (you can see a little how the red background and cube at the right part are moving/flickering, but it is awfull if done quicklier):
For the #1 (at the right) there's a good rendering but a bad experience on some browsers and for the #2 (at the left) the rotation, based on the camera, is not good but the experience is fine.
Here are some code abstracts I've implemented:
For #1:
rightCube.relPos = new THREE.Vector3(500, 0, 0); // a new property to be used
rightCube.onBeforeRender = function( renderer, scene, camera, geometry, material, group ) {
camReference.position.copy(this.relPos);
var pos = camReference.getWorldPosition();
this.position.set( pos.x, pos.y, pos.z );
};
scene.add(rightCube);
And camReference is a simple Object3D representing the camera origin
For the #2:
leftCube.onBeforeRender = function( renderer, scene, camera, geometry, material, group ) {
this.rotation.copy(camera.getWorldRotation());
};
camera.add(leftCube);
I admit on this last part, that getting the camera world rotation is not sufficient but that's where I'm stuck and need some help: I don't know how to give to an object, attached to the camera, a local rotation so that it is rotated as if it is attached to the scene.
This post is pretty long but I hope my problem can be well understood... and that someone will give me the key to make it work.
Three.js r82
I've found out how to deal with that problem. I had to go into some calculus and I am surprised that it is not yet implemented in Three.js. If I have missed something #WestLangley please let me know!
Basically my request is to have a rotateToWorld functionnality from the camera coordinate. Getting the rotation of the camera is not enought because of the orbit control position offset which could be different than the Scene Axis. From the camera coordinate, rotating to the World can be seen as a rotation around x then y (Euler rotations), z being directed to the viewer. Here is a figure I tried to draw as a better explanation:
Here is the function I have coded to do that:
var rotateToWorld = function (camera, controls) {
var p = camera.position.clone();
var o = controls.target.clone();
// position relative to the control
p.sub(o);
var Dxyz = Math.sqrt(p.x*p.x + p.y*p.y + p.z*p.z);
var Dxz = Math.sqrt(p.x*p.x + p.z*p.z);
return {
x: Math.acos(Dxz/Dxyz) * (p.y>0?1:-1), // Angle Rotation on x axis camera
y: Math.acos(p.z/Dxz) * (p.x>0?-1:1), // Angle Rotation on y axis camera
z: 0 // No rotation around z axis
};
};
Then from a cube object attached to the camera (not to the scene) it can be rendered like that:
cube.onBeforeRender = function( renderer, scene, camera, geometry, material, group ) {
var R = rotateToWorld(camera, controls); // controls defined elsewhere in the code
this.rotation.set(R.x, R.y, 0);
};
As a result this is what I got with a perspective camera:
And now it works with Microsoft Edge, Safari and other browsers despite when attaching to the scene and updating the position of the object (to keep it in front of the camera) it was only workable with Firefox and Chrome (as for today).
I wonder now if this is a functionnality that could be added to Three.js...
EDIT:
#WestLangley pointed out in comments that the clone() function of Vector3 instantiate a new object each time it is called, so better is to use a closure function to initialize once p and o as Vector3 objects then update them with the copy() function.
So here is a better version:
var rotateToWorld = function() {
var p = new THREE.Vector3();
var o = new THREE.Vector3();
return function rotateToWorld(camera, controls) {
p.copy(camera.position);
o.copy(controls.target);
// position relative to the control
p.sub(o);
var Dxyz = Math.sqrt(p.x*p.x + p.y*p.y + p.z*p.z);
var Dxz = Math.sqrt(p.x*p.x + p.z*p.z);
return {
x: Math.acos(Dxz/Dxyz) * (p.y>0?1:-1), // Angle Rotation on X axis camera
y: Math.acos(p.z/Dxz) * (p.x>0?-1:1), // Angle Rotation on Y axis camera
z: 0 // No rotation around z axis
};
}
}();
Again, thanks #WestLangley! :)

Add Points to a Point Cloud with User Mouse Clicks

I'm using Three.js to render point cloud data retrieved from a server.
For each data set, I loop over the data points and create a Three.js Vector3 object with x, y & z values corresponding to each data point. I push each of these vertices onto a list which I then pass into the vertices prop of my geometry component within my points component.
render() {
this.pointCloudVertices = [];
if (this.props.points) {
const points = this.props.points
for (let i = 0; i < points.x.length; i++) {
const vertex = new THREE.Vector3();
vertex.x = points.x[i]
vertex.y = points.y[i]
vertex.z = points.z[i]
this.pointCloudVertices.push(vertex);
}
}
return (<points>
<geometry vertices={this.pointCloudVertices}/>
<pointsMaterial
color={ (Math.floor(Math.random()*16777215)) }
size={ 0.2 }
/>
</points>);
}
https://github.com/caseysiebel/pc-client/blob/master/src/components/PointCloud.js
I'd like the user to be able to use their mouse to add points to another point cloud (points component) by clicking inside the canvas.
I found a lot of resources pointing to the Three.js' Raycaster, but this tool seems to be more for selecting out objects already in the canvas. In my case I'd like the user to be able to click on and area in the canvas not occupied by an object, have the client work out the x, y & z coordinates of that click and then add a vertex, with those x/y/z values, to a points component (likely empty until the user adds points via this modality).
I'm a little confused as to how I will convert 2D mouse events into a 3D vertex value. If anyone knows any good resources on this subject I'd love to check them out.
With THREE.Raycaster(), I see several solutions:
1. Use the .at() method of the .ray property. Like this:
raycaster.ray.at(100 + Math.random() * 150, rndPoint);
Here you can set the constraints for the distance from the origin of the ray, and it will look like this from your original camera:
and how it will look like from aside:
jsfiddle example. You can switch the lines off there.
2. Use the .intersectObjects() method. Where intersecting objects are planes of constraints. For example, we have planes in the form of a cube. When we cast a ray through them, we always intersect two planes, and the array of intersectec objects will be sorted by distance from the origin of the ray. Thus the first element in this array will be the closest plane. So, when we know it, we can take their points of intersection and sub point1 from point2, getting a new vector (its length and direction). And then we'll just set a point at a random place along the vector from point1 to point2:
intersected = raycaster.intersectObjects(planes.children);
if (intersected.length > 0){
var point1 = intersected[0].point;
var point2 = intersected[1].point;
var diff = point2.clone().sub(point1);
var diffLength = diff.length();
rndPoint = point1.clone().addScaledVector(diff.normalize(), Math.random() * diffLength);
. . .
}
It will look like this from the front camera:
and from aside:
jsfiddle example. Lines are switchable here too.
Or you can use THREE.Raycaster() with THREE.OrthographicCamera(). Which is simplier )

Three.js - Convert mesh velocity to mesh rotation

Suppose I have a three.js mesh, and a velocity vector3.
The velocity is being altered elsewhere, and then added to the mesh.position every frame.
What I want is for the mesh.rotation to correspond to the velocity, i.e, the mesh is an arrow that always points in the direction it's going in.
Here are two approaches I've tried - which get the mesh rotating yeah, but just at the wrong angles and backwards and everything.
//this doesn't work
mesh.rot.x = Math.atan2( vel.y, vel.z );
mesh.rot.y = -Math.atan2( vel.x, vel.z );
mesh.rot.z = Math.atan2( vel.x, vel.y );
//got this from a semi-related stackoverflow question; also doesn't work
mesh.rot.y = Math.asin( vel.z );
mesh.rot.z = Math.atan2( vel.y, vel.x );
var bankVec = new Vector3( -vel.y, vel.x, 0 );
var upVec = new Vector3( dot( bankVec, vel ) );
mesh.rot.x = Math.atan2(dot(bankVec, vel) / dot(upVec, vel),abs(bankVec)*abs(upVec));
I don't really get vector maths.
I'm now at the stage where I'm just arbitrarily changing signs and swapping arguments around, hoping for the best. So any kind of explanation as to how I should be doing this would be much appreciated.
Not really looking for code, just looking for the relevant equation of dot products, cross products, atan2's, whatever.
You can construct a rotation matrix from the normalized velocity vector. The vector, normalized, is 1 axis. Then you pick a 2nd axis somehow. Then you do a cross product to find the 3rd axis. Finally do a cross product between the first and 3rd to get the final 2nd axis.
Then you construct a 3x3 matrix from the 3 vectors (i can't remember their order at the moment, but something like [x1,y1,z1,x2,y2,z2,x3,y3,z3]).
Another method is the Quaternion class:
.setFromUnitVectors ( vFrom, vTo )
Sets this quaternion to the rotation required to rotate direction
vector vFrom to direction vector vTo. Adapted from
http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors.
vFrom and vTo are assumed to be normalized.
http://threejs.org/docs/#Reference/Math/Quaternion
Since internally three.js likes to use quaternions, this may be a good method. If your arrow is an upward pointing arrow, you probably want vFrom=[0,1,0], vTo=velocityVector.normalize();

Rotate object around the world axis

I found several different supposedly working ways to rotate an object around the world axis but all of them kept rotating the object around it's own axis instead of rotating it around the world's. So, what's the proper way to rotate an object around the world's axis?
If I wasn't clear about the problem here's a drawing that represents my problem
Edit: Sorry I should have specificated in the question as well that I'm using Three.js
Edit2:
//camera is the object I want to rotate
//I also found a method that uses Euler but the result was far from the expected
/**Method 1**/
//Found this somewhere here in so
var rotateAroundWorldAxis = function(object, axis, radians) {
var rotWorldMatrix;
rotWorldMatrix = new THREE.Matrix4();
rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
rotWorldMatrix.multiply(object.matrix);
object.matrix = rotWorldMatrix;
object.rotation.setFromRotationMatrix(object.matrix);
}
//this is how I call the method
rotateAroundWorldAxis(camera, new THREE.Vector3(0,1,0), movementX*-0.001);
rotateAroundWorldAxis(camera, new THREE.Vector3(1,0,0), movementY*-0.001);
/**Method 1**/
/**Method 2**/
//I've also tried this method
var q = new THREE.Quaternion();
q.setFromAxisAngle( new THREE.Vector3(0,1,0), movementX*-0.001 ); // axis must be normalized, angle in radians
camera.quaternion.multiplyQuaternions( q, camera.quaternion );
/**Method 2**/
//movementX and movementY are values provided by the mouse movement using the pointerlock.
//These values works as I've tried them using rotateOnAxis
Use quaternions.
var rotateAroundWorldAxis = function(object, axis, radians) {
var rotation = new THREE.Quaternion();
rotation.setFromAxisAngle ( axis, radians );
object.quaternion.multiply(rotation);
}
rotateAroundWorldAxis(myObject, THREE.Vector3(0,1,0), movementX*-0.001);
I believe your problem before with the quaternion method is multiplying the quaternions in the wrong order so you rotate around global the Y axis, then perform the local rotation, which ultimately rotates in the local Y axis.
I ended up using the FirstPersonControls class. I had to make a few changes to the class though

Categories

Resources