How can i loop all children of my node and check if any id is used more than once?

geometry

#1

I have a function that loops my scene to get a specific node. Once it gets this node, it traverses its children and checks if any of the children have geometry or material properties. If they do, then I dispose() then remove the child.


#2

I’m not sure if i understand you correctly. Do you want to only remove a object and it’s children, but ensure to not dispose the used geometries and materials?

If that’s the case you might could try this (not tested)

// Use a counter

THREE.Geometry.prototype.used = 0;

function removeNode(node, root) {

    var geometries = {};

    // 1. Collect all geometries of the children in a lookup map

    node.traverse(function(child) {

        if ( child.isMesh && child.geometry !== undefined ) {

            if ( geometries[ child.geometry.uuid ] === undefined ) {

                geometries[ child.geometry.uuid ] = child.geometry;


                // Set the used counter to zero, if any other mesh uses it, it wont be zero in step 3

                child.geometry.used = 0;

            }

        }

    });

    // Remove the node to exclude it from next step

    node.parent.remove(node);



    // 2. Traverse the root/scene and count the usage

    root.traverse(function(child) {

        if (
            child.isMesh && child.geometry !== undefined &&

            // The mesh shares a geometry with the node + children

            geometries[ child.geometry.uuid ] !== undefined

        ) {

            geometries[ child.geometry.uuid ].used ++ ;

        }

    });


        // 3. Check them if they are still used

        for ( var uuid in geometries ) {

            var geometry = geometries[ uuid ];

            if ( geometry.used == 0 ) {

                geometry.dispose();

            }

        }


    }

It seems THREE hasn’t a in-built resource reference counter for resource usage. @mrdoob maybe this could be implemented? It would require a explicit setter or setGeometry method though, for a reference counter, together with a explicit mesh.destroy() that removes itself from it’s parent and calls a this.setMaterial(null) and this.setGeometry(null) to unlink from the resources, on itself and the children. A resource “garbage collector” checking in intervals if trashed resources are still trashed after some time could then dispose them.


#3

The below function

1 - traverses the scene looking for if the id passed as paramters matches the id of the treenode

2- If ids match. it checks if the node has children

3- if it does. it traverses the children of node and checks if instance of Mesh

4- if the node contains children of instance Mesh it disposes geometries and
materials

5- removes child from node


#4

But according to your code you would dispose a geometry when it’s still used by another mesh. The function i posted does what you describe, but instead traversing the whole scene per child it will store a lookup map of them before traversing the scene only once.

You could try it like this, with the function i posted and yours to find the “treeNode”

    function removeBySid(sid) {
        scene.traverse(function(node) {
                sid.forEach(function(id) {
                    if (node.treeNode && node.treeNode.sid === id) {
                     
                        removeNode(node, scene);
                        
                    }
            });
        });
    }

Btw. you don’t need to check for children, more like children.length, it’s default on Object3D


#5

Can you please provide the code in one section. i dont understand what you mean by the above quote.

does the function delete check if the geometryid is used somewhere else in scene?
where in the function does it dispose only if it is not used somewhere else?

sorry. i dont quite understand it fully.

Thank you for your replies :slight_smile:


#6

Can you please provide the code in one section. i dont understand what you mean by the above quote.

With the last snippet i mean, you could use your way to find the node you want to remove, then call the removeNode(node scene) function i posted in my first reply. I hope this deconfused it.

does the function delete check if the geometryid is used somewhere else in scene?

Yes, removeNode(node, root) will remove “node” from “root” (scene) and ensure to only dispose the geometries from “node” and it’s children if it isn’t used anywhere in the “root”. I didn’t tested it but it should work.

where in the function does it dispose only if it is not used somewhere else?

After step 1 all relevant geometries from “node” and it’s children are remembered in the geometries lookup object. Then it goes through the desired root object (scene) and checks if any other mesh uses one of the remembered geometries, if so it increments used on the geometry. At step 3, it then loops the remembered geometries and if used is still zero it gets disposed.


#7

Thanks for your help :slight_smile:
you will notice in my code i have

if (child.material) {
	child.material.dispose();      
}

i want to place this somewhere too. where should this go. i dont need to check anything for this. i just want to dispose the material either way.


#8

If you just want to dispose it (without check for shared ones like with geometry) then you can place it at step 1

        node.traverse(function(child) {

            if ( child.isMesh ) {

                if ( child.material )
                    child.material.dispose();

                if ( child.geometry && geometries[ child.geometry.uuid ] === undefined ) {

                    geometries[ child.geometry.uuid ] = child.geometry;


                    // Set the used counter to zero, if any other mesh uses it, it wont be zero in step 3

                    child.geometry.used = 0;

                }

            }

        });

In case you want to check this for shared materials too, you could do it the same way as with geometries.