MeshWriter integration in Angular+Babylon project

Hi,
I have Angular+Babylon project.
I added MeshWriter extension into scripts in angular.json:
“scripts”: [
“node_modules/meshwriter/dist/meshwriter.js”
]

and use it in my engine.service.ts:

declare const MeshWriter: any;

…and call it like this:
let Writer = MeshWriter(this.scene, {scale:1});
let text1 = Writer(
“ABC”,
{
“anchor”: “center”,
“letter-height”: 50,
“color”: “#1C3870”,
“position”: {
“z”: -2
}
}
);

but I got error:
ERROR ReferenceError: BABYLON is not defined
at makeMaterial (meshwriter.js:548)
at MeshWriter (meshwriter.js:206)

Question: how to use MeshWriter if it uses BABYLON inside meshwriter.js, like:
new BABYLON.StandardMaterial(“mw-matl-”+letters+"-"+weeid(), scene);

Thank you,
ogo.

Pinging @TheLeftover

H @ogo and welcome.
This could be worth a read if you have not already done so 3D text using meshwriter in Angular

Thank you, guys!

Yes, I read suggested post, but I have a little bit different problem:
In my Angular code I import, for example, StandardMaterial as

import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial'; 

and I can directly use it like this:
let material = new StandardMaterial(’’, this.engineService.scene);

I can call MeshWriter as
let writer = MeshWriter(this.scene, {scale:1});

It returns me a function. But when I call this function, it tries to create new StandardMaterial as
new BABYLON.StandardMaterial(“mw-matl-”+letters+"-"+weeid(), scene);

it returns an error:
ERROR ReferenceError: BABYLON is not defined at makeMaterial (meshwriter.js:548)

So BABYLON is not defined in MeshWriter and it’s not defined in my code.
It should be some trick here :slight_smile:
Any ideas?

Thank you,
ogo.

@ogo, sorry for the difficulty. I wrote MeshWriter. Let me try to help.

MeshWriter is intended as an extension to BABYLON designed to operate when BABYLON is loaded and there is a live scene. So BABYLON.StandardMaterial must be accessible.

Is this a situation where you want to run MeshWriter without BABYLON? Or is the problem that the MeshWriter code cannot find BABYLON?

Hi @TheLeftover, thank you for reply.

The problem is that MeshWriter cannot see my Babylon.js. I think the problem is in the scope or namespace.

In my Angular code I have Babylon.js as a npm package. This is from my package.json:

    "dependencies": {
    "@angular/animations": "^9.1.0",
    "@angular/common": "^9.1.0",
    "@angular/compiler": "^9.1.0",
    "@angular/core": "^9.1.0",
    "@angular/forms": "^9.1.0",
    "@angular/platform-browser": "^9.1.0",
    "@angular/platform-browser-dynamic": "^9.1.0",
    "@angular/router": "^9.1.0",

    "@babylonjs/core": "^4.1.0",
    "@babylonjs/gui": "^4.1.0",
    "@babylonjs/inspector": "^4.1.0",
    "@babylonjs/materials": "^4.1.0",
    "babylonjs": "^4.1.0",

    "hammerjs": "^2.0.8",
    "meshwriter": "^1.2.3",
    "node-sass": "^4.13.1",
    "pepjs": "^0.5.2",
    "replace-in-file": "^5.0.2",
    "rxjs": "^6.5.5",
    "tslib": "^1.11.1",
    "zone.js": "^0.10.3"
  },

MeshWriter is added as external script. This is from my angular.json:

"scripts": [ "node_modules/meshwriter/dist/meshwriter.js" ]

I import Babylon.js modules like this:

import { Engine } from '@babylonjs/core/Engines/engine';
import { Scene } from '@babylonjs/core/scene';
import { ArcRotateCamera, DirectionalLight, ShadowGenerator, IShadowLight, Tools, HighlightLayer, KeyboardEventTypes } from '@babylonjs/core';
import { Vector3, Color3, Color4 } from '@babylonjs/core/Maths/math';
import { MeshBuilder } from '@babylonjs/core/Meshes/meshBuilder';
import { GridMaterial } from '@babylonjs/materials/grid/GridMaterial';

This may I can use them directly. For example:

    this.scene = new Scene(this.engine);
    this.scene.clearColor = new Color4(0, 0, 0, 0);
    this.highlightLayer = new HighlightLayer('', this.scene);

So I use “new Scene”, not “new BABYLON.Scene”, etc.

There is no BABYLON in my code.

I tried different approaches, like

import * as BABYLON from 'babylonjs';
or
declare const BABYLON: any;

but it doesn’t help.

I declare MeshWriter this way:

declare const MeshWriter: any;

then in my function I use it as:

So, MeshWriter is visible, but it cannot find BABYLON when it tries to create material.

Do you know how should I define/declare/import BABYLON to make it visible in MeshWriter?

Thank you,
ogo.

@ogo, thanks. Very clear explanation.

There must be a way but I don’t know what it is. Hopefully someone else nearby can offer some advice.

I looked through the MeshWriter code; it is highly dependent on the assumption that there is a global object, BABYLON with a load of methods. Not just StandardMaterial. Here is an exhaustive list:
~ BABYLON.Vector2
~ BABYLON.Vector3
~ BABYLON.Path2
~ BABYLON.Curve3
~ BABYLON.Color3
~ BABYLON.SolidParticleSystem
~ BABYLON.PolygonMeshBuilder
~ BABYLON.CSG
~ BABYLON.StandardMaterial
~ BABYLON.Mesh

I noticed elsewhere on the board that someone else had a similar problem. We should come up with a recipe. This must have encountered this problem with other BABYLON extensions or legacy code.

A shout out to other BABYLONians. What say ye?

Further musings:

