From 44375f019d86cd0c4e7b944720fb3a04b578c171 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 18 Mar 2011 01:56:09 +0000 Subject: [PATCH] explicit occlusion culling support from community member teedee, with additional enhancements by drwr --- panda/src/display/graphicsStateGuardian.cxx | 1 + panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx | 1 + panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx | 1 + panda/src/egg/eggGroup.I | 24 + panda/src/egg/eggGroup.cxx | 4 + panda/src/egg/eggGroup.h | 5 +- panda/src/egg/parser.yxx | 3 + panda/src/egg2pg/eggLoader.cxx | 45 +- panda/src/egg2pg/eggLoader.h | 2 + .../glstuff/glGraphicsStateGuardian_src.cxx | 1 + panda/src/pgraph/Sources.pp | 6 + panda/src/pgraph/config_pgraph.cxx | 13 +- panda/src/pgraph/config_pgraph.h | 1 + panda/src/pgraph/cullPlanes.I | 7 +- panda/src/pgraph/cullPlanes.cxx | 312 ++++++++++--- panda/src/pgraph/cullPlanes.h | 22 +- panda/src/pgraph/cullTraverser.cxx | 39 +- panda/src/pgraph/cullTraverser.h | 12 +- panda/src/pgraph/cullTraverserData.I | 2 +- panda/src/pgraph/cullTraverserData.cxx | 14 +- panda/src/pgraph/cullTraverserData.h | 2 +- panda/src/pgraph/nodePath.cxx | 101 ++++ panda/src/pgraph/nodePath.h | 5 + panda/src/pgraph/occluderEffect.I | 82 ++++ panda/src/pgraph/occluderEffect.cxx | 262 +++++++++++ panda/src/pgraph/occluderEffect.h | 102 +++++ panda/src/pgraph/occluderNode.I | 54 +++ panda/src/pgraph/occluderNode.cxx | 433 ++++++++++++++++++ panda/src/pgraph/occluderNode.h | 105 +++++ panda/src/pgraph/pgraph_composite3.cxx | 2 + panda/src/pgraphnodes/lodNode.cxx | 1 + 31 files changed, 1582 insertions(+), 82 deletions(-) create mode 100755 panda/src/pgraph/occluderEffect.I create mode 100755 panda/src/pgraph/occluderEffect.cxx create mode 100755 panda/src/pgraph/occluderEffect.h create mode 100644 panda/src/pgraph/occluderNode.I create mode 100644 panda/src/pgraph/occluderNode.cxx create mode 100644 panda/src/pgraph/occluderNode.h diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index c16f37d508..922c5b3864 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -53,6 +53,7 @@ #include "lightLensNode.h" #include "colorAttrib.h" #include "colorScaleAttrib.h" +#include "clipPlaneAttrib.h" #include #include diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index c783f5ebb4..075a9cdc0f 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -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" diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index b0ecdb18c7..3471507c4c 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -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" diff --git a/panda/src/egg/eggGroup.I b/panda/src/egg/eggGroup.I index 6fb0aa5cdd..43281cad94 100644 --- a/panda/src/egg/eggGroup.I +++ b/panda/src/egg/eggGroup.I @@ -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 diff --git a/panda/src/egg/eggGroup.cxx b/panda/src/egg/eggGroup.cxx index d1d32fdf39..7526a60935 100644 --- a/panda/src/egg/eggGroup.cxx +++ b/panda/src/egg/eggGroup.cxx @@ -260,6 +260,10 @@ write(ostream &out, int indent_level) const { indent(out, indent_level) << " portal { 1 }\n"; } + if (get_occluder_flag()) { + indent(out, indent_level) << " occluder { 1 }\n"; + } + if (get_polylight_flag()) { indent(out, indent_level) << " polylight { 1 }\n"; } diff --git a/panda/src/egg/eggGroup.h b/panda/src/egg/eggGroup.h index 7dfeb11ddf..16e7121537 100644 --- a/panda/src/egg/eggGroup.h +++ b/panda/src/egg/eggGroup.h @@ -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; diff --git a/panda/src/egg/parser.yxx b/panda/src/egg/parser.yxx index d3ac039952..b9600e13eb 100644 --- a/panda/src/egg/parser.yxx +++ b/panda/src/egg/parser.yxx @@ -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); diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index e4d407bced..e8b3809999 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -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 diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index 14e4b47e67..28d4a69c4f 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -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, diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index c2d16908f7..bd705809f8 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -61,6 +61,7 @@ #include "stencilAttrib.h" #include "lightAttrib.h" #include "scissorAttrib.h" +#include "clipPlaneAttrib.h" #include "graphicsEngine.h" #include "shaderGenerator.h" diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index acea31e1ee..76362f69b6 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -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 \ diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index f1330a5a3b..91ca87be17 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -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(); diff --git a/panda/src/pgraph/config_pgraph.h b/panda/src/pgraph/config_pgraph.h index 7ff27ab62b..b9fb6c3759 100644 --- a/panda/src/pgraph/config_pgraph.h +++ b/panda/src/pgraph/config_pgraph.h @@ -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; diff --git a/panda/src/pgraph/cullPlanes.I b/panda/src/pgraph/cullPlanes.I index bd8f74639e..8d0e45331e 100644 --- a/panda/src/pgraph/cullPlanes.I +++ b/panda/src/pgraph/cullPlanes.I @@ -29,7 +29,8 @@ CullPlanes() { //////////////////////////////////////////////////////////////////// INLINE CullPlanes:: CullPlanes(const CullPlanes ©) : - _planes(copy._planes) + _planes(copy._planes), + _occluders(copy._occluders) { } @@ -41,6 +42,7 @@ CullPlanes(const CullPlanes ©) : INLINE void CullPlanes:: operator = (const CullPlanes ©) { _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(); + } diff --git a/panda/src/pgraph/cullPlanes.cxx b/panda/src/pgraph/cullPlanes.cxx index 31befcdbfe..d74ca03596 100644 --- a/panda/src/pgraph/cullPlanes.cxx +++ b/panda/src/pgraph/cullPlanes.cxx @@ -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"; + } } diff --git a/panda/src/pgraph/cullPlanes.h b/panda/src/pgraph/cullPlanes.h index 81c101f75e..25dc4f77a0 100644 --- a/panda/src/pgraph/cullPlanes.h +++ b/panda/src/pgraph/cullPlanes.h @@ -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 Planes; Planes _planes; + + typedef pmap Occluders; + Occluders _occluders; }; #include "cullPlanes.I" diff --git a/panda/src/pgraph/cullTraverser.cxx b/panda/src/pgraph/cullTraverser.cxx index 36d50c756a..e3eceb5c8c 100644 --- a/panda/src/pgraph/cullTraverser.cxx +++ b/panda/src/pgraph/cullTraverser.cxx @@ -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); diff --git a/panda/src/pgraph/cullTraverser.h b/panda/src/pgraph/cullTraverser.h index 12791e0067..d0f9ad6771 100644 --- a/panda/src/pgraph/cullTraverser.h +++ b/panda/src/pgraph/cullTraverser.h @@ -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); diff --git a/panda/src/pgraph/cullTraverserData.I b/panda/src/pgraph/cullTraverserData.I index cb46b538d4..acd2ababfb 100644 --- a/panda/src/pgraph/cullTraverserData.I +++ b/panda/src/pgraph/cullTraverserData.I @@ -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; } diff --git a/panda/src/pgraph/cullTraverserData.cxx b/panda/src/pgraph/cullTraverserData.cxx index 80e4e422f2..47c88ff34d 100644 --- a/panda/src/pgraph/cullTraverserData.cxx +++ b/panda/src/pgraph/cullTraverserData.cxx @@ -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); } } diff --git a/panda/src/pgraph/cullTraverserData.h b/panda/src/pgraph/cullTraverserData.h index 3caacd0b28..1cca501caa 100644 --- a/panda/src/pgraph/cullTraverserData.h +++ b/panda/src/pgraph/cullTraverserData.h @@ -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(); }; diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 4f923335cf..46326ee697 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -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 diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 39f029b48b..03afbe8784 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -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; diff --git a/panda/src/pgraph/occluderEffect.I b/panda/src/pgraph/occluderEffect.I new file mode 100755 index 0000000000..3073000d37 --- /dev/null +++ b/panda/src/pgraph/occluderEffect.I @@ -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 ©) : + _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(); +} diff --git a/panda/src/pgraph/occluderEffect.cxx b/panda/src/pgraph/occluderEffect.cxx new file mode 100755 index 0000000000..27405998d9 --- /dev/null +++ b/panda/src/pgraph/occluderEffect.cxx @@ -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 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 ¶ms) { + 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()); + } +} diff --git a/panda/src/pgraph/occluderEffect.h b/panda/src/pgraph/occluderEffect.h new file mode 100755 index 0000000000..47ecb8b5d2 --- /dev/null +++ b/panda/src/pgraph/occluderEffect.h @@ -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 ©); + +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 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 ¶ms); + 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 + diff --git a/panda/src/pgraph/occluderNode.I b/panda/src/pgraph/occluderNode.I new file mode 100644 index 0000000000..19ee1c4dbe --- /dev/null +++ b/panda/src/pgraph/occluderNode.I @@ -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]; +} diff --git a/panda/src/pgraph/occluderNode.cxx b/panda/src/pgraph/occluderNode.cxx new file mode 100644 index 0000000000..0c2b66fd11 --- /dev/null +++ b/panda/src/pgraph/occluderNode.cxx @@ -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 ©) : + 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 ¶ms) { + 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); + } +} diff --git a/panda/src/pgraph/occluderNode.h b/panda/src/pgraph/occluderNode.h new file mode 100644 index 0000000000..9d488b7600 --- /dev/null +++ b/panda/src/pgraph/occluderNode.h @@ -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 ©); + +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 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 ¶ms); + 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 diff --git a/panda/src/pgraph/pgraph_composite3.cxx b/panda/src/pgraph/pgraph_composite3.cxx index 498df69807..80b0ca47ff 100644 --- a/panda/src/pgraph/pgraph_composite3.cxx +++ b/panda/src/pgraph/pgraph_composite3.cxx @@ -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" diff --git a/panda/src/pgraphnodes/lodNode.cxx b/panda/src/pgraphnodes/lodNode.cxx index d35e45d2c9..35a7b02c32 100644 --- a/panda/src/pgraphnodes/lodNode.cxx +++ b/panda/src/pgraphnodes/lodNode.cxx @@ -37,6 +37,7 @@ #include "nodePath.h" #include "shaderAttrib.h" #include "colorAttrib.h" +#include "clipPlaneAttrib.h" TypeHandle LODNode::_type_handle;