Can't import GridMaterials in latest Babylon in NextJS + Typescript - Example Repo

Hello everyone! :slight_smile:
First time for posting anything here, so I hope it’s the correct channel.

tdlr:
I can’t use import {Meshbuilder} from babylonjs with import {GridMaterials} from '@babylonjs/materials because when I try to add the gridMaterial, Typescript is throwing the error that 128 properties are missing to assign a GridMaterial to ground.material.

I wrote an example repo with some “starter-like” setup of the current configuration we have in our project. We use NextJS, the latest Babylon 6.11.2.
We would like to use the import style 'import { Scene, Meshbuilder } from “babylonjs” ’ in our project and were happy with it so far.

We used in a older project the GridMaterial and wanted to use it again. But the Material get’s importet with “@babylonjs/materials” and expects “@babylonjs/core” to be installed too. Even if I add the core ( basically double ) I got typescript issues, that the GridMaterial cannot be assigned to ground.material.
I tried a whole day and couldn’t make any version work.

Please share any suggestions here, or on github directly. Thank you!

Hi @julianhahn and wlecome to the forum
I’m not experienced enough with this kind of issue but the best I know is @RaananW

Hello and welcome!

So, mixing UMD (the babylonjs-* style packages) and ES6 (@babylonjs/* style) is not recommended, from my understanding, they work in completely different ways and having both styles in one project won’t work, as you’ve already seen. But since we build the entire project for both styles, you should be able to get everything in UMD, and GridMaterial should be available in babylonjs-materials - npm (npmjs.com) which is the one you’ll want if you want to use the UMD core. If GridMaterial is not available there then please let us know and we’ll fix it asap :slight_smile:

thanks, carol for the answer! Just to clarify:

  • import {Meshbuilder} from babylonjs
    and
  • import {GridMaterials} from ‘@babylonjs/materials’

are both ESM and should therefore work together? Sorry I never did a deep dive into the different js import possibilities and just assumed that everything with ‘require’ is UMD and ‘import’ is ESM.

If my assumption is true, then this code snippet should work?

import { Scene, MeshBuilder } from "babylonjs";
import { GridMaterial } from "@babylonjs/materials";

const scene = new Scene(engine, { useGeometryUniqueIdsMap: true });
let gridMaterial = new GridMaterial("grid", scene);

Currently that combination does not work (without even running it, just building it)
Error:

- wait compiling...
- error ./node_modules/@babylonjs/materials/cell/cell.fragment.js:2:0
Module not found: Can't resolve '@babylonjs/core/Engines/shaderStore.js'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/@babylonjs/materials/cell/cellMaterial.js
./node_modules/@babylonjs/materials/cell/index.js
./node_modules/@babylonjs/materials/index.js
./src/visualizer/scene.ts
./src/components/VisualizerCanvas/VisualizerCanvas.tsx

babylonjs, babylonjs-materials, etc… are UMD: NPM Support | Babylon.js Documentation (babylonjs.com)
@babylonjs/core, @babylonjs/materials, etc… are ESM: Babylon.js ES6 support with Tree Shaking | Babylon.js Documentation (babylonjs.com)

So the correct snipped would be:

import { Scene, MeshBuilder } from "babylonjs";
import { GridMaterial } from "babylonjs-materials";

const scene = new Scene(engine, { useGeometryUniqueIdsMap: true });
let gridMaterial = new GridMaterial("grid", scene);

It’s a bit confusing to get used to I agree, Javascript has way too many ways of doing things :rofl:

require vs import is a bit different than UMD vs ES6, the former relates to the way of, well, importing/bringing modules into your code, while the latter is the way the modules are organized internally. I asked Bing to elaborate on the difference between require and import and it gave a nice summary:

require and import are two different ways to include external modules in JavaScriptrequire is a function used to import modules in Node.js, while import is a new keyword that is used to import modules in ECMAScript 6 (ES6)1.

The major difference between require and import, is that require will automatically scan node_modules to find modules, but import, which comes from ES6, won’t2. Most people use babel to compile import and export, which makes import act the same as require2.

With import, you can selectively load only the pieces you need, which can save memory. Loading is synchronous (step by step) for require, on the other hand, import can be asynchronous (without waiting for previous import) so it can perform a little better than require3.

And on the difference between UMD and ES6:

UMD stands for Universal Module Definition. It is a pattern that allows JavaScript modules to work in different environments, such as the browser and Node.js1. UMD is usually used as a fallback module when using bundlers like Rollup or Webpack1.

ES6 modules, on the other hand, are a new standard for defining and importing modules in JavaScript. They are part of the ECMAScript 6 (ES6) specification and are supported by modern browsers and transpilers1. ES6 modules have a simple syntax, are asynchronous in nature, and support tree-shaking, which allows sites to ship less code for faster load times1.

In summary, UMD is a pattern that allows modules to work in different environments, while ES6 modules are a new standard for defining and importing modules in JavaScript

Hope this helps clear up things a bit, feel free to ask if you need any more clarification :slight_smile:

1 Like

A big thanks for the long explanation!
We definitely want to go with ESM for Treeshaking. Thanks also for the provided ES6 Link, I did not stumble upon that before.

Then on to a new “problem”. I switched for all imports to @babylonjs/core and @babylonjs/materials.
Now I need to import the previously used debug layer as it’s own package with @babylonjs/inspector right? After I imported that, I got the message that a few more dependencies were missing. So I added manually with yarn, all dependencies until the warnings stopped.

I ended up with:

"dependencies": {
    "@babylonjs/core": "^6.12.1",
    "@babylonjs/gui": "^6.12.1",
    "@babylonjs/gui-editor": "^6.12.1",
    "@babylonjs/inspector": "^6.12.1",
    "@babylonjs/loaders": "^6.12.1",
    "@babylonjs/materials": "^6.11.2",
    "@babylonjs/serializers": "^6.12.1",

then I followed the instructions on the documentation for the inspector:

But in the end I still got some importing error.

The currently used code snippet would be:

import { Engine, Scene } from "@babylonjs/core";
import "@babylonjs/core/Debug/debugLayer"; // Augments the scene with the debug methods
import "@babylonjs/inspector"; // Injects a local ES6 version of the inspector to prevent automatically relying on the none compatible version

import { GridMaterial } from "@babylonjs/materials";

const scene = new Scene(engine, { useGeometryUniqueIdsMap: true });
scene.debugLayer.show();

let gridMaterial = new GridMaterial("grid", scene);

Which results in the error:

- error ./node_modules/@babylonjs/gui-editor/dist/babylon.guiEditor.max.js:3:0
Module not found: ESM packages (@babylonjs/core) need to be imported. Use 'import' to reference the package instead. https://nextjs.org/docs/messages/import-esm-externals

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/@babylonjs/inspector/dist/babylon.inspector.bundle.max.js
./src/visualizer/scene.ts
./src/components/VisualizerCanvas/VisualizerCanvas.tsx
- warn Fast Refresh had to perform a full reload due to a runtime error

all the changes can be seen in the repo:

Oh noes :open_mouth: Let me tag @RaananW to take a look at this as he’s way more knowledgeable about everything build in javascript!

sorry to push, but could I would love to implement the Inspector again in our project.

He’s OOF until the middle of the week if I remember correctly, but once he’s back he will be glad to help :slight_smile:

Hi @julianhahn - i’ll pull the project and see what happened there. thanks for the patience :slight_smile:

Oh my, that was an interesting one :slight_smile:

So, the reason is the way the modules are being parsed (probably by webpack, but I didn’t dig too much). The inspector depends on gui-editor, which is relatively optionaly if you don’t use GUI, but we can’t detect it when compiling. The problem is that the package is still using require (and not directly import), which is “UMD”-ish. Next didn’t like it at ALL, because it is an external dependency. If you would use it in your project directly (like you do the inspector) it would parse it correctly.

The solution is async-loading (which I would recommend anyhow, since the inspector will influence your package size, and is usually not needed in production). Instead of calling

scene.debugLayer.show();

Remove all references to the inspector and debug layer, and add this instead:

void Promise.all([
    import("@babylonjs/core/Debug/debugLayer"),
    import("@babylonjs/inspector"),
  ]).then((_values) => {
    scene.debugLayer.show({
      // config goes here, if needed
    });
  });

it’ll then work correctly:

2 Likes

Thanks for your time! Obviously, it worked for you somehow, but I cannot get it to run. Where did you put your code snippet?

I removed the references and imports on top, the direct .show call and replaced them with the Promises.all(). Sadly my error is not gone yet. Just to confirm my understanding: your fix would just be to load the imports ‘manually’ in the function itself, put it in a promise.all which waits for each promise to resolve and then use the previous scene to show the debug.layer?
So we are basically just manoeuvring around the next compiler and it’s hard warning about external libraries?

In short: this change, still produces the same error for me:

that’s interesting. it’s all I did, but let me be sure.

Interessting:

In combination with your “promise solution” and the webpack config under next.config.js:

/** @type {import('next').NextConfig} */

const nextConfig = {
  experimental: {
    esmExternals: true,
  },
};

module.exports = nextConfig;

got it from here: ESM packages need to be imported | Next.js

the demo worked for a brief time. I saw my scene and the inspector. I’m not sure what happened then. On the next start or after a hotreload the whole setup crashed again and could not be started again.

Sorry, yes - I have enabled esmExternals as well, and set it to loose. The async import resolves a few issues as well.

I have submitted a PR just to show you how I would fix it. It works locally (dev mode)

Thanks again for your time and effort! I cloned your pr, changed nothing and started it with yarn dev. The scene starts and I see the Babylon inspector, but the ground and sphere are lost, due to another importing issue. I tried already to import the StandardMaterial manually but it did not change anything. So sadly I could not ‘reproduce’ a working setup on my end yet, even with directly cloning your fork and branch.


Hello @RaananW , it would be awesome if you could find some time this week to take another look at it.

The error is quite descriptive :slight_smile:

You need to import the standard material to get it to work.

Until now you were loading everything from @babylonjs/core (the base directory) which prevented tree shaking from working correctly. My PR was just showing you how you can do that a little better (and get the inspector working as well). The missing line is

import "@babylonjs/core/Materials/standardMaterial";

In scene.ts.

I pushed it to the PR, but I would recommend you reading the tree-shaking doc page that describes this: