BabylonJs Viewer: Load Error: ... Unable to import meshes from

Hello everyone!

I’m a web developer with limited experience in 3D Asset making. I was designing a 3D Asset Library and I am using BabylonJs to do so. It’s an amazing library. But I ran into many problems, and finally into this one that I need the community’s help with.

I am using BabylonJs Viewer:

<script src="https://cdn.babylonjs.com/viewer/babylon.viewer.js"></script>

I am fetching a .gltf file from a backend nodejs server to then display the file in the viewer on the client’s browser.

In frontend, on HTML side. I’m doing:

<babylon id="babylon-viewer">
  <model id="gltf-model" url="${gltf}"></model>
</babylon>

But I’m doing this dynamically through Javascript:

// Disable auto init
BabylonViewer.disableInit = true;

// Fetch file from URL
fetch(url)
.then(response => {
	// Convert Json file to String
	gltf = "data:" + JSON.stringify(response.data);
}).then( () => {
	// init now
	BabylonViewer.InitTags('babylon');
})

But the Viewer on HTML side is showing an error saying “Error loading the model”.

And in developer console it’s showing the error:

I tested the file with glTF-Validator, and it shows me that the file is valid with no errors:

{
    "uri": "[File Name].gltf",
    "mimeType": "model/gltf+json",
    "validatorVersion": "2.0.0-dev.3.4",
    "validatedAt": "2021-12-10T19:20:49.493Z",
    "issues": {
        "numErrors": 0,
        "numWarnings": 0,
        "numInfos": 2,
        "numHints": 0,
        "messages": [
            {
                "code": "NODE_EMPTY",
                "message": "Empty node encountered.",
                "severity": 2,
                "pointer": "/nodes/1"
            },
            {
                "code": "NODE_EMPTY",
                "message": "Empty node encountered.",
                "severity": 2,
                "pointer": "/nodes/3"
            }
        ],
        "truncated": false
    },
    "info": {
        "version": "2.0",
        "generator": "VoxEdit Beta e2de1723",
            {
                "pointer": "/buffers/0",
                "mimeType": "application/gltf-buffer",
                "storage": "data-uri",
                "byteLength": 1296
            },
...

After doing research on gltf, I understand that my file is a gltf (embedded) file. I converted my files using CesiumGS/gltf-pipeline to convert my embedded gltf file to glb and Draco gltf but I was still getting “Unable to import meshes from data:” error.

However if I send the gltf files as a folder to the client when they visit the website, and then do this instead:

<babylon id="babylon-viewer">
  <model id="gltf-model" url="assets/model.gltf"></model>
</babylon>

Then the same gltf file generates the scene correctly. But I can’t do this as there are going to be 1000’s of gltf files in the library.

Could someone point me in the right direction where to go from here? I’m unable to solve this issue as the file seems correct, I don’t know which step I am missing. This is a bit new for me.

Thank you for any replies, I appreciate the time any of you took to help me!

Adding @RaananW as I am not sure if you can load models from JSON and not URLs in the viewer. You could try to store it with CreateObjectURL maybe but let s double check with @RaananW

Something feels a bit off in the way you are converting the model to a base64 string, but I can’t put my finger on it…

a gltf is practically a JSON with a schema. Have you managed to make it work in the playground? if it works there it should be supported in the viewer as well.

Yes gltf is basically Json. I tested it out in the Playground and I’m getting this error:

Here is the code I am using:

var createScene = function () {
    // This creates a basic Babylon Scene object (non-mesh)
    var scene = new BABYLON.Scene(engine);

    scene.createDefaultCameraOrLight();

    fetch('[API URL]')
    .then(response => {
        BABYLON.SceneLoader.Append("", "data:"+ JSON.stringify(response.data), scene, function () { 
            scene.createDefaultCamera(true, true, true);
        });
        return scene;
    });
    
};

The playground displays a blank screen, and shows a red error bar at the bottom saying “createScene function must return a scene.”

What other methods can I use to fetch and display a gltf embedded file via BabylonJs? Thank you for the responses.

It looks like you’re returning scene from the arrow function when you should be returning it from createScene. Try moving the “return scene;” line down to the end of the createScene function.

Thank you! That did remove the red error bar at the bottom. But the error on console stayed the same.

This led me to believe I need to use an expanded gltf type instead of gltf embedded because I believed the data BabylonJs was looking for wasn’t setting properly. So I tried with glb and Draco gltf, but no luck.

That’s why I tried the other way, and I am stuck with Import Mesh error.

I would also appreciate any other ways to display a fetched gltf file in the front-end/client side.

1 Like

The only way I’ve ever done that or seen it done is by passing the root URL and filename, not by passing the pre-fetched file contents. Like below for example. :slightly_smiling_face:
https://playground.babylonjs.com/#HANACW

1 Like

It is possible to save your scene as .babylon file - https://playground.babylonjs.com/#HANACW#1
and then call
scene = await BABYLON.SceneLoader.LoadAsync("", "data:" + json, engine, undefined, ".babylon");

Still I am not sure that 1000 json files are better than 1000 gltf files…

1 Like

Thank you! That is quite insightful!

With some testing, I was able to get something working. I’ll share my findings and ask my next question. Thanks for all the responses :slight_smile:

In the backend, I was earlier doing “res.sendFile([File path])”. But I changed that to follow this method of sending files: Send file as response. So now I’m sending the file as a 3D object, instead of a Json object.

Now on the front-end, I found out that I can’t use BabylonViewer with the 3D object either. I needed to use BabylonJs and glTFLoader using a Canvas HTML element.

Then I was able to code something similar to this:

var createScene = function (gltf) {

    fetch('[Fixed Gltf Sending API]')
    .then(response => {
        gltf = JSON.stringify(response.data);

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

        BABYLON.SceneLoader.Append("", "data:"+ gltf, scene, function () { 
            scene.createDefaultCamera(true, true, true);
            scene.activeCamera.alpha += Math.PI;
        });
    
        return scene;
    });

};

And have it working using BabylonJs and Canvas. Now I can show different gltf files :slight_smile:

So now for my issue, I really lovved BabylonViewer and the ability to select different animations, play/pause animations, and fullscreen:

I loved this as you could really view the asset and it’s animations. But how do I recreate these for my Canvas element:

<canvas id="renderCanvas"></canvas>

I know the solution for this thread is solved, but I would appreciate any pointers or directions to take from here.

Thank you for all your help! I was right to reach out to this community for help :smiley:


Edit:

The main solution here was sending a gltf file instead of gltf+json file from the backend Nodejs server. You must not use the simple “res.sendFile( [Path] )” command but rather send it as a gltf buffer. Then your model will work for both BabylonJs and BabylonViewer :slight_smile:

1 Like

Happy news! I solved it :slight_smile:

So the first problem was sending it as a gltf+json object, and not a Gltf file. You can read how I solved that in the previous post. That needs to be fixed/changed in order for this to work.

Then back in the front-end, I commented everything else related to Canvas element, and I wrote code that dynamically adds this HTML element:

    <babylon
      model.url="[API that return's 3D Object gltf file]"
      model.loader=".gltf"
    ></babylon>

This must be done, otherwise I was getting the original “importMesh” error. You need to specify it as model.url and model.loader.

If you do these 2 things, you can send a file from the backend nodejs server and use it on front-end :slight_smile:

I hope this thread helps other in the future! Thank you everyone who helped me solve this issue :smiley:

3 Likes