浏览代码

bam 6.40: support writing NodePaths, allows instanced lights and clip planes

rdb 10 年之前
父节点
当前提交
41fad59ae8

+ 28 - 0
panda/src/express/ordered_vector.I

@@ -694,6 +694,34 @@ pop_back() {
   _vector.pop_back();
   _vector.pop_back();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ordered_vector::resize
+//       Access: Public
+//  Description: Resizes the vector to contain n elements.  This
+//               should not be used except to populate the vector
+//               for the first time.
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare, class Vector>
+INLINE void ordered_vector<Key, Compare, Vector>::
+resize(SIZE_TYPE n) {
+  TAU_PROFILE("ordered_vector::resize()", " ", TAU_USER);
+  _vector.resize(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ordered_vector::resize
+//       Access: Public
+//  Description: Resizes the vector to contain n elements.  This
+//               should not be used except to populate the vector
+//               for the first time.
+////////////////////////////////////////////////////////////////////
+template<class Key, class Compare, class Vector>
+INLINE void ordered_vector<Key, Compare, Vector>::
+resize(SIZE_TYPE n, const VALUE_TYPE &value) {
+  TAU_PROFILE("ordered_vector::resize()", " ", TAU_USER);
+  _vector.resize(n, value);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ordered_vector::nci
 //     Function: ordered_vector::nci
 //       Access: Private
 //       Access: Private

+ 2 - 0
panda/src/express/ordered_vector.h

@@ -219,6 +219,8 @@ public:
 
 
   INLINE void push_back(const VALUE_TYPE &key);
   INLINE void push_back(const VALUE_TYPE &key);
   INLINE void pop_back();
   INLINE void pop_back();
+  INLINE void resize(SIZE_TYPE n);
+  INLINE void resize(SIZE_TYPE n, const VALUE_TYPE &value);
 
 
 private:
 private:
   INLINE ITERATOR nci(CONST_ITERATOR i);
   INLINE ITERATOR nci(CONST_ITERATOR i);

+ 6 - 6
panda/src/gobj/texturePool.cxx

@@ -341,7 +341,7 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
 
 
   if (store_record && tex->is_cacheable()) {
   if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     // Store the on-disk cache record for next time.
-    record->set_data(tex, tex);
+    record->set_data(tex);
     cache->store(record);
     cache->store(record);
   }
   }
 
 
@@ -473,7 +473,7 @@ ns_load_texture(const Filename &orig_filename,
 
 
   if (store_record && tex->is_cacheable()) {
   if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     // Store the on-disk cache record for next time.
-    record->set_data(tex, tex);
+    record->set_data(tex);
     cache->store(record);
     cache->store(record);
   }
   }
 
 
@@ -585,7 +585,7 @@ ns_load_3d_texture(const Filename &filename_pattern,
 
 
   if (store_record && tex->is_cacheable()) {
   if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     // Store the on-disk cache record for next time.
-    record->set_data(tex, tex);
+    record->set_data(tex);
     cache->store(record);
     cache->store(record);
   }
   }
 
 
@@ -691,7 +691,7 @@ ns_load_2d_texture_array(const Filename &filename_pattern,
 
 
   if (store_record && tex->is_cacheable()) {
   if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     // Store the on-disk cache record for next time.
-    record->set_data(tex, tex);
+    record->set_data(tex);
     cache->store(record);
     cache->store(record);
   }
   }
 
 
@@ -790,7 +790,7 @@ ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps,
 
 
   if (store_record && tex->is_cacheable()) {
   if (store_record && tex->is_cacheable()) {
     // Store the on-disk cache record for next time.
     // Store the on-disk cache record for next time.
-    record->set_data(tex, tex);
+    record->set_data(tex);
     cache->store(record);
     cache->store(record);
   }
   }
 
 
@@ -1166,7 +1166,7 @@ try_load_cache(PT(Texture) &tex, BamCache *cache, const Filename &filename,
                   // from the cache.  To keep the cache current,
                   // from the cache.  To keep the cache current,
                   // rewrite it to the cache now, in its newly
                   // rewrite it to the cache now, in its newly
                   // compressed form.
                   // compressed form.
-                  record->set_data(tex, tex);
+                  record->set_data(tex);
                   cache->store(record);
                   cache->store(record);
                   compressed_cache_record = true;
                   compressed_cache_record = true;
                 }
                 }

+ 69 - 54
panda/src/pgraph/clipPlaneAttrib.cxx

@@ -960,12 +960,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   // write the off planes pointers if any
   // write the off planes pointers if any
   Planes::const_iterator fi;
   Planes::const_iterator fi;
   for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
   for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
-    NodePath plane = (*fi);
-
-    // Since we can't write out a NodePath, we write out just the
-    // plain PandaNode.  The user can use the AttribNodeRegistry on
-    // re-read if there is any ambiguity that needs to be resolved.
-    manager->write_pointer(dg, plane.node());
+    (*fi).write_datagram(manager, dg);
   }
   }
 
 
   // write the number of on planes
   // write the number of on planes
