webgl rotate an object around another object in one axis - javascript

Im trying to rotate an object around another object while maintaining its own rotation. I have each objects rotation done im just not sure how to rotate an object around another object. For example I have an array called Planets[Sun,Mercury]. I want the sun to be stationary and allow mercury to rotate around the sun on one axis.
Currently I have the sun and mercury rotating by themselves this is done by:
First changing degress to radians.
function degToRad(degrees)
{
return degrees * Math.PI / 180;
}
Then in my drawScene() I rotate the matrix:
mat4.rotate(mvMatrix, degToRad(rCube), [0, 1, 0]);
and then lastly when I animate the scene I move the object using:
var lastTime = 0;
function animate() {
var timeNow = new Date().getTime();
if (lastTime != 0)
{
var elapsed = timeNow - lastTime;
rCube -= (75 * elapsed) / 1000.0;
}
lastTime = timeNow;
}
Is there anyway I can pass an origin point into
mat4.rotate(mvMatrix, degToRad(rCube), [0, 1, 0]);
to make it like:
mat4.rotate(mvMatrix, ObjectToRotateAround, degToRad(rCube), [0, 1, 0]);
I feel as if im not explaining the code I have well. If you wish to have a look it can be found here:
https://copy.com/iIXsTtziJaJztzbe

I think you need to do a sequence of matrix operations and the order of matrix operation matters.
What you probably want in this case is to first translate Mercury to position of Sun, then do the rotation, then reverse the first translation. I have not yet implemented hierarchical objects myself so I dont want to confuse you. But here is the code for my implementation of orbit camera which the yaw function rotates the camera around a target point and you may find it useful:
yaw: function(radian){
this.q = quat.axisAngle(this.q, this.GLOBALUP, radian);
vec3.rotateByQuat(this.dir, this.dir, this.q);
vec3.cross(this.side,this.GLOBALUP,this.dir);
vec3.normalize(this.side,this.side);
this.pos[0] = this.target[0] - this.dir[0] * this.dist;
this.pos[1] = this.target[1] - this.dir[1] * this.dist;
this.pos[2] = this.target[2] - this.dir[2] * this.dist;
}
Where this.dir is a normalized vector that always gives the direction from Camera to target and this.dist is the distance between camera and target. You can use matrix rotation instead of quaternion rotation.
Edit: just to add the direction can be calculated by taking the difference in position of the two objects then normalize it.

Related

How to calculate 'start' & 'end' angle of an Arc by given 2 points?

I've been trying to draw an arc on the canvas, using p5.js. I got start & end points, the chord length i calculate using pythagoras using the two points, the height & width values are also given.
In order to draw an arc, i need to use the following function;
arc(x, y, w, h, start, stop, [mode], [detail]) for docs refer to here
The start & stop parameters refer to the start&stop angles specified in radians. I can't draw the arc without those angles and i'm unable to calculate them using what i got.
I searched for lots of examples similar to my question, but it is suggested to calculate the center angle, which i'm also unable to do so. Even though i was able to calculate the center angle, how i'm supposed to get the start&stop angles afterwards?
I have drawn some example illustrations on GeoGebra;
The angle of a vector can be calculated by atan2().
Note, that:
tan(alpha) = sin(alpha) / cos(alpha)
If you've a vector (x, y), then than angle (alpha) of the vector relative to the x-axis is:
alpha = atan2(y, x);
The start_angle and stop_angle of an arc, where the center of the arc is (cpt_x, cpt_y), the start point is (spt_x, spt_y) and the end point is (ept_x, ept_y), can be calculated by:
start_angle = atan2(spt_y-cpt_y, spt_x-cpt_x);
stop_angle = atan2(ept_y-cpt_y, ept_x-cpt_x);
See the example, where the stop angle depends on the mouse position:
var sketch = function( p ) {
p.setup = function() {
let sketchCanvas = p.createCanvas(p.windowWidth, p.windowHeight);
sketchCanvas.parent('p5js_canvas')
}
p.windowResized = function() {
p.resizeCanvas(p.windowWidth, p.windowHeight);
}
p.draw = function() {
let cpt = new p5.Vector(p.width/2, p.height/2);
let rad = p.min(p.width/2, p.height/2) * 0.9;
let stop_angle = p.atan2(p.mouseY-cpt.y, p.mouseX-cpt.x);
p.background(192);
p.stroke(255, 64, 64);
p.strokeWeight(3);
p.noFill();
p.arc(cpt.x, cpt.y, rad*2, rad*2, 0, stop_angle);
}
};
var circle = new p5(sketch);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>
<div id="p5js_canvas"></div>

