Problem in understanding three.js coordinate and axes system - three.js

I have following code:
// coordinate values
var x1 = -815723.5125568421;
var y1 = 20538442.534868136;
var z1 = -17.439584224846456;
var x2 = -815723.5125568421;
var y2 = 20538443.164575472;
var z2 = -16.620415776398275;
// make a rectangular face parallel to y-z plane
var dummySquare = new THREE.Geometry();
dummySquare.vertices.push(new THREE.Vector3(x1,y1,z1));
dummySquare.vertices.push(new THREE.Vector3(x1,y1,z2));
dummySquare.vertices.push(new THREE.Vector3(x2,y2,z1));
dummySquare.vertices.push(new THREE.Vector3(x2,y2,z2));
dummySquare.faces.push(new THREE.Face3(0,1,2));
dummySquare.faces.push(new THREE.Face3(1,2,3));
var dummySquareMaterial = new THREE.MeshBasicMaterial( { color: "#0000FF", side: THREE.DoubleSide } );
var dummySquareMesh = new THREE.Mesh(dummySquare, dummySquareMaterial);
So, I am making a rectangular face parallel to y-z plane.
During debugging I observe following:
vertices: Array(4)
0: p {x: -815723.5125568421, y: 20538442.534868136, z:
-17.439584224846456}
1: p {x: -815723.5125568421, y: 20538442.534868136, z:
-16.620415776398275}
2: p {x: -815723.5125568421, y: 20538443.164575472, z:
-17.439584224846456}
3: p {x: -815723.5125568421, y: 20538443.164575472, z:
-16.620415776398275}
position: p {x: 0, y: 0, z: 0}
So vertices are as expected. But position is at (0,0,0). I expected position to be mid point of the plane defined by above four vertices.
What is missing here in my understanding?
Another observation is as follows.
I make two faces just like above(same vertices).
For one of the two faces, I determine centre of geometry, move geometry it to origin(translate by negative of centre), create a mesh with it which then I move back to original position:
var face = new THREE.Geometry();
....add vertices as code snippet above
var faceCentre = new THREE.Vector3();
face.boundingBox.getCenter(faceCentre );
face.translate(-faceCentre .x,-faceCentre .y,-faceCentre .z);
//make mesh
var faceMaterial = new THREE.MeshBasicMaterial( { color: "#FF0000", side:
THREE.DoubleSide } );
var faceMesh= new THREE.Mesh(face, faceMaterial);
// move mesh back by setting its position to original centre of face
faceMesh.position.x = faceCentre .x;
faceMesh.position.y = faceCentre .y;
faceMesh.position.z = faceCentre .z;
Unmoved face has same vertices as for face above, as expected.
But other face has now totally different vertices, even though both are displayed at same position and in same orientation.
Why this difference in vertices?

THREE.js uses a hierarchical representation of objects and their translation. In particular, the .position of an Object3D is not generally, as you say you expected, its middle point in world space, but it can be viewed as a variable that stores the current translation of the object. It is called its local position. This translation is (0,0,0) on default.
So when you define an object by the vertices of its geometry, the object's vertices will render at these positions. However, if you .translate() it by a factor (dx, dy, dz), then a vertex (vx, vy, vz) will render at position (vx+dx, vy+dy, vz+dz).
Similarly, other transformations are also stored as members of the object. The vertices of the geometry do not change when an object is transformed, but instead the object keeps track of its current local transformations, which are applied, typically as a series of matrix multiplications, to the vertices. Using this logic, you can define a tree of objects inside each other, which have local transformations in relation to its parent, which in turn may be transformed in relation to its parent etc. This sort of representation proves very useful for slightly more complicated scenes.
This should explain your results. For example, in your first test, you are successfully creating an object exactly where you want it, but its position is still (0,0,0) because it has undergone no transformations.

Related

how to draw polygon in three js using vertices?

