R-T matrix from rotation and translation without temporary matrices - javascript

Is there a way to create a matrix from a rotation quaternion and a translation vector without converting both to matrices first?
What I'm doing right now (using my own little math library) is:
var rotation = new quat(...);
var translation = new vec3(...);
var rotationMatrix = new mat4(rotation);
var translationMatrix = new mat4(translation);
var matrix = mat4.product(translationMatrix, rotationMatrix);
Instead, I'd like to do the following:
var rotation = new quat(...);
var translation = new vec3(...);
var matrix = new mat4(rotation, translation);
It seems inefficient and wasteful to allocate two temporary matrices, especially in Javascript, where they have to be allocated on the heap.
Thank you!

The top left 3×3 submatrix is the rotation, the top right 3×1 vector is the translation, the bottom row is (0,0,0,1). (This is assuming that you're multiplying matrix times column vector, not row vector times matrix. Otherwise the situation would be transposed.) So yes, it should be easy to adapt the constructor which creates the matrix from a rotation to also cater for the case where there is an extra translation vector. Or you might create the matrix from the rotation and then change some of its entries to incorporate the translation.
Note that this assumes your notation to mean “rotate then translate”. If it's the other way round, then you'd have to apply the rotation to the translation vector before including that into the matrix.

Related

Three.js: Bounding sphere of a scaled object

I have a set of 3D shapes (pyramid, cube, octahedron, prism etc.) and I need to build described sphere around each of them. It is easy to do so using geometry.boundingSphere as it has radius of the described sphere. But if I scale an object the bounding sphere is not being updated. Is it possible to update bounding sphere relatively to the scale?
Using Three.js 0.129.
const { position } = entity.object3D;
const mesh = entity.getObject3D('mesh') as THREE.Mesh;
mesh.geometry.computeBoundingSphere();
const { radius } = mesh.geometry.boundingSphere;
createSphere(radius, position);
The geometry.boundingSphere property represents the geometry. You could technically have two meshes with different scales share the same geometry, so you would want to maintain the geometry's original bounding sphere, and then compute a new one for each mesh, individually.
One problem with scaling the bounding sphere is that you can scale your mesh in x, y, and z separately, and even invert vertex position values given negative scaling values. unequal scale values would lead to it being less of a sphere, and more of a spheroid, which would not help you in math.
What you can do is recompute a bounding sphere for your mesh, given its updated world transformation matrix. I suggest using the world matrix because other ancestors of your mesh could also influence scale in unpredictable ways.
// Given:
// (THREE.Mesh) yourMesh
// Copy the geometry
let geoClone = yourMesh.geometry.clone() // really bad for memory, but it's simple/easy
// Apply the world transformation to the copy (updates vertex positions)
geoClone.applyMatrix4( yourMesh.matrixWorld )
// Convert the vertices into Vector3s (also bad for memeory)
let vertices = []
let pos = geoClone.attributes.position.array
for( let i = 0, l = pos.length; i < l; i += 3 ){
vertices.push( new THREE.Vector3( pos[i], pos[i+1], pos[i+2] ) )
}
// Create and set your mesh's bounding sphere
yourMesh.userData.boundingSphereWorld = new THREE.Sphere()
yourMesh.userData.boundingSphereWorld.setFromPoints( vertices )
This will create a world-aligned bounding sphere for your mesh. If you want one based on local transformations, you can follow the same idea using the local yourMesh.matrix matrix instead. Just know that your sphere's center will then be based on your mesh's local transformation/rotation, not just its scale.

How to center a mesh in three.js on X & Y only

I'm currently centering objects/models (mesh) in my scene by calling mesh.geometry.center()
This helps me place the object in the center based on its bounding box.
This works perfectly for static objects that don't ever change size (for example lets say I want to render a cardboard box and I'll never change its proportions).
However, I also have some complex 3D models (for example - a bowling ball, a couple of jagged rocks and the Eiffel Tower).
At the moment, each of these models will be placed centrally based on its bounding box. This means that the jaggedy points of the rocks, the curved base of the bowling ball, or the first section of the base of the Eiffel Tower will jut through / clip through the base of the cardboard box (or, conversely, if not scaled appropriately, will just float in mid-air)
What I need is a way find the center of each mesh on only the X and Y axis to allow me to place these models flat on an XY plane without having to manually adjust the Z value. Is this possible?
A general way to do this for objects or trees of objects...
Here I get the bounds of the object.. then scale it so it ranges to 1 on the largest axis... and centers it on x,z axis, and seated on the y.
var cent = new THREE.Vector3();
var size = new THREE.Vector3();
var bbox = new THREE.Box3().setFromObject(yourObject);
bbox.getCenter(cent);
bbox.getSize(size);
//Rescale the object to normalized space
var maxAxis = Math.max(size.x, size.y, size.z);
yourObject.scale.multiplyScalar(1.0 / maxAxis);
//Now get the updated/scaled bounding box again..
bbox.setFromObject(yourObject);
bbox.getCenter(cent);
bbox.getSize(size);
yourObject.position.x = -cent.x;
yourObject.position.y = 0;
yourObject.position.z = -cent.z;

Three.js: Find the subset of faces visible to camera at a given time

