CreateGroundFromHeightMap on ServerSide NullEngine

Hello,

I am trying to create a ground from height map on a Node.js server, so that I can use the height map when testing collisions.

I’ve got everything working on the server side for NullEngine, but when I call the function CreateGroundFromHeightMap it throws this error:

\node_modules\babylonjs\babylon.max.js:128244
createImageBitmap(new Blob([data], { type: mimeType })).then(function (imgBmp) {
^

ReferenceError: createImageBitmap is not defined
at \node_modules\babylonjs\babylon.max.js:128244:17
at \node_modules\babylonjs\babylon.max.js:128382:13
at XMLHttpRequest.onReadyStateChange (\node_modules\babylonjs\babylon.max.js:128445:29)
at XMLHttpRequest.dispatchEvent (\node_modules\xhr2\lib\xhr2.js:72:22)
at XMLHttpRequest._setReadyState (\node_modules\xhr2\lib\xhr2.js:422:14)
at XMLHttpRequest._onHttpResponseEnd (\node_modules\xhr2\lib\xhr2.js:615:14)
at IncomingMessage._response.on (\node_modules\xhr2\lib\xhr2.js:567:23)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)

I guess createImageBitmap is usually defined by the browser, so I would either need to replicate it on the server. But it seems like the NullEngine should either remove functions with missing pieces or include some workaround natively?

OR maybe I’ve done something wrong?

You can have a look at this thread:

1 Like

Thanks for the feedback. I found that post when I first encountered the problem, and it seems that they ended up going with a hacky solution, as opposed to the CreateGroundFromHeightMap just working natively. I will probably end up going with something like that though.

Thanks again

I just did a quick test using node-canvas, but for some reason the height are off. I can’t spot any obvious errors on my part, so perhaps it’s due to differences in encoding or the sorts.
This Playground:
https://playground.babylonjs.com/#8YNWBF
logs:
0.47058823529411764
0.8823529411764711
0.47058823529411764
1.9215686274509804

Where as my NodeJS test logs:
0.47058823529411764
0.8823529411764705
0.47058823529411764
1.843137254901957

var BABYLON = require("babylonjs");
var LOADERS = require("babylonjs-loaders");
global.XMLHttpRequest = require('xhr2').XMLHttpRequest;
const { createCanvas, loadImage } = require('canvas')

var engine = new BABYLON.NullEngine();
var scene = new BABYLON.Scene(engine);

var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0.8, 100, BABYLON.Vector3.Zero(), scene);

var ground = CreateGroundFromHeightMap("ground", "https://d33wubrfki0l68.cloudfront.net/e07eb4ae422752ba27d10f0b4e47dae6be14405c/4dca2/img/how_to/heightmap/worldheightmap.jpg", 200, 200, 250, 0, 10, scene, false, function(){
	var pos = ground.getHeightAtCoordinates(0, 0);
	console.log(pos)
    pos = ground.getHeightAtCoordinates(-50, 50);
    console.log(pos)
    pos = ground.getHeightAtCoordinates(50, -50);
    console.log(pos)
    pos = ground.getHeightAtCoordinates(60, 60);
    console.log(pos)
});


scene.registerBeforeRender(function(){
	scene.render();
});


function CreateGroundFromHeightMap(name, url, width,height,subdivisions,minHeight,maxHeight,scene,updatable,onReady) {
	var options = {width:width,height:height,subdivisions:subdivisions,minHeight:minHeight,maxHeight:maxHeight,updatable:updatable,onReady:onReady};
    return CreateGroundFromHeightMapGBR(name, url, options, scene);
}



function CreateGroundFromHeightMapGBR(name, url, options, scene) {
    var width = options.width || 10.0;
    var height = options.height || 10.0;
    var subdivisions = options.subdivisions || 1 | 0;
    var minHeight = options.minHeight || 0.0;
    var maxHeight = options.maxHeight || 1.0;
    var filter = options.colorFilter || new BABYLON.Color3(0.3, 0.59, 0.11);
    var alphaFilter = options.alphaFilter || 0.0;
    var updatable = options.updatable;
    var onReady = options.onReady;


    var ground = new BABYLON.GroundMesh(name, scene);
    ground._subdivisionsX = subdivisions;
    ground._subdivisionsY = subdivisions;
    ground._width = width;
    ground._height = height;
    ground._maxX = ground._width / 2.0;
    ground._maxZ = ground._height / 2.0;
    ground._minX = -ground._maxX;
    ground._minZ = -ground._maxZ;

    ground._setReady(false);

    var onload = (img) => {
        var bufferWidth = img.width;
        var bufferHeight = img.height;
			
		const canvas = createCanvas(bufferWidth, bufferHeight)
		const context = canvas.getContext('2d')

        if (!context) {
            throw new Error("Unable to get 2d context for CreateGroundFromHeightMap");
        }

        if (scene.isDisposed) {
            return;
        }

        context.drawImage(img, 0, 0);

        // Create VertexData from map data
        // Cast is due to wrong definition in lib.d.ts from ts 1.3 - https://github.com/Microsoft/TypeScript/issues/949
        var buffer = context.getImageData(0, 0, bufferWidth, bufferHeight).data;
        var vertexData = BABYLON.VertexData.CreateGroundFromHeightMap({
			width: width, height: height,
			subdivisions: subdivisions,
			minHeight: minHeight, maxHeight: maxHeight, colorFilter: filter,
			buffer: buffer, bufferWidth: bufferWidth, bufferHeight: bufferHeight,
			alphaFilter: alphaFilter
        });

        vertexData.applyToMesh(ground, updatable);

        //execute ready callback, if set
        if (onReady) {
            onReady(ground);
        }

        ground._setReady(true);
    };

	loadImage(url).then((image) => onload(image))


    return ground;
}
1 Like

Thank you, I’ll have to take a look at that! My original thought was that the function should just work out of the box, but since that appears unlikely, I think writing it up from the ground will be the solution. What you’ve done is definitely on the right track, I appreciate your help & I’ll let you know if I can get it working!

Thanks again!