I have vertices(x,y,z) of a polygon as input. How can I render a polygon having these vertices in three.js? THREE.Geometry() is removed from three js. how to draw plane polygon with bufferGeometry or any other method ? now when i draw polygon with vertices it drawing incomplete mesh (polygon). following code is used to draw polygon.
const verticesGeometry = new THREE.BufferGeometry().setFromPoints(measurement.coordinates.map((coord) => new THREE.Vector3(coord.x, coord.y, coord.elevation)))
const polygon = new THREE.Mesh(verticesGeometry , new THREE.MeshBasicMaterial({ color: measurement.color, side: THREE.DoubleSide}))
scene.add(polygon)
screenshots attached of issue, which is i am facing right now, (3 points polygon working perfectly, more than it, rendering incomplete.) thanks in advance.
i also tried THREE.ShapeGeometry() but polygon are rendering to the bottom because THREE.shape() is accepting only VECTOR2 points.i am passing vector3 but it neglecting 3rd (z) point.
let polyShape = new THREE.Shape(measurement.coordinates.map((coord) =>
new THREE.Vector3(coord.x, coord.y, coord.elevation)))
const geometry = new THREE.ShapeGeometry( polyShape )
let polygon = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: measurement.color, side: THREE.DoubleSide }))
see the below image for reference.
You're just giving your points to a Mesh. They are interpreted as triples, each of which represent a triangle. If you want to render a filled polygon, it must be triangulated.
Easier though, is probably to use ShapeGeometry, see the doc in the link. This should do exactly what you need, if you can take the time to learn its interface.
If you want to make the vertices follow the height of your terrain, you can then edit the height coordinates of all vertices in the shape geometry (see how to update a buffer geometry; notice that ShapeGeometry extends BufferGeometry) back with its original Z or whatever your up direction is. The mesh topology will follow because it's already triangulated, and since you don't seem to have massive height variation, it should work nicely.
If you want to triangulate yourself instead of using ShapeGeometry, Three.js also provides a helper for this here.
As per the #berthur answer I tried following code and it worked. Thanks to https://stackoverflow.com/users/10559142/berthur
let coordinates = [
{
x : 1,
y : 1,
elevation : 10
},
{
x : 2,
y : 1,
elevation : 10
},
{
x : 2,
y : 2,
elevation : 10
},
{
x : 1,
y : 2,
elevation : 10
}
]
let polyShape = new THREE.Shape(coordinates.map((coord) => new THREE.Vector2(coord.x, coord.y)))
const polyGeometry = new THREE.ShapeGeometry(polyShape);
polyGeometry.setAttribute("position", new THREE.Float32BufferAttribute(coordinates.map(coord => [coord.x, coord.y, coord.elevation]).flat(), 3))
let polygon = new THREE.Mesh(polyGeometry, new THREE.MeshBasicMaterial({ color: "ColorYouWant, side: THREE.DoubleSide}))
scene.add(polygon);

Generate shape in three.js from multiple elements

Is there a way in three.js to create a poly from multiple individual elements, rectangle for example.
I have attached an example.
I am using:
for(i = 0; i<5; i++){
var rand = Math.floor(Math.random() * 50)+1000;
var material = new THREE.MeshBasicMaterial({
color : "#ff"+i+ rand,
side : THREE.DoubleSide,
transparent : true,
opacity : 1
});
var mesh = new THREE.Mesh( geometry, material );
if(angle) mesh.rotation.y = angle;
mesh.position.set( loop+1, 4,4);
scene.add( mesh );
}
When I apply roatation mesh.rotation.y = angle; it doesn't come up with my below design, I rather get a cross + because the panel rotates on it's y from center, not from corner...
Thank you
The
There are 3 ways to achieve what you're trying to do. The problem you are facing stems from transform origin, as you noted, origin defaults to position [0,0,0]. So, your options are:
build a transform matrix using a different transform offset for rotation, this is probably an overkill for simple use-cases.
translate geometry to not be centered on [0,0,0], for example you can move the whole quad (your geometry) right so that the left edge of the quad aligns with [0,0,0], then, when you rotate, left edge will stay put.
embed Mesh inside a Group, rotate the Mesh and translate (position.set(....)) the Group.
no matter which route you take - you will still have to deal with the some trigonometry as you will need to compute the position for the next segment to align with the edge of the previous one.
One more way around that is to build the following type of structure
Group[
Mesh 1,
Mesh 2,
Mesh 3,
Group [
Mesh 4,
Mesh 5,
Mesh 6,
Group [
Mesh 7
]
]
]
Last group is unnecessary, it's there purely for consistency.
As far as the trigonometry that I mentioned - it's simple Sin and Cos stuff, so it should be quite simple. Here is some pseudo-code that you'll need:
prevPosition, prevAngle //position and angle of previous segment
// Compute next segment transform
nextPosition.x = Math.cos(prevAngle)*segmentSize + prevPosition.x;
nextPosition.z = Math.sin(prevAngle)*segmentSize + prevPosition.z;

How to create geometry for thick lines with three.js?

I need to create thick lines in 3d that receive shadows. The lines are on a flat plane as in y is the same for all points. The lines all face up. An example would be the yellow line on a road.
I think the best way would be to create the geometry and use MeshLambertMaterial so that it would receive shadows.
Can anyone point me in the right direction to create the geometry from points? I assume I need to create the vertices with BufferGeometry.
I'm not quite sure if you already have an array of points (coordinates) for each vertex of the lines and want to create the line from them, or if you just want to create lines of a width and length that you can specify as your question is quite unclear, so I'll provide an example for the latter (and more simple) option.
If you don't have any specified vertices, you can create an object that looks like a "line" using a PlaneGeometry and specifying it's width and height.
i.e.
var geomLine = new THREE.PlaneGeometry(width, height, widthSegments, heightSegments);
var matLine = new THREE.MeshPhongMaterial({color: 0xFF0000});
var Line = new THREE.Mesh(geomLine, matLine);
Line.receiveShadow = true;
Line.castShadow = true;
scene.add(Line);
If you want the line to have some height and not be a flat plane, you could use a BoxGeometry instead of a LineGeometry. The code above would almost be the same, the only difference being you would also need to specify a depth for the box geometry, for example:
geomLine = new THREE.BoxGeometry(width, height, depth);
Bare in mind that the height for the BoxGeometry is how tall it is (on the y axis, the depth value is how "long" it is on the z axis)
EDIT: Here's how to create a polygon with pre-defined vertices
You can create a polygon from vertices with the following code:
var geom = new THREE.Geometry();
var v1 = new THREE.Vector3(0,0,0);
var v2 = new THREE.Vector3(0,500,0);
var v3 = new THREE.Vector3(0,500,500);
geom.vertices.push(v1);
geom.vertices.push(v2);
geom.vertices.push(v3);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.computeFaceNormals();
var object = new THREE.Mesh( geom, new THREE.MeshNormalMaterial() );
scene.add(object);
Copy and paste this code in and then change x, y, and z coordinates of v1, v2, and v3 (or however many vertices you need) to the coordinates of your vertices.
Essentially you are creating vertices using THREE.Vector3 to supply the coordinates and then pushing them to the vertices property of an empty THREE.Geometry();
Code is from this answer
Hope this is what you were looking for!

Access to faces in BufferGeometry

geometry.faces accessible only for new THREE.BoxGeometry. Then I try to use THREE.BoxBufferGeometry I can't change color for faces.
Not working:
var geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
for ( var i = 0; i < geometry.faces.length; i ++ ) {
geometry.faces[ i ].color.setHex( Math.random() * 0xffffff );
}
Working:
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
for ( var i = 0; i < geometry.faces.length; i ++ ) {
geometry.faces[ i ].color.setHex( Math.random() * 0xffffff );
}
BufferGeometries in three.js are fundamentally different from the regular Geometries. They are not oriented towards ease of manipulation but rather towards how meshes need to be delivered to the WebGL API.
That being said, there is no explicit notion of "faces" for BufferGeometries, they are implicit. A BufferGeometry consists of a number of attributes (for background see here), one of them is the position-attribute.
In a regular BufferGeometry (as opposed to "indexed"), the faces are stored as sequences of three vertices within that attribute (something like [x1, y1, z1, x2, y2, z2, x3, ...], so for the first face position[0] is the x-component of the first vertex and position[8] is the z-component of the third vertex). All other attributes use a similar indexing-scheme. If you define an attribute color for the geometry, you can control the face-colors by writing the same color-value at the positions of all three face-vertices (so in this example a color-attribute with [r, g, b, r, g, b, r, g, b, ...] would assign the same rgb-value to the three vertices of the first triangle).
Indexed geometries are different: Instead of repeating the vertices for all triangles, every vertex is stored only once. An additional attribute index is used to connect the vertices into triangles. So an index-attribute might look like this: [0, 1, 2, 0, 2, 3, ...] which reads as "construct first triangle from vertices at positions 0, 1 and 2" and so on.
As this is a very efficient way of storing geometries, this technique is used with most (maybe even all) of the builtin geometries in three.js.
With indexed geometries it is not possible to have colors per face-vertex because the vertex must have the same color everywhere it is used. You can however use bufferGeometry.toNonIndexed() to convert an indexed geometry into a regular one.
All necesarry here https://threejs.org/docs/index.html#api/en/core/BufferGeometry
See examples: Mesh with non-indexed faces, Mesh with indexed faces...
and i think that little example be more useful:
const geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
const colorsAttr = geometry.attributes.position.clone();
// Faces will be colored by vertex colors
geometry.setAttribute('color', colorsAttr);
const material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors
});
const cube = new THREE.Mesh( geometry, material );

Rotate a plane on its edge in three.js

I wanted to rotate a plane, but I can't figure out how to set the rotation axis. I'd like to rotate a plane around its edge.
I've seen solutions suggesting matrix transformations but they lacked explanation so I couldn't apply them.
Ok I figured it out. What you have to do is create a parent 3D object and add the plane to it. Once, added, you have to translate it by 50% and start rotating the parent object.
var object = THREE.SceneUtils.createMultiMaterialObject( new THREE.PlaneGeometry( 200, 50, 4, 4 ), [material] );
var parent = new THREE.Object3D();
object.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 25, 0 ) );
parent.add(object);
parent.rotation.x = 1;
scene.add(parent)

Resources