From 075cb14cbbb004fb167632b630d35e8d03e92e03 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 1 Nov 2017 21:49:24 +0100 Subject: [PATCH] bullet: implement debug draw via cull callback for efficiency Now the debug drawing will no longer happen if the debug node is not being visited by the cull pass, ie. in another scene graph. Furthermore, the generation code has been optimized a bit more. This change means it no longer inherits from GeomNode. Future improvements could include better culling (which is currently disabled entirely). Closes: #130 --- panda/src/bullet/bulletDebugNode.cxx | 256 +++++++++++++-------------- panda/src/bullet/bulletDebugNode.h | 20 ++- panda/src/bullet/bulletWorld.I | 19 +- panda/src/bullet/bulletWorld.cxx | 17 +- panda/src/bullet/bulletWorld.h | 3 +- 5 files changed, 163 insertions(+), 152 deletions(-) diff --git a/panda/src/bullet/bulletDebugNode.cxx b/panda/src/bullet/bulletDebugNode.cxx index 196a9af09f..b4575dd3cf 100644 --- a/panda/src/bullet/bulletDebugNode.cxx +++ b/panda/src/bullet/bulletDebugNode.cxx @@ -13,20 +13,25 @@ #include "bulletDebugNode.h" +#include "cullHandler.h" +#include "cullTraverser.h" +#include "cullableObject.h" #include "geomLines.h" #include "geomVertexData.h" #include "geomTriangles.h" #include "geomVertexFormat.h" #include "geomVertexWriter.h" #include "omniBoundingVolume.h" +#include "pStatTimer.h" TypeHandle BulletDebugNode::_type_handle; +PStatCollector BulletDebugNode::_pstat_debug("App:Bullet:DoPhysics:Debug"); /** * */ BulletDebugNode:: -BulletDebugNode(const char *name) : GeomNode(name) { +BulletDebugNode(const char *name) : PandaNode(name), _debug_stale(true) { _wireframe = true; _constraints = true; @@ -37,40 +42,6 @@ BulletDebugNode(const char *name) : GeomNode(name) { set_bounds(bounds); set_final(true); set_overall_hidden(true); - - // Lines - { - PT(GeomVertexData) vdata; - PT(Geom) geom; - PT(GeomLines) prim; - - vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); - - prim = new GeomLines(Geom::UH_stream); - prim->set_shade_model(Geom::SM_uniform); - - geom = new Geom(vdata); - geom->add_primitive(prim); - - add_geom(geom); - } - - // Triangles - { - PT(GeomVertexData) vdata; - PT(Geom) geom; - PT(GeomTriangles) prim; - - vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); - - prim = new GeomTriangles(Geom::UH_stream); - prim->set_shade_model(Geom::SM_uniform); - - geom = new Geom(vdata); - geom->add_primitive(prim); - - add_geom(geom); - } } /** @@ -174,101 +145,134 @@ draw_mask_changed() { } } +/** + * Returns true if there is some value to visiting this particular node during + * the cull traversal for any camera, false otherwise. This will be used to + * optimize the result of get_net_draw_show_mask(), so that any subtrees that + * contain only nodes for which is_renderable() is false need not be visited. + */ +bool BulletDebugNode:: +is_renderable() const { + return true; +} + +/** + * Adds the node's contents to the CullResult we are building up during the + * cull traversal, so that it will be drawn at render time. For most nodes + * other than GeomNodes, this is a do-nothing operation. + */ +void BulletDebugNode:: +add_for_draw(CullTraverser *trav, CullTraverserData &data) { + PT(Geom) debug_lines; + PT(Geom) debug_triangles; + + { + LightMutexHolder holder(_lock); + if (_debug_world == nullptr) { + return; + } + if (_debug_stale) { + nassertv(_debug_world != nullptr); + PStatTimer timer(_pstat_debug); + + // Collect debug geometry data + _drawer._lines.clear(); + _drawer._triangles.clear(); + + _debug_world->debugDrawWorld(); + + // Render lines + { + PT(GeomVertexData) vdata = + new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); + vdata->unclean_set_num_rows(_drawer._lines.size() * 2); + + GeomVertexWriter vwriter(vdata, InternalName::get_vertex()); + GeomVertexWriter cwriter(vdata, InternalName::get_color()); + + pvector::const_iterator lit; + for (lit = _drawer._lines.begin(); lit != _drawer._lines.end(); lit++) { + const Line &line = *lit; + + vwriter.set_data3(line._p0); + vwriter.set_data3(line._p1); + cwriter.set_data4(LVecBase4(line._color)); + cwriter.set_data4(LVecBase4(line._color)); + } + + PT(GeomPrimitive) prim = new GeomLines(Geom::UH_stream); + prim->set_shade_model(Geom::SM_uniform); + prim->add_next_vertices(_drawer._lines.size() * 2); + + debug_lines = new Geom(vdata); + debug_lines->add_primitive(prim); + _debug_lines = debug_lines; + } + + // Render triangles + { + PT(GeomVertexData) vdata = + new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); + vdata->unclean_set_num_rows(_drawer._triangles.size() * 3); + + GeomVertexWriter vwriter(vdata, InternalName::get_vertex()); + GeomVertexWriter cwriter(vdata, InternalName::get_color()); + + pvector::const_iterator tit; + for (tit = _drawer._triangles.begin(); tit != _drawer._triangles.end(); tit++) { + const Triangle &tri = *tit; + + vwriter.set_data3(tri._p0); + vwriter.set_data3(tri._p1); + vwriter.set_data3(tri._p2); + cwriter.set_data4(LVecBase4(tri._color)); + cwriter.set_data4(LVecBase4(tri._color)); + cwriter.set_data4(LVecBase4(tri._color)); + } + + PT(GeomPrimitive) prim = new GeomTriangles(Geom::UH_stream); + prim->set_shade_model(Geom::SM_uniform); + prim->add_next_vertices(_drawer._triangles.size() * 3); + + debug_triangles = new Geom(vdata); + debug_triangles->add_primitive(prim); + _debug_triangles = debug_triangles; + } + + // Clear collected data. + _drawer._lines.clear(); + _drawer._triangles.clear(); + + _debug_stale = false; + } else { + debug_lines = _debug_lines; + debug_triangles = _debug_triangles; + } + } + + // Record them without any state or transform. + trav->_geoms_pcollector.add_level(2); + { + CullableObject *object = + new CullableObject(move(debug_lines), RenderState::make_empty(), trav->get_scene()->get_cs_world_transform()); + trav->get_cull_handler()->record_object(object, trav); + } + { + CullableObject *object = + new CullableObject(move(debug_triangles), RenderState::make_empty(), trav->get_scene()->get_cs_world_transform()); + trav->get_cull_handler()->record_object(object, trav); + } +} + /** * */ void BulletDebugNode:: sync_b2p(btDynamicsWorld *world) { + LightMutexHolder holder(_lock); - if (is_overall_hidden()) return; - - nassertv(get_num_geoms() == 2); - - // Collect debug geometry data - _drawer._lines.clear(); - _drawer._triangles.clear(); - - world->debugDrawWorld(); - - // Get inverse of this node's net transform - NodePath np = NodePath::any_path((PandaNode *)this); - LMatrix4 m = np.get_net_transform()->get_mat(); - m.invert_in_place(); - - // Render lines - { - PT(GeomVertexData) vdata; - PT(Geom) geom; - PT(GeomLines) prim; - - vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); - - prim = new GeomLines(Geom::UH_stream); - prim->set_shade_model(Geom::SM_uniform); - - GeomVertexWriter vwriter = GeomVertexWriter(vdata, InternalName::get_vertex()); - GeomVertexWriter cwriter = GeomVertexWriter(vdata, InternalName::get_color()); - - int v = 0; - - pvector::const_iterator lit; - for (lit = _drawer._lines.begin(); lit != _drawer._lines.end(); lit++) { - Line line = *lit; - - vwriter.add_data3(m.xform_point(line._p0)); - vwriter.add_data3(m.xform_point(line._p1)); - cwriter.add_data4(LVecBase4(line._color)); - cwriter.add_data4(LVecBase4(line._color)); - - prim->add_vertex(v++); - prim->add_vertex(v++); - prim->close_primitive(); - } - - geom = new Geom(vdata); - geom->add_primitive(prim); - - set_geom(0, geom); - } - - // Render triangles - { - PT(GeomVertexData) vdata; - PT(Geom) geom; - PT(GeomTriangles) prim; - - vdata = new GeomVertexData("", GeomVertexFormat::get_v3c4(), Geom::UH_stream); - - prim = new GeomTriangles(Geom::UH_stream); - prim->set_shade_model(Geom::SM_uniform); - - GeomVertexWriter vwriter = GeomVertexWriter(vdata, InternalName::get_vertex()); - GeomVertexWriter cwriter = GeomVertexWriter(vdata, InternalName::get_color()); - - int v = 0; - - pvector::const_iterator tit; - for (tit = _drawer._triangles.begin(); tit != _drawer._triangles.end(); tit++) { - Triangle tri = *tit; - - vwriter.add_data3(m.xform_point(tri._p0)); - vwriter.add_data3(m.xform_point(tri._p1)); - vwriter.add_data3(m.xform_point(tri._p2)); - cwriter.add_data4(LVecBase4(tri._color)); - cwriter.add_data4(LVecBase4(tri._color)); - cwriter.add_data4(LVecBase4(tri._color)); - - prim->add_vertex(v++); - prim->add_vertex(v++); - prim->add_vertex(v++); - prim->close_primitive(); - } - - geom = new Geom(vdata); - geom->add_primitive(prim); - - set_geom(1, geom); - } + _debug_world = world; + _debug_stale = true; } /** @@ -431,8 +435,6 @@ register_with_read_factory() { */ void BulletDebugNode:: write_datagram(BamWriter *manager, Datagram &dg) { - // Don't upcall to GeomNode since we're not interested in storing the actual - // debug Geoms in the .bam file. PandaNode::write_datagram(manager, dg); dg.add_bool(_wireframe); @@ -464,8 +466,6 @@ make_from_bam(const FactoryParams ¶ms) { */ void BulletDebugNode:: fillin(DatagramIterator &scan, BamReader *manager) { - // Don't upcall to GeomNode since we're not interested in storing the actual - // debug Geoms in the .bam file. PandaNode::fillin(scan, manager); _wireframe = scan.get_bool(); diff --git a/panda/src/bullet/bulletDebugNode.h b/panda/src/bullet/bulletDebugNode.h index 987e1a7450..34943865f1 100644 --- a/panda/src/bullet/bulletDebugNode.h +++ b/panda/src/bullet/bulletDebugNode.h @@ -17,13 +17,12 @@ #include "pandabase.h" #include "bullet_includes.h" - -#include "geomNode.h" +#include "lightMutex.h" /** * */ -class EXPCL_PANDABULLET BulletDebugNode : public GeomNode { +class EXPCL_PANDABULLET BulletDebugNode : public PandaNode { PUBLISHED: BulletDebugNode(const char *name="debug"); @@ -53,6 +52,9 @@ public: virtual bool safe_to_combine_children() const; virtual bool safe_to_flatten_below() const; + virtual bool is_renderable() const; + virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data); + private: void sync_b2p(btDynamicsWorld *world); @@ -100,14 +102,22 @@ private: int _mode; }; + LightMutex _lock; DebugDraw _drawer; + bool _debug_stale; + btDynamicsWorld *_debug_world; + PT(Geom) _debug_lines; + PT(Geom) _debug_triangles; + bool _wireframe; bool _constraints; bool _bounds; friend class BulletWorld; + static PStatCollector _pstat_debug; + public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); @@ -121,9 +131,9 @@ public: return _type_handle; } static void init_type() { - GeomNode::init_type(); + PandaNode::init_type(); register_type(_type_handle, "BulletDebugNode", - GeomNode::get_class_type()); + PandaNode::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); diff --git a/panda/src/bullet/bulletWorld.I b/panda/src/bullet/bulletWorld.I index 958376ec60..c2ecd2cec3 100644 --- a/panda/src/bullet/bulletWorld.I +++ b/panda/src/bullet/bulletWorld.I @@ -55,21 +55,12 @@ INLINE BulletWorld:: */ INLINE void BulletWorld:: set_debug_node(BulletDebugNode *node) { - nassertv(node); - - _debug = node; - _world->setDebugDrawer(&(_debug->_drawer)); -} - -/** - * - */ -INLINE void BulletWorld:: -clear_debug_node() { - - _debug = NULL; - _world->setDebugDrawer(NULL); + if (node != _debug) { + clear_debug_node(); + _debug = node; + _world->setDebugDrawer(&(_debug->_drawer)); + } } /** diff --git a/panda/src/bullet/bulletWorld.cxx b/panda/src/bullet/bulletWorld.cxx index e7f6f3e267..a7942aee02 100644 --- a/panda/src/bullet/bulletWorld.cxx +++ b/panda/src/bullet/bulletWorld.cxx @@ -17,6 +17,7 @@ #include "bulletSoftBodyWorldInfo.h" #include "collideMask.h" +#include "lightMutexHolder.h" #define clamp(x, x_min, x_max) max(min(x, x_max), x_min) @@ -24,7 +25,6 @@ TypeHandle BulletWorld::_type_handle; PStatCollector BulletWorld::_pstat_physics("App:Bullet:DoPhysics"); PStatCollector BulletWorld::_pstat_simulation("App:Bullet:DoPhysics:Simulation"); -PStatCollector BulletWorld::_pstat_debug("App:Bullet:DoPhysics:Debug"); PStatCollector BulletWorld::_pstat_p2b("App:Bullet:DoPhysics:SyncP2B"); PStatCollector BulletWorld::_pstat_b2p("App:Bullet:DoPhysics:SyncB2P"); @@ -127,6 +127,19 @@ get_world_info() { return BulletSoftBodyWorldInfo(_info); } +/** + * Removes a debug node that has been assigned to this BulletWorld. + */ +void BulletWorld:: +clear_debug_node() { + if (_debug != nullptr) { + LightMutexHolder holder(_debug->_lock); + _debug->_debug_world = nullptr; + _world->setDebugDrawer(nullptr); + _debug = nullptr; + } +} + /** * */ @@ -184,9 +197,7 @@ do_physics(PN_stdfloat dt, int max_substeps, PN_stdfloat stepsize) { // Render debug if (_debug) { - _pstat_debug.start(); _debug->sync_b2p(_world); - _pstat_debug.stop(); } _pstat_physics.stop(); diff --git a/panda/src/bullet/bulletWorld.h b/panda/src/bullet/bulletWorld.h index b077e05f7a..558d086b37 100644 --- a/panda/src/bullet/bulletWorld.h +++ b/panda/src/bullet/bulletWorld.h @@ -63,7 +63,7 @@ PUBLISHED: // Debug INLINE void set_debug_node(BulletDebugNode *node); - INLINE void clear_debug_node(); + void clear_debug_node(); INLINE BulletDebugNode *get_debug_node() const; INLINE bool has_debug_node() const; @@ -208,7 +208,6 @@ private: static PStatCollector _pstat_physics; static PStatCollector _pstat_simulation; - static PStatCollector _pstat_debug; static PStatCollector _pstat_p2b; static PStatCollector _pstat_b2p;