THREE.js Detecting adjacent faces from a raycaster intersection point

I have a Mesh created with a BufferGeometry.
I also have the coordinates of where my mouse intersects the Mesh, using the Raycaster.
I am trying to detect faces within(and touching) a radius from the intersection point.
Once I detect the "tangent" faces, I then want to color the faces. Because I am working with a BufferGeometry, I am manipulating the buffer attributes on my geometry.
Here is my code:
let vertexA;
let vertexB;
let vertexC;
let intersection;
const radius = 3;
const color = new THREE.Color('red');
const positionsAttr = mesh.geometry.attributes.position;
const colorAttr = mesh.geometry.attributes.color;
// on every mouseMove event, do below:
vertexA = new THREE.Vector3();
vertexB = new THREE.Vector3();
vertexC = new THREE.Vector3();
intersection = raycaster.intersectObject(mesh).point;
// function to detect tangent edge
function isEdgeTouched(v1, v2, point, radius) {
const line = new THREE.Line3();
const closestPoint = new THREE.Vector3();
line.set(v1, v2);
line.closestPointToPoint(point, true, closestPoint);
return point.distanceTo(closestPoint) < radius;
}
// function to color a face
function colorFace(faceIndex) {
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.needsUpdate = true;
}
// iterate over each face, color it if tangent
for (let i=0; i < (positionsAttr.count) /3); i++) {
vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
if (isEdgeTouched(vertexA, vertexB, point, radius)
|| isEdgeTouched(vertexA, vertexB, point, radius)
|| isEdgeTouched(vertexA, vertexB, point, radius)) {
colorFace(i);
}
While this code works, it seems to be very poor in performance especially when I am working with a geometry with many many faces. When I checked the performance monitor on Chrome DevTools, I notices that both the isEdgeTouched and colorFace functions take up too much time on each iteration for a face.
Is there a way to improve this algorithm, or is there a better algorithm to use to detect adjacent faces?
Edit
I got some help from the THREE.js slack channel, and modified the algorithm to use Three's Sphere. I am now no longer doing "edge" detection, but instead checking whether a face is within the Sphere
Updated code below:
const sphere = new THREE.Sphere(intersection, radius);
// now checking if each vertex of a face is within sphere
// if all are, then color the face at index i
for (let i=0; i < (positionsAttr.count) /3); i++) {
vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
if (sphere.containsPoint(vertexA)
&& sphere.containsPoint(vertexA)
&& sphere.containsPoint(vertexA)) {
colorFace(i);
}
When I tested this in my app, I noticed that the performance has definitely improved from the previous version. However, I am still wondering if I could improve this further.
This seem to be a classic Nearest Neighbors problem.
You can narrow the search by finding the nearest triangles to a given point very fast by building a Bounding Volume Hierarchy (BVH) for the mesh, such as the AABB-tree.
BVH:
https://en.m.wikipedia.org/wiki/Bounding_volume_hierarchy
AABB-Tree:
https://www.azurefromthetrenches.com/introductory-guide-to-aabb-tree-collision-detection/
Then you can query against the BVH a range query using a sphere or a box of a given radius. That amounts to traverse the BVH using a sphere/box "query" which is used to discard quickly and very early the Bounding Volume Nodes that does not clip the sphere/box "query". At the end the real distance or intersection test is made only with triangles whose BV intersect the sphere/box "query", typically a very small fraction of the triangles.
The complexity of the query against the BVH is O(log n) in contrast with your approach which is O(n).

Handling Proper Rotation of Cannon Body Based on Quaternion?

This one is bugging me quite a bit.
I'm trying to achieve rotation of a Cannon.Body based on the mouse input.
By using the (Cannon) Three FPS example to demonstrate, you can see what the issue is.
https://codepen.io/Raggar/pen/EggaZP
https://github.com/RaggarDK/Baby/blob/baby/pl.js
When you run the code and enable pointerlockcontrols by clicking on the "click to play" area and press W for 1 second to get the sphere into the view of the camera, you'll see that the sphere moves according to the WASD keys by applying velocity. If you move the mouse, the quaternion is applied to the Body, and the proper velocity is calculated.
Now turn 180 degrees, and the rotation on the X axis is now negated somehow.
When moving the mouse up, the sphere rotates down.
How would one fix such issue? Maybe I'm doing something wrong elsewhere, that might mess with the quaternion?
Maybe I should mention, in the playercontroller(pl.js), I'm applying the rotation to the sphereBody, instead of the yaw- and pitchObjects.
Relevant code from pl.js (Line 49):
var onMouseMove = function ( event ) {
if ( scope.enabled === false ) return;
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
cannonBody.rotation.y -= movementX * 0.002;
cannonBody.rotation.x -= movementY * 0.002;
cannonBody.rotation.x = Math.max( - PI_2, Math.min( PI_2, cannonBody.rotation.x ) );
//console.log(cannonBody.rotation);
};
And (Line 174):
euler.x = cannonBody.rotation.x;
euler.y = cannonBody.rotation.y;
euler.order = "XYZ";
quat.setFromEuler(euler);
inputVelocity.applyQuaternion(quat);
cannonBody.quaternion.copy(quat);
velocity.x = inputVelocity.x;
velocity.z = inputVelocity.z;
Inside the animate() function, codepen (Line 305):
testballMesh.position.copy(sphereBody.position);
testballMesh.quaternion.copy(sphereBody.quaternion);
The problem is the way you assign angles to and from the Quaternions. The quaternion x,y,z,w properties are not directly compatible with angles, so you need to convert.
This is how to set the angle around a given axis for a CANNON.Quaternion:
var axis = new CANNON.Vec3(1,0,0);
var angle = Math.PI / 3;
body.quaternion.setFromAxisAngle(axis, angle);
Extracting the Euler angles from quaternions is probably not be the best way to attack the second part of the problem. You could instead just store the rotation around X and Y axes when the user moves the mouse:
// Declare variables outside the mouse handler
var angleX=0, angleY=0;
// Inside the handler:
angleY -= movementX * 0.002;
angleX -= movementY * 0.002;
angleX = Math.max( - PI_2, Math.min( PI_2, angleX ) );
And then to get the rotation as a quaternion, use two quaternions separately (one for X angle and one for Y) and then combine them to one:
var quatX = new CANNON.Quaternion();
var quatY = new CANNON.Quaternion();
quatX.setFromAxisAngle(new CANNON.Vec3(1,0,0), angleX);
quatY.setFromAxisAngle(new CANNON.Vec3(0,1,0), angleY);
var quaternion = quatY.mult(quatX);
quaternion.normalize();
To apply the quaternion to your velocity vector:
var rotatedVelocity = quaternion.vmult(inputVelocity);
Pro tip: don't use Euler angles if you can avoid them. They usually cause more problems than they solve.

Improve UV coordinate generation for rotated triangles/faces in THREE.js

I've seen a lot of similar questions to this on StackOverflow and elsewhere, but none directly addressed my problem. I'm generating n-sided polyhedrons using a convex hull generator in THREE.js. I want to map a square texture onto each face of each polyhedron such that it is not distorted, and gets drawn correctly with perspective (ie. not counteracting perspective or anything like that).
My first attempt, and most of what I've seen elsewhere, looked like this:
function faceUv( v1, v2, v3 ) {
return [
new THREE.Vector2(0,0),
new THREE.Vector2(1,0),
new THREE.Vector2(1,1)
];
}
This changes the aspect ratio of the texture on faces that are not XY aligned right triangles. That is, all of them ;)
So, I thought I would try rotating the face to be aligned with the XY plane, then compute the UVs as if a 1x1 square were drawn over them. So a triangle like this--after XY alignment--would get the following uv coords:
0,0 1,0
* v1(uv=1.0,0.0)
v3(uv=0.1,0.5)
*
* v2(uv=0.5,0.8)
0,1 1,1
This would be good enough for my purposes.
Here's my current solution:
function faceUv(v1, v2, v3) {
var z = new THREE.Vector3(0, 0, 1),
n = new THREE.Triangle(v1, v2, v3).normal(),
// from http://stackoverflow.com/questions/13199126/find-opengl-rotation-matrix-for-a-plane-given-the-normal-vector-after-the-rotat
// RotationAxis = cross(N, N')
// RotationAngle = arccos(dot(N, N') / (|N| * |N'|))
axis = new n.clone().cross(z),
angle = Math.acos(n.clone().dot(z) / (n.length() * z.length()));
var mtx = new THREE.Matrix4().makeRotationAxis(axis, angle);
var inv = new THREE.Matrix4().getInverse(mtx);
var v1r = v1.clone().applyMatrix4(inv),
v2r = v2.clone().applyMatrix4(inv),
v3r = v3.clone().applyMatrix4(inv),
ul = v1r.clone().min(v2r).min(v3r),
lr = v1r.clone().max(v2r).max(v3r),
scale = new THREE.Vector2(1.0 / (lr.x - ul.x), 1.0 / (lr.y - ul.y));
v1r.sub(ul);
v2r.sub(ul);
v3r.sub(ul);
return [
new THREE.Vector2(v1r.x * scale.x, v1r.y * scale.y),
new THREE.Vector2(v2r.x * scale.x, v2r.y * scale.y),
new THREE.Vector2(v3r.x * scale.x, v3r.y * scale.y)
];
}
It seems to work pretty well, with the exception of faces that are aligned with the YZ plane (ie. with a normal of 0,-1,0). Here's a screenshot, notice the bottoms of the polyhedrons.
So my questions are: a) is there a better/simpler way to do this? b) how can I fix it for the bottom faces?, c) is there a way to at least simplify the code by using more existing functionality from THREE.js?
Thanks!
Do you mean this?
axis = n.clone().cross( z );
Also, UVs are normally ( 0, 0 ) in the lower left.
Regarding questions (a) and (c): Given what you are doing, 'No'.
Regarding question (b): I expect you can track that down yourself. :-)

