STL file browser

Hi, I’m having trouble loading stls from a file browser. Below I’ll list several of the threads / documentation, and playgrounds I’ve been working with.

My troubles are with:

  1. Creating a button with file browser
  2. Loading the selected STL.

Specifics:

  1. I have only created simple buttons that use “button.onPointerDownObservable.add” (or ClickObservable) to add / attached a function to the click event. I had hoped that creating/calling a “BABYLON.FilesInput” would create the dialog box to select from. That doesn’t seem to work out for me. In most of the playgrounds I have found on the topic, the buttons seem to be html elements created with react and event listeners.

2.When I do select the files from a dialog box, I’m having trouble loading the stil. I am somewhat familiar with the assetsManager and have tried to use the file as rootURL.

I feel like I’m missing a bunch here! Can someone help me with a bone simple implementation for file dialog stl loading or modify the events in some of the PGs to load stls. It would be greatly appreciated.

I’ve also looked though the sandbox code but I haven’t figured out how to transition that code to my situation ->mainly having my button inside of babylonGUI vs html template

Thanks.

Actively modifying and moving things around in PG
A PG I have found using react and modified → opens up dialog (lower left corner file chooser and import button)
Another PG with file browser
Sandbox source
helpful thread

Thanks!

1 Like

I’ve been working on adding file loading methods to the click event with no luck. I get errors associated with the file not being where it’s looking… Below is one attempt for SceneLoader. I’d like to use assetManager but thought this may be easier…

babylon.js:16 GET https://playground.babylonjs.com/[object%20File]mystl.stl 404
BJS - [08:41:47]: Unable to load from [object File]mystl.stl: LoadFileError: Error status: 404 - Unable to load [object File]mystl.stl

