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:
Creating a button with file browser
Loading the selected STL.
Specifics:
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
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…
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.
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.
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?
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
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?
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 (one needs to tune camera to adapt to any mesh size)
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.
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.
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
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.
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).
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.
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();
});