Incorrect angle, wrong side calculated

I need to calculate the angle between 3 points. For this, I do the following:
Grab the 3 points (previous, current and next, it's within a loop)
Calculate the distance between the points with Pythagoras
Calculate the angle using Math.acos
This seems to work fine for shapes without angels of over 180 degrees, however if a shape has such an corner it calculates the short-side. Here's an illustration to show what I mean (the red values are wrong):
This is the code that does the calculations:
// Pythagoras for calculating distance between two points (2D)
pointDistance = function (p1x, p1y, p2x, p2y) {
return Math.sqrt((p1x - p2x)*(p1x - p2x) + (p1y - p2y)*(p1y - p2y));
};
// Get the distance between the previous, current and next points
// vprev, vcur and vnext are objects that look like this:
// { x:float, y:float, z:float }
lcn = pointDistance(vcur.x, vcur.z, vnext.x, vnext.z);
lnp = pointDistance(vnext.x, vnext.z, vprev.x, vprev.z);
lpc = pointDistance(vprev.x, vprev.z, vcur.x, vcur.z);
// Calculate and print the angle
Math.acos((lcn*lcn + lpc*lpc - lnp*lnp)/(2*lcn*lpc))*180/Math.PI
Is there something wrong in the code, did I forget to do something, or should it be done a completely different way?
HI there your math and calculations are perfect. Your running into the same problem most people do on calculators, which is orientation. What I would do is find out if the point lies to the left or right of the vector made by the first two points using this code, which I found from
Determine which side of a line a point lies
isLeft = function(ax,ay,bx,by,cx,cy){
return ((bx - ax)*(cy - ay) - (by - ay)*(cx - ax)) > 0;
}
Where ax and ay make up your first point bx by your second and cx cy your third.
if it is to the left just add 180 to your angle
I've got a working but not necessarily brief example of how this can work:
var point1x = 0, point1y = 0,
point2x = 10, point2y = 10,
point3x = 20, point3y = 10,
point4x = 10, point4y = 20;
var slope1 = Math.atan2(point2y-point1y,point2x-point1x)*180/Math.PI;
var slope2 = Math.atan2(point3y-point2y,point3x-point2x)*180/Math.PI;
var slope3 = Math.atan2(point4y-point3y,point4x-point3x)*180/Math.PI;
alert(slope1);
alert(slope2);
alert(slope3);
var Angle1 = slope1-slope2;
var Angle2 = slope2-slope3;
alert(180-Angle1);
alert(180-Angle2);
(see http://jsfiddle.net/ZUESt/1/)
To explain the multiple steps the slopeN variables are the slopes of the individual line segments. AngleN is the amount turned at each junction (ie point N+1). A positive angle is a right turn and a negative angle a left turn.
You can then subtract this angle from 180 to get the actual interior angle that you want.
It should be noted that this code can of course be compressed and that five lines are merely outputting variables to see what is going on. I'll let you worry about optimizing it for your own use with this being a proof of concept.
You need to check boundary conditions (apparently, if points are colinear) and apply the proper calculation to find the angle.
Also, a triangle can't have any (interior) angle greater than 180 degress. Sum of angle of triangle is 180 degrees.

Categories

Resources