diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index a4a7e17a9a..7e1da0e2b8 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -24,6 +24,13 @@ #include "cullResult.h" #include "qpcullTraverser.h" #include "clockObject.h" +#include "pStatTimer.h" +#include "pStatClient.h" + +#ifndef CPPPARSER +PStatCollector GraphicsEngine::_cull_pcollector("Cull"); +PStatCollector GraphicsEngine::_draw_pcollector("Draw"); +#endif // CPPPARSER //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::Constructor @@ -89,6 +96,7 @@ render_frame() { // **** This doesn't belong here; it really belongs in the Pipeline, // but here it is for now. ClockObject::get_global_clock()->tick(); + PStatClient::main_tick(); } //////////////////////////////////////////////////////////////////// @@ -199,6 +207,9 @@ cull_bin_draw(GraphicsWindow *win, DisplayRegion *dr) { void GraphicsEngine:: do_cull(CullHandler *cull_handler, const qpNodePath &camera, GraphicsStateGuardian *gsg) { + // Statistics + PStatTimer timer(_cull_pcollector); + if (camera.is_empty()) { // No camera, no draw. return; @@ -227,12 +238,12 @@ do_cull(CullHandler *cull_handler, const qpNodePath &camera, qpCullTraverser trav; trav.set_cull_handler(cull_handler); - // We will need both the camera transform (the net transform from - // the scene to the camera) and the world transform (the camera - // transform inverse, or the net transform from the camera to the - // scene). - CPT(TransformState) camera_transform = scene.get_transform(camera); - CPT(TransformState) world_transform = camera.get_transform(scene); + // We will need both the camera transform (the net transform to the + // camera from the scene) and the world transform (the camera + // transform inverse, or the net transform to the scene from the + // camera). + CPT(TransformState) camera_transform = camera.get_transform(scene); + CPT(TransformState) world_transform = scene.get_transform(camera); // The render transform is the same as the world transform, except // it is converted into the GSG's internal coordinate system. This @@ -247,7 +258,7 @@ do_cull(CullHandler *cull_handler, const qpNodePath &camera, render_transform = cs_transform->compose(render_transform); } - trav.set_camera_transform(scene.get_transform(camera)); + trav.set_camera_transform(camera_transform); trav.set_render_transform(render_transform); if (qpview_frustum_cull) { @@ -281,6 +292,9 @@ do_cull(CullHandler *cull_handler, const qpNodePath &camera, void GraphicsEngine:: do_draw(CullResult *cull_result, GraphicsStateGuardian *gsg, DisplayRegion *dr) { + // Statistics + PStatTimer timer(_draw_pcollector); + if (set_gsg_lens(gsg, dr)) { DisplayRegionStack old_dr = gsg->push_display_region(dr); gsg->prepare_display_region(); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 3943775ceb..d24b18b7a8 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -23,6 +23,7 @@ #include "graphicsWindow.h" #include "pointerTo.h" #include "pset.h" +#include "pStatCollector.h" class Pipeline; class DisplayRegion; @@ -68,6 +69,9 @@ private: typedef pset Windows; Windows _windows; + + static PStatCollector _cull_pcollector; + static PStatCollector _draw_pcollector; }; #include "graphicsEngine.I" diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 13cefa8190..ad9be7b126 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -383,6 +383,9 @@ add_child(PandaNode *child_node, int sort) { } child_node->fix_chain_lengths(); + + // Mark the bounding volumes stale. + force_bound_stale(); } //////////////////////////////////////////////////////////////////// @@ -430,6 +433,9 @@ remove_child(int n) { } child_node->fix_chain_lengths(); + + // Mark the bounding volumes stale. + force_bound_stale(); } //////////////////////////////////////////////////////////////////// @@ -476,6 +482,9 @@ remove_child(PandaNode *child_node) { child_node->fix_chain_lengths(); + // Mark the bounding volumes stale. + force_bound_stale(); + CDWriter cdata(_cycler); // Now, look for and remove the child node from our down list. @@ -529,6 +538,9 @@ remove_all_children() { child_node->fix_chain_lengths(); } + + // Mark the bounding volumes stale. + force_bound_stale(); } //////////////////////////////////////////////////////////////////// @@ -732,6 +744,9 @@ detach(qpNodePathComponent *child) { child_node->fix_chain_lengths(); + // Mark the bounding volumes stale. + parent_node->force_bound_stale(); + // Now look for the child and break the actual connection. // First, look for and remove the parent node from the child's up @@ -785,6 +800,9 @@ reparent(qpNodePathComponent *new_parent, qpNodePathComponent *child, int sort) cdata_child->_chains.insert(child); child_node->fix_chain_lengths(); + + // Mark the bounding volumes stale. + parent_node->force_bound_stale(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/qpnodePath.I b/panda/src/pgraph/qpnodePath.I index 99d64bc42b..139c71be76 100644 --- a/panda/src/pgraph/qpnodePath.I +++ b/panda/src/pgraph/qpnodePath.I @@ -706,18 +706,6 @@ set_pos(const qpNodePath &other, float x, float y, float z) { set_pos(other, LPoint3f(x, y, z)); } -//////////////////////////////////////////////////////////////////// -// Function: qpNodePath::get_pos -// Access: Published -// Description: Returns the relative position of the referenced node -// as seen from the other node. -//////////////////////////////////////////////////////////////////// -INLINE LPoint3f qpNodePath:: -get_pos(const qpNodePath &other) const { - LMatrix4f mat = get_mat(other); - return mat.get_row3(3); -} - INLINE float qpNodePath:: get_x(const qpNodePath &other) const { return get_pos(other)[0]; diff --git a/panda/src/pgraph/qpnodePath.cxx b/panda/src/pgraph/qpnodePath.cxx index 09a4371710..1109d4ff67 100644 --- a/panda/src/pgraph/qpnodePath.cxx +++ b/panda/src/pgraph/qpnodePath.cxx @@ -322,17 +322,17 @@ set_state(const qpNodePath &other, const RenderState *state) const { //////////////////////////////////////////////////////////////////// // Function: qpNodePath::get_transform // Access: Published -// Description: Returns the relative transform from this node to the -// other node; i.e. the transformation of the other node -// as seen from this node. +// Description: Returns the relative transform to this node from the +// other node; i.e. the transformation of this node +// as seen from the other node. //////////////////////////////////////////////////////////////////// CPT(TransformState) qpNodePath:: get_transform(const qpNodePath &other) const { - if (is_empty()) { - return other.get_net_transform(); - } if (other.is_empty()) { - return get_net_transform()->invert_compose(TransformState::make_identity()); + return get_net_transform(); + } + if (is_empty()) { + return other.get_net_transform()->invert_compose(TransformState::make_identity()); } nassertr(verify_complete(), TransformState::make_identity()); @@ -343,7 +343,7 @@ get_transform(const qpNodePath &other) const { CPT(TransformState) a_transform = r_get_partial_transform(_head, a_count); CPT(TransformState) b_transform = r_get_partial_transform(other._head, b_count); - return a_transform->invert_compose(b_transform); + return b_transform->invert_compose(a_transform); } //////////////////////////////////////////////////////////////////// @@ -351,8 +351,8 @@ get_transform(const qpNodePath &other) const { // Access: Published // Description: Sets the transform object on this node, relative to // the other node. This computes a new transform object -// that has the indicated value when seen relative to -// the other node. +// that will have the indicated value when seen from the +// other node. //////////////////////////////////////////////////////////////////// void qpNodePath:: set_transform(const qpNodePath &other, const TransformState *transform) const { @@ -361,7 +361,7 @@ set_transform(const qpNodePath &other, const TransformState *transform) const { // First, we perform a wrt to the parent, to get the conversion. qpNodePath parent = get_parent(); - CPT(TransformState) rel_trans = parent.get_transform(other); + CPT(TransformState) rel_trans = other.get_transform(parent); CPT(TransformState) new_trans = rel_trans->compose(transform); set_transform(new_trans); @@ -491,11 +491,11 @@ get_hpr(float roll) const { // leaving translation and rotation untouched. //////////////////////////////////////////////////////////////////// void qpNodePath:: -set_scale(const LVecBase3f &sv3) { +set_scale(const LVecBase3f &scale) { nassertv(!is_empty()); CPT(TransformState) transform = get_transform(); nassertv(transform->has_components()); - set_transform(transform->set_scale(sv3)); + set_transform(transform->set_scale(scale)); } void qpNodePath:: @@ -794,6 +794,18 @@ set_z(const qpNodePath &other, float z) { set_pos(other, pos); } +//////////////////////////////////////////////////////////////////// +// Function: qpNodePath::get_pos +// Access: Published +// Description: Returns the relative position of the referenced node +// as seen from the other node. +//////////////////////////////////////////////////////////////////// +LPoint3f qpNodePath:: +get_pos(const qpNodePath &other) const { + nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f)); + return get_transform(other)->get_pos(); +} + //////////////////////////////////////////////////////////////////// // Function: qpNodePath::set_hpr // Access: Published @@ -2254,7 +2266,7 @@ r_output(ostream &out, qpNodePathComponent *comp) const { } else { out << "+" << node->get_type(); } - out << "[" << comp->get_length() << "]"; + // out << "[" << comp->get_length() << "]"; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/qpnodePath.h b/panda/src/pgraph/qpnodePath.h index 0a23d10f7d..372628c0f9 100644 --- a/panda/src/pgraph/qpnodePath.h +++ b/panda/src/pgraph/qpnodePath.h @@ -168,7 +168,7 @@ PUBLISHED: INLINE void set_scale(float scale); INLINE void set_scale(float sx, float sy, float sz); - void set_scale(const LVecBase3f &sv3); + void set_scale(const LVecBase3f &scale); void set_sx(float sx); void set_sy(float sy); void set_sz(float sz); @@ -232,7 +232,7 @@ PUBLISHED: void set_x(const qpNodePath &other, float x); void set_y(const qpNodePath &other, float y); void set_z(const qpNodePath &other, float z); - INLINE LPoint3f get_pos(const qpNodePath &other) const; + LPoint3f get_pos(const qpNodePath &other) const; INLINE float get_x(const qpNodePath &other) const; INLINE float get_y(const qpNodePath &other) const; INLINE float get_z(const qpNodePath &other) const; diff --git a/panda/src/pgraph/transformState.I b/panda/src/pgraph/transformState.I index f151fcce16..aa94926b09 100644 --- a/panda/src/pgraph/transformState.I +++ b/panda/src/pgraph/transformState.I @@ -92,6 +92,18 @@ is_identity() const { return ((_flags & F_is_identity) != 0); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::is_invalid +// Access: Published +// Description: Returns true if the transform represents an invalid +// matrix, for instance the result of inverting a +// singular matrix, or false if the transform is valid. +//////////////////////////////////////////////////////////////////// +INLINE bool TransformState:: +is_invalid() const { + return ((_flags & F_is_invalid) != 0); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::is_singular // Access: Published @@ -119,8 +131,10 @@ is_singular() const { // that was constructed with a 4x4 may return true here // if the matrix is a simple affine matrix with no skew. // -// If this returns true, you may safely call get_pos(), -// etc., to retrieve the components. +// If this returns true, you may safely call get_hpr() +// and get_scale() to retrieve the components. (You +// may always safely call get_pos() whether this returns +// true or false.) //////////////////////////////////////////////////////////////////// INLINE bool TransformState:: has_components() const { @@ -128,16 +142,32 @@ has_components() const { return ((_flags & F_has_components) != 0); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::components_given +// Access: Published +// Description: Returns true if the transform was specified +// componentwise, or false if it was specified with a +// general 4x4 matrix. If this is true, the components +// returned by get_pos(), get_hpr(), and get_scale() +// will be exactly those that were set; otherwise, these +// functions will return computed values. +//////////////////////////////////////////////////////////////////// +INLINE bool TransformState:: +components_given() const { + return ((_flags & F_components_given) != 0); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::has_pos // Access: Published // Description: Returns true if the transform's pos component can be // extracted out separately. This is generally always -// true. +// true, unless the transform is invalid +// (i.e. is_invalid() returns true). //////////////////////////////////////////////////////////////////// INLINE bool TransformState:: has_pos() const { - return true; + return !is_invalid(); } //////////////////////////////////////////////////////////////////// @@ -165,6 +195,18 @@ has_scale() const { return has_components(); } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::has_mat +// Access: Published +// Description: Returns true if the transform can be described as a +// matrix. This is generally always true, unless +// is_invalid() is true. +//////////////////////////////////////////////////////////////////// +INLINE bool TransformState:: +has_mat() const { + return !is_invalid(); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::get_pos // Access: Published @@ -174,6 +216,7 @@ has_scale() const { INLINE const LVecBase3f &TransformState:: get_pos() const { check_components(); + nassertr(has_pos(), _pos); return _pos; } @@ -212,6 +255,7 @@ get_scale() const { //////////////////////////////////////////////////////////////////// INLINE const LMatrix4f &TransformState:: get_mat() const { + nassertr(has_mat(), LMatrix4f::ident_mat()); check_mat(); return _mat; } diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index e0221fee42..cbdb6151a1 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -154,12 +154,22 @@ TransformState:: //////////////////////////////////////////////////////////////////// bool TransformState:: operator < (const TransformState &other) const { - bool components_given = (_flags & F_components_given) != 0; - bool other_components_given = (other._flags & F_components_given) != 0; - if (components_given != other_components_given) { - return components_given < other_components_given; + static const int significant_flags = + (F_is_invalid | F_is_identity | F_components_given); + + int flags = (_flags & significant_flags); + int other_flags = (other._flags & significant_flags); + if (flags != other_flags) { + return flags < other_flags; } - if (components_given) { + + if ((_flags & (F_is_invalid | F_is_identity)) != 0) { + // All invalid transforms are equivalent to each other, and all + // identity transforms are equivalent to each other. + return 0; + } + + if ((_flags & F_components_given) != 0) { // If the transform was specified componentwise, compare them // componentwise. int c = _pos.compare_to(other._pos); @@ -195,6 +205,19 @@ make_identity() { return _identity_state; } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::make_invalid +// Access: Published, Static +// Description: Constructs an invalid transform; for instance, the +// result of inverting a singular matrix. +//////////////////////////////////////////////////////////////////// +CPT(TransformState) TransformState:: +make_invalid() { + TransformState *state = new TransformState; + state->_flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known; + return return_new(state); +} + //////////////////////////////////////////////////////////////////// // Function: TransformState::make_pos_hpr_scale // Access: Published, Static @@ -211,12 +234,12 @@ make_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr, return make_identity(); } - TransformState *attrib = new TransformState; - attrib->_pos = pos; - attrib->_hpr = hpr; - attrib->_scale = scale; - attrib->_flags = F_components_given | F_components_known | F_has_components; - return return_new(attrib); + TransformState *state = new TransformState; + state->_pos = pos; + state->_hpr = hpr; + state->_scale = scale; + state->_flags = F_components_given | F_components_known | F_has_components; + return return_new(state); } //////////////////////////////////////////////////////////////////// @@ -232,10 +255,10 @@ make_mat(const LMatrix4f &mat) { return make_identity(); } - TransformState *attrib = new TransformState; - attrib->_mat = mat; - attrib->_flags = F_mat_known; - return return_new(attrib); + TransformState *state = new TransformState; + state->_mat = mat; + state->_flags = F_mat_known; + return return_new(state); } //////////////////////////////////////////////////////////////////// @@ -283,7 +306,7 @@ set_hpr(const LVecBase3f &hpr) const { CPT(TransformState) TransformState:: set_scale(const LVecBase3f &scale) const { nassertr(has_components(), this); - return make_pos_hpr_scale(get_pos(), get_scale(), get_hpr()); + return make_pos_hpr_scale(get_pos(), get_hpr(), scale); } //////////////////////////////////////////////////////////////////// @@ -313,6 +336,14 @@ compose(const TransformState *other) const { return this; } + // If either transform is invalid, the result is invalid. + if (is_invalid()) { + return this; + } + if (other->is_invalid()) { + return other; + } + if (other == this) { // compose(this) has to be handled as a special case, because the // caching problem is so different. @@ -389,6 +420,14 @@ invert_compose(const TransformState *other) const { // Unlike compose(), the case of other->is_identity() is not quite as // trivial for invert_compose(). + // If either transform is invalid, the result is invalid. + if (is_invalid()) { + return this; + } + if (other->is_invalid()) { + return other; + } + if (other == this) { // a->invert_compose(a) always produces identity. return make_identity(); @@ -432,10 +471,18 @@ invert_compose(const TransformState *other) const { void TransformState:: output(ostream &out) const { out << "T:"; - if (is_identity()) { + if (is_invalid()) { + out << "(invalid)"; + + } else if (is_identity()) { out << "(identity)"; } else if (has_components()) { + if (components_given()) { + out << "c"; + } else { + out << "m"; + } char lead = '('; if (!get_pos().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f))) { out << lead << "pos " << get_pos(); @@ -477,11 +524,11 @@ write(ostream &out, int indent_level) const { // Description: This function is used to share a common TransformState // pointer for all equivalent TransformState objects. // -// See the similar logic in RenderAttrib. The idea is -// to create a new TransformState object and pass it +// See the similar logic in RenderState. The idea is to +// create a new TransformState object and pass it // through this function, which will share the pointer -// with a previously-created TransformState object if it is -// equivalent. +// with a previously-created TransformState object if it +// is equivalent. //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: return_new(TransformState *state) { @@ -517,6 +564,12 @@ return_new(TransformState *state) { //////////////////////////////////////////////////////////////////// CPT(TransformState) TransformState:: do_compose(const TransformState *other) const { + nassertr((_flags & F_is_invalid) == 0, this); + nassertr((other->_flags & F_is_invalid) == 0, other); + + // We should do this operation componentwise if both transforms were + // given componentwise. + LMatrix4f new_mat = other->get_mat() * get_mat(); return make_mat(new_mat); } @@ -528,11 +581,20 @@ 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. + nassertr((_flags & F_is_invalid) == 0, this); + nassertr((other->_flags & F_is_invalid) == 0, other); + + // We should do this operation componentwise if both transforms were + // given componentwise. + + // Perhaps we should cache the result of the inverse matrix + // operation separately, as a further optimization. LMatrix4f new_mat; - new_mat.invert_from(get_mat()); + bool invertible = new_mat.invert_from(get_mat()); + if (!invertible) { + return make_invalid(); + } new_mat = other->get_mat() * new_mat; return make_mat(new_mat); } @@ -545,6 +607,7 @@ do_invert_compose(const TransformState *other) const { //////////////////////////////////////////////////////////////////// void TransformState:: calc_singular() { + nassertv((_flags & F_is_invalid) == 0); bool singular = false; if (has_components()) { @@ -569,6 +632,7 @@ calc_singular() { //////////////////////////////////////////////////////////////////// void TransformState:: calc_components() { + nassertv((_flags & F_is_invalid) == 0); if ((_flags & F_is_identity) != 0) { _scale.set(1.0f, 1.0f, 1.0f); _hpr.set(0.0f, 0.0f, 0.0f); @@ -601,6 +665,7 @@ calc_components() { //////////////////////////////////////////////////////////////////// void TransformState:: calc_mat() { + nassertv((_flags & F_is_invalid) == 0); if ((_flags & F_is_identity) != 0) { _mat = LMatrix4f::ident_mat(); @@ -639,6 +704,11 @@ write_datagram(BamWriter *manager, Datagram &dg) { int flags = F_is_identity | F_singular_known; dg.add_uint16(flags); + } else if ((_flags & F_is_invalid) != 0) { + // Invalid, nothing much to that either. + int flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known; + dg.add_uint16(flags); + } else if ((_flags & F_components_given) != 0) { // A component-based transform. int flags = F_components_given | F_components_known | F_has_components; diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index aba2b1fbbd..3fd115198b 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -66,6 +66,7 @@ public: PUBLISHED: static CPT(TransformState) make_identity(); + INLINE static CPT(TransformState) make_invalid(); INLINE static CPT(TransformState) make_pos(const LVecBase3f &pos); INLINE static CPT(TransformState) make_hpr(const LVecBase3f &hpr); INLINE static CPT(TransformState) make_pos_hpr(const LVecBase3f &pos, @@ -78,11 +79,14 @@ PUBLISHED: static CPT(TransformState) make_mat(const LMatrix4f &mat); INLINE bool is_identity() const; + INLINE bool is_invalid() const; INLINE bool is_singular() const; INLINE bool has_components() const; + INLINE bool components_given() const; INLINE bool has_pos() const; INLINE bool has_hpr() const; INLINE bool has_scale() const; + INLINE bool has_mat() const; INLINE const LVecBase3f &get_pos() const; INLINE const LVecBase3f &get_hpr() const; INLINE const LVecBase3f &get_scale() const; @@ -149,6 +153,7 @@ private: F_components_known = 0x0010, F_has_components = 0x0020, F_mat_known = 0x0040, + F_is_invalid = 0x0080, }; LVecBase3f _pos, _hpr, _scale; LMatrix4f _mat; diff --git a/panda/src/testbed/pview.cxx b/panda/src/testbed/pview.cxx index 15c01c99ee..8b4d5288cf 100644 --- a/panda/src/testbed/pview.cxx +++ b/panda/src/testbed/pview.cxx @@ -35,6 +35,10 @@ #include "dSearchPath.h" #include "loader.h" #include "auto_bind.h" +#include "pStatClient.h" +#include "notify.h" +#include "qpnodePath.h" +#include "cullBinManager.h" // These are in support of legacy data graph operations. #include "namedNode.h" @@ -66,6 +70,12 @@ bool run_flag = true; static double start_time = 0.0; static int start_frame_count = 0; +// A priority number high enough to override any model file settings. +static const int override_priority = 100; + +// This is the main scene graph. +qpNodePath render; + void report_frame_rate() { double now = ClockObject::get_global_clock()->get_frame_time(); @@ -95,15 +105,15 @@ make_pipe() { // load-display Configrc variable. GraphicsPipe::resolve_modules(); - cerr << "Known pipe types:" << endl; - GraphicsPipe::get_factory().write_types(cerr, 2); + nout << "Known pipe types:" << endl; + GraphicsPipe::get_factory().write_types(nout, 2); PT(GraphicsPipe) pipe; pipe = GraphicsPipe::get_factory(). make_instance(InteractiveGraphicsPipe::get_class_type()); if (pipe == (GraphicsPipe*)0L) { - cerr << "No interactive pipe is available! Check your Configrc!\n"; + nout << "No interactive pipe is available! Check your Configrc!\n"; exit(1); } @@ -204,7 +214,7 @@ get_models(PandaNode *parent, int argc, char *argv[]) { for (int i = 1; i < argc; i++) { Filename filename = argv[i]; - cerr << "Loading " << filename << "\n"; + nout << "Loading " << filename << "\n"; // First, we always try to resolve a filename from the current // directory. This means a local filename will always be found @@ -213,10 +223,10 @@ get_models(PandaNode *parent, int argc, char *argv[]) { PT(PandaNode) node = loader.qpload_sync(filename); if (node == (PandaNode *)NULL) { - cerr << "Unable to load " << filename << "\n"; + nout << "Unable to load " << filename << "\n"; } else { - node->ls(cerr, 0); + node->ls(nout, 0); parent->add_child(node); } } @@ -259,9 +269,81 @@ event_esc(CPT_Event) { void event_f(CPT_Event) { + // 'f' : report frame rate. report_frame_rate(); } +void +event_t(CPT_Event) { + // 't' : toggle texture. + static bool texture_off = false; + + texture_off = !texture_off; + if (texture_off) { + nout << "Disabling texturing.\n"; + render.set_texture_off(override_priority); + } else { + nout << "Enabling texturing.\n"; + render.clear_texture(); + } +} + +void +event_w(CPT_Event) { + // 'w' : toggle wireframe. + static bool wireframe = false; + + wireframe = !wireframe; + if (wireframe) { + nout << "Setting wireframe mode.\n"; + render.set_render_mode_wireframe(override_priority); + } else { + nout << "Clearing wireframe mode.\n"; + render.clear_render_mode(); + } +} + +void +event_s(CPT_Event) { + // 's' : toggle state sorting by putting everything into an 'unsorted' bin. + static bool sorting_off = false; + + sorting_off = !sorting_off; + if (sorting_off) { + nout << "Disabling state sorting.\n"; + render.set_bin("unsorted", 0, override_priority); + } else { + nout << "Enabling state sorting.\n"; + render.clear_bin(); + } +} + +void +event_S(CPT_Event) { + // shift 'S' : active PStats. +#ifdef DO_PSTATS + nout << "Connecting to stats host" << endl; + PStatClient::connect(); +#else + nout << "Stats host not supported." << endl; +#endif +} + +void +event_A(CPT_Event) { + // shift 'A' : deactive PStats. +#ifdef DO_PSTATS + if (PStatClient::is_connected()) { + nout << "Disconnecting from stats host" << endl; + PStatClient::disconnect(); + } else { + nout << "Stats host is already disconnected." << endl; + } +#else + nout << "Stats host not supported." << endl; +#endif +} + int main(int argc, char *argv[]) { // First, we need a GraphicsPipe, before we can open a window. @@ -275,10 +357,14 @@ main(int argc, char *argv[]) { PT(qpCamera) camera = make_camera(window); // Now we just need to make a scene graph for the camera to render. - PT(PandaNode) render = new PandaNode("render"); - render->add_child(camera); + render = qpNodePath(new PandaNode("render")); + render.attach_new_node(camera); camera->set_scene(qpNodePath(render)); + // We will take advantage of this bin if the user toggles state + // sorting, above, in event_s(). + CullBinManager::get_global_ptr()->add_bin("unsorted", CullBinManager::BT_unsorted, 0); + // Set up a data graph for tracking user input. For now, this uses // the old-style graph interface. PT(NamedNode) data_root = new NamedNode("data_root"); @@ -290,16 +376,21 @@ main(int argc, char *argv[]) { event_handler.add_hook("escape", event_esc); event_handler.add_hook("q", event_esc); event_handler.add_hook("f", event_f); + event_handler.add_hook("t", event_t); + event_handler.add_hook("w", event_w); + event_handler.add_hook("s", event_s); + event_handler.add_hook("shift-s", event_S); + event_handler.add_hook("shift-a", event_A); // Put something in the scene graph to look at. - get_models(render, argc, argv); + get_models(render.node(), argc, argv); // If we happened to load up both a character file and its matching // animation file, attempt to bind them together now and start the // animations looping. AnimControlCollection anim_controls; - auto_bind(render, anim_controls, ~0); + auto_bind(render.node(), anim_controls, ~0); anim_controls.loop_all(true);