filesInput.onProcessFileCallback = (function (file, name, extension) {
                    BABYLON.FilesInput.FilesToLoad[name] = file;
                    console.log(name,file)
                    // The first parameter can be set to null to load all meshes and skeletons
                    BABYLON.SceneLoader.Append(file, name, scene, function (scene) {
                        // do something with the scene
                    });

I have seen a post referencing a URL. Maybe I have to create a URL with stl?

Also, Using code below doesn’t work with AssetsManager… I’m passing the file as rootURL which I thought may work (based on other posts).

              // Files input
                var filesInput = new BABYLON.FilesInput(engine, null, null, null, null, null, function () { BABYLON.Tools.ClearLogCache() }, function () {}, null);
                filesInput.onProcessFileCallback = (function (file, name, extension) {
                    BABYLON.FilesInput.FilesToLoad[name] = file;
                    console.log(name,file)
                    var assetsManager = new BABYLON.AssetsManager(scene);
                    var meshTask = assetsManager.addMeshTask("", "", file, file);
                    meshTask.onSuccess = function (task) {
                        console.log('Success!')
                        task.loadedMeshes[0].position = BABYLON.Vector3.Zero();
                    }	
                    meshTask.onError = function(error){
                        console.log('Error')
                    }
                   


                    return true;
                }).bind(this);

Hi there! I saw this quickly last night before going to bed and was trying to wrap my head around the question being asked. This follow up post I think gives some better clarity.

So you want to be able to just load a Stil from a file on your machine? You have the file browser working with a button, but its the scene loader part that is not? My familiarity with scene loaders has been using a URL link from a hosted website.

Posting the documentation just for anyone’s reference: Loading Any File Type | Babylon.js Documentation

Is it possible to load files from a local source? Should be able to. If not let me go figure that out for you in the next 10 min! :blush:

Also not sure what your second link

[https://playground.babylonjs.com/[object%20File]mystl.stl]

is doing since I get an error on the page.
Hope this gets some balls rolling.

I appreciate your mindshare here. Sorry for the cloudy description. I’m moving around frantically now trying all sorts of way to achieve my goal → I want to load an STL from my local machine using a file browser as simple as possible.

I can load STLs from my public directory but not from a file browser. I can get a file browser to bring the file and I can print the stl file to the console. I can’t seem to load it using any of the resources from the documentation.

My latest PG here

I’m working off of other PG’s that have way more in them than I need, but when I try a bone simple approach I fail to get the file browser up. So i’m confused on how that works (even though I have it working thanks to others PGs). Once I have the file I can’t get it into the AssetManager as it seems to want to be located in a URL (from what I can tell).

The link that doesn’t work is actually an error message from my playground, not a link… But I believe it means the loadrer is looking for a URL and not the file I’m passing it. The error is from me trying to load an assset by passing the file to the URL parameter but the URL doesn’t exist as I just have the file with now server route to it… I think?

1 Like

Ok! I think this makes a lot of sense now. Thank for clarifying even more!

Here is what I found out: It is possible to load an STL from a local directory but it’s a little more tricky then just “drop file” into scene loader or asset manager.

In summary answer from the awesome @RaananW : “It is possible to load the model as a base664 data url or create a blob of the file in any way you like”

AHA! And what do you know I started digging around the forums and came across this thread and playground :wink:

Really weird thing about this demo though. Was I had to go into the debug view to find the file button that was added to the document at the very bottom. Not sure why it’s hidden normally, maybe css?

image

Anyways the important part I think you’ll want to check out is lines 49-65

        var files = evt.target.files;
        var filename = files[0].name;
        var blob = new Blob([files[0]]);

        BABYLON.FilesInput.FilesToLoad[filename] = blob;
        
        assetsManager.addMeshTask(name, "", "file:", filename);
        assetsManager.load();

Hope this helps and let me know if you have any questions!

I added some style to the input button - https://playground.babylonjs.com/#SA6QBM#32
Now it is somewhere at the right top. And it works :slight_smile: (one needs to tune camera to adapt to any mesh size)

1 Like

Hey there! Wanted to take a look at this too and found an alternative approach! Looking at the source code, the ContainerAssetTask’s (which is the one created by addContainerTask) constructor accepts a File object as well as a string. So you can do:

const task = new BABYLON.ContainerAssetTask("task", "", "", file);
assetsManager._tasks.push(task);

and then

assetsManager.load();

I tried this with the FilesInput's drag and drop functionality, but I think it will work as well with a file browser.

FilesInput for general file types | Babylon.js Playground (babylonjs.com)

I really appreciate all of your support here. I feel slightly better knowing that is isn’t as straight forward as I initially thought it would be.

I can’t seem to get that PG to load any of my STLs (that do load successfully using my hard coded assetsmanager). I’ll play with it a bit more as it sounds like it is working for others.

1 Like

This sounds promising → drag and drop would be a good feature to add. I just figured since I had trouble with file browser I had not chance at drag and drop :slight_smile:

I modified the PG by labris here. I added an onsuccess function to set the camera position. I still can’t load STLs. I’ll keep playing.

Thanks.

1 Like

While I have no problems with GLBs, with STL files I have an error:
Not allowed to load local resource: file://channel9.stl/

@labris, you need to do this one : BABYLON.FilesInput.FilesToLoad[filename.toLowerCase()] = blob; I guess this should help :wink:

3 Likes

@twinturbotom I’ve tried the approach with AssetsManager locally and I could drag and drop STLs with no issues!

I also realized that instead of doing:

const task = new BABYLON.ContainerAssetTask("task", "", "", file);
assetsManager._tasks.push(task);

you can just do:

const task = assetsManager.addContainerTask("container", "", "", file);

on Javascript. There was a type signature missing on Typescript so I added it on this PR:
Update addContainerTask and addMeshTask signatures by carolhmj · Pull Request #11552 · BabylonJS/Babylon.js (github.com)

2 Likes

It really helped :slight_smile: - https://playground.babylonjs.com/#SA6QBM#34

4 Likes

I appreciate everyone’s help and am impressed by your collaboration and support.

I love how the latest PG is clear and simple. That said I can’t get it to load my stls / show up visually or appear in inspector nodes. Is there a specific STL file you are using? I can’t get any of mine to load, even though they load in the sandbox.

If I send the mesh to the log it shows there is something (name, id, boundingbox) but it’s not in the inspector.

Any ideas?

I use .stl file from the Asset library - The Meshes Library | Babylon.js Documentation
The file itself is here - https://playground.babylonjs.com/#AJJ8U5#2
When loaded it is not shown in the camera, but it is possible to find it (and it is shown in the Inspector).
Check your STL files in Sandbox - if they load there, they should load here (but you may not see them at the first sight because they may be away from camera).

Here - https://playground.babylonjs.com/#SA6QBM#37 - the mesh is automatically scaled to cube 1x1x1 and now it is in camera view immediately after loading.

task.loadedMeshes[0].normalizeToUnitCube()

1 Like

Labris. I appreciate you walking me through this and all of the help from others here as well. The PG and sandbox are such helpful development tools coupled with this community.

This is fantastic. Thank you so much!

1 Like

Hopefully last hurdle on this…

When I try to move from PG to local the file input doesn’t work. See below.

As an open question, why is it that I can’t use standard babylon GUI buttons with a clickobservable event to bring up a file browser? Similar to other UI features.

I was able to get this working if I create the element outside of babylon (on my html template). I’m going to look into hiding / disguise it using css, and then programmatically click it when a user clicks my babylon GUI as I want to add this button to my existing controls. Does this seem like the correct approach?

What I have outside of playground.

const canvas = document.getElementById("renderCanvas"); // Get the canvas element
const engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine

var fileInput = document.getElementById("loadFile");
if(!fileInput){
    fileInput = document.createElement("INPUT");
    fileInput.setAttribute("id", "loadFile");
    fileInput.setAttribute("type", "file");
    fileInput.style.position = "absolute";
    fileInput.style.top = "80px";
    fileInput.style.width = "200px"
    fileInput.style.height = "100px";
    fileInput.style.right = "40px"
    document.body.children[0].appendChild(fileInput);
}


var createScene = function () {


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

    // This creates and positions a free camera (non-mesh)
    var 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)
    var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);

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


    let  assetsManager = new BABYLON.AssetsManager(scene);  

    //called when a single task has been sucessfull
    assetsManager.onTaskSuccessObservable.add(function(task) {

       // var mesh = task.loadedMeshes[0]; //will hold the mesh that has been loaded recently
        console.log('task successful', task);
        console.log(task.loadedMeshes[0]);
        task.loadedMeshes[0].normalizeToUnitCube()
    });

    assetsManager.onTaskErrorObservable.add(function(task) {
        console.log('task failed', task.errorObject.message, task.errorObject.exception);
    });

    var loadButton = document.getElementById('loadFile');

    loadButton.onchange = function(evt){

        var files = evt.target.files;
        var filename = files[0].name;
        var blob = new Blob([files[0]]);

        BABYLON.FilesInput.FilesToLoad[filename.toLowerCase()] = blob;
        
        assetsManager.addMeshTask(name, "", "file:", filename);
        assetsManager.load();
    }; 
    return scene;

};

const scene = createScene(); //Call the createScene function

// Register a render loop to repeatedly render the scene
engine.runRenderLoop(function () {
        scene.render();
});

// Watch for browser/canvas resize events
window.addEventListener("resize", function () {
        engine.resize();
});