MeshWriter definitely needs all of those methods. Taking them from a single object BABYLON has worked fine. If they are now in multiple modules, I could support a way of injecting them into MeshWriter as a parameter. Like you inject “scene” currently.

Internally, MeshWriter could duct-tape those into an object, fauxBABYLON, and run per normal. Or you could do the same with a global variable BABYLON you make. Both approaches sound a little hacky but they would work fine.

Assuming that seemed like the chosen approach, we still need a programmers guide to the methods that MeshWriter uses. I don’t know where to find them in the modules and you load them.

1 Like

Just some ideas…

I wonder if putting this would work @ogo - Do you have a repo we can test on?

import * as BABYLON from '@babylonjs/core/Legacy/legacy';
window.BABYLON = BABYLON;

Otherwise it would need an ES6 version of MeshWriter or you can pass BABYLON as an optional constructor param - like is done for the Physics Plugins?

Wow! Babylonjs community works!

Thank you, @TheLeftover and @brianzinn for your help!

abc

See result above? I use suggested code:

import * as BABYLON from '@babylonjs/core/Legacy/legacy';
(<any>window).BABYLON = BABYLON;
declare const MeshWriter: any;

But I have to pass BABYLON to MeshWriter:

let writer = MeshWriter(this.scene, {scale:1 } );
writer(BABYLON,
    "ABC",
    {
        "anchor": "center",
        "letter-height": 50,
        "color": "#1C3870",
        "position": {"z": -2}
    }
);

and change MeshWriter source code - commented out this code:

 // if ( typeof BABYLON === "object" ) {
 //   BABYLON.MeshWriter         = Wrapper;
 //   supplementCurveFunctions();
 // };

added first parameter, and added supplementCurveFunctions() function call:

  function MeshWriter(babylon, lttrs,opt){
    supplementCurveFunctions();
    ...

It’s not ideal solution, it works for me, but it should be some official way to attach MeshWriter to Angular BABYLON object or pass BABYLON object to MeshWriter.
It would be interested how other Angular projects include MeshWriter, maybe there is some simple solution…

Thank you very much!
ogo.

2 Likes

@ogo, I am very pleased this worked for you. Very resourceful of you to find the right places to make the changes. Also, thank you for coming back with the resolution. Super useful for the rest of us.

@brianzinn, thanks for bridging the gap.

I will publish an update that allows something similar to be done with unadulterated code . . . once I pick a specific mechanism.

1 Like

@TheLeftover, after additional testing I found that the BABYLON object declared in Angular code is visible to MeshWriter at the time Angular calls MeshWriter(), but it’s not visible at the time meshwriter initialized. That’s why in my case this block in your code is not executed at the start:

if ( typeof BABYLON === "object" ) {
          BABYLON.MeshWriter         = Wrapper;
          supplementCurveFunctions();
};

But BABYLON is visible in MeshWriter function. It means that I don’t have to pass BABYLON to MeshWriter!
The only problem is that supplementCurveFunctions() was not called. But we can call it in MeshWriter() function. This call is safe because you have a check inside the function if it was already executed.

So if we add only one line to your code, it works for me and for all other cases.

function MeshWriter(lttrs,opt){
    supplementCurveFunctions();  // call this function in case it was not called before
    ...
}

In summary: for now I use meshwriter.js with one added line and I have following code in Angular project:

    import * as BABYLON from '@babylonjs/core/Legacy/legacy';
    (<any>window).BABYLON = BABYLON;
    declare const MeshWriter: any;

Thanks for your help and for your excellent plugin!
ogo.

3 Likes

Dang!! Well investigated and brilliantly explained.

I will take a look, and probably update, this weekend. Appreciate the extra effort to give back to future programmers, @ogo.

1 Like
import * as BABYLON from '@babylonjs/core/Legacy/legacy';

Thanks, this line fix my error:
Uncaught TypeError: Cannot read property ‘prototype’ of undefined
at supplementCurveFunctions

Hi @TheLeftover, would you still be keen to implement this injection of the methods you need? With Babylon supporting tree shaking, it would be ideal so we don’t need to import the whole library, but only required files. That typically helps us shrink the size of our app significantly.

1 Like

I understand . . . I think.

In broad terms, you would like to have a method where you could pass in the BABYLON functions MeshWriter needs (e.g. Vector2, Vector3 . . .).

It could be a parameter into the so-called “superconstructor”. So, to amend the readme at https://github.com/briantbutton/meshwriter:

funObj    = { Vector2 : mysrc.Vector2, Vector3: mysrc.Vector3 . . . };
Writer    = MeshWriter ( scene, { scale : scale , functions : funObj } );

Is that what you were thinking?

1 Like

Yes that’s exactly what I have in mind. Doing that would mean I can import only the bits and pieces I need from Babylon and not the whole thing, I’d then pass over to you the parts you need. This would cut down on app bundle size.

PS: I’m open to helping with a potential PR, but might require some guidance, I’ll have a look into the code today.

I suspect it would be easier for me to do the code changes. It won’t be too complex and I made some notes last time.

The changes are low risk but we have a pile of users these days. Once I get a prototype, would you be open to performing some testing?

1 Like

Definitely, I can install from a github branch to test the new functionality. I don’t have a codebase to test backward compatibility though, but I’m sure you got that.

There are some new files: index.js and dist/meshwriter.js and dist/meshwriter.min.js. First cut.

The new call allows a field called ‘methods’ in the options object. If present, it will pull all the methods from there. Otherwise it will try to pull them from the BABYLON global object.

I used the syntax below in https://www.babylonjs-playground.com/#PL752W#374

        Writer = BABYLON.MeshWriter(scene, {scale:scale,defaultFont:"Arial",methods:BABYLON});

If you could test, inserting the methods but without BABYLON in the global address space, that would be helpful.

2 Likes