diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index c7426c7299..afd11108c0 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -1294,6 +1294,29 @@ prepare_now(PreparedGraphicsObjects *prepared_objects, return gc; } +/** + * Returns true if the Geom is within the given view frustum. + */ +bool Geom:: +is_in_view(const BoundingVolume *view_frustum, Thread *current_thread) const { + CDLockedReader cdata(_cycler, current_thread); + + if (cdata->_user_bounds != nullptr) { + const GeometricBoundingVolume *gbv = cdata->_user_bounds->as_geometric_bounding_volume(); + return view_frustum->contains(gbv) != BoundingVolume::IF_no_intersection; + } + else if (!cdata->_internal_bounds_stale) { + const GeometricBoundingVolume *gbv = cdata->_internal_bounds->as_geometric_bounding_volume(); + return view_frustum->contains(gbv) != BoundingVolume::IF_no_intersection; + } + else { + CDWriter cdataw(((Geom *)this)->_cycler, cdata, false); + compute_internal_bounds(cdataw, current_thread); + const GeometricBoundingVolume *gbv = cdataw->_internal_bounds->as_geometric_bounding_volume(); + return view_frustum->contains(gbv) != BoundingVolume::IF_no_intersection; + } +} + /** * Actually draws the Geom with the indicated GSG, using the indicated vertex * data (which might have been pre-munged to support the GSG's needs). diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index 6ecc852723..425d2b0a0a 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -157,6 +157,8 @@ PUBLISHED: GraphicsStateGuardianBase *gsg); public: + bool is_in_view(const BoundingVolume *view_frustum, Thread *current_thread) const; + bool draw(GraphicsStateGuardianBase *gsg, const GeomVertexData *vertex_data, size_t num_instances, bool force, Thread *current_thread) const; diff --git a/panda/src/pgraph/geomNode.cxx b/panda/src/pgraph/geomNode.cxx index 5cf987af48..fa4a81beac 100644 --- a/panda/src/pgraph/geomNode.cxx +++ b/panda/src/pgraph/geomNode.cxx @@ -516,44 +516,50 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { trav->_geoms_pcollector.add_level(num_geoms); CPT(TransformState) internal_transform = data.get_internal_transform(trav); - for (int i = 0; i < num_geoms; i++) { - CPT(Geom) geom = geoms.get_geom(i); - if (geom->is_empty()) { - continue; + if (num_geoms == 1) { + // If there's only one Geom, we don't need to bother culling each individual + // Geom bounding volume against the view frustum, since we've already + // checked the one on the GeomNode itself. + CPT(Geom) geom = geoms.get_geom(0); + if (!geom->is_empty()) { + CPT(RenderState) state = data._state->compose(geoms.get_geom_state(0)); + if (!state->has_cull_callback() || state->cull_callback(trav, data)) { + CullableObject *object = + new CullableObject(std::move(geom), std::move(state), std::move(internal_transform)); + object->_instances = data._instances; + trav->get_cull_handler()->record_object(object, trav); + } } + } + else { + // More than one Geom. + for (int i = 0; i < num_geoms; i++) { + CPT(Geom) geom = geoms.get_geom(i); + if (geom->is_empty()) { + continue; + } - CPT(RenderState) state = data._state->compose(geoms.get_geom_state(i)); - if (state->has_cull_callback() && !state->cull_callback(trav, data)) { - // Cull. - continue; - } + CPT(RenderState) state = data._state->compose(geoms.get_geom_state(i)); + if (state->has_cull_callback() && !state->cull_callback(trav, data)) { + // Cull. + continue; + } - if (data._instances != nullptr) { - // Draw each individual instance. We don't bother culling each - // individual Geom for each instance; that is probably way too slow. - CullableObject *object = - new CullableObject(std::move(geom), std::move(state), internal_transform); - object->_instances = data._instances; - trav->get_cull_handler()->record_object(object, trav); - continue; - } + if (data._instances != nullptr) { + // Draw each individual instance. We don't bother culling each + // individual Geom for each instance; that is probably way too slow. + CullableObject *object = + new CullableObject(std::move(geom), std::move(state), internal_transform); + object->_instances = data._instances; + trav->get_cull_handler()->record_object(object, trav); + continue; + } - // Cull the Geom bounding volume against the view frustum andor the cull - // planes. Don't bother unless we've got more than one Geom, since - // otherwise the bounding volume of the GeomNode is (probably) the same as - // that of the one Geom, and we've already culled against that. - if (num_geoms > 1) { - if (data._view_frustum != nullptr) { - // Cull the individual Geom against the view frustum. - CPT(BoundingVolume) geom_volume = geom->get_bounds(current_thread); - const GeometricBoundingVolume *geom_gbv = - geom_volume->as_geometric_bounding_volume(); - - int result = data._view_frustum->contains(geom_gbv); - if (result == BoundingVolume::IF_no_intersection) { - // Cull this Geom. - continue; - } + // Cull the individual Geom against the view frustum. + if (data._view_frustum != nullptr && + !geom->is_in_view(data._view_frustum, current_thread)) { + // Cull this Geom. + continue; } if (!data._cull_planes->is_empty()) { // Also cull the Geom against the cull planes. @@ -567,11 +573,11 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) { continue; } } - } - CullableObject *object = - new CullableObject(std::move(geom), std::move(state), internal_transform); - trav->get_cull_handler()->record_object(object, trav); + CullableObject *object = + new CullableObject(std::move(geom), std::move(state), internal_transform); + trav->get_cull_handler()->record_object(object, trav); + } } }