Three.js load separated object - javascript

My object separated into 9 files, so I need to load all 9 files: file_1.obj, ..., file_9.obj, merge them all and after that somehow use file.mtl with the result "big" object. How am I suppose to do it?
I thought about this solution:
mainObjGeometry = new THREE.Geometry();
loader.load( 'file_1.obj', function ( object ) {
object.updateMatrix();
mainObjGeometry.merge(object.geometry, object.matrix);
});
...
loader.load( 'file_9.obj', function ( object ) {
object.updateMatrix();
mainObjGeometry.merge(object.geometry, object.matrix);
});
And after that load .mtl file and connect them (even though I don't know how to do it).
But I think that using this technique I can not know the time when all objects are loaded.
How can I solve this problem? And can I connect "mainObjGeometry" and loaded from .mtl "mainObjMaterial"?

You can know the time when all the files load. For example, you have:
loader.load( 'file_9.obj', function ( object ) {
object.updateMatrix();
mainObjGeometry.merge(object.geometry, object.matrix);
});
do this:
var totalModels = 0, loadedModels = 0;
function allLoadedCallback(){...}
loader.load( 'file_9.obj', function ( object ) {
object.updateMatrix();
mainObjGeometry.merge(object.geometry, object.matrix);
loadedModels++
if(loadedModel == totalModels) allLoadedCallback();
});
totalModels++;
The other part of the question i'm not so sure about. Why can't you merge the model before even exporting it. That way at least the exported material would make sense. I'm not too familiar with the merge utility, but I can see you having problems if you are trying to merge the materials without properly loading them first.

Related

How to combine .gltf file with other assets; bin and png files for use in babylonjs

I feel like I'm close but still missing something. Here's what I have so far
var files = window.files;
console.log(files) // => (3) [File, File, File]
// File objects:
// 0: File {correctName: 'test.bin', name: 'test.bin' …}
// 1: File {correctName: 'test.gltf', name: 'test.gltf', …}
// 2: File {correctName: 'test.png', name: 'teest.png', …}
var fi = new BABYLON.FilesInput(engine, scene, (s) => {
console.log('here in scene loaded callback');
})
fi.loadFiles({ dataTransfer: { files: asheFiles } });
The scene loaded callback never gets called however. I also don't see anything being rendered on the canvas element in the page.
I know there's clearly a way to do it based on the example they have here:
https://sandbox.babylonjs.com/
I'm just trying to figure out how to recreate that.
Okay folks, I do have a solution.
Overview
After searching through many of their documentation pages, forums online and similar questions here on Stackoverflow, I've realized that Babylon js doesn't really sport a direct file load API. They have functions that can take file objects as arguments, but ultimately that does not meet the needs of digesting a gltf file that's split up into various parts, namely .gltf, .png, and .bin. As using a provided method such as BABYLON.SceneLoader.Append on the gltf file will fail once BABYLON parses it and immediately starts requesting the accompanying files (.png and .bin) over the web. Here you could theoretically use a .glb file, but that's not the file format I'm using in my project.
You need to hack the library into allowing you to do what you need. I started looking at the example code used in the linked sandbox above. They use this FileInput class to somehow load all the accompanying files together after a drag and drop event. I emulated the drag and drop functionality via:
function sceneLoadedCB(newScene) { window.scene = newSene; initCamera(newScene, canvas); }
const fi = new BABYLON.FilesInput(engine, scene, sceneLoadedCB)
fi.loadFiles({
dataTransfer: { files: [gltfFileObject, pngFileObject, binFileObject] }
})
This would've been good enough for my purposes except that it will always create another scene object in place of the current one, stopping me from loading up a scene with multiple gltf objects. Asset containers will not work to solve this as they are always bound to the scene they are created with; you cannot transfer assets from one scene to another.
The solution
I didn't reverse engineer the logic that parses and groups the files as all being part of the same gltf object. But I did find this static property of the FilesInputStore class being used as a place to store actual javascript File objects. If you populate this store with your gltf file accompanying file assets you will be able to load them together as all being part of your gltf file.
Demo:
var theScene // your babylon scene instance
const renderLoop = (sceneToRender) => {
sceneToRender = sceneToRender || window.scene
sceneToRender.render();
sceneToRender.getEngine().runRenderLoop(renderLoop);
};
yourGLTFFiles.forEach((file) => {
window.BABYLON.FilesInputStore.FilesToLoad[file.name] = file
})
const gltfFile = yourGLTFFiles.find((file) => file.name.includes('gltf'))
BABYLON.SceneLoader.Append('file:', gltfFile, theScene, function (loadedScene) {
console.log('here in scene loaded callback, scene: ', loadedScene)
// below will be true since append adds to the scene instance you passed it
console.log(loadedScene === theScene)
renderLoop(theScene)
});
This took me forever to solve. I hope this solution saves you time in building your awesome project!

how to generically import a gltf model in threejs?

I need to create a generic advanced viewer in three js which could generate Gltf files. How could I get every information about each component of the model?
I try to search into the loader class with loader.load() of THREE.GLTFLOADER, I found the information ( in scene.children which are the models' components) but I can't make it generic.
Is there any libraries or function that give you each component?
like the function .getElementById, something like .GetAllComponents or .GetMaterialsTextures (like i need to get every path for texture and model'components)
I don't ask u to give me the answer I will not learn.
var dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath( 'js/draco_decoder.js' );
let loader = new THREE.GLTFLoader(); // I use this as all the video i saw but i you coud explain it (does it help to pack data or just to encode?)
loader.setDRACOLoader( dracoLoader );
loader.load('assets/BM1294_EOS_TABLE_220X280_OUVERT/BM1294.gltf',
function(gltf){
console.log(gltf);
let mesh = gltf.scene.children[0]; //one of my model
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2;
scene.add(mesh);
});
Thank you If you help :)
If you want to modify plain JSON, without using threejs objects, you should probably load the JSON outside of GLTFLoader first. For example:
// Load JSON using the native fetch() API.
fetch('model.gltf')
.then((response) => response.json())
.then((json) => {
// View and edit the glTF JSON content.
console.log(json);
json.materials[0].baseColorFactor = [1, 0, 0, 1];
// Parse the modified glTF JSON using THREE.GLTFLoader.
const jsonString = JSON.stringify(json);
const loader = new THREE.GLTFLoader();
loader.parse( jsonString, '', ( gltf ) => {
// Add the result to the scene.
scene.add(gltf.scene);
});
});
NOTE: This assumes you are using the .gltf extension. Files ending in .glb are binary, and parsing them isn't covered by this answer. You can convert easily between the two variants with glTF-Pipeline.
Modifying the glTF content in complex ways will require some understanding of the format specification, and is somewhat outside the scope of a Stack Overflow question. You may find it easier to load threejs objects using GLTFLoader normally and modify those, instead.

Loading an OBJMTL Object with three.js and get geometry parameter of the Mesh

I just load an MTLOBJ and everything is fine but when I want to get the Geometry attribute of the object to get the vertices, there is no way because apparently, it loads a Object3D which should have a Mesh. But I hardly try to find a way to solve this problem.
It seems that Mrdoob proposes to get the parses data but every parameters used in the parse function are set private ..
I try to get the vertices parameters from the geometry parameter which should be in a mesh but no way, even looking through the doc.
You can find the geometry in the hierarchy by doing this:
object.traverse( function ( child ) {
if ( child.geometry !== undefined ) {
console.log( child.geometry.vertices );
}
} );
After some researchs, it appeared that the Object3D is composed with Meshs.
And in the case of loading an OBJMTL (.obj and .mtl), the Object3D named modele had the Mesh accessible by doing modele.children[O] and the array vertices of geometry by doing modele.children[0].geometry.vertices .
I was looking for a way to be sure the object I attribute to my modele was the Mesh with his geometry parameter, thank you Mr Doob for that.
function loadModel(obj, mtl) {
loader.load(obj, mtl, function ( object ) {
modele = object;
//loadingDone = true;
analyseModel();
//putModel();
});
}
function analyseModel() {
analyser = new AnalyseObj(modele.children[0]); //I give the Mesh of my model
}

Java script preload contents three.js

Simply i would like to pre-load contents with a bar or what ever really in my three.js project or JavaScript either way should be fine, im familiar with action-script to do this task but cant seem to manage this in javascript:
Heres the code:
var loader = new THREE.OBJMTLLoader();
loader.load( 'myobject.obj', 'myobject.mtl', function ( object ) {
for(k in object.children){
object.children[k].position.z =-5;
console.log("position Changed");
}
scene.add( object );
console.log("the item is loaded");
I have looked at the loader examples to do with three.js sample files but i really dont follow it to well, is there way to check the file size vs file bytes received and implement it with the above, i have tried but returns under-fine. Jquery is also welcomed
I have similar problem. I have created an Array to preload and store all the objects in there. Passed and returned the currentId, to be able to store them after that. In Objects3DListJson I put the info for the Paths
for (var curobj = 0; curobj < Objects3DListJson.length; curobj++) {
loaderMTL.load(curobj, ObjPath, mtlPath, function (object, currentID) {
var obj3D = new Obj3D(Objects3DListJson[currentID].ObjectName, object)
objects3D.push(obj3D);
}
}
Of course I changed OBJMTLLoader to return the Current ID. I hope this help you on some way.

Parameterizing and editing a model using ThreeJS or X3DOM in the browser

I am using ThreeJs to render some STL files in the browser.
I am trying to figure out if I can add parameters to these models, so that I can edit these models using ThreeJs or X3DOM.
For example I have a cube and I want to add a text parameter to this cube.
When the text parameter is passed to ThreeJS, it embosses the text on the cube at a specific location.
I don't mind converting the STL files to the ThreeJS model Js file or X3D files as long as I can parameterize the rendering to add text to the basic structure.
I want to know if anyone has had experience doing this kind of 3D parameterization.
Is this possible in ThreeJS or X3DOM?
If yes, then is there any documentation that I could use to achieve this?
If these libraries cannot handle this case, then are there any other libraries which can achieve the same?
Edit
My question is more about how I can add parameters to the model itself. Can we have parameters in ThreeJS models themselves, which ThreeJS understands out of the box. Let me ignore the text example, if I consider a simple cube in a model file, is there a way to make Threejs understand its side length as param from the model, and any changes to this param automatically gets reflected into the visualization. I.e. IF I change the side length from 1 to 3, Threejs renders a larger cube.
I'm not sure it answers your question, but personally I would create a subclass of an empty 3D object, and apply your effects programmatically, after the base model is loaded.
Here's how I do with three.js (ideally, this is in a separated file) :
var EmbossedCube = function( text, onLoaded ) {
THREE.Object3D.apply(this);
var self = this;
var loader = new THREE.STLLoader();
loader.addEventListener( 'load', function ( event ) {
var material = new THREE.MeshPhongMaterial( { ambient: 0xff5533 } );
// apply effects on the material, or the geometry
// according to `text`
var mesh = new THREE.Mesh( event.content, material );
// transform the mesh locally if needed
self.add( mesh );
onLoaded && onLoaded( self );
} );
loader.load( './model.stl' );
};
EmbossedCube.prototype = Object.create( THREE.Object3D.prototype );
// methods of EmbossedCube
EmbossedCube.prototype.constructor = THREE.Object3D;
Then you can create such an object, and add it to your scene:
var cube = new EmbossedCube("beep", function(obj){
// you can translate/rotate the object if needed
scene.add( obj );
});
It could be not the simplest way to do it, but I think it offers a good reusability on the long term.
Explore the va3c viewer, which has options for opening files from REVIT, grasshopper etc.
You might try opening your files first in Rhino + GH, then creating those parameters that you need and then importing it using va3c.
This is a hunch and I cannot try until you give an actual file with details about the usecase.
Tell me if this works. ;)

Categories

Resources