Faces on BufferGeometry

I’m trying to draw the just the faces of my path outline, like a wall. In “geometry” this was happening with vertices. But now, in BufferGeometry and uvs the faces are a little different. What’s the proper way to convert this function now?

Previous Method:

var ngeometry = new THREE.SphereGeometry();

function assignUVs( geometry ) {
    
    geometry.faceVertexUvs[ 0 ]  = [];
    geometry.faces.forEach( function( face ) {
     var uvs = [];
     var ids = [ 'a', 'b', 'c'];
     for( var i = 0; i < ids.length; i++ ) {
          var vertex = geometry.vertices[ face[ ids[ i ] ] ].clone();
          var n = vertex.normalize();
          var yaw = .5 - Math.atan( n.z, - n.x ) / ( 2.0 * Math.PI );
          var pitch = .5 - Math.asin( n.y ) / Math.PI;
          var u = yaw,
          v = pitch;
          uvs.push( new THREE.Vector2( u, v ) );
     }
    });
}

// then trace the points in my path
for( var i = 0; i < points.length; i++ ) {

    ngeometry.vertices.push( points[ i ] ),  
    ngeometry.faces.push( new THREE.Face3( 0, i + 1, i  ));
  
}
                      
assignUVs( ngeometry );
ngeometry.computeVertexNormals();

Attempts:

var ngeometry = new THREE.SphereBufferGeometry().setFromPoints( positions );

function assignUVs( geometry ) {

    const uvAttr = geometry.getAttribute( 'uv' );
    const uvs = new THREE.Vector2();
    for ( let i = 0; i < uvAttribute.count; i += 3 ) {
      
      const uv = new THREE.Vector3( uvAttribute[ i ], uvAttribute[ i + 1 ], uvAttribute[ i + 2 ] );
      uvAttribute[ i ]     = uv.x
      uvAttribute[ i + 1 ] = uv.y
      uvAttribute[ i + 2 ] = uv.z

      var yaw = .5 - Math.atan( uv.z, - uv.x ) / ( 2.0 * Math.PI );
      var pitch = .5 - Math.asin( uv.y ) / Math.PI;
      var u = yaw,
      v = pitch;
      .... ?
    }
}

I know I’m making this harder than it is. I’m getting results but not what I want. My faces look like they are pile of jagged rocks.

Can you show us the desired result? I’m not sure I understand what you mean by just drawing the faces of a path outline.

Sure. This is a model I created in r115. I converted the model but lost the walls on my countries. I was drawing the walls as a separate mesh using the xyz points from my country path.

I did this by assigning the path as faces to a spheregeometry. Of course the old method iterates over the vertices which dont exist in buffergeometry and the faces array also doesnt exist. How do I do this in current method? (r133)

Oh, also, the dataSet I’m using for the country data has points for (0,0,0) where a line ends. So all of my vertices in my geometry start from 0 and go outward. Obviously im drawing a lot of face I dont need. If I remove the (0,0,0) then I get a continuous line with no stops which also is not desirable. I’ll create another post for that though if I cant figure it out. For now, how to draw the path as walls on my SphereBufferGeometry.fromPoints()?

Thank You!

You can still access the vertices by getting the "position" bufferAttribute.

const geom = new THREE.SphereBufferGeometry(20.1, 200, 200);

// This gets the array of all positions [x, y, z, x, y, z, x, y z,...]
const positions = geom.getAttribute("position").array;

// This gets # of vertices
const vertexCount = geom.getAttribute("position").count;

// We'll store each vertex in this Vec3
const singleVertex = new THREE.Vector3();

// Each loop counts up by 3
for (let i3 = 0; i < vertexCount; i3 +=3) {
	singleVertex.set(
		positions[i3 + 0],
		positions[i3 + 1],
		positions[i3 + 2]
	);

	console.log(singleVertex.toArray().toString());
}

Once you have the vertex positions, you could get each face by reversing the method used to create them. For example, when you have 4 vertices a, b, c, d, then face1 is made up of the a, b, d triangle and face2 is b, c, d:

const faces = geom.getIndex().array;

const a = 0, b = 1, c = 2, d = 3;
// 1st triangle
faces[a]
faces[b]
faces[d]

// 2nd triangle
faces[b]
faces[c]
faces[d]

// ... etc

I didn’t get this line. You create an indexed buffer geometry for ~40K vertices, then re-write its position attribute with new values, and I doubt that it has the same amount of vertices; but index of the geometry is intact :thinking: Highly likely you’ll get a very weird visual result.

Hello. Well actually youre right, I copied a piece of code for the example. The geometry I’m actually using, and the one from the previous model have no size or vertices. It eventually comes from what I put into it with the array. The one in use is just SphereBufferGeometry(); Sorry about that, I’ll fix the post.

Original Code:

ngeometry = new THREE.SphereGeometry();

...

