How to animate and object along an elliptical path [Three.js]? - three.js

I am trying to move an Object3D group along a specific ellipse. I got it to move in a circle successfully using:
group.position.x = Math.cos(toRadians(loc+=5)) * radius;
group.position.z = Math.sin(toRadians(loc+=5)) * radius;
However, I want it to follow a specific 3 dimensional ellipse. I have the ellipse made successfully and visualized here:
curve = new THREE.EllipseCurve(0,0,80,60,0,toRad(360),false);
path = new THREE.Path( curve.getPoints( 2000 ) );
var geo = path.createPointsGeometry( 50 );
ellipse = new THREE.Line(
geo,
new THREE.LineBasicMaterial( { color : 0xff0000 } )
);
DISPLAY.scene.add(ellipse);
ellipse.rotation.x=toRadians(94);
ellipse.rotation.y=toRadians(12);
And I try to animate it like this (outside my 'render' function):
var pathAnim = new PathAnimation(group, path, 0.4);
Or even like this: (within the render function)
group.position=path.getPoint(t+=0.01); //i've tried getPoints, getPointAt, etc..
It doesn't seem to work. Any ideas as to how I can get this working? Thanks

Related

Move Threejs mesh to the center of another mesh

I have the following structure in my scene (imported from glb):
I try to move the mesh svgGroupsvg_test to the center of mesh named M_Top (red cross is the expected location).
Here is my code:
function engraveSVG(object, value) {
//object is the name of the target mesh
var svgMeshName = 'svgGroup' + value
loadSVGandFit(svgMeshName, object, value).then(res => {
var svgMesh = scene.getObjectByName(svgMeshName);
svgMesh.scale.set(0.1, 0.1, 1)
const axesHelper = new THREE.AxesHelper( 20 );
svgMesh.parent.add(axesHelper)
moveCenterMeshToOtherMeshCenter(svgMesh, scene.getObjectByName(object))
})
}
I tried the following functions:
function moveCenterMeshToOtherMeshCenter(centerMesh, otherMesh) {
// get the center positions of both meshes in the local world
const centerMeshPosition = new THREE.Vector3();
const otherMeshPosition = new THREE.Vector3();
centerMesh.getWorldPosition(centerMeshPosition);
otherMesh.getWorldPosition(otherMeshPosition);
// calculate the difference between the center positions of both meshes
const difference = otherMeshPosition.sub(centerMeshPosition);
// translate the center mesh by the difference
centerMesh.translateX(difference.x);
centerMesh.translateY(difference.y);
centerMesh.translateZ(difference.z);
}
function moveCenterToOther(centerMesh, otherMesh) {
const centerBox = new THREE.Box3().setFromObject(centerMesh);
const otherBox = new THREE.Box3().setFromObject(otherMesh);
const centerPosition = centerBox.getCenter(new THREE.Vector3());
const otherPosition = otherBox.getCenter(new THREE.Vector3());
const offset = new THREE.Vector3().subVectors(otherPosition, centerPosition);
centerMesh.position.add(offset);
}
Is there something wrong ? Get center return a value around 0.
I chose to add the svg mesh in the same group of my target mesh. But it changes nothing.
The axes is also in the local coordinate.
Any help would be very appreciate.
I found a solution. I add my SVG in the root of the scene. I am now able to get the correct bounding box size and center in the world coordinates.

How can i store decals in Three.js and load them?

