How do I rotate some moons around a planet with THREE.js? - three.js

I think this picture best explains my issue:
First I translate the box along the red line. Next, I want the effect of rotation to be the blue line in a, but what's actually happening is more like the blue line in b. It feels like changing the rotation is always relative to the original object space, but the translation (despite happening first) is always relative to the parent, and doesn't really affect the geometry points in relation to the object space. I apologize if that's confusing; clearly I'm new at this.
The important part of the code which produces this effect is below. Please keep in mind that the orientation of the image is different than this code produces; the image is merely an example to show the effect clearly.
var objectContainer = new THREE.Object3D();
var tubeRadius = 100;
var tubeGeometry = new THREE.CylinderGeometry(tubeRadius, tubeRadius, tubeRadius * 3, 36, 1, false);
var tube = new THREE.Mesh(tubeGeomtry, material);
scene.add( tube );
var boxes = new THREE.Object3D();
var boxEdge = 50;
var boxGeometry = new THREE.CubeGeometry(boxEdge, boxEdge, boxEdge);
var box1 = new THREE.Mesh( boxGeometry, material );
box1.translateX(tubeRadius + boxEdge / 2 + 5);
box1.translateY(boxEdge / 2);
box1.rotation = new THREE.Vector3(0, 2*Math.PI/3*0, 0);
boxes.add(box1);
var box2 = box1.clone();
box2.rotation = new THREE.Vector3(0, 2*Math.PI/3*1, 0);
boxes.add(box2);
var box3 = box1.clone();
box3.rotation = new THREE.Vector3(0, 2*Math.PI/3*2, 0);
boxes.add(box3);
scene.add( boxes );
The only solution I can think of is to wrap each box in another object space and rotate about that, but it seems like excessive work. What is the preferred method to achieve the result I'm looking for?

There are several ways of doing what you want, but I think the easiest is like so:
// parent
parent = new THREE.Group();
scene.add( parent );
// pivots
var pivot1 = new THREE.Object3D();
var pivot2 = new THREE.Object3D();
var pivot3 = new THREE.Object3D();
pivot1.rotation.z = 0;
pivot2.rotation.z = 2 * Math.PI / 3;
pivot3.rotation.z = 4 * Math.PI / 3;
parent.add( pivot1 );
parent.add( pivot2 );
parent.add( pivot3 );
// mesh
var mesh1 = new THREE.Mesh( geometry, material );
var mesh2 = new THREE.Mesh( geometry, material );
var mesh3 = new THREE.Mesh( geometry, material );
mesh1.position.y = 5;
mesh2.position.y = 5;
mesh3.position.y = 5;
pivot1.add( mesh1 );
pivot2.add( mesh2 );
pivot3.add( mesh3 );
Then in your render loop:
parent.rotation.z += 0.01;
three.js r.147

Creating a compound object whose centre will be the point about which the inner objects rotate is one obvious answer, and would be very quick to write. Just create an Object3D and add your box to it.
A similar approach is covered by this question. It shifts the point of the vertices for an object, so it effectively has a new centre.
Alternatively, you can mess around with the matrices by hand. Try this:
var boxGeometry = new THREE.CubeGeometry(boxEdge, boxEdge, boxEdge);
var mr = new THREE.Matrix4();
var mt = new THREE.Matrix4();
mt.setPosition(new THREE.Vector3(0,tubeRadius,0));
var box1 = new THREE.Mesh( boxGeometry, material );
box1.applyMatrix(mt);
var box2 = box1.clone();
mr.makeRotationZ(2 * Math.PI /3);
box2.applyMatrix(mr);
boxes.add(box2);
var box3 = box1.clone();
mr.makeRotationZ(4 * Math.PI /3);
box3.applyMatrix(mr);
boxes.add(box3);
boxes.add(box1);
scene.add( boxes );

Related

Raycaster Set getting Z value for respective XY

I have array of XY coordinates from which i have to get the respective Z positions. I have created the following code to achieve same.
This function loops through array and calls further function to get the Z value.
function generate_section(){
for(var i=0;i<points.length;i++){
//temporary try to get for the same.
var pts = points[i];
var z = sectioncall(pts.x,pts.y);
console.log(pts,z);
}
}
The following function is a raycaster which cast the ray for the provided x& y value and cast a downward ray.
function sectioncall(x,y){ //grabs the Z value for the provided XY
var top = new THREE.Vector3(x, y , 30 );
var bottom = new THREE.Vector3(x , y , -30 );
var direction = new THREE.Vector3();
direction = direction.subVectors( bottom, top ).normalize();
//start raycaster
var raycaster = new THREE.Raycaster();
raycaster.set( top, direction );
// calculate objects intersecting the picking ray
var intersects = rayCaster.intersectObjects(scene.getObjectByName('MyObj_s').children);
var rpt = intersects[0].point;
//draw a line the way ray caster casting the ray
var geometry = new THREE.Geometry();
geometry.vertices.push( top );
geometry.vertices.push( rpt );
var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
var line = new THREE.Line( geometry, material );
scene.add( line );
return rpt;
}
With the above code i get the result like this:
but what i want to achive is the result like this:
So that the returning values will be about for respective XY instead of last XY as you see on the console.
You have slightly made a mistake in the code.
it should be
var intersects = raycaster.intersectObjects(scene.getObjectByName('MyObj_s').children);
you misspelled it as rayCaster.

