Three.js - object.visible = true, not showing up right away - javascript

I'm hiding and unhiding parts of my Three.js scene using the .visible property on my objects.
By default certain objects (rooms) have the .visible property to false. When the camera is within a certain mesh (the room bounding box) the .visible property is set to true and the room shows up.
But there seems to be a delay (seconds or less) after setting the .visible property to true and the room actually being rendered. This delay is shortened after entering the rooms more than once.
What is the cause of this delay? Is there a way to know when or if the room is ready for rendering? It doesn't seem like update events are being fired after setting the .visible property to true, so listening for those won't help.
I appreciate any help,
Greets!
EDIT
Because I couldn't use ColladaLoader2.js I decided to simply traverse the models loaded with the ColladaLoader.js and replace all geometry properties with a BufferGeometry copy made from the existing Geometry object. After that I found out that setting the .dynamic property of an existing Geometry object to false seems to have the same effect.
dae.traverse(function (obj) {
if (obj.hasOwnProperty('geometry')) {
obj.dynamic = false;
//obj.geometry = new THREE.BufferGeometry().fromGeometry(obj.geometry);
}
});
Now when I set the object's .visible property to true the engine freezes for a little while, instead of the earlier mentioned delay before the object becomes visible. For now I'll have to decide where I want to little freeze to occur, because I don't think all objects can be visible at the same time for performance reasons.
It would be nice to have more control and information about if an object and it's geometry is loaded and ready to be viewed, or if it needs to be reloaded into memory. Now it's unclear if a BufferGeometry will show up immediately when .visible is set to true or a short freeze will occur.

Geometry needs to be converted to BufferGeometry prior to rendering. This conversion will not occur if mesh.visible is false. The conversion can take some time if your geometries are complex, or if there are a lot of geometries to convert.
A work-around is to create your meshes using BufferGeometry.
var bufferGeometry = new THREE.BufferGeometry().fromGeometry( geometry );
var mesh = new THREE.Mesh( bufferGeometry, material );
three.js r.73

In my code I see the same thing when objects are first switched to visible, but subsequent switches between visible true/false do not seem to cause another delay.
Hence one workround would be to set the visible flag on every object and render everything when WebGLRenderer is not shown on the browser (e.g. when it is behind a level loading screen) then set the objects visible flags appropriately to how you want them prior to showing the screen... Now there should be no delay as you toggle them visible.

Related

How do I assemble proper uniforms for Threejs shaders?

I am trying to figure out how to properly use Three.js' built in ShaderChunks for lighting and fog and such, and I figured a good first step was to just copy one of the ShaderLib shaders' setup. So to start with I used:
customMaterial = new ShaderMaterial({
lights: true,
uniforms: UniformsUtils.merge( [
UniformsLib.common,
UniformsLib.specularmap,
UniformsLib.envmap,
UniformsLib.aomap,
UniformsLib.lightmap,
UniformsLib.emissivemap,
UniformsLib.fog,
UniformsLib.lights,
{
emissive: { value: new Color( 0x000000 ) },
diffuse: { value: new Color( 1,1,1 ) }
}
]),
vertexShader: document.getElementById("vertexShader").textContent,
fragmentShader: document.getElementById("fragmentShader").textContent
})
Where the shader code is just directly copied from meshlambert_vert.glsl and meshlambert_frag.glsl, and that section there is based on this entry in the ShaderLib
However, I am rendering my test scene from two different cameras/renderers at once, and I immediately noticed an issue. Changing one camera's perspective changes the second camera's lighting angle, for objects with this customMaterial applied.
I assume this is due to these UniformLib objects being referenced elsewhere?
I'm not sure what I should be passing here instead, nor why this doesn't work but the standard material does. I guess I'm skipping a step, but I don't understand what it might be.
Here is a codepen where I have isolated the problem as much as I can. Now it is almost a direct copy of the ShaderLib source. At this point I'm thinking this is a pass-by-reference where it should have been a copy, somewhere inside the WebGLRenderer. https://codepen.io/cl4ws0n/pen/dVewdZ
For whatever its worth, I also tried adding a second scene, and moving the objects between them. That didn't fix it, nor did separate objects in separate scenes sharing the material.
WebGLRenderer has some hardcoded logic for certain materials. In this case it's looking for a flag called isMeshLambertMaterial: https://github.com/mrdoob/three.js/blob/r87/src/renderers/WebGLRenderer.js#L1780
So try setting isMeshLambertMaterial: true, isMeshBasicMaterial: false in your material.
As Matjaz Drolc indicated with his comments, this is a bug in three r87. The needsUpdate flags are not functioning properly for ShaderMaterials. You can force an update before each render pass in my codepen link and it will render correctly. See the fork https://codepen.io/cl4ws0n/pen/qPYwzp
I have made an issue on the repo, if anyone would like to track it's progress it can be found here.

