mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
explicit occlusion culling support from community member teedee, with additional enhancements by drwr
This commit is contained in:
parent
9f9ed8bc56
commit
44375f019d
@ -53,6 +53,7 @@
|
||||
#include "lightLensNode.h"
|
||||
#include "colorAttrib.h"
|
||||
#include "colorScaleAttrib.h"
|
||||
#include "clipPlaneAttrib.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include "stencilAttrib.h"
|
||||
#include "lightAttrib.h"
|
||||
#include "scissorAttrib.h"
|
||||
#include "clipPlaneAttrib.h"
|
||||
#include "graphicsEngine.h"
|
||||
#include "shaderGenerator.h"
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
82
panda/src/pgraph/occluderEffect.I
Executable file
82
panda/src/pgraph/occluderEffect.I
Executable 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 ©) :
|
||||
_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();
|
||||
}
|
262
panda/src/pgraph/occluderEffect.cxx
Executable file
262
panda/src/pgraph/occluderEffect.cxx
Executable 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 ¶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());
|
||||
}
|
||||
}
|
102
panda/src/pgraph/occluderEffect.h
Executable file
102
panda/src/pgraph/occluderEffect.h
Executable 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 ©);
|
||||
|
||||
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 ¶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
|
||||
|
54
panda/src/pgraph/occluderNode.I
Normal file
54
panda/src/pgraph/occluderNode.I
Normal 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];
|
||||
}
|
433
panda/src/pgraph/occluderNode.cxx
Normal file
433
panda/src/pgraph/occluderNode.cxx
Normal 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 ©) :
|
||||
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);
|
||||
}
|
||||
}
|
105
panda/src/pgraph/occluderNode.h
Normal file
105
panda/src/pgraph/occluderNode.h
Normal 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 ©);
|
||||
|
||||
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 ¶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
|
@ -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"
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "nodePath.h"
|
||||
#include "shaderAttrib.h"
|
||||
#include "colorAttrib.h"
|
||||
#include "clipPlaneAttrib.h"
|
||||
|
||||
TypeHandle LODNode::_type_handle;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user