how to draw polygon in three js using vertices? - three.js

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);

Related

Why is three.js inconsistent about gouraud interpolation?

I want to shade a THREE.BoxBufferGeometry using a simple THREE.MeshLambertMaterial. The material is supposed to use a Lambert illumination model to pick the colors for each vertex (and it does), and then use Gouraud shading to produce smooth gradients on each face.
The Gouraud part is not happening. Instead, the cube's faces are each shaded with one single, solid color.
I have tried various other BufferGeometrys, and gotten inconsistent results.
For example, if instead I make an IcosahedronBufferGeometry, I get the same problem: each face is one single, solid color.
geometry = new THREE.IcosahedronBufferGeometry(2, 0); // no Gouraud shading.
geometry = new THREE.IcosahedronBufferGeometry(2, 2); // no Gouraud shading.
On the other hand, if I make a SphereBufferGeometry, the Gouraud is present.
geometry = new THREE.SphereBufferGeometry(2, 3, 2); // yes Gouraud shading.
geometry = new THREE.SphereBufferGeometry(2, 16, 16); // yes Gouraud shading.
But then if I make a cube using a PolyhedronBufferGeometry, the Gouraud shading doesn't appear unless I set the detail to something other than 0.
const verticesOfCube = [
-1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
-1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
];
const indicesOfFaces = [
2,1,0, 0,3,2,
0,4,7, 7,3,0,
0,1,5, 5,4,0,
1,2,6, 6,5,1,
2,3,7, 7,6,2,
4,5,6, 6,7,4
];
const geometry = new THREE.PolyhedronBufferGeometry(verticesOfCube, indicesOfFaces, 1, 1); // no Gouraud shading
geometry = new THREE.PolyhedronBufferGeometry(verticesOfCube, indicesOfFaces, 1, 1); // yes Gouraud shading
I am aware of the existence of the BufferGeometry methods computeFaceNormals() and computeVertexNormals(). Normals are emphatically important here, as they are used to determine the colors for each face and vertice, respectively. But while they help with the Icosahedron, they have no effect on the Box, no matter whether they are present, only one is present, or both are present in both possible orders.
Here is the code I expect to work:
const geometry = new THREE.BoxBufferGeometry(2, 2, 2);
geometry.computeFaceNormals();
geometry.computeVertexNormals();
const material = new THREE.MeshLambertMaterial({
color: 0xBE6E37
});
const mesh = new THREE.Mesh(geometry, material);
I should be getting a cube whose faces (the real, triangular ones) are shaded with a gradient. First, the face normals should be computed, and then the vertex normals by averaging the normals of the faces formed by them. Here is a triangular bipyramid on which correct Gouraud shading is being applied:
But the code above produces this instead:
At no point does three.js log any errors or warnings to the console.
So what is it that's going on here? The only explanation I can think of is that the Box is actually comprised of 24 vertices, three at each corner of the cube, and that they form faces such that each vertex's computed normal is an average of at most two faces pointing in the same direction. But I can't find that written down anywhere, and that explanation doesn't fly for the Polyhedron where vertices and faces were explicitly specified in code.

Problem in understanding three.js coordinate and axes system

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.

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!

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