Three JS Pivot point - javascript

What I'm trying to achieve is a rotation of the geometry around pivot point and make that the new definition of the geometry. I do not want te keep editing the rotationZ but I want to have the current rotationZ to be the new rotationZ 0.
This way when I create a new rotation task, it will start from the new given pivot point and the newly given rad.
What I've tried, but then the rotation point moves:
// Add cube to do calculations
var box = new THREE.Box3().setFromObject( o );
var size = box.getSize();
var offsetZ = size.z / 2;
o.geometry.translate(0, -offsetZ, 0)
// Do ratation
o.rotateZ(CalcUtils.degreeToRad(degree));
o.geometry.translate(0, offsetZ, 0)
I also tried to add a Group and rotate that group and then remove the group. But I need to keep the rotation without all the extra objects. The code I created
var box = new THREE.Box3().setFromObject( o );
var size = box.size();
var geometry = new THREE.BoxGeometry( 20, 20, 20 );
var material = new THREE.MeshBasicMaterial( { color: 0xcc0000 } );
var cube = new THREE.Mesh( geometry, material );
cube.position.x = o.position.x;
cube.position.y = 0; // Height / 2
cube.position.z = -size.z / 2;
o.position.x = 0;
o.position.y = 0;
o.position.z = size.z / 2;
cube.add(o);
scene.add(cube);
// Do ratation
cube.rotateY(CalcUtils.degreeToRad(degree));
// Remove cube, and go back to single object
var position = o.getWorldPosition();
scene.add(o)
scene.remove(cube);
console.log(o);
o.position.x = position.x;
o.position.y = position.y;
o.position.z = position.z;
So my question, how do I save the current rotation as the new 0 rotation point. Make the rotation final
EDIT
I added an image of what I want to do. The object is green. I have a 0 point of the world (black). I have a 0 point of the object (red). And I have rotation point (blue).
How can I rotate the object around the blue point?

I wouldn't recommend updating the vertices, because you'll run into trouble with the normals (unless you keep them up-to-date, too). Basically, it's a lot of hassle to perform an action for which the transformation matrices were intended.
You came pretty close by translating, rotating, and un-translating, so you were on the right track. There are some built-in methods which can help make this super easy.
// obj - your object (THREE.Object3D or derived)
// point - the point of rotation (THREE.Vector3)
// axis - the axis of rotation (normalized THREE.Vector3)
// theta - radian value of rotation
// pointIsWorld - boolean indicating the point is in world coordinates (default = false)
function rotateAboutPoint(obj, point, axis, theta, pointIsWorld){
pointIsWorld = (pointIsWorld === undefined)? false : pointIsWorld;
if(pointIsWorld){
obj.parent.localToWorld(obj.position); // compensate for world coordinate
}
obj.position.sub(point); // remove the offset
obj.position.applyAxisAngle(axis, theta); // rotate the POSITION
obj.position.add(point); // re-add the offset
if(pointIsWorld){
obj.parent.worldToLocal(obj.position); // undo world coordinates compensation
}
obj.rotateOnAxis(axis, theta); // rotate the OBJECT
}
After this method completes, the rotation/position IS persisted. The next time you call the method, it will transform the object from its current state to wherever your inputs define next.
Also note the compensation for using world coordinates. This allows you to use a point in either world coordinates or local space by converting the object's position vector into the correct coordinate system. It's probably best to use it this way any time your point and object are in different coordinate systems, though your observations may differ.

As a simple solution for anyone trying to quickly change the pivot point of an object, I would recommend creating a group and adding the mesh to the group, and rotating around that.
Full example
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)
Right now, this will just rotate around its center
cube.rotation.z = Math.PI / 4
Create a new group and add the cube
const group = new THREE.Group();
group.add(cube)
scene.add(group)
At this point we are back where we started. Now move the mesh:
cube.position.set(0.5,0.5,0)
Then move the group
group.position.set(-0.5, -0.5, 0)
Now use your group to rotate the object:
group.rotation.z = Math.PI / 4

Related

Get 3D Position of shadow pixels in ThreeJS