@@ -973,8 +968,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   // write the on planes pointers if any
   // write the on planes pointers if any
   Planes::const_iterator nti;
   Planes::const_iterator nti;
   for (nti = _on_planes.begin(); nti != _on_planes.end(); ++nti) {
   for (nti = _on_planes.begin(); nti != _on_planes.end(); ++nti) {
-    NodePath plane = (*nti);
-    manager->write_pointer(dg, plane.node());
+    (*nti).write_datagram(manager, dg);
   }
   }
 }
 }
 
 
@@ -990,38 +984,62 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = RenderAttrib::complete_pointers(p_list, manager);
   int pi = RenderAttrib::complete_pointers(p_list, manager);
   AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
   AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
 
 
-  Planes::iterator ci = _off_planes.begin();
-  while (ci != _off_planes.end()) {
-    PandaNode *node;
-    DCAST_INTO_R(node, p_list[pi++], pi);
-    
-    // We go through some effort to look up the node in the registry
-    // without creating a NodePath around it first (which would up,
-    // and then down, the reference count, possibly deleting the
-    // node).
-    int ni = areg->find_node(node->get_type(), node->get_name());
-    if (ni != -1) {
-      (*ci) = areg->get_node(ni);
-    } else {
-      (*ci) = NodePath(node);
+  if (manager->get_file_minor_ver() >= 40) {
+    for (int i = 0; i < _off_planes.size(); ++i) {
+      pi += _off_planes[i].complete_pointers(p_list + pi, manager);
+
+      int n = areg->find_node(_off_planes[i]);
+      if (n != -1) {
+        // If it's in the registry, replace it.
+        _off_planes[i] = areg->get_node(n);
+      }
     }
     }
-    ++ci;
-  }
-  _off_planes.sort();
 
 
-  ci = _on_planes.begin();
-  while (ci != _on_planes.end()) {
-    PandaNode *node;
-    DCAST_INTO_R(node, p_list[pi++], pi);
+    for (int i = 0; i < _on_planes.size(); ++i) {
+      pi += _on_planes[i].complete_pointers(p_list + pi, manager);
 
 
-    int ni = areg->find_node(node->get_type(), node->get_name());
-    if (ni != -1) {
-      (*ci) = areg->get_node(ni);
-    } else {
-      (*ci) = NodePath(node);
+      int n = areg->find_node(_on_planes[i]);
+      if (n != -1) {
+        // If it's in the registry, replace it.
+        _on_planes[i] = areg->get_node(n);
+      }
+    }
+
+  } else {
+    Planes::iterator ci = _off_planes.begin();
+    while (ci != _off_planes.end()) {
+      PandaNode *node;
+      DCAST_INTO_R(node, p_list[pi++], pi);
+
+      // We go through some effort to look up the node in the registry
+      // without creating a NodePath around it first (which would up,
+      // and then down, the reference count, possibly deleting the
+      // node).
+      int ni = areg->find_node(node->get_type(), node->get_name());
+      if (ni != -1) {
+        (*ci) = areg->get_node(ni);
+      } else {
+        (*ci) = NodePath(node);
+      }
+      ++ci;
+    }
+
+    ci = _on_planes.begin();
+    while (ci != _on_planes.end()) {
+      PandaNode *node;
+      DCAST_INTO_R(node, p_list[pi++], pi);
+
+      int ni = areg->find_node(node->get_type(), node->get_name());
+      if (ni != -1) {
+        (*ci) = areg->get_node(ni);
+      } else {
+        (*ci) = NodePath(node);
+      }
+      ++ci;
     }
     }
-    ++ci;
   }
   }
+
+  _off_planes.sort();
   _on_planes.sort();
   _on_planes.sort();
 
 
   return pi;
   return pi;
@@ -1073,31 +1091,28 @@ void ClipPlaneAttrib::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderAttrib::fillin(scan, manager);
   RenderAttrib::fillin(scan, manager);
 
 
-  // We cheat a little bit here.  In the middle of bam version 4.10,
-  // we completely redefined the bam storage definition for
-  // ClipPlaneAttribs, without bothering to up the bam version or even to
-  // attempt to read the old definition.  We get away with this,
-  // knowing that the egg loader doesn't create ClipPlaneAttribs, and
-  // hence no old bam files have the old definition for ClipPlaneAttrib
-  // within them.
-
   _off_all_planes = scan.get_bool();
   _off_all_planes = scan.get_bool();
 
 
   int num_off_planes = scan.get_uint16();
   int num_off_planes = scan.get_uint16();
-    
+
   // Push back an empty NodePath for each off Plane for now, until we
   // Push back an empty NodePath for each off Plane for now, until we
   // get the actual list of pointers later in complete_pointers().
   // get the actual list of pointers later in complete_pointers().
