Difficulty loading node material from json file using NodeMaterial.Parse()

I’m early in my learning of all things BABYLON (both the framework and Javascript) and have spent several hours on trying to load a NodeMaterial from a locally stored json file as exported by the NodeMaterial editor.

I have tried a number of different solutions. All end in tears.

This method seems to have gotten the furthest in that all parameters to LoadNodeMaterial() have reasonable values as printed by the log lines.

  const LoadNodeMaterial = function(data, mesh) {
  	console.log('Loaded json data:', JSON.stringify(data));
  	console.log('Supplied mesh:', mesh);
  	console.log("Scene:", scene);
  	mesh.material = BABYLON.NodeMaterial.Parse(data, scene);
  	console.log('and now here!');
  } 

and

  fetch('./nm.json')
  		.then(response => response.json())
  		.then(data => LoadNodeMaterial(data, box))
  		.catch(error => {
  			console.error('Fetch of ./nm.json has failed.', error);
  		});

This is the resulting error:

TypeError: undefined is not an object (evaluating ‘t.inputs[i].isExposedOnFrame=e.isExposedOnFrame’)
(anonymous function) — babylon.js:16:2238555
forEach
(anonymous function) — babylon.js:16:2238452
(anonymous function) — babylon.js:16:2238331
(anonymous function) — babylon.js:16:2390718
(anonymous function) — babylon.js:16:2339873
(anonymous function) — babylon.js:16:2340992
LoadNodeMaterial — localhost:45
(anonymous function) — localhost:51
promiseReactionJob

I have also tried solutions using:

  const box_material = new BABYLON.NodeMaterial("box_material");
  box_material.loadAsync("./nm.json").then(() => {
  		console.log('before');
  		box_material.build(true);
  		console.log("after");
  });
  box.material = box_material;

This also fails, differently.

Any help would be greatly appreciated.

Thank you

2 Likes

Hello and welcome!

Seems that in your last example you need also the second argument scene for the new NodeMaterial.

const box_material = new BABYLON.NodeMaterial("box_material", scene);

Here is working PG - https://playground.babylonjs.com/#Y642I8#17

4 Likes

Thank you @labris. I have this code now which tracks your playground:

  let box_material = new BABYLON.NodeMaterial("box_material", scene);
  box_material.loadAsync("./nm.json").then(() => {
  		console.log('before');
  		box_material.build(true);
  		console.log("after");
  });
  box.material = box_material;

None of the console.log()'s are printed and the following error is returned:

Unhandled Promise Rejection: TypeError: undefined is not an object (evaluating ‘t.inputs[i].isExposedOnFrame=e.isExposedOnFrame’)

Thinking perhaps there is something wrong with my ./nm.json, I substituted your URI to make:

  var box = BABYLON.MeshBuilder.CreateBox("box", {});
  let box_material = new BABYLON.NodeMaterial("box_material", scene);
  box_material.loadAsync("https://raw.githubusercontent.com/eldinor/ForBJS/master/nodeMaterial.json").then(() => {
  		console.log('before');
  		box_material.build(true);
  		console.log("after");
  });
  box.material = box_material;

and this receives the same error as pasted above.

1 Like

Probably you have some import missing in your setup.
Do you import NodeMaterial from Materials/Node?
(Babylon.js/src/Materials/Node at master · BabylonJS/Babylon.js · GitHub)

2 Likes

Derp! No?
I don’t have knowledge of “import.” As I mentioned, I’m new to Javascript (almost a half century of C and C++ mostly). I haven’t imported anything as yet. Here is a link to the complete project (1 file).

https://www.mediafire.com/file/58w1z5y5shnb7wm/index.html/file

At the risk of imposing too much… how would I import in this case?

Thanks

1 Like

It seems there is an awful lot I don’t know. :cry:
I’ll start working through:

tomorrow.

Depending on whether you want TS or JS there are a couple reference repositories available that you can use to help get yourself going with the BJS NPM packages and such. This thread also has useful info

Here’s a link to an early, simple branch of a JS application. See the develop Branch for a more complex app, and if you want bleeding edge, this is the latest and greatest (as of today).

HTH!

1 Like

Because of the using Node material you need to include babylonjs.materials.min.js too, so the head section now looks like:

<head>
	<script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>

index.zip (1.2 KB)
(see attachment with working file)

There are several more scripts which could be included (or required for some tasks) with Babylon.js. Below is the script list as it is when one downloads Playground example.
While NPM building is great, the old way of including scripts is still simple and usable.

        <!-- Babylon.js -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
        <script src="https://preview.babylonjs.com/ammo.js"></script>
        <script src="https://preview.babylonjs.com/cannon.js"></script>
        <script src="https://preview.babylonjs.com/Oimo.js"></script>
        <script src="https://preview.babylonjs.com/earcut.min.js"></script>
        <script src="https://preview.babylonjs.com/babylon.js"></script>
        <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
        <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
        <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
        <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
        <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
        <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
        <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
