Loading OBJ Model in BabylonJs Not Loading

244 Views Asked by At

I have a Blazor WASM project that I load Babylon scene. When I load a simple scene (drawing a sphere on a plane) everything loads fine. But, I am trying to load an *.obj file, which is stored on storage on Azure and I want to display it in Babylon scene. The page loads without errors but the scene does not load. This is my Babylon code:

        var createMeshScene = async function () {
            var scene = new BABYLON.Scene(engine);
            //Adding a light
            var light = new BABYLON.PointLight("Omni", new BABYLON.Vector3(20, 20, 100), scene);
            //Adding an Arc Rotate Camera
            var camera = new BABYLON.ArcRotateCamera("Camera", 0, 1, 0, BABYLON.Vector3.Zero(), scene);
            camera.attachControl(canvas, false);        
            fetch("https://myapp.myorg.appserviceenvironment.us/myFile.obj",
                {
                    mode: "no-cors",
                    headers: {
                        'Content-Type': 'application/octet-stream'
                    },
                }
            ).then((val) => {
                val.blob().then(blob => {
                    const file = new File([blob], "myFile.obj");
                    BABYLON.SceneLoader.ImportMesh("", "", file, scene, function (newMeshes) {
                        // Set the target of the camera to the first imported mesh
                        camera.target = newMeshes[0];
                        newMeshes[0].scaling.scaleInPlace(50);
                    });
                });
            })
            scene.registerBeforeRender(function () {
                light.position = camera.position;
            });
           
            return scene;
        }

In my _Host.cshtml file I load these libraries:

    <script src="https://assets.babylonjs.com/generated/Assets.js"></script>
    <script src="https://cdn.babylonjs.com/recast.js"></script>
    <script src="https://cdn.babylonjs.com/ammo.js"></script>
    <script src="https://cdn.babylonjs.com/havok/HavokPhysics_umd.js"></script>
    <script src="https://cdn.babylonjs.com/cannon.js"></script>
    <script src="https://cdn.babylonjs.com/Oimo.js"></script>
    <script src="https://cdn.babylonjs.com/earcut.min.js"></script>
    <script src="https://cdn.babylonjs.com/babylon.js"></script>
    <script src="https://cdn.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://cdn.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://cdn.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://cdn.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://cdn.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://cdn.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

When the scene loads, in the console, I can see there are no errors and this is displayed:

Babylon.js v6.22.1 - WebGL2 [.WebGL-0000505C0711AA00]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels

I also debug into BABYLON.SceneLoader.ImportMesh and can see the files load fine but nothing is shown.

EDIT For more context, this is the full JS :

window.initializeBabylon = () => {
    var canvas = document.getElementById("renderCanvas");

    var startRenderLoop = function (engine, canvas) {
        engine.runRenderLoop(function () {
            if (sceneToRender && sceneToRender.activeCamera) {
                sceneToRender.render();
            }
        });
    }

    var engine = null;
    var scene = null;
    var sceneToRender = null;
    var createDefaultEngine = function () { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false }); };

    var createMeshScene = async function () {
        var scene = new BABYLON.Scene(engine);
        //Adding a light
        var light = new BABYLON.PointLight("Omni", new BABYLON.Vector3(20, 20, 100), scene);
        //Adding an Arc Rotate Camera
        var camera = new BABYLON.ArcRotateCamera("Camera", 0, 1, 0, BABYLON.Vector3.Zero(), scene);
        camera.position = new BABYLON.Vector3(500, 500, 500); 

        camera.attachControl(canvas, false);       
        var response = await fetch("assets/MyObj.obj");

        if (!response.ok) throw new Error("Failed to load: MyObj.obj " + response);
        var blob = await response.blob();
        var file = new File([blob], "MyObj.obj");

        BABYLON.SceneLoader.ImportMesh("", "", file, scene, function (newMeshes) {
                    // Set the target of the camera to the first imported mesh
                    camera.target = newMeshes[0];
                    newMeshes.forEach(m => m.scaling.scaleInPlace(0.5));
        });

        scene.registerBeforeRender(function () {
            light.position = camera.position;
            light.intensity = 0.7;
        });
       
        return scene;
    }


    engine = createDefaultEngine();
    if (!engine) throw 'engine should not be null.';
    startRenderLoop(engine, canvas);
    scene = createMeshScene();
    sceneToRender = scene;
};

This gets called in my razor page like this:

 protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JSRuntime.InvokeVoidAsync("initializeBabylon");
        }
    }
1

There are 1 best solutions below

1
LoneSpawn On

Short answer:

  • Move your camera so it is not inside the mesh.
camera.position = new BABYLON.Vector3(500, 500, 500); 
  • Omit increasing the size of your mesh or even scale it down instead. Also, scale the entire mesh array not just the first mesh.
// newMeshes[0].scaling.scaleInPlace(50);
newMeshes.forEach(m => m.scaling.scaleInPlace(0.5));

Long answer:
I created a test app and here is the code that worked for me. You didn't share you .obj asset so I used a random one off the internet (filing_cabinet.obj download) but that may cause differences in results.

The main issue I had was that my camera was inside my mesh. Changing the camera start location fixed it. Also, I realized the .obj I was using had over 100 meshes that make up the filing cabinet. So, when I was trying to resize the mesh I was only resizing the first mesh which wasn't even visible.

I set up the engine according to the starter doc here.

Here is my scene code, based off of yours, that worked. Mine is in C# but it follows the same pattern as Javascript (aside from some capitalization differences.)

async Task<BABYLON.Scene> CreateMeshScene()
{
    // Creates a basic Babylon Scene object
    var scene = new BABYLON.Scene(engine);
    // Adding a light
    var light = new BABYLON.PointLight("Omni", new BABYLON.Vector3(20, 20, 100), scene);
    // Adding an Arc Rotate Camera
    var camera = new BABYLON.ArcRotateCamera("Camera", 0, 1, 0, BABYLON.Vector3.Zero(), scene);
    // Change camera starting position so not inside Mesh
    camera.Position = new BABYLON.Vector3(500, 500, 500);
    // This attaches the camera to the canvas
    camera.AttachControl(canvas, false);
    // load .obj
    // https://free3d.com/3d-model/office-filing-cabinet-341696.html
    var response = await JS.Fetch("assets/filing_cabinet.obj");
    if (!response.Ok) throw new Exception("Failed to load: filing_cabinet.obj");
    var blob = await response.Blob();
    var file = new File([blob], "filing_cabinet.obj");
    BABYLON.SceneLoader.ImportMesh("", "", file, scene, Callback.CreateOne<Array<AbstractMesh>, Array<BaseParticleSystem>, Array<Skeleton>, Array<AnimationGroup>, Array<TransformNode>, Array<Geometry>, Array<Light>>((meshes, particaleSystems, skeletons, animationGroups, transformNodes, geometries, lights) =>
    {
        // Set the target of the camera to the first imported mesh
        camera.Target = meshes[0];
        // Scale to half size (My mesh was rather large)
        meshes.ForEach(m => m.Scaling.ScaleInPlace(0.5));
    }));
    scene.RegisterBeforeRender(SceneBeforeRenderCallback = new ActionCallback(() => {
        light.Position = camera.Position;
    }));
    return scene;
}

For this to work I only needed to load the below files found here
babylon.js
babylonjs.loaders.min.js
babylonjs.materials.min.js

My Demo
My Demo repo (Blazor WASM)