NullEngine and CreateGroundFromHeightMap

Hello boys.

I’m trying to create a nullEngine (for collision / movement) checking as serverSide.
What it should do:

  • Easy multiplayer game
  • Player press Key (forward, backward …) and this will send to the serverSide nullengine, All calculations will be done with MoveWithCollisions etc… and send back a position

Problem:

  • Im using at the client side CreateGroundFromHeightMap based on PNG image, When i try to load it into NullEngine for collisions and GroundY checking it wont work.

Error message:

ReferenceError: Image is not defined
    at Function.e.LoadImage (\node_modules\babylonjs\babylon.js:16:56416)
    at Function.e.CreateGroundFromHeightMap (\node_modules\babylonjs\babylon.js:16:1066376)
    at Function.CreateGroundFromHeightMap (\node_modules\babylonjs\babylon.js:16:1787480)
    at Terrain.loadMap (\server\src\World/terrain.js:49:42)
    at new Loader (\server\src\World/loader.js:10:21)
    at new Scene (\server\src/scene.js:12:9)
    at Object.<anonymous> (c:\Program Files\XAMPP\htdocs\2darena/app.js:60:1)
    at Module._compile (internal/modules/cjs/loader.js:955:30)
    at Module._compile (\node_modules\pirates\lib\index.js:99:24)
    at Module._extensions..js (internal/modules/cjs/loader.js:991:10)

ImageFile is in

  • app.ts
  • /server/src/
    • scene.ts
    • /World/
      – heightmap.png
      – Terrain.ts (Where im loading img)
Terrain.mapData[0] = {
            mapX: 1024,
            mapZ: 1024,
            terrainSub: 256,
            maxHills: 200,
            minHills: -2,
            heightMap: "./heightmap.png"
        };
           let mapX = Terrain.mapData[mapId].mapX;
            let mapZ = Terrain.mapData[mapId].mapZ;
            let terrainSub = Terrain.mapData[mapId].terrainSub;
            let maxHills = Terrain.mapData[mapId].maxHills;  
            let minHills = Terrain.mapData[mapId].minHills;  
            let options = {width: mapX, height: mapZ, subdivisions: terrainSub, minHeight: minHills,  maxHeight: maxHills, onReady: onGroundReady, updatable: false };

            let ground = BABYLON.MeshBuilder.CreateGroundFromHeightMap("ground", Terrain.mapData[mapId].heightMap, options, Scene._scene);

When trying to use (FileSystem) it says file exists at this path.

Sorry i’m not very experienced and still learning.

Thanks for all answers

I tried to use vertex Data and load image with UINT8Array. Using Filesystem and Buffer converted to Uint8Array

      fs.readFile(Terrain.mapData[mapId].heightMap, function(err: any, data:any) 
     {
         var ab = new ArrayBuffer(data.length);
         var view = new Uint8Array(ab);
         for (var i = 0; i < data.length; ++i) {
             view[i] = data[i];
         }
         //let mapdata = new Uint8Array(data);

         let options = {width: mapX, height: mapZ, subdivisions: terrainSub };
         let vertex_options = {buffer: view, width: mapX, height: mapZ, subdivisions: terrainSub, minHeight: minHills, maxHeight: maxHills, bufferHeight: 1024, bufferWidth: 1024 }
        
         let ground = BABYLON.MeshBuilder.CreateGround("map_"+ mapId, options, Scene._scene)
         let vertex = BABYLON.VertexData.CreateGroundFromHeightMap(vertex_options)
         vertex.applyToMesh(ground, false);

         ground.updateCoordinateHeights();
          console.log(ground.getHeightAtCoordinates(0, 10));
...}

But this load without error, but does not work with getHeightAtCoordinates (Always return -2) as minHills: -2.

I had the same issue being unable to install the canvas module on Node.js.
I did the same thing as you, difference being how I create the mesh:

const ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 4000, height: 4000, subdivisions:100, updatable:true}, scene);
const positions = this.ground.getVerticesData(BABYLON.VertexBuffer.PositionKind);
let groundHeightIndex = -1;
for(let i=0;i<positions.length;i+=3){
    positions[i+1] = groundHeights[groundHeightIndex++];
}
ground.updateVerticesData(BABYLON.VertexBuffer.PositionKind, positions);
ground.updateCoordinateHeights();

I did something like that to make it work.

2 Likes

@Raggar thanks for response, ill try it.

I wonder if i can store vertexData into a file and load it on server Nodejs null engine

F.e.

  • Creating custom.app with canvas engine and create groundFromHeightMaps then loop throu position and normals then save it in file and load it at the server side.

Hi Saqirm,

Yes, you can do that :slight_smile:

You can export your scene (or mesh(es)) using the scene serializer: Save Your Scene - Babylon.js Documentation

This way you can get the vertices data needed to (re)create the ground mesh.

1 Like

@RaananW I tried it with the function in the DOC but it does not work

BABYLON.SceneLoader.ImportMesh(“ground_” + mapId, “http://127.0.0.1:3000/img/”, “ground_0.babylon”, Scene._scene, (newMeshes:any) =>
{
console.log(newMeshes);
});

returns newMeshes[0] returns undefined

.babylon is here (In ZIP due to forum protection)
ground_0.zip (1.5 MB)

Thanks :slight_smile:
Any chance of hosting this file somewhere and providing a playground for us to play with? I think it will be more helpful for everyone

Babylon.js Playground This does works for me in Console show me a Mesh

Im using: Express, http, XHR

however if i do

BABYLON.SceneLoader.ImportMesh(“ground_” + mapId, “https://raw.githubusercontent.com/Quentis/test/master/”, “ground_0.babylon”, Scene._scene, (newMeshes:any) =>
{
console.log(newMeshes);
});

my SceneCode is

> constructor()
> 
>     {
> 
>         Scene._engine = new BABYLON.NullEngine();
> 
>         Scene._scene = new BABYLON.Scene(Scene._engine);
> 
>         new BABYLON.PointLight("Omni", new BABYLON.Vector3(20, 20, 100), Scene._scene);
> 
>         new BABYLON.ArcRotateCamera("Camera", 0, 0.8, 100, BABYLON.Vector3.Zero(), Scene._scene);
> 
>         let time = Date.now();
> 
>         console.log("Starting BabylonJS Server");
> 
>         // we need load all objects and world now
> 
>         new Loader(() => 
> 
>         {
> 
>             console.log("Server is loaded in ", (Date.now() - time) + "ms");
> 
>             Scene._engine.runRenderLoop(function() {
> 
>                 Scene._scene.render();
> 
>             });
> 
>         });
> 
>     }

Loader:

    let terrain = new Terrain(); // Loading MapID

    for (let i in Terrain.mapData)

    {

        terrain.loadMap(0, (map: any) => 

        {

@RaananW Okay sorry, this works for me if i m not using Arrow function, I dont know why but when i use function(newMeshes) instead of () => it works for some reason.

THanks for suggestion

— Edit: Okay im retarded prolly, Problem was in meshNames not in Arrow function.

1 Like