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.
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()?
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 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?
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();