Panda3D

bamWriter.cxx

00001 // Filename: bamWriter.cxx
00002 // Created by:  jason (08Jun00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pandabase.h"
00016 #include "pnotify.h"
00017 
00018 #include "typedWritable.h"
00019 #include "config_util.h"
00020 #include "bam.h"
00021 #include "bamWriter.h"
00022 #include "bamReader.h"
00023 #include "lightMutexHolder.h"
00024 
00025 #include <algorithm>
00026 
00027 ////////////////////////////////////////////////////////////////////
00028 //     Function: BamWriter::Constructor
00029 //       Access: Published
00030 //  Description:
00031 ////////////////////////////////////////////////////////////////////
00032 BamWriter::
00033 BamWriter(DatagramSink *target, const Filename &name) :
00034   _filename(name),
00035   _target(target)
00036 {
00037   ++_writing_seq;
00038   _next_boc = BOC_adjunct;
00039   _needs_init = true;
00040 
00041   // Initialize the next object and PTA ID's.  These start counting at
00042   // 1, since 0 is reserved for NULL.
00043   _next_object_id = 1;
00044   _long_object_id = false;
00045   _next_pta_id = 1;
00046   _long_pta_id = false;
00047 
00048   _file_endian = bam_endian;
00049   _file_texture_mode = bam_texture_mode;
00050 }
00051 
00052 ////////////////////////////////////////////////////////////////////
00053 //     Function: BamWriter::Destructor
00054 //       Access: Published
00055 //  Description:
00056 ////////////////////////////////////////////////////////////////////
00057 BamWriter::
00058 ~BamWriter() {
00059   // Tell all the TypedWritables whose pointer we are still keeping to
00060   // forget about us.
00061   StateMap::iterator si;
00062   for (si = _state_map.begin(); si != _state_map.end(); ++si) {
00063     TypedWritable *object = (TypedWritable *)(*si).first;
00064     LightMutexHolder holder(TypedWritable::_bam_writers_lock);
00065     nassertv(object->_bam_writers != (TypedWritable::BamWriters *)NULL);
00066     TypedWritable::BamWriters::iterator wi = 
00067       find(object->_bam_writers->begin(), object->_bam_writers->end(), this);
00068     nassertv(wi != object->_bam_writers->end());
00069     object->_bam_writers->erase(wi);
00070   }
00071 }
00072 
00073 ////////////////////////////////////////////////////////////////////
00074 //     Function: BamWriter::set_target
00075 //       Access: Published
00076 //  Description: Changes the destination of future datagrams written
00077 //               by the BamWriter.  This also implicitly calls init()
00078 //               if it has not already been called.
00079 ////////////////////////////////////////////////////////////////////
00080 void BamWriter::
00081 set_target(DatagramSink *target) {
00082   if (_target != NULL) {
00083     _target->flush();
00084   }
00085   _target = target;
00086 
00087   if (_needs_init && _target != NULL) {
00088     init();
00089   }
00090 }
00091 
00092 ////////////////////////////////////////////////////////////////////
00093 //     Function: BamWriter::init
00094 //       Access: Published
00095 //  Description: Initializes the BamWriter prior to writing any
00096 //               objects to its output stream.  This includes writing
00097 //               out the Bam header.
00098 //
00099 //               This returns true if the BamWriter successfully
00100 //               initialized, false otherwise.
00101 ////////////////////////////////////////////////////////////////////
00102 bool BamWriter::
00103 init() {
00104   nassertr(_target != NULL, false);
00105   nassertr(_needs_init, false);
00106   _needs_init = false;
00107 
00108   // Initialize the next object and PTA ID's.  These start counting at
00109   // 1, since 0 is reserved for NULL.
00110   _next_object_id = 1;
00111   _long_object_id = false;
00112   _next_pta_id = 1;
00113   _long_pta_id = false;
00114 
00115   _file_endian = bam_endian;
00116   _file_texture_mode = bam_texture_mode;
00117 
00118   // Write out the current major and minor BAM file version numbers.
00119   Datagram header;
00120 
00121   header.add_uint16(_bam_major_ver);
00122   header.add_uint16(_bam_minor_ver);
00123   header.add_uint8(_file_endian);
00124 
00125   if (!_target->put_datagram(header)) {
00126     util_cat.error()
00127       << "Unable to write Bam header.\n";
00128     return false;
00129   }
00130   return true;
00131 }
00132 
00133 ////////////////////////////////////////////////////////////////////
00134 //     Function: BamWriter::write_object
00135 //       Access: Published
00136 //  Description: Writes a single object to the Bam file, so that the
00137 //               BamReader::read_object() can later correctly restore
00138 //               the object and all its pointers.
00139 //
00140 //               This implicitly also writes any additional objects
00141 //               this object references (if they haven't already been
00142 //               written), so that pointers may be fully resolved.
00143 //
00144 //               This may be called repeatedly to write a sequence of
00145 //               objects to the Bam file, but typically (especially
00146 //               for scene graph files, indicated with the .bam
00147 //               extension), only one object is written directly from
00148 //               the Bam file: the root of the scene graph.  The
00149 //               remaining objects will all be written recursively by
00150 //               the first object.
00151 //
00152 //               Returns true if the object is successfully written,
00153 //               false otherwise.
00154 ////////////////////////////////////////////////////////////////////
00155 bool BamWriter::
00156 write_object(const TypedWritable *object) {
00157   nassertr(_target != NULL, false);
00158 
00159   // Increment the _writing_seq, so we can check for newly stale
00160   // objects during this operation.
00161   ++_writing_seq;
00162 
00163   // If there are any freed objects to indicate, write them out now.
00164   if (!_freed_object_ids.empty()) {
00165     Datagram dg;
00166     dg.add_uint8(BOC_remove);
00167 
00168     FreedObjectIds::iterator fi;
00169     for (fi = _freed_object_ids.begin(); fi != _freed_object_ids.end(); ++fi) {
00170       write_object_id(dg, (*fi));
00171     }
00172     _freed_object_ids.clear();
00173 
00174     if (!_target->put_datagram(dg)) {
00175       util_cat.error()
00176         << "Unable to write data to output.\n";
00177       return false;
00178     }
00179   }
00180 
00181   nassertr(_object_queue.empty(), false);
00182   _next_boc = BOC_push;
00183 
00184   int object_id = enqueue_object(object);
00185   nassertr(object_id != 0, false);
00186   if (!flush_queue()) {
00187     return false;
00188   }
00189 
00190   // Finally, write the closing pop.
00191   if (_next_boc != BOC_push) {
00192     Datagram dg;
00193     dg.add_uint8(BOC_pop);
00194     if (!_target->put_datagram(dg)) {
00195       util_cat.error()
00196         << "Unable to write data to output.\n";
00197       return false;
00198     }
00199   }
00200 
00201   return true;
00202 }
00203 
00204 ////////////////////////////////////////////////////////////////////
00205 //     Function: BamWriter::has_object
00206 //       Access: Published
00207 //  Description: Returns true if the object has previously been
00208 //               written (or at least requested to be written) to the
00209 //               bam file, or false if we've never heard of it before.
00210 ////////////////////////////////////////////////////////////////////
00211 bool BamWriter::
00212 has_object(const TypedWritable *object) const {
00213   StateMap::const_iterator si = _state_map.find(object);
00214   return (si != _state_map.end());
00215 }
00216 
00217 ////////////////////////////////////////////////////////////////////
00218 //     Function: BamWriter::flush
00219 //       Access: Published
00220 //  Description: Ensures that all data written thus far is manifested
00221 //               on the output stream.
00222 ////////////////////////////////////////////////////////////////////
00223 void BamWriter::
00224 flush() {
00225   nassertv(_target != NULL);
00226   _target->flush();
00227 }
00228 
00229 ////////////////////////////////////////////////////////////////////
00230 //     Function: BamWriter::consider_update
00231 //       Access: Public
00232 //  Description: Should be called from
00233 //               TypedWritable::update_bam_nested() to recursively
00234 //               check the entire hiererachy of writable objects for
00235 //               needed updates.  This tests the indicated
00236 //               TypedWritable object and writes it to the bam stream
00237 //               if it has recently been modified, then recurses
00238 //               through update_bam_nested.
00239 ////////////////////////////////////////////////////////////////////
00240 void BamWriter::
00241 consider_update(const TypedWritable *object) {
00242   StateMap::iterator si = _state_map.find(object);
00243   if (si == _state_map.end()) {
00244     // This object has never even been seen before.
00245     enqueue_object(object);
00246 
00247   } else if ((*si).second._written_seq.is_initial()) {
00248     // This object has not been written yet.
00249     enqueue_object(object);
00250 
00251   } else if ((*si).second._written_seq == _writing_seq) {
00252     // We have already visited this object this pass, so no need to
00253     // look closer.
00254 
00255   } else if ((*si).second._modified != object->get_bam_modified()) {
00256     // This object has been recently modified and needs to be rewritten.
00257     enqueue_object(object);
00258 
00259   } else {
00260     // Mark that we have now visited this object and pronounced it clean.
00261     (*si).second._written_seq = _writing_seq;
00262     
00263     // Recurse to child objects.
00264     ((TypedWritable *)object)->update_bam_nested(this);
00265   }
00266 }
00267 
00268 ////////////////////////////////////////////////////////////////////
00269 //     Function: BamWriter::write_pointer
00270 //       Access: Public
00271 //  Description: The interface for writing a pointer to another object
00272 //               to a Bam file.  This is intended to be called by the
00273 //               various objects that write themselves to the Bam
00274 //               file, within the write_datagram() method.
00275 //
00276 //               This writes the pointer out in such a way that the
00277 //               BamReader will be able to restore the pointer later.
00278 //               If the pointer is to an object that has not yet
00279 //               itself been written to the Bam file, that object will
00280 //               automatically be written.
00281 ////////////////////////////////////////////////////////////////////
00282 void BamWriter::
00283 write_pointer(Datagram &packet, const TypedWritable *object) {
00284   // If the pointer is NULL, we always simply write a zero for an
00285   // object ID and leave it at that.
00286   if (object == (const TypedWritable *)NULL) {
00287     write_object_id(packet, 0);
00288 
00289   } else {
00290     StateMap::iterator si = _state_map.find(object);
00291     if (si == _state_map.end()) {
00292       // We have not written this pointer out yet.  This means we must
00293       // queue the object definition up for later.
00294       int object_id = enqueue_object(object);
00295       write_object_id(packet, object_id);
00296 
00297     } else {
00298       // We have already assigned this pointer an ID, so it has
00299       // previously been written; but we might still need to rewrite
00300       // it if it is stale.
00301       int object_id = (*si).second._object_id;
00302       bool already_written = !(*si).second._written_seq.is_initial();
00303       if ((*si).second._written_seq != _writing_seq &&
00304           (*si).second._modified != object->get_bam_modified()) {
00305         // This object was previously written, but it has since been
00306         // modified, so we should write it again.
00307         already_written = false;
00308       }
00309 
00310       write_object_id(packet, object_id);
00311 
00312       if (!already_written) {
00313         // It's stale, so queue the object for rewriting too.
00314         enqueue_object(object);
00315       } else {
00316         // Not stale, but maybe its child object is.
00317         ((TypedWritable *)object)->update_bam_nested(this);
00318       }
00319     }
00320   }
00321 }
00322 
00323 ////////////////////////////////////////////////////////////////////
00324 //     Function: BamWriter::write_cdata
00325 //       Access: Public
00326 //  Description: Writes out the indicated CycleData object.  This
00327 //               should be used by classes that store some or all of
00328 //               their data within a CycleData subclass, in support of
00329 //               pipelining.  This will call the virtual
00330 //               CycleData::write_datagram() method to do the actual
00331 //               writing.
00332 ////////////////////////////////////////////////////////////////////
00333 void BamWriter::
00334 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler) {
00335   const CycleData *cdata = cycler.read(Thread::get_current_thread());
00336   cdata->write_datagram(this, packet);
00337   cycler.release_read(cdata);
00338 }
00339 
00340 ////////////////////////////////////////////////////////////////////
00341 //     Function: BamWriter::write_cdata
00342 //       Access: Public
00343 //  Description: This version of write_cdata allows passing an
00344 //               additional parameter to cdata->write_datagram().
00345 ////////////////////////////////////////////////////////////////////
00346 void BamWriter::
00347 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler,
00348             void *extra_data) {
00349   const CycleData *cdata = cycler.read(Thread::get_current_thread());
00350   cdata->write_datagram(this, packet, extra_data);
00351   cycler.release_read(cdata);
00352 }
00353 
00354 ////////////////////////////////////////////////////////////////////
00355 //     Function: BamWriter::register_pta
00356 //       Access: Public
00357 //  Description: Prepares to write a PointerToArray to the Bam file,
00358 //               unifying references to the same pointer across the
00359 //               Bam file.
00360 //
00361 //               The writing object should call this prior to writing
00362 //               out a PointerToArray.  It will return true if the
00363 //               same pointer has been written previously, in which
00364 //               case the writing object need do nothing further; or
00365 //               it will return false if this particular pointer has
00366 //               not yet been written, in which case the writing
00367 //               object must then write out the contents of the array.
00368 //
00369 //               Also see the WRITE_PTA() macro, which consolidates
00370 //               the work that must be done to write a PTA.
00371 ////////////////////////////////////////////////////////////////////
00372 bool BamWriter::
00373 register_pta(Datagram &packet, const void *ptr) {
00374   if (ptr == (const void *)NULL) {
00375     // A zero for the PTA ID indicates a NULL pointer.  This is a
00376     // special case.
00377     write_pta_id(packet, 0);
00378 
00379     // We return false to indicate the user must now write out the
00380     // "definition" of the NULL pointer.  This is necessary because of
00381     // a quirk in the BamReader's design, which forces callers to read
00382     // the definition of every NULL pointer.  Presumably, the caller
00383     // will be able to write the definition in a concise way that will
00384     // clearly indicate a NULL pointer; in the case of a
00385     // PointerToArray, this will generally be simply a zero element
00386     // count.
00387     return false;
00388   }
00389 
00390   PTAMap::iterator pi = _pta_map.find(ptr);
00391   if (pi == _pta_map.end()) {
00392     // We have not encountered this pointer before.
00393     int pta_id = _next_pta_id;
00394     _next_pta_id++;
00395 
00396     bool inserted = _pta_map.insert(PTAMap::value_type(ptr, pta_id)).second;
00397     nassertr(inserted, false);
00398 
00399     write_pta_id(packet, pta_id);
00400 
00401     // Return false to indicate the caller must now write out the
00402     // array definition.
00403     return false;
00404 
00405   } else {
00406     // We have encountered this pointer before.
00407     int pta_id = (*pi).second;
00408     write_pta_id(packet, pta_id);
00409 
00410     // Return true to indicate the caller need do nothing further.
00411     return true;
00412   }
00413 }
00414 
00415 ////////////////////////////////////////////////////////////////////
00416 //     Function: BamWriter::write_handle
00417 //       Access: Public
00418 //  Description: Writes a TypeHandle to the file in such a way that
00419 //               the BamReader can read the same TypeHandle later via
00420 //               read_handle().
00421 ////////////////////////////////////////////////////////////////////
00422 void BamWriter::
00423 write_handle(Datagram &packet, TypeHandle type) {
00424   // We encode TypeHandles within the Bam file by writing a unique
00425   // index number for each one to the file.  When we write a
00426   // particular TypeHandle for the first time, we assign it a new
00427   // index number and then immediately follow it by its definition;
00428   // when we write the same TypeHandle on subsequent times we only
00429   // write the index number.
00430 
00431   // The unique number we choose is actually the internal index number
00432   // of the TypeHandle.  Why not?
00433   int index = type.get_index();
00434 
00435   // Also make sure the index number fits within a PN_uint16.
00436   nassertv(index <= 0xffff);
00437 
00438   packet.add_uint16(index);
00439 
00440   if (index != 0) {
00441     bool inserted = _types_written.insert(index).second;
00442 
00443     if (inserted) {
00444       // This is the first time this TypeHandle has been written, so
00445       // also write out its definition.
00446       packet.add_string(type.get_name());
00447 
00448       // We also need to write the derivation of the TypeHandle, in case
00449       // the program reading this file later has never heard of this
00450       // type before.
00451       int num_parent_classes = type.get_num_parent_classes();
00452       nassertv(num_parent_classes <= 255);  // Good grief!
00453       packet.add_uint8(num_parent_classes);
00454       for (int i = 0; i < num_parent_classes; i++) {
00455         write_handle(packet, type.get_parent_class(i));
00456       }
00457     }
00458   }
00459 }
00460 
00461 
00462 ////////////////////////////////////////////////////////////////////
00463 //     Function: BamWriter::object_destructs
00464 //       Access: Private
00465 //  Description: This is called by the TypedWritable destructor.  It
00466 //               should remove the pointer from any structures that
00467 //               keep a reference to it, and also write a flag to the
00468 //               bam file (if it is open) so that a reader will know
00469 //               the object id will no longer be used.
00470 ////////////////////////////////////////////////////////////////////
00471 void BamWriter::
00472 object_destructs(TypedWritable *object) {
00473   StateMap::iterator si = _state_map.find(object);
00474   if (si != _state_map.end()) {
00475     // We ought to have written out the object by the time it
00476     // destructs, or we're in trouble when we do write it out.
00477     nassertv(!(*si).second._written_seq.is_initial());
00478 
00479     int object_id = (*si).second._object_id;
00480     _freed_object_ids.push_back(object_id);
00481 
00482     _state_map.erase(si);
00483   }
00484 }
00485 
00486 ////////////////////////////////////////////////////////////////////
00487 //     Function: BamWriter::write_object_id
00488 //       Access: Private
00489 //  Description: Writes the indicated object id to the datagram.
00490 ////////////////////////////////////////////////////////////////////
00491 void BamWriter::
00492 write_object_id(Datagram &dg, int object_id) {
00493   if (_long_object_id) {
00494     dg.add_uint32(object_id);
00495     
00496   } else {
00497     dg.add_uint16(object_id);
00498     // Once we fill up our uint16, we write all object id's
00499     // thereafter with a uint32.
00500     if (object_id == 0xffff) {
00501       _long_object_id = true;
00502     }
00503   }
00504 }
00505 
00506 ////////////////////////////////////////////////////////////////////
00507 //     Function: BamWriter::write_pta_id
00508 //       Access: Private
00509 //  Description: Writes the indicated pta id to the datagram.
00510 ////////////////////////////////////////////////////////////////////
00511 void BamWriter::
00512 write_pta_id(Datagram &dg, int pta_id) {
00513   if (_long_pta_id) {
00514     dg.add_uint32(pta_id);
00515     
00516   } else {
00517     dg.add_uint16(pta_id);
00518     // Once we fill up our uint16, we write all pta id's
00519     // thereafter with a uint32.
00520     if (pta_id == 0xffff) {
00521       _long_pta_id = true;
00522     }
00523   }
00524 }
00525 
00526 ////////////////////////////////////////////////////////////////////
00527 //     Function: BamWriter::enqueue_object
00528 //       Access: Private
00529 //  Description: Assigns an object ID to the object and queues it up
00530 //               for later writing to the Bam file.
00531 //
00532 //               The return value is the object ID, or 0 if there is
00533 //               an error.
00534 ////////////////////////////////////////////////////////////////////
00535 int BamWriter::
00536 enqueue_object(const TypedWritable *object) {
00537   Datagram dg;
00538 
00539   nassertr(object != TypedWritable::Null, 0);
00540 
00541   // No object should ever be written out that is not registered as a
00542   // child of TypedWritable.  The only way this can happen is if
00543   // someone failed to initialize their type correctly in init_type().
00544 #ifndef NDEBUG
00545   if (!object->is_of_type(TypedWritable::get_class_type())) {
00546     util_cat.error()
00547       << "Type " << object->get_type() 
00548       << " does not indicate inheritance from TypedWritable.\n"
00549       << "(this is almost certainly an oversight in " << object->get_type()
00550       << "::init_type().)\n";
00551   }
00552 #endif
00553 
00554   // We need to assign a unique index number to every object we write
00555   // out.  Has this object been assigned a number yet?
00556   int object_id;
00557 
00558   StateMap::iterator si = _state_map.find(object);
00559   if (si == _state_map.end()) {
00560     // No, it hasn't, so assign it the next number in sequence
00561     // arbitrarily.
00562     object_id = _next_object_id;
00563 
00564     bool inserted =
00565       _state_map.insert(StateMap::value_type(object, StoreState(_next_object_id))).second;
00566     nassertr(inserted, false);
00567     {
00568       LightMutexHolder holder(TypedWritable::_bam_writers_lock);
00569       if (object->_bam_writers == ((TypedWritable::BamWriters *)NULL)) {
00570         ((TypedWritable *)object)->_bam_writers = new TypedWritable::BamWriters;
00571       }
00572       object->_bam_writers->push_back(this);
00573     }
00574     _next_object_id++;
00575 
00576   } else {
00577     // Yes, it has; get the object ID.
00578     object_id = (*si).second._object_id;
00579   }
00580 
00581   _object_queue.push_back(object);
00582   return object_id;
00583 }
00584 
00585 ////////////////////////////////////////////////////////////////////
00586 //     Function: BamWriter::flush_queue
00587 //       Access: Private
00588 //  Description: Writes all of the objects on the _object_queue to the
00589 //               bam stream, until the queue is empty.
00590 //
00591 //               Returns true on success, false on failure.
00592 ////////////////////////////////////////////////////////////////////
00593 bool BamWriter::
00594 flush_queue() {
00595   nassertr(_target != NULL, false);
00596   // Each object we write may append more to the queue.
00597   while (!_object_queue.empty()) {
00598     const TypedWritable *object = _object_queue.front();
00599     _object_queue.pop_front();
00600 
00601     // Look up the object in the map.  It had better be there!
00602     StateMap::iterator si = _state_map.find(object);
00603     nassertr(si != _state_map.end(), false);
00604 
00605     if ((*si).second._written_seq == _writing_seq) {
00606       // We have already visited this object; no need to consider it again.
00607       continue;
00608     }
00609 
00610     int object_id = (*si).second._object_id;
00611     bool already_written = !(*si).second._written_seq.is_initial();
00612     if ((*si).second._modified != object->get_bam_modified()) {
00613       // This object was previously written, but it has since been
00614       // modified, so we should write it again.
00615       already_written = false;
00616     }
00617 
00618     Datagram dg;
00619     dg.add_uint8(_next_boc);
00620     _next_boc = BOC_adjunct;
00621 
00622     if (!already_written) {
00623       // The first time we write a particular object, or when we
00624       // update the same object later, we do so by writing its
00625       // TypeHandle (which had better not be TypeHandle::none(), since
00626       // that's our code for a previously-written object), followed by
00627       // the object ID number, followed by the object definition.
00628 
00629       TypeHandle type = object->get_type();
00630       nassertr(type != TypeHandle::none(), false);
00631 
00632       // Determine what the nearest kind of type is that the reader
00633       // will be able to handle, and write that instead.
00634       TypeHandle registered_type = 
00635         BamReader::get_factory()->find_registered_type(type);
00636       if (registered_type == TypeHandle::none()) {
00637         // We won't be able to read this type again.
00638         util_cat.warning()
00639           << "Objects of type " << type << " cannot be read; bam file is invalid.\n";
00640       } else if (registered_type != type) {
00641         util_cat.info()
00642           << "Writing " << registered_type << " instead of " << type << "\n";
00643         type = registered_type;
00644 
00645       } else if (util_cat.is_debug()) {
00646         util_cat.debug()
00647           << "Writing " << type << " object id " << object_id
00648           << " to bam file\n";
00649       }
00650 
00651       write_handle(dg, type);
00652       write_object_id(dg, object_id);
00653 
00654       // We cast the const pointer to non-const so that we may call
00655       // write_datagram() on it.  Really, write_datagram() should be a
00656       // const method anyway, but there may be times when a class
00657       // object wants to update some transparent cache value during
00658       // writing or something like that, so it's more convenient to
00659       // cheat and define it as a non-const method.
00660       ((TypedWritable *)object)->write_datagram(this, dg);
00661 
00662       (*si).second._written_seq = _writing_seq;
00663       (*si).second._modified = object->get_bam_modified();
00664 
00665     } else {
00666       // On subsequent times when we write a particular object, we
00667       // write simply TypeHandle::none(), followed by the object ID.
00668       // The occurrence of TypeHandle::none() is an indicator to the
00669       // BamReader that this is a previously-written object.
00670 
00671       write_handle(dg, TypeHandle::none());
00672       write_object_id(dg, object_id);
00673 
00674       // The object has not been modified, but maybe one of its child
00675       // objects has.
00676       ((TypedWritable *)object)->update_bam_nested(this);
00677     }
00678 
00679     if (!_target->put_datagram(dg)) {
00680       util_cat.error()
00681         << "Unable to write data to output.\n";
00682       return false;
00683     }
00684   }
00685 
00686   return true;
00687 }
 All Classes Functions Variables Enumerations