I have an large array (50,000 to 100,000 elements) with each element containing another array of 3 points that define the vertices of a polygon. Some vertices have a parameter addition built in to the array, e.g;
var array = [
[[967.6719, 657.401, -1008.1],[967.6719, 657.401, -1001.1],[967.1551, 657.4806, -1008.1]],
[[967.1551, 657.4806, -1008.1 + LENGTH],[967.6719, 657.401, -1001.1],[967.1551, 657.4806, -1001.1]],
...etc
];
The length parameter is controlled by a slider bar. Currently the initial load time is way too long and any changes to the parameter also takes ages to update. Is there a way to optimise this?
I am currently rendering the polygons with this code;
function drawShapes(array) {
scene.remove( all_shapes );
all_shapes = new THREE.Object3D();
var LENGTH = inpLength.valueAsNumber;
var material = new THREE.LineBasicMaterial( { color: 0x02B700, linewidth: 2 } );
var triangleGeometry = new THREE.Geometry();
for (var i=0; i < array.length; i++){
for (var n=0; n<3; n++){
triangleGeometry.vertices.push(new THREE.Vector3( array[i][n][0], array[i][n][1], array[i][n][2]));
}
triangleGeometry.faces.push(new THREE.Face3(0, 1, 2));
triangleMesh = new THREE.Mesh(triangleGeometry, material);
all_shapes.add(triangleMesh)
var triangleGeometry = new THREE.Geometry();
}
scene.add( all_shapes );
}
Related
I have an array of Vector3s that define an arbitrary curved shape in 3D space. I can render an outline of the curve in Three.js by using a THREE.Geometry and a THREE.Line, but I'd like to fill it with color.
I tried using THREE.ShapeGeometry and THREE.Mesh, but it seems as though THREE.ShapeGeometry is for 2D planes only (the z-coordinates of my vertices are being ignored). I also tried to use THREE.Geometry and define the faces in addition to the vertices I wanted, but had no luck.
How should I go about doing this?
Code:
geom.vertices = curve.getPoints(100);
for (var i = 0; i < 97; i++) {
geom.faces.push(new THREE.Face3(i, i + 1, i + 2));
}
var material = new THREE.MeshNormalMaterial();
obj = new THREE.Mesh(geom, material);
scene.add(obj);
Fixed by changing the code in the question above to the excerpt below:
geom.vertices = curve.getPoints(100);
for (var i = 0; i < 98; i++) {
geom.faces.push(new THREE.Face3(0, i + 1, i + 2));
}
var materialObj = { color : 0xff0000, side: THREE.DoubleSide };
var material = new THREE.MeshBasicMaterial(materialObj);
obj = new THREE.Mesh(geom, material);
scene.add(obj);
I'm using the ExplodeModifier to duplicate the vertices so I can have individual control over Face3 objects.
For my specific example, this alone looks visually poor, so I decided to add 3 extra faces (per existing face) so I can have a pyramid shape pointing inwards the geometry.
I managed to modify the ExplodeModifier and create the extra faces, however I get several errors:
THREE.DirectGeometry.fromGeometry(): Undefined vertexUv and THREE.BufferAttribute.copyVector3sArray(): vector is undefined
I understand that now I have 9 extra vertices per face, so I need according uv's, and since I don't need a texture but a solid color I don't mind having the wrong uvs... So, I also duplicated the uvs and avoid the first warning but I can't get rid of the copyVector2sArray...
pseudo code:
var geometry = new THREE.IcosahedronGeometry( 200, 1 );
var material = new THREE.MeshPhongMaterial( { shading: THREE.FlatShading } );
var explodeModifier = new THREE.ExplodeModifier();
explodeModifier.modify( geometry );
var mesh = new THREE.Mesh( geometry, material );
scene.addChild( mesh );
The Explode Modifier has this pseudo code:
var vertices = [];
var faces = [];
for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) {
(...)
var extraFace1 = new THREE.Face3().copy(face)
extraFace1.c = geometry.vertices[0]
var extraFace2 = new THREE.Face3().copy(face)
extraFace2.b = geometry.vertices[0]
var extraFace3 = new THREE.Face3().copy(face)
extraFace3.a = geometry.vertices[0]
faces.push( extraFace1 );
faces.push( extraFace2 );
faces.push( extraFace3 );
}
geometry.vertices = vertices;
geometry.faces = faces;
```
I added an example HERE. It works, but I want to avoid the console warnings...
As pointed out by #mrdoob I was assigning a THREE.Vector3 and not an index to the added THREE.Face3.
var extraFace1 = new THREE.Face3().copy(face)
extraFace1.a = geometry.faces.length * 3 - 1
var extraFace2 = new THREE.Face3().copy(face)
extraFace2.b = geometry.faces.length * 3 - 1
var extraFace3 = new THREE.Face3().copy(face)
extraFace3.c = geometry.faces.length * 3 - 1
jsfiddle updated
I don't know what I'm doing wrong. I have multiple meshes that I am trying to merge into one mesh so that I can save on draw calls.
Each of my meshes has a unique materials. In this example it just has a different color, but really they will have unique textures mapped.
This is my code:
materials = [];
blocks = [];
var tempMat;
var tempCube;
var tempGeo;
var tempvec;
// block 1
tempMat = new THREE.MeshLambertMaterial({ color: '0x0000ff' });
materials.push( tempMat );
tempGeo = new THREE.CubeGeometry(1, 1, 1);
for (var ix=0; ix<tempGeo.faces.length; ix++) {
tempGeo.faces[ix].materialIndex = 0;
}
tempCube = new THREE.Mesh( tempGeo, tempMat );
tempCube.position.set(0, 3, -6);
blocks.push( tempCube );
// block 2
tempMat = new THREE.MeshLambertMaterial({ color: '0x00ff00' });
materials.push( tempMat );
tempGeo = new THREE.CubeGeometry(1, 1, 1);
for (var ix=0; ix<tempGeo.faces.length; ix++) {
tempGeo.faces[ix].materialIndex = 1;
}
tempCube = new THREE.Mesh( tempGeo, tempMat );
tempCube.position.set(1, 3, -6);
blocks.push( tempCube );
// Merging them all into one
var geo = new THREE.Geometry();
for (var i=0; i<blocks.length; i++) {
blocks[i].updateMatrix();
geo.merge(blocks[i].geometry, blocks[i].matrix, i);
}
var newmesh = new THREE.Mesh( geo, new THREE.MeshFaceMaterial( materials ) );
scene.add(newmesh);
Basically, that gives me an error that says:
Uncaught TypeError: Cannot read property 'visible' of undefined
every time my render function is called.
Where did I go wrong?
You are merging geometries into one, and using MeshFaceMaterial (renamed MultiMaterial in r.72).
It does not make any sense to merge geometries having different material indices.
WebGLRenderer needs to segment the geometry by material to render it.
As a rule-of-thumb, only merge geometries if they will be rendered with a single material.
three.js r.72
I'm working on a project that makes a sort of randomized sheet. It stores arrays of x, y, and z coordinates and draws triangles between the points. You can see this working pretty well in this screenshot.
I used MeshBasicMaterial to make that sheet, but wanted to switch to MeshLambertMaterial to take advantage of lighting. When I try this, I get a sheet that looks like this.
This is the working Basic Mesh code on green tiles:
for(j = 0; j < h-1; j++) { //h is the number of tiles vertically
for(i = 0; i < w-1; i++) { //w is the number of tiles horizontally
o = ((j%2==1)?1:0); //checks if the row is odd
var geom = new THREE.Geometry();
var a = new THREE.Vector3(x[i][j], y[i][j] ,z[i][j]);
var b = new THREE.Vector3(x[i+1][j], y[i+1][j] ,z[i+1][j]);
var c = new THREE.Vector3(x[i+o][j+1], y[i+o][j+1] ,z[i+o][j+1]);
geom.vertices.push(a);
geom.vertices.push(b);
geom.vertices.push(c);
geom.faces.push(new THREE.Face3(0,1,2));
tile1[i][j] = new THREE.Mesh(
geom,
new THREE.MeshBasicMaterial({color: 'green'})
);
scene.add(tile1[i][j]);
}
}
And this is the failing Lambert Mesh code on red tiles (note that I only changed 'Basic' to 'Lambert'):
for(j = 0; j < h-1; j++) { //h is the number of tiles vertically
for(i = 0; i < w-1; i++) { //w is the number of tiles horizontally
o = ((j%2==1)?0:1); //checks if the row is even
var geom = new THREE.Geometry();
var a = new THREE.Vector3(x[i+o][j], y[i+o][j] ,z[i+o][j]);
var b = new THREE.Vector3(x[i+1][j+1], y[i+1][j+1] ,z[i+1][j+1]);
var c = new THREE.Vector3(x[i][j+1], y[i][j+1] ,z[i][j+1]);
geom.vertices.push(a);
geom.vertices.push(b);
geom.vertices.push(c);
geom.faces.push(new THREE.Face3(0,1,2));
tile2[i][j] = new THREE.Mesh(
geom,
new THREE.MeshLambertMaterial({color: 'red'})
);
scene.add(tile2[i][j]);
}
}
A cube created with the following Lambert Mesh works perfectly and catches light properly.
scene.add(new THREE.Mesh(new THREE.BoxGeometry(10,1000,5),new THREE.MeshLambertMaterial({color:'red'})));
Why does the Lambert Mesh not work on a geometry that Basic Mesh works on?
EDIT: I placed a colored box under the sheet to test how the box would react to lighting and found that the tiles above it weren't failing to render, but were just black. They are opaque, but don't use the color or pick up light the way the box does.
You should have lights in your scene to profit from THREE.LambertMaterial. Did you setup your scene lighting correctly?
EDIT:
I found out where your problem is. You should add a normal to your faces, otherwise the WebGL renderer does not know how to render the light bouncing of the THREE.LambertMaterial on the surfaces. So change your code like this:
face = new THREE.Face3( 0, 1, 2 );
face.normal = new THREE.Vector3().crossVectors(
new THREE.Vector3().subVectors( b, a ),
new THREE.Vector3().subVectors( c, b )
).normalize();
geom.faces.push( face );
Now your faces should render.
Instead of doing this manually you can also use the geometry methods for calculating them:
geometry.computeFaceNormals();
geometry.computeVertexNormals();
I'm making a custom three.js geometry for non-orthogonal cubes. It is loosely based on the existing Box-geometry in three.js, but greatly simplified insofar that it only supports one segment per side and also has the absolute position of its vertices fed directly to it.
I have problems both in wire frame rendering and texture rendering. In wire frame rendering I only get to see one of the six sides, as can be seen here:
This is the snippet that I use for setting the material:
if (woodTexture) {
texture = THREE.ImageUtils.loadTexture( 'crate.gif' );
texture.anisotropy = makeRenderer.renderer.getMaxAnisotropy();
material = new THREE.MeshBasicMaterial( { map: texture } );
} else {
material = new THREE.MeshBasicMaterial( { color: color, wireframe: true, side: THREE.DoubleSide } );
}
I know for sure the path for crate.gif is valid, as it works for Box geometries.
Here follows my faulty geometry. The 'quadruplets' array contains six arrays with each four Vector3 instances. Each inner array delineates a side of the cube.
THREE.Box3Geometry = function (quadruplets, debug) {
THREE.Geometry.call(this);
var constructee = this; // constructee = the instance currently being constructed by the Box3Geometry constructor
buildPlane(quadruplets[0], 0, debug); // px
buildPlane(quadruplets[1], 1); // nx
buildPlane(quadruplets[2], 2); // py
buildPlane(quadruplets[3], 3); // ny
buildPlane(quadruplets[4], 4); // pz
buildPlane(quadruplets[5], 5); // nz
function buildPlane(quadruplet, materialIndex, debug) {
// populate the vertex array:
constructee.vertices.push(quadruplet[0]);
constructee.vertices.push(quadruplet[1]);
constructee.vertices.push(quadruplet[2]);
constructee.vertices.push(quadruplet[3]);
// construct faceVertexUvs:
var uva = new THREE.Vector2(0, 1);
var uvb = new THREE.Vector2(0, 0);
var uvc = new THREE.Vector2(1, 0);
var uvd = new THREE.Vector2(1, 1);
// construct faces:
var a = 0; // vertex: u:50, v:50
var b = 2; // vertex: u:50, v:-50
var c = 3; // vertex: u:-50, v:-50
var d = 1; // vertex: u:-50, v:50
// construct normal:
var pv0 = quadruplet[1].clone().sub(quadruplet[0]); // pv = plane vector
var pv1 = quadruplet[2].clone().sub(quadruplet[0]);
normal = new THREE.Vector3(0,0,0).crossVectors(pv0, pv1).normalize();;
var face1 = new THREE.Face3(a, b, d);
face1.normal.copy(normal);
face1.vertexNormals.push(normal.clone(), normal.clone(), normal.clone());
face1.materialIndex = materialIndex;
constructee.faces.push(face1);
constructee.faceVertexUvs[ 0 ].push([ uva, uvb, uvd ]);
var face2 = new THREE.Face3(b, c, d);
face2.normal.copy(normal);
face2.vertexNormals.push(normal.clone(), normal.clone(), normal.clone());
face2.materialIndex = materialIndex;
constructee.faces.push(face2);
constructee.faceVertexUvs[ 0 ].push([ uvb.clone(), uvc, uvd.clone() ]);
}
this.mergeVertices();
};
THREE.Box3Geometry.prototype = Object.create(THREE.Geometry.prototype);
And this is the Box geometry from which I was "inspired".
You build a nice array of vertices, but give every face1/face2 combo the same set of indexes into that array: 0, 1, 2, 3. You essentially define the same quad 6 times.
What you need to do is keep a running base offset and add that to the vertex indices used to define each face. If you look at your BoxGeometry example, you'll that they do exactly that.