THREEJS - How to traverse all layers of model loaded with GLTFLoader? - javascript

How does one traverse a mesh loaded with GLTFLoader properly to walk through all layers?
I am trying to do a simple selective bloom pass on a model by traversing the model’s all parts, setting them to the bloom layer, and then rendering the combined original + bloomed layers. However, as we can see in the images below, only the yellow outer part of the model is actually found during the traversal, does anyone know how to extract the rest of the model for layer setting?
For reproduction, the model can be downloaded from here:
https://github.com/whatsmycode/Models/blob/master/PrimaryIonDrive.glb
This is the code I currently use:
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
let BLOOM_LAYER = 1;
new GLTFLoader().load( 'models/PrimaryIonDrive.glb', function ( gltf ) {
const model = gltf.scene;
model.traverse( function( child ) {
child.layers.enable(BLOOM_LAYER);
});
scene.add( model );
});
This is the resulting image, bloom is applied to the yellow outer rings only.
This is the bloom-mask only

The issue was that I had not added the point- and ambient lights to both layers. The bloomed object has properties that requires light to show color for all parts except for the emitting yellow rings. To fix the problem, I simply enabled the lights for both layers before adding the lights to the scene.
const pointLight = new THREE.PointLight(0xffffff);
pointLight.layers.enable(ENTIRE_LAYER);
pointLight.layers.enable(BLOOM_LAYER);
const ambientLight = new THREE.AmbientLight(0xffffff);
ambientLight.layers.enable(ENTIRE_LAYER);
ambientLight.layers.enable(BLOOM_LAYER);
scene.add(pointLight, ambientLight);

Related

I would like to make objects appear and disappear in the viewer with some degree of transparency

I'm working on a revi model to put in the forge where you can read the history of the building. from the nascia - collapses - demolitions - reconstructions.
between one passage and the next I would like to add a degree of transparency (fade in fade out) for the change of models or model parts.
this is my function:
function T00()
{
NOP_VIEWER.model.search('00', function onSuccess(dbids) {NOP_VIEWER.showAll();
NOP_VIEWER.isolate(dbids); }, function onError(err) { console.error(err); },
'Timeline_Storico');}.fadeToggle(400)
I isolate the parts of the model that I want to see.
how can i add the degree of transparency?
Firstly, I'd suggest check the other post for some backgrounds on rendering machanism:
How to set opacity of each nodes
The default materials are defined at
viewer.impl.matman()._materials;
Different fragments might share with the same material.
In the blog below, I demoed how to make the model grey out, by changing the material parameters.
https://forge.autodesk.com/blog/showhide-textures-object-forge-viewer
Similarly, you can set their opacity to different values. e.g.
//iterate materials
for (index in mats) {
//index is the material name (unique string in the list)
m = mats[index];
//store texture info
oldTextures[index] = m.map;
oldColors[index] = m.color;
//set the material without texture and the grey color
//var grey = new THREE.Color(0.5, 0.5, 0.5);
//m.map = null;
//m.color = grey;
//change transparency
m.opacity = 0.1*index;
m.transparent = true;
//force update
material.needsUpdate = true
//mark the material dirty. The viewer will refresh
m.needsUpdate = true;
}
//refresh the scene
viewer.impl.invalidate(true, true, false);
However the problem is: since the fragments share with the material, all other fragments (with same material)will be also updated, instead of by transparency objects.
While, it can be workaround by cloning the material of specific object, changing its opacity, and finally applying this material to the object only. Of course, because it is cloned material, you need to add it to the materials list of viewer.impl.matman(). The other post demos with the codes
How to set independent material for specific dbId
copying here for your convenience:
let material = fragList.getMaterial(fragId).clone();
if (material) {
material.opacity = opacity
material.transparent = true
material.needsUpdate = true
}
viewer.impl.matman().addMaterial ('myCustomMaterial', material,
true);
viewer.model.getFragmentList().setMaterial(fragId, material);
viewer.impl.invalidate(true);

Accessing multiple objs using .getObjectById() with JSONLoader in Three.js

Is it possible to differentiate between the meshes within one .js file exported from blender and animate them separately using Three.js?
The cube I would like to select is named "Cube" loads properly. However, when I try to get it by Name or Id, it doesn't recognize the var item1.
loader = new THREE.JSONLoader();
loader.load('engine.js', function (geometry, materials) {
var mesh, material;
material = new THREE.MeshFaceMaterial(materials);
mesh = new THREE.Mesh(geometry, material);
mesh.scale.set(1, 1, 1);
var item1 = scene.getObjectByName("Cube");
item1.position.x = 15;
scene.add(mesh);
});
I found this post but it seems unresolved: Three.js load multiple separated objects / JSONLoader
What is the best approach to loading multiple meshes via JSONLoader? I'd prefer to load them together as one .js file and just select the ones I would like to animate.
Thanks for your help!
In your blender scene you need to name every mesh you want to access independently in three.js. Then you can use Object3D.getObjectByName() to access your mesh in three.js.
Yes, it is possible to load an entire scene with several meshes from a json file exported from Blender!
You can see the complete process described on my answer of the cited post
So, you can differentiate between the meshes using the getObjectByName method and manipulate them separately. But it is important to know that the loaded object isn't a Geometry anymore. It is labeled with the Scene type by now and it must be handled in a different way.
You must change the loading code for one like this:
loader = new THREE.JSONLoader();
loader.load( "obj/Books.json", function ( loadedObj ) {
var surface = loadedObj.getObjectByName("Surface");
var outline = loadedObj.getObjectByName("Outline");
var mask = loadedObj.getObjectByName("Mask");
mask.scale.set(0.9, 0.9, 0.9);
scene.add(surface);
scene.add(outline);
scene.add(mask);
} );
In the above code we can indeed animate the surface, outline and mask meshes independently.

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. ;)