Raycaster does not move BoxMesh objects

I'm using Physijs script for physics like gravitation.
I want to move objects in my scene with Raycaster from THREE.js script.
My problem is that Raycaster only move objects (simple box) declared like:
var box = new Physijs.Mesh(cubeGeomtery.clone(), createMaterial);
But here physics does not work. It only works if I declare it like:
var create = new Physijs.BoxMesh(cubeGeomtery.clone(), createMaterial);
But here Raycaster / moving does not work.
The difference between these two is that in the first it's just Mesh and in the second it's BoxMesh.
Does anyone know why this doesn't work? I need BoxMesh in order to use gravity and other physics.
Code to add cube
function addCube()
{
controls.enable = false;
var cubeGeomtery = new THREE.CubeGeometry(85, 85, 85);
var createTexture = new THREE.ImageUtils.loadTexture("images/rocks.jpg");
var createMaterial = new THREE.MeshBasicMaterial({ map: createTexture });
var box = new Physijs.BoxMesh(cubeGeomtery.clone(), createMaterial);
box.castShadow = true;
box.receiveShadow = true;
box.position.set(0, 300, 0);
objects.push(box);
scene.add(box);
}
Explanation
In Physijs, all primitive shapes (such as the Physijs.BoxMesh) inherit from Physijs.Mesh, which in turn inherits from THREE.Mesh. In the Physijs.Mesh constructor, there is a small internal object: the ._physijs field. And, in that object, there is... a shape type declaration, set to null by default. That field must be re-assigned by one of its children. If not, when the shape is passed to the scene, the Physijs worker script won't know what kind of shape to generate and simply abort. Since the Physijs.Scene inherits from the THREE.Scene, the scene keeps a reference of the mesh internally like it should, which means that all methods from THREE.js will work (raycasting, for instance). However, it is never registered as a physical object because it has no type!
Now, when you are trying to move the Physijs.BoxMesh directly with its position and rotation fields, it is immediately overridden by the physics updates, which started with the .simulate method in your scene object. When called, it delegates to the worker to compute new positions and rotations that correspond to the physics configurations in your scene. Once it's finished, the new values are transferred back to the main thread and updated automatically so that you don't have to do anything. This can be a problem in some cases (like this one!). Fortunately, the developer included 2 special fields in Physijs.Mesh: the .__dirtyPosition and .__dirtyRotation flags. Here's how you use them:
// Place box already in scene somewhere else
box.position.set(10, 10, 10);
// Set .__dirtyPosition to true to override physics update
box.__dirtyPosition = true;
// Rotate box ourselves
box.rotation.set(0, Math.PI, 0);
box.__dirtyRotation = true;
The flags get reset to false after updating the scene again via the .simulate method.
Conclusion
It is basically useless to create a Physijs.Mesh yourself, use one of the primitives provided instead. It is just a wrapper for THREE.Mesh for Physijs and has no physical properties until modified properly by one of its children.
Also, when using a Physijs mesh, always set either the .__dirtyPosition or the .__dirtyRotation property in the object to directly modify position or rotation, respectively. Take a look in the above code snippet and here.

Simulate an infinite number of objects

