Remove adjoining faces in three.js - javascript

I'm trying to optimize a scene where I'm rendering cubes based off of an image's pixel data:
http://jsfiddle.net/majman/4sukB/
The code simply checks each pixel in an image and creates & positions a cube mesh accordingly.
However, as you can see if you toggle wireframes on, there is an abundance of unnecessary internal faces.
I am using mergeVertices as well as THREE.GeometryUtils.merge - so things are partially optimized.
I ran across this approach of comparing all the faces of merged geometry, but because each cube face is now a pair of tri's - they are difficult to compare as the two triangles of adjoining faces will be flipped.
I've also looked at the minecraft example, but I havne't been able to wrap my head around that approach.

Ok, with WestLangley's help - I was able to get there.
http://jsfiddle.net/majman/4sukB/2/
Took some fiddling to figure out which faces to adjust within buildPlane. After that, comparing centroids was relatively straight forward:
function removeDuplicateFaces(geometry){
for(var i=0; i<geometry.faces.length; i++){
var centroid = geometry.faces[i].centroid;
for(var j=0; j < i; j++){
var f2 = geometry.faces[j];
if( f2 !== undefined ){
var centroid2 = f2.centroid;
if(centroid.equals(centroid2)){
delete geometry.faces[i];
delete geometry.faces[j];
}
}
}
}
geometry.faces = geometry.faces.filter( function(a){ return a!== undefined });
return geometry;
}

Related

THREE.JS, change default index of buffer geometry, converting a geometry designed for a THREE.PointsMaterial to a THREE.MeshBasicMaterial (or other)

In this code pen,
you'll find a parametric flower that is using a red THREE.MeshBasicMaterial with a wireframe, it looks like this:
with wireframe: false, it looks like this:
Basically, I just want it to be a solid geometry
I tried to set the indices of the buffer geometry manually, but actually setting the indices as defined by this code:
let faces = []
for ( var i=0; i<positions.length; i+=3) {
faces.push( i, i+3, i+2 )
faces.push( i, i+1, i+3 )
}
var indices = new Uint16Array( faces );
geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
does nothing, and returns a black screen. So, the default view of the buffergeometry with the indices defined by the position buffer is what appears in the codepen.
Essentially, the problem I'm having is that I can't figure out the proper index to use, and why using any index just returns a black screen.
I looked at other examples from stackoverflow, like
how to understand setindex and index in buffer geometry
and
threejs correct way to setindex indices for buffergeometry
but they basically just described what I'm doing now, which is unhelpful.
The original code for this comes from another stackoverflow article, which is linked to, here, it defines the rose as a parametric equation, but it's based on a THREE.PointsMaterial, and does not work as a solid geometry. So I tried to make it work myself, and this is the result I got and if anyone knows how to fix it please let me know.
I did also decide to make a version of this using a THREE.ParametricGeometry but that method is extremely inefficient for what I want to accomplish, so my road has led me here, generating the points manually and adding them to a buffergeometry is SO much more efficient than using a THREE.ParametricGeometry. If you can help figure out how to properly set the index of the points to appear properly as faces, I would be so grateful because it would solve the problem I'm having of setting the index, so that the flower would appear as a solid geometry.
I figured it out!
https://codepen.io/ricky1280/pen/mdjOYba?editors=1010
the 2D loop should look like this:
for(let r=0; r < v.length; r++){
// fill(340,100, -20+r*r_D*120)
for(let theta =0; theta < v[r].length; theta++){
if(r < v.length-1 && theta< v[r].length-1){
positions.push(v[r][theta].x*1, v[r][theta].y*1, v[r][theta].z*1);
positions.push(v[r+1][theta].x*1, v[r+1][theta].y*1, v[r+1][theta].z*1);
positions.push(v[r+1][theta+1].x*1,v[r+1][theta+1].y*1,v[r+1][theta+1].z*1);
positions.push(v[r][theta].x*1, v[r][theta].y*1, v[r][theta].z*1);
positions.push(v[r][theta+1].x*1,v[r][theta+1].y*1,v[r][theta+1].z*1);
positions.push(v[r+1][theta+1].x*1,v[r+1][theta+1].y*1,v[r+1][theta+1].z*1);
}
}
}
Generally, instead of adding the positions to the array and then setting an index, add them to the array as if they were being used with respect to an index.
With this answer in mind the vertices of the p5js geometry should be modified along the same lines.

Proper pattern for merging curved lines into one geometry?

