I'm attempting to create a snazzy VR menu so that when the user looks down the menu items are in a column below camera curved around circle so they look the same and are rotated towards camera.
Here is my attempt thus far
And a CodePen with the example
http://codepen.io/bknill/pen/BLOwLj?editors=0010
I'm using some code I found that calculates the position
var radius = 60; // radius of the circle
var height = 60,
angle = 0,
step = (Math.PI /2 ) / menuItems.length;
menuItems.forEach(function(item,index){
var menuItem = createMenuItem(item.title);
menuItem.position.y = - Math.round(height/2 + radius * Math.sin(angle));
menuItem.position.z = Math.round(height/2 + radius * Math.sin(angle));
// menuItem.rotation.x = -Math.round(Math.PI * Math.sin(angle));
angle += step;
menu.add(menuItem);
})
Which is almost right, the next stage is to get them to rotate in a uniform way towards the camera. Using menuItem.lookAt(camera.position) isn't working - they're not uniform rotation.
child.lookAt(camera.position.normalize()) does this
Anyone let me know the clever maths I need to get the rotation of the item so they face the camera and look like they're on a curve?
The simplest way to have an object facing another object is using lookAt.
Be aware this method needs a direction vector, not the point where you want it to look at.
(position you want to look at - position of your object).normalize();
In your case:
var dirToLookAt = new THREE.Vector3();
dirToLookAt.subVectors(menuItem.position, camera.position);
// could be: dirToLookAt.subVectors(camera.position, menuItem.position);
dirToLookAt.normalize();
Operations based on Vector3 documentation.
For more info you can read the last discussion I had about this method.
Related
I'm working on a 2D rendering system in 3D space, for practice. The sprites are 2D, but they're rendered in 3D space. The "camera" can move in 3D space and turn 360 degrees horizontally. I'm having trouble figuring out the right formula to calculate, based on the position/rotation of the camera and the position of the assets, where they should exist on the screen.
What I have is like this:
chunk.assets.forEach(asset => {
let x = Math.round(
asset.coords.x * Math.cos(angle) - asset.coords.y * Math.sin(angle)
);
let y = Math.round(
asset.coords.y * Math.cos(angle) + asset.coords.x * Math.sin(angle)
);
if (!depthMap[y]) {
depthMap[y] = [];
}
depthMap[y].push(asset);
});
But this does not take into account the camera's (player's) position (stored at player.coords.x, player.coords.y), only the angle the camera/player is facing (angle). So right now the camera can't move. Note: Depth map is just storing the assets in order so the renderer knows which order to render the sprites so things appear in the right order based on which is closer to the player/camera.
How can I incorporate the camera's position into this algorithm?
Some assumptions:
If the camera and the sprite are at the same position, the sprite will be rendered at 0|0.
Wether the camera moves left or the sprite moves right does not matter.
From that we can conclude that only the relative position matters, and that can be easily calculated (by subtracting both positions).
let x = Math.round(
(asset.coords.x - player.coords.x) * Math.cos(angle) - (asset.coords.y - player coords.y) * Math.sin(angle)
);
let y = Math.round(
(asset.coords.y - player.coords.y) * Math.cos(angle) + (asset.coords.x - player.coords.x) * Math.sin(angle)
);
I'm working on a particle system project. I want to add a little delay to my Points Object rotation in three.js. I'm currently using D3.js linear scale which gives 1:1 rotation. For example, if you quickly move your cursor all the way to right, my Points Object will match your cursor movement speed. What I want is to ease in the rotation so the rotation would finish ~1sec after you move your cursor all the way to the right. Here is my current code.
var rotYScale = d3.scale.linear().domain([0, window.innerWidth]).range([25,-25]);
var rotXScale = d3.scale.linear().domain([0, window.innerHeight]).range([15,-15]);
d3.select("body").on("mousemove", function() {
particleSystem.rotation.y = rotYScale(d3.mouse(this)[0]) * Math.PI / 180;
particleSystem.rotation.x = rotXScale(d3.mouse(this)[1]) * Math.PI / 180;
});`
I got it to work by using Tween.js. This is the code I added.
var tween = new TWEEN.Tween(particleSystem.rotation).to({ x: scaledX, y: scaledY, z: 0})
tween.easing( TWEEN.Easing.Quadratic.Out)
tween.start();
I'm building an app in which I present some planes with textures. However, I would like to calculate the radius of the helix (which I use in my calculations to create a helix), dynamically based on the frustum width and the camera position.
The helix is positioned at the center of the screen x=0, y=0, z=0.
I would like this to take under consideration the screen orientation (landscape/ portrait).So far this is the code I have but it seems that I'm missing something because the planes at the left and the right are not inside the viewport.
App.prototype.calculateHelixRadius = function(){
// plane width = height = 512;
var friend = this.getFriend();
var vFOV = friend.camera.fov * Math.PI / 180;
var dist = utils.getAbsPointsDistance3D(friend.camera.position, friend.scene.position);
var aspect = friend.settings.container.clientWidth / friend.settings.container.clientHeight;
var frustumHeight = 2.0 * dist * Math.tan(0.5 * vFOV);
var frustumWidth = frustumHeight * aspect;
return utils.isLandscape() ? frustumHeight / 2 : frustumWidth / 2 ;
};
What am I doing wrong and why are the planes at the edges of the screen not inside?
Also for reference here is the code of getAbsPointsDistance3D
var utils = {
// other helpers...
getAbsPointsDistance3D: function(p1, p2) {
var xd = p2.x - p1.x;
var yd = p2.y - p1.y;
var zd = p2.z - p1.z;
return Math.sqrt(xd * xd + yd * yd + zd * zd);
}
};
update
I tried decreasing the dist parameter but the results are not consistent...
I wonder if the following explains your clipping.
You calculate your frustum characteristics, then calculate the helix radius using, say, the frustum width (width or height depending on the screen aspect...I may be getting some of the particulars wrong here because your question does not completely explain the details, but the general concepts still hold). The image below is a top view of the scenario which shows a circle representing the cylinder that encloses the helix. I believe you have calculated radius1. If so, note that there will be clipping of the cylinder (the shaded area), and thus the helix, in "front" of the cylinder centre.
Instead you need to calculate the cylinder/helix radius as shown in the second image, i.e. you need radius2. If the large angle at the image left is fov (again, vFOV? or hFOV?, etc., depending on whether your helix is going up-down or side-to-side, etc.), then its half angle is fov/2. This is the same angle shown in the centre of the cylinder. Thus, you need to decrease your helix radius as follows: radius2 = radius1 * cos(fov/2).
Im creating a simple particle experiment on canvas. Now i want them to "run away" from mouse coursor over canvas. detecting the distance from the mouse is not a problem, but how to code their behaviour?
each particle is created as following:
var particle = {
x: Math.floor(Math.random() * width),
y: Math.floor(Math.random() * height),
xVel: Math.random() * 10 - 5,
yVel: Math.random() * 10 - 5,
}
so i assume i should also save the direction somehow, and if the distance from pointer is < x, reverse the direction? maybe also save old speed, and decrease it slowly while moving away?
how to detect the direction?
Velocity (xVel, yVel, together) is a 2D vector. And so is the distance between the mouse and the particles. A vector contains both direction and magnitude. So you want a vector that is the difference between the mouse position and the particle position.
var posRelativeToMouse = {
x: particle.x - mousPosX,
y: particle.y - mousPosY
};
So small numbers of x and y mean the the particle is close to the mouse, and big mean it's far away.
Next we need to figure out how these numbers should affect the velocity of the particle. So we need 2 things.
What direction do we push them in?
We already have this, mostly. posRelativeToMouse is a vector that has the direction we want. We just normalize it, which means to set the length of the vector to 1. To do that, we divide each component by the current length of the vector. The length of this vector is always the distance to from the particle to the mouse.
var distance = Math.sqrt(
posRelativeToMouse.x * posRelativeToMouse.x +
posRelativeToMouse.y * posRelativeToMouse.y
);
var forceDirection = {
x: posRelativeToMouse.x / distance,
y: posRelativeToMouse.y / distance,
};
How hard do we push the particles?
This is an inverse of the distance. Close means a big push, far means a little push. So lets reuse our distance we calculated above.
// distance past which the force is zero
var maxDistance = 1000;
// convert (0...maxDistance) range into a (1...0).
// Close is near 1, far is near 0
// for example:
// 250 => 0.75
// 100 => 0.9
// 10 => 0.99
var force = (maxDistance - distance) / maxDistance;
// if we went below zero, set it to zero.
if (force < 0) force = 0;
Ok we have a direction, and we have the force. All that's left is to apply this to the particle velocity.
particle.xVel += forceDirection.x * force * timeElapsedSinceLastFrame;
particle.yVel += forceDirection.y * force * timeElapsedSinceLastFrame;
And assuming you are animating your position each frame by that xVel/yVel, you should now have particles being pushed away by the mouse.
you can obtain a vector v by subtracting the position of particle from position of mouse,
then you can find the magnitude of this vector my taking sqrt(x^2 + y^2)
by dividing v by magnitude, you obtain a unit vector in the direction you want your particles to go.
for instance.
suppose I have 10 particles in a list U, each has an x and y field.
I can obtain it's vector from each particle v by setting v = (xpart - mousepart, ypart - mousepart)
then you need to find the magnitude vmag by taking sqrt(vx^2 + vy^2)
then you obtain vunit = (vx / vmag, vy / vmag)
This is the vector "away from the mouse".
the rest can be left to detemining speed you want to move at, and ensuring you bounce of walls and such.
I have a similar project at github open source:
https://github.com/dmitrymakhnin/JavaParticleSystem/blob/master/Main.java
Over the last two days I've effectively figured out how NOT to rotate Raphael Elements.
Basically I am trying to implement a multiple pivot points on element to rotate it by mouse.
When a user enters rotation mode 5 pivots are created. One for each corner of the bounding box and one in the center of the box.
When the mouse is down and moving it is simple enough to rotate around the pivot using Raphael elements.rotate(degrees, x, y) and calculating the degrees based on the mouse positions and atan2 to the pivot point.
The problem arises after I've rotated the element, bbox, and the other pivots. There x,y position in the same only there viewport is different.
In an SVG enabled browser I can create new pivot points based on matrixTransformation and getCTM. However after creating the first set of new pivots, every rotation after the pivots get further away from the transformed bbox due to rounding errors.
The above is not even an option in IE since in is VML based and cannot account for transformation.
Is the only effective way to implement
element rotation is by using rotate
absolute or rotating around the center
of the bounding box?
Is it possible at all the create multi
pivot points for an object and update
them after mouseup to remain in the
corners and center of the transformed
bbox?
UPDATE:
I've attempted to use jQuery offset to find the pivot after it's been rotated, and to use that offset location as the pivot point.
Demo site ...
http://weather.speedfetishperformance.com/dev/raphael/rotation.html
The best cross-browser way I can think of to do what you want is to implement the rotation yourself rather than let SVG do it. Rotating x,y coordinates is fairly simple and I've been using this (tcl) code whenever I need to do 2D rotation: Canvas Rotation.
The upside to this is you have maximum control of the rotation since you're doing it manually. This solves the problems you're having trying to guess the final coordinates after rotation. Also, this should be cross browser compatible.
The downside is you have to use paths. So no rects (though it should be easy to convert them to paths) or ellipses (a little bit harder to convert to path but doable). Also, since you're doing it manually, it should be slower than letting SVG do it for you.
Here's a partial implementation of that Tcl code in javascript:
first we need a regexp to tokenize SVG paths:
var svg_path_regexp = (function(){
var number = '-?[0-9.]+';
var comma = '\s*[, \t]\s*';
var space = '\s+';
var xy = number + comma + number;
var standard_paths = '[mlcsqt]';
var horiz_vert = '[hv]\s*' + number;
var arc = 'a\s*' + xy + space + number + space + xy + space + xy;
var OR = '\s*|';
return new RegExp(
standard_paths +OR+
xy +OR+
horiz_vert +OR+
arc,
'ig'
);
})();
now we can implement the rotate function:
function rotate_SVG_path (path, Ox, Oy, angle) {
angle = angle * Math.atan(1) * 4 / 180.0; // degrees to radians
var tokens = path.match(svg_path_regexp);
for (var i=0; i<tokens.length; i++) {
var token = tokens[i].replace(/^\s+|\s+$/g,''); // trim string
if (token.match(/\d/)) { // assume it's a coordinate
var xy = token.split(/[, \t]+/);
var x = parseFloat(xy[0]);
var y = parseFloat(xy[1]);
x = x - Ox; // Shift to origin
y = y - Oy;
var xx = x * Math.cos(angle) - y * Math.sin(angle); // Rotate
var yy = x * Math.sin(angle) + y * Math.cos(angle);
x = xx + Ox; // Shift back
y = yy + Oy;
token = x + ',' + y;
}
else if (token.match(/^[hv]/)) {
// handle horizontal/vertical line here
}
else if (token.match(/^a/)) {
// handle arcs here
}
tokens[i] = token;
}
return tokens.join('');
}
The above rotate function implements everything except horizontal/vertical lines (you need to keep track of previous xy value) and arcs. Neither should be too hard to implement.