private void process(Spatial scene) {
            if (scene.getCullHint() == Spatial.CullHint.Always) return;

            RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
            if (scene instanceof Geometry) {
                // convert bounding box to light's viewproj space
                Geometry occluder = (Geometry)scene;
                if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive
                        && !occluder.isGrouped() && occluder.getWorldBound()!=null) {
                    BoundingVolume bv = occluder.getWorldBound();
                    BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);

                    boolean intersects = splitBB.intersects(occBox);
                    if (!intersects && occBox instanceof BoundingBox) {
                        BoundingBox occBB = (BoundingBox) occBox;
                        // Kirill 01/10/2011
                        // Extend the occluder further into the frustum
                        // This fixes shadow disappearing issues when
                        // the caster itself is not in the view camera
                        // but its shadow is in the camera
                        //      The number is in world units
                        occBB.setZExtent(occBB.getZExtent() + 50);
                        occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
                        if (splitBB.intersects(occBB)) {
                            //Nehon : prevent NaN and infinity values to screw the final bounding box
                            if (!Float.isNaN(occBox.getCenter().x) && !Float.isInfinite(occBox.getCenter().x)) {
                                // To prevent extending the depth range too much
                                // We return the bound to its former shape
                                // Before adding it
                                occBB.setZExtent(occBB.getZExtent() - 50);
                                occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));
                                casterBB.mergeLocal(occBox);
                                casterCount++;
                            }
                            if (splitOccluders != null) {
                                splitOccluders.add(occluder);
                            }
                        }
                    } else if (intersects) {
                        casterBB.mergeLocal(occBox);
                        casterCount++;
                        if (splitOccluders != null) {
                            splitOccluders.add(occluder);
                        }
                    }
                }
            } else if (scene instanceof Node && ((Node)scene).getWorldBound() != null) {
                Node nodeOcc = (Node)scene;
                boolean intersects = false;
                // some
                BoundingVolume bv = nodeOcc.getWorldBound();
                BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);

                intersects = splitBB.intersects(occBox);
                if (!intersects && occBox instanceof BoundingBox) {
                    BoundingBox occBB = (BoundingBox) occBox;
                    //Kirill 01/10/2011
                    // Extend the occluder further into the frustum
                    // This fixes shadow disappearing issues when
                    // the caster itself is not in the view camera
                    // but its shadow is in the camera
                    //      The number is in world units
                    occBB.setZExtent(occBB.getZExtent() + 50);
                    occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
                    intersects = splitBB.intersects(occBB);
                }

                if (intersects) {
                    for (Spatial child : ((Node)scene).getChildren()) {
                        process(child);
                    }
                }
            }
        }
Check the rootScene against camera frustum and if intersects process it recursively.
The global OccludersExtractor variables need to be initialized first.
Variables are updated and used in {@link ShadowUtil#updateShadowCamera} at last.

@param scene the root of the scene to check (may be null)
@return the number of shadow casters found
jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java