[resolved] Frustum culling confusion

I understand Frustum culling design, and I see in the code how Bounding volumes are merged and checked, etc.  I am quite confused, however, as to why my terrain blocks always generate FrustumIntersect.Inside, regardless of where my camera is.



Details which may or may not matter:


  • Using a ChaseCamera

  • My individual (custom) terrain blocks always assigned a BoundingBox and update it, upon load

  • I do have some Nodes interposed between the rootNode and the terrain blocks, but these are Nodes which do not override any Bound-related method, and therefore recursions initiated by rootNode should work as designed (i.e., the same as if there were no interposed Nodes).

  • I depend on default game update's rootNode.updateGeometricState() to update the world bounding volumes of everything.

  • I'm using default Frustum settings.  I understand why items are assumed to be inside the Frustum if there is null world bounding volume, but it seems to defy the goals of Frustum culling to include ALL of my terrain blocks which do have world bounding volumes.



I am now going to check active Frustum settings; and also visually inspect all levels of bounding volumes.  But I implore you to reply if you have any tips, because both of these tasks are very time consuming, since the Frustum geometry is very complex (nothing like a bounding volume) and it's going to take a good amount of coding to view each of the several bv's I need to see without being obscured by other bv's...  plus what if I find that some bv's are indeed entirely outside of the frustum but are still not culled?

UPDATE:  I've verified that the frustum settings are what they should be, and the camera location and direction get updated correctly as I move the cam.  If my Bounding Volumes all look right, I guess I'll have to examine the operation of AbstractCamera.contains().
Now, I've also verified that all bounding volumes are exactly what they should be, including intermediate ones.  Looks like either muti-threading contention or a bug in AbstractCamera.contain(), since the camera is in the +/+/+ quadrant pointing in a +Z direction, yet it is incorrectly returning FrustumIntersect.Inside for a terrain block residing entirely in the -X/-Z plane.  It is impossible for this block to intersect (much less be inside) the camera frustum, even if the X and Y coordinates are ignored.

One problem that wasted a few hours of my time was that the Camera.contains() method should not be used unless you save then restore the planeState like is done in Spatial.onDraw(), and that you make sure not to invoke it while a render thread runs.  The dangerous, public usage requirements for Camera.planeState should be documented…  Far better to re-work that aspect to get kill this violation of encapsulation, but since neither I not anybody else has time to do that, I'll document it when I get time.

This is something I noticed as well, apparently its a sort of an optimization. As far as I understand, the plane state variable can be used to prevent certain frustum planes from checking against the bounding volume after it has been checked already, thus making frustum culling more efficient (?). I really would like more information on this as well, since I am using jME's frustum checking method in the jME3 engine.

Momoko_Fan said:

This is something I noticed as well, apparently its a sort of an optimization. As far as I understand, the plane state variable can be used to prevent certain frustum planes from checking against the bounding volume after it has been checked already, thus making frustum culling more efficient (?). I really would like more information on this as well, since I am using jME's frustum checking method in the jME3 engine.


I understand that completely now.  I'll document it properly when I have time.

Because of .contains() being documented as if it were for general usage, but not working that way, I got diverted from my real problem for 6 hours with false diagnostics.  Turns out that my original problem was due to the funny recursive overwrite behavior of Node.setModelBound().  This replaces all descendant nodes with clones of the specified BoundingVolume instance (and of course the replacement BVs will be initialized to size 0).  That's an extremely liberal choice of behavior for a recursive routine.  N.p. with replacing null bounding volumes, but...
blaine said:

I understand that completely now.  I'll document it properly when I have time.

Great  :) glad you figured it out

blaine said:

Turns out that my original problem was due to the funny recursive overwrite behavior of Node.setModelBound().  This replaces all descendant nodes with clones of the specified BoundingVolume instance (and of course the replacement BVs will be initialized to size 0).  That's an extremely liberal choice of behavior for a recursive routine.  N.p. with replacing null bounding volumes, but...

The model bound is the type of bound that is set on geometry, so when you set the model bound on a node higher up in the heirarchy, it assumes you want to change the type of bound that is set on that node, and for that, all bounds under the node must be replaced. It also assumes that you would call updateModelBound()+updateWorldBound() afterwards, which would update the values of the new (invalid) boundings and merge them up until the node you are manipulating.
Momoko_Fan said:

blaine said:

Turns out that my original problem was due to the funny recursive overwrite behavior of Node.setModelBound().  This replaces all descendant nodes with clones of the specified BoundingVolume instance (and of course the replacement BVs will be initialized to size 0).  That's an extremely liberal choice of behavior for a recursive routine.  N.p. with replacing null bounding volumes, but...

The model bound is the type of bound that is set on geometry, so when you set the model bound on a node higher up in the heirarchy, it assumes you want to change the type of bound that is set on that node, and for that, all bounds under the node must be replaced...


That would justify if a Node's world BV type had to match that of the descendant Geometry BV implementations, but that is not so, otherwise an entire scene could use only one BV impl type since every scene has a Node root.  It often is very advantageous for the merging, world BV to be of different impl. type than the aggregated BVs, and the merging logic is documented for this reason.  Your assertion, and for that, all bounds under the node must be replaced is only true because Node.setBoundingVolume() implies this unnecessary constraint.

Would also be acceptable if this method was only to used for local "model BV" purposes, but as there is no method in the API (correct me if wrong) to specify a desired WorldBoundary impl type, this is the method I would have to use, for example, if I simply want to dictate that my space ship be bounded with a sphere, regardless of how many boxes are inside.