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.
Related
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!
I am trying to create a scene using .obj and .mtl files exported from blender. The object is literally just a rectangle (it needs to be a .obj file. more objects will be added to create a scene) I am able to see the material load, but cannot see the texture being applied in chrome or firefox.
the mtl file text
the obj file text
Here is the javascript code:
const obj_loader = new THREE.OBJLoader(),
mtl_loader = new THREE.MTLLoader();
// uses example of OBJ + MTL from three.js/examples
mtl_loader
.setTexturePath('bar/')
.setPath('bar/')
.load('floor.mtl', (materials) => {
materials.preload()
obj_loader
.setMaterials(materials)
setPath('bar/')
.load('floor.obj', (object) => {
// everything returns status 200!
// material is being applied but no texture
scene.add(object);
})
});
project file structure
Checking the console, requests for the mtl, obj, and image files are returning 200 status codes
but the model renders without texture
No errors are in the console at all. What would cause this issue in Three.js? I suspect something is wrong with the .obj or .mtl but I cannot find the problem. (the file paths are correct based on the logged ajax request).
This may be due to the .preload() method not being called on the materials object returned in the material load callback. The preload() method basically creates the material objects loaded by the MTLLoader.
Consider the following updates, with this method call added:
const mtl_loader = new THREE.MTLLoader();
mtl_loader.setTexturePath('bar/');
mtl_loader.setPath('bar/');
mtl_loader.load('floor.mtl', function(materials) {
// Add this (see link below for more detail)
materials.preload()
const obj_loader = new THREE.OBJLoader();
obj_loader.setMaterials(materials);
obj_loader.setPath('bar/');
obj_loader.load('floor.obj', function(object) {
scene.add(object);
})
});
Here is a link to the THREE source code which shows the inner workings of .preload() - hope this helps!
It turned out to be an issue with the UV mapping of the texture. In Blender, you can create a texture, and Blender will show the texture in 'render' mode, even if the UV map is not set (It will wrap automatically).
Blender showing render, even without UV Map
This was especially frustrating because I had these settings when exporting the file:
export settings for OBJ
The MTL and OBJ file appeared to have all the content in it, but by manually UV mapping the texture (instead of using a simple repeat in the "texture" menu) the issue was resolved when I exported again.
UV/Image editor needed to be used
Sorry for any confusion that may have caused. I hope it helps anyone who has the same issue exporting from Blender.
I am building a website with 3D graphics using THREE JavaScript, I am trying to import the following scene to site but in the site all the objects are facing to the inside of the planet: The scene problem
Here is the code I am using to import the scene:
//ADDING GEOMETRY
var objects = [];
var loader = new THREE.ObjectLoader();
loader.load('models/planeta-NoMerge.json', function( obj ){
scene.add( obj );
scene.traverse(function( children ){
objects.push(children);
});
});
I´m doing some raycasting because I need to show a different scene if the user clicks on a model.
And if you need something else, please let me know: D
All in all the probem is not in code you have posted. I have had similar issues here and then, but I could fix it by tweaking the export settings. There you can flip normals, include/exclude several things like Material, Bones, Animations, etc...
So it's hard to tell which Settings you need, since that is up to your needs, but playing arround with the settings will bring you further.
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.
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. ;)