On this example we can move inside a field of spheres but into certain limits. I want to be able to move infinitely among them. How can I do that ?
The trick is to reuse the spheres that are behind the camera and put them in front of it. Look at how it is done in this example. Here the programmer knows that the user will continue in the same direction so he removes the trees that come at a certain position.
If you use something like the example you quoted, you cannot know which direction the user will take. And so, you can use the same trick, but have to code it an other way. The most obvious is to check the distances with all the spheres regularly, if the user moves. If one sphere is too far behind the camera, you mirror it so it faces the camera, behind the fog.
'Regularly' can mean two things depending on your real number of spheres in your scene :
If you have a small scene and few spheres you can check those distances in your render loop. Neither cheap nor useful, 60 per seconds, but that can be the first coding step
Then the best way would be to use a web worker : you send the positions of the camera and those of the spheres, you let the worker compute all the stuff in its thread, and send instructions back : 'move those spheres to those positions'. Every seconds is more reasonable in the threejs example, but up to you to decide that depending on your scene.
NOTE : if you have a lot of spheres, or any meshes you use instead, like more than 20-30, having a mesh for each of them will slower performances. With few trees on the examples i linked it is ok, but with more objects and/or a heavier scene,
think about merging them all in a single geometry. You can check which sphere is where by deducing from the vertices indices, or adding an attribute that defines each sphere.
this will also impact the worker delay : it will have more to compute so it will need more time.
NOTE 2 : Note 1 would of course delete the level of details that the example aims to illustrate :) (Unless you also implement your own while checking the distances of the spheres....)
If you want to have an illusion of infinite world then you could:
Break your world space into regions (for example cubes).
Detect which region you are currently in.
Make sure you have objects (spheres) in neighbour regions. If some of regions are empty - fix it.
Clear regions which are not needed anymore.
For this you might want to have some class like this:
Class Region {
bool isEmpty = true;
Vector3 center;
float radius; // or 'range'
Array<Sphere> = null; // storage of your objects
// constructors / destructor
generateObjects(params); // perlin noise might be helpful there
removeObjects();
}
and do something like this periodically:
void updateRegions() {
computeClosestGridCoord(myPosition); // which is center of your current region
lookForNeighbourRegions(regionsArray); // and add new Region if needed
deleteOldRegionsStuff(regionsArray);
}

three js same position (reference) for 2 Object3D()

I am trying to add 2 (or more) Meshes to the same scene, but i want all the Object3D to share the same position (but with a different rotation for each object).
I know the ".copy()" method, but in my case, there are so many objects and using a loop to change the position of all the objects (60 times per seconds) is resulting a poor performance.
So, I tried to use the same position reference for the objects:
var o=new THREE.Object3D(); // the first object
var p=new THREE.Object3D(); // the second object
o.position=p.position; // the position of the first object becomes the reference to the position of the second object
o.position==p.position; // false, but why?
But it isn't working and I don't get why!
My question is:
Is it possible to change the position reference of a THREE.Object3D()?
And if it isn't, how can I improve the performance of my scene?
Thanks in advance!
Edit: seems to works for someone (this answer is exactly what i can't do): How to set the Object3D position and rotation same as the added mesh ? [three.js]

how to use traverse in three.js to modify wireframe

I have several objects, when it was selected the check box would change the wireframe property to true or false ( run time) .
function toggleWireFrame(obj){
var f = function(obj2)
{
if(obj2.hasOwnProperty("material")){
obj2.material.wireframe=!obj2.material.wireframe;
}
}
obj.traverse(f);
}
1) Your code should work, if you call toggleWireFrame on each of the meshes, one by one.
toggleWireFrame(meshA);
toggleWireFrame(meshB);
This would make sense if each of these meshes are made up of several meshes and you need to toggle all the sub-meshes too. You might get mesh hierarchies like that a lot if you import models from an OBJ file, for example.
2) Or did you want to call toggleWireFrame only once and have all your meshes' wireframe toggled?
If that's the case, you will have to call
toggleWireFrame(scene);
or even
toggleWireFrame(myObject3D);
where myObject3D is an Object3D instance which is the parent of all the meshes that you want to toggle wireframe status.
traverse() works by iterating through all children and grandchildren of the starting object. You need to make sure that all the objects that you want to toggle to wireframe is parented under this starting object, as shown in the examples above.
3) Another option is to use an array to store each material as they are created, and then iterate through this array to change the wireframe attribute when the user toggles the check box.
This is what I use on my projects, it just inverses the boolean since it will be true or false.
function wireframeToggle(i) {
bool = i.material.wireframe;
i.material.wireframe = !bool;
}
Working example (x for wireframe)

Categories

Resources