I know THREE.Geometry was deprecated in favor of THREE.BufferGeometry(). However, there doesn't seem to be any clear information about how to implement what was once "Face3()". My understanding is that it was somehow related to the Geometry() object, but I'm not sure how because the documentation has been removed.
I'm trying to get some old example running that used Face3().
Could someone provide a simple translation from Face3() to its suggested newer alternative please?
Here's an example to work with:
const points = []
points.push(new THREE.Vector3(-5, 0, 0))
points.push(new THREE.Vector3(5, 0, 0))
points.push(new THREE.Vector3(0, 5, 0))
let geometry = new THREE.BufferGeometry().setFromPoints( points )
var face = new THREE.Face3(0, 1, 2); //Error: Face3 is not a constructor
I've tried reading through this thread but there doesn't seem to be any real information on Face3() here. Also, when I search the threejs.org documentaiton for "face", there are no relevant results.
Appreciate any help!
The change from THREE.Geometry to THREE.BufferGeometry gives three.js a similar representation of the mesh to what WebGL requires, allowing better performance without additional processing overhead. Each 'face' is specified as the indices of three vertices in the vertex list — not as an explicit JavaScript object for each face, which is expensive. An example constructing a new geometry would be:
const geometry = new THREE.BufferGeometry();
// specify vertex positions
geometry.setAttribute( 'position',
new THREE.BufferAttribute( new Float32Array( [
-5, 0, 0, // vertex 1
5, 0, 0, // vertex 2
0, 5, 0 // vertex 3
], 3 ) )
);
// specify triangles, as triplets of indexes into the vertex list.
geometry.setIndex( new THREE.BufferAttribute( [ 0, 1, 2 ], 1 ) );
The index in the example above is optional. If omitted, each triplet of three vertices in the geometry will be considered a unique triangle. The index is needed when you want to share the same vertices among multiple triangles.
See THREE.BufferGeometry documentation for more details.
In some situations it may be useful to construct a THREE.Triangle to temporarily represent a particular face of the mesh. These are not part of the rendering process but may be useful for computations.
const face = new THREE.Triangle()
.setFromAttributeAndIndices( geometry.attributes.position, 0, 1, 2 );
const area = face.getArea();
Related
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.
geometry.faces accessible only for new THREE.BoxGeometry. Then I try to use THREE.BoxBufferGeometry I can't change color for faces.
Not working:
var geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
for ( var i = 0; i < geometry.faces.length; i ++ ) {
geometry.faces[ i ].color.setHex( Math.random() * 0xffffff );
}
Working:
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
for ( var i = 0; i < geometry.faces.length; i ++ ) {
geometry.faces[ i ].color.setHex( Math.random() * 0xffffff );
}
BufferGeometries in three.js are fundamentally different from the regular Geometries. They are not oriented towards ease of manipulation but rather towards how meshes need to be delivered to the WebGL API.
That being said, there is no explicit notion of "faces" for BufferGeometries, they are implicit. A BufferGeometry consists of a number of attributes (for background see here), one of them is the position-attribute.
In a regular BufferGeometry (as opposed to "indexed"), the faces are stored as sequences of three vertices within that attribute (something like [x1, y1, z1, x2, y2, z2, x3, ...], so for the first face position[0] is the x-component of the first vertex and position[8] is the z-component of the third vertex). All other attributes use a similar indexing-scheme. If you define an attribute color for the geometry, you can control the face-colors by writing the same color-value at the positions of all three face-vertices (so in this example a color-attribute with [r, g, b, r, g, b, r, g, b, ...] would assign the same rgb-value to the three vertices of the first triangle).
Indexed geometries are different: Instead of repeating the vertices for all triangles, every vertex is stored only once. An additional attribute index is used to connect the vertices into triangles. So an index-attribute might look like this: [0, 1, 2, 0, 2, 3, ...] which reads as "construct first triangle from vertices at positions 0, 1 and 2" and so on.
As this is a very efficient way of storing geometries, this technique is used with most (maybe even all) of the builtin geometries in three.js.
With indexed geometries it is not possible to have colors per face-vertex because the vertex must have the same color everywhere it is used. You can however use bufferGeometry.toNonIndexed() to convert an indexed geometry into a regular one.
All necesarry here https://threejs.org/docs/index.html#api/en/core/BufferGeometry
See examples: Mesh with non-indexed faces, Mesh with indexed faces...
and i think that little example be more useful:
const geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
const colorsAttr = geometry.attributes.position.clone();
// Faces will be colored by vertex colors
geometry.setAttribute('color', colorsAttr);
const material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors
});
const cube = new THREE.Mesh( geometry, material );
I am searching for a library which can do the decomposition of polygons. I want define define directions or lines in which the polygon should be fragmented, as seen here:
So that I get the small polygons. Anyone know a library which supports this?
Or any ideas?
I'm not sure which language are you using. I have a library, written for my purposes, that can get a full partition by given line set and return polygons as a result. It is written on PHP and called dimension and, using it, you can solve your question like way:
Define your polygon by a set of lines LineSet_2D or a Polygon_2D
Define partition lines also through Line_2D
Use LineSet_2D method getPolygons to find all polygons
I've written an example:
//define or polygon. Note that Polygon_2D can also be used
$rPolygon = new LineSet_2D(
new Line_2D( 0, 3, 1, 1),
new Line_2D( 1, 1, 3, 0),
new Line_2D( 3, 0, 1,-1),
new Line_2D( 1,-1, 0,-3),
new Line_2D( 0,-3,-1,-1),
new Line_2D(-1,-1,-3,0),
new Line_2D(-3, 0,-1, 1),
new Line_2D(-1, 1, 0, 3)
);
//define partition line set
$rPartition = new LineSet_2D(
new Line_2D(-1, 1, 1,-1),
new Line_2D(-1,-1, 1, 1)
);
//result line set:
$rResultSet = LineSet_2D::createFromArray(array_merge(
$rPolygon->getLines(),
$rPartition->getLines()
));
//for example, dump plain result:
var_dump($rResultSet->getPolygons());
You can also find this example here But I think it is not exact solution for your question, since my LineSet_2D class will return all looped polygons (i.e. not only 'pieces').
You are looking for a "polygon chop" boolean operation. You may google it for resources available.
Some queues on doing this on your own..
For each slicing line..
Find the intersection points of the slicing line with the edges of the polygon.
For each edge that it intersects, split the edge into two.
Split the polygon corresponding to the splitted edge, into two polygons.
Do the same for all polygons.
You will have to take care of special cases, like splitting line going through vertex and so on...
After a BufferGeometry has been created and rendered at least once, I need to increase the size of the geometry. Since increasing the size of the attribute arrays is not possible, can I replace the arrays with new, larger ones?
Something like myGeometry.attributes.position.array = newPositionArray;
I've tried this, and set all the .*NeedsUpdate fields on the geometry, but I get a long string of WebGL errors. Is there something going on internally in THREE.js that would prevent me from operating this way?
From the documentation.
"You can emulate resizing by pre-allocating larger buffer and then keeping unneeded vertices collapsed / hidden."
https://github.com/mrdoob/three.js/wiki/Updates#geometries
No. But you can fake it (to a point).
From the documentation (emphasis mine):
With regards to updating BufferGeometries, the most important thing to understand is that you cannot resize buffers (this is very costly, basically the equivalent to creating a new geometry). You can however update the content of buffers. This means that if you know an attribute of your BufferGeometry will grow, ...you must pre-allocate a buffer large enough to hold any new vertices that may be created. Of course, this also means that there will be a maximum size for your BufferGeometry - there is no way to create a BufferGeometry that can efficiently be extended indefinitely.
So, by pre-allocating more than you need, and then only drawing the parts currently populated (to maintain performance), you can achieve the desired result.
Code example (from the same documentation):
var MAX_POINTS = 500;
// geometry
var geometry = new THREE.BufferGeometry();
// attributes
var positions = new Float32Array( MAX_POINTS * 3 ); // 3 vertices per point
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
// draw range
var drawCount = 2; // draw the first 2 points, only
geometry.setDrawRange( 0, drawCount );
// material
var material = new THREE.LineBasicMaterial( { color: 0xff0000, linewidth: 2 } );
// line
var line = new THREE.Line( geometry, material );
scene.add( line );
And then to later update after adding new point information:
line.geometry.setDrawRange( 0, newValue );
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)