How to load a mesh with Babylon.js server-side (NullEngine)

Hello, I’m trying to load a .glb mesh in a Node.js (server-side) environment with NullEngine.
But I get this error:

Server running on port 8000Failed to load mesh: ReferenceError: XMLHttpRequest is not definedat createXMLHttpRequest (file:///D:/Mohammad%20hossein/Web/Back/soccer-star-3d/dev/core/src/Misc/webRequest.ts:15:9)at new WebRequest (file:///D:/Mohammad%20hossein/Web/Back/soccer-star-3d/dev/core/src/Misc/webRequest.ts:23:29)at file:///D:/Mohammad%20hossein/Web/Back/soccer-star-3d/dev/core/src/Misc/webRequest.fetch.ts:16:25at new Promise ()at _FetchAsync (file:///D:/Mohammad%20hossein/Web/Back/soccer-star-3d/dev/core/src/Misc/webRequest.fetch.ts:15:18)at loadDataAsync (file:///D:/Mohammad%20hossein/Web/Back/soccer-star-3d/dev/core/src/Loading/sceneLoader.ts:592:36)at importMeshAsync (file:///D:/Mohammad%20hossein/Web/Back/soccer-star-3d/dev/core/src/Loading/sceneLoader.ts:929:18)at file:///D:/Mohammad%20hossein/Web/Back/soccer-star-3d/dev/core/src/Loading/sceneLoader.ts:994:13at new Promise ()
import BABYLON from "@babylonjs/core";
import LOADERS from "babylonjs-loaders";
import "@babylonjs/havok";

const initScene = () => {
  // constant
  let queue: Array<any> = [];


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

  var light = new BABYLON.PointLight(
    "Omni",
    new BABYLON.Vector3(20, 20, 100),
    scene
  );

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

  BABYLON.ImportMeshAsync(
    "./src/public/models/ball.glb", 
    scene
  ).then((result) => {
    console.log("Mesh loaded:", result.meshes.map((m) => m.name));
  }).catch((err) => {
    console.error("Failed to load mesh:", err);
  });

  console.log("render started");
  engine.runRenderLoop(function () {
    scene.render();
  });
};

export default initScene

Hi!
XMLHttpRequest is available when running in a browser.

You need to something like this (node example):

  const buffer = await fs.readFile(glbPath);
  const data = new Uint8Array(buffer); // provide all parameters needed here
  await ImportMeshAsync(data);

ImportMeshAsync supports multiple data types for the first parameter:

See:

2 Likes

I wonder why it doesn’t try to use fetch natively - now that undici is supported officially.

I believe this should also work:

import XMLHttpRequest from 'xhr2';

global.XMLHttpRequest = XMLHttpRequest;

Source: XMLHttpRequest issues when running Babylonjs on a server (nodejs and TypeScript) - #2 by Panuchka

4 Likes

When you use

app.use(express.static("public"));

the public folder becomes the root for static files.
So in your project:

public/
 └─ images/
     └─ btn.png

The correct URL is:

http://localhost:8000/images/btn.png

:cross_mark: Not:

http://localhost:8000/src/public/images/btn.png

IMHO, this is not the right way :wink: Mimicking browser behavior instead of using Node’s native functions is a no-go, at least for me.

You can read from the filesystem directly or use fetch (Node18+).

However if the OP is satisfied, let it be. For future readers - do not do this.

4 Likes

@RaananW
@Cedric

Ah good point :smiley: agree that using Node’s built-in functions is better

1 Like