explicit occlusion culling support from community member teedee, with additional enhancements by drwr

This commit is contained in:
David Rose 2011-03-18 01:56:09 +00:00
parent 9f9ed8bc56
commit 44375f019d
31 changed files with 1582 additions and 82 deletions

View File

@ -53,6 +53,7 @@
#include "lightLensNode.h"
#include "colorAttrib.h"
#include "colorScaleAttrib.h"
#include "clipPlaneAttrib.h"
#include <algorithm>
#include <limits.h>

View File

@ -42,6 +42,7 @@
#include "lightAttrib.h"
#include "stencilAttrib.h"
#include "scissorAttrib.h"
#include "clipPlaneAttrib.h"
#include "fog.h"
#include "throw_event.h"
#include "geomVertexFormat.h"

View File

@ -43,6 +43,7 @@
#include "lightAttrib.h"
#include "stencilAttrib.h"
#include "scissorAttrib.h"
#include "clipPlaneAttrib.h"
#include "fog.h"
#include "throw_event.h"
#include "geomVertexFormat.h"

View File

@ -498,6 +498,30 @@ get_portal_flag() const {
return ((_flags2 & F2_portal_flag) != 0);
}
////////////////////////////////////////////////////////////////////
// Function: EggGroup::set_occluder_flag
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void EggGroup::
set_occluder_flag(bool flag) {
if (flag) {
_flags2 |= F2_occluder_flag;
} else {
_flags2 &= ~F2_occluder_flag;
}
}
////////////////////////////////////////////////////////////////////
// Function: EggGroup::get_occluder_flag
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool EggGroup::
get_occluder_flag() const {
return ((_flags2 & F2_occluder_flag) != 0);
}
////////////////////////////////////////////////////////////////////
// Function: EggGroup::set_polylight_flag
// Access: Published

View File

@ -260,6 +260,10 @@ write(ostream &out, int indent_level) const {
indent(out, indent_level) << "<Scalar> portal { 1 }\n";
}
if (get_occluder_flag()) {
indent(out, indent_level) << "<Scalar> occluder { 1 }\n";
}
if (get_polylight_flag()) {
indent(out, indent_level) << "<Scalar> polylight { 1 }\n";
}

View File

@ -222,6 +222,9 @@ PUBLISHED:
INLINE void set_portal_flag(bool flag);
INLINE bool get_portal_flag() const;
INLINE void set_occluder_flag(bool flag);
INLINE bool get_occluder_flag() const;
INLINE void set_polylight_flag(bool flag);
INLINE bool get_polylight_flag() const;
@ -361,8 +364,8 @@ private:
F2_polylight_flag = 0x00000100,
F2_indexed_flag = 0x00000200,
F2_has_indexed_flag = 0x00000400,
F2_has_blend_color = 0x00000800,
F2_occluder_flag = 0x00001000,
};
int _flags;

View File

