diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 00a8ecc6c5..4391f59b51 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -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 diff --git a/panda/src/egg2pg/Sources.pp b/panda/src/egg2pg/Sources.pp index 271f2fedf1..4fa891cb28 100644 --- a/panda/src/egg2pg/Sources.pp +++ b/panda/src/egg2pg/Sources.pp @@ -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 diff --git a/panda/src/egg2pg/egg2pg_composite1.cxx b/panda/src/egg2pg/egg2pg_composite1.cxx index 0dec6bfa70..8feb78b7ec 100644 --- a/panda/src/egg2pg/egg2pg_composite1.cxx +++ b/panda/src/egg2pg/egg2pg_composite1.cxx @@ -1,2 +1,2 @@ #include "config_egg2pg.cxx" -#include "qpeggLoader.cxx" +#include "eggBinner.cxx" diff --git a/panda/src/egg2pg/egg2pg_composite2.cxx b/panda/src/egg2pg/egg2pg_composite2.cxx index 33234eafef..d1946167eb 100644 --- a/panda/src/egg2pg/egg2pg_composite2.cxx +++ b/panda/src/egg2pg/egg2pg_composite2.cxx @@ -1 +1,3 @@ +#include "qpeggLoader.cxx" #include "qpload_egg_file.cxx" + diff --git a/panda/src/egg2sg/eggBinner.cxx b/panda/src/egg2pg/eggBinner.cxx similarity index 97% rename from panda/src/egg2sg/eggBinner.cxx rename to panda/src/egg2pg/eggBinner.cxx index 4e654f4d4a..3ccc1c51f1 100644 --- a/panda/src/egg2sg/eggBinner.cxx +++ b/panda/src/egg2pg/eggBinner.cxx @@ -18,10 +18,9 @@ #include "eggBinner.h" -#include -#include - -#include +#include "eggSwitchCondition.h" +#include "eggGroup.h" +#include "dcast.h" //////////////////////////////////////////////////////////////////// // Function: EggBinner::get_bin_number diff --git a/panda/src/egg2sg/eggBinner.h b/panda/src/egg2pg/eggBinner.h similarity index 97% rename from panda/src/egg2sg/eggBinner.h rename to panda/src/egg2pg/eggBinner.h index 7b2c03ccfc..929dd9e6b0 100644 --- a/panda/src/egg2sg/eggBinner.h +++ b/panda/src/egg2pg/eggBinner.h @@ -19,9 +19,9 @@ #ifndef EGGBINNER_H #define EGGBINNER_H -#include +#include "pandabase.h" -#include +#include "eggBinMaker.h" /////////////////////////////////////////////////////////////////// // Class : EggBinner diff --git a/panda/src/egg2pg/qpeggLoader.cxx b/panda/src/egg2pg/qpeggLoader.cxx index 93a2ad1cc4..7e73b328a7 100644 --- a/panda/src/egg2pg/qpeggLoader.cxx +++ b/panda/src/egg2pg/qpeggLoader.cxx @@ -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 #include -/* // 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 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. diff --git a/panda/src/egg2sg/Sources.pp b/panda/src/egg2sg/Sources.pp index 9db6835e4c..3a0e515a95 100644 --- a/panda/src/egg2sg/Sources.pp +++ b/panda/src/egg2sg/Sources.pp @@ -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 \ diff --git a/panda/src/egg2sg/egg2sg_composite2.cxx b/panda/src/egg2sg/egg2sg_composite2.cxx index fe3086b33a..ed6b09b916 100644 --- a/panda/src/egg2sg/egg2sg_composite2.cxx +++ b/panda/src/egg2sg/egg2sg_composite2.cxx @@ -1,6 +1,5 @@ #include "config_egg2sg.cxx" -#include "eggBinner.cxx" #include "eggLoader.cxx" #include "load_egg_file.cxx" #include "loaderFileTypeEgg.cxx" diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index a440652313..60f43f9a95 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -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 \ diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index abf46e87b5..a33afb817c 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -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(); diff --git a/panda/src/pgraph/cullTraverserData.I b/panda/src/pgraph/cullTraverserData.I new file mode 100644 index 0000000000..cd8ec4b709 --- /dev/null +++ b/panda/src/pgraph/cullTraverserData.I @@ -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 ©) : + _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 ©) { + _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); +} diff --git a/panda/src/pgraph/cullTraverserData.cxx b/panda/src/pgraph/cullTraverserData.cxx new file mode 100644 index 0000000000..60bbfb9d02 --- /dev/null +++ b/panda/src/pgraph/cullTraverserData.cxx @@ -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; +} + diff --git a/panda/src/pgraph/cullTraverserData.h b/panda/src/pgraph/cullTraverserData.h new file mode 100644 index 0000000000..1f3b93d17e --- /dev/null +++ b/panda/src/pgraph/cullTraverserData.h @@ -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 ©); + INLINE void operator = (const CullTraverserData ©); + 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 diff --git a/panda/src/pgraph/cullableObject.I b/panda/src/pgraph/cullableObject.I index 0c6f00905c..d22b6fe0f1 100644 --- a/panda/src/pgraph/cullableObject.I +++ b/panda/src/pgraph/cullableObject.I @@ -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 diff --git a/panda/src/pgraph/cullableObject.h b/panda/src/pgraph/cullableObject.h index 25c286131c..ce69e160ea 100644 --- a/panda/src/pgraph/cullableObject.h +++ b/panda/src/pgraph/cullableObject.h @@ -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 ©); diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 513e74c2c2..8853002bc4 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -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 diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 3ec5503b1a..326f8dd36a 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -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; diff --git a/panda/src/pgraph/pgraph_composite1.cxx b/panda/src/pgraph/pgraph_composite1.cxx index 83bc85f155..5c3eb2abe0 100644 --- a/panda/src/pgraph/pgraph_composite1.cxx +++ b/panda/src/pgraph/pgraph_composite1.cxx @@ -13,5 +13,6 @@ #include "cullHandler.cxx" #include "cullResult.cxx" #include "qpcullTraverser.cxx" +#include "cullTraverserData.cxx" #include "cullableObject.cxx" diff --git a/panda/src/pgraph/pgraph_composite2.cxx b/panda/src/pgraph/pgraph_composite2.cxx index 1538ab03eb..2fbfabfdda 100644 --- a/panda/src/pgraph/pgraph_composite2.cxx +++ b/panda/src/pgraph/pgraph_composite2.cxx @@ -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" diff --git a/panda/src/pgraph/qpcamera.cxx b/panda/src/pgraph/qpcamera.cxx index 0f9d0d21c3..1f34828588 100644 --- a/panda/src/pgraph/qpcamera.cxx +++ b/panda/src/pgraph/qpcamera.cxx @@ -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 ¶ms) { + 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); +} diff --git a/panda/src/pgraph/qpcamera.h b/panda/src/pgraph/qpcamera.h index ed0db4d84b..83d2b67ebf 100644 --- a/panda/src/pgraph/qpcamera.h +++ b/panda/src/pgraph/qpcamera.h @@ -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 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 ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/pgraph/qpcullTraverser.cxx b/panda/src/pgraph/qpcullTraverser.cxx index 51a3a8b493..bd63ca3631 100644 --- a/panda/src/pgraph/qpcullTraverser.cxx +++ b/panda/src/pgraph/qpcullTraverser.cxx @@ -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; -} - diff --git a/panda/src/pgraph/qpcullTraverser.h b/panda/src/pgraph/qpcullTraverser.h index 99e149aff3..877141d45c 100644 --- a/panda/src/pgraph/qpcullTraverser.h +++ b/panda/src/pgraph/qpcullTraverser.h @@ -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; diff --git a/panda/src/pgraph/qplensNode.cxx b/panda/src/pgraph/qplensNode.cxx index c2ec24a90b..34358cf7ae 100644 --- a/panda/src/pgraph/qplensNode.cxx +++ b/panda/src/pgraph/qplensNode.cxx @@ -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 ¶ms) { + 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); +} diff --git a/panda/src/pgraph/qplensNode.h b/panda/src/pgraph/qplensNode.h index 6747473746..3b366f895a 100644 --- a/panda/src/pgraph/qplensNode.h +++ b/panda/src/pgraph/qplensNode.h @@ -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 ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/pgraph/qplodNode.I b/panda/src/pgraph/qplodNode.I new file mode 100644 index 0000000000..f767486239 --- /dev/null +++ b/panda/src/pgraph/qplodNode.I @@ -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 ©) : + _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 ¢er) { + 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; +} diff --git a/panda/src/pgraph/qplodNode.cxx b/panda/src/pgraph/qplodNode.cxx new file mode 100644 index 0000000000..bbaa2f79fd --- /dev/null +++ b/panda/src/pgraph/qplodNode.cxx @@ -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 ©) : + 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 ©) { + 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 ¶ms) { + 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); +} diff --git a/panda/src/pgraph/qplodNode.h b/panda/src/pgraph/qplodNode.h new file mode 100644 index 0000000000..1ecda9fed8 --- /dev/null +++ b/panda/src/pgraph/qplodNode.h @@ -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 ©); + INLINE void operator = (const qpLODNode ©); + + 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 ¢er); + 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 ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + +private: + class EXPCL_PANDA CData : public CycleData { + public: + INLINE CData(); + INLINE CData(const CData ©); + virtual CycleData *make_copy() const; + + LOD _lod; + }; + + PipelineCycler _cycler; + typedef CycleDataReader CDReader; + typedef CycleDataWriter 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 diff --git a/panda/src/pgraph/qpsequenceNode.I b/panda/src/pgraph/qpsequenceNode.I new file mode 100644 index 0000000000..c5b401ce3d --- /dev/null +++ b/panda/src/pgraph/qpsequenceNode.I @@ -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 ©) : + _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()); +} diff --git a/panda/src/pgraph/qpsequenceNode.cxx b/panda/src/pgraph/qpsequenceNode.cxx new file mode 100644 index 0000000000..1b0bfae04e --- /dev/null +++ b/panda/src/pgraph/qpsequenceNode.cxx @@ -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 ©) : + 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 ©) { + 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 ¶ms) { + 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; +} diff --git a/panda/src/pgraph/qpsequenceNode.h b/panda/src/pgraph/qpsequenceNode.h new file mode 100644 index 0000000000..165a23791a --- /dev/null +++ b/panda/src/pgraph/qpsequenceNode.h @@ -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 ©); + void operator = (const qpSequenceNode ©); + + 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 ©); + virtual CycleData *make_copy() const; + + float _cycle_rate; + float _frame_offset; + float _start_time; + }; + + PipelineCycler _cycler; + typedef CycleDataReader CDReader; + typedef CycleDataWriter CDWriter; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +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() { + 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 diff --git a/panda/src/pgraph/selectiveChildNode.I b/panda/src/pgraph/selectiveChildNode.I new file mode 100644 index 0000000000..41b566c10d --- /dev/null +++ b/panda/src/pgraph/selectiveChildNode.I @@ -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 ©) : + PandaNode(copy), + _selected_child(copy._selected_child) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SelectiveChildNode::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void SelectiveChildNode:: +operator = (const SelectiveChildNode ©) { + 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; +} diff --git a/panda/src/pgraph/selectiveChildNode.cxx b/panda/src/pgraph/selectiveChildNode.cxx new file mode 100644 index 0000000000..0871161250 --- /dev/null +++ b/panda/src/pgraph/selectiveChildNode.cxx @@ -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(); +} diff --git a/panda/src/pgraph/selectiveChildNode.h b/panda/src/pgraph/selectiveChildNode.h new file mode 100644 index 0000000000..c99fc86539 --- /dev/null +++ b/panda/src/pgraph/selectiveChildNode.h @@ -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 ©); + INLINE void operator = (const SelectiveChildNode ©); + + 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 diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index 87a92bdf88..21b8b67037 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -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;