I have a file with sparse elevations. It is based off of gps data. I have been using this data to populate an PlaneBuffer array with elevations.
var vertices = new Float32Array( (grid.NCOL*grid.NROW) * 4 );
for (var i = 0, q = vertices.length; i < q; i++){
vertices[ i*3 + 0 ] = parseInt(i % (grid.NCOL+1)*4);
vertices[ i*3 + 1 ] = parseInt(i / (grid.NCOL+1)*4);
// vertices[ i*3 + 2 ] = null; // makes no difference
}
for (var i = 0, l = grid.NODES.length; i < l; i++) {
var nodeNumber = grid.NODES[i][0];
var elevation= grid.NODES[i][1];
vertices[ nodeNumber*3 + 2 ] = elevation;
}
My problem is that there are nodes that the elevation values are unknown(Vertex array is sparse with elevations) and should be represented by holes/cutouts in the plane. What I end up with is the null elevations being interpreted as 0 not as holes. I have started down the path of using a rawshader, but still not sure that making null values transparent is the correct method.
The below picture shows my issues. The circled area is a high wall that should not be there, because it triangulating to the "null/0" floor. The red-lines area is where we should have a hole.
EDIT:
Maybe this picture will help to. It is from the bottom. The null elevations being set to zero block the view of the plane and cause the edge of the plane to be triangulated to 0 elevation:
Here is what our desktop application displays. Notice the edges of the plane are not triangulated down to zero but instead left sharp?
Plane buffer Geometries take a Float32Array. This array is default set to 0. Using undefined setter allowed me to set the sparse array out of the float32 type. Attempts to set any value to null and NanN did not work.
RTFM:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array
final result as expected:
Your use case seems more appropriate for a point cloud with THREE.Points. potree.org/demo/potree_1.3/showcase/ca13.html – WestLangley 14 mins ago
This example helped:
http://threejs.org/examples/#webgl_buffergeometry_points
Related
i am making a virtual Hama beads tool online. (its a kids toy where you put plastic beads on a matrix and iron them to make them stick together and form a solid shape)like this one below (real hama)
So far i managed to make this work (you can try it out there : https://static.club1.fr/alixturcq/hama%20dev/ ) but i would like to make it more elaborate and have the beads on the edges more realistic with round corners where there are no neighbors (virtual hama)
This would mean having a special image for each of theses neighbor cases like this (neighbor_cases)
So each bead is an object, and i have added a "neighbor" parameter to display the correct image.
Every object is automatically placed on the matrix at start but "isOn" is false, so nothing is displayed. When I click on the matrix, I change the 'isOn' boolean to true and the hama bead displays.
The objects are stored in a 1D array called hamas[], but are being displayed as a 2D array, so i can manipulate it as 2D.
class hama {
constructor(isOn, posx, posy, color, neighbor) {
this.posx = posx;
this.posy = posy;
this.color = color;
this.isOn = isOn;
this.neighbor = neighbor;
this.variation = int(random(0,6));
map
}
display() {
if (this.isOn == true) {
if (ironed == true) {
image(hamas_img[this.color][this.neighbor], this.posx, this.posy, CELL_SIZE, CELL_SIZE);
}
else {
image(ironed_hamas_img[this.color][this.neighbor], this.posx, this.posy, CELL_SIZE, CELL_SIZE);
}
}
}
}
Every time I place a bead i need the program to check every objects if they are On, and also what is the 'isOn' state of each of the neighbors in the matrix.
So far I've done this in pseudo-code but I hope there are smarter ways.
// creating an array of neighbor indexes (clockwise around the object)
checkAround[(+1); (width+1); (width); (width-1); (-1); (-width-1); (-width); (-width+1)]
// creating an empty string that is going to be the neighbor code
neighborCode= "";
// check around object to get neighborstate
for (int i=0; i<checkAround.count; i++){
if (hammas[hammaIndex+checkAround[i]].isOn==true){
neighborCode += "O"
}
else {
neighborCode += "X"
}
}
Then I get neighbourCode strings looking like "XOOXOXOO" and map all these code to appropriate neighbour state number and display the correct image.
So this is rather twisted, I was hoping someone could help me find a smarter solution !
And also I have to find a way to avoid checking non existing indexes on the edges of the matrix, but this should not be to hard to find.
Thanks in advance
Al
What you are looking for is edge detection on a pixel array and has been solved many times before. You can solve it yourself and it's fun to figure out yourself.
When a problem exists in 2D space, it often helps to think in 2D.
But your data is structured in 1D (which is fine, and the convention)
Each pixel/bead has a position in the 1D array -let's call that i- and a position in 2D space, defined by x and y.
The relationship between those is like this: i = x + y * w
( Where w is the width of the image)
With this information you can traverse the 1D array through x and y quite easily and decide for each pixel what its neighbours are doing.
example:
for(var x = 0; x < width; x++){
for(var y = 0; y < height; y++){
var i = x + y * width; // id of the current pixel being checked.
// Calculate neighbour ids:
var iNorth = x + (y - 1) * width;
var iSouth = x + (y + 1) * width;
var iEast = x + 1 + y * width;
var iWest = x - 1 + y * width;
var iNorthWest = (x - 1) + (y - 1) * width; // haha Kanye
// etc.. you get the point
// Now encode all those vars in one single byte
// Use that byte to choose the right image for the bead at position I
// Don't forget to deal with your edge cases
}
}
You can optimize later. (eg. You don't have to update the whole image each time you change one pixel, only its neighbors are affected.)
Your "OXOOXOOO" string is a perfectly acceptable way of storing the different states of a bead. A more nerdy approach would be to just store bits in a 8 bit byte.(because each pixel has only 8 neighbours..)
Those bytes can be stored in a buffer with the same exact structure as your actual pixel data.
I think this should get you started without spoiling too much of the fun?
In an effort to learn a little bit more about custom geometry in three.js, I tried adapting Paul Bourke's capsule geometry example.
With my custom capsule geometry, I am currently having two issues:
The middle face normals are not oriented properly.
There is a hard seam along the side. (EDIT: fixed by deliberately computing the face normals. updated code in the gist)
And maybe one bonus question that has been lingering on my mind:
What might a general strategy be to add vertex loops in that middle segment?
I'm really happy with the geometry in general, but would anyone be able to give me some direction on how to address these issues? I feel like the normal issue in the middle segment must be the orientation of the faces, and here is the related face construction snippet:
for(let i = 0; i <= N/2; i++){
for(let j = 0; j < N; j++){
let vec = new THREE.Vector4(
i * ( N + 1 ) + j ,
i * ( N + 1 ) + ( j + 1 ) ,
( i + 1 ) * ( N + 1 ) + ( j + 1 ) ,
( i + 1 ) * ( N + 1 ) + j
);
let face_1 = new THREE.Face3(vec.x,vec.y,vec.z);
let face_2 = new THREE.Face3(vec.x,vec.z,vec.w);
geometry.faces.push(face_1);
geometry.faces.push(face_2);
}
}
CapsuleGeometry.js
The shading/normal seam is there because you have probably explicitly defined a hard edge there.
When you run your loops to generate the vertices, you probably duplicate the starting position. If you start at 0, and go all the way to 2PI, 0==2PI. When you weave the triangles, you probably tell the lest one to use the 2PI instead of 0 and even though they are in the same position, as far as triangles are concerned, they point to different vertices, and are thus not connected.
for(let i = 0; i <= N/4; i++){ //change to i < N
for(let j = 0; j <= N; j++){
If you tell the last triangle in the loop to point to the beginning vertex you will make a continous surface that geometry.computeVertexNormals() can smooth out.
You can also just compute these normals directly. All the normals can be obtained in these case from the vertex positions of the original sphere before expanding it.
I'm trying to implement a wireframe which displays quads instead of tris using this code
var geo = new THREE.EdgesGeometry( _this.geometry ); // or WireframeGeometry
var mat = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 2 } );
var wireframe = new THREE.LineSegments( geo, mat );
_this.scene.add( wireframe );
This produces the following when the model is rendered
As you can see from the image it is not displaying all of the edges, and is still displaying some tris. I need it to be similar to how Maya displays wireframes.
I have read that ThreeJS no longer supports Face4 which is why it always displays tris instead of quads, but i was wondering if there was a way around this? I have also seen some mention of using a pixel shader to display only the edges of a mesh but i havnt been able to understand/get that working.
I would love some assistance on this one, either using existing threejs functionality, or by using a pixel shader somehow.
This is the model source (http://pastebin.com/21XUKYbw)
Cheers
Answering this for anyone who stumbles upon this issue in the future.
Following on from a comment made by WestLangley, i have modified the code within WireframeGeometry.js to ignore rendering the interior diagonals that are present when rendering a wireframe, which gives the appearance of quad faces. Which is more familiar to 3d artists.
This is the change I've made to the bottom of WireframeGeometry.js. This is admittedly a pretty hacky fix. The alternative would be to compute the lines you want to display before threejs performs triangulation. You could store the pre triangluated faces in a separate buffer.
// non-indexed BufferGeometry
position = geometry.attributes.position;
var _i = 0;
for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) {
for ( j = 0; j < 3; j ++ )
// three edges per triangle, an edge is represented as (index1, index2)
// e.g. the first triangle has the following edges: (0,1),(1,2),(2,0)
// Added a simple check here to only push the vertices that are not diagonal lines
if(!(j == 2 && _i == 1) || !(j == 1 && _i == 0)){
index1 = 3 * i + j;
vertex.fromBufferAttribute(position, index1);
vertices.push(vertex.x, vertex.y, vertex.z);
index2 = 3 * i + ( ( j + 1 ) % 3 );
vertex.fromBufferAttribute(position, index2);
vertices.push(vertex.x, vertex.y, vertex.z);
}
}
_i++;
if(_i == 2){
_i = 0
}
}
Beginner question:
I create some random points in java script. How can I later "call" each single one of them? I believe it was called an Object (the list of all points), where I can manipulate their position in the list or "pull" the ones I need. How do I do that in js?
what is the line:
var dots = []; for?
The comment after that was added from another person and I don't get it.
How can I make a line between two points (let's say the first and second from the list - the 0 and the 1) in Three.js?
More complicated question: I create here X (in this case 20) random points in java script. I am trying to figure out a way to randomly create faces using groups of 3 points and form a mesh.
Well, not so randomly, because I need a result, where all faces bind together in a continuous mesh. The result should look like a terrain poly-surface made out of the many vertex points.
What rules should be applied?
var numpoints = 20;
var dots = []; //If you want to use for other task
for (var i = 0 ; i < numpoints ; i++) {
var x = Math.random() * (80 - 1) + 1 //Math.random() * (max - min) + min
var y = Math.random() * (80 - 1) + 1
var z = Math.random() * (10 - 1) + 1
var dotGeometry = new THREE.Geometry();
dots.push(dotGeometry);
dotGeometry.vertices.push(new THREE.Vector3(x, y, z));
var dotMaterial = new THREE.PointCloudMaterial( { size: 3, sizeAttenuation: false, color: 0xFF0000 });
var dot = new THREE.PointCloud( dotGeometry, dotMaterial);
scene.add(dot);
}
To get a random element from array:
function randChoice(array){
return array[Math.floor(Math.random()*array.length)];
};
There are 2 method to generate a mesh from random points that I know of:
convex hull and alpha shape. Creating a mesh by repeating picking 3 random points would almost surely result in spaghetti.
If you just want to generate terrain like in the picture, there is terrain generation from heightmap.
Hey guys I have been working on a little project of mine utilizing the awesome library three.js
Now I have been working with the example of https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Collision-Detection.html to handle collision detection, more so on when an object over laps another object utilizing voxel's.
To reference my problem I am using the http://threejs.org/examples/#canvas_interactive_voxelpainter example.
Anyways to continue, when I render a voxel onto the screen, anything above the cube will allow me to render another voxel anything within a certain radius below the volex will not let me render:
Here is shown above cube:
Now here is my neat little function I put together using the example provided by stemkoski:
checkOverlapObject: function(voxel) // THIS IS USED TO SEE IF WE ARE OVER LAPPING ANY OBJECTS
{
var originPoint = voxel.position.clone();
var collidableObjs = this.rooms;
for (var vertexIndex = 0; vertexIndex < voxel.geometry.vertices.length; vertexIndex++)
{
var localVertex = voxel.geometry.vertices[vertexIndex].clone();
console.log(localVertex);
var globalVertex = localVertex.applyMatrix4( voxel.matrix );
console.log(globalVertex);
var directionVector = globalVertex.sub( voxel.position );
console.log(directionVector);
console.log(originPoint);
console.log(directionVector.clone().normalize());
if(collidableObjs.length > 0)
{
var ray = new THREE.Raycaster( originPoint, directionVector.clone().normalize() );
var collisionResults = ray.intersectObjects( collidableObjs );
if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
{
console.log(collisionResults);
console.log(collisionResults[0].distance);
console.log( directionVector.length() );
return false
}
}
}
return true;
},
Now what happens here is, before actually adding a rendered volex the user gets a preview of if they have permission to add the volex So we pass a volex made by:
var voxel = new THREE.Mesh( this.room.cubeGeometry, this.room.cubeTmpHoverMaterial );
voxel.geometry.computeBoundingBox();
voxel.position.copy( intersect.point ).add( intersect.face.normal );
voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
voxel.material.color.setHex(this.colorTmpHover);
into our checkOverlapObject function to see if the object is overlapping a object that has been rendered onto the screen/grid
Now following my little neat function I made, I have placed console.log to output parameters and here is some of that output:
T…E.Vector3 {x: 25, y: 25, z: 25} <!-- our localVertex
T…E.Vector3 {x: 25, y: 25, z: 25} <!-- our globalVertex
T…E.Vector3 {x: 0, y: 0, z: -350} <!-- our directionVector
T…E.Vector3 {x: 25, y: 25, z: 375} <!-- our originPoint
T…E.Vector3 {x: 0, y: 0, z: -1} <!-- our directionVector.clone().normalize()
[Object, Object] <!-- our collisionResults
225 <!-- our collisionResults[0].distance
350 <!-- our directionVector.length()
This data is based off of the first picture.
Please understand that I have other volex that take up 2 blocks on the grid or more. So the reason for this is, I have a center location of the position but I need to take into account the rest of the object if it takes up 2 blocks on the grid to check if that overlaps the already rendered volex I don't care if they touch each other.
Any suggestions or thoughts as to what might be the problem?
As asked above I finally found a solution. I created a custom function for object collision/overlap.
Firstly, here is whats wrong with the above collision code as shown above, it process's the raycaster starting with the direction of our object - shooting a line straight across from its orgin to see if anything comes into contact with that ray. Now that is great and all but not very sufficient in telling when we have a overlap/collision if any part of the object is touching another object
As shwon with our rays:
So here is my solution.
When ever we have an object on our plane, we know everything about it, our coordinates of x,y,z - I am not dealing with y in this example so ignore that.
What we care about is our objects width = x and our objects depth/length = z. So in essence when we are checking to see if an object is overlapping another object we simply need to process through each min x/z and max x/z of our to be added object and our already rendered obj
How we do that is, loop through our object to be added starting min z position with a for loop. This starting min z position contains our position z + min z of object. This gives us the exact z position on our plane.
We than move on to our width or our object to be added starting min x position with a for loop. Don't forget our x gives us the exact position on our plane
Now the reason for this is simple. We want to start at position 0,0 of our object to be added. Increment by one up our length/z - increment all the way down our width/x and while doing this process each object on our plane that is already rendered and check to see if any exact point matches ours.
Here is the actual method:
checkOverlapObject: function(voxel) // THIS IS USED TO SEE IF WE ARE OVER LAPPING ANY OBJECTS
{
var collidableObjs = this.rooms;
//lets get our voxel min and max vars
var voxelMX = voxel.geometry.boundingBox.max.x;
var voxelMZ = voxel.geometry.boundingBox.max.z;
var voxelmX = voxel.geometry.boundingBox.min.x;
var voxelmZ = voxel.geometry.boundingBox.min.z;
// we need to get our voxel position ad to do some math to see if voxel min and max do not go beyound our boundries for our plane
var voxelPX = voxel.position.x;
var voxelPZ = voxel.position.z;
var totalPositionVoxelminZ = (voxelPZ + voxelmZ);
var totalPositionVoxelminX = (voxelPX + voxelmX);
var totalPositionVoxelMAXZ = (voxelPZ + voxelMZ);
var totalPositionVoxelMAXX = (voxelPX + voxelMX);
// start loop for object to add Z cordinate
for(var length = totalPositionVoxelminZ; length < totalPositionVoxelMAXZ; length++)
{
// start loop for object to add X cordinate
for(var width = totalPositionVoxelminX; width < totalPositionVoxelMAXX; width++)
{
for(var i = 0; i < collidableObjs.length;i++)
{
//lets get our voxel min and max vars
var thisvoxelMX = this.rooms[i].geometry.boundingBox.max.x;
var thisvoxelMZ = this.rooms[i].geometry.boundingBox.max.z;
var thisvoxelmX = this.rooms[i].geometry.boundingBox.min.x;
var thisvoxelmZ = this.rooms[i].geometry.boundingBox.min.z;
// we need to get our voxel position ad to do some math to see if voxel min and max do not go beyound our boundries for our plane
var thisvoxelPX = this.rooms[i].position.x;
var thisvoxelPZ = this.rooms[i].position.z;
var thistotalPositionVoxelminZ = (thisvoxelPZ + thisvoxelmZ);
var thistotalPositionVoxelminX = (thisvoxelPX + thisvoxelmX);
var thistotalPositionVoxelMAXZ = (thisvoxelPZ + thisvoxelMZ);
var thistotalPositionVoxelMAXX = (thisvoxelPX + thisvoxelMX);
for(var insideZ = thistotalPositionVoxelminZ; insideZ < thistotalPositionVoxelMAXZ; insideZ++)
{
for(var insideX = thistotalPositionVoxelminX; insideX < thistotalPositionVoxelMAXX; insideX++)
{
if(insideZ == length && insideX == width)
{
return false;
}
}
}
}
}
}
return true;
}
Here is our result:
This really does provide an exact answer on if your object is touching something it should not be touching because of the point by point it processes.
I hope this helps! Please feel free to use this at anytime.
Also note this really could effect overhead/memory usage if you are not careful on how you use this. I am working on a way to better optimize this in the case of having hundred of objects to process. So if you have any suggestions on modifying or adding to my existing code to accomplish this better and offer better performance in case of hundreds of objects passed, feel free provide details!