I have a model that when i click on the faces - it's adds decals (points or marks on the face) , i wonder how can i store the selection and load it later on.
I've followed the example Here and this is then function that stores the decal in array
function shoot() {
position.copy( intersection.point );
orientation.copy( mouseHelper.rotation );
if ( params.rotate ) orientation.z = Math.random() * 2 * Math.PI;
var scale = params.minScale + Math.random() * ( params.maxScale - params.minScale );
size.set( scale, scale, scale );
var material = decalMaterial.clone();
material.color.setHex( 0xffffff );
var m = new THREE.Mesh( new DecalGeometry( mesh, position, orientation, size ), material );
decals.push( m );
scene.add( m );
}
So i've tried to store the decals array and when the scene is loading - add them to the scene
scene.add( decals ); // just an idea
I also run in to this example - but still - i couldn't figure out how to do so.
Since decals are just meshes, you can serialize them with Object3D.toJSON() and restore them via ObjectLoader. I've update your fiddle to demonstrate this approach. The relevant code is:
const jsonString = JSON.stringify( scene.toJSON() );
scene = new THREE.ObjectLoader().parse( JSON.parse( jsonString ) );
When the scene object is restored, the decals should still be there. The only thing that disappears is the axes helper (since helpers can't be serialized/deserialized yet).

Three.js light position visibly changes but position attribute stays the same

I have a light that is a child to a pivot object:
var pivotpoint = new THREE.Object3D();
pivotpoint.name="pivot";
scene.add(pivotpoint);
var light = new THREE.PointLight( 0xffffff, 1, 100 );
light.name = "light";
light.castShadow = true;
pivotpoint.add( light );
light.position.set(10,25,0);
Now, in my update() method I rotate the pivot object:
var o = scene.getObjectByName("pivot");
if(GLOBAL_KEYS['a'])
{
o.rotation.y += 0.05;
}
if(GLOBAL_KEYS['d'])
{
o.rotation.y -= 0.05;
}
This works perfectly well. I can see my light rotating around the pivot point, casting shadows and all.
However, if I do...
console.log(light.position);
...the position attribute always stays at (10,25,0).
What in god's name do I need to do in order to get the actual light position??
Thanks in advance!
object.position is a local position, relative to the object's parent in the scene graph. To compute position in global space, use getWorldPosition:
const worldPos = new THREE.Vector3();
light.getWorldPosition(worldPos);
console.log(worldPos);

How to center a THREE.Group based on the width of its children?

I am using Three.js (r86) to render a group of spheres. I'm doing this by calling a createSphereGroup function from an external library:
// External library code.
function createSphereGroup(sphereCount) [
var group = new THREE.Group();
for (var i = 0; i < sphereCount; i++) {
var geometry = new THREE.SphereGeometry(50);
var material = new THREE.MeshPhongMaterial({ color: 0xFF0000 });
var sphere = new THREE.Mesh(geometry, material);
sphere.position.x = i * 150;
group.add(sphere);
}
return group;
}
(Assume that the external library is opaque and I can't modify any code in it.)
// My code.
var group = createSphereGroup(5);
scene.add(group);
...which looks like this:
As you can see, the group of spheres is off-center. I would like the group to be centered, with the 3rd sphere to be at the point where the two lines intersect (x=0).
So is there some way that I can center the group? Maybe by computing the width of the group and then positioning it at x=-width/2? I know you can call computeBoundingBox on a geometry to determine its width, but a THREE.Group doesn't have a geometry, so I'm not sure how to do this...
If you want to center an object (or group) and its children, you can do so by setting the object's position like so:
new THREE.Box3().setFromObject( object ).getCenter( object.position ).multiplyScalar( - 1 );
scene.add( object );
three.js r.92
If all the objects in the group are rather the same size, as in your example, you can just take the average of the children's position :
function computeGroupCenter(count) {
var center = new THREE.Vector3();
var children = group.children;
var count = children.length;
for (var i = 0; i < count; i++) {
center.add(children[i].position);
}
center.divideScalar(count);
return center;
}
Another (more robust) way to do this is to create a bounding box containing the bounding boxes of every child of the group.
There are some important things to consider :
A THREE.Group can contain others THREE.Group, therefore, the algorithm should be recursive
Bounding boxes are computed in local space, however, the object may be translated with respect to its parent so we need to work in world space.
The group itself might be translated to! So we have to jump back from world space to the group's local space to have the center defined in the group's space.
You can achieve this easily with THREE.Object3D.traverse, THREE.BufferGeometry.computeBoundingBox and THREE.Box3.ApplyMatrix4 :
/**
* Compute the center of a THREE.Group by creating a bounding box
* containing every children's bounding box.
* #param {THREE.Group} group - the input group
* #param {THREE.Vector3=} optionalTarget - an optional output point
* #return {THREE.Vector3} the center of the group
*/
var computeGroupCenter = (function () {
var childBox = new THREE.Box3();
var groupBox = new THREE.Box3();
var invMatrixWorld = new THREE.Matrix4();
return function (group, optionalTarget) {
if (!optionalTarget) optionalTarget = new THREE.Vector3();
group.traverse(function (child) {
if (child instanceof THREE.Mesh) {
if (!child.geometry.boundingBox) {
child.geometry.computeBoundingBox();
childBox.copy(child.geometry.boundingBox);
child.updateMatrixWorld(true);
childBox.applyMatrix4(child.matrixWorld);
groupBox.min.min(childBox.min);
groupBox.max.max(childBox.max);
}
}
});
// All computations are in world space
// But the group might not be in world space
group.matrixWorld.getInverse(invMatrixWorld);
groupBox.applyMatrix4(invMatrixWorld);
groupBox.getCenter(optionalTarget);
return optionalTarget;
}
})();
Here's a fiddle.

Three.js: Calling lookAt() method causes mesh to disappear

I'm trying to use the three.js lookAt() method on a meshes (from CylinderBufferGeometry) so that it is oriented toward a point, but when I use the .lookAt() method, it causes the mesh to disappear from view.
The cylinder shows up fine if I comment out the .lookAt() method. I'm using a THREE.PerspectiveCamera and the THREE.WebGLRenderer incase that could have anything to do with the issue.
// Build cylinder
var cylinderRadius = 0.15
var cylinderHeight = 20
var geometry = new THREE.CylinderBufferGeometry(cylinderRadius, cylinderRadius, cylinderHeight);
var material = new THREE.MeshBasicMaterial({color: 0xffffff});
var cylinder = new THREE.Mesh(geometry, material);
// Point the cylinder up
cylinder.geometry.rotateX( Math.PI / 2);
cylinder.geometry.translate(0,0, cylinderHeight/2 );
// Move cylinder to position
cylinder.position.x = 10;
cylinder.position.y = 10;
// Look at point
cylinder.lookAt(0,0,15); // <-- ISSUE OCCURS HERE
scene.add(cylinder);
render();
Use cylinder.lookAt(new THREE.Vector3(0,0,15)); instead of cylinder.lookAt(0,0,15);

Resources