-  _off_planes.reserve(num_off_planes);
-  int i;
-  for (i = 0; i < num_off_planes; i++) {
-    manager->read_pointer(scan);
-    _off_planes.push_back(NodePath());
+  _off_planes.resize(num_off_planes);
+  if (manager->get_file_minor_ver() >= 40) {
+    for (int i = 0; i < num_off_planes; i++) {
+      _off_planes[i].fillin(scan, manager);
+    }
+  } else {
+    manager->read_pointers(scan, num_off_planes);
   }
   }
-    
+
   int num_on_planes = scan.get_uint16();
   int num_on_planes = scan.get_uint16();
-  _on_planes.reserve(num_on_planes);
-  for (i = 0; i < num_on_planes; i++) {
-    manager->read_pointer(scan);
-    _on_planes.push_back(NodePath());
+  _on_planes.resize(num_on_planes);
+  if (manager->get_file_minor_ver() >= 40) {
+    for (int i = 0; i < num_on_planes; i++) {
+      manager->read_pointer(scan);
+    }
+  } else {
+    manager->read_pointers(scan, num_on_planes);
   }
   }
 }
 }

+ 104 - 62
panda/src/pgraph/lightAttrib.cxx

@@ -990,12 +990,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   // write the off lights pointers if any
   // write the off lights pointers if any
   Lights::const_iterator fi;
   Lights::const_iterator fi;
   for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
   for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
-    NodePath light = (*fi);
-
-    // Since we can't write out a NodePath, we write out just the
-    // plain PandaNode.  The user can use the AttribNodeRegistry on
-    // re-read if there is any ambiguity that needs to be resolved.
-    manager->write_pointer(dg, light.node());
+    (*fi).write_datagram(manager, dg);
   }
   }
 
 
   // write the number of on lights
   // write the number of on lights
@@ -1003,8 +998,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   // write the on lights pointers if any
   // write the on lights pointers if any
   Lights::const_iterator nti;
   Lights::const_iterator nti;
   for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
   for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
-    NodePath light = (*nti);
-    manager->write_pointer(dg, light.node());
+    (*nti).write_datagram(manager, dg);
   }
   }
 }
 }
 
 
@@ -1019,22 +1013,33 @@ int LightAttrib::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = RenderAttrib::complete_pointers(p_list, manager);
   int pi = RenderAttrib::complete_pointers(p_list, manager);
 
 
-  BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "lights");
-  nassertr(aux != NULL, pi);
+  if (manager->get_file_minor_ver() >= 40) {
+    for (int i = 0; i < _off_lights.size(); ++i) {
+      pi += _off_lights[i].complete_pointers(p_list + pi, manager);
+    }
 
 
-  int i;
-  aux->_off_list.reserve(aux->_num_off_lights);
-  for (i = 0; i < aux->_num_off_lights; ++i) {
-    PandaNode *node;
-    DCAST_INTO_R(node, p_list[pi++], pi);
-    aux->_off_list.push_back(node);
-  }
+    for (int i = 0; i < _on_lights.size(); ++i) {
+      pi += _on_lights[i].complete_pointers(p_list + pi, manager);
+    }
 
 
-  aux->_on_list.reserve(aux->_num_on_lights);
-  for (i = 0; i < aux->_num_on_lights; ++i) {
-    PandaNode *node;
-    DCAST_INTO_R(node, p_list[pi++], pi);
-    aux->_on_list.push_back(node);
+  } else {
+    BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "lights");
+    nassertr(aux != NULL, pi);
+
+    int i;
+    aux->_off_list.reserve(aux->_num_off_lights);
+    for (i = 0; i < aux->_num_off_lights; ++i) {
+      PandaNode *node;
+      DCAST_INTO_R(node, p_list[pi++], pi);
+      aux->_off_list.push_back(node);
+    }
+
+    aux->_on_list.reserve(aux->_num_on_lights);
+    for (i = 0; i < aux->_num_on_lights; ++i) {
+      PandaNode *node;
+      DCAST_INTO_R(node, p_list[pi++], pi);
+      aux->_on_list.push_back(node);
+    }
   }
   }
 
 
   return pi;
   return pi;
