diff --git a/panda/src/pgraph/cullPlanes.cxx b/panda/src/pgraph/cullPlanes.cxx index 825680e3b0..6cf677f125 100644 --- a/panda/src/pgraph/cullPlanes.cxx +++ b/panda/src/pgraph/cullPlanes.cxx @@ -130,6 +130,10 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data, if (node_effect != (OccluderEffect *)NULL) { CPT(TransformState) center_transform = NULL; + // We'll need to know the occluder's frustum in cull-center + // space. + SceneSetup *scene = trav->get_scene(); + const Lens *lens = scene->get_lens(); int num_on_occluders = node_effect->get_num_on_occluders(); for (int i = 0; i < num_on_occluders; ++i) { @@ -139,10 +143,6 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data, // Here's a new occluder; consider adding it to the list. OccluderNode *occluder_node = DCAST(OccluderNode, occluder.node()); nassertr(occluder_node->get_num_vertices() == 4, new_planes); - - // We'll need to know the occluder's frustum in cull-center - // space. - SceneSetup *scene = trav->get_scene(); CPT(TransformState) occluder_transform = occluder.get_transform(scene->get_cull_center()); @@ -163,45 +163,137 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data, // we might as well make a BoundingBox, which is as tight as // possible, and creating one isn't any less efficient than // transforming the existing bounding volume. - PT(BoundingBox) occluder_gbv; - { - // Get a transform from the occluder directly to this node's - // space for comparing with the current view frustum. - CPT(TransformState) composed_transform = center_transform->compose(occluder_transform); - const LMatrix4f &composed_mat = composed_transform->get_mat(); - LPoint3f ccp[4]; - ccp[0] = occluder_node->get_vertex(0) * composed_mat; - ccp[1] = occluder_node->get_vertex(1) * composed_mat; - ccp[2] = occluder_node->get_vertex(2) * composed_mat; - ccp[3] = occluder_node->get_vertex(3) * composed_mat; + PT(BoundingBox) occluder_gbv; + // Get a transform from the occluder directly to this node's + // space for comparing with the current view frustum. + CPT(TransformState) composed_transform = center_transform->compose(occluder_transform); + const LMatrix4f &composed_mat = composed_transform->get_mat(); + LPoint3f ccp[4]; + ccp[0] = occluder_node->get_vertex(0) * composed_mat; + ccp[1] = occluder_node->get_vertex(1) * composed_mat; + ccp[2] = occluder_node->get_vertex(2) * composed_mat; + ccp[3] = occluder_node->get_vertex(3) * composed_mat; - LPoint3f ccp_min(min(min(ccp[0][0], ccp[1][0]), - min(ccp[2][0], ccp[3][0])), - min(min(ccp[0][1], ccp[1][1]), - min(ccp[2][1], ccp[3][1])), - min(min(ccp[0][2], ccp[1][2]), - min(ccp[2][2], ccp[3][2]))); - LPoint3f ccp_max(max(max(ccp[0][0], ccp[1][0]), - max(ccp[2][0], ccp[3][0])), - max(max(ccp[0][1], ccp[1][1]), - max(ccp[2][1], ccp[3][1])), - max(max(ccp[0][2], ccp[1][2]), - max(ccp[2][2], ccp[3][2]))); - - occluder_gbv = new BoundingBox(ccp_min, ccp_max); + LPoint3f ccp_min(min(min(ccp[0][0], ccp[1][0]), + min(ccp[2][0], ccp[3][0])), + min(min(ccp[0][1], ccp[1][1]), + min(ccp[2][1], ccp[3][1])), + min(min(ccp[0][2], ccp[1][2]), + min(ccp[2][2], ccp[3][2]))); + LPoint3f ccp_max(max(max(ccp[0][0], ccp[1][0]), + max(ccp[2][0], ccp[3][0])), + max(max(ccp[0][1], ccp[1][1]), + max(ccp[2][1], ccp[3][1])), + max(max(ccp[0][2], ccp[1][2]), + max(ccp[2][2], ccp[3][2]))); + + occluder_gbv = new BoundingBox(ccp_min, ccp_max); + + if (data->_view_frustum != (GeometricBoundingVolume *)NULL) { + int occluder_result = data->_view_frustum->contains(occluder_gbv); + if (occluder_result == BoundingVolume::IF_no_intersection) { + // This occluder is outside the view frustum; ignore it. + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << "Ignoring occluder " << occluder << ": outside view frustum.\n"; + } + continue; + } } - if (data->_view_frustum != (GeometricBoundingVolume *)NULL) { - int occluder_result = data->_view_frustum->contains(occluder_gbv); - if (occluder_result == BoundingVolume::IF_no_intersection) { - // This occluder is outside the view frustum; ignore it. - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << "Ignoring occluder " << occluder << ": outside view frustum.\n"; - } - continue; - } - } + // Get the occluder geometry in cull-center space. + const LMatrix4f &occluder_mat = occluder_transform->get_mat(); + LPoint3f points_near[4]; + points_near[0] = occluder_node->get_vertex(0) * occluder_mat; + points_near[1] = occluder_node->get_vertex(1) * occluder_mat; + points_near[2] = occluder_node->get_vertex(2) * occluder_mat; + points_near[3] = occluder_node->get_vertex(3) * occluder_mat; + Planef plane(points_near[0], points_near[1], points_near[2]); + + if (plane.get_normal().dot(LVector3f::forward()) >= 0.0) { + if (occluder_node->is_double_sided()) { + swap(points_near[0], points_near[3]); + swap(points_near[1], points_near[2]); + plane = Planef(points_near[0], points_near[1], points_near[2]); + } else { + // This occluder is facing the wrong direction. Ignore it. + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << "Ignoring occluder " << occluder << ": wrong direction.\n"; + } + continue; + } + } + + float near_clip = lens->get_near(); + if (plane.dist_to_plane(LPoint3f::zero()) <= near_clip) { + // This occluder is behind the camera's near plane. Ignore it. + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << "Ignoring occluder " << occluder << ": behind near plane.\n"; + } + continue; + } + + float d0 = points_near[0].dot(LVector3f::forward()); + float d1 = points_near[1].dot(LVector3f::forward()); + float d2 = points_near[2].dot(LVector3f::forward()); + float d3 = points_near[3].dot(LVector3f::forward()); + + if (d0 <= near_clip && d1 <= near_clip && d2 <= near_clip && d3 <= near_clip) { + // All four corners of the occluder are behind the camera's + // near plane. Ignore it. + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << "Ignoring occluder " << occluder << ": behind near plane (test 2).\n"; + } + continue; + } + + // TODO: it's possible for part of the occlusion polygon to + // intersect the camera's y = 0 plane. If this happens, the + // frustum will go insane and the occluder won't work. The + // proper fix for this is to clip the polygon against the near + // plane, producing a smaller polygon, and use that to + // generate the frustum. But maybe it doesn't matter. In + // lieu of this, we just toss out any occluder with *any* + // corner behind the y = 0 plane. + if (d0 <= 0.0 || d1 <= 0.0 || d2 <= 0.0 || d3 <= 0.0) { + // One of the corners is behind the y = 0 plane. We can't + // handle this case. Ignore it. + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << "Ignoring occluder " << occluder << ": partly behind zero plane.\n"; + } + continue; + } + + if (occluder_node->get_min_coverage()) { + LPoint3f coords[4]; + lens->project(points_near[0], coords[0]); + lens->project(points_near[1], coords[1]); + lens->project(points_near[2], coords[2]); + lens->project(points_near[3], coords[3]); + coords[0][0] = max(-1, min(1, coords[0][0])); + coords[0][1] = max(-1, min(1, coords[0][1])); + coords[1][0] = max(-1, min(1, coords[1][0])); + coords[1][1] = max(-1, min(1, coords[1][1])); + coords[2][0] = max(-1, min(1, coords[2][0])); + coords[2][1] = max(-1, min(1, coords[2][1])); + coords[3][0] = max(-1, min(1, coords[3][0])); + coords[3][1] = max(-1, min(1, coords[3][1])); + float coverage = ((coords[0] - coords[1]).cross(coords[0] - coords[2]).length() + + (coords[3] - coords[1]).cross(coords[3] - coords[2]).length()) + * 0.125; + if (coverage < occluder_node->get_min_coverage()) { + // The occluder does not cover enough screen space. Ignore it. + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << "Ignoring occluder " << occluder << ": coverage less than minimum.\n"; + } + continue; + } + } // Also check if the new occluder is completely within any of // our existing occluder volumes. @@ -217,80 +309,14 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data, if (is_enclosed) { // No reason to add this occluder; it's behind an existing // occluder. - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << "Ignoring occluder " << occluder << ": behind another.\n"; - } + if (pgraph_cat.is_spam()) { + pgraph_cat.spam() + << "Ignoring occluder " << occluder << ": behind another.\n"; + } continue; } // TODO: perhaps we should also check whether any existing // occluders are fully contained within this new one. - - // Get the occluder geometry in cull-center space. - const LMatrix4f &occluder_mat = occluder_transform->get_mat(); - LPoint3f points_near[4]; - points_near[0] = occluder_node->get_vertex(0) * occluder_mat; - points_near[1] = occluder_node->get_vertex(1) * occluder_mat; - points_near[2] = occluder_node->get_vertex(2) * occluder_mat; - points_near[3] = occluder_node->get_vertex(3) * occluder_mat; - - Planef plane(points_near[0], points_near[1], points_near[2]); - if (plane.get_normal().dot(LVector3f::forward()) >= 0.0) { - if (occluder_node->is_double_sided()) { - swap(points_near[0], points_near[3]); - swap(points_near[1], points_near[2]); - plane = Planef(points_near[0], points_near[1], points_near[2]); - } else { - // This occluder is facing the wrong direction. Ignore it. - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << "Ignoring occluder " << occluder << ": wrong direction.\n"; - } - continue; - } - } - - float near_clip = scene->get_lens()->get_near(); - if (plane.dist_to_plane(LPoint3f::zero()) <= near_clip) { - // This occluder is behind the camera's near plane. Ignore it. - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << "Ignoring occluder " << occluder << ": behind near plane.\n"; - } - continue; - } - - float d0 = points_near[0].dot(LVector3f::forward()); - float d1 = points_near[1].dot(LVector3f::forward()); - float d2 = points_near[2].dot(LVector3f::forward()); - float d3 = points_near[3].dot(LVector3f::forward()); - if (d0 <= near_clip && d1 <= near_clip && d2 <= near_clip && d3 <= near_clip) { - // All four corners of the occluder are behind the camera's - // near plane. Ignore it. - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << "Ignoring occluder " << occluder << ": behind near plane (test 2).\n"; - } - continue; - } - - // TODO: it's possible for part of the occlusion polygon to - // intersect the camera's y = 0 plane. If this happens, the - // frustum will go insane and the occluder won't work. The - // proper fix for this is to clip the polygon against the near - // plane, producing a smaller polygon, and use that to - // generate the frustum. But maybe it doesn't matter. In - // lieu of this, we just toss out any occluder with *any* - // corner behind the y = 0 plane. - if (d0 <= 0.0 || d1 <= 0.0 || d2 <= 0.0 || d3 <= 0.0) { - // One of the corners is behind the y = 0 plane. We can't - // handle this case. Ignore it. - if (pgraph_cat.is_spam()) { - pgraph_cat.spam() - << "Ignoring occluder " << occluder << ": partly behind zero plane.\n"; - } - continue; - } // Project those four lines to the camera's far plane. float far_clip = scene->get_lens()->get_far(); diff --git a/panda/src/pgraph/occluderNode.I b/panda/src/pgraph/occluderNode.I index 7e380c0f67..f562d95eeb 100644 --- a/panda/src/pgraph/occluderNode.I +++ b/panda/src/pgraph/occluderNode.I @@ -70,3 +70,24 @@ INLINE void OccluderNode::set_double_sided(bool value) { INLINE bool OccluderNode::is_double_sided() { return _double_sided; } + +//////////////////////////////////////////////////////////////////// +// Function: OccluderNode::set_min_coverage +// Access: Published +// Description: Minimum screen coverage needed before occluder used. +// Range should be 0 to 1. For example, setting to 0.2 +// would mean that the occluder needs to cover 20% of +// the screen to be considered. +//////////////////////////////////////////////////////////////////// +INLINE void OccluderNode::set_min_coverage(float value) { + _min_coverage = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: OccluderNode::get_min_coverage +// Access: Published +// Description: Returns the minimum screen coverage. +//////////////////////////////////////////////////////////////////// +INLINE float OccluderNode::get_min_coverage() { + return _min_coverage; +} diff --git a/panda/src/pgraph/occluderNode.cxx b/panda/src/pgraph/occluderNode.cxx index 573761ecdb..6b55379990 100644 --- a/panda/src/pgraph/occluderNode.cxx +++ b/panda/src/pgraph/occluderNode.cxx @@ -62,6 +62,7 @@ OccluderNode(const string &name) : // OccluderNodes are hidden by default. set_overall_hidden(true); set_double_sided(false); + set_min_coverage(0.0); set_vertices(LPoint3f::rfu(-1.0, 0.0, -1.0), LPoint3f::rfu(1.0, 0.0, -1.0), LPoint3f::rfu(1.0, 0.0, 1.0), @@ -77,7 +78,8 @@ OccluderNode:: OccluderNode(const OccluderNode ©) : PandaNode(copy), _vertices(copy._vertices), - _double_sided(copy._double_sided) + _double_sided(copy._double_sided), + _min_coverage(copy._min_coverage) { } diff --git a/panda/src/pgraph/occluderNode.h b/panda/src/pgraph/occluderNode.h index b6ad4d7f7c..432f6cbbfc 100644 --- a/panda/src/pgraph/occluderNode.h +++ b/panda/src/pgraph/occluderNode.h @@ -53,6 +53,8 @@ public: PUBLISHED: INLINE void set_double_sided(bool value); INLINE bool is_double_sided(); + INLINE void set_min_coverage(float value); + INLINE float get_min_coverage(); INLINE void set_vertices(const LPoint3f &v0, const LPoint3f &v1, const LPoint3f &v2, const LPoint3f &v3); INLINE int get_num_vertices() const; @@ -70,6 +72,7 @@ protected: private: bool _double_sided; + float _min_coverage; typedef pvector Vertices; Vertices _vertices;