Jump to content

CreateGroundFromHeightMap use 2d array instead of image url


Recommended Posts

Hi there,

I'd like to procedurally generate a height map in run time and don't want to save it to the image, as it costs in additional overhead.

I'm looking for a way to use 2d array instead of image url (the array dimension may serve as a number of subdivisions). 

Obviously I don't want to rewrite the CreateGroundFromHeightMap function, because this function is works fine and there is no need at all to reinvent a wheel.

What I'd expect for, is that CreateGroundFromHeightMap will consist of two functions - the basic one, which receives the two dimensional array, and the wrapper function which converts the image to the 2d array.

I'm wondering how can I do it without changing the code of the framework (maybe I'm missing something), and why its's not done this way so far?


P.s. If you will look at the implementation of CreateGroundFromHeightMap, you will notice that this is what actually happening there - first of all get the data from the image, and then build the height map. It's just not organized well (or, again, maybe I'm missing something). 

Link to comment
Share on other sites

Binyan, why not invent your own wheel?


You have seen the Mesh.CreateGroundFromHeightMap function near the bottom of Mesh-class source code.  Why not copy it, and put it into your own code as a standard function?  See where it says var buffer = blah blah?  Just force a 2-dimensional array of 0-255 values in there.... with a crowbar. :)


Once you have your own CreateGroundFrom2DArray function, you are the boss.  Just a thought.  :)


Maybe put a little note at the top:  "I didn't borrow any of this code from babylon.js core.  I wrote it all myself while blindfolded and working outside during a hail storm.  Ain't I wonderful?"  :D

Link to comment
Share on other sites

  • 2 weeks later...

Can anybody explain me what's going on in this line in CreateGroundFromHeightMap method?

var position = new BABYLON.Vector3((col * width) / subdivisions - (width / 2.0), 0, ((subdivisions - row) * height) / subdivisions - (height / 2.0));

I'm staring at it almost an hour and still cannot figure out how subdivisions are taken in account here.

As far as I see there is a quite simple transformation but it doesn't come to my mind.

Link to comment
Share on other sites

Hiya binyan.  That is a mesh traverser.  Given a ground of ANY width (x) and height (z), divided by any amount of subdivisions, this positioner line visits each row and column of that subdivided width and height.  Again, height is Z, not Y.


If you make a ground... and set it's material wireframe, it visits every intersection point (every vertex).  (it traverses vertices)


Here is a test I made, and its zip.  You will see your line in the 'traverse' function which is called once per frame.  i have a speed-slower in there, so this test will give you terrible frame rates.  It is slowed down so you can watch the box traverse.  Notice I used the global variables width, height, and subdivisions... to make the ground.  Then later, down by the traverse function, I use those globals to make a few more globals such as rows and cols.  I could not use nested FOR loops inside a animation function such as traverse(), because that would bring the render loop to a dead stop.


My code is probably quite bad.  I wrote it quick.


Hope this helps.

Link to comment
Share on other sites

Thank you Wingnut, now it's much clearer.

But what happens when a number of subdivisions is greater than width and height?


P.S. Btw, height is very confusing in this case. Don't you think that length would be a better name?

Link to comment
Share on other sites

My pleasure.  It is quite common for the number of subdivisions to be a larger number than the width and height (length/depth).  Lets say you made a ground 10 units by 10 units, with 100 subdivisions.  Then each row and column would be how wide?  Yep. .1 units (1/10 of a unit).  The 'subdivisions'  = how many times do we divide-up the width and height.  Keep your ground.material.wireframe = true/1... and make a few hundred different grounds.  Experiment, friend.  Put in some hours...  drive the framework.  Its not just really fun, but its full of learning, too.  :) 


Yes, the term 'height' meaning the z-axis... is a bit confusing... especially when used with heightMaps.  The authors of the framework are reading our conversation, and if there is a way to change it without breaking lots of things, they will. 


I noticed another thing today... with createSphere... where the subdivisions is before the size.... in the constructor parameters/args.  They should probably switch places.  We will see what happens in the future.  But you are really smart, I can tell, so you can figure out any strange things.  :)


You are probably close to building your array.  Good luck with that.

Link to comment
Share on other sites

  • 4 months later...

Any luck yet with this? I see that this will be available in the next version, I checked the source now and it looks like it still needs to be added or did i overlook something? 


it would be nice if we can somehow create "holes" in the ground too, so that if there is no data a hole is visible in the ground.

Link to comment
Share on other sites

i took a look at the VertexData.CreateGroundFromHeightMap you pointed out. Either i didnt understand it fully or its just not made for what i was looking for. Id like to be able to create a ground using an array filled with heights. the ground should be able to have holes in it. 


i made a function which does this, its a very dirty way of achieving this as it still keeps the vertex data in the ground mesh it just skips the indices. I thought why not share it but if there is a better way to do this please let me know. 