I've got thousands of sparsely arranged curved lines (made with CubicBezierCurve3) being merged into one geometry for obvious performance reasons.
Is it possible to feed them all into a single THREE.Line without the interconnecting segments becoming connected? Or make those interconnecting line segments invisible?
LinePieces/LineSegments (line strip) would work, but it would double the number of vertices and doesn't seem like the right approach..
So yes you can in fact send over NaN to instruct OpenGL to jump. Then just give three.js a bogus empty bounding sphere (or do a proper calculation of if). The bogus bounding sphere will mess up normal calculation, which didn't matter in my use but might in others'. Here's the pseudocode solution:
var positions = [];
for(....)
{
...
var pts = curve.getPoints(...);
for(var p = 0; p < pts.length; p++)
positions.push(pts[p].x, pts[p].y, pts[p].z);
positions.push(NaN, NaN, NaN);
}
var geometry = new THREE.BufferGeometry();
geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array(positions), 3, true ) );
geometry.boundingSphere = new THREE.Sphere();

Experiencing something odd when using THREE.Raycaster for collision detection (r68)

I've been using the THREE.Raycaster successfully to test collisions for many things in my game engine so far, it's great and it works well.
However, recently I've run into something quite peculiar which I cannot seem to figure out. From my point of view, my logic and code are sound but the expected result is not correct.
Perhaps I'm just missing something obvious so I thought I'd ask for some help.
I am casting rays out from the center of the top of a group of meshes, one by one, in a circular arc. The meshes are all children of a parent Object3D and the goal is to test collisions between the origin mesh and other meshes which are also children of the parent. To test my rays, I am using the THREE.ArrowHelper.
Here's an image of the result of my code - http://imgur.com/ipzYUsa
In this image, the ArrowHelper objects are positioned (origin:direction) exactly how I want them. But yeah, there's something wrong with this picture, the code that is produces this is:
var degree = Math.PI / 16,
tiles = this.tilesContainer.children,
tilesNum = tiles.length,
raycaster = new THREE.Raycaster(),
rayDirections, rayDirectionsNum, rayOrigin, rayDirection, collisions,
tile, i, j, k;
for (i = 0; i < tilesNum; i++) {
tile = tiles[i];
rayOrigin = new THREE.Vector3(
tile.position.x,
tile.geometry.boundingBox.max.y,
tile.position.z
);
rayDirections = [];
for (j = 0; j < Math.PI * 2; j += degree) {
rayDirections.push(new THREE.Vector3(Math.sin(j), 0, Math.cos(j)).normalize());
}
rayDirectionsNum = rayDirections.length;
for (k = 0; k < rayDirectionsNum; k++) {
rayDirection = rayDirections[k];
raycaster.set(rayOrigin, rayDirection);
collisions = raycaster.intersectObjects(tiles);
this.testRay(rayOrigin, rayDirection, collisions);
}
}
The testRay method looks like this:
testRay: function (origin, direction, collisions) {
var arrowHelper = new THREE.ArrowHelper(
direction,
origin,
1,
(collisions.length === 0) ? 0xFF0000 : 0x0000FF
);
this.scene.add(arrowHelper);
}
Now, obviously, something is off about this image. The rays that collide with other meshes should be blue, while those that do not collide should be red.
It's clear from this image that something is totally out of whack, and when I inspect the collisions, I get some really off results. For a lot of those rays which appear blue in the image, I'm getting a huge number of collisions, something like 30 collisions for a single ray sometimes, but nothing for the others even when they are right next to other tiles.
I just can't figure out what it might be. How can it be that so many rays that should be blue are red? And how can rays from tiles at the edge of the level have blue collisions to tiles that do not exist?
Really scratching my head (read: bashing my head repeatedly) over this one, any help would be super appreciated!
The solution was actually outside this code and not, at least I don't believe, related to the outdated r68 build.
When making the tile meshes, I needed to set three properties on them
tileMesh.matrixAutoUpdate = false;
tileMesh.updateMatrix();
tileMesh.updateMatrixWorld(); // this is new
I was doing the first two, just not the last one. Why this is necessary, I do not know, it seems a little odd to me but this is what fixed my problem. I had an AxisHelper in the scene, if you look at the original image, you'll notice that all the ArrowHelper objects that are blue are actually pointing towards the AxisHelper. This is really weird because the AxisHelper was added to the scene, not to tilesContainer. Adding the ArrowHelper objects to tilesContainer did not help.
The process to render the scene had the raycaster code run before the AxisHelper was added to the scene and before the initial render happened. The problem was also fixed if I moved the raycaster code call after the AxisHelper was added, but this was a hacky solution.
So the true fix was to add .updateMatrixWorld() to the tiles. The result now looks like this http://imgur.com/8LewqxL, which is correct (the ArrowHelper objects have been shortened in length so they don't overlap).
Big thanks to Manthrax for his help on this one.
I think you make some local vs global space error. I don't see so fast where exactly you go wrong, but all your position and direction calculations seem to be in the local system of the tilesContainer. Are you consistent in your local vs global coordinate system handling?
For example you add your arrowHelper to the scene instead of to the tilesContainer. It could be that the tilesContainer has some rotation set and because of this the arrows are pointing in another direction then you expected.
What happens for example if you add the arrows to the tilesContainer instead?

Three.js: Updating Geometries vs Replacing