for ( let feature in ... ) {

     geoFeature = geojson.features[ ...];
     coordinates = geoFeature.geometry.coordinates;

     for ( let coordinate... ) {
                                        
           multipolygons = coordinates[ ... ];
           polygons = coordinates[ ... ];
                                    
           for ( let coord in multipolygons ){
                                                       
                linePoint = returnSphericalCoordinates( [[ ... ], multipolygons[ ... ]], distance );
                lineInnerPoint = returnSphericalCoordinates( [ ],  ], radius );
                if( !isNaN( linePoint.x ) && !isNaN( linePoint.y ) && !isNaN( linePoint.z ) )
                  
                    lineGeometry.vertices.push( linePoint );
                    point = new THREE.Vector3( lineInnerPoint.x, lineInnerPoint.y, lineInnerPoint.z );                                      
                    points.push( lineInnerPoint );
                }
                                                

                 for ( let coord in polygons ){
                     
                       linePoint = returnSphericalCoordinates( [ polygons[ coord ][ 1 ], polygons[ coord ][ 0 ] ], distance );
                       lineInnerPoint = returnSphericalCoordinates( [ polygons[ coord ][ 1 ], polygons[ coord ][ 0 ] ], 209 );
                       if( !isNaN( linePoint.x ) && !isNaN( linePoint.y ) && isNaN( linePoint.z ) )
                           lineGeometry.vertices.push( linePoint );                           
                           point = new THREE.Vector3( lineInnerPoint.x, lineInnerPoint.y, lineInnerPoint.z );  
                           points.push( lineInnerPoint );
                        }
                  }   
            } 
      }
      for( var i = 0; i < points.length; i++ ) {

            ngeometry.vertices.push( points[ i ] ), 
            ngeometry.faces.push( new THREE.Face3( 0, i + 1, i  )); 
  
      }

      assignUVs( ngeometry );
      ngeometry.mergeVertices();  
      ngeometry.computeVertexNormals();       
                    
      var nline = new THREE.Mesh( ngeometry, globeMaterial );
          nline.rotation.y = Math.PI; 
          nline.position.set( 0, 0, 0 ); 
          nline.scale.set( 1, 1, 1 ); 
          target.add( nline );

           

I was attempting to avoid iterating over the points by creating the geometry from points and then assign the faces along its vertices ( positions ). Is that possible? Or do I have to literally retrace the points and create from the point array?

A triplet of numbers in .index defines a face by indices of vertices. That’s what @marquizzo above said.
Or, without index, a triplet of vertices defines a face.

This is great and thank you! I think I had gotten to this point:

i.e. -

    const position = geometry.getAttribute( 'position' );
    const uvs = new THREE.Vector2();
    for ( let i = 0; i < position.count; i += 3 ) {
      
      const uv = new THREE.Vector3( position[ i ], position[ i + 1 ], position[ i + 2 ] );
      position[ i ]     = uv.x
      position[ i + 1 ] = uv.y
      position[ i + 2 ] = uv.z

However, if I create the geometry from points can I then not just assign the faces to the geometry? Or do I have to literally iterate over each point and create the faces? Is there an easier way to do this? In the original example I set the faces, and the geometry, at the same time, per each point of my point array. However, if instead I use SphereBufferGeometry().fromPoints( pointsArray ); then can I just skip the iteration and simply assign faces ( a.k.a - material) to its geometry? Is there a way to do that? If I set geometry from points I should be able to just assign a material to the mesh, no? As a 2D vector instead of a Vector3, that way I only get the faces on the edges. Right?

So, how do I set the faces to the geometry as a 2D vector so I only get the side faces? Iteration only?

@prisoner849 as well.

I’m sorry, what I think in my head, and what comes out, sometimes arent the same. I overthink what I am trying to say. Thanks for your patience. I already have the faces of the countries as a separate layer on my model. I dont want to draw the front faces of the countries. I only want to draw the sides. So a mesh of only the side faces of the geometry. If I create my geometry fromPoints( points ), then the geometry is technically the path Im looking for (I think) so then I should be able to assign a material to that geometry that only applies the “2D” faces to that?

There, I think I said that right. I was thinking that I there might be a way to just apply the material to the geometry and not have to iterate over each index[ i * 3 ] and apply faces one-by-one like before.

So the faces are Vector3() of course but like

const uv = new THREE.Vector2( a, b );

and a face would be Vector3( uv.x, uv.y, 0 ). z will always be 0; All the faces are then vertical from 0;

You don’t have to index your vertices. Indexing just lets you re-use vertices to make your arrays smaller and more efficient, but they’re not mandatory. For example:

Indexed quad (4 verts):

Face1: a, b, d
Face2: b, c, d

Non-indexed quad (6 verts):

Face1: a, b, c
Face2: d, e, f

The benefits of indexing become greater when you’re dealing with meshes that create a grid, because you can re-use a vertex many times. But it’s not mandatory.

@marquizzo - I have the faces. How do I apply the faces now?

   var ngeometry = new THREE.SphereBufferGeometry().setFromPoints( points ) ;

   // This gets the array of all positions [x, y, z, x, y, z, x, y z,...]
   const positions1 = ngeometry.getAttribute("position").array;
   const uvArr = ngeometry.getAttribute("uv").array;
   const vertexCounts = ngeometry.getAttribute("position").count;

   // We'll store each vertex in this Vec3
   const singleVertex = new THREE.Vector3();

   // Each loop counts up by 3
   for (let i3 = 0; i3 < vertexCounts; i3 +=3) {
        var uvs = [];
        singleVertex.set(
           positions1[i3 + 0],
           positions1[i3 + 1],
           positions1[i3 + 2]
        );

        var vertex = singleVertex.clone();
        var n = vertex.normalize();
        var yaw = .5 - Math.atan( n.z, - n.x ) / ( 2.0 * Math.PI );
        var pitch = .5 - Math.asin( n.y ) / Math.PI;
        var u = yaw,
        v = pitch;
        uvs.push( new THREE.Vector2( u, v ) );

}
  // uvArr.push( uvs ); // <-- apply faces ???
 ngeometry.computeVertexNormals();             

Used to be

geometry.faceVertexUvs[ 0 ]  = [];
geometry.faceVertexUvs[ 0 ].push( uvs );

What’s the replacement for that?