Tiled Ground using SPS and TextureAtlas. Updated

Updated version of my tiled SPS ground with TextureAtlas.

The lower left corner is not broken, but intentional :slight_smile:

Playground w/ Material

Of course, we can also use an array of colors:


Playground w/ Colors

Code and Explaination.

// creation function, returns the SPS.
// @options
//     tileSize: INT, The size of each tile, in BABYLON units. default 1.
//     rows: INT, The number of rows of tiles, default 1.
//     cols: INT, The number of columns of tiles, default 1.

// Options when using a material / textureAtlas
//     material: BABYLON.Material, default Null.
//     sheetHeight: INT, The number of ROWS of sprites/textures in the material texture/Atlas, default 1.
//     sheetWidth: INT, The number of COLUMNS of sprites/textures in the material texture/Atlas, default 1.
//     tileUvs: Array [ {u: 1, v: 1} ], An array indexing each tile's UV, starting from Array[0] being the first tile, default Null.

// Option when Not using a material / textureAtlas
//     colors: Array[ BABYLON.COLOR3 ], An array, like tileUvs, but containing BABYLON.Color3's for each tile instead of uv data. default Null.

function createTiledGround(options){
    if(!options) options = {};

    let tileSize = options.tileSize || 1;
    let rows = options.rows || 1;
    let cols = options.cols || 1;
    let sheetHeight = options.sheetHeight || 1;
    let sheetWidth = options.sheetWidth || 1;
    let material = options.material || null;
    let tileUvs = options.tileUvs || null;
    let colors = options.colors || null;

  // SPS creation
  var plane = BABYLON.MeshBuilder.CreateGround('plane', {width: tileSize, height: tileSize});
  var SPS = new BABYLON.SolidParticleSystem('SPS', scene, {isPickable: true});
   SPS.addShape(plane, rows * cols);

    var mesh = SPS.buildMesh();
        mesh.material = material ? material : null;
    plane.dispose();

  SPS.initParticles = function() {
      var _this = this;
    // just prepare everything
     for (var _c = 0, xlen = cols; _c < xlen; _c++) {
         for (var _r = 0, zlen = rows; _r < zlen; _r++) {
            var particle = _this.particles[_r + (rows*_c)];
                particle.indexNum = (_r + (rows*_c));
                particle.position.x = (((_r - (rows/2))) + (tileSize/2)) * tileSize;
                particle.position.z = (((_c - (cols/2))) + (tileSize/2)) * tileSize;
                if(material && tileUvs){
                    particle.__uv = tileUvs[_r + (rows*_c)];
                }
                else if(colors){
                    particle.__color = colors[_r + (rows*_c)];
                }
        }
    }
    return SPS;
  };

  SPS.updateParticle = function(particle) {
      if(particle.__uv){
          let u = particle.__uv.u;
          let v = particle.__uv.v;
            particle.uvs.x = u/sheetWidth;
            particle.uvs.z = (u+1) /sheetWidth;
            particle.uvs.y = v/sheetHeight;
            particle.uvs.w = (v+1) /sheetHeight;
      }
      else if(!particle.__hasColor) {
            particle.__hasColor = true;
            if(particle.__color){
                particle.color = particle.__color;
            }
            else {
                particle.color = new BABYLON.Color3((Math.random()), (Math.random()), (Math.random()));
            }
      }
      return SPS;
  };

  
// __processPicking - Optional.
// pickResult - The result from a scene.pick()
// callback - "onComplete" callback, returns the picked particle, if any.
    SPS.__processPicking = function(pickResult, callback){
        let _this = SPS;
        if(!pickResult || pickResult.pickedMesh !== _this.mesh || pickResult.faceId === -1){
            return;
        }
        if(typeof(callback) === "function"){
            callback(_this.particles[_this.pickedParticles[pickResult.faceId].idx]);
        }
        
        // Update SPS
        _this.setParticles();
    };

  return SPS;
}

.
Stressing the SPS.

10.000 tiles.

Colors
https://www.babylonjs-playground.com/#1ALQPZ#40

Material
https://www.babylonjs-playground.com/#1ALQPZ#41
.

101.124 tiles.

Colors
https://www.babylonjs-playground.com/#1ALQPZ#43

Material
https://www.babylonjs-playground.com/#1ALQPZ#44

250.000 tiles.

Colors
https://www.babylonjs-playground.com/#1ALQPZ#45

Material
https://www.babylonjs-playground.com/#1ALQPZ#46

501.264 tiles.

Colors
https://www.babylonjs-playground.com/#1ALQPZ#47

Material
https://www.babylonjs-playground.com/#1ALQPZ#48

1 Million tiles ? View at own risk. Will mostlikely crash on low-end devices ;-)

Colors
https://www.babylonjs-playground.com/#1ALQPZ#49

Material
https://www.babylonjs-playground.com/#1ALQPZ#50

9 Likes

You totally rock, aWeirdo! THANK YOU!

When do we get to see a picture of you, aW? Perhaps an “about” page, somewhere? :slight_smile:

1 Like

The SPS is usually a cool guy, he doesn’t get stressed easily :smiley:

4 Likes

Thanks!
But no pictures i’m afraid :smile:

1 Like

That’s a lot of tiles. I was surprised to get 144 fps at 1 million.

Might be interesting to make a tile picker algo that generates a line from the ray, determines the point at which the line would cross the plane of the tiles (via math, not a real collision check), and then just does floor(point / tileWidth) which would be the coordinates of the tile that was picked. That’d be like picking one out of a million in a few nanoseconds.

Are you making something in specific out of this? I’ve been digging through SPS wanting to make something very large as well.

What a nice example! I am new to BablyonJS (and game dev in general). Last night I messed around with trying to create a tiled ground with procedurally generated grass texture. However as I increased the length and width of the tiled ground, the FPS dropped significantly. It was disheartening to see that even at 100x100 the FPS was down to 5 or 7 at best. I saw that at 20x20 it was still functional and decided to settle for that. I found this post by stumbling through an entirely different topic and was elated to see tiled ground implemented with SPS, and that it can even handle 1 million tiles.

Keep up the good work.

Cheers,
Frank

2 Likes

Have you seen that it’s now expandable ?

You can now add/remove solid particles on demand (at some cost though)

3 Likes

I did see that actually, just today :slight_smile:

This link shows some large scale use of SPS for those who are interested. It’s just a webpage with a video of the demo but you can see around 200K SPS over San Francisco.

http://blog.illuminated.city/

3 Likes