Issues with mesh loading in Electron renderer.ts

Its’ my first time trying to create an electron app coming from a C/C++/Python with some django background and I am not quite understanding whats happening in this code. It’s from this repo here. BabylonJS-Electron/renderer.ts at master · justinctlam/BabylonJS-Electron · GitHub

I am trying to load a obj file knight.obj and display it on the screen. However it’s not working.

Nothing seems wrong, and I do not see any obviously glaring errors in the code, or the console output from Electron.

I know its not the Obj file because I am able to load it using an html example found in the playground (Can’t find original link) and the Knight loads up fine.

Does anyone have a working example? What am I doing wrong?

import * as BABYLON from 'babylonjs';
import 'babylonjs-loaders';

export default class Renderer {
    private _canvas: HTMLCanvasElement;
    private _engine: BABYLON.Engine;
    private _scene: BABYLON.Scene;

    createScene(canvas: HTMLCanvasElement, engine: BABYLON.Engine) {
        this._canvas = canvas;

        this._engine = engine;

        // This creates a basic Babylon Scene object (non-mesh)
        const scene = new BABYLON.Scene(engine);
        this._scene = scene;

        // This creates and positions a free camera (non-mesh)
        const camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);

        // This targets the camera to scene origin
        camera.setTarget(BABYLON.Vector3.Zero());

        // This attaches the camera to the canvas
        camera.attachControl(canvas, true);

        // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
        const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);

        // Default intensity is 1. Let's dim the light a small amount
        light.intensity = 1;

        // Our built-in 'sphere' shape. Params: name, subdivs, size, scene
        //const sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);

        // Move the sphere upward 1/2 its height
        //sphere.position.y = 1;

        var loader = new BABYLON.AssetsManager(scene);

        var head = loader.addMeshTask("head", "", "http://192.168.1.54:8080/", "knight.obj");
        var meshThin;
        var meshFat;
        
        head.onSuccess = function() {
            meshThin = head.loadedMeshes[1];
            meshFat = head.loadedMeshes[0];
            console.log("No. of vertices for thin: "+meshThin.getTotalVertices());
            console.log("No. of vertices for fat: "+meshFat.getTotalVertices());
        }

        // Our built-in 'ground' shape. Params: name, width, depth, subdivs, scene
       const ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
    }

    initialize(canvas: HTMLCanvasElement) {
        const engine = new BABYLON.Engine(canvas, true);
        this.createScene(canvas, engine);

        engine.runRenderLoop(() => {
            this._scene.render();
        });

        window.addEventListener('resize', function () {
            engine.resize();
        });
    }
}

const renderer = new Renderer();

I used this not ts in browsesr playground as a test for the obj https://www.babylonjs-playground.com/#28YUR5#713

It loads fine

I think @julien-moreau has some experiences with Electron

AssetTasks catches errors and only reports them via the onError handler. Can you try adding an onError callback and see if it reports anything?

Hmm interesting, changed it to add a onError handler but nothing was output. Not sure if I did it 100% right but this is what I changed.

head.onSuccess = function(head) {
    meshFat = head.loadedMeshes[0];
    console.log("No. of vertices for fat: "+meshFat.getTotalVertices());
}

head.onError = function (head, message, exception) {
    console.log(head, message, exception);
}

It wasn’t this. LOOL

head.onSuccess = function(head) {
meshFat = head.loadedMeshes[0];
console.log("No. of vertices for fat: "+meshFat.getTotalVertices());
}

head.onError = function (head, message, exception) {
console.log(head, message, exception);
}

Bahaha it was stupid easy and took me days.

    var loader = new BABYLON.AssetsManager(scene);

var head = loader.addMeshTask("head", "", "http://192.168.1.54:8080/", "knight.obj");
var meshThin;
var meshFat;

loader.load();

head.onSuccess = function() {
1 Like

I recently started babylon.js, so it might be wrong …

Looking at the code written above, it looks like the onSuccess callback is assigned after the load. Perhaps the order in which the code is executed is different?

The implementation of AssetsManager.addMeshTask () is as follows.

public addMeshTask(taskName: string, meshesNames: any, rootUrl: string, sceneFilename: string): MeshAssetTask {

        var task = new MeshAssetTask(taskName, meshesNames, rootUrl, sceneFilename);

        this._tasks.push(task);

        return task;

    }

The task is executed and the onSuccess or onError callback is executed.

public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {

        SceneLoader.ImportMesh(this.meshesNames, this.rootUrl, this.sceneFilename, scene,

            (meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[]) => {

                this.loadedMeshes = meshes;

                this.loadedParticleSystems = particleSystems;

                this.loadedSkeletons = skeletons;

                this.loadedAnimationGroups = animationGroups;

                onSuccess();

            }, null, (scene, message, exception) => {

                onError(message, exception);

            }

        );

    }

Task execution is started with AssetManager.load ().

    public load(): AssetsManager {
        if (this._isLoading) {
            return this;
        }
        this._isLoading = true;
        this._waitingTasksCount = this._tasks.length;
        this._totalTasksCount = this._tasks.length;

        if (this._waitingTasksCount === 0) {
            this._isLoading = false;
            if (this.onFinish) {
                this.onFinish(this._tasks);
            }
            this.onTasksDoneObservable.notifyObservers(this._tasks);
            return this;
        }

        if (this.useDefaultLoadingScreen) {
            this._scene.getEngine().displayLoadingUI();
        }

        for (var index = 0; index < this._tasks.length; index++) {
            var task = this._tasks[index];
            if (task.taskState === AssetTaskState.INIT) {
                this._runTask(task);
            }
        }

        return this;
    }

I think it doesn’t work because your code is assigning a callback after load.
This code works fine.

import * as BABYLON from 'babylonjs';
import 'babylonjs-loaders'

export default class Renderer {
    private _canvas: HTMLCanvasElement;
    private _engine: BABYLON.Engine;
    private _scene: BABYLON.Scene;

    createScene(canvas: HTMLCanvasElement, engine: BABYLON.Engine) {
        this._canvas = canvas;

        this._engine = engine;

        // This creates a basic Babylon Scene object (non-mesh)
        const scene = new BABYLON.Scene(engine);
        this._scene = scene;

        var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 0.84, Math.PI/2.4,
        20, new BABYLON.Vector3(0,0,0), scene);
       camera.attachControl(canvas, true);

        // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
        const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);

        // Default intensity is 1. Let's dim the light a small amount
        light.intensity = 0.7;

        var loader = new BABYLON.AssetsManager(scene);
        console.log("loader created");

        var head = loader.addMeshTask("head", "", "your-local-server", "knight.obj");
        var meshThin;
        var meshFat;
        
        head.onSuccess = function() {
            meshThin = head.loadedMeshes[1];
            meshFat = head.loadedMeshes[0];
            console.log("head.loadedMeshes.length = ", head.loadedMeshes.length)
        }

        head.onError = function (head, message, exception) {
            console.log(head, message, exception);
        }

        loader.loadAsync()
    }

    initialize(canvas: HTMLCanvasElement) {
        const engine = new BABYLON.Engine(canvas, true);
        this.createScene(canvas, engine);

        engine.runRenderLoop(() => {
            this._scene.render();
        });

        window.addEventListener('resize', function () {
            engine.resize();
        });
    }
}

const renderer = new Renderer();
renderer.initialize(document.getElementById('render-canvas') as HTMLCanvasElement);

1 Like

Thanks yes. This was the solution to the issue, and I agree that loadAsync is a better use here. Appreciate the help!

1 Like