Three.js skinned animation mesh disappears when material skinning is true

I've exported an animated model from Blender which doesn't seem to have any issue instantiating. I'm able to create the THREE.Animation and model, but I was finding there was no animation. I realized I needed to set skinning true on each material, but when I do that the entire mesh goes missing.
Below is my (quick and messy) code trying to get everything to work.
function loadModel() {
var loader = new THREE.JSONLoader();
loader.load('assets/models/Robot.js', function(geom, mat) {
_mesh = new THREE.Object3D();
_scene.add(_mesh);
geom.computeBoundingBox();
ensureLoop(geom.animation);
THREE.AnimationHandler.add(geom.animation);
for (var i = 0; i < mat.length; i++) {
var m = mat[i];
//m.skinning = true; <-- Uncommenting this makes the model disappear
//m.morphTargets = true; <-- This causes all sorts of WebGL warnings
m.wrapAround = true;
}
var mesh = new THREE.SkinnedMesh(geom, new THREE.MeshFaceMaterial(mat));
mesh.scale.set(400, 400, 400);
mesh.position.set(0, -200, 0);
mesh.rotation.set(Utils.toRadians(-90), 0, 0);
_mesh.add(mesh);
_robot = mesh;
Render.startRender(loop);
var animation = new THREE.Animation(mesh, geom.animation.name);
animation.JITCompile = false;
animation.interpolationType = THREE.AnimationHandler.LINEAR;
animation.play();
});
}
I believe I'm updating the AnimationHandler correctly in my loop
function loop() {
_mesh.rotation.y += 0.01;
var delta = 0.75 * _clock.getDelta();
THREE.AnimationHandler.update(delta);
}
In the section metadata of the exported JSON file the number of morphTargets and bones are both greater than 0?
I think that you followed the example here:
http://threejs.org/examples/#webgl_animation_skinning_morph
in which the animated model uses Morph Target and Skeletal Animation (see Wikipedia for the theoretical concepts).
If the animated model uses only Skeletal Animation as in this example http://alteredqualia.com/three/examples/webgl_animation_skinning_tf2.html
you have to instantiate a THREE.SkinnedMesh Object and then set only the m.skinning property to true.
I was having the same problem just now. What worked for me was to remake the model with applied scale and have keyframes for LocRotScale, not just location.
lately, I've encoutered a similar issue of mesh disapearing while exporting blender skinning animation to json. It turned out, the mesh I was using had double vertex (one vertice hidding another). All looks good While creating the vertex groups and the animations in blender, but when I imported the mesh via three.js, it kept disapearing as soon as the animation started. In other words, If 1 vertice from your mesh is omitted from the vertex groups, you will experience this disapearing behavior. To prevent this issue, I now use the "remove doubles" function from blender to validate the mesh integrity before exporting it to json. You might have encountered the same issue and redoing your mesh work fix it... Anyways, the question is pretty old, but the topic is still valid as of today, so I hope this fresh info will help someone out there...
Peace INF1

Collada model faces not displaying correctly in three.js

After importing a collada model into three.js, some of the model's faces are only visible from the inside of the model and not from the outside.
How can I fix the problem with the faces in question?
Is it possible to have the model's faces visible from both sides?
The reason it doesn't work properly is because your file has this double_sided flag set:
<effect id="material_3_4_0-effect" name="material_3_4_0-effect">
<profile_COMMON>
...
<extra>
<technique profile="GOOGLEEARTH">
<double_sided>1</double_sided>
</technique>
</extra>
</profile_COMMON>
</effect>
The three.js ColladaLoader doesn't look for this flag and set doubleSided on the material like it should. I've filed a bug for the issue.
To fix the faces being oriented incorrectly, load the model into a 3D modeling program like Blender and flip the normals of the faces that aren't displaying correctly.
Three.js meshes have a doublesided property you can set which will usually allow you to display the model with the faces visible from both sides.
Here's a short example of how to load a collada mesh and enable double-sided rendering.
var loader = new THREE.ColladaLoader();
loader.load('path/to/mesh.dae', loadModel);
function loadModel(geom) {
var mesh = new THREE.Mesh(geom, new THREE.MeshBasicMaterial());
mesh.doublesided = true;
scene.add(mesh);
}
And a live example: http://jsfiddle.net/r7Yq2/

Categories

Resources