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) :
00034   _target(target)
00035 {
00036   ++_writing_seq;
00037   _next_boc = BOC_adjunct;
00038   _needs_init = true;
00039 
00040   // Initialize the next object and PTA ID's.  These start counting at
00041   // 1, since 0 is reserved for NULL.
00042   _next_object_id = 1;
00043   _long_object_id = false;
00044   _next_pta_id = 1;
00045   _long_pta_id = false;
00046 
00047   _file_endian = bam_endian;
00048   _file_stdfloat_double = bam_stdfloat_double;
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   header.add_bool(_file_stdfloat_double);
00125 
00126   if (!_target->put_datagram(header)) {
00127     util_cat.error()
00128       << "Unable to write Bam header.\n";
00129     return false;
00130   }
00131   return true;
00132 }
00133 
00134 ////////////////////////////////////////////////////////////////////
00135 //     Function: BamWriter::write_object
00136 //       Access: Published
00137 //  Description: Writes a single object to the Bam file, so that the
00138 //               BamReader::read_object() can later correctly restore
00139 //               the object and all its pointers.
00140 //
00141 //               This implicitly also writes any additional objects
00142 //               this object references (if they haven't already been
00143 //               written), so that pointers may be fully resolved.
00144 //
00145 //               This may be called repeatedly to write a sequence of
00146 //               objects to the Bam file, but typically (especially
00147 //               for scene graph files, indicated with the .bam
00148 //               extension), only one object is written directly from
00149 //               the Bam file: the root of the scene graph.  The
00150 //               remaining objects will all be written recursively by
00151 //               the first object.
00152 //
00153 //               Returns true if the object is successfully written,
00154 //               false otherwise.
00155 ////////////////////////////////////////////////////////////////////
00156 bool BamWriter::
00157 write_object(const TypedWritable *object) {
00158   nassertr(_target != NULL, false);
00159 
00160   // Increment the _writing_seq, so we can check for newly stale
00161   // objects during this operation.
00162   ++_writing_seq;
00163 
00164   // If there are any freed objects to indicate, write them out now.
00165   if (!_freed_object_ids.empty()) {
00166     Datagram dg;
00167     dg.add_uint8(BOC_remove);
00168 
00169     FreedObjectIds::iterator fi;
00170     for (fi = _freed_object_ids.begin(); fi != _freed_object_ids.end(); ++fi) {
00171       write_object_id(dg, (*fi));
00172     }
00173     _freed_object_ids.clear();
00174 
00175     if (!_target->put_datagram(dg)) {
00176       util_cat.error()
00177         << "Unable to write data to output.\n";
00178       return false;
00179     }
00180   }
00181 
00182   nassertr(_object_queue.empty(), false);
00183   _next_boc = BOC_push;
00184 
00185   int object_id = enqueue_object(object);
00186   nassertr(object_id != 0, false);
00187   if (!flush_queue()) {
00188     return false;
00189   }
00190 
00191   // Finally, write the closing pop.
00192   if (_next_boc != BOC_push) {
00193     Datagram dg;
00194     dg.add_uint8(BOC_pop);
00195     if (!_target->put_datagram(dg)) {
00196       util_cat.error()
00197         << "Unable to write data to output.\n";
00198       return false;
00199     }
00200   }
00201 
00202   return true;
00203 }
00204 
00205 ////////////////////////////////////////////////////////////////////
00206 //     Function: BamWriter::has_object
00207 //       Access: Published
00208 //  Description: Returns true if the object has previously been
00209 //               written (or at least requested to be written) to the
00210 //               bam file, or false if we've never heard of it before.
00211 ////////////////////////////////////////////////////////////////////
00212 bool BamWriter::
00213 has_object(const TypedWritable *object) const {
00214   StateMap::const_iterator si = _state_map.find(object);
00215   return (si != _state_map.end());
00216 }
00217 
00218 ////////////////////////////////////////////////////////////////////
00219 //     Function: BamWriter::flush
00220 //       Access: Published
00221 //  Description: Ensures that all data written thus far is manifested
00222 //               on the output stream.
00223 ////////////////////////////////////////////////////////////////////
00224 void BamWriter::
00225 flush() {
00226   nassertv(_target != NULL);
00227   _target->flush();
00228 }
00229 
00230 ////////////////////////////////////////////////////////////////////
00231 //     Function: BamWriter::consider_update
00232 //       Access: Public
00233 //  Description: Should be called from
00234 //               TypedWritable::update_bam_nested() to recursively
00235 //               check the entire hiererachy of writable objects for
00236 //               needed updates.  This tests the indicated
00237 //               TypedWritable object and writes it to the bam stream
00238 //               if it has recently been modified, then recurses
00239 //               through update_bam_nested.
00240 ////////////////////////////////////////////////////////////////////
00241 void BamWriter::
00242 consider_update(const TypedWritable *object) {
00243   StateMap::iterator si = _state_map.find(object);
00244   if (si == _state_map.end()) {
00245     // This object has never even been seen before.
00246     enqueue_object(object);
00247 
00248   } else if ((*si).second._written_seq.is_initial()) {
00249     // This object has not been written yet.
00250     enqueue_object(object);
00251 
00252   } else if ((*si).second._written_seq == _writing_seq) {
00253     // We have already visited this object this pass, so no need to
00254     // look closer.
00255 
00256   } else if ((*si).second._modified != object->get_bam_modified()) {
00257     // This object has been recently modified and needs to be rewritten.
00258     enqueue_object(object);
00259 
00260   } else {
00261     // Mark that we have now visited this object and pronounced it clean.
00262     (*si).second._written_seq = _writing_seq;
00263     
00264     // Recurse to child objects.
00265     ((TypedWritable *)object)->update_bam_nested(this);
00266   }
00267 }
00268 
00269 ////////////////////////////////////////////////////////////////////
00270 //     Function: BamWriter::write_pointer
00271 //       Access: Public
00272 //  Description: The interface for writing a pointer to another object
00273 //               to a Bam file.  This is intended to be called by the
00274 //               various objects that write themselves to the Bam
00275 //               file, within the write_datagram() method.
00276 //
00277 //               This writes the pointer out in such a way that the
00278 //               BamReader will be able to restore the pointer later.
00279 //               If the pointer is to an object that has not yet
00280 //               itself been written to the Bam file, that object will
00281 //               automatically be written.
00282 ////////////////////////////////////////////////////////////////////
00283 void BamWriter::
00284 write_pointer(Datagram &packet, const TypedWritable *object) {
00285   // If the pointer is NULL, we always simply write a zero for an
00286   // object ID and leave it at that.
00287   if (object == (const TypedWritable *)NULL) {
00288     write_object_id(packet, 0);
00289 
00290   } else {
00291     StateMap::iterator si = _state_map.find(object);
00292     if (si == _state_map.end()) {
00293       // We have not written this pointer out yet.  This means we must
00294       // queue the object definition up for later.
00295       int object_id = enqueue_object(object);
00296       write_object_id(packet, object_id);
00297 
00298     } else {
00299       // We have already assigned this pointer an ID, so it has
00300       // previously been written; but we might still need to rewrite
00301       // it if it is stale.
00302       int object_id = (*si).second._object_id;
00303       bool already_written = !(*si).second._written_seq.is_initial();
00304       if ((*si).second._written_seq != _writing_seq &&
00305           (*si).second._modified != object->get_bam_modified()) {
00306         // This object was previously written, but it has since been
00307         // modified, so we should write it again.
00308         already_written = false;
00309       }
00310 
00311       write_object_id(packet, object_id);
00312 
00313       if (!already_written) {
00314         // It's stale, so queue the object for rewriting too.
00315         enqueue_object(object);
00316       } else {
00317         // Not stale, but maybe its child object is.
00318         ((TypedWritable *)object)->update_bam_nested(this);
00319       }
00320     }
00321   }
00322 }
00323 
00324 ////////////////////////////////////////////////////////////////////
00325 //     Function: BamWriter::write_file_data
00326 //       Access: Public
00327 //  Description: Writes a block of auxiliary file data from the
00328 //               indicated file (within the vfs).  This can be a block
00329 //               of arbitrary size, and it is assumed it may be quite
00330 //               large.  This must be balanced by a matching call to
00331 //               read_file_data() on restore.
00332 ////////////////////////////////////////////////////////////////////
00333 void BamWriter::
00334 write_file_data(SubfileInfo &result, const Filename &filename) {
00335   // We write file data by preceding with a singleton datagram that
00336   // contains only the BOC_file_data token.
00337   Datagram dg;
00338   dg.add_uint8(BOC_file_data);
00339   if (!_target->put_datagram(dg)) {
00340     util_cat.error()
00341       << "Unable to write data to output.\n";
00342     return;
00343   }
00344 
00345   // Then we can write the file data itself, as its own (possibly
00346   // quite large) followup datagram.
00347   if (!_target->copy_datagram(result, filename)) {
00348     util_cat.error()
00349       << "Unable to write file data to output.\n";
00350     return;
00351   }
00352 
00353   // Both of those get written to the bam stream prior to the datagram
00354   // that represents this particular object, but they'll get pulled
00355   // out in the same order and queued up in the BamReader.
00356 }
00357 
00358 ////////////////////////////////////////////////////////////////////
00359 //     Function: BamWriter::write_file_data
00360 //       Access: Public
00361 //  Description: Writes a block of auxiliary file data from the
00362 //               indicated file (outside of the vfs).  This can be a
00363 //               block of arbitrary size, and it is assumed it may be
00364 //               quite large.  This must be balanced by a matching
00365 //               call to read_file_data() on restore.
00366 ////////////////////////////////////////////////////////////////////
00367 void BamWriter::
00368 write_file_data(SubfileInfo &result, const SubfileInfo &source) {
00369   // We write file data by preceding with a singleton datagram that
00370   // contains only the BOC_file_data token.
00371   Datagram dg;
00372   dg.add_uint8(BOC_file_data);
00373   if (!_target->put_datagram(dg)) {
00374     util_cat.error()
00375       << "Unable to write data to output.\n";
00376     return;
00377   }
00378 
00379   // Then we can write the file data itself, as its own (possibly
00380   // quite large) followup datagram.
00381   if (!_target->copy_datagram(result, source)) {
00382     util_cat.error()
00383       << "Unable to write file data to output.\n";
00384     return;
00385   }
00386 
00387   // Both of those get written to the bam stream prior to the datagram
00388   // that represents this particular object, but they'll get pulled
00389   // out in the same order and queued up in the BamReader.
00390 }
00391 
00392 ////////////////////////////////////////////////////////////////////
00393 //     Function: BamWriter::write_cdata
00394 //       Access: Public
00395 //  Description: Writes out the indicated CycleData object.  This
00396 //               should be used by classes that store some or all of
00397 //               their data within a CycleData subclass, in support of
00398 //               pipelining.  This will call the virtual
00399 //               CycleData::write_datagram() method to do the actual
00400 //               writing.
00401 ////////////////////////////////////////////////////////////////////
00402 void BamWriter::
00403 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler) {
00404   const CycleData *cdata = cycler.read(Thread::get_current_thread());
00405   cdata->write_datagram(this, packet);
00406   cycler.release_read(cdata);
00407 }
00408 
00409 ////////////////////////////////////////////////////////////////////
00410 //     Function: BamWriter::write_cdata
00411 //       Access: Public
00412 //  Description: This version of write_cdata allows passing an
00413 //               additional parameter to cdata->write_datagram().
00414 ////////////////////////////////////////////////////////////////////
00415 void BamWriter::
00416 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler,
00417             void *extra_data) {
00418   const CycleData *cdata = cycler.read(Thread::get_current_thread());
00419   cdata->write_datagram(this, packet, extra_data);
00420   cycler.release_read(cdata);
00421 }
00422 
00423 ////////////////////////////////////////////////////////////////////
00424 //     Function: BamWriter::register_pta
00425 //       Access: Public
00426 //  Description: Prepares to write a PointerToArray to the Bam file,
00427 //               unifying references to the same pointer across the
00428 //               Bam file.
00429 //
00430 //               The writing object should call this prior to writing
00431 //               out a PointerToArray.  It will return true if the
00432 //               same pointer has been written previously, in which
00433 //               case the writing object need do nothing further; or
00434 //               it will return false if this particular pointer has
00435 //               not yet been written, in which case the writing
00436 //               object must then write out the contents of the array.
00437 //
00438 //               Also see the WRITE_PTA() macro, which consolidates
00439 //               the work that must be done to write a PTA.
00440 ////////////////////////////////////////////////////////////////////
00441 bool BamWriter::
00442 register_pta(Datagram &packet, const void *ptr) {
00443   if (ptr == (const void *)NULL) {
00444     // A zero for the PTA ID indicates a NULL pointer.  This is a
00445     // special case.
00446     write_pta_id(packet, 0);
00447 
00448     // We return false to indicate the user must now write out the
00449     // "definition" of the NULL pointer.  This is necessary because of
00450     // a quirk in the BamReader's design, which forces callers to read
00451     // the definition of every NULL pointer.  Presumably, the caller
00452     // will be able to write the definition in a concise way that will
00453     // clearly indicate a NULL pointer; in the case of a
00454     // PointerToArray, this will generally be simply a zero element
00455     // count.
00456     return false;
00457   }
00458 
00459   PTAMap::iterator pi = _pta_map.find(ptr);
00460   if (pi == _pta_map.end()) {
00461     // We have not encountered this pointer before.
00462     int pta_id = _next_pta_id;
00463     _next_pta_id++;
00464 
00465     bool inserted = _pta_map.insert(PTAMap::value_type(ptr, pta_id)).second;
00466     nassertr(inserted, false);
00467 
00468     write_pta_id(packet, pta_id);
00469 
00470     // Return false to indicate the caller must now write out the
00471     // array definition.
00472     return false;
00473 
00474   } else {
00475     // We have encountered this pointer before.
00476     int pta_id = (*pi).second;
00477     write_pta_id(packet, pta_id);
00478 
00479     // Return true to indicate the caller need do nothing further.
00480     return true;
00481   }
00482 }
00483 
00484 ////////////////////////////////////////////////////////////////////
00485 //     Function: BamWriter::write_handle
00486 //       Access: Public
00487 //  Description: Writes a TypeHandle to the file in such a way that
00488 //               the BamReader can read the same TypeHandle later via
00489 //               read_handle().
00490 ////////////////////////////////////////////////////////////////////
00491 void BamWriter::
00492 write_handle(Datagram &packet, TypeHandle type) {
00493   // We encode TypeHandles within the Bam file by writing a unique
00494   // index number for each one to the file.  When we write a
00495   // particular TypeHandle for the first time, we assign it a new
00496   // index number and then immediately follow it by its definition;
00497   // when we write the same TypeHandle on subsequent times we only
00498   // write the index number.
00499 
00500   // The unique number we choose is actually the internal index number
00501   // of the TypeHandle.  Why not?
00502   int index = type.get_index();
00503 
00504   // Also make sure the index number fits within a PN_uint16.
00505   nassertv(index <= 0xffff);
00506 
00507   packet.add_uint16(index);
00508 
00509   if (index != 0) {
00510     bool inserted = _types_written.insert(index).second;
00511 
00512     if (inserted) {
00513       // This is the first time this TypeHandle has been written, so
00514       // also write out its definition.
00515       packet.add_string(type.get_name());
00516 
00517       // We also need to write the derivation of the TypeHandle, in case
00518       // the program reading this file later has never heard of this
00519       // type before.
00520       int num_parent_classes = type.get_num_parent_classes();
00521       nassertv(num_parent_classes <= 255);  // Good grief!
00522       packet.add_uint8(num_parent_classes);
00523       for (int i = 0; i < num_parent_classes; i++) {
00524         write_handle(packet, type.get_parent_class(i));
00525       }
00526     }
00527   }
00528 }
00529 
00530 
00531 ////////////////////////////////////////////////////////////////////
00532 //     Function: BamWriter::object_destructs
00533 //       Access: Private
00534 //  Description: This is called by the TypedWritable destructor.  It
00535 //               should remove the pointer from any structures that
00536 //               keep a reference to it, and also write a flag to the
00537 //               bam file (if it is open) so that a reader will know
00538 //               the object id will no longer be used.
00539 ////////////////////////////////////////////////////////////////////
00540 void BamWriter::
00541 object_destructs(TypedWritable *object) {
00542   StateMap::iterator si = _state_map.find(object);
00543   if (si != _state_map.end()) {
00544     // We ought to have written out the object by the time it
00545     // destructs, or we're in trouble when we do write it out.
00546     nassertv(!(*si).second._written_seq.is_initial());
00547 
00548     int object_id = (*si).second._object_id;
00549     _freed_object_ids.push_back(object_id);
00550 
00551     _state_map.erase(si);
00552   }
00553 }
00554 
00555 ////////////////////////////////////////////////////////////////////
00556 //     Function: BamWriter::write_object_id
00557 //       Access: Private
00558 //  Description: Writes the indicated object id to the datagram.
00559 ////////////////////////////////////////////////////////////////////
00560 void BamWriter::
00561 write_object_id(Datagram &dg, int object_id) {
00562   if (_long_object_id) {
00563     dg.add_uint32(object_id);
00564     
00565   } else {
00566     dg.add_uint16(object_id);
00567     // Once we fill up our uint16, we write all object id's
00568     // thereafter with a uint32.
00569     if (object_id == 0xffff) {
00570       _long_object_id = true;
00571     }
00572   }
00573 }
00574 
00575 ////////////////////////////////////////////////////////////////////
00576 //     Function: BamWriter::write_pta_id
00577 //       Access: Private
00578 //  Description: Writes the indicated pta id to the datagram.
00579 ////////////////////////////////////////////////////////////////////
00580 void BamWriter::
00581 write_pta_id(Datagram &dg, int pta_id) {
00582   if (_long_pta_id) {
00583     dg.add_uint32(pta_id);
00584     
00585   } else {
00586     dg.add_uint16(pta_id);
00587     // Once we fill up our uint16, we write all pta id's
00588     // thereafter with a uint32.
00589     if (pta_id == 0xffff) {
00590       _long_pta_id = true;
00591     }
00592   }
00593 }
00594 
00595 ////////////////////////////////////////////////////////////////////
00596 //     Function: BamWriter::enqueue_object
00597 //       Access: Private
00598 //  Description: Assigns an object ID to the object and queues it up
00599 //               for later writing to the Bam file.
00600 //
00601 //               The return value is the object ID, or 0 if there is
00602 //               an error.
00603 ////////////////////////////////////////////////////////////////////
00604 int BamWriter::
00605 enqueue_object(const TypedWritable *object) {
00606   Datagram dg;
00607 
00608   nassertr(object != TypedWritable::Null, 0);
00609 
00610   // No object should ever be written out that is not registered as a
00611   // child of TypedWritable.  The only way this can happen is if
00612   // someone failed to initialize their type correctly in init_type().
00613 #ifndef NDEBUG
00614   if (!object->is_of_type(TypedWritable::get_class_type())) {
00615     util_cat.error()
00616       << "Type " << object->get_type() 
00617       << " does not indicate inheritance from TypedWritable.\n"
00618       << "(this is almost certainly an oversight in " << object->get_type()
00619       << "::init_type().)\n";
00620   }
00621 #endif
00622 
00623   // We need to assign a unique index number to every object we write
00624   // out.  Has this object been assigned a number yet?
00625   int object_id;
00626 
00627   StateMap::iterator si = _state_map.find(object);
00628   if (si == _state_map.end()) {
00629     // No, it hasn't, so assign it the next number in sequence
00630     // arbitrarily.
00631     object_id = _next_object_id;
00632 
00633     bool inserted =
00634       _state_map.insert(StateMap::value_type(object, StoreState(_next_object_id))).second;
00635     nassertr(inserted, false);
00636     {
00637       LightMutexHolder holder(TypedWritable::_bam_writers_lock);
00638       if (object->_bam_writers == ((TypedWritable::BamWriters *)NULL)) {
00639         ((TypedWritable *)object)->_bam_writers = new TypedWritable::BamWriters;
00640       }
00641       object->_bam_writers->push_back(this);
00642     }
00643     _next_object_id++;
00644 
00645   } else {
00646     // Yes, it has; get the object ID.
00647     object_id = (*si).second._object_id;
00648   }
00649 
00650   _object_queue.push_back(object);
00651   return object_id;
00652 }
00653 
00654 ////////////////////////////////////////////////////////////////////
00655 //     Function: BamWriter::flush_queue
00656 //       Access: Private
00657 //  Description: Writes all of the objects on the _object_queue to the
00658 //               bam stream, until the queue is empty.
00659 //
00660 //               Returns true on success, false on failure.
00661 ////////////////////////////////////////////////////////////////////
00662 bool BamWriter::
00663 flush_queue() {
00664   nassertr(_target != NULL, false);
00665   // Each object we write may append more to the queue.
00666   while (!_object_queue.empty()) {
00667     const TypedWritable *object = _object_queue.front();
00668     _object_queue.pop_front();
00669 
00670     // Look up the object in the map.  It had better be there!
00671     StateMap::iterator si = _state_map.find(object);
00672     nassertr(si != _state_map.end(), false);
00673 
00674     if ((*si).second._written_seq == _writing_seq) {
00675       // We have already visited this object; no need to consider it again.
00676       continue;
00677     }
00678 
00679     int object_id = (*si).second._object_id;
00680     bool already_written = !(*si).second._written_seq.is_initial();
00681     if ((*si).second._modified != object->get_bam_modified()) {
00682       // This object was previously written, but it has since been
00683       // modified, so we should write it again.
00684       already_written = false;
00685     }
00686 
00687     Datagram dg;
00688     dg.set_stdfloat_double(_file_stdfloat_double);
00689     dg.add_uint8(_next_boc);
00690     _next_boc = BOC_adjunct;
00691 
00692     if (!already_written) {
00693       // The first time we write a particular object, or when we
00694       // update the same object later, we do so by writing its
00695       // TypeHandle (which had better not be TypeHandle::none(), since
00696       // that's our code for a previously-written object), followed by
00697       // the object ID number, followed by the object definition.
00698 
00699       TypeHandle type = object->get_type();
00700       nassertr(type != TypeHandle::none(), false);
00701 
00702       // Determine what the nearest kind of type is that the reader
00703       // will be able to handle, and write that instead.
00704       TypeHandle registered_type = 
00705         BamReader::get_factory()->find_registered_type(type);
00706       if (registered_type == TypeHandle::none()) {
00707         // We won't be able to read this type again.
00708         util_cat.warning()
00709           << "Objects of type " << type << " cannot be read; bam file is invalid.\n";
00710       } else if (registered_type != type) {
00711         util_cat.info()
00712           << "Writing " << registered_type << " instead of " << type << "\n";
00713         type = registered_type;
00714 
00715       } else if (util_cat.is_debug()) {
00716         util_cat.debug()
00717           << "Writing " << type << " object id " << object_id
00718           << " to bam file\n";
00719       }
00720 
00721       write_handle(dg, type);
00722       write_object_id(dg, object_id);
00723 
00724       // We cast the const pointer to non-const so that we may call
00725       // write_datagram() on it.  Really, write_datagram() should be a
00726       // const method anyway, but there may be times when a class
00727       // object wants to update some transparent cache value during
00728       // writing or something like that, so it's more convenient to
00729       // cheat and define it as a non-const method.
00730       ((TypedWritable *)object)->write_datagram(this, dg);
00731 
00732       (*si).second._written_seq = _writing_seq;
00733       (*si).second._modified = object->get_bam_modified();
00734 
00735     } else {
00736       // On subsequent times when we write a particular object, we
00737       // write simply TypeHandle::none(), followed by the object ID.
00738       // The occurrence of TypeHandle::none() is an indicator to the
00739       // BamReader that this is a previously-written object.
00740 
00741       write_handle(dg, TypeHandle::none());
00742       write_object_id(dg, object_id);
00743 
00744       // The object has not been modified, but maybe one of its child
00745       // objects has.
00746       ((TypedWritable *)object)->update_bam_nested(this);
00747     }
00748 
00749     if (!_target->put_datagram(dg)) {
00750       util_cat.error()
00751         << "Unable to write data to output.\n";
00752       return false;
00753     }
00754   }
00755 
00756   return true;
00757 }
 All Classes Functions Variables Enumerations