pgraph lod, sequence nodes

This commit is contained in:
David Rose 2002-03-06 18:06:00 +00:00
parent 6cdf757749
commit 8b743a58fe
36 changed files with 1867 additions and 247 deletions

View File

@ -942,8 +942,8 @@ begin_decal_base_first() {
////////////////////////////////////////////////////////////////////
CPT(RenderState) GraphicsStateGuardian::
begin_decal_nested() {
// We keep the depth buffer off during this operation, although
// perhaps it doesn't matter so much here.
// We should keep the depth buffer off during this operation, so
// that decals on decals will render properly.
static CPT(RenderState) decal_nested;
if (decal_nested == (const RenderState *)NULL) {
decal_nested = RenderState::make

View File

@ -10,11 +10,13 @@
#define SOURCES \
config_egg2pg.h \
eggBinner.h \
qpeggLoader.h \
qpload_egg_file.h
#define INCLUDED_SOURCES \
config_egg2pg.cxx \
eggBinner.cxx \
qpeggLoader.cxx \
qpload_egg_file.cxx
@ -26,6 +28,7 @@
#define INSTALL_HEADERS \
config_egg2pg.h \
eggBinner.h \
qpeggLoader.h \
qpload_egg_file.h

View File

@ -1,2 +1,2 @@
#include "config_egg2pg.cxx"
#include "qpeggLoader.cxx"
#include "eggBinner.cxx"

View File

@ -1 +1,3 @@
#include "qpeggLoader.cxx"
#include "qpload_egg_file.cxx"

View File

@ -18,10 +18,9 @@
#include "eggBinner.h"
#include <eggSwitchCondition.h>
#include <eggGroup.h>
#include <assert.h>
#include "eggSwitchCondition.h"
#include "eggGroup.h"
#include "dcast.h"
////////////////////////////////////////////////////////////////////
// Function: EggBinner::get_bin_number

View File

@ -19,9 +19,9 @@
#ifndef EGGBINNER_H
#define EGGBINNER_H
#include <pandabase.h>
#include "pandabase.h"
#include <eggBinMaker.h>
#include "eggBinMaker.h"
///////////////////////////////////////////////////////////////////
// Class : EggBinner

View File

@ -35,6 +35,8 @@
#include "materialAttrib.h"
#include "materialPool.h"
#include "qpgeomNode.h"
#include "qpsequenceNode.h"
#include "qplodNode.h"
#include "string_utils.h"
#include "eggPrimitive.h"
#include "eggPoint.h"
@ -45,41 +47,40 @@
#include "eggPolygon.h"
#include "eggBin.h"
#include "eggTable.h"
#include "eggBinner.h"
#include "nodeChain.h"
#include <ctype.h>
#include <algorithm>
/*
// This class is used in make_node(EggBin *) to sort LOD instances in
// order by switching distance.
class LODInstance {
public:
LODInstance(EggNode *egg_node, RenderRelation *arc);
LODInstance(EggNode *egg_node);
bool operator < (const LODInstance &other) const {
return _d->_switch_in < other._d->_switch_in;
}
RenderRelation *_arc;
EggNode *_egg_node;
const EggSwitchConditionDistance *_d;
};
LODInstance::
LODInstance(EggNode *egg_node, RenderRelation *arc) {
assert(arc != NULL);
_arc = arc;
LODInstance(EggNode *egg_node) {
nassertv(egg_node != NULL);
_egg_node = egg_node;
// We expect this egg node to be an EggGroup with an LOD
// specification. That's what the EggBinner collected together,
// after all.
EggGroup *egg_group = DCAST(EggGroup, egg_node);
assert(egg_group->has_lod());
nassertv(egg_group->has_lod());
const EggSwitchCondition &sw = egg_group->get_lod();
// For now, this is the only kind of switch condition there is.
_d = DCAST(EggSwitchConditionDistance, &sw);
}
*/
////////////////////////////////////////////////////////////////////
@ -116,11 +117,9 @@ void qpEggLoader::
build_graph() {
// _deferred_arcs.clear();
/*
// First, bin up the LOD nodes.
EggBinner binner;
binner.make_bins(&_data);
*/
// Then load up all of the textures.
load_textures();
@ -1201,23 +1200,18 @@ make_node(EggPrimitive *egg_prim, PandaNode *parent) {
////////////////////////////////////////////////////////////////////
PandaNode *qpEggLoader::
make_node(EggBin *egg_bin, PandaNode *parent) {
return (PandaNode *)NULL;
/*
// Presently, an EggBin can only mean an LOD node (i.e. a parent of
// one or more EggGroups with LOD specifications). Later it might
// mean other things as well.
assert((EggBinner::BinNumber)egg_bin->get_bin_number() == EggBinner::BN_lod);
LODNode *lod_node = new LODNode;
lod_node->set_name(egg_bin->get_name());
nassertr((EggBinner::BinNumber)egg_bin->get_bin_number() == EggBinner::BN_lod, NULL);
qpLODNode *lod_node = new qpLODNode(egg_bin->get_name());
pvector<LODInstance> instances;
EggGroup::const_iterator ci;
for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
PandaNode *arc = make_node(*ci, lod_node);
assert(arc != (PandaNode *)NULL);
LODInstance instance(*ci, arc);
LODInstance instance(*ci);
instances.push_back(instance);
}
@ -1228,26 +1222,24 @@ make_node(EggBin *egg_bin, PandaNode *parent) {
if (!instances.empty()) {
// Set up the LOD node's center. All of the children should have
// the same center, because that's how we binned them.
lod_node->_lod._center = LCAST(float, instances[0]._d->_center);
lod_node->set_center(LCAST(float, instances[0]._d->_center));
}
for (size_t i = 0; i < instances.size(); i++) {
// Put the children in the proper order within the scene graph.
// Create the children in the proper order within the scene graph.
const LODInstance &instance = instances[i];
make_node(instance._egg_node, lod_node);
// All of the children should have the same center, because that's
// how we binned them.
assert(lod_node->_lod._center.almost_equal
(LCAST(float, instance._d->_center), 0.01));
instance._arc->set_sort(i);
nassertr(lod_node->get_center().almost_equal
(LCAST(float, instance._d->_center), 0.01), NULL);
// Tell the LOD node about this child's switching distances.
lod_node->add_switch(instance._d->_switch_in, instance._d->_switch_out);
}
return create_group_arc(egg_bin, parent, lod_node);
*/
}
@ -1379,16 +1371,14 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
} else if (egg_group->get_switch_flag() &&
egg_group->get_switch_fps() != 0.0) {
/*
// Create a sequence node.
node = new SequenceNode(1.0 / egg_group->get_switch_fps());
node->set_name(egg_group->get_name());
node = new qpSequenceNode(egg_group->get_switch_fps(),
egg_group->get_name());
EggGroup::const_iterator ci;
for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
make_node(*ci, node);
}
*/
} else if (egg_group->get_model_flag() || egg_group->get_dcs_flag()) {
// A model or DCS flag; create a model node.

View File

@ -13,13 +13,13 @@
#define SOURCES \
animBundleMaker.h characterMaker.h computedVerticesMaker.I \
computedVerticesMaker.h config_egg2sg.h \
deferredArcProperty.h deferredArcTraverser.h eggBinner.h \
deferredArcProperty.h deferredArcTraverser.h \
eggLoader.h load_egg_file.h loaderFileTypeEgg.h
#define INCLUDED_SOURCES \
animBundleMaker.cxx characterMaker.cxx computedVerticesMaker.cxx \
config_egg2sg.cxx deferredArcProperty.cxx \
deferredArcTraverser.cxx eggBinner.cxx eggLoader.cxx \
deferredArcTraverser.cxx eggLoader.cxx \
load_egg_file.cxx loaderFileTypeEgg.cxx
#define INSTALL_HEADERS \

View File

@ -1,6 +1,5 @@
#include "config_egg2sg.cxx"
#include "eggBinner.cxx"
#include "eggLoader.cxx"
#include "load_egg_file.cxx"
#include "loaderFileTypeEgg.cxx"

View File

@ -21,6 +21,7 @@
cullHandler.h cullHandler.I \
cullResult.h cullResult.I \
qpcullTraverser.h qpcullTraverser.I \
cullTraverserData.h cullTraverserData.I \
cullableObject.h cullableObject.I \
decalAttrib.h decalAttrib.I \
depthTestAttrib.h depthTestAttrib.I \
@ -28,12 +29,15 @@
drawCullHandler.h drawCullHandler.I \
qpgeomNode.h qpgeomNode.I \
qplensNode.h qplensNode.I \
qplodNode.h qplodNode.I \
materialAttrib.h materialAttrib.I \
nodeChain.h nodeChain.I \
nodeChainComponent.h nodeChainComponent.I \
pandaNode.h pandaNode.I \
renderAttrib.h renderAttrib.I \
renderState.h renderState.I \
selectiveChildNode.h selectiveChildNode.I \
qpsequenceNode.h qpsequenceNode.I \
textureApplyAttrib.h textureApplyAttrib.I \
textureAttrib.h textureAttrib.I \
transformState.h transformState.I \
@ -56,6 +60,7 @@
cullHandler.cxx \
cullResult.cxx \
qpcullTraverser.cxx \
cullTraverserData.cxx \
cullableObject.cxx \
decalAttrib.cxx \
depthTestAttrib.cxx \
@ -63,12 +68,15 @@
drawCullHandler.cxx \
qpgeomNode.cxx \
qplensNode.cxx \
qplodNode.cxx \
materialAttrib.cxx \
nodeChain.cxx \
nodeChainComponent.cxx \
pandaNode.cxx \
renderAttrib.cxx \
renderState.cxx \
selectiveChildNode.cxx \
qpsequenceNode.cxx \
textureApplyAttrib.cxx \
textureAttrib.cxx \
transformState.cxx \
@ -96,6 +104,7 @@
cullHandler.h cullHandler.I \
cullResult.h cullResult.I \
qpcullTraverser.h qpcullTraverser.I \
cullTraverserData.h cullTraverserData.I \
cullableObject.h cullableObject.I \
decalAttrib.h decalAttrib.I \
depthTestAttrib.h depthTestAttrib.I \
@ -103,12 +112,15 @@
drawCullHandler.h drawCullHandler.I \
qpgeomNode.h qpgeomNode.I \
qplensNode.h qplensNode.I \
qplodNode.h qplodNode.I \
materialAttrib.h materialAttrib.I \
nodeChain.h nodeChain.I \
nodeChainComponent.h nodeChainComponent.I \
pandaNode.h pandaNode.I \
renderAttrib.h renderAttrib.I \
renderState.h renderState.I \
selectiveChildNode.h selectiveChildNode.I \
qpsequenceNode.h qpsequenceNode.I \
textureApplyAttrib.h textureApplyAttrib.I \
textureAttrib.h textureAttrib.I \
transformState.h transformState.I \

View File

@ -33,12 +33,15 @@
#include "depthWriteAttrib.h"
#include "qpgeomNode.h"
#include "qplensNode.h"
#include "qplodNode.h"
#include "materialAttrib.h"
#include "nodeChain.h"
#include "nodeChainComponent.h"
#include "pandaNode.h"
#include "renderAttrib.h"
#include "renderState.h"
#include "selectiveChildNode.h"
#include "qpsequenceNode.h"
#include "textureApplyAttrib.h"
#include "textureAttrib.h"
#include "transformState.h"
@ -90,18 +93,22 @@ init_libpgraph() {
DepthWriteAttrib::init_type();
qpGeomNode::init_type();
qpLensNode::init_type();
qpLODNode::init_type();
MaterialAttrib::init_type();
NodeChain::init_type();
NodeChainComponent::init_type();
PandaNode::init_type();
RenderAttrib::init_type();
RenderState::init_type();
SelectiveChildNode::init_type();
qpSequenceNode::init_type();
TextureApplyAttrib::init_type();
TextureAttrib::init_type();
TransformState::init_type();
TransparencyAttrib::init_type();
BillboardAttrib::register_with_read_factory();
qpCamera::register_with_read_factory();
ColorAttrib::register_with_read_factory();
ColorWriteAttrib::register_with_read_factory();
CullBinAttrib::register_with_read_factory();
@ -110,9 +117,12 @@ init_libpgraph() {
DepthTestAttrib::register_with_read_factory();
DepthWriteAttrib::register_with_read_factory();
qpGeomNode::register_with_read_factory();
qpLensNode::register_with_read_factory();
qpLODNode::register_with_read_factory();
MaterialAttrib::register_with_read_factory();
PandaNode::register_with_read_factory();
RenderState::register_with_read_factory();
qpSequenceNode::register_with_read_factory();
TextureApplyAttrib::register_with_read_factory();
TextureAttrib::register_with_read_factory();
TransformState::register_with_read_factory();

View File

@ -0,0 +1,97 @@
// Filename: cullTraverserData.I
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE CullTraverserData::
CullTraverserData(const TransformState *render_transform,
const TransformState *net_transform,
const RenderState *state,
GeometricBoundingVolume *view_frustum,
GeometricBoundingVolume *guard_band,
const TransformState *camera_transform) :
_render_transform(render_transform),
_net_transform(net_transform),
_state(state),
_view_frustum(view_frustum),
_guard_band(guard_band),
_camera_transform(camera_transform)
{
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE CullTraverserData::
CullTraverserData(const CullTraverserData &copy) :
_render_transform(copy._render_transform),
_net_transform(copy._net_transform),
_state(copy._state),
_view_frustum(copy._view_frustum),
_guard_band(copy._guard_band),
_camera_transform(copy._camera_transform)
{
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void CullTraverserData::
operator = (const CullTraverserData &copy) {
_render_transform = copy._render_transform;
_net_transform = copy._net_transform;
_state = copy._state;
_view_frustum = copy._view_frustum;
_guard_band = copy._guard_band;
_camera_transform = copy._camera_transform;
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE CullTraverserData::
~CullTraverserData() {
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::is_in_view
// Access: Public
// Description: Returns true if the node is within the view frustum,
// false otherwise. If the node's bounding volume falls
// completely within the view frustum, this will also
// reset the view frustum pointer, saving some work for
// future nodes.
////////////////////////////////////////////////////////////////////
INLINE bool CullTraverserData::
is_in_view(PandaNode *node) {
if (_view_frustum == (GeometricBoundingVolume *)NULL) {
// If we don't have a frustum, we're always in.
return true;
}
return is_in_view_impl(node);
}

View File

@ -0,0 +1,158 @@
// Filename: cullTraverserData.cxx
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "cullTraverserData.h"
#include "config_pgraph.h"
#include "pandaNode.h"
#include "colorAttrib.h"
#include "textureAttrib.h"
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::apply_transform_and_state
// Access: Public
// Description: Applies the transform and state from the indicated
// node onto the current data. This also evaluates
// billboards, etc.
////////////////////////////////////////////////////////////////////
void CullTraverserData::
apply_transform_and_state(PandaNode *node) {
const TransformState *node_transform = node->get_transform();
if (!node_transform->is_identity()) {
_render_transform = _render_transform->compose(node_transform);
_net_transform = _net_transform->compose(node_transform);
if ((_view_frustum != (GeometricBoundingVolume *)NULL) ||
(_guard_band != (GeometricBoundingVolume *)NULL)) {
// We need to move the viewing frustums into the node's
// coordinate space by applying the node's inverse transform.
if (node_transform->is_singular()) {
// But we can't invert a singular transform! Instead of
// trying, we'll just give up on frustum culling from this
// point down.
_view_frustum = (GeometricBoundingVolume *)NULL;
_guard_band = (GeometricBoundingVolume *)NULL;
} else {
CPT(TransformState) inv_transform =
node_transform->invert_compose(TransformState::make_identity());
// Copy the bounding volumes for the frustums so we can
// transform them.
if (_view_frustum != (GeometricBoundingVolume *)NULL) {
_view_frustum = DCAST(GeometricBoundingVolume, _view_frustum->make_copy());
_view_frustum->xform(inv_transform->get_mat());
}
if (_guard_band != (GeometricBoundingVolume *)NULL) {
_guard_band = DCAST(GeometricBoundingVolume, _guard_band->make_copy());
_guard_band->xform(inv_transform->get_mat());
}
}
}
}
const RenderState *node_state = node->get_state();
_state = _state->compose(node_state);
const BillboardAttrib *billboard = node_state->get_billboard();
if (billboard != (const BillboardAttrib *)NULL) {
// Got to apply a billboard transform here.
CPT(TransformState) billboard_transform =
billboard->do_billboard(_net_transform, _camera_transform);
_render_transform = _render_transform->compose(billboard_transform);
_net_transform = _net_transform->compose(billboard_transform);
// We can't reliably cull within a billboard, because the geometry
// might get rotated out of its bounding volume. So once we get
// within a billboard, we consider it all visible.
_view_frustum = (GeometricBoundingVolume *)NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::is_in_view_impl
// Access: Private
// Description: The private implementation of is_in_view().
////////////////////////////////////////////////////////////////////
bool CullTraverserData::
is_in_view_impl(PandaNode *node) {
// By the time we get here, we know we have a viewing frustum.
nassertr(_view_frustum != (GeometricBoundingVolume *)NULL, true);
const BoundingVolume &node_volume = node->get_bound();
nassertr(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()), false);
const GeometricBoundingVolume *node_gbv =
DCAST(GeometricBoundingVolume, &node_volume);
int result = _view_frustum->contains(node_gbv);
if (result == BoundingVolume::IF_no_intersection) {
// No intersection at all. Cull.
if (!qpfake_view_frustum_cull) {
return false;
}
// If we have fake view-frustum culling enabled, instead of
// actually culling an object we simply force it to be drawn in
// red wireframe.
_view_frustum = (GeometricBoundingVolume *)NULL;
CPT(RenderState) fake_effect = get_fake_view_frustum_cull_effect();
_state = _state->compose(fake_effect);
} else if ((result & BoundingVolume::IF_all) != 0) {
// The node and its descendants are completely enclosed within
// the frustum. No need to cull further.
_view_frustum = (GeometricBoundingVolume *)NULL;
} else {
if (node->is_final()) {
// The bounding volume is partially, but not completely,
// within the viewing frustum. Normally we'd keep testing
// child bounding volumes as we continue down. But this node
// has the "final" flag, so the user is claiming that there is
// some important reason we should consider everything visible
// at this point. So be it.
_view_frustum = (GeometricBoundingVolume *)NULL;
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: CullTraverserData::get_fake_view_frustum_cull_effect
// Access: Private, Static
// Description: Returns a RenderState for rendering stuff in red
// wireframe, strictly for the fake_view_frustum_cull
// effect.
////////////////////////////////////////////////////////////////////
CPT(RenderState) CullTraverserData::
get_fake_view_frustum_cull_effect() {
// Once someone asks for this pointer, we hold its reference count
// and never free it.
static CPT(RenderState) effect = (const RenderState *)NULL;
if (effect == (const RenderState *)NULL) {
effect = RenderState::make
(ColorAttrib::make_flat(Colorf(1.0f, 0.0f, 0.0f, 1.0f)),
TextureAttrib::make_off(),
1000);
}
return effect;
}

View File

@ -0,0 +1,79 @@
// Filename: cullTraverserData.h
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef CULLTRAVERSERDATA_H
#define CULLTRAVERSERDATA_H
#include "pandabase.h"
#include "renderState.h"
#include "transformState.h"
#include "geometricBoundingVolume.h"
#include "pointerTo.h"
class PandaNode;
////////////////////////////////////////////////////////////////////
// Class : CullTraverserData
// Description : This collects together the pieces of data that are
// accumulated for each node while walking the scene
// graph during the cull traversal.
//
// Having this as a separate object simplifies the
// parameter list to CullTraverser::r_traverse(), as
// well as to other functions like
// PandaNode::cull_callback(). It also makes it easier
// to add cull parameters, and provides a place to
// abstract out some of the cull behavior (like
// view-frustum culling).
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA CullTraverserData {
public:
INLINE CullTraverserData(const TransformState *render_transform,
const TransformState *net_transform,
const RenderState *state,
GeometricBoundingVolume *view_frustum,
GeometricBoundingVolume *guard_band,
const TransformState *camera_transform);
INLINE CullTraverserData(const CullTraverserData &copy);
INLINE void operator = (const CullTraverserData &copy);
INLINE ~CullTraverserData();
INLINE bool is_in_view(PandaNode *node);
void apply_transform_and_state(PandaNode *node);
CPT(TransformState) _render_transform;
CPT(TransformState) _net_transform;
CPT(RenderState) _state;
PT(GeometricBoundingVolume) _view_frustum;
PT(GeometricBoundingVolume) _guard_band;
// This one is not modified during traversal, so it doesn't need to
// be reference counted (we trust the original owner of this pointer
// to reference count it and hold it during the lifetime of the
// traversal).
const TransformState *_camera_transform;
private:
bool is_in_view_impl(PandaNode *node);
static CPT(RenderState) get_fake_view_frustum_cull_effect();
};
#include "cullTraverserData.I"
#endif

View File

@ -19,7 +19,8 @@
////////////////////////////////////////////////////////////////////
// Function: CullableObject::Constructor
// Access: Public
// Description:
// Description: Creates an empty CullableObject whose pointers can be
// filled in later.
////////////////////////////////////////////////////////////////////
INLINE CullableObject::
CullableObject(CullableObject *next) :
@ -27,6 +28,25 @@ CullableObject(CullableObject *next) :
{
}
////////////////////////////////////////////////////////////////////
// Function: CullableObject::Constructor
// Access: Public
// Description: Creates a CullableObject based on the ith Geom from
// the indicated GeomNode, with the render state from
// the indicated CullTraverserData.
////////////////////////////////////////////////////////////////////
INLINE CullableObject::
CullableObject(const CullTraverserData &data,
qpGeomNode *geom_node, int i,
CullableObject *next) :
_geom(geom_node->get_geom(i)),
_state(data._state->compose(geom_node->get_geom_state(i))),
_transform(data._render_transform),
_next(next)
{
}
////////////////////////////////////////////////////////////////////
// Function: CullableObject::Copy Constructor
// Access: Private

View File

@ -26,6 +26,8 @@
#include "transformState.h"
#include "pointerTo.h"
#include "referenceCount.h"
#include "qpgeomNode.h"
#include "cullTraverserData.h"
////////////////////////////////////////////////////////////////////
// Class : CullableObject
@ -37,6 +39,14 @@
class EXPCL_PANDA CullableObject {
public:
INLINE CullableObject(CullableObject *next = NULL);
INLINE CullableObject(const CullTraverserData &data,
qpGeomNode *geom_node, int i,
CullableObject *next = NULL);
// We will allocate and destroy hundreds or thousands of these a
// frame during the normal course of rendering. As an optimization,
// then, we should consider implementing operator new and delete
// here to minimize this overhead. Should be simple.
private:
INLINE CullableObject(const CullableObject &copy);

View File

@ -234,6 +234,97 @@ combine_with(PandaNode *other) {
return (PandaNode *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::has_cull_callback
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if cull_callback() has been defined. Otherwise,
// returns false to indicate cull_callback() does not
// need to be called for this node during the cull
// traversal.
////////////////////////////////////////////////////////////////////
bool PandaNode::
has_cull_callback() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::cull_callback
// Access: Public, Virtual
// Description: If has_cull_callback() returns true, 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.
//
// 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 PandaNode::
cull_callback(CullTraverserData &) {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::has_selective_visibility
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if this kind of node has some restrictions on
// the set of children that should be rendered. Node
// with this property include LODNodes, SwitchNodes, and
// SequenceNodes.
//
// If this function returns true,
// get_first_visible_child() and
// get_next_visible_child() will be called to walk
// through the list of children during cull, instead of
// iterating through the entire list. This method is
// called after cull_callback(), so cull_callback() may
// be responsible for the decisions as to which children
// are visible at the moment.
////////////////////////////////////////////////////////////////////
bool PandaNode::
has_selective_visibility() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_first_visible_child
// Access: Public, Virtual
// Description: Returns the index number of the first visible child
// of this node, or a number >= get_num_children() if
// there are no visible children of this node. This is
// called during the cull traversal, but only if
// has_selective_visibility() has already returned true.
// See has_selective_visibility().
////////////////////////////////////////////////////////////////////
int PandaNode::
get_first_visible_child() const {
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_next_visible_child
// Access: Public, Virtual
// Description: Returns the index number of the next visible child
// of this node following the indicated child, or a
// number >= get_num_children() if there are no more
// visible children of this node. See
// has_selective_visibility() and
// get_first_visible_child().
////////////////////////////////////////////////////////////////////
int PandaNode::
get_next_visible_child(int n) const {
return n + 1;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::find_child
// Access: Published

View File

@ -38,6 +38,7 @@
#include "notify.h"
class NodeChainComponent;
class CullTraverserData;
////////////////////////////////////////////////////////////////////
// Class : PandaNode
@ -65,6 +66,12 @@ public:
virtual void xform(const LMatrix4f &mat);
virtual PandaNode *combine_with(PandaNode *other);
virtual bool has_cull_callback() const;
virtual bool cull_callback(CullTraverserData &data);
virtual bool has_selective_visibility() const;
virtual int get_first_visible_child() const;
virtual int get_next_visible_child(int n) const;
PUBLISHED:
INLINE int get_num_parents() const;
INLINE PandaNode *get_parent(int n) const;

View File

@ -13,5 +13,6 @@
#include "cullHandler.cxx"
#include "cullResult.cxx"
#include "qpcullTraverser.cxx"
#include "cullTraverserData.cxx"
#include "cullableObject.cxx"

View File

@ -4,12 +4,15 @@
#include "drawCullHandler.cxx"
#include "qpgeomNode.cxx"
#include "qplensNode.cxx"
#include "qplodNode.cxx"
#include "materialAttrib.cxx"
#include "nodeChain.cxx"
#include "nodeChainComponent.cxx"
#include "pandaNode.cxx"
#include "renderAttrib.cxx"
#include "renderState.cxx"
#include "selectiveChildNode.cxx"
#include "qpsequenceNode.cxx"
#include "test_pgraph.cxx"
#include "textureApplyAttrib.cxx"
#include "textureAttrib.cxx"

View File

@ -141,3 +141,57 @@ remove_display_region(DisplayRegion *display_region) {
_display_regions.erase(dri);
}
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// qpCamera.
////////////////////////////////////////////////////////////////////
void qpCamera::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void qpCamera::
write_datagram(BamWriter *manager, Datagram &dg) {
qpLensNode::write_datagram(manager, dg);
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type qpCamera is encountered
// in the Bam file. It should create the qpCamera
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *qpCamera::
make_from_bam(const FactoryParams &params) {
qpCamera *node = new qpCamera("");
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
node->fillin(scan, manager);
return node;
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::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 qpCamera.
////////////////////////////////////////////////////////////////////
void qpCamera::
fillin(DatagramIterator &scan, BamReader *manager) {
qpLensNode::fillin(scan, manager);
}

View File

@ -16,8 +16,8 @@
//
////////////////////////////////////////////////////////////////////
#ifndef QPCAMERA_H
#define QPCAMERA_H
#ifndef qpCAMERA_H
#define qpCAMERA_H
#include "pandabase.h"
@ -65,6 +65,14 @@ private:
typedef pvector<DisplayRegion *> DisplayRegions;
DisplayRegions _display_regions;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;

View File

@ -17,14 +17,13 @@
////////////////////////////////////////////////////////////////////
#include "qpcullTraverser.h"
#include "cullTraverserData.h"
#include "transformState.h"
#include "renderState.h"
#include "billboardAttrib.h"
#include "cullHandler.h"
#include "dcast.h"
#include "qpgeomNode.h"
#include "colorAttrib.h"
#include "textureAttrib.h"
#include "config_pgraph.h"
////////////////////////////////////////////////////////////////////
@ -130,8 +129,10 @@ void qpCullTraverser::
traverse(PandaNode *root) {
nassertv(_cull_handler != (CullHandler *)NULL);
r_traverse(root, _render_transform, TransformState::make_identity(),
_initial_state, _view_frustum, _guard_band);
CullTraverserData data(_render_transform, TransformState::make_identity(),
_initial_state, _view_frustum, _guard_band,
_camera_transform);
r_traverse(root, data);
}
////////////////////////////////////////////////////////////////////
@ -140,135 +141,56 @@ traverse(PandaNode *root) {
// Description: The recursive traversal implementation.
////////////////////////////////////////////////////////////////////
void qpCullTraverser::
r_traverse(PandaNode *node,
const TransformState *render_transform,
const TransformState *net_transform,
const RenderState *state,
GeometricBoundingVolume *view_frustum,
GeometricBoundingVolume *guard_band) {
CPT(RenderState) next_state = state;
r_traverse(PandaNode *node, const CullTraverserData &data) {
CullTraverserData next_data(data);
if (view_frustum != (GeometricBoundingVolume *)NULL) {
// If we have a viewing frustum, check to see if the node's
// bounding volume falls within it.
const BoundingVolume &node_volume = node->get_bound();
nassertv(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()));
const GeometricBoundingVolume *node_gbv =
DCAST(GeometricBoundingVolume, &node_volume);
// Most nodes will have no transform or state, and will not
// contain decals or require a special cull callback. As an
// optimization, we should tag nodes with these properties as
// being "fancy", and skip this processing for non-fancy nodes.
int result = view_frustum->contains(node_gbv);
if (result == BoundingVolume::IF_no_intersection) {
// No intersection at all. Cull.
if (!qpfake_view_frustum_cull) {
if (next_data.is_in_view(node)) {
next_data.apply_transform_and_state(node);
if (node->has_cull_callback()) {
if (!node->cull_callback(next_data)) {
return;
}
// If we have fake view-frustum culling enabled, instead of
// actually culling an object we simply force it to be drawn in
// red wireframe.
view_frustum = (GeometricBoundingVolume *)NULL;
CPT(RenderState) fake_effect = get_fake_view_frustum_cull_effect();
next_state = next_state->compose(fake_effect);
} else if ((result & BoundingVolume::IF_all) != 0) {
// The node and its descendants are completely enclosed within
// the frustum. No need to cull further.
view_frustum = (GeometricBoundingVolume *)NULL;
} else {
if (node->is_final()) {
// The bounding volume is partially, but not completely,
// within the viewing frustum. Normally we'd keep testing
// child bounding volumes as we continue down. But this node
// has the "final" flag, so the user is claiming that there is
// some important reason we should consider everything visible
// at this point. So be it.
view_frustum = (GeometricBoundingVolume *)NULL;
}
}
}
CPT(TransformState) next_render_transform = render_transform;
CPT(TransformState) next_net_transform = net_transform;
PT(GeometricBoundingVolume) next_view_frustum = view_frustum;
PT(GeometricBoundingVolume) next_guard_band = guard_band;
const RenderState *node_state = node->get_state();
if (node_state->has_decal()) {
start_decal(node, next_data);
} else {
if (node->is_geom_node()) {
qpGeomNode *geom_node = DCAST(qpGeomNode, node);
// Get all the Geoms, with no decalling.
int num_geoms = geom_node->get_num_geoms();
for (int i = 0; i < num_geoms; i++) {
CullableObject *object = new CullableObject(next_data, geom_node, i);
_cull_handler->record_object(object);
}
}
const TransformState *node_transform = node->get_transform();
if (!node_transform->is_identity()) {
next_render_transform = render_transform->compose(node_transform);
next_net_transform = net_transform->compose(node_transform);
if ((view_frustum != (GeometricBoundingVolume *)NULL) ||
(guard_band != (GeometricBoundingVolume *)NULL)) {
// We need to move the viewing frustums into the node's
// coordinate space by applying the node's inverse transform.
if (node_transform->is_singular()) {
// But we can't invert a singular transform! Instead of
// trying, we'll just give up on frustum culling from this
// point down.
view_frustum = (GeometricBoundingVolume *)NULL;
guard_band = (GeometricBoundingVolume *)NULL;
// Now visit all the node's children.
PandaNode::Children cr = node->get_children();
int num_children = cr.get_num_children();
if (node->has_selective_visibility()) {
int i = node->get_first_visible_child();
while (i < num_children) {
r_traverse(cr.get_child(i), next_data);
i = node->get_next_visible_child(i);
}
} else {
CPT(TransformState) inv_transform =
node_transform->invert_compose(TransformState::make_identity());
if (view_frustum != (GeometricBoundingVolume *)NULL) {
next_view_frustum = DCAST(GeometricBoundingVolume, view_frustum->make_copy());
next_view_frustum->xform(inv_transform->get_mat());
}
if (guard_band != (GeometricBoundingVolume *)NULL) {
next_guard_band = DCAST(GeometricBoundingVolume, guard_band->make_copy());
next_guard_band->xform(inv_transform->get_mat());
for (int i = 0; i < num_children; i++) {
r_traverse(cr.get_child(i), next_data);
}
}
}
}
const RenderState *node_state = node->get_state();
next_state = next_state->compose(node_state);
const BillboardAttrib *billboard = node_state->get_billboard();
if (billboard != (const BillboardAttrib *)NULL) {
// Got to apply a billboard transform here.
CPT(TransformState) billboard_transform =
billboard->do_billboard(net_transform, _camera_transform);
next_render_transform = next_render_transform->compose(billboard_transform);
next_net_transform = next_net_transform->compose(billboard_transform);
// We can't reliably cull within a billboard, because the geometry
// might get rotated out of its bounding volume. So once we get
// within a billboard, we consider it all visible.
next_view_frustum = (GeometricBoundingVolume *)NULL;
}
if (node_state->has_decal()) {
start_decal(node, next_render_transform, next_state);
} else {
if (node->is_geom_node()) {
qpGeomNode *geom_node = DCAST(qpGeomNode, node);
// Get all the Geoms, with no decalling.
int num_geoms = geom_node->get_num_geoms();
for (int i = 0; i < num_geoms; i++) {
CullableObject *object = new CullableObject;
object->_geom = geom_node->get_geom(i);
object->_state = next_state->compose(geom_node->get_geom_state(i));
object->_transform = next_render_transform;
_cull_handler->record_object(object);
}
}
// Now visit all the node's children.
PandaNode::Children cr = node->get_children();
int num_children = cr.get_num_children();
for (int i = 0; i < num_children; i++) {
r_traverse(cr.get_child(i), next_render_transform, next_net_transform,
next_state, next_view_frustum, next_guard_band);
}
}
}
////////////////////////////////////////////////////////////////////
@ -276,16 +198,10 @@ r_traverse(PandaNode *node,
// Access: Private
// Description: Collects a base node and all of the decals applied to
// it. This involves recursing below the base GeomNode
// to find all the decal geoms; we don't bother to apply
// any view-frustum culling at this point, and we don't
// presently support billboards or LOD's within the
// decals. Hard to justify the duplicate code this
// would require.
// to find all the decal geoms.
////////////////////////////////////////////////////////////////////
void qpCullTraverser::
start_decal(PandaNode *node,
const TransformState *render_transform,
const RenderState *state) {
start_decal(PandaNode *node, const CullTraverserData &data) {
if (!node->is_geom_node()) {
pgraph_cat.error()
<< "DecalAttrib applied to " << *node << ", not a GeomNode.\n";
@ -297,17 +213,22 @@ start_decal(PandaNode *node,
// CullableObject node, followed by all of the decal Geoms, in
// order.
const TransformState *next_render_transform = render_transform;
const RenderState *next_state = state;
// Since the CullableObject is a linked list which gets built in
// LIFO order, we start with the decals.
CullableObject *decals = (CullableObject *)NULL;
PandaNode::Children cr = node->get_children();
int num_children = cr.get_num_children();
for (int i = num_children - 1; i >= 0; i--) {
decals =
r_get_decals(cr.get_child(i), next_render_transform, next_state, decals);
if (node->has_selective_visibility()) {
int i = node->get_first_visible_child();
while (i < num_children) {
decals = r_get_decals(cr.get_child(i), data, decals);
i = node->get_next_visible_child(i);
}
} else {
for (int i = num_children - 1; i >= 0; i--) {
decals = r_get_decals(cr.get_child(i), data, decals);
}
}
// Now create a new, empty CullableObject to separate the decals
@ -317,14 +238,9 @@ start_decal(PandaNode *node,
// And now get the base Geoms, again in reverse order.
CullableObject *object = separator;
qpGeomNode *geom_node = DCAST(qpGeomNode, node);
// Get all the Geoms, with no decalling.
int num_geoms = geom_node->get_num_geoms();
for (int i = num_geoms - 1; i >= 0; i--) {
object = new CullableObject(object);
object->_geom = geom_node->get_geom(i);
object->_state = next_state->compose(geom_node->get_geom_state(i));
object->_transform = next_render_transform;
object = new CullableObject(data, geom_node, i, object);
}
if (object != separator) {
@ -345,60 +261,39 @@ start_decal(PandaNode *node,
// they were encountered in the scene graph).
////////////////////////////////////////////////////////////////////
CullableObject *qpCullTraverser::
r_get_decals(PandaNode *node,
const TransformState *render_transform,
const RenderState *state,
r_get_decals(PandaNode *node, const CullTraverserData &data,
CullableObject *decals) {
const TransformState *node_transform = node->get_transform();
const RenderState *node_state = node->get_state();
CullTraverserData next_data(data);
CPT(TransformState) next_render_transform =
render_transform->compose(node_transform);
CPT(RenderState) next_state =
state->compose(node_state);
if (next_data.is_in_view(node)) {
next_data.apply_transform_and_state(node);
// First, visit all of the node's children.
PandaNode::Children cr = node->get_children();
int num_children = cr.get_num_children();
for (int i = num_children - 1; i >= 0; i--) {
decals =
r_get_decals(cr.get_child(i), next_render_transform, next_state, decals);
}
// First, visit all of the node's children.
PandaNode::Children cr = node->get_children();
int num_children = cr.get_num_children();
if (node->has_selective_visibility()) {
int i = node->get_first_visible_child();
while (i < num_children) {
decals = r_get_decals(cr.get_child(i), next_data, decals);
i = node->get_next_visible_child(i);
}
} else {
for (int i = num_children - 1; i >= 0; i--) {
decals = r_get_decals(cr.get_child(i), next_data, decals);
}
}
// Now, tack on any geoms within the node.
if (node->is_geom_node()) {
qpGeomNode *geom_node = DCAST(qpGeomNode, node);
int num_geoms = geom_node->get_num_geoms();
for (int i = num_geoms - 1; i >= 0; i--) {
decals = new CullableObject(decals);
decals->_geom = geom_node->get_geom(i);
decals->_state = next_state->compose(geom_node->get_geom_state(i));
decals->_transform = next_render_transform;
// Now, tack on any geoms within the node.
if (node->is_geom_node()) {
qpGeomNode *geom_node = DCAST(qpGeomNode, node);
int num_geoms = geom_node->get_num_geoms();
for (int i = num_geoms - 1; i >= 0; i--) {
decals = new CullableObject(next_data, geom_node, i, decals);
}
}
}
return decals;
}
////////////////////////////////////////////////////////////////////
// Function: qpCullTraverser::get_fake_view_frustum_cull_effect
// Access: Private, Static
// Description: Returns a RenderState for rendering stuff in red
// wireframe, strictly for the fake_view_frustum_cull
// effect.
////////////////////////////////////////////////////////////////////
CPT(RenderState) qpCullTraverser::
get_fake_view_frustum_cull_effect() {
// Once someone asks for this pointer, we hold its reference count
// and never free it.
static CPT(RenderState) effect = (const RenderState *)NULL;
if (effect == (const RenderState *)NULL) {
effect = RenderState::make
(ColorAttrib::make_flat(Colorf(1.0f, 0.0f, 0.0f, 1.0f)),
TextureAttrib::make_off(),
1000);
}
return effect;
}

View File

@ -28,6 +28,7 @@
class PandaNode;
class CullHandler;
class CullTraverserData;
////////////////////////////////////////////////////////////////////
// Class : CullTraverser
@ -51,21 +52,12 @@ public:
void traverse(PandaNode *root);
private:
void r_traverse(PandaNode *node, const TransformState *render_transform,
const TransformState *net_transform,
const RenderState *state,
GeometricBoundingVolume *view_frustum,
GeometricBoundingVolume *guard_band);
void start_decal(PandaNode *node,
const TransformState *render_transform,
const RenderState *state);
CullableObject *r_get_decals(PandaNode *node,
const TransformState *render_transform,
const RenderState *state,
void r_traverse(PandaNode *node, const CullTraverserData &data);
void start_decal(PandaNode *node, const CullTraverserData &data);
CullableObject *r_get_decals(PandaNode *node,
const CullTraverserData &data,
CullableObject *decals);
static CPT(RenderState) get_fake_view_frustum_cull_effect();
CPT(RenderState) _initial_state;
CPT(TransformState) _camera_transform;
CPT(TransformState) _render_transform;

View File

@ -80,3 +80,70 @@ write(ostream &out, int indent_level) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// qpLensNode.
////////////////////////////////////////////////////////////////////
void qpLensNode::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void qpLensNode::
write_datagram(BamWriter *manager, Datagram &dg) {
PandaNode::write_datagram(manager, dg);
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::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 qpLensNode::
complete_pointers(TypedWritable **p_list, BamReader *manager) {
int pi = PandaNode::complete_pointers(p_list, manager);
return pi;
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type qpLensNode is encountered
// in the Bam file. It should create the qpLensNode
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *qpLensNode::
make_from_bam(const FactoryParams &params) {
qpLensNode *node = new qpLensNode("");
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
node->fillin(scan, manager);
return node;
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::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 qpLensNode.
////////////////////////////////////////////////////////////////////
void qpLensNode::
fillin(DatagramIterator &scan, BamReader *manager) {
PandaNode::fillin(scan, manager);
}

View File

@ -16,8 +16,8 @@
//
////////////////////////////////////////////////////////////////////
#ifndef QPLENSNODE_H
#define QPLENSNODE_H
#ifndef qpLENSNODE_H
#define qpLENSNODE_H
#include "pandabase.h"
@ -55,6 +55,16 @@ PUBLISHED:
protected:
PT(Lens) _lens;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
virtual int complete_pointers(TypedWritable **plist,
BamReader *manager);
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;

View File

@ -0,0 +1,163 @@
// Filename: qplodNode.I
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::CData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpLODNode::CData::
CData() {
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::CData::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpLODNode::CData::
CData(const qpLODNode::CData &copy) :
_lod(copy._lod)
{
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpLODNode::
qpLODNode(const string &name) :
SelectiveChildNode(name)
{
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::add_switch
// Access: Published
// Description: Adds a switch range to the qpLODNode. This implies
// that the corresponding child node has been parented
// to the node.
//
// The sense of in vs. out distances is as if the object
// were coming towards you from far away: it switches
// "in" at the far distance, and switches "out" at the
// close distance. Thus, "in" should be larger than
// "out".
////////////////////////////////////////////////////////////////////
INLINE void qpLODNode::
add_switch(float in, float out) {
CDWriter cdata(_cycler);
cdata->_lod._switch_vector.push_back(LODSwitch(in, out));
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::set_switch
// Access: Published
// Description: Changes the switching range of a particular child of
// the qpLODNode. See add_switch().
////////////////////////////////////////////////////////////////////
INLINE bool qpLODNode::
set_switch(int index, float in, float out) {
CDWriter cdata(_cycler);
nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), false);
cdata->_lod._switch_vector[index].set_range(in, out);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::clear_switches
// Access: Published
// Description: Removes the set of switching ranges for the qpLODNode,
// presumably in conjunction with removing all of its
// children. See add_switch().
////////////////////////////////////////////////////////////////////
INLINE void qpLODNode::
clear_switches(void) {
CDWriter cdata(_cycler);
cdata->_lod._switch_vector.erase(cdata->_lod._switch_vector.begin(),
cdata->_lod._switch_vector.end());
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::get_num_switches
// Access: Published
// Description: Returns the number of switch ranges added to the
// qpLODNode. This should correspond to the number of
// children of the node in order for the qpLODNode to
// function correctly.
////////////////////////////////////////////////////////////////////
INLINE int qpLODNode::
get_num_switches() const {
CDReader cdata(_cycler);
return cdata->_lod._switch_vector.size();
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::get_in
// Access: Published
// Description: Returns the "in" distance of the indicated switch
// range. This should be larger than the "out" distance
// of the same range.
////////////////////////////////////////////////////////////////////
INLINE float qpLODNode::
get_in(int index) const {
CDReader cdata(_cycler);
nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), 0.0);
return cdata->_lod._switch_vector[index].get_in();
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::get_out
// Access: Published
// Description: Returns the "out" distance of the indicated switch
// range. This should be smaller than the "in" distance
// of the same range.
////////////////////////////////////////////////////////////////////
INLINE float qpLODNode::
get_out(int index) const {
CDReader cdata(_cycler);
nassertr(index >= 0 && index < (int)cdata->_lod._switch_vector.size(), 0.0);
return cdata->_lod._switch_vector[index].get_out();
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::set_center
// Access: Published
// Description: Specifies the center of the LOD. This is the point
// that is compared to the camera (in camera space) to
// determine the particular LOD that should be chosen.
////////////////////////////////////////////////////////////////////
INLINE void qpLODNode::
set_center(const LPoint3f &center) {
CDWriter cdata(_cycler);
cdata->_lod._center = center;
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::get_center
// Access: Published
// Description: Returns the center of the LOD. This is the point
// that is compared to the camera (in camera space) to
// determine the particular LOD that should be chosen.
////////////////////////////////////////////////////////////////////
INLINE const LPoint3f &qpLODNode::
get_center() const {
CDReader cdata(_cycler);
return cdata->_lod._center;
}

View File

@ -0,0 +1,218 @@
// Filename: qplodNode.cxx
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "qplodNode.h"
#include "cullTraverserData.h"
TypeHandle qpLODNode::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::CData::make_copy
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CycleData *qpLODNode::CData::
make_copy() const {
return new CData(*this);
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
qpLODNode::
qpLODNode(const qpLODNode &copy) :
SelectiveChildNode(copy)
{
CDWriter cdata(_cycler);
CDReader cdata_copy(copy._cycler);
cdata->_lod = cdata_copy->_lod;
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void qpLODNode::
operator = (const qpLODNode &copy) {
SelectiveChildNode::operator = (copy);
CDWriter cdata(_cycler);
CDReader cdata_copy(copy._cycler);
cdata->_lod = cdata_copy->_lod;
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::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 *qpLODNode::
make_copy() const {
return new qpLODNode(*this);
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::xform
// Access: Public, Virtual
// Description: Transforms the contents of this PandaNode by the
// indicated matrix, if it means anything to do so. For
// most kinds of PandaNodes, this does nothing.
////////////////////////////////////////////////////////////////////
void qpLODNode::
xform(const LMatrix4f &mat) {
CDWriter cdata(_cycler);
cdata->_lod.xform(mat);
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::has_cull_callback
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if cull_callback() has been defined. Otherwise,
// returns false to indicate cull_callback() does not
// need to be called for this node during the cull
// traversal.
////////////////////////////////////////////////////////////////////
bool qpLODNode::
has_cull_callback() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::cull_callback
// Access: Public, Virtual
// Description: If has_cull_callback() returns true, 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.
//
// 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 qpLODNode::
cull_callback(CullTraverserData &data) {
if (data._net_transform->is_singular()) {
// If we're under a singular transform, we can't compute the LOD;
// select none of them instead.
select_child(get_num_children());
} else {
CDReader cdata(_cycler);
LPoint3f camera_pos(0, 0, 0);
// Get the LOD center in camera space
CPT(TransformState) rel_transform =
data._net_transform->invert_compose(data._camera_transform);
LPoint3f center = cdata->_lod._center * rel_transform->get_mat();
// Determine which child to traverse
int index = cdata->_lod.compute_child(camera_pos, center);
select_child(index);
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void qpLODNode::
output(ostream &out) const {
SelectiveChildNode::output(out);
CDReader cdata(_cycler);
out << " ";
cdata->_lod.output(out);
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// qpLODNode.
////////////////////////////////////////////////////////////////////
void qpLODNode::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void qpLODNode::
write_datagram(BamWriter *manager, Datagram &dg) {
SelectiveChildNode::write_datagram(manager, dg);
CDReader cdata(_cycler);
cdata->_lod.write_datagram(dg);
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type qpLODNode is encountered
// in the Bam file. It should create the qpLODNode
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *qpLODNode::
make_from_bam(const FactoryParams &params) {
qpLODNode *node = new qpLODNode("");
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
node->fillin(scan, manager);
return node;
}
////////////////////////////////////////////////////////////////////
// Function: qpLODNode::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 qpLODNode.
////////////////////////////////////////////////////////////////////
void qpLODNode::
fillin(DatagramIterator &scan, BamReader *manager) {
SelectiveChildNode::fillin(scan, manager);
CDWriter cdata(_cycler);
cdata->_lod.read_datagram(scan);
}

View File

@ -0,0 +1,109 @@
// Filename: qplodNode.h
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef qpLODNODE_H
#define qpLODNODE_H
#include "pandabase.h"
#include "selectiveChildNode.h"
#include "LOD.h"
////////////////////////////////////////////////////////////////////
// Class : qpLODNode
// Description : A Level-of-Detail node. This selects only one of its
// children for rendering, according to the distance
// from the camera and the table indicated in the
// associated LOD object.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA qpLODNode : public SelectiveChildNode {
PUBLISHED:
INLINE qpLODNode(const string &name);
public:
INLINE qpLODNode(const qpLODNode &copy);
INLINE void operator = (const qpLODNode &copy);
virtual PandaNode *make_copy() const;
virtual void xform(const LMatrix4f &mat);
virtual bool has_cull_callback() const;
virtual bool cull_callback(CullTraverserData &data);
virtual void output(ostream &out) const;
PUBLISHED:
// The sense of in vs. out distances is as if the object were coming
// towards you from far away: it switches "in" at the far distance,
// and switches "out" at the close distance. Thus, "in" should be
// larger than "out".
INLINE void add_switch(float in, float out);
INLINE bool set_switch(int index, float in, float out);
INLINE void clear_switches(void);
INLINE int get_num_switches() const;
INLINE float get_in(int index) const;
INLINE float get_out(int index) const;
INLINE void set_center(const LPoint3f &center);
INLINE const LPoint3f &get_center() const;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
private:
class EXPCL_PANDA CData : public CycleData {
public:
INLINE CData();
INLINE CData(const CData &copy);
virtual CycleData *make_copy() const;
LOD _lod;
};
PipelineCycler<CData> _cycler;
typedef CycleDataReader<CData> CDReader;
typedef CycleDataWriter<CData> CDWriter;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
SelectiveChildNode::init_type();
register_type(_type_handle, "qpLODNode",
SelectiveChildNode::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 "qplodNode.I"
#endif

View File

@ -0,0 +1,130 @@
// Filename: qpsequenceNode.I
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::CData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpSequenceNode::CData::
CData() {
_cycle_rate = 0.0f;
_frame_offset = 0.0f;
_start_time = 0.0f;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::CData::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpSequenceNode::CData::
CData(const qpSequenceNode::CData &copy) :
_cycle_rate(copy._cycle_rate),
_frame_offset(copy._frame_offset),
_start_time(copy._start_time)
{
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpSequenceNode::
qpSequenceNode(float cycle_rate, const string &name) :
SelectiveChildNode(name)
{
CDWriter cdata(_cycler);
cdata->_cycle_rate = cycle_rate;
cdata->_frame_offset = 0.0f;
float now = ClockObject::get_global_clock()->get_frame_time();
cdata->_start_time = now;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::set_cycle_rate
// Access: Published
// Description: Sets the rate of cycling for the children of the
// SequenceNode, in cycles per second.
////////////////////////////////////////////////////////////////////
INLINE void qpSequenceNode::
set_cycle_rate(float cycle_rate) {
// Do some fussing so we keep the same frame visible while we
// change this.
CDWriter cdata(_cycler);
float now = ClockObject::get_global_clock()->get_frame_time();
cdata->_frame_offset = calc_frame(now);
cdata->_start_time = now;
cdata->_cycle_rate = cycle_rate;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::get_cycle_rate
// Access: Published
// Description: Returns the rate of cycling for the children of the
// SequenceNode, in cycles per second.
////////////////////////////////////////////////////////////////////
INLINE float qpSequenceNode::
get_cycle_rate() const {
CDReader cdata(_cycler);
return cdata->_cycle_rate;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::get_visible_child
// Access: Published
// Description: Returns the index of the child that should be visible
// for this particular frame, if there are any children.
////////////////////////////////////////////////////////////////////
INLINE int qpSequenceNode::
get_visible_child() const {
int num_children = get_num_children();
if (num_children == 0) {
return 0;
}
float frame = calc_frame();
return ((int)frame) % num_children;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::calc_frame
// Access: Private
// Description: Returns the floating-point frame number at the
// indicated time.
////////////////////////////////////////////////////////////////////
INLINE float qpSequenceNode::
calc_frame(float now) const {
CDReader cdata(_cycler);
return (now - cdata->_start_time) * cdata->_cycle_rate + cdata->_frame_offset;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::calc_frame
// Access: Private
// Description: Returns the floating-point frame number at the
// current time.
////////////////////////////////////////////////////////////////////
INLINE float qpSequenceNode::
calc_frame() const {
return calc_frame(ClockObject::get_global_clock()->get_frame_time());
}

View File

@ -0,0 +1,183 @@
// Filename: qpsequenceNode.cxx
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "pandabase.h"
#include "qpsequenceNode.h"
TypeHandle qpSequenceNode::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::CData::make_copy
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CycleData *qpSequenceNode::CData::
make_copy() const {
return new CData(*this);
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
qpSequenceNode::
qpSequenceNode(const qpSequenceNode &copy) :
SelectiveChildNode(copy)
{
CDWriter cdata(_cycler);
CDReader cdata_copy(copy._cycler);
cdata->_cycle_rate = cdata_copy->_cycle_rate;
cdata->_start_time = cdata_copy->_start_time;
cdata->_frame_offset = cdata_copy->_frame_offset;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void qpSequenceNode::
operator = (const qpSequenceNode &copy) {
SelectiveChildNode::operator = (copy);
CDWriter cdata(_cycler);
CDReader cdata_copy(copy._cycler);
cdata->_cycle_rate = cdata_copy->_cycle_rate;
cdata->_start_time = cdata_copy->_start_time;
cdata->_frame_offset = cdata_copy->_frame_offset;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::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 *qpSequenceNode::
make_copy() const {
return new qpSequenceNode(*this);
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::has_cull_callback
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if cull_callback() has been defined. Otherwise,
// returns false to indicate cull_callback() does not
// need to be called for this node during the cull
// traversal.
////////////////////////////////////////////////////////////////////
bool qpSequenceNode::
has_cull_callback() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::cull_callback
// Access: Public, Virtual
// Description: If has_cull_callback() returns true, 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.
//
// 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 qpSequenceNode::
cull_callback(CullTraverserData &) {
select_child(get_visible_child());
return true;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// qpSequenceNode.
////////////////////////////////////////////////////////////////////
void qpSequenceNode::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void qpSequenceNode::
write_datagram(BamWriter *manager, Datagram &dg) {
SelectiveChildNode::write_datagram(manager, dg);
CDReader cdata(_cycler);
dg.add_float32(cdata->_cycle_rate);
dg.add_float32(calc_frame());
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type qpSequenceNode is encountered
// in the Bam file. It should create the qpSequenceNode
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *qpSequenceNode::
make_from_bam(const FactoryParams &params) {
qpSequenceNode *node = new qpSequenceNode(0.0f, "");
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
node->fillin(scan, manager);
return node;
}
////////////////////////////////////////////////////////////////////
// Function: qpSequenceNode::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 qpSequenceNode.
////////////////////////////////////////////////////////////////////
void qpSequenceNode::
fillin(DatagramIterator &scan, BamReader *manager) {
SelectiveChildNode::fillin(scan, manager);
CDWriter cdata(_cycler);
cdata->_cycle_rate = scan.get_float32();
cdata->_frame_offset = scan.get_float32();
float now = ClockObject::get_global_clock()->get_frame_time();
cdata->_start_time = now;
}

View File

@ -0,0 +1,98 @@
// Filename: qpsequenceNode.h
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef qpSEQUENCENODE_H
#define qpSEQUENCENODE_H
#include "pandabase.h"
#include "selectiveChildNode.h"
#include "clockObject.h"
////////////////////////////////////////////////////////////////////
// Class : SequenceNode
// Description : A node that automatically cycles through rendering
// each one of its children according to its frame rate.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA qpSequenceNode : public SelectiveChildNode {
PUBLISHED:
INLINE qpSequenceNode(float cycle_rate, const string &name);
public:
qpSequenceNode(const qpSequenceNode &copy);
void operator = (const qpSequenceNode &copy);
virtual PandaNode *make_copy() const;
virtual bool has_cull_callback() const;
virtual bool cull_callback(CullTraverserData &data);
PUBLISHED:
INLINE void set_cycle_rate(float cycle_rate);
INLINE float get_cycle_rate() const;
INLINE int get_visible_child() const;
private:
INLINE float calc_frame(float now) const;
INLINE float calc_frame() const;
class EXPCL_PANDA CData : public CycleData {
public:
INLINE CData();
INLINE CData(const CData &copy);
virtual CycleData *make_copy() const;
float _cycle_rate;
float _frame_offset;
float _start_time;
};
PipelineCycler<CData> _cycler;
typedef CycleDataReader<CData> CDReader;
typedef CycleDataWriter<CData> CDWriter;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
SelectiveChildNode::init_type();
register_type(_type_handle, "qpSequenceNode",
SelectiveChildNode::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 "qpsequenceNode.I"
#endif

View File

@ -0,0 +1,65 @@
// Filename: selectiveChildNode.I
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: SelectiveChildNode::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE SelectiveChildNode::
SelectiveChildNode(const string &name) :
PandaNode(name),
_selected_child(0)
{
}
////////////////////////////////////////////////////////////////////
// Function: SelectiveChildNode::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE SelectiveChildNode::
SelectiveChildNode(const SelectiveChildNode &copy) :
PandaNode(copy),
_selected_child(copy._selected_child)
{
}
////////////////////////////////////////////////////////////////////
// Function: SelectiveChildNode::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void SelectiveChildNode::
operator = (const SelectiveChildNode &copy) {
PandaNode::operator = (copy);
_selected_child = copy._selected_child;
}
////////////////////////////////////////////////////////////////////
// Function: SelectiveChildNode::select_child
// Access: Protected
// Description: Selects the indicated child for rendering. This is
// normally called during the cull_callback() method,
// but it may be called at any time.
////////////////////////////////////////////////////////////////////
INLINE void SelectiveChildNode::
select_child(int n) {
_selected_child = n;
}

View File

@ -0,0 +1,75 @@
// Filename: selectiveChildNode.cxx
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "selectiveChildNode.h"
TypeHandle SelectiveChildNode::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: SelectiveChildNode::has_selective_visibility
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if this kind of node has some restrictions on
// the set of children that should be rendered. Node
// with this property include LODNodes, SwitchNodes, and
// SequenceNodes.
//
// If this function returns true,
// get_first_visible_child() and
// get_next_visible_child() will be called to walk
// through the list of children during cull, instead of
// iterating through the entire list. This method is
// called after cull_callback(), so cull_callback() may
// be responsible for the decisions as to which children
// are visible at the moment.
////////////////////////////////////////////////////////////////////
bool SelectiveChildNode::
has_selective_visibility() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: SelectiveChildNode::get_first_visible_child
// Access: Public, Virtual
// Description: Returns the index number of the first visible child
// of this node, or a number >= get_num_children() if
// there are no visible children of this node. This is
// called during the cull traversal, but only if
// has_selective_visibility() has already returned true.
// See has_selective_visibility().
////////////////////////////////////////////////////////////////////
int SelectiveChildNode::
get_first_visible_child() const {
return _selected_child;
}
////////////////////////////////////////////////////////////////////
// Function: SelectiveChildNode::get_next_visible_child
// Access: Public, Virtual
// Description: Returns the index number of the next visible child
// of this node following the indicated child, or a
// number >= get_num_children() if there are no more
// visible children of this node. See
// has_selective_visibility() and
// get_first_visible_child().
////////////////////////////////////////////////////////////////////
int SelectiveChildNode::
get_next_visible_child(int n) const {
return get_num_children();
}

View File

@ -0,0 +1,69 @@
// Filename: selectiveChildNode.h
// Created by: drose (06Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef SELECTIVECHILDNODE_H
#define SELECTIVECHILDNODE_H
#include "pandabase.h"
#include "pandaNode.h"
////////////////////////////////////////////////////////////////////
// Class : SelectiveChildNode
// Description : A base class for nodes like LODNode and SequenceNode
// that select only one visible child at a time.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA SelectiveChildNode : public PandaNode {
PUBLISHED:
INLINE SelectiveChildNode(const string &name);
public:
INLINE SelectiveChildNode(const SelectiveChildNode &copy);
INLINE void operator = (const SelectiveChildNode &copy);
virtual bool has_selective_visibility() const;
virtual int get_first_visible_child() const;
virtual int get_next_visible_child(int n) const;
protected:
INLINE void select_child(int n);
private:
int _selected_child;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
PandaNode::init_type();
register_type(_type_handle, "SelectiveChildNode",
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 "selectiveChildNode.I"
#endif

View File

@ -480,6 +480,9 @@ do_compose(const TransformState *other) const {
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
do_invert_compose(const TransformState *other) const {
// Perhaps we should cache the inverse matrix operation separately,
// as a further optimization.
LMatrix4f new_mat;
new_mat.invert_from(get_mat());
new_mat = other->get_mat() * new_mat;