Three.js remove holes / inner lines in LineSegments - three.js

I create a Buffergeometry with my data and extract LineSegments to only show the outer lines:
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
const edges = new THREE.EdgesGeometry(geometry);
const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xc4eaff }));
Because there's an 'hole' in the data, I get e result something like this:
Is there an easy way to remove the inner lines in my LineSegments to get e result like this:

Related

Why is texture not rendering on vertices in Three.js (missing UVs)

I have created a scene with THREE.js. Most of the surfaces are BoxGeometries with zero width and a texture applied. Everything works well, including transparent windows on the house. When I went to add roof, I needed to make angled flat panels and triangular spaces. I decided to use a technique borrowed from another StackOverflow page (How to create a custom mesh on THREE.JS?).
The mesh is rendering, but not the texture. I've tried with different texture images and it does change the COLOR of the rendered panel, but still no visible texture. I'm missing something. If I can get this to work, I'll start using more vertex-based meshes to fill in my building. Why isn't the texture rendering?
//texture
var texture = new THREE.ImageUtils.loadTexture("shingles.jpg");
texture["shingles"] = new THREE.MeshBasicMaterial({ map:texture, side:THREE.DoubleSide});
//roof
var v1 = new THREE.Vector3(farRight,level+height,back);
var v2 = new THREE.Vector3(farRight,level+height,front);
var v3 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),front-(3*width/2));
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var geom = new THREE.Geometry();
geom.vertices.push(v1);
geom.vertices.push(v2);
geom.vertices.push(v3);
geom.vertices.push(v4);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faces.push( new THREE.Face3( 3, 0, 2 ) );
var object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
----------------Updated with full answer-------------------------------
As noted in the accepted answer, I missed adding the UV vectors. After some reading I was able to figure out how they work and get the textures to map appropriately. I'm including the full solution here for future reference. First two snippets of code.
var v3 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),front-(3*width/2));
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var v5 = new THREE.Vector3(farLeft,level+height,back);
var v6 = new THREE.Vector3(farLeft,level+height,front);
geom = new THREE.Geometry();
geom.vertices.push(v3);
geom.vertices.push(v4);
geom.vertices.push(v5);
geom.vertices.push(v6);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faces.push( new THREE.Face3( 3, 0, 2 ) );
geom.faceVertexUvs[0].push([new THREE.Vector2(.3, 1),
new THREE.Vector2(.7, 1),
new THREE.Vector2(1, 0)]);
geom.faceVertexUvs[0].push([new THREE.Vector2(0, 0),
new THREE.Vector2(.3, 1),
new THREE.Vector2(1, 0)]);
object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
var v1 = new THREE.Vector3(farRight,level+height,back);
var v4 = new THREE.Vector3((farRight+farLeft)/2,level+(3*height/2),back+(3*width/2));
var v5 = new THREE.Vector3(farLeft,level+height,back);
geom = new THREE.Geometry();
geom.vertices.push(v1);
geom.vertices.push(v4);
geom.vertices.push(v5);
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
geom.faceVertexUvs[0].push([new THREE.Vector2(0, 0),
new THREE.Vector2(.5, 1),
new THREE.Vector2(1, 0)]);
object = new THREE.Mesh( geom, textures["shingles"] );
scene.add(object);
Now a picture and an explanation. The two geometries in the code above are highlighted in the picture. Because I got fancy, vertices 3 and 4 are slightly inward making a trapezoid shape. Likewise the UV vertices that correspond to those points are .3 and .7 inward from the corners to make a trapezoid. The triangular geometry has two points at the base and one at the top-middle.
Basically, create the vertices, add the vertices, define the faces, and (this is the part I missed) add UV vertices corresponding to the locations in the texture.
You will need to add texture coordinates as well.. (aka uv coordinates). There is a field called faceVertexUVs that has to be filled out. You will need to set 0,0 for the top left corner of each face, 1,0 for top right, 0,1 for bottom left and 1,1 for bottom right corner.

Raycaster Set getting Z value for respective XY

I have array of XY coordinates from which i have to get the respective Z positions. I have created the following code to achieve same.
This function loops through array and calls further function to get the Z value.
function generate_section(){
for(var i=0;i<points.length;i++){
//temporary try to get for the same.
var pts = points[i];
var z = sectioncall(pts.x,pts.y);
console.log(pts,z);
}
}
The following function is a raycaster which cast the ray for the provided x& y value and cast a downward ray.
function sectioncall(x,y){ //grabs the Z value for the provided XY
var top = new THREE.Vector3(x, y , 30 );
var bottom = new THREE.Vector3(x , y , -30 );
var direction = new THREE.Vector3();
direction = direction.subVectors( bottom, top ).normalize();
//start raycaster
var raycaster = new THREE.Raycaster();
raycaster.set( top, direction );
// calculate objects intersecting the picking ray
var intersects = rayCaster.intersectObjects(scene.getObjectByName('MyObj_s').children);
var rpt = intersects[0].point;
//draw a line the way ray caster casting the ray
var geometry = new THREE.Geometry();
geometry.vertices.push( top );
geometry.vertices.push( rpt );
var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
var line = new THREE.Line( geometry, material );
scene.add( line );
return rpt;
}
With the above code i get the result like this:
but what i want to achive is the result like this:
So that the returning values will be about for respective XY instead of last XY as you see on the console.
You have slightly made a mistake in the code.
it should be
var intersects = raycaster.intersectObjects(scene.getObjectByName('MyObj_s').children);
you misspelled it as rayCaster.

