mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -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 "lightLensNode.h"
|
||||||
#include "colorAttrib.h"
|
#include "colorAttrib.h"
|
||||||
#include "colorScaleAttrib.h"
|
#include "colorScaleAttrib.h"
|
||||||
|
#include "clipPlaneAttrib.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "lightAttrib.h"
|
#include "lightAttrib.h"
|
||||||
#include "stencilAttrib.h"
|
#include "stencilAttrib.h"
|
||||||
#include "scissorAttrib.h"
|
#include "scissorAttrib.h"
|
||||||
|
#include "clipPlaneAttrib.h"
|
||||||
#include "fog.h"
|
#include "fog.h"
|
||||||
#include "throw_event.h"
|
#include "throw_event.h"
|
||||||
#include "geomVertexFormat.h"
|
#include "geomVertexFormat.h"
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include "lightAttrib.h"
|
#include "lightAttrib.h"
|
||||||
#include "stencilAttrib.h"
|
#include "stencilAttrib.h"
|
||||||
#include "scissorAttrib.h"
|
#include "scissorAttrib.h"
|
||||||
|
#include "clipPlaneAttrib.h"
|
||||||
#include "fog.h"
|
#include "fog.h"
|
||||||
#include "throw_event.h"
|
#include "throw_event.h"
|
||||||
#include "geomVertexFormat.h"
|
#include "geomVertexFormat.h"
|
||||||
|
@ -498,6 +498,30 @@ get_portal_flag() const {
|
|||||||
return ((_flags2 & F2_portal_flag) != 0);
|
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
|
// Function: EggGroup::set_polylight_flag
|
||||||
// Access: Published
|
// Access: Published
|
||||||
|
@ -260,6 +260,10 @@ write(ostream &out, int indent_level) const {
|
|||||||
indent(out, indent_level) << "<Scalar> portal { 1 }\n";
|
indent(out, indent_level) << "<Scalar> portal { 1 }\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (get_occluder_flag()) {
|
||||||
|
indent(out, indent_level) << "<Scalar> occluder { 1 }\n";
|
||||||
|
}
|
||||||
|
|
||||||
if (get_polylight_flag()) {
|
if (get_polylight_flag()) {
|
||||||
indent(out, indent_level) << "<Scalar> polylight { 1 }\n";
|
indent(out, indent_level) << "<Scalar> polylight { 1 }\n";
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,9 @@ PUBLISHED:
|
|||||||
INLINE void set_portal_flag(bool flag);
|
INLINE void set_portal_flag(bool flag);
|
||||||
INLINE bool get_portal_flag() const;
|
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 void set_polylight_flag(bool flag);
|
||||||
INLINE bool get_polylight_flag() const;
|
INLINE bool get_polylight_flag() const;
|
||||||
|
|
||||||
@ -361,8 +364,8 @@ private:
|
|||||||
F2_polylight_flag = 0x00000100,
|
F2_polylight_flag = 0x00000100,
|
||||||
F2_indexed_flag = 0x00000200,
|
F2_indexed_flag = 0x00000200,
|
||||||
F2_has_indexed_flag = 0x00000400,
|
F2_has_indexed_flag = 0x00000400,
|
||||||
|
|
||||||
F2_has_blend_color = 0x00000800,
|
F2_has_blend_color = 0x00000800,
|
||||||
|
F2_occluder_flag = 0x00001000,
|
||||||
};
|
};
|
||||||
|
|
||||||
int _flags;
|
int _flags;
|
||||||
|
@ -1292,6 +1292,9 @@ group_body:
|
|||||||
} else if (cmp_nocase_uh(name, "portal") == 0) {
|
} else if (cmp_nocase_uh(name, "portal") == 0) {
|
||||||
group->set_portal_flag(value != 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) {
|
} else if (cmp_nocase_uh(name, "polylight") == 0) {
|
||||||
group->set_polylight_flag(value != 0);
|
group->set_polylight_flag(value != 0);
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include "sequenceNode.h"
|
#include "sequenceNode.h"
|
||||||
#include "switchNode.h"
|
#include "switchNode.h"
|
||||||
#include "portalNode.h"
|
#include "portalNode.h"
|
||||||
|
#include "occluderNode.h"
|
||||||
#include "polylightNode.h"
|
#include "polylightNode.h"
|
||||||
#include "lodNode.h"
|
#include "lodNode.h"
|
||||||
#include "modelNode.h"
|
#include "modelNode.h"
|
||||||
@ -1860,7 +1861,20 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
|
|||||||
egg2pg_cat.warning()
|
egg2pg_cat.warning()
|
||||||
<< "Portal " << egg_group->get_name() << " has no vertices!\n";
|
<< "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()) {
|
} else if (egg_group->get_polylight_flag()) {
|
||||||
// Create a polylight instead of a regular polyset.
|
// Create a polylight instead of a regular polyset.
|
||||||
// use make_sphere to get the center, radius and color
|
// 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
|
// Function: EggLoader::find_first_polygon
|
||||||
// Access: Private
|
// Access: Private
|
||||||
|
@ -53,6 +53,7 @@ class CollisionNode;
|
|||||||
class CollisionPlane;
|
class CollisionPlane;
|
||||||
class CollisionPolygon;
|
class CollisionPolygon;
|
||||||
class PortalNode;
|
class PortalNode;
|
||||||
|
class OccluderNode;
|
||||||
class PolylightNode;
|
class PolylightNode;
|
||||||
class EggRenderState;
|
class EggRenderState;
|
||||||
class CharacterMaker;
|
class CharacterMaker;
|
||||||
@ -156,6 +157,7 @@ private:
|
|||||||
bool has_overall_color, const Colorf &overall_color);
|
bool has_overall_color, const Colorf &overall_color);
|
||||||
|
|
||||||
void set_portal_polygon(EggGroup *egg_group, PortalNode *pnode);
|
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);
|
PT(EggPolygon) find_first_polygon(EggGroup *egg_group);
|
||||||
|
|
||||||
bool make_sphere(EggGroup *start_group, EggGroup::CollideFlags flags,
|
bool make_sphere(EggGroup *start_group, EggGroup::CollideFlags flags,
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include "stencilAttrib.h"
|
#include "stencilAttrib.h"
|
||||||
#include "lightAttrib.h"
|
#include "lightAttrib.h"
|
||||||
#include "scissorAttrib.h"
|
#include "scissorAttrib.h"
|
||||||
|
#include "clipPlaneAttrib.h"
|
||||||
#include "graphicsEngine.h"
|
#include "graphicsEngine.h"
|
||||||
#include "shaderGenerator.h"
|
#include "shaderGenerator.h"
|
||||||
|
|
||||||
|
@ -74,6 +74,8 @@
|
|||||||
nodePathCollection.I nodePathCollection.h \
|
nodePathCollection.I nodePathCollection.h \
|
||||||
nodePathComponent.I nodePathComponent.h \
|
nodePathComponent.I nodePathComponent.h \
|
||||||
nodePathLerps.h \
|
nodePathLerps.h \
|
||||||
|
occluderEffect.I occluderEffect.h \
|
||||||
|
occluderNode.I occluderNode.h \
|
||||||
pandaNode.I pandaNode.h \
|
pandaNode.I pandaNode.h \
|
||||||
pandaNodeChain.I pandaNodeChain.h \
|
pandaNodeChain.I pandaNodeChain.h \
|
||||||
planeNode.I planeNode.h \
|
planeNode.I planeNode.h \
|
||||||
@ -171,6 +173,8 @@
|
|||||||
nodePathCollection.cxx \
|
nodePathCollection.cxx \
|
||||||
nodePathComponent.cxx \
|
nodePathComponent.cxx \
|
||||||
nodePathLerps.cxx \
|
nodePathLerps.cxx \
|
||||||
|
occluderEffect.cxx \
|
||||||
|
occluderNode.cxx \
|
||||||
pandaNode.cxx \
|
pandaNode.cxx \
|
||||||
pandaNodeChain.cxx \
|
pandaNodeChain.cxx \
|
||||||
planeNode.cxx \
|
planeNode.cxx \
|
||||||
@ -266,6 +270,8 @@
|
|||||||
nodePathCollection.I nodePathCollection.h \
|
nodePathCollection.I nodePathCollection.h \
|
||||||
nodePathComponent.I nodePathComponent.h \
|
nodePathComponent.I nodePathComponent.h \
|
||||||
nodePathLerps.h \
|
nodePathLerps.h \
|
||||||
|
occluderEffect.I occluderEffect.h \
|
||||||
|
occluderNode.I occluderNode.h \
|
||||||
pandaNode.I pandaNode.h \
|
pandaNode.I pandaNode.h \
|
||||||
pandaNodeChain.I pandaNodeChain.h \
|
pandaNodeChain.I pandaNodeChain.h \
|
||||||
planeNode.I planeNode.h \
|
planeNode.I planeNode.h \
|
||||||
|
@ -63,6 +63,8 @@
|
|||||||
#include "polylightEffect.h"
|
#include "polylightEffect.h"
|
||||||
#include "polylightNode.h"
|
#include "polylightNode.h"
|
||||||
#include "portalNode.h"
|
#include "portalNode.h"
|
||||||
|
#include "occluderEffect.h"
|
||||||
|
#include "occluderNode.h"
|
||||||
#include "portalClipper.h"
|
#include "portalClipper.h"
|
||||||
#include "renderAttrib.h"
|
#include "renderAttrib.h"
|
||||||
#include "renderEffect.h"
|
#include "renderEffect.h"
|
||||||
@ -108,8 +110,7 @@ ConfigVariableBool clip_plane_cull
|
|||||||
("clip-plane-cull", true,
|
("clip-plane-cull", true,
|
||||||
PRC_DESC("This is normally true; set it false to disable culling of objects "
|
PRC_DESC("This is normally true; set it false to disable culling of objects "
|
||||||
"that are completely behind one or more clip planes (primarily "
|
"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
|
ConfigVariableBool allow_portal_cull
|
||||||
("allow-portal-cull", false,
|
("allow-portal-cull", false,
|
||||||
@ -123,6 +124,10 @@ ConfigVariableBool debug_portal_cull
|
|||||||
"(You first need to enable portal culling, using the allow-portal-cull"
|
"(You first need to enable portal culling, using the allow-portal-cull"
|
||||||
"variable.)"));
|
"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
|
ConfigVariableBool unambiguous_graph
|
||||||
("unambiguous-graph", false,
|
("unambiguous-graph", false,
|
||||||
@ -454,6 +459,8 @@ init_libpgraph() {
|
|||||||
PolylightNode::init_type();
|
PolylightNode::init_type();
|
||||||
PolylightEffect::init_type();
|
PolylightEffect::init_type();
|
||||||
PortalNode::init_type();
|
PortalNode::init_type();
|
||||||
|
OccluderEffect::init_type();
|
||||||
|
OccluderNode::init_type();
|
||||||
PortalClipper::init_type();
|
PortalClipper::init_type();
|
||||||
RenderAttrib::init_type();
|
RenderAttrib::init_type();
|
||||||
RenderEffect::init_type();
|
RenderEffect::init_type();
|
||||||
@ -515,6 +522,8 @@ init_libpgraph() {
|
|||||||
PlaneNode::register_with_read_factory();
|
PlaneNode::register_with_read_factory();
|
||||||
PolylightNode::register_with_read_factory();
|
PolylightNode::register_with_read_factory();
|
||||||
PortalNode::register_with_read_factory();
|
PortalNode::register_with_read_factory();
|
||||||
|
OccluderEffect::register_with_read_factory();
|
||||||
|
OccluderNode::register_with_read_factory();
|
||||||
RenderEffects::register_with_read_factory();
|
RenderEffects::register_with_read_factory();
|
||||||
RenderModeAttrib::register_with_read_factory();
|
RenderModeAttrib::register_with_read_factory();
|
||||||
RenderState::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 clip_plane_cull;
|
||||||
extern ConfigVariableBool allow_portal_cull;
|
extern ConfigVariableBool allow_portal_cull;
|
||||||
extern ConfigVariableBool debug_portal_cull;
|
extern ConfigVariableBool debug_portal_cull;
|
||||||
|
extern ConfigVariableBool show_occluder_volumes;
|
||||||
extern ConfigVariableBool unambiguous_graph;
|
extern ConfigVariableBool unambiguous_graph;
|
||||||
extern ConfigVariableBool detect_graph_cycles;
|
extern ConfigVariableBool detect_graph_cycles;
|
||||||
extern ConfigVariableBool no_unsupported_copy;
|
extern ConfigVariableBool no_unsupported_copy;
|
||||||
|
@ -29,7 +29,8 @@ CullPlanes() {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE CullPlanes::
|
INLINE CullPlanes::
|
||||||
CullPlanes(const CullPlanes ©) :
|
CullPlanes(const CullPlanes ©) :
|
||||||
_planes(copy._planes)
|
_planes(copy._planes),
|
||||||
|
_occluders(copy._occluders)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ CullPlanes(const CullPlanes ©) :
|
|||||||
INLINE void CullPlanes::
|
INLINE void CullPlanes::
|
||||||
operator = (const CullPlanes ©) {
|
operator = (const CullPlanes ©) {
|
||||||
_planes = copy._planes;
|
_planes = copy._planes;
|
||||||
|
_occluders = copy._occluders;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -60,5 +62,6 @@ INLINE CullPlanes::
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
INLINE bool CullPlanes::
|
INLINE bool CullPlanes::
|
||||||
is_empty() const {
|
is_empty() const {
|
||||||
return _planes.empty();
|
return _planes.empty() && _occluders.empty();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "cullPlanes.h"
|
#include "cullPlanes.h"
|
||||||
#include "cullTraverserData.h"
|
#include "cullTraverserData.h"
|
||||||
|
#include "clipPlaneAttrib.h"
|
||||||
|
#include "occluderEffect.h"
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -59,6 +61,15 @@ xform(const LMatrix4f &mat) const {
|
|||||||
(*pi).second->xform(mat);
|
(*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;
|
return new_planes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +88,9 @@ xform(const LMatrix4f &mat) const {
|
|||||||
CPT(CullPlanes) CullPlanes::
|
CPT(CullPlanes) CullPlanes::
|
||||||
apply_state(const CullTraverser *trav, const CullTraverserData *data,
|
apply_state(const CullTraverser *trav, const CullTraverserData *data,
|
||||||
const ClipPlaneAttrib *net_attrib,
|
const ClipPlaneAttrib *net_attrib,
|
||||||
const ClipPlaneAttrib *off_attrib) const {
|
const ClipPlaneAttrib *off_attrib,
|
||||||
if (net_attrib == (ClipPlaneAttrib *)NULL) {
|
const OccluderEffect *node_effect) const {
|
||||||
|
if (net_attrib == (ClipPlaneAttrib *)NULL && node_effect == (OccluderEffect *)NULL) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,28 +103,164 @@ apply_state(const CullTraverser *trav, const CullTraverserData *data,
|
|||||||
|
|
||||||
CPT(TransformState) net_transform = NULL;
|
CPT(TransformState) net_transform = NULL;
|
||||||
|
|
||||||
int num_on_planes = net_attrib->get_num_on_planes();
|
if (net_attrib != (ClipPlaneAttrib *)NULL) {
|
||||||
for (int i = 0; i < num_on_planes; ++i) {
|
int num_on_planes = net_attrib->get_num_on_planes();
|
||||||
NodePath clip_plane = net_attrib->get_on_plane(i);
|
for (int i = 0; i < num_on_planes; ++i) {
|
||||||
Planes::const_iterator pi = new_planes->_planes.find(clip_plane);
|
NodePath clip_plane = net_attrib->get_on_plane(i);
|
||||||
if (pi == new_planes->_planes.end()) {
|
Planes::const_iterator pi = new_planes->_planes.find(clip_plane);
|
||||||
if (!off_attrib->has_off_plane(clip_plane)) {
|
if (pi == new_planes->_planes.end()) {
|
||||||
// Here's a new clip plane; add it to the list. For this we
|
if (!off_attrib->has_off_plane(clip_plane)) {
|
||||||
// need the net transform to this node.
|
// Here's a new clip plane; add it to the list. For this we
|
||||||
if (net_transform == (TransformState *)NULL) {
|
// need the net transform to this node.
|
||||||
net_transform = data->get_net_transform(trav);
|
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;
|
return new_planes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,45 +285,74 @@ do_cull(int &result, CPT(RenderState) &state,
|
|||||||
BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
|
BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
|
||||||
|
|
||||||
CPT(ClipPlaneAttrib) orig_cpa = DCAST(ClipPlaneAttrib, state->get_attrib(ClipPlaneAttrib::get_class_slot()));
|
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(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;
|
} else {
|
||||||
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
|
CPT(ClipPlaneAttrib) new_cpa = orig_cpa;
|
||||||
int plane_result = (*pi).second->contains(node_gbv);
|
|
||||||
if (plane_result == BoundingVolume::IF_no_intersection) {
|
Planes::const_iterator pi;
|
||||||
// The node is completely behind this clip plane. Short-circuit
|
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
|
||||||
// the rest of the logic; none of the other planes matter.
|
int plane_result = (*pi).second->contains(node_gbv);
|
||||||
result = plane_result;
|
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;
|
return new_planes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((plane_result & BoundingVolume::IF_all) != 0) {
|
result &= occluder_result;
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_planes;
|
return new_planes;
|
||||||
@ -204,6 +381,29 @@ remove_plane(const NodePath &clip_plane) const {
|
|||||||
return new_planes;
|
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
|
// Function: CullPlanes::write
|
||||||
// Access: Public
|
// Access: Public
|
||||||
@ -211,9 +411,15 @@ remove_plane(const NodePath &clip_plane) const {
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
void CullPlanes::
|
void CullPlanes::
|
||||||
write(ostream &out) const {
|
write(ostream &out) const {
|
||||||
out << "CullPlanes (" << _planes.size() << " planes):\n";
|
out << "CullPlanes (" << _planes.size() << " planes and "
|
||||||
|
<< _occluders.size() << " occluders):\n";
|
||||||
Planes::const_iterator pi;
|
Planes::const_iterator pi;
|
||||||
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
|
for (pi = _planes.begin(); pi != _planes.end(); ++pi) {
|
||||||
out << " " << (*pi).first << " : " << *(*pi).second << "\n";
|
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 "pandabase.h"
|
||||||
#include "referenceCount.h"
|
#include "referenceCount.h"
|
||||||
#include "nodePath.h"
|
#include "nodePath.h"
|
||||||
#include "clipPlaneAttrib.h"
|
#include "boundingHexahedron.h"
|
||||||
#include "boundingPlane.h"
|
#include "boundingPlane.h"
|
||||||
#include "pointerTo.h"
|
#include "pointerTo.h"
|
||||||
#include "luse.h"
|
#include "luse.h"
|
||||||
#include "deletedChain.h"
|
#include "deletedChain.h"
|
||||||
|
|
||||||
|
class ClipPlaneAttrib;
|
||||||
|
class OccluderEffect;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Class : CullPlanes
|
// Class : CullPlanes
|
||||||
// Description : This represents the set of clip planes that are
|
// Description : This represents the set of clip planes and/or
|
||||||
// definitely in effect for the current node of the
|
// occluders that are definitely in effect for the
|
||||||
// CullTraverserData, as well as on all child nodes.
|
// current node of the CullTraverserData, as well as on
|
||||||
// Any clip planes in this list may be safely culled
|
// all child nodes. Any clip planes and occluders in
|
||||||
// against.
|
// this list may be safely culled against.
|
||||||
//
|
//
|
||||||
// This does not include the clip planes that are in
|
// This does not include the clip planes that are in
|
||||||
// effect now, but might later be turned off by a child
|
// effect now, but might later be turned off by a child
|
||||||
@ -57,17 +60,22 @@ public:
|
|||||||
CPT(CullPlanes) apply_state(const CullTraverser *trav,
|
CPT(CullPlanes) apply_state(const CullTraverser *trav,
|
||||||
const CullTraverserData *data,
|
const CullTraverserData *data,
|
||||||
const ClipPlaneAttrib *net_attrib,
|
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,
|
CPT(CullPlanes) do_cull(int &result, CPT(RenderState) &state,
|
||||||
const GeometricBoundingVolume *node_gbv) const;
|
const GeometricBoundingVolume *node_gbv) const;
|
||||||
|
|
||||||
CPT(CullPlanes) remove_plane(const NodePath &clip_plane) const;
|
CPT(CullPlanes) remove_plane(const NodePath &clip_plane) const;
|
||||||
|
CPT(CullPlanes) remove_occluder(const NodePath &occluder) const;
|
||||||
|
|
||||||
void write(ostream &out) const;
|
void write(ostream &out) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef pmap<NodePath, PT(BoundingPlane) > Planes;
|
typedef pmap<NodePath, PT(BoundingPlane) > Planes;
|
||||||
Planes _planes;
|
Planes _planes;
|
||||||
|
|
||||||
|
typedef pmap<NodePath, PT(BoundingHexahedron) > Occluders;
|
||||||
|
Occluders _occluders;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "cullPlanes.I"
|
#include "cullPlanes.I"
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "geomTristrips.h"
|
#include "geomTristrips.h"
|
||||||
#include "geomTriangles.h"
|
#include "geomTriangles.h"
|
||||||
#include "geomLinestrips.h"
|
#include "geomLinestrips.h"
|
||||||
|
#include "geomLines.h"
|
||||||
#include "geomVertexWriter.h"
|
#include "geomVertexWriter.h"
|
||||||
|
|
||||||
PStatCollector CullTraverser::_nodes_pcollector("Nodes");
|
PStatCollector CullTraverser::_nodes_pcollector("Nodes");
|
||||||
@ -224,7 +225,6 @@ traverse(CullTraverserData &data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse_below(data);
|
traverse_below(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,7 +313,7 @@ end_traverse() {
|
|||||||
void CullTraverser::
|
void CullTraverser::
|
||||||
draw_bounding_volume(const BoundingVolume *vol,
|
draw_bounding_volume(const BoundingVolume *vol,
|
||||||
const TransformState *net_transform,
|
const TransformState *net_transform,
|
||||||
const TransformState *modelview_transform) {
|
const TransformState *modelview_transform) const {
|
||||||
PT(Geom) bounds_viz = make_bounds_viz(vol);
|
PT(Geom) bounds_viz = make_bounds_viz(vol);
|
||||||
|
|
||||||
if (bounds_viz != (Geom *)NULL) {
|
if (bounds_viz != (Geom *)NULL) {
|
||||||
@ -387,7 +387,7 @@ show_bounds(CullTraverserData &data, bool tight) {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: CullTraverser::make_bounds_viz
|
// Function: CullTraverser::make_bounds_viz
|
||||||
// Access: Private
|
// Access: Private, Static
|
||||||
// Description: Returns an appropriate visualization of the indicated
|
// Description: Returns an appropriate visualization of the indicated
|
||||||
// bounding volume.
|
// bounding volume.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -427,6 +427,37 @@ make_bounds_viz(const BoundingVolume *vol) {
|
|||||||
geom = new Geom(vdata);
|
geom = new Geom(vdata);
|
||||||
geom->add_primitive(strip);
|
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())) {
|
} else if (vol->is_of_type(FiniteBoundingVolume::get_class_type())) {
|
||||||
const FiniteBoundingVolume *fvol = DCAST(FiniteBoundingVolume, vol);
|
const FiniteBoundingVolume *fvol = DCAST(FiniteBoundingVolume, vol);
|
||||||
|
|
||||||
@ -487,7 +518,7 @@ make_bounds_viz(const BoundingVolume *vol) {
|
|||||||
// node's "tight" bounding volume.
|
// node's "tight" bounding volume.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
PT(Geom) CullTraverser::
|
PT(Geom) CullTraverser::
|
||||||
make_tight_bounds_viz(PandaNode *node) {
|
make_tight_bounds_viz(PandaNode *node) const {
|
||||||
PT(Geom) geom;
|
PT(Geom) geom;
|
||||||
|
|
||||||
NodePath np = NodePath::any_path(node);
|
NodePath np = NodePath::any_path(node);
|
||||||
|
@ -89,7 +89,7 @@ PUBLISHED:
|
|||||||
|
|
||||||
void draw_bounding_volume(const BoundingVolume *vol,
|
void draw_bounding_volume(const BoundingVolume *vol,
|
||||||
const TransformState *net_transform,
|
const TransformState *net_transform,
|
||||||
const TransformState *modelview_transform);
|
const TransformState *modelview_transform) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool is_in_view(CullTraverserData &data);
|
virtual bool is_in_view(CullTraverserData &data);
|
||||||
@ -103,13 +103,13 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void show_bounds(CullTraverserData &data, bool tight);
|
void show_bounds(CullTraverserData &data, bool tight);
|
||||||
PT(Geom) make_bounds_viz(const BoundingVolume *vol);
|
static PT(Geom) make_bounds_viz(const BoundingVolume *vol);
|
||||||
PT(Geom) make_tight_bounds_viz(PandaNode *node);
|
PT(Geom) make_tight_bounds_viz(PandaNode *node) const;
|
||||||
static Vertexf compute_point(const BoundingSphere *sphere,
|
static Vertexf compute_point(const BoundingSphere *sphere,
|
||||||
float latitude, float longitude);
|
float latitude, float longitude);
|
||||||
CPT(RenderState) get_bounds_outer_viz_state();
|
static CPT(RenderState) get_bounds_outer_viz_state();
|
||||||
CPT(RenderState) get_bounds_inner_viz_state();
|
static CPT(RenderState) get_bounds_inner_viz_state();
|
||||||
CPT(RenderState) get_depth_offset_state();
|
static CPT(RenderState) get_depth_offset_state();
|
||||||
void start_decal(const CullTraverserData &data);
|
void start_decal(const CullTraverserData &data);
|
||||||
CullableObject *r_get_decals(CullTraverserData &data,
|
CullableObject *r_get_decals(CullTraverserData &data,
|
||||||
CullableObject *decals);
|
CullableObject *decals);
|
||||||
|
@ -167,7 +167,7 @@ is_in_view(const DrawMask &camera_mask) {
|
|||||||
if (_view_frustum == (GeometricBoundingVolume *)NULL &&
|
if (_view_frustum == (GeometricBoundingVolume *)NULL &&
|
||||||
_cull_planes->is_empty()) {
|
_cull_planes->is_empty()) {
|
||||||
// If the transform is valid, but we don't have a frustum or any
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,8 @@ apply_transform_and_state(CullTraverser *trav,
|
|||||||
if (clip_plane_cull) {
|
if (clip_plane_cull) {
|
||||||
_cull_planes = _cull_planes->apply_state(trav, this,
|
_cull_planes = _cull_planes->apply_state(trav, this,
|
||||||
DCAST(ClipPlaneAttrib, node_state->get_attrib(ClipPlaneAttrib::get_class_slot())),
|
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) {
|
if (result == BoundingVolume::IF_no_intersection) {
|
||||||
// No intersection at all. Cull.
|
// 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) {
|
} else if ((result & BoundingVolume::IF_all) != 0) {
|
||||||
// The node and its descendents are completely in front of all
|
// The node and its descendents are completely in front of all
|
||||||
// of the clip planes. The do_cull() call should therefore have
|
// of the clip planes and occluders. The do_cull() call should
|
||||||
// removed all of the clip planes.
|
// therefore have removed all of the clip planes and occluders.
|
||||||
nassertr(_cull_planes->is_empty(), true);
|
nassertr(_cull_planes->is_empty(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "pointerTo.h"
|
#include "pointerTo.h"
|
||||||
#include "drawMask.h"
|
#include "drawMask.h"
|
||||||
#include "cullTraverser.h"
|
#include "cullTraverser.h"
|
||||||
|
#include "pvector.h"
|
||||||
|
|
||||||
class PandaNode;
|
class PandaNode;
|
||||||
|
|
||||||
@ -87,7 +88,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_in_view_impl();
|
bool is_in_view_impl();
|
||||||
|
|
||||||
static CPT(RenderState) get_fake_view_frustum_cull_state();
|
static CPT(RenderState) get_fake_view_frustum_cull_state();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "materialCollection.h"
|
#include "materialCollection.h"
|
||||||
#include "lightAttrib.h"
|
#include "lightAttrib.h"
|
||||||
#include "clipPlaneAttrib.h"
|
#include "clipPlaneAttrib.h"
|
||||||
|
#include "occluderEffect.h"
|
||||||
#include "polylightEffect.h"
|
#include "polylightEffect.h"
|
||||||
#include "fogAttrib.h"
|
#include "fogAttrib.h"
|
||||||
#include "renderModeAttrib.h"
|
#include "renderModeAttrib.h"
|
||||||
@ -50,6 +51,7 @@
|
|||||||
#include "scissorEffect.h"
|
#include "scissorEffect.h"
|
||||||
#include "texturePool.h"
|
#include "texturePool.h"
|
||||||
#include "planeNode.h"
|
#include "planeNode.h"
|
||||||
|
#include "occluderNode.h"
|
||||||
#include "lensNode.h"
|
#include "lensNode.h"
|
||||||
#include "materialPool.h"
|
#include "materialPool.h"
|
||||||
#include "look_at.h"
|
#include "look_at.h"
|
||||||
@ -3186,6 +3188,105 @@ has_clip_plane_off(const NodePath &clip_plane) const {
|
|||||||
return false;
|
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
|
// Function: NodePath::set_scissor
|
||||||
// Access: Published
|
// Access: Published
|
||||||
|
@ -583,6 +583,11 @@ PUBLISHED:
|
|||||||
void clear_scissor();
|
void clear_scissor();
|
||||||
bool has_scissor() const;
|
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 set_bin(const string &bin_name, int draw_order, int priority = 0);
|
||||||
void clear_bin();
|
void clear_bin();
|
||||||
bool has_bin() const;
|
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 "nodePathCollection.cxx"
|
||||||
#include "nodePathComponent.cxx"
|
#include "nodePathComponent.cxx"
|
||||||
#include "nodePathLerps.cxx"
|
#include "nodePathLerps.cxx"
|
||||||
|
#include "occluderEffect.cxx"
|
||||||
|
#include "occluderNode.cxx"
|
||||||
#include "pandaNode.cxx"
|
#include "pandaNode.cxx"
|
||||||
#include "pandaNodeChain.cxx"
|
#include "pandaNodeChain.cxx"
|
||||||
#include "planeNode.cxx"
|
#include "planeNode.cxx"
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "nodePath.h"
|
#include "nodePath.h"
|
||||||
#include "shaderAttrib.h"
|
#include "shaderAttrib.h"
|
||||||
#include "colorAttrib.h"
|
#include "colorAttrib.h"
|
||||||
|
#include "clipPlaneAttrib.h"
|
||||||
|
|
||||||
TypeHandle LODNode::_type_handle;
|
TypeHandle LODNode::_type_handle;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user