I'm working on a three.js scene in which it would be hugely beneficial to be able to determine the subset of all faces (among all geometries) that are visible to the camera at a given time.
I understand one can determine whether a vertex is visible to the camera by doing something like:
camera.updateMatrix();
camera.updateMatrixWorld();
var frustum = new THREE.Frustum();
frustum.setFromMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
// The 3d point to check
var pos = new THREE.Vector3(x, y, z);
if (frustum.containsPoint(pos)) {
// Do something crazy...
}
My geometry has tens of thousands of 2d plane faces, all sitting on a larger plane, and I'd like to determine the set of of their faces that are visible to the camera fairly often (each time the camera zooms past a certain hyperplane, if possible).
I know one can do scene.children[childIndex].visible to see if a mesh is visible, but I have many faces on a mesh and want to determine which of the faces are visible. (All of my meshes are always rendered unless the user zooms wildly). I also know one can adapt this approach:
var frustum = new THREE.Frustum();
var cameraViewProjectionMatrix = new THREE.Matrix4();
// every time the camera or objects change position (or every frame)
camera.updateMatrixWorld(); // make sure the camera matrix is updated
camera.matrixWorldInverse.getInverse( camera.matrixWorld );
cameraViewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
frustum.setFromMatrix( cameraViewProjectionMatrix );
console.log( frustum.intersectsBox( meshes[0].geometry.vertices[0] ) );
Is there a shortcut one can take to find the set of all faces visible to the camera at a given time? In my case, I could precompute the geometric mean of each planar face then use the code above to determine which of the faces is visible, but is there anything better than O(n) in this case?
I'd be very grateful for any ideas others can offer on this question!
Better than O(n)?
Given a sorted array of integers I believe one can determine the subset of those integers that line between an upper and lower bound in ~O(log(n)). Each face's geometric mean is a 3d point, so it seems possible to determine the set of points within the frustum with 3*O(log(n)), i.e. better than O(n) complexity.
Better than O(log(n))
An approximation better than O(log(n)) after some precomputing. Suppose we're dealing with only 1D (then we can generalize to 3D). Quantize the space of each axis then create a hash table with the following structure. For each unit in the quantized space, store the index position of the first point in the sorted array with that unit +- error value. Then given an upper and lower bound, round each to the nearest quantized unit and look up the values for those keys to identify the range of index positions within the span. This returns a list. Repeat for the other two dimensions and take the set intersection. [The frustum provides the upper and lower bounds for each dimension.]

measuring angles on HTML 5 canvas element?

I am confused when implementing (measure) angles in an HTML5 canvas especially after rotating objects
Let's assume I have drawn a shape like this
ctx.moveTo(100,100)//center of canvas
ctx.lineTo(0,200)//left bottom
ctx.lineTo(200,200)//right bottom
We know it is a 45 degrees or pi*2/4 angle.
But how do I figure it out using Math functions to see if the shape was rotated or not?
And how to re-measure it after changing the x and y of the shape(first point) ?
First things first, you will need to make sure the points are stored in some sort of data structure, as it won't be easy to pull the points from the canvas itself. Something like an array of arrays:
var angle = [[100,100], [0,200], [200,200]];
Now, you need to convert your lines to vectors:
var AB = [angle[1][0]-angle[0][0], angle[1][1]-angle[0][1]];
var BC = [angle[2][0]-angle[1][0], angle[2][1]-angle[1][1]];
Now find the dot-product of the two:
var dot_product = (AB[0]*BC[0]) + (AB[1]*BC[1]);//not dot-product
Now you need to find the length (magnitude) of the vectors:
var ABmagnitude = Math.sqrt(Math.pow(AB[0],2) + Math.pow(AB[1],2));
var BCmagnitude = Math.sqrt(Math.pow(BC[0],2) + Math.pow(BC[1],2));
Now you put it all together by dividing the dot product by the product of the two magnitudes and getting the arcosine:
var theta = Math.acos(dot_product/(ABmagnitude*BCmagnitude));
You mentioned rotation, but unless you are only rotating one line, the angle will stay the same.

How to get Rotation around world axis Three js

I would like to get the rotation values form an Object3D according to world axises such as
roation.x, rotation.y, rotation.z
currently when I call
object.rotation.x
I get the rotation value according to the objects local axises.
Thanks.
Sorry for the thread necro.
I had the same problematic, and couldn't find a solution until I read some ThreeJS doc about Euler angles. It became clear all of a sudden.
Three.js uses intrinsic (Tait-Bryan) ordering, also known as yaw, pitch and roll. This means that rotations are performed with respect to the local coordinate system. That is, for order 'XYZ', the rotation is first around world-X, then around local-Y (which may now be different from the world Y-axis), then local-Z (which may be different from the world Z-axis).
Some implementations may use extrinsic (proper) ordering, in which case rotations are performed with respect to the world coordinate system, so that for order 'XYZ', the rotations are around world-X, world-Y, and world-Z.
Converting between the two types is relatively straightforward, you just need to reverse the order and the rotation, so that an intrinsic (three.js) Euler rotation of angles a, b, c about XYZ will be equivalent to to an extrinsic Euler rotation of angles c, b, a about ZYX.
If you need to get the world rotations in XYZ order I suppose you can do the following :
var worldRot = new THREE.Euler();
obj.getWorldRotation().copy(worldRot);
worldRot.reorder("ZYX");
// use worldRot.x, worldRot.y, worldRot.z
To rotate an object in world axis, you can use below function
var rtWorldMatrix;
function rotateAroundWorldAxis(object, axis, radians)
{
rtWorldMatrix= new THREE.Matrix4();
rtWorldMatrix.makeRotationAxis(axis.normalize(), radians);
rtWorldMatrix.multiplySelf(object.matrix);
object.matrix = rtWorldMatrix;
object.rotation.getRotationFromMatrix(object.matrix, object.scale);
}
And call
rotateAroundWorldAxis(objectToRotate, new THREE.Vector3(1,0,0), 90 * Math.PI/180);

Categories