Raycaster does not find intersect even though it should

I am using a raycaster for a projection of a point onto a face. But somehow that doesn't seem to work. I.e. taking point (25,25,300) and direction (0,0,-1) the raycaster doesn't find and intersect with a box of size (30,30,30) located at (0,0,0). Am I doing sth wrong?
var geometry = new THREE.BoxGeometry(30, 30, 30);
var material = new THREE.MeshBasicMaterial( );
var mesh = new THREE.Mesh(geometry, material);
var dir = new THREE.Vector3(0,0,-1);
var p = new THREE.Vector3(25,25,300);
var raycaster = new THREE.Raycaster(p, dir);
var intersects = raycaster.intersectObjects(mesh); // returns an empty array
There are two problems with your example, first is that you are using the method raycaster.intersectObjects which takes an array as argument, when you should be using raycaster.intersectObject which takes an object.
Secondly, you are missing the mesh.
Try these values: var p = new THREE.Vector3(15,15,300); instead. The image below illustrates the problem..

Three.js: mapping two textures to a cube

It's tough to find an updated answer to this question. Seems like a lot of solutions are using MeshFaceMaterial which is long gone from three.js (around version 53, currently on 84)
From the research I've done, it seems like it's a better strategy to create a Box, turn it into a Sphere and then do the mappings.
Here's what I've got:
I have textures loaded at vars t1 and t2
var geometry = new THREE.BoxGeometry(1,1,1);
for (var i in geometry.vertices) {
var vertex = geometry.vertices[i];
vertex.normalize().multiplyScalar(1);
}
var mat1 = new THREE.MeshLambertMaterial({ map: t1 });
var mat2 = new THREE.MeshLambertMaterial({ map: t2 });
var materials = [mat1, mat2];
var mats = new THREE.MultiMaterial(materials);
var cube = new THREE.Mesh(geometry, mats);
scene.add(cube);
Problems:
This is resulting in Uncaught TypeError: Cannot read property 'visible' of undefined in my console
For shading reasons, I'd like to use a MeshPhongMaterial instead of a MeshLambertMaterial
Any tips would be hugely appreciated!
Take a look at the materialIndex value inside the faces of the geometry. These values are indexes into your materials array.
In your example you will see they have values from 0 to 5 (corresponding to sides of the box), but since only two materials are provided then you'll see an error.
To solve your problem you can just change the materialIndex values on the geometry faces so that they just reference your two materials.
For example:
for (var i = 0; i < geometry.faces.length; i += 1) {
var face = geometry.faces[i];
face.materialIndex = face.materialIndex % 2;
}

Merge two planes with texture into one mesh

I want to merge two meshes in threejs. I want to create a geometry of two planes that are intersecting each other perpendicular. Both of the planes must have the same texture.
I've tried the following.
Currently this error occurs: THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.
var texture = THREE.ImageUtils.loadTexture('/img/foo.png');
var material = new THREE.MeshPhongMaterial( { map: texture, side: THREE.DoubleSide, transparent: true });
var m_plane_1 = new THREE.PlaneGeometry( 128, 128);
var m_plane_2 = new THREE.PlaneGeometry(128, 128);
var plane_1 = new THREE.Mesh(m_plane_1, material);
var plane_2 = new THREE.Mesh(m_plane_2, material);
plane_2.rotation.y = Math.PI / 2;
var combined = new THREE.Geometry();
combined.merge(plane_1); // does not work
//combined.merge(plane_1.geometry, plane_1.matrix); // this does not work
//combined.merge(m_plane_1.geometry, m_plane_1.matrix); // this does not work
scene.add(combined);
I've tried to read the source code for merge() but could not come to any conclusion. I've read stackoverflow threads but their approach does not work for me.
How can I solve this?
You want to get the intersection of the geometries. Then you can apply whatever texture you want to that new geometry.
To get the intersection, you should use Chandler Prall's Constructive Solid Geometry code: http://evanw.github.io/csg.js/
There are several ways to merge two geometries, but probably the simplest way to achieve what you want is to use the following pattern:
var geometry = new THREE.PlaneGeometry( 128, 128 );
var geometry2 = new THREE.PlaneGeometry( 128, 128 );
geometry2.applyMatrix( new THREE.Matrix4().makeRotationY( Math.PI / 2 ) );
geometry.merge( geometry2 );
three.js r.69
Try this:
plane_1.updateMatrix();
//Now the function merge
combined.merge(plane_1.geometry, plane_1.matrix);
//combined.merge(plane_1); // does not work
//combined.merge(plane_1.geometry, plane_1.matrix); // this does not work
//combined.merge(m_plane_1.geometry, m_plane_1.matrix); // this does not work

Resources