setBounds setFinal not behaving as expected

I have a scene full of tall, thin plants. By default, their bounding volumes are spheres. I would like to force their bounding volumes to be tight boxes to improve culling performance.

To achieve this I use the following code.

from pandac.PandaModules import *
import direct.directbase.DirectStart

kelpCollection = loader.loadModel( "models/environment/kelp02_model.egg" ).getChildren()
for kelp in kelpCollection:
	min = Point3()
	max = Point3()
	kelp.calcTightBounds( min, max )
	box = BoundingBox( min, max )
	kelp.node().setBounds( box )
	kelp.node().setFinal( True )

	kelp.reparentTo( render )
	kelp.showBounds()

On its own, this code is not enough. The bounding volume will still be a sphere. If I add the following to the my prc file:

bounds-type best

then my bounds do show up as the expected tight box. The problem is the prc setting makes many other objects in my scene use boxes instead of spheres for the bounding volume.

What is the correct way to force just the kelp to use the tight box I specify without affecting the bounding volumes of the rest of my scene?

I am just guessing with this, but I think it might do what you want.

kelp.node().setBoundsType( BoundingVolume.BT_box )
kelp.node().setBounds( box )
kelp.node().setFinal( True )

I removed ‘bounds-type best’ from the prc file and added:

kelp.node().setBoundsType( BoundingVolume.BTBox )

This sort of works. The bounding volume is indeed a box, but its sides are all the same length. It looks like it is the smallest cube that could still fit the default sphere that I didn’t want.

Calling kelp.getBounds() returns:
bbox, (-3.23845 -3.27429 -0.207731) to (3.33854 3.30269 6.36925)

Calling kelp.node().getInternalBounds() returns:
bbox, (-0.786121 -0.77569 0) to (0.886206 0.804088 6.16152)

The internal box is the tight one that I want. Why the difference between the two?

Digging a little deeper, I found a GeomNode under the kelp nodepath that contains a sphere bounding volume with radius 3, so that appears to be why kelp’s getBounds is such a large box.

I thought that the setFinal( True ) call on kelp’s node would mean Panda wouldn’t bother going through any child nodepaths to determine the final bounding volume? Is that incorrect?

This is want seemed to work in the end.


from pandac.PandaModules import *
import direct.directbase.DirectStart

def forceTightBox( nodePath ):
	min = Point3()
	max = Point3()
	nodePath.calcTightBounds( min, max )
	box = BoundingBox( min, max )
	nodePath.node().setBoundsType( BoundingVolume.BTBox )
	nodePath.node().setBounds( box )
	nodePath.node().setFinal( True )

	for child in nodePath.getChildren():
		forceTightBox( child )

kelpCollection = loader.loadModel( "models/environment/kelp02_model.egg" ).getChildren()
for kelp in kelpCollection:
	forceTightBox( kelp )

	kelp.reparentTo( render )
	kelp.showBounds()

run()

setFinal(True) only means that Panda will not visit any nodes below this node when testing bounding volumes. It has nothing to do with the computation of bounding volumes.

By default, Panda uses a fairly casual approach to computing bounding volumes, on the theory that a tight bounding volume doesn’t usually grant enough performance benefit over a loose one to justify a lot of effort put into computing it. Of course, every situation is different, and computing the bounding volumes explicitly as you have done is a fine way to force the tighter bounding volumes; it particularly makes sense in a static scene where the bounding volumes don’t have to be recomputed after loading.

The setBoundsType() method specifies the type of bounding volume that will be computed for this node. It’s necessary to set this to BTBox. Note that when you specify a bounding volume with setBounds(), you do not replace the bounding volume; this method only adds an additional bounding volume to the set of bounding volumes that contribute to the node. This is why it is necessary to call both setBoundsType() and setBounds() in order to completely replace the bounding volume.

David