How to know when an object is added to the scene? not just the “added” or “removed” events from the children itself. If I create an Object3D then add things to it, nested childs… how can the childs know when they are added to the visual Scene? not just a hierarchy?
Your object is added to the scene as soon as you add or attach it.
But you won’t see it, unless you call render
Object3D also has an onAfterRender callback that is executed immediately after the object has been rendered onto the canvas
no because you could add objects to a new Object3D that is not yet in the scene.
@Lawrence3DPK that only works if the object has geometry, it doesnt work on empty containers like a plain Object3D.
In the end I had to code a class to wrap THREE.Object3D
@bandinopla I’m interested to know your use case and thinking behind this, I may be missing something but a whole class for the detection of adding and removing objects seems like overkill, would the following not serve the same purpose…
const addObj3D = (obj, scene) => {
scene.add(obj)
console.log(`${obj} was added to ${scene} `)
}
const removeObj3D = (obj, scene) => {
scene.remove(obj)
console.log(`${obj} was removed from ${scene} `)
}
then you could simply use these methods on an application level as apposed to three’s native add
and remove
methods…
In my case, I have a class wrapping the THREE.Scene that handles all the frame updates, so on the main loop I do something like:
private mainLoop() {
const delta = this.clock.getDelta();
//
// enter frame listeners
//
if( this.enterFrameListeners.length )
{
const listeners = this.enterFrameListeners.slice(0);
for (let i = 0; i < listeners.length; i++) {
listeners[i](delta);
}
}
this.composer.render(delta);
this.stats.update();
}
And then I create and add many diferent objects on nested childs all over the place, this is the only way I found to reach the “enterFrameListeners” array… the idea is as soon as something is added, I will bubble up and try to find the current scene, and register to the enterFrameListeners.
Because if you have an object3D on which you want to do stuff on everyframe, how else would you handle this scenario if you want to keep things decoupled?
I’m not sure there’s enough information provided that describes the scenario, you can get the ancestry tree of any object with the traverseAncestors method of Object3D
, are you trying to implement a culling technique?
the scenario I’m talking about is triggering an action when and only when the object3d is on the scene. When some nested object3D is in the scene, besides the method I showed above, is there a way to know when it is on the main scene? traversing the ancestors is something you do once you know that the scene might be there… I’m trying to get a way of knowing when is the right time to do that.
The most brute force dumb way would be to traverseAncestors on an interval until the scene is found… the other way is the method I showed…
Do you know any better way? A cleaner and less convoluted way?
The most brute force dumb way (I’m using your words), would be to add event listeners inside the animation loop, just like what you’re doing.
What you’re facing is an app level problem, you’re the one triggering the add
or attach
actions, you should add an event dispatcher after those actions are called, then listen to the events outside the animation loop, and proceed with your logic.
I’m not sure why you would interact with discarded objects, but since the child object could be added to any parent Object3D
, and the parent object is not guarantied to be part of a scene (or the “main scene” you’re referring to). The traverse ancestors method is the best way to get to the top, if you don’t find any scene (object.isScene
) at the top, then the object is not part of the scene graph. And If you find a scene you can check it it’s your main scene
Do you understand what I’m trying to do? I did an example on codesandbox have you watched it at all?
“The traverse ancestors method is the best way to get to the top” : the entire point of this thread is to know WHEN to do that… traversing to the top in itself was not the subject here at all. It is WHEN to do it…
When an Object is added!
In my case I’m building a complex stucture of nested object3d, imagine it being a building object, with an array of floor object, and each have appartment objects and room objects etc…
the building is dynamic based on some logic. When i add the building to the stage I want, for example, a chair to know that it is on stage… and also, let’s say I’m building a new floor, part by part, in a process that takes some time, at the end, I add the floor to the building… again, I want a chair to know that it has been added.
A building will be added to the stage, but a floor will be added to a building, and an apartment to a floor… I dont have everything on the stage.
And if I remove a floor, I want a char to know that it is no longer part of the scene.
The solution is either on add or remove traverse the childs down, or each child interested in the scene should traverse upwards…
I like the traverse upwards because that way only the interested objects do work and i don’t have to traverse so many childrens from above…
All this is because, I want a chair (using this as an example) to be able to execute code on every frame using the main loop (that in my case lives in the main scene)
I could use a singleton, or have each object just register it’s own “this.renderer.setAnimationLoop” but still I would need to know when to stop (when removed from scene)
Adding objects is not a performance issue, and it shouldn’t be. This operation typically happens during initialization or occasionally at runtime. You’re not going to add an object (or multiple objects) every frame, so putting an object added/removed listener inside the animation loop is a bad idea.
Now that we’ve ruled out performance concerns, and I hope we agree that adding objects should be a relatively slow process (not something done every frame), traversing ancestors isn’t as bad as it might seem.
Let’s follow your example. Suppose you have a chair object that needs to know where it is at all times:
- The chair object should only be added to a room or specific parent objects (like a table or desk).
- When the chair is added, trigger an
add
event. - In the event listener, traverse the chair’s ancestors (yes, traverse up). Once you find a room parent, register it as the chair’s parent room (
chair.userData.room = room
). If you find an apartment, register it as the parent apartment (chair.userData.apartment = apartment
), and do the same for the floor and building. - You don’t need to do this traversal every frame, only when the object is added.
- Use the cached data (
chair.userData.room
,chair.userData.apartment
, etc.) whenever you need to determine the chair’s location. - If the chair is removed, trigger a
remove
event. In the event listener, clear the chair’s cache because it no longer has any parents.
Is this about deciding what to render?
You can turn off a whole hierachy using the object3d visible property.
Example, try each visible
check box in this demo.
I’m trying to have an object that extends Object3D to run code on every frame only when it is added or any of it’s parents it is added to the scene.
You might add this object to an object that it is still not in the scene, and so on… but once any ancestor is added to the scene, it should start executing some function on every frame. And stop if it is removed or any of its ancestors…
Sounds like a simple task but the Object3D “added” and “removed” are only relevant to the object itself, I have no idea if any ancestor was added to the scene or not unless I do what I shared on my code on codesandbox…
I haven’t found an easier way.
what about this pattern
...
const objectsWhereThingsNeedToBeDone = new Set()
...
// you've created some new object and you want to add it somehwere in any hierarchy
parent.add(someObject3d)
objectsWhereThingsNeedToBeDone.add(someObject3d)
// you could also add o.parent to objectsWhereThingsNeedToBeDone
// or o.parent.parent if it exists
// or even traverse o.children if it helps
...
// in animation loop
for (const o of objectsWhereThingsNeedToBeDone) {
doSomethingWithThing(o)
})
...
// if thing with object is not needed to be done anymore
objectsWhereThingsNeedToBeDone.delete(referenceToSomeObject3d)
I’m sorry for introducing more noise to the discussion:
- traversing up until reaching a scene is not sufficient in the general case. If you have 10 scenes (this is possible) and you render only 5 of them (also possible), you have absolutely no clue if the scene where an object is added will be actually rendered.
- if I have to run object-specific code in the main loop, I will add a flag to every object (while creating, adding and removing it). The flag will say whether the object is active (i.e. its code must be executed) or inactive (no code is executed). For example, if you add a chair to some floor, you do not need to scan till the top, because the floor already knows whether it is active or not. The chair will have the same active/inactive status. Another example, you detach a building from the scene. Then scan all building’s children and set their flags to inactive. Third example - you attach a floor with chairs to a building – then scan the floor and make all its children as active as is the building itself.
@seanwasere that is the overall way in which i’m doing it, the issue is that in your example you just casually wrote “objectsWhereThingsNeedToBeDone.add(someObject3d)” but the entire point of this thread is to find this “objectsWhereThingsNeedToBeDone” reference in a non singleton’ish way… like moving up in the hierarchy like in the example I coded…
@PavelBoytchev 1) in my use-case the “scene” is the one that handles the render loop of all it’s childrens. So 1 or many it doesn’t matter. 2) Adding a flag on “every object” is way overkill. Some objects might be just simple groups or empty objects used as placeholders, they don’t need any logic. Scanning down will always take more time and effort than scanning up from the interested subject…
I’m shocked to see how not so trivial this task is… one would think that having an object know when it is on a scene would be easier. THREE handled the case of a single object having “added” or “removed” to a parent, but it didn’t handled the case where you want to know if it is added to a Scene…
maybe you want layers
As far as I understand your code, when an object is created it adds event listeners to all its parents. Consider the following scenario with scene-building-floor-chair hierarchy:
- building is created and added to the scene
- floor is created, but not added to the scene
- chair is created and added to the floor (chair creates listeners in floor)
- the existing floor is added to the building (floor creates listeners in building)
What part of your code will create chair’s listeners in the building (so that when the building is removed the chair becomes inactive)?