I have the following project below created using ThreeJS. You will notice the gold object creates a shadow behind it on a sphere where I'm only rendering the backside so we can see the objects inside. I'm using a point light in the very center of the eye model to create the shadow evenly in all directions. This is the reason the shadow is curved.
I need to know how to get the 3D coordinates (x,y,z) of each pixel of this shadow that was created. For reference here is the code that creates the shadow with a lot removed for simplicity.
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap//THREE.PCFSoftShadowMap;
const light = new THREE.PointLight( 0xffffff, 20, 0 );
light.position.set( 0, 0, 0 );
light.castShadow = true;
light.shadow.mapSize.width = 512;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500;
scene.add( light );
const sphereGeometry = new THREE.SphereGeometry( 25, 32, 32 );
const sphereMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } );
sphereMaterial.side=THREE.BackSide;
const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
sphere.castShadow = false;
sphere.receiveShadow = true;
scene.add( sphere );
I have researched some into this and I think it may be storing the shadow information in the matrix property of the model but this is not clear for sure in any documentation, so I'm not sure where to look to get this information. Any help is appreciated!
--- Extra not important info ---
Also, in case you are curious, the reason I need the shadow coordinates is because I will use those to raycast back into the eye and create a different kind of shadow on an azimuthal equidistant project on the right (it's complicated...), but just know that if I have the 3D coordinates of the shadow pixels I can do this :). I'm already doing it for the muscles of the eye for example.
You can't extract the shadow into a new geometry because this is all calculated in the GPU shaders upon rendertime, so JavaScript doesn't really have access to the shadowMap positions. However, there is a solution.
Assuming your point light is at (0, 0, 0), and it's at the center of the sphere, you could iterate through the vertices of the gold object and project these positions onto the sphere:
// Sphere radius
const radius = 25;
const vec3 = new THREE.Vector3();
// Get the vertex position array
const vertices = goldObject.geometry.getAttribute("position").array;
// Loop that iterates through all vertex positions
for (let i3 = 0; i3 < vertices.length; i3 += 3) {
// Set this vertex into our vec3
vec3.set(
vertices[i3 + 0], // x
vertices[i3 + 1], // y
vertices[i3 + 2] // z
);
// Set vector magnitude to 1
vec3.normalize();
// Set vector magnitude to radius of sphere
vec3.multiplyScalar(sphereRadius);
// Now you have the spherical projection of this vertex!
console.log(vec3);
}
Since the light source is the exact center of the sphere, you could take the position of each vertex of the gold object, normalize it, then multiply it by the radius of the sphere. Now that you have the vec3 on each iteration, you could add it to your own array to build your own THREE.BufferGeometry that's pushed against the sphere.
Of course, if you've translated or rotated the gold object, then that will affect the vertex positions, so you'd have to undo those translations, rotations, etc. when iterating through all the vertices.

Three.js grid Axis label issue and how to plot x,y,z data on the grid

I am supposed to plot the well deviation surveys on a 3D grid. With the help of a few articles on the web, I have accomplished a 3D grid with required size. The current problem I am facing right now is that the labels for x,y and z axis are attached to the grid, rather they are misplaced on the scene.
var labelsH = labelAxis(height, data.labels.y,"y");
labelsH.position.x = width;
labelsH.position.y = - height +(2*height/a)-20;
labelsH.position.z = depth;
scene.add(labelsH);
function labelAxis(width, data, direction){
var separator = 2*width/data.length,
p = {
x:0,
y:0,
z:0
},
dobj = new THREE.Object3D();
for ( var i = 0; i < data.length; i ++ ) {
var label = makeTextSprite(data[i]);
label.position.set(p.x,p.y,p.z);
dobj.add( label );
if (direction=="y"){
p[direction]+=separator;
}else{
p[direction]-=separator;
}
//console.log(p.x+":"+p.y+":"+p.z)
}
return dobj;
}
See the https://jsfiddle.net/3tw3dt1u/ for full code example.
Further more, the data that I need to plot is already in the jsFiddle mentioned above. Having minimal javascript skills, I have no idea how this data will be plotted on the grid to form something like this:
see image for required result
Thanks a lot in advance for any help.
Your question is regarding the plotting of the points, this is how it could be done:
JSFiddle working example
The key points are below.
// Not necessary, but I prefer to use the same scale as they use in their example. It's also easier since the data is according to those scales.
var graphDimensions = {
w:3000,
d:3000,
h:7000
};
var vectors = realData.map(function(d) { // Create vectors from your data
return new THREE.Vector3(d[0], d[1], d[2]);
});
var curve = new THREE.CatmullRomCurve3(vectors); // Create a curve with the vectors created above
var material = new THREE.LineBasicMaterial({color: "blue"}); // Material for the curve
var geometry = new THREE.Geometry();
var splinePoints = curve.getPoints(5000); // The 5000 in here is the resolution (number of points) on the curve. Increase for a smoother curve, decrease for a more jagged curve.
for (var i = 0; i < splinePoints.length; i++) { // Loop through the points to create vertices in the geometry
geometry.vertices.push(splinePoints[i]);
}
var line = new THREE.Line(geometry, material); // Create a line with your geometry and material
scene.add(line); // Add it to the scene
line.rotateX(Math.PI / 2); // Orient the line correctly
boundingGrid.position.set(0, -3.5, 1.5); // Move the grid into position
boundingGrid.scale.set(0.001, 0.001, 0.001); // Reduce by whatever scale you want to decrease it in size (otherwise you have to scroll out forever)
line.scale.set(0.001, 0.001, 0.001);

Dynamically add and rotate a geometry in three.js

Refer https://jsfiddle.net/pmankar/svt0nhuv/
Main large red icosahedron geometry keeps rotating about the y axis. I added a small red sphere geometry and merged it to the main large red icosahedron geometry. Until here it works fine. For this I used, THREE.GeometryUtils.merge(point_sphere_iso_geom, sphere);
However, when I try to add spheres dynamically with a mouse click, they are added (yellow spheres), but they do not rotate with the main large red icosahedron geometry.
Can anyone explain why does it works in the initial case, but not when added dynamically and how to achieve it dynamically as well.
I hope I understood you correctly. Every mouse click you have to create a new geometry based on the previous one (mesh geometry and mesh matrix), merging it with the geometry of a new sphere, and apply it to a new mesh, then remove the old mesh and add the new one.
some changes in vars
var geometry, material, point_sphere_iso_geom, mesh;
in creation of the start merged mesh
point_sphere_iso_geom = new THREE.IcosahedronGeometry(100, 4);
cygeo = new THREE.SphereGeometry(5, 10, 10);
cygeo.translate(0,0,120);
point_sphere_iso_geom.merge( cygeo );
mesh = new THREE.Mesh(point_sphere_iso_geom, material);
and in the function of addYellowPoint
function addYellowPoint(valX, valY) {
var sgeometry = new THREE.SphereGeometry(2.5, 10, 10);
var range = 150;
var x = Math.random() * range - range / 2;
var y = Math.random() * range - range / 2;
var z = Math.random() * range - range / 2;
sgeometry.translate(x,y,z);
point_sphere_iso_geom = mesh.geometry.clone();
point_sphere_iso_geom.applyMatrix(mesh.matrix);
point_sphere_iso_geom.merge(sgeometry);
scene.remove(mesh);
mesh.geometry.dispose();
mesh.material.dispose();
mesh = new THREE.Mesh(point_sphere_iso_geom, material);
scene.add(mesh);
}

rotate around pendulum 0.85 cylinder height

im stuck with a work that i need to do, basicly first i needed to create a pendulum that basicly is 2 spheres and 1 cylinder the 2 spheres are in the up and down a bigger 1 and a smaller 1, what i need to do is the pendulum should move not around his own center but about a center that is 0.85 the height of my cylinder, my idea was to create a pivot point, but since i dont know very good how the pivot point work i tryed a lot of things, i tryed first to add the cylinder to the scene and the spheres to the cylinder, then i created the pivot point and add the pivot point to the cylinder, in the animate function i just tried to rotate the pivot point in the x axis but nothing happens :/
Here is my code guys hope someone can give me a hand
var scene, camera, renderer;
var caixaGrande, caixaPequena1, caixaPequena2,cylinder,Cylinder2,esferaGrande;
var pivotPoint1, pivotPoint2;
const RAIOCILINDRO = 2.5;
const ALTURACILINDRO = 100;
const RAIOESFERAGRANDE = 15;
const RAIOESFERAPEQUENA = 5;
var rotacao = Math.PI/180;
window.onload = function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.up=new THREE.Vector3(0,1,0);
camera.position.set(150,50,50);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// Add a directional light to show off the object
var light = new THREE.DirectionalLight( 0xffffff, 1.5);
// Position the light out from the scene, pointing at the origin
light.position.set(1.5, 1.5, 1);
scene.add( light );
var textureCylindro = new THREE.TextureLoader().load("CylinderTexture.png");
var textureSphere = new THREE.TextureLoader().load("SphereTexture.png");
geometry = new THREE.CylinderGeometry(RAIOCILINDRO,RAIOCILINDRO,ALTURACILINDRO);
material = new THREE.MeshPhongMaterial( {color: 0xffffff , map:textureCylindro} );
cylinder = new THREE.Mesh(geometry,material);
scene.add(cylinder);
pivotPoint1 = new THREE.Object3D();
pivotPoint1.position.y=ALTURACILINDRO*0.15;
cylinder.add(pivotPoint1);
var geometry = new THREE.SphereGeometry(RAIOESFERAGRANDE);
var material = new THREE.MeshPhongMaterial( {color: 0xffffff,map:textureSphere} );
esferaGrande = new THREE.Mesh( geometry, material );
esferaGrande.position.y = -ALTURACILINDRO/2;
cylinder.add( esferaGrande );
var geometry = new THREE.SphereGeometry(RAIOESFERAPEQUENA);
var material = new THREE.MeshPhongMaterial( {color: 0xffffff,map:textureSphere} );
var esferaPequena = new THREE.Mesh( geometry, material );
esferaPequena.position.y = ALTURACILINDRO/2;
cylinder.add( esferaPequena );
Cylinder2 = cylinder.clone();
Cylinder2.position.z = 3 * RAIOESFERAGRANDE;
scene.add(Cylinder2);
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
animate();
}
function animate() {
pivotPoint1.rotation.x += 10;
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
What you should do is create an Object3D that you add the other 3 elements to, then you add that Object3D to the scene. Then when you want to rotate the whole thing, you rotate your Object3D.
So for example:
var axis = new Object3D();
sphere1.position.y = -.15 * pendulumLength;
sphere2.position.y = .85 * pendulumLength;
cylinder.position.y = .35 * pendulumLength;
//assuming the cylinder is pendulumLength long, this^ puts it right between the two balls
axis.add(sphere1);
axis.add(sphere2);
axis.add(cylinder);
scene.add(axis);
then in your animate() function, simply rotate your Axis:
axis.rotation.z += .01;
EDIT:
So, here is my bad drawing of what happens. If you look here, the circle when positioned at 0,0 when rotated, rotates around the axis. Again when you move it up to 1,1 it rotates around its center point, because rotation is relative to the center point of the circle.
Again the same is true of the cylinder. At 0,0 it rotates around its center. At .5,.5 it also rotates around its center. It doesn't care where it is, it will rotate around its position point.
So if we want to rotate these two as a group relative to some other point, we need to make them the children of another object, because when we move the parent, the children maintain their relationship to the parent, even though their positions are still 1,1 and .5,.5 respectively.
The reason that they get rotated in the manner on the right is because their relationship to the parent object is 1,1 and .5,.5 relative to a parent object with a rotation of 0 radians. When we rotate the parent object by some number of radians, they need to move in order to maintain their original relationship.
Another way to think of it: You have a wooden board with a nail perfectly through the center and you have a shoe. You set the shoe in the upper right hand corner of the board with the toe pointing away from you. If you rotate the wooden board Math.PI/2 radians, the shoe will not stay in the upper right hand corner (relative to you), even though that's where you put it, because the shoe has been added to the board, and you have moved the board. Now, the shoe should be in the bottom right, and will be facing to your right. Object3D.add() is like the process of putting the shoe on the board.

Raycast doesnt hit mesh when casted from the inside

I have set up a simple scene where I have my camera inside a sphere geometry
var mat = new THREE.MeshBasicMaterial({map: THREE.ImageUtils.loadTexture('0.jpg') , overdraw:true, color: 0xffffff, wireframe: false });
var sphereGeo = new THREE.SphereGeometry(1000,50,50);
var sphere = new THREE.Mesh(sphereGeo,mat);
sphere.scale.x = -1;
sphere.doubleSided = false;
scene.add(sphere);
I set up a funcionality where I can look around inside that sphere and my point is to be able to cast a ray on mouse down, hit the sphere and get the coordinates where that hit occured. Im casting a ray but still the intersects are empty.
var vector = new THREE.Vector3();
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
vector.unproject( camera );
raycaster.ray.set( camera.position, vector.sub( camera.position ).normalize());
var intersects = raycaster.intersectObjects(scene.children, true);
Everything works with a test cube also put inside my sphere.
My question is, does it matter whether you hit the object from the inside or no ? Because that is the only explanation that comes to my mind.
Thanks in advance.
sphere.doubleSided was changed to sphere.material.side = THREE.DoubleSide some years ago.
It does matter if you hit the object from the inside. Usually a ray will pass through an "inverted" surface due to backface culling which happens on the pipeline level.
Inverted/flipped surfaces are usually ignored in both rendering and raycasting.
In your case, however, i'd go ahead and try setting sphere.doubleSided = false; to sphere.doubleSided = true;. This should make the raycast return the intersection point with your sphere. [shouldn't work with negative scale]
You can also enter the "dirty vertices" mode, and flip the normals manually:
mesh.geometry.dynamic = true
mesh.geometry.__dirtyVertices = true;
mesh.geometry.__dirtyNormals = true;
mesh.flipSided = true;
//flip every vertex normal in mesh by multiplying normal by -1
for(var i = 0; i<mesh.geometry.faces.length; i++) {
mesh.geometry.faces[i].normal.x = -1*mesh.geometry.faces[i].normal.x;
mesh.geometry.faces[i].normal.y = -1*mesh.geometry.faces[i].normal.y;
mesh.geometry.faces[i].normal.z = -1*mesh.geometry.faces[i].normal.z;
}
mesh.geometry.computeVertexNormals();
mesh.geometry.computeFaceNormals();
I also suggest you set scale back to 1.0 instead of -1.0.
Let me know if it worked!

Categories