Browse Source

steps toward async flatten

David Rose 18 years ago
parent
commit
9b575fbe58

+ 16 - 4
panda/src/chan/partBundle.I

@@ -146,15 +146,27 @@ get_root_xform() const {
   return cdata->_root_xform;
   return cdata->_root_xform;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundle::get_num_nodes
+//       Access: Published
+//  Description: Returns the number of PartBundleNodes that contain a
+//               pointer to this PartBundle.
+////////////////////////////////////////////////////////////////////
+INLINE int PartBundle::
+get_num_nodes() const {
+  return _nodes.size();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PartBundle::get_node
 //     Function: PartBundle::get_node
 //       Access: Published
 //       Access: Published
-//  Description: Returns the PartBundleNode associated with this
-//               PartBundle.
+//  Description: Returns the nth PartBundleNode associated with
+//               this PartBundle.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PartBundleNode *PartBundle::
 INLINE PartBundleNode *PartBundle::
-get_node() const {
-  return _node;
+get_node(int n) const {
+  nassertr(n >= 0 && n < (int)_nodes.size(), NULL);
+  return _nodes[n];
 }
 }
 
 
 
 

+ 27 - 9
panda/src/chan/partBundle.cxx

@@ -30,6 +30,8 @@
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "configVariableEnum.h"
 #include "configVariableEnum.h"
 
 
+#include <algorithm>
+
 TypeHandle PartBundle::_type_handle;
 TypeHandle PartBundle::_type_handle;
 
 
 
 
@@ -64,8 +66,7 @@ PartBundle(const PartBundle &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PartBundle::
 PartBundle::
 PartBundle(const string &name) : 
 PartBundle(const string &name) : 
-  PartGroup(name),
-  _node(NULL)
+  PartGroup(name)
 {
 {
 }
 }
 
 
@@ -319,16 +320,33 @@ control_activated(AnimControl *control) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PartBundle::set_node
+//     Function: PartBundle::add_node
+//       Access: Protected, Virtual
+//  Description: Adds the PartBundleNode pointer to the set of nodes
+//               associated with the PartBundle.  Normally called only
+//               by the PartBundleNode itself, for instance when the
+//               bundle is flattened with another node.
+////////////////////////////////////////////////////////////////////
+void PartBundle::
+add_node(PartBundleNode *node) {
+  nassertv(find(_nodes.begin(), _nodes.end(), node) == _nodes.end());
+  _nodes.push_back(node);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PartBundle::remove_node
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
-//  Description: Changes the PartBundleNode pointer associated with
-//               the PartBundle.  Normally called only by the
-//               PartBundleNode itself, for instance when the bundle
-//               is flattened with another node.
+//  Description: Removes the PartBundleNode pointer from the set of
+//               nodes associated with the PartBundle.  Normally
+//               called only by the PartBundleNode itself, for
+//               instance when the bundle is flattened with another
+//               node.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PartBundle::
 void PartBundle::
-set_node(PartBundleNode *node) {
-  _node = node;
+remove_node(PartBundleNode *node) {
+  Nodes::iterator ni = find(_nodes.begin(), _nodes.end(), node);
+  nassertv(ni != _nodes.end());
+  _nodes.erase(ni);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 7 - 3
panda/src/chan/partBundle.h

@@ -31,6 +31,7 @@
 #include "cycleDataReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "cycleDataWriter.h"
 #include "luse.h"
 #include "luse.h"
+#include "pvector.h"
 
 
 class AnimBundle;
 class AnimBundle;
 class PartBundleNode;
 class PartBundleNode;
@@ -105,7 +106,8 @@ PUBLISHED:
   INLINE void xform(const LMatrix4f &mat);
   INLINE void xform(const LMatrix4f &mat);
   INLINE const LMatrix4f &get_root_xform() const;
   INLINE const LMatrix4f &get_root_xform() const;
 
 
-  INLINE PartBundleNode *get_node() const;
+  INLINE int get_num_nodes() const;
+  INLINE PartBundleNode *get_node(int n) const;
 
 
   void clear_control_effects();
   void clear_control_effects();
   INLINE void set_control_effect(AnimControl *control, float effect);
   INLINE void set_control_effect(AnimControl *control, float effect);
@@ -128,7 +130,8 @@ public:
   virtual void control_activated(AnimControl *control);
   virtual void control_activated(AnimControl *control);
 
 
 protected:
 protected:
-  virtual void set_node(PartBundleNode *node);
+  virtual void add_node(PartBundleNode *node);
+  virtual void remove_node(PartBundleNode *node);
 
 
 private:
 private:
   class CData;
   class CData;
@@ -138,7 +141,8 @@ private:
   void recompute_net_blend(CData *cdata);
   void recompute_net_blend(CData *cdata);
   void clear_and_stop_intersecting(AnimControl *control, CData *cdata);
   void clear_and_stop_intersecting(AnimControl *control, CData *cdata);
 
 
-  PartBundleNode *_node;
+  typedef pvector<PartBundleNode *> Nodes;
+  Nodes _nodes;
 
 
   // This is the data that must be cycled between pipeline stages.
   // This is the data that must be cycled between pipeline stages.
   class CData : public CycleData {
   class CData : public CycleData {

+ 25 - 14
panda/src/chan/partBundleNode.cxx

@@ -33,23 +33,29 @@ PartBundleNode::
 ~PartBundleNode() {
 ~PartBundleNode() {
   Bundles::iterator bi;
   Bundles::iterator bi;
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
-    nassertv((*bi)->_node == this);
-    (*bi)->_node = NULL;
+    (*bi)->remove_node(this);
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PartBundleNode::safe_to_flatten
+//     Function: PartBundleNode::safe_to_transform
 //       Access: Public, Virtual
 //       Access: Public, Virtual
-//  Description: Returns true if it is generally safe to flatten out
-//               this particular kind of Node by duplicating
-//               instances, false otherwise (for instance, a Camera
-//               cannot be safely flattened, because the Camera
-//               pointer itself is meaningful).
+//  Description: Returns true if it is generally safe to transform
+//               this particular kind of PandaNode by calling the
+//               xform() method, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PartBundleNode::
 bool PartBundleNode::
-safe_to_flatten() const {
-  return false;
+safe_to_transform() const {
+  // If any of our bundles appear on multiple nodes, we can't
+  // transform any of them without transforming all of them at once.
+  Bundles::const_iterator bi;
+  for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
+    if ((*bi)->get_num_nodes() > 1) {
+      return false;
+    }
+  }
+
+  return true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -61,6 +67,11 @@ safe_to_flatten() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PartBundleNode::
 void PartBundleNode::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
+  if (mat.almost_equal(LMatrix4f::ident_mat())) {
+    // Don't bother.
+    return;
+  }
+
   Bundles::iterator bi;
   Bundles::iterator bi;
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
     (*bi)->xform(mat);
     (*bi)->xform(mat);
@@ -74,9 +85,8 @@ xform(const LMatrix4f &mat) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PartBundleNode::
 void PartBundleNode::
 add_bundle(PartBundle *bundle) {
 add_bundle(PartBundle *bundle) {
-  nassertv(bundle->_node == NULL);
   _bundles.push_back(bundle);
   _bundles.push_back(bundle);
-  bundle->set_node(this);
+  bundle->add_node(this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -91,7 +101,8 @@ steal_bundles(PartBundleNode *other) {
   for (bi = other->_bundles.begin(); bi != other->_bundles.end(); ++bi) {
   for (bi = other->_bundles.begin(); bi != other->_bundles.end(); ++bi) {
     PartBundle *bundle = (*bi);
     PartBundle *bundle = (*bi);
     _bundles.push_back(bundle);
     _bundles.push_back(bundle);
-    bundle->set_node(this);
+    bundle->remove_node(other);
+    bundle->add_node(this);
   }
   }
   other->_bundles.clear();
   other->_bundles.clear();
 }
 }
@@ -127,7 +138,7 @@ complete_pointers(TypedWritable **p_list, BamReader* manager) {
   Bundles::iterator bi;
   Bundles::iterator bi;
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
   for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
     (*bi) = DCAST(PartBundle, p_list[pi++]);
     (*bi) = DCAST(PartBundle, p_list[pi++]);
-    (*bi)->_node = this;
+    (*bi)->add_node(this);
   }
   }
 
 
   return pi;
   return pi;

+ 1 - 1
panda/src/chan/partBundleNode.h

@@ -43,7 +43,7 @@ protected:
 
 
 public:
 public:
   virtual ~PartBundleNode();
   virtual ~PartBundleNode();
-  virtual bool safe_to_flatten() const;
+  virtual bool safe_to_transform() const;
   virtual void xform(const LMatrix4f &mat);
   virtual void xform(const LMatrix4f &mat);
 
 
 PUBLISHED:
 PUBLISHED:

+ 33 - 39
panda/src/char/character.cxx

@@ -40,25 +40,29 @@ PStatCollector Character::_animation_pcollector("*:Animation");
 //  Description: Use make_copy() or copy_subgraph() to copy a Character.
 //  Description: Use make_copy() or copy_subgraph() to copy a Character.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 Character::
 Character::
-Character(const Character &copy) :
+Character(const Character &copy, bool copy_bundles) :
   PartBundleNode(copy),
   PartBundleNode(copy),
   _joints_pcollector(copy._joints_pcollector),
   _joints_pcollector(copy._joints_pcollector),
   _skinning_pcollector(copy._skinning_pcollector)
   _skinning_pcollector(copy._skinning_pcollector)
 {
 {
   set_cull_callback();
   set_cull_callback();
 
 
-  // Copy the bundle(s).
-  int num_bundles = copy.get_num_bundles();
-  for (int i = 0; i < num_bundles; ++i) {
-    PartBundle *orig_bundle = copy.get_bundle(i);
-    PartBundle *new_bundle = 
-      new CharacterJointBundle(orig_bundle->get_name());
-    add_bundle(new_bundle);
-
-    // Make a copy of the joint/slider hierarchy.
-    copy_joints(new_bundle, orig_bundle);
-  }
-
+  if (copy_bundles) {
+    // Copy the bundle(s).
+    int num_bundles = copy.get_num_bundles();
+    for (int i = 0; i < num_bundles; ++i) {
+      PartBundle *orig_bundle = copy.get_bundle(i);
+      PT(PartBundle) new_bundle = DCAST(PartBundle, orig_bundle->copy_subgraph());
+      add_bundle(new_bundle);
+    }
+  } else {
+    // Share the bundles.
+    int num_bundles = copy.get_num_bundles();
+    for (int i = 0; i < num_bundles; ++i) {
+      PartBundle *orig_bundle = copy.get_bundle(i);
+      add_bundle(orig_bundle);
+    }
+  }    
   _last_auto_update = -1.0;
   _last_auto_update = -1.0;
 }
 }
 
 
@@ -102,7 +106,22 @@ Character::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *Character::
 PandaNode *Character::
 make_copy() const {
 make_copy() const {
-  return new Character(*this);
+  return new Character(*this, true);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Character::dupe_for_flatten
+//       Access: Public, Virtual
+//  Description: This is similar to make_copy(), but it makes a copy
+//               for the specific purpose of flatten.  Typically, this
+//               will be a new PandaNode with a new pointer, but all
+//               of the internal data will always be shared with the
+//               original; whereas the new node returned by
+//               make_copy() might not share the internal data.
+////////////////////////////////////////////////////////////////////
+PandaNode *Character::
+dupe_for_flatten() const {
+  return new Character(*this, false);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -352,31 +371,6 @@ do_update() {
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: Character::copy_joints
-//       Access: Private
-//  Description: Recursively walks the joint/slider hierarchy and
-//               creates a new copy of the hierarchy.
-////////////////////////////////////////////////////////////////////
-void Character::
-copy_joints(PartGroup *copy, PartGroup *orig) {
-  if (copy->get_type() != orig->get_type()) {
-    char_cat.warning()
-      << "Don't know how to copy " << orig->get_type() << "\n";
-  }
-
-  PartGroup::Children::const_iterator ci;
-  for (ci = orig->_children.begin(); ci != orig->_children.end(); ++ci) {
-    PartGroup *orig_child = (*ci);
-    PartGroup *copy_child = orig_child->make_copy();
-    if (copy_child->is_of_type(CharacterJoint::get_class_type())) {
-      DCAST(CharacterJoint, copy_child)->set_character(this);
-    }
-    copy->_children.push_back(copy_child);
-    copy_joints(copy_child, orig_child);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Character::r_copy_children
 //     Function: Character::r_copy_children
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 2 - 2
panda/src/char/character.h

@@ -44,13 +44,14 @@ class ComputedVertices;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA Character : public PartBundleNode {
 class EXPCL_PANDA Character : public PartBundleNode {
 protected:
 protected:
-  Character(const Character &copy);
+  Character(const Character &copy, bool copy_bundles);
 
 
 public:
 public:
   Character(const string &name);
   Character(const string &name);
   virtual ~Character();
   virtual ~Character();
 
 
   virtual PandaNode *make_copy() const;
   virtual PandaNode *make_copy() const;
+  virtual PandaNode *dupe_for_flatten() const;
 
 
   virtual PandaNode *combine_with(PandaNode *other); 
   virtual PandaNode *combine_with(PandaNode *other); 
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
@@ -76,7 +77,6 @@ PUBLISHED:
 
 
 private:
 private:
   void do_update();
   void do_update();
-  void copy_joints(PartGroup *copy, PartGroup *orig);
 
 
   typedef pmap<const PandaNode *, PandaNode *> NodeMap;
   typedef pmap<const PandaNode *, PandaNode *> NodeMap;
   typedef pmap<const PartGroup *, PartGroup *> JointMap;
   typedef pmap<const PartGroup *, PartGroup *> JointMap;

+ 4 - 4
panda/src/char/characterJointBundle.I

@@ -37,10 +37,10 @@ CharacterJointBundle(const CharacterJointBundle &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CharacterJointBundle::get_node
 //     Function: CharacterJointBundle::get_node
 //       Access: Public
 //       Access: Public
-//  Description: Returns the Character node associated with this
-//               PartBundle.
+//  Description: Returns the nth Character associated with
+//               this PartBundle.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Character *CharacterJointBundle::
 INLINE Character *CharacterJointBundle::
-get_node() const {
-  return DCAST(Character, PartBundle::get_node());
+get_node(int n) const {
+  return DCAST(Character, PartBundle::get_node(n));
 }
 }

+ 27 - 7
panda/src/char/characterJointBundle.cxx

@@ -45,22 +45,42 @@ make_copy() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CharacterJointBundle::set_node
+//     Function: CharacterJointBundle::add_node
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
-//  Description: Changes the PartBundleNode pointer associated with
-//               the PartBundle.  Normally called only by the
-//               PartBundleNode itself, for instance when the bundle
-//               is flattened with another node.
+//  Description: Adds the PartBundleNode pointer to the set of nodes
+//               associated with the PartBundle.  Normally called only
+//               by the PartBundleNode itself, for instance when the
+//               bundle is flattened with another node.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CharacterJointBundle::
 void CharacterJointBundle::
-set_node(PartBundleNode *node) {
-  PartBundle::set_node(node);
+add_node(PartBundleNode *node) {
+  PartBundle::add_node(node);
   if (node->is_of_type(Character::get_class_type())) {
   if (node->is_of_type(Character::get_class_type())) {
     Character *character = DCAST(Character, node);
     Character *character = DCAST(Character, node);
     r_set_character(this, character);
     r_set_character(this, character);
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CharacterJointBundle::remove_node
+//       Access: Protected, Virtual
+//  Description: Removes the PartBundleNode pointer from the set of
+//               nodes associated with the PartBundle.  Normally
+//               called only by the PartBundleNode itself, for
+//               instance when the bundle is flattened with another
+//               node.
+////////////////////////////////////////////////////////////////////
+void CharacterJointBundle::
+remove_node(PartBundleNode *node) {
+  PartBundle::remove_node(node);
+
+  // If there is still a Character on the list, assign that one to all
+  // of the joints.
+  if (get_num_nodes() > 0) {
+    r_set_character(this, get_node(get_num_nodes() - 1));
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CharacterJointBundle::r_set_character
 //     Function: CharacterJointBundle::r_set_character
 //       Access: Private
 //       Access: Private

+ 3 - 2
panda/src/char/characterJointBundle.h

@@ -40,11 +40,12 @@ public:
   CharacterJointBundle(const string &name = "");
   CharacterJointBundle(const string &name = "");
 
 
 PUBLISHED:
 PUBLISHED:
-  INLINE Character *get_node() const;
+  INLINE Character *get_node(int n) const;
 
 
 protected:
 protected:
   virtual PartGroup *make_copy() const;
   virtual PartGroup *make_copy() const;
-  virtual void set_node(PartBundleNode *node);
+  virtual void add_node(PartBundleNode *node);
+  virtual void remove_node(PartBundleNode *node);
 
 
 private:
 private:
   void r_set_character(PartGroup *group, Character *character);
   void r_set_character(PartGroup *group, Character *character);

+ 1 - 1
panda/src/display/graphicsOutput.cxx

@@ -315,7 +315,7 @@ add_render_texture(Texture *tex, RenderTextureMode mode,
   tex->set_x_size(Texture::up_to_power_2(get_x_size()));
   tex->set_x_size(Texture::up_to_power_2(get_x_size()));
   tex->set_y_size(Texture::up_to_power_2(get_y_size()));
   tex->set_y_size(Texture::up_to_power_2(get_y_size()));
 
 
-  if ((mode == RTM_bind_or_copy)&&(support_render_texture==0)) {
+  if (mode == RTM_bind_or_copy && !support_render_texture) {
     mode = RTM_copy_texture;
     mode = RTM_copy_texture;
   }
   }
   else {
   else {

+ 1 - 1
panda/src/framework/windowFramework.cxx

@@ -1043,7 +1043,7 @@ make_camera() {
 
 
   PT(Lens) lens = new PerspectiveLens;
   PT(Lens) lens = new PerspectiveLens;
 
 
-  if (aspect_ratio != 0.0f) {
+  if (aspect_ratio != 0.0) {
     // If we're given an explict aspect ratio, use it
     // If we're given an explict aspect ratio, use it
     lens->set_aspect_ratio(aspect_ratio);
     lens->set_aspect_ratio(aspect_ratio);
 
 

+ 3 - 0
panda/src/pgraph/Sources.pp

@@ -69,6 +69,7 @@
     lodNode.I lodNode.h \
     lodNode.I lodNode.h \
     materialAttrib.I materialAttrib.h \
     materialAttrib.I materialAttrib.h \
     materialCollection.I materialCollection.h \
     materialCollection.I materialCollection.h \
+    modelFlattenRequest.I modelFlattenRequest.h \
     modelLoadRequest.I modelLoadRequest.h \
     modelLoadRequest.I modelLoadRequest.h \
     modelNode.I modelNode.h \
     modelNode.I modelNode.h \
     modelPool.I modelPool.h \
     modelPool.I modelPool.h \
@@ -176,6 +177,7 @@
     lodNode.cxx \
     lodNode.cxx \
     materialAttrib.cxx \
     materialAttrib.cxx \
     materialCollection.cxx \
     materialCollection.cxx \
+    modelFlattenRequest.cxx \
     modelLoadRequest.cxx \
     modelLoadRequest.cxx \
     modelNode.cxx \
     modelNode.cxx \
     modelPool.cxx \
     modelPool.cxx \
@@ -278,6 +280,7 @@
     lodNode.I lodNode.h \
     lodNode.I lodNode.h \
     materialAttrib.I materialAttrib.h \
     materialAttrib.I materialAttrib.h \
     materialCollection.I materialCollection.h \
     materialCollection.I materialCollection.h \
+    modelFlattenRequest.I modelFlattenRequest.h \
     modelLoadRequest.I modelLoadRequest.h \
     modelLoadRequest.I modelLoadRequest.h \
     modelNode.I modelNode.h \
     modelNode.I modelNode.h \
     modelPool.I modelPool.h \
     modelPool.I modelPool.h \

+ 8 - 0
panda/src/pgraph/config_pgraph.cxx

@@ -60,6 +60,7 @@
 #include "loaderFileTypeRegistry.h"
 #include "loaderFileTypeRegistry.h"
 #include "lodNode.h"
 #include "lodNode.h"
 #include "materialAttrib.h"
 #include "materialAttrib.h"
+#include "modelFlattenRequest.h"
 #include "modelLoadRequest.h"
 #include "modelLoadRequest.h"
 #include "modelNode.h"
 #include "modelNode.h"
 #include "modelRoot.h"
 #include "modelRoot.h"
@@ -127,6 +128,12 @@ ConfigVariableBool unambiguous_graph
           "assertion failure instead of just a warning (which can then be "
           "assertion failure instead of just a warning (which can then be "
           "trapped with assert-abort)."));
           "trapped with assert-abort)."));
 
 
+ConfigVariableBool no_unsupported_copy
+("no-unsupported-copy", false,
+ PRC_DESC("Set this true to make an attempt to copy an unsupported type "
+          "generate an assertion failure instead of just a warning (which "
+          "can then be trapped with assert-abort)."));
+
 ConfigVariableBool allow_unrelated_wrt
 ConfigVariableBool allow_unrelated_wrt
 ("allow-unrelated-wrt", true,
 ("allow-unrelated-wrt", true,
  PRC_DESC("Set this true to allow unrelated NodePaths (that is, nodes which "
  PRC_DESC("Set this true to allow unrelated NodePaths (that is, nodes which "
@@ -341,6 +348,7 @@ init_libpgraph() {
   LoaderFileType::init_type();
   LoaderFileType::init_type();
   LoaderFileTypeBam::init_type();
   LoaderFileTypeBam::init_type();
   MaterialAttrib::init_type();
   MaterialAttrib::init_type();
+  ModelFlattenRequest::init_type();
   ModelLoadRequest::init_type();
   ModelLoadRequest::init_type();
   ModelNode::init_type();
   ModelNode::init_type();
   ModelRoot::init_type();
   ModelRoot::init_type();

+ 1 - 0
panda/src/pgraph/config_pgraph.h

@@ -37,6 +37,7 @@ NotifyCategoryDecl(portal, EXPCL_PANDA, EXPTP_PANDA);
 extern ConfigVariableBool fake_view_frustum_cull;
 extern ConfigVariableBool fake_view_frustum_cull;
 extern ConfigVariableBool allow_portal_cull;
 extern ConfigVariableBool allow_portal_cull;
 extern ConfigVariableBool unambiguous_graph;
 extern ConfigVariableBool unambiguous_graph;
+extern ConfigVariableBool no_unsupported_copy;
 extern ConfigVariableBool allow_unrelated_wrt;
 extern ConfigVariableBool allow_unrelated_wrt;
 extern ConfigVariableBool paranoid_compose;
 extern ConfigVariableBool paranoid_compose;
 extern ConfigVariableBool compose_componentwise;
 extern ConfigVariableBool compose_componentwise;

+ 1 - 0
panda/src/pgraph/findApproxLevelEntry.I

@@ -47,6 +47,7 @@ FindApproxLevelEntry(const FindApproxLevelEntry &parent,
   _approx_path(parent._approx_path),
   _approx_path(parent._approx_path),
   _next(next)
   _next(next)
 {
 {
+  nassertv(validate_ptr(this) && validate_ptr(&parent));
   nassertv(_node_path.is_valid());
   nassertv(_node_path.is_valid());
 }
 }
 
 

+ 66 - 0
panda/src/pgraph/modelFlattenRequest.I

@@ -0,0 +1,66 @@
+// Filename: modelFlattenRequest.I
+// Created by:  drose (30Mar07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModelFlattenRequest::Constructor
+//       Access: Published
+//  Description: Create a new ModelFlattenRequest, and add it to the loader
+//               via load_async(), to begin an asynchronous load.
+////////////////////////////////////////////////////////////////////
+INLINE ModelFlattenRequest::
+ModelFlattenRequest(PandaNode *orig) :
+  AsyncTask(orig->get_name()),
+  _orig(orig),
+  _is_ready(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModelFlattenRequest::get_orig
+//       Access: Published
+//  Description: Returns the original, unflattened node.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *ModelFlattenRequest::
+get_orig() const {
+  return _orig;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModelFlattenRequest::is_ready
+//       Access: Published
+//  Description: Returns true if this request has completed, false if
+//               it is still pending.  When this returns true, you may
+//               retrieve the model loaded by calling get_result().
+////////////////////////////////////////////////////////////////////
+INLINE bool ModelFlattenRequest::
+is_ready() const {
+  return _is_ready;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModelFlattenRequest::get_model
+//       Access: Published
+//  Description: Returns the flattened copy of the model.  It is an
+//               error to call this unless is_ready() returns true.
+////////////////////////////////////////////////////////////////////
+INLINE PandaNode *ModelFlattenRequest::
+get_model() const {
+  nassertr(_is_ready, NULL);
+  return _model;
+}

+ 42 - 0
panda/src/pgraph/modelFlattenRequest.cxx

@@ -0,0 +1,42 @@
+// Filename: modelFlattenRequest.cxx
+// Created by:  drose (30Mar07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "modelFlattenRequest.h"
+#include "nodePath.h"
+
+TypeHandle ModelFlattenRequest::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModelFlattenRequest::do_task
+//       Access: Protected, Virtual
+//  Description: Performs the task: that is, copies and flattens the
+//               model.
+////////////////////////////////////////////////////////////////////
+bool ModelFlattenRequest::
+do_task() {
+  // We make another instance of the original node, so we can safely
+  // flatten that without affecting the original copy.
+  NodePath np("flatten_root");
+  np.attach_new_node(_orig);
+  np.flatten_strong();
+  _model = np.get_child(0).node();
+  _is_ready = true;
+
+  // Don't continue the task; we're done.
+  return false;
+}

+ 74 - 0
panda/src/pgraph/modelFlattenRequest.h

@@ -0,0 +1,74 @@
+// Filename: modelFlattenRequest.h
+// Created by:  drose (30Mar07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MODELFLATTENREQUEST
+#define MODELFLATTENREQUEST
+
+#include "pandabase.h"
+
+#include "asyncTask.h"
+#include "pandaNode.h"
+#include "pointerTo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ModelFlattenRequest
+// Description : This class object manages a single asynchronous
+//               request to flatten a model.  The model will be
+//               duplicated and flattened in a sub-thread (if
+//               threading is available), without affecting the
+//               original model; and when the result is done it may be
+//               retrieved from this object.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA ModelFlattenRequest : public AsyncTask {
+PUBLISHED:
+  INLINE ModelFlattenRequest(PandaNode *orig);
+  
+  INLINE PandaNode *get_orig() const;
+  
+  INLINE bool is_ready() const;
+  INLINE PandaNode *get_model() const;
+  
+protected:
+  virtual bool do_task();
+  
+private:
+  PT(PandaNode) _orig;
+  bool _is_ready;
+  PT(PandaNode) _model;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    AsyncTask::init_type();
+    register_type(_type_handle, "ModelFlattenRequest",
+                  AsyncTask::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 "modelFlattenRequest.I"
+
+#endif

+ 23 - 3
panda/src/pgraph/pandaNode.cxx

@@ -196,14 +196,30 @@ make_copy() const {
   return new PandaNode(*this);
   return new PandaNode(*this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::dupe_for_flatten
+//       Access: Public, Virtual
+//  Description: This is similar to make_copy(), but it makes a copy
+//               for the specific purpose of flatten.  Typically, this
+//               will be a new PandaNode with a new pointer, but all
+//               of the internal data will always be shared with the
+//               original; whereas the new node returned by
+//               make_copy() might not share the internal data.
+////////////////////////////////////////////////////////////////////
+PandaNode *PandaNode::
+dupe_for_flatten() const {
+  return make_copy();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::safe_to_flatten
 //     Function: PandaNode::safe_to_flatten
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: Returns true if it is generally safe to flatten out
 //  Description: Returns true if it is generally safe to flatten out
 //               this particular kind of PandaNode by duplicating
 //               this particular kind of PandaNode by duplicating
-//               instances, false otherwise (for instance, a Camera
-//               cannot be safely flattened, because the Camera
-//               pointer itself is meaningful).
+//               instances (by calling dupe_for_flatten()), false
+//               otherwise (for instance, a Camera cannot be safely
+//               flattened, because the Camera pointer itself is
+//               meaningful).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PandaNode::
 bool PandaNode::
 safe_to_flatten() const {
 safe_to_flatten() const {
@@ -2397,6 +2413,10 @@ r_copy_subgraph(PandaNode::InstanceMap &inst_map, Thread *current_thread) const
   if (copy->get_type() != get_type()) {
   if (copy->get_type() != get_type()) {
     pgraph_cat.warning()
     pgraph_cat.warning()
       << "Don't know how to copy nodes of type " << get_type() << "\n";
       << "Don't know how to copy nodes of type " << get_type() << "\n";
+
+    if (no_unsupported_copy) {
+      nassertr(false, NULL);
+    }
   }
   }
 
 
   copy->r_copy_children(this, inst_map, current_thread);
   copy->r_copy_children(this, inst_map, current_thread);

+ 1 - 0
panda/src/pgraph/pandaNode.h

@@ -83,6 +83,7 @@ private:
 
 
 public:
 public:
   virtual PandaNode *make_copy() const;
   virtual PandaNode *make_copy() const;
+  virtual PandaNode *dupe_for_flatten() const;
 
 
   virtual bool safe_to_flatten() const;
   virtual bool safe_to_flatten() const;
   virtual bool safe_to_transform() const;
   virtual bool safe_to_transform() const;

+ 1 - 0
panda/src/pgraph/pgraph_composite3.cxx

@@ -12,6 +12,7 @@
 #include "lodNode.cxx"
 #include "lodNode.cxx"
 #include "materialAttrib.cxx"
 #include "materialAttrib.cxx"
 #include "materialCollection.cxx"
 #include "materialCollection.cxx"
+#include "modelFlattenRequest.cxx"
 #include "modelLoadRequest.cxx"
 #include "modelLoadRequest.cxx"
 #include "modelNode.cxx"
 #include "modelNode.cxx"
 #include "modelPool.cxx"
 #include "modelPool.cxx"

+ 6 - 2
panda/src/pgraph/sceneGraphReducer.cxx

@@ -175,15 +175,19 @@ r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
           pgraph_cat.spam()
           pgraph_cat.spam()
             << "Cannot duplicate nodes of type " << child_node->get_type()
             << "Cannot duplicate nodes of type " << child_node->get_type()
             << ".\n";
             << ".\n";
-          resist_copy = true;
         }
         }
+        resist_copy = true;
 
 
       } else {
       } else {
-        PT(PandaNode) new_node = child_node->make_copy();
+        PT(PandaNode) new_node = child_node->dupe_for_flatten();
         if (new_node->get_type() != child_node->get_type()) {
         if (new_node->get_type() != child_node->get_type()) {
           pgraph_cat.error()
           pgraph_cat.error()
             << "Don't know how to copy nodes of type "
             << "Don't know how to copy nodes of type "
             << child_node->get_type() << "\n";
             << child_node->get_type() << "\n";
+
+          if (no_unsupported_copy) {
+            nassertv(false);
+          }
           resist_copy = true;
           resist_copy = true;
 
 
         } else {
         } else {

+ 1 - 1
panda/src/pgraph/workingNodePath.h

@@ -74,7 +74,7 @@ private:
   const WorkingNodePath *_next;
   const WorkingNodePath *_next;
   PT(NodePathComponent) _start;
   PT(NodePathComponent) _start;
 
 
-  PandaNode *_node;
+  PT(PandaNode) _node;
 };
 };
 
 
 INLINE ostream &operator << (ostream &out, const WorkingNodePath &node_path);
 INLINE ostream &operator << (ostream &out, const WorkingNodePath &node_path);

+ 7 - 0
panda/src/pipeline/config_pipeline.cxx

@@ -32,6 +32,13 @@ ConfigureFn(config_pipeline) {
   init_libpipeline();
   init_libpipeline();
 }
 }
 
 
+ConfigVariableBool support_threads
+("support-threads", true,
+ PRC_DESC("Set this false to disallow the creation of threads using Panda's "
+          "Thread interface, even if threading support is compiled in.  This "
+          "does not affect the operation of mutexes and other synchronization "
+          "primitives, just the creation of threads."));
+
 ConfigVariableInt thread_stack_size
 ConfigVariableInt thread_stack_size
 ("thread-stack-size", 4194304,
 ("thread-stack-size", 4194304,
  PRC_DESC("Specifies the minimum size, in bytes, of the stack that will be "
  PRC_DESC("Specifies the minimum size, in bytes, of the stack that will be "

+ 1 - 0
panda/src/pipeline/config_pipeline.h

@@ -29,6 +29,7 @@ ConfigureDecl(config_pipeline, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(pipeline, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(pipeline, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(thread, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(thread, EXPCL_PANDA, EXPTP_PANDA);
 
 
+extern ConfigVariableBool support_threads;
 extern ConfigVariableInt thread_stack_size;
 extern ConfigVariableInt thread_stack_size;
 extern ConfigVariableBool threads_always_global;
 extern ConfigVariableBool threads_always_global;
 extern ConfigVariableBool threads_never_global;
 extern ConfigVariableBool threads_never_global;

+ 3 - 0
panda/src/pipeline/thread.I

@@ -233,6 +233,9 @@ get_current_pipeline_stage() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool Thread::
 INLINE bool Thread::
 is_threading_supported() {
 is_threading_supported() {
+  if (!support_threads) {
+    return false;
+  }
   return ThreadImpl::is_threading_supported();
   return ThreadImpl::is_threading_supported();
 }
 }
 
 

+ 6 - 0
panda/src/pipeline/thread.cxx

@@ -146,6 +146,12 @@ bool Thread::
 start(ThreadPriority priority, bool global, bool joinable) {
 start(ThreadPriority priority, bool global, bool joinable) {
   nassertr(!_started, false);
   nassertr(!_started, false);
 
 
+  if (!support_threads) {
+    thread_cat.warning()
+      << *this << " could not be started: support-threads is false.\n";
+    return false;
+  }
+
   if (threads_always_global) {
   if (threads_always_global) {
     global = true;
     global = true;
   } else if (threads_never_global) {
   } else if (threads_never_global) {

+ 1 - 0
panda/src/pipeline/thread.h

@@ -25,6 +25,7 @@
 #include "threadPriority.h"
 #include "threadPriority.h"
 #include "threadImpl.h"
 #include "threadImpl.h"
 #include "pnotify.h"
 #include "pnotify.h"
+#include "config_pipeline.h"
 
 
 class Mutex;
 class Mutex;
 class ReMutex;
 class ReMutex;

+ 0 - 3
panda/src/pipeline/threadWin32Impl.h

@@ -29,9 +29,6 @@
 #include "mutexWin32Impl.h"
 #include "mutexWin32Impl.h"
 #include "conditionVarWin32Impl.h"
 #include "conditionVarWin32Impl.h"
 
 
-#define WIN32_LEAN_AND_MEAN 1
-#include <windows.h>
-
 class Thread;
 class Thread;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////