I have a scene with lots of objects using ExtrudeGeometry. Each of these need to update each frame, where the shape that is being extruded is changing, along with the amount of extrusion. The shapes are being generated using d3's voronoi algorithm.
See example.
Right now I am achieving this by removing every object from the scene and redrawing them each frame. This is very costly and causing performance issues. Is there a way to edit each mesh/geometry instead of removing from the scene? Would this help with performance? Or is there a more efficient way of redrawing the scene?
I'd need to edit both the shape of the extrusion and the amount of extrusion.
Thanks for taking a look!
If you're not changing the number of faces, you can use morph targets http://threejs.org/examples/webgl_morphtargets.html
You should
Create your geometry
Clone the geometry and make your modifications to it, such as the maximum length of your geometry pillar
Set both geometries as morph targets to your base geometry, for example
baseGeo.morphTargets.push(
{ name: "targetName", vertices: [ modifiedVertexArray ] }
);
After that, you can animate the mesh this using mesh.updateMorphTargets()
See http://threejs.org/examples/webgl_morphtargets.html
So I managed to come up with a way of not having to redraw the scene every time and it massively improved performance.
http://jsfiddle.net/x00xsdrt/4/
This is how I did it:
Created a "template geometry" with ExtrudeGeometry using a dummy
10 sided polygon.
As before, created a bunch of "points", this time assigning each
point one of these template geometries.
On each frame, iterated through each geometry, updating each vertex
to that of the new one (using the voronoi alg as before).
If there are extra vertices left over, "bunch" them up into a single point. (see http://github.com/mrdoob/three.js/wiki/Updates.)
Looking at it now, it's quite a simple process. Before, the thought of manipulating each vertex seemed otherworldly to me, but it's not actually too tricky with simple shapes!
Here's how I did the iteration, polycColumn is just a 2 item array with the same polygon in each item:
// Set the vertex index
var v = 0;
// Iterate over both top and bottom of poly
for (var p=0;p<polyColumn.length;p++) {
// Iterate over half the vertices
for (var j=0;j<verts.length/2;j++) {
// create correct z-index depending on top/bottom
if (p == 1) {
var z = point.extrudeAmount;
} else {
var z = 0;
}
// If there are still legitimate verts
if (j < poly.length) {
verts[v].x = poly[j][0];
verts[v].y = poly[j][1];
verts[v].z = z;
// If we've got extra verts, bunch them up in the same place
} else {
verts[v].x = verts[v - 1].x;
verts[v].y = verts[v - 1].y;
verts[v].z = z;
}
v++;
}
}
point.mesh.geometry.verticesNeedUpdate = true;

Three.js Raycaster.intersectObjects only detects intersection on front surface

I am creating a program which populate a world with blocks on the ground upon mouse click (the coordinate is based on the mouse click coordinate, which I've calculated). When creating a new block, it should not intersects with the ones already made beforehand. I achieved this checking with THREE.Raycaster. Basically I cast rays from the block's center in all direction of its vertices.
var ln = preview.geometry.vertices.length;
//for each vertices we throw a ray
for(var i = 0; i < ln; i++){
var pr_vertex = preview.geometry.vertices[i].clone();
var gl_vertex = pr_vertex.applyMatrix4(preview.matrix);
var dr_vector = gl_vertex.sub(preview.position);
var ray = new THREE.Raycaster(preview.position, dr_vector.clone().normalize());
var intersects = ray.intersectObjects(objmanager.getAllObject(), true);
//if there is intersection
if(intersects.length > 0 && intersects[0].distance < dr_vector.length()){
//hide preview material
preview.material.opacity = 0;
//exit checking
break;
}
}
preview is the name of the object. This code already worked perfectly. The problem is, Raycaster.intersectObjects only worked when the ray intersects with the front face of the tested object. I've tested it when I ran the code, if the ray start from outside the tested object, they intersected just fine. But when the ray started if from the inside of the tested block, it didn't catch the intersection with the tested block's surface. I presume that this happens because intersectObjects only worked with the tested object's front surface. Is this true?
Intuitively, I tried to solve this problem using THREE.DoubleSide (making a double-sided objects). But it produced an error because the shader failed to initialize.
Could not initialise shader
VALIDATE_STATUS: false, gl error [0] three.js:25254
buildProgram three.js:25254
initMaterial three.js:23760
setProgram three.js:23830
renderBuffer three.js:22371
renderObjects three.js:23061
render three.js:22935
animLoop
I'm using THREE.MeshLambertMaterial. When I used MeshBasicMaterial, the Raycaster.intersectObjects just didn't work at all.
So I hit a roadblock now. Is there any idea to solve this problem? Either that, or I would have to test the collision manually with each objects' individual faces (which I'm afraid would hit the performance hard).
Thank you in advance.
===============
edit: I forgot to mention, I'm using the newest three.js version, which should be around r63
If you set:
object.material.side = THREE.DoubleSided
you should get hits on both surfaces.
If double sided you'll have two intersections :
object.material.side = THREE.DoubleSide

Categories

Resources