@@ -1049,43 +1054,68 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void LightAttrib::
 void LightAttrib::
 finalize(BamReader *manager) {
 finalize(BamReader *manager) {
-  // Now it's safe to convert our saved PandaNodes into NodePaths.
-  BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "lights");
-  nassertv(aux != NULL);
-  nassertv(aux->_num_off_lights == (int)aux->_off_list.size());
-  nassertv(aux->_num_on_lights == (int)aux->_on_list.size());
-
-  AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
-
-  _off_lights.reserve(aux->_off_list.size());
-  NodeList::iterator ni;
-  for (ni = aux->_off_list.begin(); ni != aux->_off_list.end(); ++ni) {
-    PandaNode *node = (*ni);
-    int n = areg->find_node(node->get_type(), node->get_name());
-    if (n != -1) {
-      // If it's in the registry, add that NodePath.
-      _off_lights.push_back(areg->get_node(n));
-    } else {
-      // Otherwise, add any arbitrary NodePath.  Complain if it's
-      // ambiguous.
-      _off_lights.push_back(NodePath(node));
+  if (manager->get_file_minor_ver() >= 40) {
+    AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
+
+    // Check if any of the nodes we loaded are mentioned in the
+    // AttribNodeRegistry.  If so, replace them.
+    for (int i = 0; i < _off_lights.size(); ++i) {
+      int n = areg->find_node(_off_lights[i]);
+      if (n != -1) {
+        // If it's in the registry, replace it.
+        _off_lights[i] = areg->get_node(n);
+      }
     }
     }
-  }
-  _off_lights.sort();
 
 
-  _on_lights.reserve(aux->_on_list.size());
-  for (ni = aux->_on_list.begin(); ni != aux->_on_list.end(); ++ni) {
-    PandaNode *node = (*ni);
-    int n = areg->find_node(node->get_type(), node->get_name());
-    if (n != -1) {
-      // If it's in the registry, add that NodePath.
-      _on_lights.push_back(areg->get_node(n));
-    } else {
-      // Otherwise, add any arbitrary NodePath.  Complain if it's
-      // ambiguous.
-      _on_lights.push_back(NodePath(node));
+    for (int i = 0; i < _on_lights.size(); ++i) {
+      int n = areg->find_node(_on_lights[i]);
+      if (n != -1) {
+        // If it's in the registry, replace it.
+        _on_lights[i] = areg->get_node(n);
+      }
+    }
+
+  } else {
+    // Now it's safe to convert our saved PandaNodes into NodePaths.
+    BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "lights");
+    nassertv(aux != NULL);
+    nassertv(aux->_num_off_lights == (int)aux->_off_list.size());
+    nassertv(aux->_num_on_lights == (int)aux->_on_list.size());
+
+    AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
+
+    _off_lights.reserve(aux->_off_list.size());
+    NodeList::iterator ni;
+    for (ni = aux->_off_list.begin(); ni != aux->_off_list.end(); ++ni) {
+      PandaNode *node = (*ni);
+      int n = areg->find_node(node->get_type(), node->get_name());
+      if (n != -1) {
+        // If it's in the registry, add that NodePath.
+        _off_lights.push_back(areg->get_node(n));
+      } else {
+        // Otherwise, add any arbitrary NodePath.  Complain if it's
+        // ambiguous.
+        _off_lights.push_back(NodePath(node));
+      }
+    }
+
+    _on_lights.reserve(aux->_on_list.size());
+    for (ni = aux->_on_list.begin(); ni != aux->_on_list.end(); ++ni) {
+      PandaNode *node = (*ni);
+      int n = areg->find_node(node->get_type(), node->get_name());
+      if (n != -1) {
+        // If it's in the registry, add that NodePath.
+        _on_lights.push_back(areg->get_node(n));
+      } else {
+        // Otherwise, add any arbitrary NodePath.  Complain if it's
+        // ambiguous.
+        _on_lights.push_back(NodePath(node));
+      }
     }
     }
   }
   }
+
+  // Now that the NodePaths have been filled in, we can sort the list.
+  _off_lights.sort();
   _on_lights.sort();
   _on_lights.sort();
 }
 }
 
 
@@ -1124,12 +1154,24 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 
 
   _off_all_lights = scan.get_bool();
   _off_all_lights = scan.get_bool();
 
 
-  BamAuxData *aux = new BamAuxData;
-  manager->set_aux_data(this, "lights", aux);
+  if (manager->get_file_minor_ver() >= 40) {
+    _off_lights.resize(scan.get_uint16());
+    for (int i = 0; i < _off_lights.size(); ++i) {
+      _off_lights[i].fillin(scan, manager);
+    }
 
 
-  aux->_num_off_lights = scan.get_uint16();
-  manager->read_pointers(scan, aux->_num_off_lights);
-    
-  aux->_num_on_lights = scan.get_uint16();
-  manager->read_pointers(scan, aux->_num_on_lights);
+    _on_lights.resize(scan.get_uint16());
+    for (int i = 0; i < _on_lights.size(); ++i) {
+      _on_lights[i].fillin(scan, manager);
+    }
+  } else {
+    BamAuxData *aux = new BamAuxData;
+    manager->set_aux_data(this, "lights", aux);
+
+    aux->_num_off_lights = scan.get_uint16();
+    manager->read_pointers(scan, aux->_num_off_lights);
+
+    aux->_num_on_lights = scan.get_uint16();
+    manager->read_pointers(scan, aux->_num_on_lights);
+  }
 }
 }

