mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
further occluder enhancements from teedee
This commit is contained in:
parent
ee5c561acc
commit
1b8d55ce34
@ -130,6 +130,10 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data,
|
|||||||
|
|
||||||
if (node_effect != (OccluderEffect *)NULL) {
|
if (node_effect != (OccluderEffect *)NULL) {
|
||||||
CPT(TransformState) center_transform = 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();
|
int num_on_occluders = node_effect->get_num_on_occluders();
|
||||||
for (int i = 0; i < num_on_occluders; ++i) {
|
for (int i = 0; i < num_on_occluders; ++i) {
|
||||||
@ -140,10 +144,6 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data,
|
|||||||
OccluderNode *occluder_node = DCAST(OccluderNode, occluder.node());
|
OccluderNode *occluder_node = DCAST(OccluderNode, occluder.node());
|
||||||
nassertr(occluder_node->get_num_vertices() == 4, new_planes);
|
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());
|
CPT(TransformState) occluder_transform = occluder.get_transform(scene->get_cull_center());
|
||||||
|
|
||||||
// And the transform from cull-center space into the current
|
// And the transform from cull-center space into the current
|
||||||
@ -163,45 +163,137 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data,
|
|||||||
// we might as well make a BoundingBox, which is as tight as
|
// we might as well make a BoundingBox, which is as tight as
|
||||||
// possible, and creating one isn't any less efficient than
|
// possible, and creating one isn't any less efficient than
|
||||||
// transforming the existing bounding volume.
|
// transforming the existing bounding volume.
|
||||||
PT(BoundingBox) occluder_gbv;
|
PT(BoundingBox) occluder_gbv;
|
||||||
{
|
// Get a transform from the occluder directly to this node's
|
||||||
// Get a transform from the occluder directly to this node's
|
// space for comparing with the current view frustum.
|
||||||
// space for comparing with the current view frustum.
|
CPT(TransformState) composed_transform = center_transform->compose(occluder_transform);
|
||||||
CPT(TransformState) composed_transform = center_transform->compose(occluder_transform);
|
const LMatrix4f &composed_mat = composed_transform->get_mat();
|
||||||
const LMatrix4f &composed_mat = composed_transform->get_mat();
|
LPoint3f ccp[4];
|
||||||
LPoint3f ccp[4];
|
ccp[0] = occluder_node->get_vertex(0) * composed_mat;
|
||||||
ccp[0] = occluder_node->get_vertex(0) * composed_mat;
|
ccp[1] = occluder_node->get_vertex(1) * composed_mat;
|
||||||
ccp[1] = occluder_node->get_vertex(1) * composed_mat;
|
ccp[2] = occluder_node->get_vertex(2) * composed_mat;
|
||||||
ccp[2] = occluder_node->get_vertex(2) * composed_mat;
|
ccp[3] = occluder_node->get_vertex(3) * composed_mat;
|
||||||
ccp[3] = occluder_node->get_vertex(3) * composed_mat;
|
|
||||||
|
|
||||||
LPoint3f ccp_min(min(min(ccp[0][0], ccp[1][0]),
|
LPoint3f ccp_min(min(min(ccp[0][0], ccp[1][0]),
|
||||||
min(ccp[2][0], ccp[3][0])),
|
min(ccp[2][0], ccp[3][0])),
|
||||||
min(min(ccp[0][1], ccp[1][1]),
|
min(min(ccp[0][1], ccp[1][1]),
|
||||||
min(ccp[2][1], ccp[3][1])),
|
min(ccp[2][1], ccp[3][1])),
|
||||||
min(min(ccp[0][2], ccp[1][2]),
|
min(min(ccp[0][2], ccp[1][2]),
|
||||||
min(ccp[2][2], ccp[3][2])));
|
min(ccp[2][2], ccp[3][2])));
|
||||||
LPoint3f ccp_max(max(max(ccp[0][0], ccp[1][0]),
|
LPoint3f ccp_max(max(max(ccp[0][0], ccp[1][0]),
|
||||||
max(ccp[2][0], ccp[3][0])),
|
max(ccp[2][0], ccp[3][0])),
|
||||||
max(max(ccp[0][1], ccp[1][1]),
|
max(max(ccp[0][1], ccp[1][1]),
|
||||||
max(ccp[2][1], ccp[3][1])),
|
max(ccp[2][1], ccp[3][1])),
|
||||||
max(max(ccp[0][2], ccp[1][2]),
|
max(max(ccp[0][2], ccp[1][2]),
|
||||||
max(ccp[2][2], ccp[3][2])));
|
max(ccp[2][2], ccp[3][2])));
|
||||||
|
|
||||||
occluder_gbv = new BoundingBox(ccp_min, ccp_max);
|
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) {
|
// Get the occluder geometry in cull-center space.
|
||||||
int occluder_result = data->_view_frustum->contains(occluder_gbv);
|
const LMatrix4f &occluder_mat = occluder_transform->get_mat();
|
||||||
if (occluder_result == BoundingVolume::IF_no_intersection) {
|
LPoint3f points_near[4];
|
||||||
// This occluder is outside the view frustum; ignore it.
|
points_near[0] = occluder_node->get_vertex(0) * occluder_mat;
|
||||||
if (pgraph_cat.is_spam()) {
|
points_near[1] = occluder_node->get_vertex(1) * occluder_mat;
|
||||||
pgraph_cat.spam()
|
points_near[2] = occluder_node->get_vertex(2) * occluder_mat;
|
||||||
<< "Ignoring occluder " << occluder << ": outside view frustum.\n";
|
points_near[3] = occluder_node->get_vertex(3) * occluder_mat;
|
||||||
}
|
Planef plane(points_near[0], points_near[1], points_near[2]);
|
||||||
continue;
|
|
||||||
}
|
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
|
// Also check if the new occluder is completely within any of
|
||||||
// our existing occluder volumes.
|
// our existing occluder volumes.
|
||||||
@ -217,81 +309,15 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data,
|
|||||||
if (is_enclosed) {
|
if (is_enclosed) {
|
||||||
// No reason to add this occluder; it's behind an existing
|
// No reason to add this occluder; it's behind an existing
|
||||||
// occluder.
|
// occluder.
|
||||||
if (pgraph_cat.is_spam()) {
|
if (pgraph_cat.is_spam()) {
|
||||||
pgraph_cat.spam()
|
pgraph_cat.spam()
|
||||||
<< "Ignoring occluder " << occluder << ": behind another.\n";
|
<< "Ignoring occluder " << occluder << ": behind another.\n";
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// TODO: perhaps we should also check whether any existing
|
// TODO: perhaps we should also check whether any existing
|
||||||
// occluders are fully contained within this new one.
|
// 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.
|
// Project those four lines to the camera's far plane.
|
||||||
float far_clip = scene->get_lens()->get_far();
|
float far_clip = scene->get_lens()->get_far();
|
||||||
Planef far_plane(-LVector3f::forward(), LVector3f::forward() * far_clip);
|
Planef far_plane(-LVector3f::forward(), LVector3f::forward() * far_clip);
|
||||||
|
@ -70,3 +70,24 @@ INLINE void OccluderNode::set_double_sided(bool value) {
|
|||||||
INLINE bool OccluderNode::is_double_sided() {
|
INLINE bool OccluderNode::is_double_sided() {
|
||||||
return _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;
|
||||||
|
}
|
||||||
|
@ -62,6 +62,7 @@ OccluderNode(const string &name) :
|
|||||||
// OccluderNodes are hidden by default.
|
// OccluderNodes are hidden by default.
|
||||||
set_overall_hidden(true);
|
set_overall_hidden(true);
|
||||||
set_double_sided(false);
|
set_double_sided(false);
|
||||||
|
set_min_coverage(0.0);
|
||||||
set_vertices(LPoint3f::rfu(-1.0, 0.0, -1.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),
|
||||||
LPoint3f::rfu(1.0, 0.0, 1.0),
|
LPoint3f::rfu(1.0, 0.0, 1.0),
|
||||||
@ -77,7 +78,8 @@ OccluderNode::
|
|||||||
OccluderNode(const OccluderNode ©) :
|
OccluderNode(const OccluderNode ©) :
|
||||||
PandaNode(copy),
|
PandaNode(copy),
|
||||||
_vertices(copy._vertices),
|
_vertices(copy._vertices),
|
||||||
_double_sided(copy._double_sided)
|
_double_sided(copy._double_sided),
|
||||||
|
_min_coverage(copy._min_coverage)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@ public:
|
|||||||
PUBLISHED:
|
PUBLISHED:
|
||||||
INLINE void set_double_sided(bool value);
|
INLINE void set_double_sided(bool value);
|
||||||
INLINE bool is_double_sided();
|
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,
|
INLINE void set_vertices(const LPoint3f &v0, const LPoint3f &v1,
|
||||||
const LPoint3f &v2, const LPoint3f &v3);
|
const LPoint3f &v2, const LPoint3f &v3);
|
||||||
INLINE int get_num_vertices() const;
|
INLINE int get_num_vertices() const;
|
||||||
@ -70,6 +72,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool _double_sided;
|
bool _double_sided;
|
||||||
|
float _min_coverage;
|
||||||
typedef pvector<LPoint3f> Vertices;
|
typedef pvector<LPoint3f> Vertices;
|
||||||
Vertices _vertices;
|
Vertices _vertices;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user