CreateGroundFromArray = function (width, height, subdivisions, buffer, bufferWidth, bufferHeight) {    var vertexData = new BABYLON.VertexData();    vertexData.positions = [];    vertexData.normals = [];    vertexData.indices = [];    vertexData.uvs  = [];    var row, col;    var indicesToSkip = {};    var pos = 0;    for (row = 0; row <= subdivisions; row++) {        for (col = 0; col <= subdivisions; col++) {            if (buffer[pos] * 0.1 === -12345.6) {                indicesToSkip[pos] = true;                vertexData.positions.push(0, 0, 0);                vertexData.uvs.push(0,0);            }            else            {                // Add  vertex                vertexData.positions.push((col * width) / subdivisions - (width / 2.0), buffer[pos], ((subdivisions - row) * height) / subdivisions - (height / 2.0));                vertexData.uvs.push(col / subdivisions, 1.0 - row / subdivisions);            }                        pos++;        }    }    var subdivisionsPlus = subdivisions + 1;    for (row = 0; row < subdivisions; row++) {        for (col = 0; col < subdivisions; col++) {                        var indexA = col + 1 + (row + 1) * subdivisionsPlus;            var indexB = col + 1 + row * subdivisionsPlus;            var indexC = col + row * subdivisionsPlus;            var indexD = col + (row + 1) * subdivisionsPlus;            var indexE = col + row * subdivisionsPlus;                        var needsToBeSkipped = false;            if (!!indicesToSkip[indexA])                needsToBeSkipped = true;            else if (!!indicesToSkip[indexB])                needsToBeSkipped = true;            else if (!!indicesToSkip[indexC])                needsToBeSkipped = true;            else if (!!indicesToSkip[indexD])                needsToBeSkipped = true;            else if (!!indicesToSkip[indexE])                needsToBeSkipped = true;                        if (!needsToBeSkipped)            {                //Triangle 1 and triangle 2                vertexData.indices.push(indexA, indexB, indexC, indexD, indexA, indexE);            }        }    }    // Normals    BABYLON.VertexData.ComputeNormals(vertexData.positions, vertexData.indices, vertexData.normals);    // Result        return vertexData;};


to use this function just pass it a "buffer" with heights for each vertex (a height of -12345.6 will result in a hole)


Edit: Updated code with my latest version

Edit 2: Updated code with my latest version

Edit 3: Updated code major performance increase


If anyone finds any further optimizations please let me know ;)

Link to comment
Share on other sites

  • 2 years later...

Re-up this thread: i'm having problems using function CreateGroundFromHeightMap with a buffer containing elevation data, rather than using a .png. I'm using babylon 2.5.

I've not problems creating the terrain with a png:

var ground = BABYLON.Mesh.CreateGroundFromHeightMap("ground", "perlin.png", 1000, 1000, 100, -10, 10, newScene, false);

But when I try to use as elevation buffer an array containing one million (1000*1000) integers in the range 0-255:

var vertexData = BABYLON.VertexData.CreateGroundFromHeightMap(1000, 1000, 100, -10, 10, perlinbuffer, 1000, 1000);
vertexData.applyToMesh(ground, false);

I obtain empty structures.

Also if I put in firebug only the latter part BABYLON.VertexData.CreateGroundFromHeightMap(1000, 1000, 100, -10, 10, perlinbuffer, 1000, 1000) the result are empty arrays for positions[], indices[], normals[] and uvs[].

I'm I calling that function in a wrong way or with wrong parameters?

Link to comment
Share on other sites

Many tnx for your reply adam, you've always a solution for everything :)

Following your suggestions solves the terrain creation problem, but now i've problems "interacting" with the terrain by the mouse. Let me explain: with a terrain created with CreateGroundFromHeightMap() I could by example raise terrain faces at mouseclicks using VertexBuffer:

        var pickInfo=window.engine.scenes[0].pick(offsetX, offsetY);
        var mesh = pickInfo.pickedMesh;
        var faceID = pickInfo.faceId;
        var indices = mesh.getIndices();
        var vertices = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
//Raise the height of vertices in this face id
        vertices [indices[faceID*3]*3+1]+=1;
        vertices [indices[faceID*3+1]*3+1]+=1;
        vertices [indices[faceID*3+2]*3+1]+=1;
        mesh.updateVerticesData(BABYLON.VertexBuffer.PositionKind, vertices,true,true);

Now i've problems doing this: this code runs well only for the first time I click the terrain, from the second time the mesh seems to become "invisible" to mouse clicks (but it's still visible on screen). It seems that mesh.updateVerticesData() breaks something in the mesh structure... Any ideas?

Link to comment
Share on other sites

Wow tnx adam, I see your PG working, but I've always the same problem using a very similar code: first click works, then terrain becomes unselectable (and I noticed that also boundingBox disappears). Quite strange, i can't explain myself.

I try to upload here an example, if someone wants to give a check and report me what's wrong...


Many thanks


Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...