+ 1 - 1
panda/src/pgraph/loader.cxx

@@ -357,7 +357,7 @@ try_load_file(const Filename &pathname, const LoaderOptions &options,
     if (result != (PandaNode *)NULL) {
     if (result != (PandaNode *)NULL) {
       if (record != (BamCacheRecord *)NULL) {
       if (record != (BamCacheRecord *)NULL) {
         // Store the loaded model in the model cache.
         // Store the loaded model in the model cache.
-        record->set_data(result, result);
+        record->set_data(result);
         cache->store(record);
         cache->store(record);
       }
       }
 
 

+ 134 - 0
panda/src/pgraph/nodePath.cxx

@@ -6345,6 +6345,10 @@ write_bam_file(const Filename &filename) const {
   bool okflag = false;
   bool okflag = false;
 
 
   if (bam_file.open_write(filename)) {
   if (bam_file.open_write(filename)) {
+    // Tell the BamWriter which node is the root node, for making
+    // NodePaths relative to when writing them out to the file.
+    bam_file.get_writer()->set_root_node(node());
+
     if (bam_file.write_object(node())) {
     if (bam_file.write_object(node())) {
       okflag = true;
       okflag = true;
     }
     }
@@ -6368,6 +6372,10 @@ write_bam_stream(ostream &out) const {
   bool okflag = false;
   bool okflag = false;
 
 
   if (bam_file.open_write(out)) {
   if (bam_file.open_write(out)) {
+    // Tell the BamWriter which node is the root node, for making
+    // NodePaths relative to when writing them out to the file.
+    bam_file.get_writer()->set_root_node(node());
+
     if (bam_file.write_object(node())) {
     if (bam_file.write_object(node())) {
       okflag = true;
       okflag = true;
     }
     }
@@ -6434,6 +6442,10 @@ encode_to_bam_stream(string &data, BamWriter *writer) const {
     num_nodes = 1;
     num_nodes = 1;
   }
   }
 
 
+  // Tell the BamWriter which node is the root node, for making
+  // NodePaths relative to when writing them out to the file.
+  writer->set_root_node(node());
+
   // Write an initial Datagram to represent the error type and
   // Write an initial Datagram to represent the error type and
   // number of nodes.
   // number of nodes.
   Datagram dg;
   Datagram dg;
@@ -7451,3 +7463,125 @@ r_find_all_materials(PandaNode *node, const RenderState *state,
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::write_datagram
+//       Access: Public
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  PandaNode *root = DCAST(PandaNode, manager->get_root_node());
+
+  // We have no root node to measure from.
+  if (root == (PandaNode *)NULL || root == node()) {
+    manager->write_pointer(dg, node());
+    manager->write_pointer(dg, NULL);
+    return;
+  }
+
+  Thread *current_thread = Thread::get_current_thread();
+  int pipeline_stage = current_thread->get_pipeline_stage();
+
+  // Record the chain of nodes from the root to this node.
+  pvector<PandaNode *> path;
+  NodePathComponent *comp = _head;
+  while (comp != NULL) {
+    PandaNode *node = comp->get_node();
+    path.push_back(node);
+
+    if (node == root) {
+      break;
+    }
+
+    comp = comp->get_next(pipeline_stage, current_thread);
+  }
+
+  if (comp == (NodePathComponent *)NULL) {
+    // We did not encounter the root node.  Not much we can do.
+    manager->write_pointer(dg, node());
+    manager->write_pointer(dg, NULL);
+    return;
+  }
+
+  // Write out the nodes in reverse order, for fast reconstructing.
+  for (int i = path.size() - 1; i >= 0; --i) {
+    manager->write_pointer(dg, path[i]);
+  }
+  manager->write_pointer(dg, NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::complete_pointers
+//       Access: Public
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int NodePath::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = 0;
+  PT(PandaNode) node = DCAST(PandaNode, p_list[pi++]);
+  if (node.is_null()) {
+    // An empty NodePath.
+    _head = (NodePathComponent *)NULL;
+    return pi;
+  }
+
+  Thread *current_thread = Thread::get_current_thread();
+  int pipeline_stage = current_thread->get_pipeline_stage();
+
+  // Take an arbitrary path to the root of the NodePath.  This probably
+  // won't be ambiguous, as this is usually the root of the model or scene
+  // we are currently loading.
+  PT(NodePathComponent) comp = node->get_generic_component(false, pipeline_stage, current_thread);
+  nassertd(!comp.is_null()) {
+    while (p_list[pi++]) {}
+    return pi;
+  }
+
+  // Build up the chain of NodePathComponents leading up to this node.
+  while (p_list[pi] != NULL) {
+    PT(PandaNode) node = DCAST(PandaNode, p_list[pi++]);
+
+    LightReMutexHolder holder(node->_paths_lock);
+
+    // First, walk through the list of NodePathComponents we already
+    // have on the child, looking for one that already exists,
+    // referencing the indicated parent component.
+    PandaNode::Paths::const_iterator it;
+    for (it = node->_paths.begin(); it != node->_paths.end(); ++it) {
+      if ((*it)->get_next(pipeline_stage, current_thread) == comp) {
+        // If we already have such a component, use that.
+        comp = (*it);
+        break;
+      }
+    }
+
+    if (it == node->_paths.end()) {
+      // We don't already have a NodePathComponent referring to this
+      // parent-child relationship.  Create a new one.  Note that we can't
+      // verify that they are actually related because we may not have
+      // completed the node's pointers yet, so we trust that the .bam is right.
+      comp = new NodePathComponent(node, comp, pipeline_stage, current_thread);
+      node->_paths.insert(comp);
+    }
+  }
+  // One more for the final NULL node.
+  ++pi;
+
+  _head = comp;
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::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 NodePath.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  while(manager->read_pointer(scan)) {};
+}

+ 5 - 0
panda/src/pgraph/nodePath.h

@@ -994,6 +994,11 @@ private:
   static PStatCollector _get_transform_pcollector;
   static PStatCollector _get_transform_pcollector;
   static PStatCollector _verify_complete_pcollector;
   static PStatCollector _verify_complete_pcollector;
 
 
+public:
+  void write_datagram(BamWriter *manager, Datagram &dg) const;
+  int complete_pointers(TypedWritable **plist, BamReader *manager);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;

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

@@ -129,6 +129,7 @@ public:
 private:
 private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
   friend class PandaNode;
   friend class PandaNode;
+  friend class NodePath;
 };
 };
 
 
 INLINE ostream &operator << (ostream &out, const NodePathComponent &comp);
 INLINE ostream &operator << (ostream &out, const NodePathComponent &comp);

+ 33 - 18
panda/src/pgraph/occluderEffect.cxx

@@ -169,8 +169,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   // write the on occluders pointers if any
   // write the on occluders pointers if any
   Occluders::const_iterator nti;
   Occluders::const_iterator nti;
   for (nti = _on_occluders.begin(); nti != _on_occluders.end(); ++nti) {
   for (nti = _on_occluders.begin(); nti != _on_occluders.end(); ++nti) {
-    NodePath occluder = (*nti);
-    manager->write_pointer(dg, occluder.node());
+    (*nti).write_datagram(manager, dg);
   }
   }
 }
 }
 
 
@@ -186,19 +185,32 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = RenderEffect::complete_pointers(p_list, manager);
   int pi = RenderEffect::complete_pointers(p_list, manager);
   AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
   AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
 
 
-  Occluders::iterator ci;
-  ci = _on_occluders.begin();
-  while (ci != _on_occluders.end()) {
-    PandaNode *node;
-    DCAST_INTO_R(node, p_list[pi++], pi);
+  if (manager->get_file_minor_ver() >= 40) {
+    for (int i = 0; i < _on_occluders.size(); ++i) {
+      pi += _on_occluders[i].complete_pointers(p_list + pi, manager);
 
 
-    int ni = areg->find_node(node->get_type(), node->get_name());
-    if (ni != -1) {
-      (*ci) = areg->get_node(ni);
-    } else {
-      (*ci) = NodePath(node);
+      int n = areg->find_node(_on_occluders[i]);
+      if (n != -1) {
+        // If it's in the registry, replace it.
+        _on_occluders[i] = areg->get_node(n);
+      }
+    }
+
+  } else {
+    Occluders::iterator ci;
+    ci = _on_occluders.begin();
+    while (ci != _on_occluders.end()) {
+      PandaNode *node;
+      DCAST_INTO_R(node, p_list[pi++], pi);
+
+      int ni = areg->find_node(node->get_type(), node->get_name());
+      if (ni != -1) {
+        (*ci) = areg->get_node(ni);
+      } else {
+        (*ci) = NodePath(node);
+      }
+      ++ci;
     }
     }
-    ++ci;
   }
   }
   _on_occluders.sort();
   _on_occluders.sort();
 
 
@@ -250,13 +262,16 @@ make_from_bam(const FactoryParams &params) {
 void OccluderEffect::
 void OccluderEffect::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   RenderEffect::fillin(scan, manager);
   RenderEffect::fillin(scan, manager);
-    
+
   // Push back an empty NodePath for each Occluder for now, until we
   // Push back an empty NodePath for each Occluder for now, until we
   // get the actual list of pointers later in complete_pointers().
   // get the actual list of pointers later in complete_pointers().
   int num_on_occluders = scan.get_uint16();
   int num_on_occluders = scan.get_uint16();
-  _on_occluders.reserve(num_on_occluders);
-  for (int i = 0; i < num_on_occluders; i++) {
-    manager->read_pointer(scan);
-    _on_occluders.push_back(NodePath());
+  _on_occluders.resize(num_on_occluders);
+  if (manager->get_file_minor_ver() >= 40) {
+    for (int i = 0; i < num_on_occluders; i++) {
+      _on_occluders[i].fillin(scan, manager);
+    }
+  } else {
+    manager->read_pointers(scan, num_on_occluders);
   }
   }
 }
 }

+ 13 - 11
panda/src/pgraph/paramNodePath.cxx

@@ -48,15 +48,7 @@ register_with_read_factory() {
 void ParamNodePath::
 void ParamNodePath::
 write_datagram(BamWriter *manager, Datagram &dg) {
 write_datagram(BamWriter *manager, Datagram &dg) {
   ParamValueBase::write_datagram(manager, dg);
   ParamValueBase::write_datagram(manager, dg);
-
-  // We can't store a NodePath, so we store a pointer to the
-  // underlying node, and pray that there is an unambiguous path
-  // from the root to it.
-  if (_node_path.is_empty()) {
-    manager->write_pointer(dg, NULL);
-  } else {
-    manager->write_pointer(dg, _node_path.node());
-  }
+  _node_path.write_datagram(manager, dg);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -69,7 +61,12 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 int ParamNodePath::
 int ParamNodePath::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = ParamValueBase::complete_pointers(p_list, manager);
   int pi = ParamValueBase::complete_pointers(p_list, manager);
-  _node_path = NodePath(DCAST(PandaNode, p_list[pi++]));
+
+  if (manager->get_file_minor_ver() >= 40) {
+    pi += _node_path.complete_pointers(p_list + pi, manager);
+  } else {
+    _node_path = NodePath(DCAST(PandaNode, p_list[pi++]));
+  }
 
 
   return pi;
   return pi;
 }
 }
@@ -104,5 +101,10 @@ make_from_bam(const FactoryParams &params) {
 void ParamNodePath::
 void ParamNodePath::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   ParamValueBase::fillin(scan, manager);
   ParamValueBase::fillin(scan, manager);
-  manager->read_pointer(scan);
+
+  if (manager->get_file_minor_ver() >= 40) {
+    _node_path.fillin(scan, manager);
+  } else {
+    manager->read_pointer(scan);
+  }
 }
 }

+ 2 - 1
panda/src/putil/bam.h

@@ -33,7 +33,7 @@ static const unsigned short _bam_major_ver = 6;
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 
 
 static const unsigned short _bam_first_minor_ver = 14;
 static const unsigned short _bam_first_minor_ver = 14;
-static const unsigned short _bam_minor_ver = 39;
+static const unsigned short _bam_minor_ver = 40;
 // Bumped to minor version 14 on 12/19/07 to change default ColorAttrib.
 // Bumped to minor version 14 on 12/19/07 to change default ColorAttrib.
 // Bumped to minor version 15 on 4/9/08 to add TextureAttrib::_implicit_sort.
 // Bumped to minor version 15 on 4/9/08 to add TextureAttrib::_implicit_sort.
 // Bumped to minor version 16 on 5/13/08 to add Texture::_quality_level.
 // Bumped to minor version 16 on 5/13/08 to add Texture::_quality_level.
@@ -60,5 +60,6 @@ static const unsigned short _bam_minor_ver = 39;
 // Bumped to minor version 37 on 1/22/15 to add GeomVertexArrayFormat::_divisor.
 // Bumped to minor version 37 on 1/22/15 to add GeomVertexArrayFormat::_divisor.
 // Bumped to minor version 38 on 4/15/15 to add various Bullet classes.
 // Bumped to minor version 38 on 4/15/15 to add various Bullet classes.
 // Bumped to minor version 39 on 1/9/16 to change lights and materials.
 // Bumped to minor version 39 on 1/9/16 to change lights and materials.
+// Bumped to minor version 40 on 1/11/16 to make NodePaths writable.
 
 
 #endif
 #endif

+ 6 - 0
panda/src/putil/bamCache.cxx

@@ -258,6 +258,12 @@ store(BamCacheRecord *record) {
       writer.set_file_texture_mode(BamWriter::BTM_fullpath);
       writer.set_file_texture_mode(BamWriter::BTM_fullpath);
     }
     }
 
 
+    // This is necessary for relative NodePaths to work.
+    TypeHandle node_type = type_registry->find_type("PandaNode");
+    if (record->get_data()->is_of_type(node_type)) {
+      writer.set_root_node(record->get_data());
+    }
+
     if (!writer.write_object(record)) {
     if (!writer.write_object(record)) {
       util_cat.error()
       util_cat.error()
         << "Unable to write object to " << temp_pathname << "\n";
         << "Unable to write object to " << temp_pathname << "\n";

+ 22 - 0
panda/src/putil/bamCacheRecord.I

@@ -206,6 +206,28 @@ set_data(TypedWritable *ptr, ReferenceCount *ref_ptr) {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamCacheRecord::set_data
+//       Access: Published
+//  Description: This variant on set_data() is provided to easily
+//               pass objects deriving from TypedWritable.
+////////////////////////////////////////////////////////////////////
+INLINE void BamCacheRecord::
+set_data(TypedWritable *ptr) {
+  set_data(ptr, ptr->as_reference_count());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamCacheRecord::set_data
+//       Access: Published
+//  Description: This variant on set_data() is provided to easily
+//               pass objects deriving from TypedWritableReferenceCount.
+////////////////////////////////////////////////////////////////////
+INLINE void BamCacheRecord::
+set_data(TypedWritableReferenceCount *ptr) {
+  set_data((TypedWritable *)ptr, (ReferenceCount *)ptr);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamCacheRecord::set_data
 //     Function: BamCacheRecord::set_data
 //       Access: Published
 //       Access: Published

+ 4 - 0
panda/src/putil/bamCacheRecord.h

@@ -72,8 +72,12 @@ PUBLISHED:
   INLINE TypedWritable *get_data() const;
   INLINE TypedWritable *get_data() const;
   INLINE bool extract_data(TypedWritable *&ptr, ReferenceCount *&ref_ptr);
   INLINE bool extract_data(TypedWritable *&ptr, ReferenceCount *&ref_ptr);
   INLINE void set_data(TypedWritable *ptr, ReferenceCount *ref_ptr);
   INLINE void set_data(TypedWritable *ptr, ReferenceCount *ref_ptr);
+  INLINE void set_data(TypedWritable *ptr);
+  INLINE void set_data(TypedWritableReferenceCount *ptr);
   INLINE void set_data(TypedWritable *ptr, int dummy);
   INLINE void set_data(TypedWritable *ptr, int dummy);
 
 
+  MAKE_PROPERTY2(data, has_data, get_data, set_data, clear_data);
+
   void output(ostream &out) const;
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
   void write(ostream &out, int indent_level = 0) const;
 
 

+ 24 - 0
panda/src/putil/bamWriter.I

@@ -94,3 +94,27 @@ INLINE void BamWriter::
 set_file_texture_mode(BamTextureMode file_texture_mode) {
 set_file_texture_mode(BamTextureMode file_texture_mode) {
   _file_texture_mode = file_texture_mode;
   _file_texture_mode = file_texture_mode;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::get_root_node
+//       Access: Published
+//  Description: Returns the root node of the part of the scene
+//               graph we are currently writing out.  This is used
+//               for determining what to make NodePaths relative to.
+////////////////////////////////////////////////////////////////////
+INLINE TypedWritable *BamWriter::
+get_root_node() const {
+  return _root_node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::set_root_node
+//       Access: Published
+//  Description: Sets the root node of the part of the scene graph
+//               we are currently writing out.  NodePaths written
+//               to this bam file will be relative to this node.
+////////////////////////////////////////////////////////////////////
+INLINE void BamWriter::
+set_root_node(TypedWritable *root_node) {
+  _root_node = root_node;
+}

+ 9 - 0
panda/src/putil/bamWriter.h

@@ -90,12 +90,16 @@ PUBLISHED:
   INLINE BamTextureMode get_file_texture_mode() const;
   INLINE BamTextureMode get_file_texture_mode() const;
   INLINE void set_file_texture_mode(BamTextureMode file_texture_mode);
   INLINE void set_file_texture_mode(BamTextureMode file_texture_mode);
 
 
+  INLINE TypedWritable *get_root_node() const;
+  INLINE void set_root_node(TypedWritable *root_node);
+
 PUBLISHED:
 PUBLISHED:
   MAKE_PROPERTY(target, get_target, set_target);
   MAKE_PROPERTY(target, get_target, set_target);
   MAKE_PROPERTY(filename, get_filename);
   MAKE_PROPERTY(filename, get_filename);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
   MAKE_PROPERTY(file_texture_mode, get_file_texture_mode);
   MAKE_PROPERTY(file_texture_mode, get_file_texture_mode);
+  MAKE_PROPERTY(root_node, get_root_node, set_root_node);
 
 
 public:
 public:
   // Functions to support classes that write themselves to the Bam.
   // Functions to support classes that write themselves to the Bam.
@@ -125,6 +129,11 @@ private:
   bool _file_stdfloat_double;
   bool _file_stdfloat_double;
   BamTextureMode _file_texture_mode;
   BamTextureMode _file_texture_mode;
 
 
+  // Stores the PandaNode representing the root of the node hierarchy
+  // we are currently writing, if any, for the purpose of writing NodePaths.
+  // This is a TypedWritable since PandaNode is defined in pgraph.
+  TypedWritable *_root_node;
+
   // This is the set of all TypeHandles already written.
   // This is the set of all TypeHandles already written.
   pset<int, int_hash> _types_written;
   pset<int, int_hash> _types_written;