Solar system using Three js animation

I am creating a solar system using three js.In that I want to display some of the details on clicking on any objects.I have used object picking concepts.In that I am trying to get the objects which are intersecting with the
clicking.But I am unable get any objects which are intersecting.When I tried to print the objects in the intersects array I am getting as "undefined" and length of the intersects array as 0.
function mous(event) {
var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
vector = vector.unproject(camera);
raycaster = new THREE.Raycaster(camera.position, vector);
var intersects = raycaster.intersectObjects([orbitDir1,orbitDir2,orbitDir3,orbitDir4,orbitDir5]);
alert(intersects[0]);
alert(intersects.length);
}`
And here is the code for orbitDir.
geometry = new THREE.CircleGeometry(2.3, 100);
geometry.vertices.shift();
circle = new THREE.Line(
geometry,
new THREE.LineDashedMaterial({color: 'red'})
);
circle.rotation.x = Math.PI * 0.5 ;
tex = new THREE.ImageUtils.loadTexture("Mercury.jpeg") ;
planet = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.3, 32, 32),
new THREE.MeshPhongMaterial({ map : tex})
);
planet.position.set(2.3, 0, 0);
scene.add(planet);
orbit = new THREE.Group();
orbit.add(circle);
orbit.add(planet);
orbitDir = new THREE.Group();
orbitDir.add(orbit);
//orbitDir.position.x += 0.1 ;
orbitDir.position.y += 4 ;
orbitDir.position.z += 5 ;
orbitDir.rotation.x +=2.3 ;
scene.add(orbitDir);
The code for »unprojection« and raycasting look fine, so I guess that the x and y values might not be right. You are using clientX and clientY which are the mouse coordinates relative to the upper left corner of the window. Those are only valid if your <canvas> is full page. If that is not the case, make sure to use mouse coordinates relative to the upper left edge of the <canvas>.
I think you can do the raycasting like that:
raycaster.intersectObjects(scene, true) //scan the whole scene recursive
docs
Probably the answer you are looking for is here
projector.unprojectVector( vector, camera.position );

Draw line in direction of raycaster in three.js

In three.js, I'm using PointerLock controls the make a basic first person shooter.
I use
function onDocumentMouseDown( event ) {
var raycaster = new THREE.Raycaster();
mouse3D.normalize();
controls.getDirection( mouse3D );
raycaster.set( controls.getObject().position, mouse3D );
var intersects = raycaster.intersectObjects( objects );
...
}
to detect a collision with an object, which means you "shot" the object.
Now, I want to visualize the path the bullet took. I was thinking about drawing a line from where the user is looking to, in direction of the raycaster, but I can't figure out how to do this... Anyone who can help me? I'm new to three.js, never thought drawing a line would be this hard.
Update:
I'm trying to draw a line using:
var geometry = new THREE.Geometry();
geometry.vertices.push(...);
geometry.vertices.push(...);
var line = new THREE.Line(geometry, material);
scene.add(line);
but I can't figure out what to put in place of the "..." . How can I detect which point the line should go to? And how to determine which point it starts from? The player is able to move and even jump so the starting point is always different too.
You can use the following (using r83):
// Draw a line from pointA in the given direction at distance 100
var pointA = new THREE.Vector3( 0, 0, 0 );
var direction = new THREE.Vector3( 10, 0, 0 );
direction.normalize();
var distance = 100; // at what distance to determine pointB
var pointB = new THREE.Vector3();
pointB.addVectors ( pointA, direction.multiplyScalar( distance ) );
var geometry = new THREE.Geometry();
geometry.vertices.push( pointA );
geometry.vertices.push( pointB );
var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
var line = new THREE.Line( geometry, material );
scene.add( line );
Codepen at: https://codepen.io/anon/pen/evNqGy
You can use something like this:
function animate_Line(frame, totalFrames) {
//Calculate how much of the line should be drawn every iteration
var delta = lineDistance/(totalFrames);
var deltaSpeed = delta * frame;
for(var i=0; i<f_Ray_List[0].length; i++) {
for(var j=0; j<f_Ray_List[1].length; j++) {
//Change Offsets
line.geometry.vertices[1].y = line.geometry.vertices[0].y - deltaSpeed;
//Update Rays = true (Make FRT rays draw-able)
line.geometry.verticesNeedUpdate = true;
}
}
}
where frame is the current frame (a counter in your animate function), totalFrames would be the amount of frames that the line would take to be animated. The lineDistance can be calculated by using this:
lineDistance = line.geometry.vertices[0].y - line.vertices[1].y; //Add this line where you create the line object.
and remember to call line.geometry.verticesNeedUpdate = true; in every line individually, so that the line would able to be animated.
Notice that this is only based on Line.y axis. This would not be great at first. I'm currently working on converting this to Polar coordinates instead but I have no idea what is going on hahah.

How to cast a visible ray threejs

I want to aim for objects with cameras' vision (as the user would look at the object, not point at it with mouse).
I'm casting a ray from the camera like this
rotation.x = camera.rotation.x;
rotation.y = camera.rotation.y;
rotation.z = camera.rotation.z;
raycaster.ray.direction.copy( direction ).applyEuler(rotation);
raycaster.ray.origin.copy( camera.position );
var intersections = raycaster.intersectObjects( cubes.children );
This gets me the intersections but it seems to wander off sometimes. So I'd like to add aim (crosshair). That would be somekind on object (mesh) at the end or in the middle of the ray.
How can I add it? When I created a regular line it was in front of the camera so the screen would go black.
You can add a crosshair constructed from simple geometry to your camera like this:
var material = new THREE.LineBasicMaterial({ color: 0xAAFFAA });
// crosshair size
var x = 0.01, y = 0.01;
var geometry = new THREE.Geometry();
// crosshair
geometry.vertices.push(new THREE.Vector3(0, y, 0));
geometry.vertices.push(new THREE.Vector3(0, -y, 0));
geometry.vertices.push(new THREE.Vector3(0, 0, 0));
geometry.vertices.push(new THREE.Vector3(x, 0, 0));
geometry.vertices.push(new THREE.Vector3(-x, 0, 0));
var crosshair = new THREE.Line( geometry, material );
// place it in the center
var crosshairPercentX = 50;
var crosshairPercentY = 50;
var crosshairPositionX = (crosshairPercentX / 100) * 2 - 1;
var crosshairPositionY = (crosshairPercentY / 100) * 2 - 1;
crosshair.position.x = crosshairPositionX * camera.aspect;
crosshair.position.y = crosshairPositionY;
crosshair.position.z = -0.3;
camera.add( crosshair );
scene.add( camera );
Three.js r107
http://jsfiddle.net/5ksydn6u/2/
In case you dont have a special usecase where you need to retrieve the position and rotation from your camera like you are doing, I guess your "wandering off" could be fixed by calling your raycaster with these arguments.:
raycaster.set( camera.getWorldPosition(), camera.getWorldDirection() );
var intersections = raycaster.intersectObjects( cubes.children );
Cast visible ray
Then you can visualize your raycast in 3D space by drawing an arrow with the arrow helper. Do this after your raycast:
scene.remove ( arrow );
arrow = new THREE.ArrowHelper( camera.getWorldDirection(), camera.getWorldPosition(), 100, Math.random() * 0xffffff );
scene.add( arrow );

Merge two planes with texture into one mesh

I want to merge two meshes in threejs. I want to create a geometry of two planes that are intersecting each other perpendicular. Both of the planes must have the same texture.
I've tried the following.
Currently this error occurs: THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.
var texture = THREE.ImageUtils.loadTexture('/img/foo.png');
var material = new THREE.MeshPhongMaterial( { map: texture, side: THREE.DoubleSide, transparent: true });
var m_plane_1 = new THREE.PlaneGeometry( 128, 128);
var m_plane_2 = new THREE.PlaneGeometry(128, 128);
var plane_1 = new THREE.Mesh(m_plane_1, material);
var plane_2 = new THREE.Mesh(m_plane_2, material);
plane_2.rotation.y = Math.PI / 2;
var combined = new THREE.Geometry();
combined.merge(plane_1); // does not work
//combined.merge(plane_1.geometry, plane_1.matrix); // this does not work
//combined.merge(m_plane_1.geometry, m_plane_1.matrix); // this does not work
scene.add(combined);
I've tried to read the source code for merge() but could not come to any conclusion. I've read stackoverflow threads but their approach does not work for me.
How can I solve this?
You want to get the intersection of the geometries. Then you can apply whatever texture you want to that new geometry.
To get the intersection, you should use Chandler Prall's Constructive Solid Geometry code: http://evanw.github.io/csg.js/
There are several ways to merge two geometries, but probably the simplest way to achieve what you want is to use the following pattern:
var geometry = new THREE.PlaneGeometry( 128, 128 );
var geometry2 = new THREE.PlaneGeometry( 128, 128 );
geometry2.applyMatrix( new THREE.Matrix4().makeRotationY( Math.PI / 2 ) );
geometry.merge( geometry2 );
three.js r.69
Try this:
plane_1.updateMatrix();
//Now the function merge
combined.merge(plane_1.geometry, plane_1.matrix);
//combined.merge(plane_1); // does not work
//combined.merge(plane_1.geometry, plane_1.matrix); // this does not work
//combined.merge(m_plane_1.geometry, m_plane_1.matrix); // this does not work

Resources