2 Likes

Thank you very much!
I was concerned I would have a great deal more to learn all up-front but your advice makes the added burden unnecessary! I hadn’t picked up on the need for additional includes. I figured all of BJS was included in the babylon.js file.
Thank you again!

2 Likes

Seems sometimes the need for the additional scripts is not as evident in the documentation as it could be :slight_smile:
Below there are short explanations:

dat.gui.min.js - Only if you use DAT GUI
ammo.js, cannon.js, Oimo.js - Only if you use one of these physics engines
earcut.min.js - Only if you need irregular polygons
babylonjs.materials.min.js - If you use Babylon.js materials library or Node materials
postProcess.min.js - If you use postprocesses
babylonjs.loaders.js - If you load any 3D models - GLTF, OBJ etc (no need if you use native .babylon format)
babylonjs.serializers.min.js - If you need to export scene or meshes to different formats
babylon.gui.min.js - If you use Babylon GUI
babylon.inspector.bundle.js - Recommended to use at development stage, call Inspector with scene.debugLayer.show();

pep.js - for touch interactions (see “First App on the Web”)

5 Likes

Note that the Ammo plugin requires an Async initialization, e.g. let ammo = await Ammo(); then you can initialize the AmmoJsPlugin normally

4 Likes

I feel badly that I have to ask this but I get a syntax error when I use:

let ammo = await Ammo();

or

const ammo = await Ammo();

or

await Ammo();

The error specifically is:

SyntaxError: Unexpected identifier ‘Ammo’. Expected ‘;’ after variable declaration.

The code is:

let ammo = await Ammo();
const physicsPlugin = new BABYLON.AmmoJSPlugin();
scene.enablePhysics(gravityVector, physicsPlugin);

Without the await, Ammo will report that it is not yet read, as expected.

I have included both this:

and also from “preview”.

I am sorry to be a burden. I appreciate your help.

1 Like

It’s no burden - thinking through your question has caused me to recall all the difficulties I had getting it working myself, which I’d mostly forgotten!

What I ended up doing is adding a reference directly to the ammo GH repos via npm imstall <GitHub repos>. Then I created a promise wrapper for the async initialization:


import * as Ammo from "ammo.js";

export let ammoModule;
export const ammoReadyPromise = new Promise((resolve) => {
    new Ammo().then((res) => {
        ammoModule = res;
        resolve(res);
    });
});

Then you can await the ammoReadyPromise which resolves with the actual ammo module

1 Like

Alas, I am not using node or npm. Just straight javascript / html.
What’s prompting this is:

  • Oimo has insufficient accuracy to avoid glaring problems (like a “brick” falling through the ground).
  • Cannon has the initial glitchy impulse on page reload.
  • I can’t get Ammo loaded.
    Color me sad.
1 Like

Hey there, if you do like below does it produce any error message in the console or editor? You may have missed the step to initialize it first. :slightly_smiling_face:

HTML:
<script src="https://preview.babylonjs.com/ammo.js"></script>
<script src="https://preview.babylonjs.com/babylon.js"></script>

JS:
await Ammo();
scene.enablePhysics(new BABYLON.Vector3(0,-9.81, 0), new BABYLON.AmmoJSPlugin());
1 Like

I implemented exactly what you suggest. I continue to get a syntax error on the line containing the await.
It says:

SyntaxError: Unexpected identifier ‘Ammo’

My “includes” are:

The other relevant code is:

const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);

1 Like

How about like this at the top of your script? Await needs to be used from within an async function, is the issue I think. I can confirm that below is working for me without error, for example: :slightly_smiling_face:

window.addEventListener("DOMContentLoaded", async () => {
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);
    const scene = new BABYLON.Scene(engine);

    await Ammo();
    scene.enablePhysics(new BABYLON.Vector3(0,-9.81, 0), new BABYLON.AmmoJSPlugin());

    // other stuff to setup the scene, etc.
});

Edit, you can also create and call an async setup function if you don’t want to use the DOMContentLoaded event handler:

const setup = async () => {
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);
    const scene = new BABYLON.Scene(engine);

    await Ammo();
    scene.enablePhysics(new BABYLON.Vector3(0,-9.81, 0), new BABYLON.AmmoJSPlugin());

    // other stuff to setup the scene, etc.
};

await setup();
3 Likes

@Blake your first solution is working for me. I will try the optimization later.

Thank you all very much for your help - you each have been very generous.

On to adding more functionality (and bumping into more problems).

3 Likes

Awesome, and PS I just included the other version for completeness, just in case, but I would use the DOMContentLoaded event handler to ensure that the canvas element is created and ready before trying to use it from javascript, for example. :slightly_smiling_face: :beers:

2 Likes