How to create geometry for thick lines with three.js? - 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!

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

Three.js - get terrain height (position.y) of the mesh at specific position.x,z - without mouse and raycaster?

Let's say I have a sort of rather simple terrain from Blender exported as GLB object, which is a Group and contains a Mesh with BufferGeometry. I have another GLB object which is a model of vehicle. How can I read proper position.y at specific x,z locations (idealy 4 locations for setting car position and rotation) without moving mouse and using raycaster? I need to know what is elevation and height at specific region. Any simple clue without game-physics engine on top of ThreeJS?
Just use a Raycaster. I don't know why you don't want to use it, it's the easiest way to find an intersection without a physics engine and without tons of math.
Simply use Raycaster.set() to point straight down from your XZ coords and see where it intersects the terrain:
var ray = new THREE.Raycaster();
var rayPos = new THREE.Vector3();
// Use y = 100 to ensure ray starts above terran
rayPos.set(x, 100, z);
var rayDir = new THREE.Vector3(0, -1, 0); // Ray points down
// Set ray from pos, pointing down
ray.set(rayPos, rayDir);
// Check where it intersects terrain Mesh
let intersect = ray.intersectObject(terrainMesh);
console.log(intersect);
See here for the intersect object. It includes the point in space where the intersection takes place.

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.

Make text always appear orthogonal to the plane when rotating a cube

I'm creating text labels that appear on a 3D cube using the following pattern:
canvas = createTextCanvas(text, color, font, size);
texture = new THREE.Texture(canvas);
geom = new THREE.PlaneBufferGeometry(canvas.width, canvas.height, segW, segH);
material = new THREE.MeshBasicMaterial({map: texture, transparent: true});
mesh = new THREE.Mesh(geom, material);
mesh.position.x = x;
mesh.position.y = y;
mesh.position.z = z;
texture.needsUpdate = true;
The labels and their positions get set within a for loop for each edge of the cube. This results in labels appearing similar to this:
But then when I rotate the cube (using OrbitControls), you'll see that the label no longer appears vertically like above:
So using the Cost label as an example, I would want the text to remain vertically oriented whenever the cube is rotated. Basically, I'm trying to mimic the behavior of axis labeling in VTK.
So I believe the solution here is to set the up vector of the label to a vector that's always orthogonal to the plane. But I'm not sure how to implement this. Any suggestions or examples would be greatly appreciated.
If it helps, I'm constructing the cube using a BoxGeometry and MeshNormalMaterial.
Do you mean the label keeps moving with the cube or not?
If not, there is a example: http://stemkoski.github.io/Three.js/Sprite-Text-Labels.html. The label keeps facing to you but may not vertical.
Else ,you may need a canvas texture,the label is a object just like the cube and you can set its position to keep it vertical.But it doesn't look good sometime.the example:http://stemkoski.github.io/Three.js/Texture-From-Canvas.html.
I think you just want the label always facing to you when you change your sight.

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