@ -1292,6 +1292,9 @@ group_body:
} else if (cmp_nocase_uh(name, "portal") == 0) {
group->set_portal_flag(value != 0);
} else if (cmp_nocase_uh(name, "occluder") == 0) {
group->set_occluder_flag(value != 0);
} else if (cmp_nocase_uh(name, "polylight") == 0) {
group->set_polylight_flag(value != 0);

View File

@ -43,6 +43,7 @@
#include "sequenceNode.h"
#include "switchNode.h"
#include "portalNode.h"
#include "occluderNode.h"
#include "polylightNode.h"
#include "lodNode.h"
#include "modelNode.h"
@ -1860,7 +1861,20 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
egg2pg_cat.warning()
<< "Portal " << egg_group->get_name() << " has no vertices!\n";
}
} else if (egg_group->get_occluder_flag()) {
// Create an occluder instead of a regular polyset. Scan the
// children of this node looking for a polygon, the same as the
// portal polygon case, above.
OccluderNode *pnode = new OccluderNode(egg_group->get_name());
node = pnode;
set_occluder_polygon(egg_group, pnode);
if (pnode->get_num_vertices() == 0) {
egg2pg_cat.warning()
<< "Occluder " << egg_group->get_name() << " has no vertices!\n";
}
} else if (egg_group->get_polylight_flag()) {
// Create a polylight instead of a regular polyset.
// use make_sphere to get the center, radius and color
@ -2609,6 +2623,35 @@ set_portal_polygon(EggGroup *egg_group, PortalNode *pnode) {
}
}
////////////////////////////////////////////////////////////////////
// Function: EggLoader::set_occluder_polygon
// Access: Private
// Description: Defines the OccluderNode from the first polygon found
// within this group.
////////////////////////////////////////////////////////////////////
void EggLoader::
set_occluder_polygon(EggGroup *egg_group, OccluderNode *pnode) {
PT(EggPolygon) poly = find_first_polygon(egg_group);
if (poly != (EggPolygon *)NULL) {
if (poly->size() != 4) {
egg2pg_cat.error()
<< "Invalid number of vertices for " << egg_group->get_name() << "\n";
} else {
LMatrix4d mat = poly->get_vertex_to_node();
EggPolygon::const_iterator vi;
LPoint3d v0 = (*poly)[0]->get_pos3() * mat;
LPoint3d v1 = (*poly)[1]->get_pos3() * mat;
LPoint3d v2 = (*poly)[2]->get_pos3() * mat;
LPoint3d v3 = (*poly)[3]->get_pos3() * mat;
pnode->set_vertices(LCAST(float, v0),
LCAST(float, v1),
LCAST(float, v2),
LCAST(float, v3));
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggLoader::find_first_polygon
// Access: Private

View File

@ -53,6 +53,7 @@ class CollisionNode;
class CollisionPlane;
class CollisionPolygon;
class PortalNode;
class OccluderNode;
class PolylightNode;
class EggRenderState;
class CharacterMaker;
@ -156,6 +157,7 @@ private:
bool has_overall_color, const Colorf &overall_color);
void set_portal_polygon(EggGroup *egg_group, PortalNode *pnode);
void set_occluder_polygon(EggGroup *egg_group, OccluderNode *pnode);
PT(EggPolygon) find_first_polygon(EggGroup *egg_group);
bool make_sphere(EggGroup *start_group, EggGroup::CollideFlags flags,

View File

@ -61,6 +61,7 @@
#include "stencilAttrib.h"
#include "lightAttrib.h"
#include "scissorAttrib.h"
#include "clipPlaneAttrib.h"
#include "graphicsEngine.h"
#include "shaderGenerator.h"

View File

@ -74,6 +74,8 @@
nodePathCollection.I nodePathCollection.h \
nodePathComponent.I nodePathComponent.h \
nodePathLerps.h \
occluderEffect.I occluderEffect.h \
occluderNode.I occluderNode.h \
pandaNode.I pandaNode.h \
pandaNodeChain.I pandaNodeChain.h \
planeNode.I planeNode.h \
@ -171,6 +173,8 @@
nodePathCollection.cxx \
nodePathComponent.cxx \
nodePathLerps.cxx \
occluderEffect.cxx \
occluderNode.cxx \
pandaNode.cxx \
pandaNodeChain.cxx \
planeNode.cxx \
@ -266,6 +270,8 @@
nodePathCollection.I nodePathCollection.h \
nodePathComponent.I nodePathComponent.h \
nodePathLerps.h \
occluderEffect.I occluderEffect.h \
occluderNode.I occluderNode.h \
pandaNode.I pandaNode.h \
pandaNodeChain.I pandaNodeChain.h \
planeNode.I planeNode.h \

View File

@ -63,6 +63,8 @@
#include "polylightEffect.h"
#include "polylightNode.h"
#include "portalNode.h"
#include "occluderEffect.h"
#include "occluderNode.h"
#include "portalClipper.h"
#include "renderAttrib.h"
#include "renderEffect.h"
@ -108,8 +110,7 @@ ConfigVariableBool clip_plane_cull
("clip-plane-cull", true,
PRC_DESC("This is normally true; set it false to disable culling of objects "
"that are completely behind one or more clip planes (primarily "
"useful for debugging)."));
"useful for debugging) This also disables the use of occluders."));
ConfigVariableBool allow_portal_cull
("allow-portal-cull", false,
@ -123,6 +124,10 @@ ConfigVariableBool debug_portal_cull
"(You first need to enable portal culling, using the allow-portal-cull"
"variable.)"));
ConfigVariableBool show_occluder_volumes
("show-occluder-volumes", false,
PRC_DESC("Set this true to enable debug visualization of the volumes used "
"to cull objects behind an occluder."));
ConfigVariableBool unambiguous_graph
("unambiguous-graph", false,
@ -454,6 +459,8 @@ init_libpgraph() {
PolylightNode::init_type();
PolylightEffect::init_type();
PortalNode::init_type();
OccluderEffect::init_type();
OccluderNode::init_type();
PortalClipper::init_type();
RenderAttrib::init_type();
RenderEffect::init_type();
@ -515,6 +522,8 @@ init_libpgraph() {
PlaneNode::register_with_read_factory();
PolylightNode::register_with_read_factory();
PortalNode::register_with_read_factory();
OccluderEffect::register_with_read_factory();
OccluderNode::register_with_read_factory();
RenderEffects::register_with_read_factory();
RenderModeAttrib::register_with_read_factory();
RenderState::register_with_read_factory();

View File

@ -34,6 +34,7 @@ extern ConfigVariableBool fake_view_frustum_cull;
extern ConfigVariableBool clip_plane_cull;
extern ConfigVariableBool allow_portal_cull;
extern ConfigVariableBool debug_portal_cull;
extern ConfigVariableBool show_occluder_volumes;
extern ConfigVariableBool unambiguous_graph;
extern ConfigVariableBool detect_graph_cycles;
extern ConfigVariableBool no_unsupported_copy;

View File

@ -29,7 +29,8 @@ CullPlanes() {
////////////////////////////////////////////////////////////////////
INLINE CullPlanes::
CullPlanes(const CullPlanes &copy) :
_planes(copy._planes)
_planes(copy._planes),
_occluders(copy._occluders)
{
}
@ -41,6 +42,7 @@ CullPlanes(const CullPlanes &copy) :
INLINE void CullPlanes::
operator = (const CullPlanes &copy) {
_planes = copy._planes;
_occluders = copy._occluders;
}
////////////////////////////////////////////////////////////////////
@ -60,5 +62,6 @@ INLINE CullPlanes::
////////////////////////////////////////////////////////////////////
INLINE bool CullPlanes::
is_empty() const {
return _planes.empty();
return _planes.empty() && _occluders.empty();
}

View File

@ -14,6 +14,8 @@
#include "cullPlanes.h"
#include "cullTraverserData.h"
#include "clipPlaneAttrib.h"
#include "occluderEffect.h"
////////////////////////////////////////////////////////////////////
@ -59,6 +61,15 @@ xform(const LMatrix4f &mat) const {
(*pi).second->xform(mat);
}
for (Occluders::iterator oi = new_planes->_occluders.begin();
oi != new_planes->_occluders.end();
++oi) {
if ((*oi).second->get_ref_count() != 1) {
(*oi).second = DCAST(BoundingHexahedron, (*oi).second->make_copy());
}
(*oi).second->xform(mat);
}
return new_planes;
}
@ -77,8 +88,9 @@ xform(const LMatrix4f &mat) const {
CPT(CullPlanes) CullPlanes::
apply_state(const CullTraverser *trav, const CullTraverserData *data,
const ClipPlaneAttrib *net_attrib,
const ClipPlaneAttrib *off_attrib) const {
if (net_attrib == (ClipPlaneAttrib *)NULL) {
const ClipPlaneAttrib *off_attrib,
const OccluderEffect *node_effect) const {
if (net_attrib == (ClipPlaneAttrib *)NULL && node_effect == (OccluderEffect *)NULL) {
return this;
}
@ -91,28 +103,164 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data,
CPT(TransformState) net_transform = NULL;
int num_on_planes = net_attrib->get_num_on_planes();
for (int i = 0; i < num_on_planes; ++i) {
NodePath clip_plane = net_attrib->get_on_plane(i);
Planes::const_iterator pi = new_planes->_planes.find(clip_plane);
if (pi == new_planes->_planes.end()) {
if (!off_attrib->has_off_plane(clip_plane)) {
// Here's a new clip plane; add it to the list. For this we
// need the net transform to this node.
if (net_transform == (TransformState *)NULL) {
net_transform = data->get_net_transform(trav);
if (net_attrib != (ClipPlaneAttrib *)NULL) {
int num_on_planes = net_attrib->get_num_on_planes();
for (int i = 0; i < num_on_planes; ++i) {
NodePath clip_plane = net_attrib->get_on_plane(i);
Planes::const_iterator pi = new_planes->_planes.find(clip_plane);
if (pi == new_planes->_planes.end()) {
if (!off_attrib->has_off_plane(clip_plane)) {
// Here's a new clip plane; add it to the list. For this we
// need the net transform to this node.
if (net_transform == (TransformState *)NULL) {
net_transform = data->get_net_transform(trav);
}
PlaneNode *plane_node = DCAST(PlaneNode, clip_plane.node());
CPT(TransformState) new_transform =
net_transform->invert_compose(clip_plane.get_net_transform());
Planef plane = plane_node->get_plane() * new_transform->get_mat();
new_planes->_planes[clip_plane] = new BoundingPlane(-plane);
}
PlaneNode *plane_node = DCAST(PlaneNode, clip_plane.node());
CPT(TransformState) new_transform =
net_transform->invert_compose(clip_plane.get_net_transform());
Planef plane = plane_node->get_plane() * new_transform->get_mat();
new_planes->_planes[clip_plane] = new BoundingPlane(-plane);
}
}
}
if (node_effect != (OccluderEffect *)NULL) {
CPT(TransformState) center_transform = NULL;
int num_on_occluders = node_effect->get_num_on_occluders();
for (int i = 0; i < num_on_occluders; ++i) {
NodePath occluder = node_effect->get_on_occluder(i);
Occluders::const_iterator oi = new_planes->_occluders.find(occluder);
if (oi == new_planes->_occluders.end()) {
// 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());
// And the transform from cull-center space into the current
// node's coordinate space.
if (center_transform == (TransformState *)NULL) {
if (net_transform == (TransformState *)NULL) {
net_transform = data->get_net_transform(trav);
}
center_transform = net_transform->invert_compose(scene->get_cull_center().get_net_transform());
}
// Compare the occluder node's bounding volume to the view
// frustum.
PT(GeometricBoundingVolume) occluder_gbv = DCAST(GeometricBoundingVolume, occluder_node->get_internal_bounds()->make_copy());
{
CPT(TransformState) composed_transform = occluder_transform->compose(center_transform);
occluder_gbv->xform(composed_transform->get_mat());
}
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.
continue;
}
// Also check if the new occluder is completely within any of
// our existing occluder volumes.
bool is_enclosed = false;
Occluders::const_iterator oi;
for (oi = _occluders.begin(); oi != _occluders.end(); ++oi) {
int occluder_result = (*oi).second->contains(occluder_gbv);
if ((occluder_result & BoundingVolume::IF_all) != 0) {
is_enclosed = true;
break;
}
}
if (is_enclosed) {
// No reason to add this occluder; it's behind an existing
// occluder.
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();
Vertexf 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) {
// This occluder is facing the wrong direction. Ignore it.
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.
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.
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 polygon 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.
continue;
}
// Project those four lines to the camera's far plane.
float far_clip = scene->get_lens()->get_far();
Planef far_plane(-LVector3f::forward(), LVector3f::forward() * far_clip);
LPoint3f points_far[4];
far_plane.intersects_line(points_far[0], LPoint3f::zero(), points_near[0]);
far_plane.intersects_line(points_far[1], LPoint3f::zero(), points_near[1]);
far_plane.intersects_line(points_far[2], LPoint3f::zero(), points_near[2]);
far_plane.intersects_line(points_far[3], LPoint3f::zero(), points_near[3]);
// With these points, construct the bounding frustum of the
// occluded region.
PT(BoundingHexahedron) frustum =
new BoundingHexahedron(points_far[1], points_far[2], points_far[3], points_far[0],
points_near[1], points_near[2], points_near[3], points_near[0]);
frustum->xform(center_transform->get_mat());
new_planes->_occluders[occluder] = frustum;
if (show_occluder_volumes) {
// Draw the frustum for visualization.
nassertr(net_transform != NULL, new_planes);
trav->draw_bounding_volume(frustum, net_transform,
data->get_modelview_transform(trav));
}
}
}
}
return new_planes;
}
@ -137,45 +285,74 @@ do_cull(int &result, CPT(RenderState) &state,
BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
CPT(ClipPlaneAttrib) orig_cpa = DCAST(ClipPlaneAttrib, state->get_attrib(ClipPlaneAttrib::get_class_slot()));
// If there are no clip planes in the state, the node is completely
// in front of all zero of the clip planes. (This can happen if
// someone directly changes the state during the traversal.)
if (orig_cpa == (ClipPlaneAttrib *)NULL) {
return new CullPlanes;
}
CPT(CullPlanes) new_planes = this;
CPT(ClipPlaneAttrib) new_cpa = orig_cpa;
if (orig_cpa == (ClipPlaneAttrib *)NULL) {
// If there are no clip planes in the state, the node is completely
// in front of all zero of the clip planes. (This can happen if
// someone directly changes the state during the traversal.)
CullPlanes *planes = new CullPlanes;
planes->_occluders = _occluders;
new_planes = planes;
Planes::const_iterator pi;
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
int plane_result = (*pi).second->contains(node_gbv);
if (plane_result == BoundingVolume::IF_no_intersection) {
// The node is completely behind this clip plane. Short-circuit
// the rest of the logic; none of the other planes matter.
result = plane_result;
} else {
CPT(ClipPlaneAttrib) new_cpa = orig_cpa;
Planes::const_iterator pi;
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
int plane_result = (*pi).second->contains(node_gbv);
if (plane_result == BoundingVolume::IF_no_intersection) {
// The node is completely behind this clip plane and gets
// culled. Short-circuit the rest of the logic; none of the
// other planes matter.
result = plane_result;
return new_planes;
} else if ((plane_result & BoundingVolume::IF_all) != 0) {
// The node is completely in front of this clip plane. We don't
// need to consider this plane ever again for any descendents of
// this node.
new_planes = new_planes->remove_plane((*pi).first);
nassertr(new_planes != this, new_planes);
new_cpa = DCAST(ClipPlaneAttrib, new_cpa->remove_on_plane((*pi).first));
}
result &= plane_result;
}
if (new_cpa != orig_cpa) {
if (new_cpa->is_identity()) {
state = state->remove_attrib(ClipPlaneAttrib::get_class_slot());
} else {
state = state->add_attrib(new_cpa);
}
}
}
Occluders::const_iterator oi;
for (oi = _occluders.begin(); oi != _occluders.end(); ++oi) {
int occluder_result = (*oi).second->contains(node_gbv);
if (occluder_result == BoundingVolume::IF_no_intersection) {
// The node is completely in front of this occluder. We don't
// need to consider this occluder ever again for any descendents of
// this node.
// Reverse the sense of the test, because an occluder volume is
// the inverse of a cull plane volume: it describes the volume
// that is to be culled, not the volume that is to be kept.
occluder_result = BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
new_planes = new_planes->remove_occluder((*oi).first);
nassertr(new_planes != this, new_planes);
} else if ((occluder_result & BoundingVolume::IF_all) != 0) {
// The node is completely behind this occluder and gets culled.
// Short-circuit the rest of the logic; none of the other
// occluders matter.
result = BoundingVolume::IF_no_intersection;
return new_planes;
}
if ((plane_result & BoundingVolume::IF_all) != 0) {
// The node is completely in front of this clip plane. We don't
// need to consider this plane ever again for any descendents of
// this node.
new_planes = new_planes->remove_plane((*pi).first);
nassertr(new_planes != this, new_planes);
new_cpa = DCAST(ClipPlaneAttrib, new_cpa->remove_on_plane((*pi).first));
}
result &= plane_result;
}
if (new_cpa != orig_cpa) {
if (new_cpa->is_identity()) {
state = state->remove_attrib(ClipPlaneAttrib::get_class_slot());
} else {
state = state->add_attrib(new_cpa);
}
result &= occluder_result;
}
return new_planes;
@ -204,6 +381,29 @@ remove_plane(const NodePath &clip_plane) const {
return new_planes;
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::remove_occluder
// Access: Public
// Description: Returns a pointer to a new CullPlanes object that is
// the same as this one, but with the indicated
// occluder removed.
////////////////////////////////////////////////////////////////////
CPT(CullPlanes) CullPlanes::
remove_occluder(const NodePath &occluder) const {
PT(CullPlanes) new_planes;
if (get_ref_count() == 1) {
new_planes = (CullPlanes *)this;
} else {
new_planes = new CullPlanes(*this);
}
Occluders::iterator pi = new_planes->_occluders.find(occluder);
nassertr(pi != new_planes->_occluders.end(), new_planes);
new_planes->_occluders.erase(pi);
return new_planes;
}
////////////////////////////////////////////////////////////////////
// Function: CullPlanes::write
// Access: Public
@ -211,9 +411,15 @@ remove_plane(const NodePath &clip_plane) const {
////////////////////////////////////////////////////////////////////
void CullPlanes::
write(ostream &out) const {
out << "CullPlanes (" << _planes.size() << " planes):\n";
out << "CullPlanes (" << _planes.size() << " planes and "
<< _occluders.size() << " occluders):\n";
Planes::const_iterator pi;
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
out << " " << (*pi).first << " : " << *(*pi).second << "\n";
}
Occluders::const_iterator oi;
for (oi = _occluders.begin(); oi != _occluders.end(); ++oi) {
out << " " << (*oi).first << " : " << *(*oi).second << "\n";
}
}

View File

@ -18,19 +18,22 @@
#include "pandabase.h"
#include "referenceCount.h"
#include "nodePath.h"
#include "clipPlaneAttrib.h"
#include "boundingHexahedron.h"
#include "boundingPlane.h"
#include "pointerTo.h"
#include "luse.h"
#include "deletedChain.h"
class ClipPlaneAttrib;
class OccluderEffect;
////////////////////////////////////////////////////////////////////
// Class : CullPlanes
// Description : This represents the set of clip planes that are
// definitely in effect for the current node of the
// CullTraverserData, as well as on all child nodes.
// Any clip planes in this list may be safely culled
// against.
// Description : This represents the set of clip planes and/or
// occluders that are definitely in effect for the
// current node of the CullTraverserData, as well as on
// all child nodes. Any clip planes and occluders in
// this list may be safely culled against.
//
// This does not include the clip planes that are in
// effect now, but might later be turned off by a child
@ -57,17 +60,22 @@ public:
CPT(CullPlanes) apply_state(const CullTraverser *trav,
const CullTraverserData *data,
const ClipPlaneAttrib *net_attrib,
const ClipPlaneAttrib *off_attrib) const;
const ClipPlaneAttrib *off_attrib,
const OccluderEffect *node_effect) const;
CPT(CullPlanes) do_cull(int &result, CPT(RenderState) &state,
const GeometricBoundingVolume *node_gbv) const;
CPT(CullPlanes) remove_plane(const NodePath &clip_plane) const;
CPT(CullPlanes) remove_occluder(const NodePath &occluder) const;
void write(ostream &out) const;
private:
typedef pmap<NodePath, PT(BoundingPlane) > Planes;
Planes _planes;
typedef pmap<NodePath, PT(BoundingHexahedron) > Occluders;
Occluders _occluders;
};
#include "cullPlanes.I"

View File

@ -34,6 +34,7 @@
#include "geomTristrips.h"
#include "geomTriangles.h"
#include "geomLinestrips.h"
#include "geomLines.h"
#include "geomVertexWriter.h"
PStatCollector CullTraverser::_nodes_pcollector("Nodes");
@ -224,7 +225,6 @@ traverse(CullTraverserData &data) {
return;
}
}
traverse_below(data);
}
}
@ -313,7 +313,7 @@ end_traverse() {
void CullTraverser::
draw_bounding_volume(const BoundingVolume *vol,
const TransformState *net_transform,
const TransformState *modelview_transform) {
const TransformState *modelview_transform) const {
PT(Geom) bounds_viz = make_bounds_viz(vol);
if (bounds_viz != (Geom *)NULL) {
@ -387,7 +387,7 @@ show_bounds(CullTraverserData &data, bool tight) {
////////////////////////////////////////////////////////////////////
// Function: CullTraverser::make_bounds_viz
// Access: Private
// Access: Private, Static
// Description: Returns an appropriate visualization of the indicated
// bounding volume.
////////////////////////////////////////////////////////////////////
@ -427,6 +427,37 @@ make_bounds_viz(const BoundingVolume *vol) {
geom = new Geom(vdata);
geom->add_primitive(strip);
} else if (vol->is_of_type(BoundingHexahedron::get_class_type())) {
const BoundingHexahedron *fvol = DCAST(BoundingHexahedron, vol);
PT(GeomVertexData) vdata = new GeomVertexData
("bounds", GeomVertexFormat::get_v3(),
Geom::UH_stream);
GeomVertexWriter vertex(vdata, InternalName::get_vertex());
for (int i = 0; i < 8; ++i ) {
vertex.add_data3f(fvol->get_point(i));
}
PT(GeomLines) lines = new GeomLines(Geom::UH_stream);
lines->add_vertices(0, 1); lines->close_primitive();
lines->add_vertices(1, 2); lines->close_primitive();
lines->add_vertices(2, 3); lines->close_primitive();
lines->add_vertices(3, 0); lines->close_primitive();
lines->add_vertices(4, 5); lines->close_primitive();
lines->add_vertices(5, 6); lines->close_primitive();
lines->add_vertices(6, 7); lines->close_primitive();
lines->add_vertices(7, 4); lines->close_primitive();
lines->add_vertices(0, 4); lines->close_primitive();
lines->add_vertices(1, 5); lines->close_primitive();
lines->add_vertices(2, 6); lines->close_primitive();
lines->add_vertices(3, 7); lines->close_primitive();
geom = new Geom(vdata);
geom->add_primitive(lines);
} else if (vol->is_of_type(FiniteBoundingVolume::get_class_type())) {
const FiniteBoundingVolume *fvol = DCAST(FiniteBoundingVolume, vol);
@ -487,7 +518,7 @@ make_bounds_viz(const BoundingVolume *vol) {
// node's "tight" bounding volume.
////////////////////////////////////////////////////////////////////
PT(Geom) CullTraverser::
make_tight_bounds_viz(PandaNode *node) {
make_tight_bounds_viz(PandaNode *node) const {
PT(Geom) geom;
NodePath np = NodePath::any_path(node);

View File

@ -89,7 +89,7 @@ PUBLISHED:
void draw_bounding_volume(const BoundingVolume *vol,
const TransformState *net_transform,
const TransformState *modelview_transform);
const TransformState *modelview_transform) const;
protected:
virtual bool is_in_view(CullTraverserData &data);
@ -103,13 +103,13 @@ public:
private:
void show_bounds(CullTraverserData &data, bool tight);
PT(Geom) make_bounds_viz(const BoundingVolume *vol);
PT(Geom) make_tight_bounds_viz(PandaNode *node);
static PT(Geom) make_bounds_viz(const BoundingVolume *vol);
PT(Geom) make_tight_bounds_viz(PandaNode *node) const;
static Vertexf compute_point(const BoundingSphere *sphere,
float latitude, float longitude);
CPT(RenderState) get_bounds_outer_viz_state();
CPT(RenderState) get_bounds_inner_viz_state();
CPT(RenderState) get_depth_offset_state();
static CPT(RenderState) get_bounds_outer_viz_state();
static CPT(RenderState) get_bounds_inner_viz_state();
static CPT(RenderState) get_depth_offset_state();
void start_decal(const CullTraverserData &data);
CullableObject *r_get_decals(CullTraverserData &data,
CullableObject *decals);

View File

@ -167,7 +167,7 @@ is_in_view(const DrawMask &camera_mask) {
if (_view_frustum == (GeometricBoundingVolume *)NULL &&
_cull_planes->is_empty()) {
// If the transform is valid, but we don't have a frustum or any
// clip planes, it's always in.
// clip planes or occluders, it's always in.
return true;
}

View File

@ -117,7 +117,8 @@ apply_transform_and_state(CullTraverser *trav,
if (clip_plane_cull) {
_cull_planes = _cull_planes->apply_state(trav, this,
DCAST(ClipPlaneAttrib, node_state->get_attrib(ClipPlaneAttrib::get_class_slot())),
DCAST(ClipPlaneAttrib, off_clip_planes));
DCAST(ClipPlaneAttrib, off_clip_planes),
DCAST(OccluderEffect, node_effects->get_effect(OccluderEffect::get_class_type())));
}
}
@ -198,12 +199,17 @@ is_in_view_impl() {
if (result == BoundingVolume::IF_no_intersection) {
// No intersection at all. Cull.
return false;
if (!fake_view_frustum_cull) {
return false;
}
_cull_planes = CullPlanes::make_empty();
CPT(RenderState) fake_state = get_fake_view_frustum_cull_state();
_state = _state->compose(fake_state);
} else if ((result & BoundingVolume::IF_all) != 0) {
// The node and its descendents are completely in front of all
// of the clip planes. The do_cull() call should therefore have
// removed all of the clip planes.
// of the clip planes and occluders. The do_cull() call should
// therefore have removed all of the clip planes and occluders.
nassertr(_cull_planes->is_empty(), true);
}
}

View File

@ -24,6 +24,7 @@
#include "pointerTo.h"
#include "drawMask.h"
#include "cullTraverser.h"
#include "pvector.h"
class PandaNode;
@ -87,7 +88,6 @@ public:
private:
bool is_in_view_impl();
static CPT(RenderState) get_fake_view_frustum_cull_state();
};

View File

@ -31,6 +31,7 @@
#include "materialCollection.h"
#include "lightAttrib.h"
#include "clipPlaneAttrib.h"
#include "occluderEffect.h"
#include "polylightEffect.h"
#include "fogAttrib.h"
#include "renderModeAttrib.h"
@ -50,6 +51,7 @@
#include "scissorEffect.h"
#include "texturePool.h"
#include "planeNode.h"
#include "occluderNode.h"
#include "lensNode.h"
#include "materialPool.h"
#include "look_at.h"
@ -3186,6 +3188,105 @@ has_clip_plane_off(const NodePath &clip_plane) const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: NodePath::set_occluder
// Access: Published
// Description: Adds the indicated occluder to the list of
// occluders that apply to geometry at this node and below.
// The occluder itself, an OccluderNode, should be
// parented into the scene graph elsewhere, to represent
// the occluder's position in space; but until
// set_occluder() is called it will clip no geometry.
////////////////////////////////////////////////////////////////////
void NodePath::
set_occluder(const NodePath &occluder) {
nassertv_always(!is_empty());
if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) {
const RenderEffect *effect =
node()->get_effect(OccluderEffect::get_class_type());
if (effect != (const RenderEffect *)NULL) {
const OccluderEffect *la = DCAST(OccluderEffect, effect);
// Modify the existing OccluderEffect to add the indicated
// occluder.
node()->set_effect(la->add_on_occluder(occluder));
} else {
// Create a new OccluderEffect for this node.
CPT(OccluderEffect) la = DCAST(OccluderEffect, OccluderEffect::make());
node()->set_effect(la->add_on_occluder(occluder));
}
return;
}
nassert_raise("Not an OccluderNode object.");
}
////////////////////////////////////////////////////////////////////
// Function: NodePath::clear_occluder
// Access: Published
// Description: Completely removes any occluders that may have been
// set via set_occluder() from this particular node.
////////////////////////////////////////////////////////////////////
void NodePath::
clear_occluder() {
nassertv_always(!is_empty());
node()->clear_effect(OccluderEffect::get_class_type());
}
////////////////////////////////////////////////////////////////////
// Function: NodePath::clear_occluder
// Access: Published
// Description: Removes any reference to the indicated occluder
// from the NodePath.
////////////////////////////////////////////////////////////////////
void NodePath::
clear_occluder(const NodePath &occluder) {
nassertv_always(!is_empty());
if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) {
const RenderEffect *effect =
node()->get_effect(OccluderEffect::get_class_type());
if (effect != (const RenderEffect *)NULL) {
CPT(OccluderEffect) la = DCAST(OccluderEffect, effect);
la = DCAST(OccluderEffect, la->remove_on_occluder(occluder));
if (la->is_identity()) {
node()->clear_effect(OccluderEffect::get_class_type());
} else {
node()->set_effect(la);
}
}
return;
}
nassert_raise("Not an OccluderNode object.");
}
////////////////////////////////////////////////////////////////////
// Function: NodePath::has_occluder
// Access: Published
// Description: Returns true if the indicated occluder has been
// specifically applied to this particular node. This
// means that someone called set_occluder() on this
// node with the indicated occluder.
////////////////////////////////////////////////////////////////////
bool NodePath::
has_occluder(const NodePath &occluder) const {
nassertr_always(!is_empty(), false);
if (!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type())) {
const RenderEffect *effect =
node()->get_effect(OccluderEffect::get_class_type());
if (effect != (const RenderEffect *)NULL) {
const OccluderEffect *la = DCAST(OccluderEffect, effect);
return la->has_on_occluder(occluder);
}
return false;
}
nassert_raise("Not an OccluderNode object.");
return false;
}
////////////////////////////////////////////////////////////////////
// Function: NodePath::set_scissor
// Access: Published

View File

@ -583,6 +583,11 @@ PUBLISHED:
void clear_scissor();
bool has_scissor() const;
void set_occluder(const NodePath &occluder);
void clear_occluder();
void clear_occluder(const NodePath &occluder);
bool has_occluder(const NodePath &occluder) const;
void set_bin(const string &bin_name, int draw_order, int priority = 0);
void clear_bin();
bool has_bin() const;

View File

@ -0,0 +1,82 @@
// Filename: occluderEffect.I
// Created by: drose (17Mar11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::Constructor
// Access: Protected
// Description: Use OccluderEffect::make() to construct a new
// OccluderEffect object.
////////////////////////////////////////////////////////////////////
INLINE OccluderEffect::
OccluderEffect() {
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::Copy Constructor
// Access: Protected
// Description: Use OccluderEffect::make() to construct a new
// OccluderEffect object. The copy constructor is only
// defined to facilitate methods like add_on_occluder().
////////////////////////////////////////////////////////////////////
INLINE OccluderEffect::
OccluderEffect(const OccluderEffect &copy) :
_on_occluders(copy._on_occluders)
{
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::get_num_on_occluders
// Access: Published
// Description: Returns the number of occluders that are enabled by
// the effectute.
////////////////////////////////////////////////////////////////////
INLINE int OccluderEffect::
get_num_on_occluders() const {
return _on_occluders.size();
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::get_on_occluder
// Access: Published
// Description: Returns the nth occluder enabled by the effectute,
// sorted in render order.
////////////////////////////////////////////////////////////////////
INLINE NodePath OccluderEffect::
get_on_occluder(int n) const {
nassertr(n >= 0 && n < (int)_on_occluders.size(), NodePath::fail());
return _on_occluders[n];
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::has_on_occluder
// Access: Published
// Description: Returns true if the indicated occluder is enabled by
// the effect, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool OccluderEffect::
has_on_occluder(const NodePath &occluder) const {
return _on_occluders.find(occluder) != _on_occluders.end();
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::is_identity
// Access: Published
// Description: Returns true if this is an identity effect: it does
// not change the set of occluders in use.
////////////////////////////////////////////////////////////////////
INLINE bool OccluderEffect::
is_identity() const {
return _on_occluders.empty();
}

View File

@ -0,0 +1,262 @@
// Filename: occluderEffect.cxx
// Created by: drose (17Mar11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "occluderEffect.h"
#include "pandaNode.h"
#include "graphicsStateGuardianBase.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "config_pgraph.h"
#include "attribNodeRegistry.h"
CPT(RenderEffect) OccluderEffect::_empty_effect;
TypeHandle OccluderEffect::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::make
// Access: Published, Static
// Description: Constructs a new OccluderEffect object that does
// nothing.
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) OccluderEffect::
make() {
// We make it a special case and store a pointer to the empty effect
// forever once we find it the first time, as an optimization.
if (_empty_effect == (RenderEffect *)NULL) {
_empty_effect = return_new(new OccluderEffect);
}
return _empty_effect;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::add_on_occluder
// Access: Published
// Description: Returns a new OccluderEffect, just like this one, but
// with the indicated occluder added to the list of occluders
// enabled by this effect.
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) OccluderEffect::
add_on_occluder(const NodePath &occluder) const {
nassertr(!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type()), this);
OccluderEffect *effect = new OccluderEffect(*this);
effect->_on_occluders.insert(occluder);
pair<Occluders::iterator, bool> insert_result =
effect->_on_occluders.insert(Occluders::value_type(occluder));
return return_new(effect);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::remove_on_occluder
// Access: Published
// Description: Returns a new OccluderEffect, just like this one, but
// with the indicated occluder removed from the list of
// occluders enabled by this effect.
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) OccluderEffect::
remove_on_occluder(const NodePath &occluder) const {
nassertr(!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type()), this);
OccluderEffect *effect = new OccluderEffect(*this);
effect->_on_occluders.erase(occluder);
return return_new(effect);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void OccluderEffect::
output(ostream &out) const {
out << get_type() << ":";
if (_on_occluders.empty()) {
out << "identity";
} else {
out << "on";
Occluders::const_iterator li;
for (li = _on_occluders.begin(); li != _on_occluders.end(); ++li) {
NodePath occluder = (*li);
out << " " << occluder;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::compare_to_impl
// Access: Protected, Virtual
// Description: Intended to be overridden by derived OccluderEffect
// types to return a unique number indicating whether
// this OccluderEffect is equivalent to the other one.
//
// This should return 0 if the two OccluderEffect
// objects are equivalent, a number less than zero if
// this one should be sorted before the other one, and a
// number greater than zero otherwise.
//
// This will only be called with two OccluderEffect
// objects whose get_type() functions return the same.
////////////////////////////////////////////////////////////////////
int OccluderEffect::
compare_to_impl(const RenderEffect *other) const {
const OccluderEffect *ta;
DCAST_INTO_R(ta, other, 0);
Occluders::const_iterator li = _on_occluders.begin();
Occluders::const_iterator oli = ta->_on_occluders.begin();
while (li != _on_occluders.end() && oli != ta->_on_occluders.end()) {
NodePath occluder = (*li);
NodePath other_occluder = (*oli);
int compare = occluder.compare_to(other_occluder);
if (compare != 0) {
return compare;
}
++li;
++oli;
}
if (li != _on_occluders.end()) {
return 1;
}
if (oli != ta->_on_occluders.end()) {
return -1;
}
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// OccluderEffect.
////////////////////////////////////////////////////////////////////
void OccluderEffect::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void OccluderEffect::
write_datagram(BamWriter *manager, Datagram &dg) {
RenderEffect::write_datagram(manager, dg);
// write the number of on occluders
dg.add_uint16(get_num_on_occluders());
// write the on occluders pointers if any
Occluders::const_iterator nti;
for (nti = _on_occluders.begin(); nti != _on_occluders.end(); ++nti) {
NodePath occluder = (*nti);
manager->write_pointer(dg, occluder.node());
}
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::complete_pointers
// Access: Public, Virtual
// Description: Receives an array of pointers, one for each time
// manager->read_pointer() was called in fillin().
// Returns the number of pointers processed.
////////////////////////////////////////////////////////////////////
int OccluderEffect::
complete_pointers(TypedWritable **p_list, BamReader *manager) {
int pi = RenderEffect::complete_pointers(p_list, manager);
AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
Occluders::iterator ci;
ci = _on_occluders.begin();
while (ci != _on_occluders.end()) {
PandaNode *node;
DCAST_INTO_R(node, p_list[pi++], pi);
int ni = areg->find_node(node->get_type(), node->get_name());
if (ni != -1) {
(*ci) = areg->get_node(ni);
} else {
(*ci) = NodePath(node);
}
++ci;
}
_on_occluders.sort();
return pi;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::require_fully_complete
// Access: Public, Virtual
// Description: Some objects require all of their nested pointers to
// have been completed before the objects themselves can
// be completed. If this is the case, override this
// method to return true, and be careful with circular
// references (which would make the object unreadable
// from a bam file).
////////////////////////////////////////////////////////////////////
bool OccluderEffect::
require_fully_complete() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type OccluderEffect is encountered
// in the Bam file. It should create the OccluderEffect
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *OccluderEffect::
make_from_bam(const FactoryParams &params) {
OccluderEffect *effect = new OccluderEffect;
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
effect->fillin(scan, manager);
return effect;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderEffect::fillin
// Access: Protected
// Description: This internal function is called by make_from_bam to
// read in all of the relevant data from the BamFile for
// the new OccluderEffect.
////////////////////////////////////////////////////////////////////
void OccluderEffect::
fillin(DatagramIterator &scan, BamReader *manager) {
RenderEffect::fillin(scan, manager);
// Push back an empty NodePath for each Occluder for now, until we
// get the actual list of pointers later in complete_pointers().
int num_on_occluders = scan.get_uint16();
_on_occluders.reserve(num_on_occluders);
for (int i = 0; i < num_on_occluders; i++) {
manager->read_pointer(scan);
_on_occluders.push_back(NodePath());
}
}

102
panda/src/pgraph/occluderEffect.h Executable file
View File

@ -0,0 +1,102 @@
// Filename: occluderEffect.h
// Created by: drose (17Mar11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef OCCLUDEREFFECT_H
#define OCCLUDEREFFECT_H
#include "pandabase.h"
#include "occluderNode.h"
#include "renderEffect.h"
#include "nodePath.h"
#include "ordered_vector.h"
#include "pmap.h"
////////////////////////////////////////////////////////////////////
// Class : OccluderEffect
// Description : This functions similarly to a LightAttrib or
// ClipPlaneAttrib. It indicates the set of occluders
// that modify the geometry at this level and below.
// Unlike a ClipPlaneAttrib, an OccluderEffect takes
// effect immediately when it is encountered during
// traversal, and thus can only add occluders; it may
// not remove them.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_PGRAPH OccluderEffect : public RenderEffect {
private:
INLINE OccluderEffect();
INLINE OccluderEffect(const OccluderEffect &copy);
PUBLISHED:
static CPT(RenderEffect) make();
INLINE int get_num_on_occluders() const;
INLINE NodePath get_on_occluder(int n) const;
MAKE_SEQ(get_on_occluders, get_num_on_occluders, get_on_occluder);
INLINE bool has_on_occluder(const NodePath &occluder) const;
INLINE bool is_identity() const;
CPT(RenderEffect) add_on_occluder(const NodePath &occluder) const;
CPT(RenderEffect) remove_on_occluder(const NodePath &occluder) const;
public:
virtual void output(ostream &out) const;
protected:
virtual int compare_to_impl(const RenderEffect *other) const;
private:
void sort_on_occluders();
private:
typedef ov_set<NodePath> Occluders;
Occluders _on_occluders;
UpdateSeq _sort_seq;
static CPT(RenderEffect) _empty_effect;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual bool require_fully_complete() const;
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
RenderEffect::init_type();
register_type(_type_handle, "OccluderEffect",
RenderEffect::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "occluderEffect.I"
#endif

View File

@ -0,0 +1,54 @@
// Filename: occluderNode.I
// Created by: jenes (11Mar11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::set_vertices
// Access: Published
// Description: Replaces the four vertices of the occluder polygon.
// The vertices should be defined in a counterclockwise
// orientation when looking at the face of the occluder.
////////////////////////////////////////////////////////////////////
INLINE void OccluderNode::
set_vertices(const LPoint3f &v0, const LPoint3f &v1,
const LPoint3f &v2, const LPoint3f &v3) {
_vertices.clear();
_vertices.reserve(4);
_vertices.push_back(v0);
_vertices.push_back(v1);
_vertices.push_back(v2);
_vertices.push_back(v3);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::get_num_vertices
// Access: Published
// Description: Returns the number of vertices in the occluder
// polygon. This should always return 4.
////////////////////////////////////////////////////////////////////
INLINE int OccluderNode::
get_num_vertices() const {
return _vertices.size();
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::get_vertex
// Access: Published
// Description: Returns the nth vertex of the occluder polygon.
////////////////////////////////////////////////////////////////////
INLINE const LPoint3f &OccluderNode::
get_vertex(int n) const {
nassertr(n >= 0 && n < (int)_vertices.size(), LPoint3f::zero());
return _vertices[n];
}

View File

@ -0,0 +1,433 @@
// Filename: occluderNode.cxx
// Created by: jenes (11Mar11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "occluderNode.h"
#include "geomNode.h"
#include "cullTraverserData.h"
#include "cullTraverser.h"
#include "renderState.h"
#include "plane.h"
#include "pnmImage.h"
#include "textureAttrib.h"
#include "colorAttrib.h"
#include "depthOffsetAttrib.h"
#include "cullFaceAttrib.h"
#include "transparencyAttrib.h"
#include "transformState.h"
#include "cullableObject.h"
#include "cullHandler.h"
#include "boundingSphere.h"
#include "geomVertexData.h"
#include "geomTriangles.h"
#include "geomLinestrips.h"
#include "geomVertexWriter.h"
#include "geom.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "plane.h"
TypeHandle OccluderNode::_type_handle;
PT(Texture) OccluderNode::_viz_tex;
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::Constructor
// Access: Public
// Description: The default constructor creates a default occlusion
// polygon in the XZ plane (or XY plane in a y-up
// coordinate system). Use the normal Panda set_pos(),
// set_hpr(), set_scale() to position it appropriately,
// or replace the vertices with set_vertices().
////////////////////////////////////////////////////////////////////
OccluderNode::
OccluderNode(const string &name) :
PandaNode(name)
{
set_cull_callback();
// OccluderNodes are hidden by default.
set_overall_hidden(true);
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));
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::Copy Constructor
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
OccluderNode::
OccluderNode(const OccluderNode &copy) :
PandaNode(copy),
_vertices(copy._vertices)
{
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
OccluderNode::
~OccluderNode() {
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::make_copy
// Access: Public, Virtual
// Description: Returns a newly-allocated Node that is a shallow copy
// of this one. It will be a different Node pointer,
// but its internal data may or may not be shared with
// that of the original Node.
////////////////////////////////////////////////////////////////////
PandaNode *OccluderNode::
make_copy() const {
return new OccluderNode(*this);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::preserve_name
// Access: Public, Virtual
// Description: Returns true if the node's name has extrinsic meaning
// and must be preserved across a flatten operation,
// false otherwise.
////////////////////////////////////////////////////////////////////
bool OccluderNode::
preserve_name() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::xform
// Access: Public, Virtual
// Description: Transforms the contents of this node by the indicated
// matrix, if it means anything to do so. For most
// kinds of nodes, this does nothing.
////////////////////////////////////////////////////////////////////
void OccluderNode::
xform(const LMatrix4f &mat) {
nassertv(!mat.is_nan());
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::cull_callback
// Access: Public, Virtual
// Description: This function will be called during the cull
// traversal to perform any additional operations that
// should be performed at cull time. This may include
// additional manipulation of render state or additional
// visible/invisible decisions, or any other arbitrary
// operation.
//
// Note that this function will *not* be called unless
// set_cull_callback() is called in the constructor of
// the derived class. It is necessary to call
// set_cull_callback() to indicated that we require
// cull_callback() to be called.
//
// By the time this function is called, the node has
// already passed the bounding-volume test for the
// viewing frustum, and the node's transform and state
// have already been applied to the indicated
// CullTraverserData object.
//
// The return value is true if this node should be
// visible, or false if it should be culled.
////////////////////////////////////////////////////////////////////
bool OccluderNode::
cull_callback(CullTraverser *trav, CullTraverserData &data) {
// Normally, an OccluderNode is invisible. But if someone shows it,
// we will draw a visualization, a checkerboard-textured polygon.
CullableObject *occluder_viz =
new CullableObject(get_occluder_viz(trav, data), get_occluder_viz_state(trav, data),
data.get_net_transform(trav),
data.get_modelview_transform(trav),
trav->get_gsg());
trav->get_cull_handler()->record_object(occluder_viz, trav);
// Also get the frame.
nassertr(_frame_viz != (Geom *)NULL, false);
CullableObject *frame_viz =
new CullableObject(_frame_viz, get_frame_viz_state(trav, data),
data.get_net_transform(trav),
data.get_modelview_transform(trav),
trav->get_gsg());
trav->get_cull_handler()->record_object(frame_viz, trav);
// Now carry on to render our child nodes.
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::is_renderable
// Access: Public, Virtual
// Description: Returns true if there is some value to visiting this
// particular node during the cull traversal for any
// camera, false otherwise. This will be used to
// optimize the result of get_net_draw_show_mask(), so
// that any subtrees that contain only nodes for which
// is_renderable() is false need not be visited.
////////////////////////////////////////////////////////////////////
bool OccluderNode::
is_renderable() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::output
// Access: Public, Virtual
// Description: Writes a brief description of the node to the
// indicated output stream. This is invoked by the <<
// operator. It may be overridden in derived classes to
// include some information relevant to the class.
////////////////////////////////////////////////////////////////////
void OccluderNode::
output(ostream &out) const {
PandaNode::output(out);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::compute_internal_bounds
// Access: Protected, Virtual
// Description: Called when needed to recompute the node's
// _internal_bound object. Nodes that contain anything
// of substance should redefine this to do the right
// thing.
////////////////////////////////////////////////////////////////////
void OccluderNode::
compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
int &internal_vertices,
int pipeline_stage,
Thread *current_thread) const {
// First, get ourselves a fresh, empty bounding volume.
PT(BoundingVolume) bound = new BoundingSphere;
GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
// Now actually compute the bounding volume by putting it around all
// of our vertices.
if (!_vertices.empty()) {
const LPoint3f *vertices_begin = &_vertices[0];
const LPoint3f *vertices_end = vertices_begin + _vertices.size();
gbv->around(vertices_begin, vertices_end);
}
internal_bounds = bound;
internal_vertices = 0;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::get_occluder_viz
// Access: Protected
// Description: Returns a Geom that represents the visualization of
// the OccluderNode, as seen from the front.
////////////////////////////////////////////////////////////////////
PT(Geom) OccluderNode::
get_occluder_viz(CullTraverser *trav, CullTraverserData &data) {
if (_occluder_viz == (Geom *)NULL) {
nassertr(_vertices.size() == 4, NULL);
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
<< "Recomputing viz for " << *this << "\n";
}
PT(GeomVertexData) vdata = new GeomVertexData
(get_name(), GeomVertexFormat::get_v3n3t2(), Geom::UH_static);
// Compute the polygon normal from the first three vertices.
Planef plane(_vertices[0], _vertices[1], _vertices[2]);
LVector3f poly_normal = plane.get_normal();
GeomVertexWriter vertex(vdata, InternalName::get_vertex());
GeomVertexWriter normal(vdata, InternalName::get_normal());
GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
vertex.add_data3f(_vertices[0]);
normal.add_data3f(poly_normal);
texcoord.add_data2f(0.0, 0.0);
vertex.add_data3f(_vertices[1]);
normal.add_data3f(poly_normal);
texcoord.add_data2f(8.0, 0.0);
vertex.add_data3f(_vertices[2]);
normal.add_data3f(poly_normal);
texcoord.add_data2f(8.0, 8.0);
vertex.add_data3f(_vertices[3]);
normal.add_data3f(poly_normal);
texcoord.add_data2f(0.0, 8.0);
PT(GeomTriangles) triangles = new GeomTriangles(Geom::UH_static);
triangles->add_vertices(0, 1, 2);
triangles->close_primitive();
triangles->add_vertices(0, 2, 3);
triangles->close_primitive();
_occluder_viz = new Geom(vdata);
_occluder_viz->add_primitive(triangles);
PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static);
lines->add_vertices(0, 1, 2, 3);
lines->add_vertex(0);
lines->close_primitive();
_frame_viz = new Geom(vdata);
_frame_viz->add_primitive(lines);
}
return _occluder_viz;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::get_occluder_viz_state
// Access: Protected
// Description: Returns the RenderState to apply to the visualization.
////////////////////////////////////////////////////////////////////
CPT(RenderState) OccluderNode::
get_occluder_viz_state(CullTraverser *trav, CullTraverserData &data) {
if (_viz_tex == NULL) {
// Create a default texture. We set it up as a 2x2 graytone
// checkerboard, since that's real easy, and it doesn't look like
// a CollisionPolygon.
_viz_tex = new Texture("occluder_viz");
_viz_tex->setup_2d_texture(2, 2, Texture::T_unsigned_byte, Texture::F_luminance);
PTA_uchar image;
image.set_data("\x20\x80\x80\x20");
_viz_tex->set_ram_image(image);
_viz_tex->set_minfilter(Texture::FT_nearest);
_viz_tex->set_magfilter(Texture::FT_nearest);
}
static CPT(RenderState) viz_state;
if (viz_state == NULL) {
viz_state = RenderState::make
(ColorAttrib::make_flat(LVecBase4f(1.0f, 1.0f, 1.0f, 0.5f)),
TransparencyAttrib::make(TransparencyAttrib::M_alpha),
DepthOffsetAttrib::make(),
CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise));
viz_state = viz_state->set_attrib(TextureAttrib::make(_viz_tex));
viz_state = viz_state->adjust_all_priorities(1);
}
return data._state->compose(viz_state);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::get_frame_viz_state
// Access: Protected
// Description: Returns the RenderState to apply to the frame.
////////////////////////////////////////////////////////////////////
CPT(RenderState) OccluderNode::
get_frame_viz_state(CullTraverser *trav, CullTraverserData &data) {
static CPT(RenderState) viz_state;
if (viz_state == NULL) {
viz_state = RenderState::make
(ColorAttrib::make_flat(LVecBase4f(0.0f, 0.0f, 0.0f, 1.0f)),
TextureAttrib::make_off());
viz_state = viz_state->adjust_all_priorities(1);
}
return data._state->compose(viz_state);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// OccluderNode.
////////////////////////////////////////////////////////////////////
void OccluderNode::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void OccluderNode::
write_datagram(BamWriter *manager, Datagram &dg) {
PandaNode::write_datagram(manager, dg);
dg.add_uint16(_vertices.size());
for (Vertices::const_iterator vi = _vertices.begin();
vi != _vertices.end();
++vi) {
(*vi).write_datagram(dg);
}
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::complete_pointers
// Access: Public, Virtual
// Description: Receives an array of pointers, one for each time
// manager->read_pointer() was called in fillin().
// Returns the number of pointers processed.
////////////////////////////////////////////////////////////////////
int OccluderNode::
complete_pointers(TypedWritable **p_list, BamReader *manager) {
int pi = PandaNode::complete_pointers(p_list, manager);
return pi;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type OccluderNode is encountered
// in the Bam file. It should create the OccluderNode
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *OccluderNode::
make_from_bam(const FactoryParams &params) {
OccluderNode *node = new OccluderNode("");
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
node->fillin(scan, manager);
return node;
}
////////////////////////////////////////////////////////////////////
// Function: OccluderNode::fillin
// Access: Protected
// Description: This internal function is called by make_from_bam to
// read in all of the relevant data from the BamFile for
// the new OccluderNode.
////////////////////////////////////////////////////////////////////
void OccluderNode::
fillin(DatagramIterator &scan, BamReader *manager) {
PandaNode::fillin(scan, manager);
int num_vertices = scan.get_uint16();
_vertices.reserve(num_vertices);
for (int i = 0; i < num_vertices; i++) {
LPoint3f vertex;
vertex.read_datagram(scan);
_vertices.push_back(vertex);
}
}

View File

@ -0,0 +1,105 @@
// Filename: occluderNode.h
// Created by: jenes (11Mar11)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef OCCLUDERNODE_H
#define OCCLUDERNODE_H
#include "pandabase.h"
#include "pandaNode.h"
#include "nodePath.h"
#include "pvector.h"
#include "geom.h"
#include "texture.h"
////////////////////////////////////////////////////////////////////
// Class : OccluderNode
// Description : A node in the scene graph that can hold an
// occluder polygon, which must be a rectangle. When
// the occluder is activated with something like
// render.set_occluder(), then objects whose bouding
// volume lies entirely behind the occluder will not be
// rendered.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_PGRAPH OccluderNode : public PandaNode {
PUBLISHED:
OccluderNode(const string &name);
protected:
OccluderNode(const OccluderNode &copy);
public:
virtual ~OccluderNode();
virtual PandaNode *make_copy() const;
virtual bool preserve_name() const;
virtual void xform(const LMatrix4f &mat);
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
virtual bool is_renderable() const;
virtual void output(ostream &out) const;
PUBLISHED:
INLINE void set_vertices(const LPoint3f &v0, const LPoint3f &v1,
const LPoint3f &v2, const LPoint3f &v3);
INLINE int get_num_vertices() const;
INLINE const LPoint3f &get_vertex(int n) const;
MAKE_SEQ(get_vertices, get_num_vertices, get_vertex);
protected:
virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
int &internal_vertices,
int pipeline_stage,
Thread *current_thread) const;
PT(Geom) get_occluder_viz(CullTraverser *trav, CullTraverserData &data);
CPT(RenderState) get_occluder_viz_state(CullTraverser *trav, CullTraverserData &data);
CPT(RenderState) get_frame_viz_state(CullTraverser *trav, CullTraverserData &data);
private:
typedef pvector<LPoint3f> Vertices;
Vertices _vertices;
PT(Geom) _occluder_viz, _frame_viz;
static PT(Texture) _viz_tex;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
PandaNode::init_type();
register_type(_type_handle, "OccluderNode",
PandaNode::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "OccluderNode.I"
#endif

View File

@ -17,6 +17,8 @@
#include "nodePathCollection.cxx"
#include "nodePathComponent.cxx"
#include "nodePathLerps.cxx"
#include "occluderEffect.cxx"
#include "occluderNode.cxx"
#include "pandaNode.cxx"
#include "pandaNodeChain.cxx"
#include "planeNode.cxx"

View File

@ -37,6 +37,7 @@
#include "nodePath.h"
#include "shaderAttrib.h"
#include "colorAttrib.h"
#include "clipPlaneAttrib.h"
TypeHandle LODNode::_type_handle;