Panda3D
bamWriter.cxx
1 // Filename: bamWriter.cxx
2 // Created by: jason (08Jun00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pandabase.h"
16 #include "pnotify.h"
17 
18 #include "typedWritable.h"
19 #include "config_util.h"
20 #include "bam.h"
21 #include "bamWriter.h"
22 #include "bamReader.h"
23 #include "lightMutexHolder.h"
24 
25 #include <algorithm>
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: BamWriter::Constructor
29 // Access: Published
30 // Description:
31 ////////////////////////////////////////////////////////////////////
32 BamWriter::
33 BamWriter(DatagramSink *target) :
34  _target(target)
35 {
36  ++_writing_seq;
37  _next_boc = BOC_adjunct;
38  _needs_init = true;
39 
40  // Initialize the next object and PTA ID's. These start counting at
41  // 1, since 0 is reserved for NULL.
42  _next_object_id = 1;
43  _long_object_id = false;
44  _next_pta_id = 1;
45  _long_pta_id = false;
46 
47  _file_endian = bam_endian;
48  _file_stdfloat_double = bam_stdfloat_double;
49  _file_texture_mode = bam_texture_mode;
50 }
51 
52 ////////////////////////////////////////////////////////////////////
53 // Function: BamWriter::Destructor
54 // Access: Published
55 // Description:
56 ////////////////////////////////////////////////////////////////////
57 BamWriter::
58 ~BamWriter() {
59  // Tell all the TypedWritables whose pointer we are still keeping to
60  // forget about us.
61  StateMap::iterator si;
62  for (si = _state_map.begin(); si != _state_map.end(); ++si) {
63  TypedWritable *object = (TypedWritable *)(*si).first;
64  object->remove_bam_writer(this);
65  }
66 }
67 
68 ////////////////////////////////////////////////////////////////////
69 // Function: BamWriter::set_target
70 // Access: Published
71 // Description: Changes the destination of future datagrams written
72 // by the BamWriter. This also implicitly calls init()
73 // if it has not already been called.
74 ////////////////////////////////////////////////////////////////////
75 void BamWriter::
77  if (_target != NULL) {
78  _target->flush();
79  }
80  _target = target;
81 
82  if (_needs_init && _target != NULL) {
83  init();
84  }
85 }
86 
87 ////////////////////////////////////////////////////////////////////
88 // Function: BamWriter::init
89 // Access: Published
90 // Description: Initializes the BamWriter prior to writing any
91 // objects to its output stream. This includes writing
92 // out the Bam header.
93 //
94 // This returns true if the BamWriter successfully
95 // initialized, false otherwise.
96 ////////////////////////////////////////////////////////////////////
97 bool BamWriter::
98 init() {
99  nassertr(_target != NULL, false);
100  nassertr(_needs_init, false);
101  _needs_init = false;
102 
103  // Initialize the next object and PTA ID's. These start counting at
104  // 1, since 0 is reserved for NULL.
105  _next_object_id = 1;
106  _long_object_id = false;
107  _next_pta_id = 1;
108  _long_pta_id = false;
109 
110  _file_endian = bam_endian;
111  _file_texture_mode = bam_texture_mode;
112 
113  // Write out the current major and minor BAM file version numbers.
114  Datagram header;
115 
116  header.add_uint16(_bam_major_ver);
117  header.add_uint16(_bam_minor_ver);
118  header.add_uint8(_file_endian);
119  header.add_bool(_file_stdfloat_double);
120 
121  if (!_target->put_datagram(header)) {
122  util_cat.error()
123  << "Unable to write Bam header.\n";
124  return false;
125  }
126  return true;
127 }
128 
129 ////////////////////////////////////////////////////////////////////
130 // Function: BamWriter::write_object
131 // Access: Published
132 // Description: Writes a single object to the Bam file, so that the
133 // BamReader::read_object() can later correctly restore
134 // the object and all its pointers.
135 //
136 // This implicitly also writes any additional objects
137 // this object references (if they haven't already been
138 // written), so that pointers may be fully resolved.
139 //
140 // This may be called repeatedly to write a sequence of
141 // objects to the Bam file, but typically (especially
142 // for scene graph files, indicated with the .bam
143 // extension), only one object is written directly from
144 // the Bam file: the root of the scene graph. The
145 // remaining objects will all be written recursively by
146 // the first object.
147 //
148 // Returns true if the object is successfully written,
149 // false otherwise.
150 ////////////////////////////////////////////////////////////////////
151 bool BamWriter::
152 write_object(const TypedWritable *object) {
153  nassertr(_target != NULL, false);
154 
155  // Increment the _writing_seq, so we can check for newly stale
156  // objects during this operation.
157  ++_writing_seq;
158 
159  // If there are any freed objects to indicate, write them out now.
160  if (!_freed_object_ids.empty()) {
161  Datagram dg;
162  dg.add_uint8(BOC_remove);
163 
164  FreedObjectIds::iterator fi;
165  for (fi = _freed_object_ids.begin(); fi != _freed_object_ids.end(); ++fi) {
166  write_object_id(dg, (*fi));
167  }
168  _freed_object_ids.clear();
169 
170  if (!_target->put_datagram(dg)) {
171  util_cat.error()
172  << "Unable to write data to output.\n";
173  return false;
174  }
175  }
176 
177  nassertr(_object_queue.empty(), false);
178  _next_boc = BOC_push;
179 
180  int object_id = enqueue_object(object);
181  nassertr(object_id != 0, false);
182  if (!flush_queue()) {
183  return false;
184  }
185 
186  // Finally, write the closing pop.
187  if (_next_boc != BOC_push) {
188  Datagram dg;
189  dg.add_uint8(BOC_pop);
190  if (!_target->put_datagram(dg)) {
191  util_cat.error()
192  << "Unable to write data to output.\n";
193  return false;
194  }
195  }
196 
197  return true;
198 }
199 
200 ////////////////////////////////////////////////////////////////////
201 // Function: BamWriter::has_object
202 // Access: Published
203 // Description: Returns true if the object has previously been
204 // written (or at least requested to be written) to the
205 // bam file, or false if we've never heard of it before.
206 ////////////////////////////////////////////////////////////////////
207 bool BamWriter::
208 has_object(const TypedWritable *object) const {
209  StateMap::const_iterator si = _state_map.find(object);
210  return (si != _state_map.end());
211 }
212 
213 ////////////////////////////////////////////////////////////////////
214 // Function: BamWriter::flush
215 // Access: Published
216 // Description: Ensures that all data written thus far is manifested
217 // on the output stream.
218 ////////////////////////////////////////////////////////////////////
219 void BamWriter::
220 flush() {
221  nassertv(_target != NULL);
222  _target->flush();
223 }
224 
225 ////////////////////////////////////////////////////////////////////
226 // Function: BamWriter::consider_update
227 // Access: Public
228 // Description: Should be called from
229 // TypedWritable::update_bam_nested() to recursively
230 // check the entire hiererachy of writable objects for
231 // needed updates. This tests the indicated
232 // TypedWritable object and writes it to the bam stream
233 // if it has recently been modified, then recurses
234 // through update_bam_nested.
235 ////////////////////////////////////////////////////////////////////
236 void BamWriter::
238  StateMap::iterator si = _state_map.find(object);
239  if (si == _state_map.end()) {
240  // This object has never even been seen before.
241  enqueue_object(object);
242 
243  } else if ((*si).second._written_seq.is_initial()) {
244  // This object has not been written yet.
245  enqueue_object(object);
246 
247  } else if ((*si).second._written_seq == _writing_seq) {
248  // We have already visited this object this pass, so no need to
249  // look closer.
250 
251  } else if ((*si).second._modified != object->get_bam_modified()) {
252  // This object has been recently modified and needs to be rewritten.
253  enqueue_object(object);
254 
255  } else {
256  // Mark that we have now visited this object and pronounced it clean.
257  (*si).second._written_seq = _writing_seq;
258 
259  // Recurse to child objects.
260  ((TypedWritable *)object)->update_bam_nested(this);
261  }
262 }
263 
264 ////////////////////////////////////////////////////////////////////
265 // Function: BamWriter::write_pointer
266 // Access: Public
267 // Description: The interface for writing a pointer to another object
268 // to a Bam file. This is intended to be called by the
269 // various objects that write themselves to the Bam
270 // file, within the write_datagram() method.
271 //
272 // This writes the pointer out in such a way that the
273 // BamReader will be able to restore the pointer later.
274 // If the pointer is to an object that has not yet
275 // itself been written to the Bam file, that object will
276 // automatically be written.
277 ////////////////////////////////////////////////////////////////////
278 void BamWriter::
279 write_pointer(Datagram &packet, const TypedWritable *object) {
280  // If the pointer is NULL, we always simply write a zero for an
281  // object ID and leave it at that.
282  if (object == (const TypedWritable *)NULL) {
283  write_object_id(packet, 0);
284 
285  } else {
286  StateMap::iterator si = _state_map.find(object);
287  if (si == _state_map.end()) {
288  // We have not written this pointer out yet. This means we must
289  // queue the object definition up for later.
290  int object_id = enqueue_object(object);
291  write_object_id(packet, object_id);
292 
293  } else {
294  // We have already assigned this pointer an ID, so it has
295  // previously been written; but we might still need to rewrite
296  // it if it is stale.
297  int object_id = (*si).second._object_id;
298  bool already_written = !(*si).second._written_seq.is_initial();
299  if ((*si).second._written_seq != _writing_seq &&
300  (*si).second._modified != object->get_bam_modified()) {
301  // This object was previously written, but it has since been
302  // modified, so we should write it again.
303  already_written = false;
304  }
305 
306  write_object_id(packet, object_id);
307 
308  if (!already_written) {
309  // It's stale, so queue the object for rewriting too.
310  enqueue_object(object);
311  } else {
312  // Not stale, but maybe its child object is.
313  ((TypedWritable *)object)->update_bam_nested(this);
314  }
315  }
316  }
317 }
318 
319 ////////////////////////////////////////////////////////////////////
320 // Function: BamWriter::write_file_data
321 // Access: Public
322 // Description: Writes a block of auxiliary file data from the
323 // indicated file (within the vfs). This can be a block
324 // of arbitrary size, and it is assumed it may be quite
325 // large. This must be balanced by a matching call to
326 // read_file_data() on restore.
327 ////////////////////////////////////////////////////////////////////
328 void BamWriter::
329 write_file_data(SubfileInfo &result, const Filename &filename) {
330  // We write file data by preceding with a singleton datagram that
331  // contains only the BOC_file_data token.
332  Datagram dg;
333  dg.add_uint8(BOC_file_data);
334  if (!_target->put_datagram(dg)) {
335  util_cat.error()
336  << "Unable to write data to output.\n";
337  return;
338  }
339 
340  // Then we can write the file data itself, as its own (possibly
341  // quite large) followup datagram.
342  if (!_target->copy_datagram(result, filename)) {
343  util_cat.error()
344  << "Unable to write file data to output.\n";
345  return;
346  }
347 
348  // Both of those get written to the bam stream prior to the datagram
349  // that represents this particular object, but they'll get pulled
350  // out in the same order and queued up in the BamReader.
351 }
352 
353 ////////////////////////////////////////////////////////////////////
354 // Function: BamWriter::write_file_data
355 // Access: Public
356 // Description: Writes a block of auxiliary file data from the
357 // indicated file (outside of the vfs). This can be a
358 // block of arbitrary size, and it is assumed it may be
359 // quite large. This must be balanced by a matching
360 // call to read_file_data() on restore.
361 ////////////////////////////////////////////////////////////////////
362 void BamWriter::
363 write_file_data(SubfileInfo &result, const SubfileInfo &source) {
364  // We write file data by preceding with a singleton datagram that
365  // contains only the BOC_file_data token.
366  Datagram dg;
367  dg.add_uint8(BOC_file_data);
368  if (!_target->put_datagram(dg)) {
369  util_cat.error()
370  << "Unable to write data to output.\n";
371  return;
372  }
373 
374  // Then we can write the file data itself, as its own (possibly
375  // quite large) followup datagram.
376  if (!_target->copy_datagram(result, source)) {
377  util_cat.error()
378  << "Unable to write file data to output.\n";
379  return;
380  }
381 
382  // Both of those get written to the bam stream prior to the datagram
383  // that represents this particular object, but they'll get pulled
384  // out in the same order and queued up in the BamReader.
385 }
386 
387 ////////////////////////////////////////////////////////////////////
388 // Function: BamWriter::write_cdata
389 // Access: Public
390 // Description: Writes out the indicated CycleData object. This
391 // should be used by classes that store some or all of
392 // their data within a CycleData subclass, in support of
393 // pipelining. This will call the virtual
394 // CycleData::write_datagram() method to do the actual
395 // writing.
396 ////////////////////////////////////////////////////////////////////
397 void BamWriter::
398 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler) {
399  const CycleData *cdata = cycler.read(Thread::get_current_thread());
400  cdata->write_datagram(this, packet);
401  cycler.release_read(cdata);
402 }
403 
404 ////////////////////////////////////////////////////////////////////
405 // Function: BamWriter::write_cdata
406 // Access: Public
407 // Description: This version of write_cdata allows passing an
408 // additional parameter to cdata->write_datagram().
409 ////////////////////////////////////////////////////////////////////
410 void BamWriter::
411 write_cdata(Datagram &packet, const PipelineCyclerBase &cycler,
412  void *extra_data) {
413  const CycleData *cdata = cycler.read(Thread::get_current_thread());
414  cdata->write_datagram(this, packet, extra_data);
415  cycler.release_read(cdata);
416 }
417 
418 ////////////////////////////////////////////////////////////////////
419 // Function: BamWriter::register_pta
420 // Access: Public
421 // Description: Prepares to write a PointerToArray to the Bam file,
422 // unifying references to the same pointer across the
423 // Bam file.
424 //
425 // The writing object should call this prior to writing
426 // out a PointerToArray. It will return true if the
427 // same pointer has been written previously, in which
428 // case the writing object need do nothing further; or
429 // it will return false if this particular pointer has
430 // not yet been written, in which case the writing
431 // object must then write out the contents of the array.
432 //
433 // Also see the WRITE_PTA() macro, which consolidates
434 // the work that must be done to write a PTA.
435 ////////////////////////////////////////////////////////////////////
436 bool BamWriter::
437 register_pta(Datagram &packet, const void *ptr) {
438  if (ptr == (const void *)NULL) {
439  // A zero for the PTA ID indicates a NULL pointer. This is a
440  // special case.
441  write_pta_id(packet, 0);
442 
443  // We return false to indicate the user must now write out the
444  // "definition" of the NULL pointer. This is necessary because of
445  // a quirk in the BamReader's design, which forces callers to read
446  // the definition of every NULL pointer. Presumably, the caller
447  // will be able to write the definition in a concise way that will
448  // clearly indicate a NULL pointer; in the case of a
449  // PointerToArray, this will generally be simply a zero element
450  // count.
451  return false;
452  }
453 
454  PTAMap::iterator pi = _pta_map.find(ptr);
455  if (pi == _pta_map.end()) {
456  // We have not encountered this pointer before.
457  int pta_id = _next_pta_id;
458  _next_pta_id++;
459 
460  bool inserted = _pta_map.insert(PTAMap::value_type(ptr, pta_id)).second;
461  nassertr(inserted, false);
462 
463  write_pta_id(packet, pta_id);
464 
465  // Return false to indicate the caller must now write out the
466  // array definition.
467  return false;
468 
469  } else {
470  // We have encountered this pointer before.
471  int pta_id = (*pi).second;
472  write_pta_id(packet, pta_id);
473 
474  // Return true to indicate the caller need do nothing further.
475  return true;
476  }
477 }
478 
479 ////////////////////////////////////////////////////////////////////
480 // Function: BamWriter::write_handle
481 // Access: Public
482 // Description: Writes a TypeHandle to the file in such a way that
483 // the BamReader can read the same TypeHandle later via
484 // read_handle().
485 ////////////////////////////////////////////////////////////////////
486 void BamWriter::
488  // We encode TypeHandles within the Bam file by writing a unique
489  // index number for each one to the file. When we write a
490  // particular TypeHandle for the first time, we assign it a new
491  // index number and then immediately follow it by its definition;
492  // when we write the same TypeHandle on subsequent times we only
493  // write the index number.
494 
495  // The unique number we choose is actually the internal index number
496  // of the TypeHandle. Why not?
497  int index = type.get_index();
498 
499  // Also make sure the index number fits within a PN_uint16.
500  nassertv(index <= 0xffff);
501 
502  packet.add_uint16(index);
503 
504  if (index != 0) {
505  bool inserted = _types_written.insert(index).second;
506 
507  if (inserted) {
508  // This is the first time this TypeHandle has been written, so
509  // also write out its definition.
510  packet.add_string(type.get_name());
511 
512  // We also need to write the derivation of the TypeHandle, in case
513  // the program reading this file later has never heard of this
514  // type before.
515  int num_parent_classes = type.get_num_parent_classes();
516  nassertv(num_parent_classes <= 255); // Good grief!
517  packet.add_uint8(num_parent_classes);
518  for (int i = 0; i < num_parent_classes; i++) {
519  write_handle(packet, type.get_parent_class(i));
520  }
521  }
522  }
523 }
524 
525 
526 ////////////////////////////////////////////////////////////////////
527 // Function: BamWriter::object_destructs
528 // Access: Private
529 // Description: This is called by the TypedWritable destructor. It
530 // should remove the pointer from any structures that
531 // keep a reference to it, and also write a flag to the
532 // bam file (if it is open) so that a reader will know
533 // the object id will no longer be used.
534 ////////////////////////////////////////////////////////////////////
535 void BamWriter::
536 object_destructs(TypedWritable *object) {
537  StateMap::iterator si = _state_map.find(object);
538  if (si != _state_map.end()) {
539  // We ought to have written out the object by the time it
540  // destructs, or we're in trouble when we do write it out.
541  nassertv(!(*si).second._written_seq.is_initial());
542 
543  int object_id = (*si).second._object_id;
544  _freed_object_ids.push_back(object_id);
545 
546  _state_map.erase(si);
547  }
548 }
549 
550 ////////////////////////////////////////////////////////////////////
551 // Function: BamWriter::write_object_id
552 // Access: Private
553 // Description: Writes the indicated object id to the datagram.
554 ////////////////////////////////////////////////////////////////////
555 void BamWriter::
556 write_object_id(Datagram &dg, int object_id) {
557  if (_long_object_id) {
558  dg.add_uint32(object_id);
559 
560  } else {
561  dg.add_uint16(object_id);
562  // Once we fill up our uint16, we write all object id's
563  // thereafter with a uint32.
564  if (object_id == 0xffff) {
565  _long_object_id = true;
566  }
567  }
568 }
569 
570 ////////////////////////////////////////////////////////////////////
571 // Function: BamWriter::write_pta_id
572 // Access: Private
573 // Description: Writes the indicated pta id to the datagram.
574 ////////////////////////////////////////////////////////////////////
575 void BamWriter::
576 write_pta_id(Datagram &dg, int pta_id) {
577  if (_long_pta_id) {
578  dg.add_uint32(pta_id);
579 
580  } else {
581  dg.add_uint16(pta_id);
582  // Once we fill up our uint16, we write all pta id's
583  // thereafter with a uint32.
584  if (pta_id == 0xffff) {
585  _long_pta_id = true;
586  }
587  }
588 }
589 
590 ////////////////////////////////////////////////////////////////////
591 // Function: BamWriter::enqueue_object
592 // Access: Private
593 // Description: Assigns an object ID to the object and queues it up
594 // for later writing to the Bam file.
595 //
596 // The return value is the object ID, or 0 if there is
597 // an error.
598 ////////////////////////////////////////////////////////////////////
599 int BamWriter::
600 enqueue_object(const TypedWritable *object) {
601  Datagram dg;
602 
603  nassertr(object != TypedWritable::Null, 0);
604 
605  // No object should ever be written out that is not registered as a
606  // child of TypedWritable. The only way this can happen is if
607  // someone failed to initialize their type correctly in init_type().
608 #ifndef NDEBUG
609  if (!object->is_of_type(TypedWritable::get_class_type())) {
610  util_cat.error()
611  << "Type " << object->get_type()
612  << " does not indicate inheritance from TypedWritable.\n"
613  << "(this is almost certainly an oversight in " << object->get_type()
614  << "::init_type().)\n";
615  }
616 #endif
617 
618  // We need to assign a unique index number to every object we write
619  // out. Has this object been assigned a number yet?
620  int object_id;
621 
622  StateMap::iterator si = _state_map.find(object);
623  if (si == _state_map.end()) {
624  // No, it hasn't, so assign it the next number in sequence
625  // arbitrarily.
626  object_id = _next_object_id;
627 
628  bool inserted =
629  _state_map.insert(StateMap::value_type(object, StoreState(_next_object_id))).second;
630  nassertr(inserted, false);
631 
632  // Store ourselves on the TypedWritable so that we get notified
633  // when it destructs.
634  (const_cast<TypedWritable*>(object))->add_bam_writer(this);
635  _next_object_id++;
636 
637  } else {
638  // Yes, it has; get the object ID.
639  object_id = (*si).second._object_id;
640  }
641 
642  _object_queue.push_back(object);
643  return object_id;
644 }
645 
646 ////////////////////////////////////////////////////////////////////
647 // Function: BamWriter::flush_queue
648 // Access: Private
649 // Description: Writes all of the objects on the _object_queue to the
650 // bam stream, until the queue is empty.
651 //
652 // Returns true on success, false on failure.
653 ////////////////////////////////////////////////////////////////////
654 bool BamWriter::
655 flush_queue() {
656  nassertr(_target != NULL, false);
657  // Each object we write may append more to the queue.
658  while (!_object_queue.empty()) {
659  const TypedWritable *object = _object_queue.front();
660  _object_queue.pop_front();
661 
662  // Look up the object in the map. It had better be there!
663  StateMap::iterator si = _state_map.find(object);
664  nassertr(si != _state_map.end(), false);
665 
666  if ((*si).second._written_seq == _writing_seq) {
667  // We have already visited this object; no need to consider it again.
668  continue;
669  }
670 
671  int object_id = (*si).second._object_id;
672  bool already_written = !(*si).second._written_seq.is_initial();
673  if ((*si).second._modified != object->get_bam_modified()) {
674  // This object was previously written, but it has since been
675  // modified, so we should write it again.
676  already_written = false;
677  }
678 
679  Datagram dg;
680  dg.set_stdfloat_double(_file_stdfloat_double);
681  dg.add_uint8(_next_boc);
682  _next_boc = BOC_adjunct;
683 
684  if (!already_written) {
685  // The first time we write a particular object, or when we
686  // update the same object later, we do so by writing its
687  // TypeHandle (which had better not be TypeHandle::none(), since
688  // that's our code for a previously-written object), followed by
689  // the object ID number, followed by the object definition.
690 
691  TypeHandle type = object->get_type();
692  nassertr(type != TypeHandle::none(), false);
693 
694  // Determine what the nearest kind of type is that the reader
695  // will be able to handle, and write that instead.
696  TypeHandle registered_type =
698  if (registered_type == TypeHandle::none()) {
699  // We won't be able to read this type again.
700  util_cat.warning()
701  << "Objects of type " << type << " cannot be read; bam file is invalid.\n";
702  } else if (registered_type != type) {
703  util_cat.info()
704  << "Writing " << registered_type << " instead of " << type << "\n";
705  type = registered_type;
706 
707  } else if (util_cat.is_debug()) {
708  util_cat.debug()
709  << "Writing " << type << " object id " << object_id
710  << " to bam file\n";
711  }
712 
713  write_handle(dg, type);
714  write_object_id(dg, object_id);
715 
716  // We cast the const pointer to non-const so that we may call
717  // write_datagram() on it. Really, write_datagram() should be a
718  // const method anyway, but there may be times when a class
719  // object wants to update some transparent cache value during
720  // writing or something like that, so it's more convenient to
721  // cheat and define it as a non-const method.
722  ((TypedWritable *)object)->write_datagram(this, dg);
723 
724  (*si).second._written_seq = _writing_seq;
725  (*si).second._modified = object->get_bam_modified();
726 
727  } else {
728  // On subsequent times when we write a particular object, we
729  // write simply TypeHandle::none(), followed by the object ID.
730  // The occurrence of TypeHandle::none() is an indicator to the
731  // BamReader that this is a previously-written object.
732 
734  write_object_id(dg, object_id);
735 
736  // The object has not been modified, but maybe one of its child
737  // objects has.
738  ((TypedWritable *)object)->update_bam_nested(this);
739  }
740 
741  if (!_target->put_datagram(dg)) {
742  util_cat.error()
743  << "Unable to write data to output.\n";
744  return false;
745  }
746  }
747 
748  return true;
749 }
void add_uint8(PN_uint8 value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:138
void add_string(const string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:351
static TypeHandle none()
Returns a special zero-valued TypeHandle that is used to indicate no type.
Definition: typeHandle.I:274
bool has_object(const TypedWritable *obj) const
Returns true if the object has previously been written (or at least requested to be written) to the b...
Definition: bamWriter.cxx:208
UpdateSeq get_bam_modified() const
Returns the current bam_modified counter.
Definition: typedWritable.I:66
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:398
This is the trivial, non-threaded implementation of PipelineCyclerBase.
int get_index() const
Returns the integer index associated with this TypeHandle.
Definition: typeHandle.I:253
string get_name(TypedObject *object=(TypedObject *) NULL) const
Returns the name of the type.
Definition: typeHandle.I:132
void set_target(DatagramSink *target)
Changes the destination of future datagrams written by the BamWriter.
Definition: bamWriter.cxx:76
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
void set_stdfloat_double(bool stdfloat_double)
Changes the stdfloat_double flag, which defines the operation performed by add_stdfloat() and Datagra...
Definition: datagram.I:523
void write_handle(Datagram &packet, TypeHandle type)
Writes a TypeHandle to the file in such a way that the BamReader can read the same TypeHandle later v...
Definition: bamWriter.cxx:487
virtual void write_datagram(BamWriter *, Datagram &) const
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: cycleData.cxx:34
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:118
This class defines the abstract interface to sending datagrams to any target, whether it be into a fi...
Definition: datagramSink.h:32
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
virtual bool copy_datagram(SubfileInfo &result, const Filename &filename)
Copies the file data from the entire indicated file (via the vfs) as the next datagram.
bool write_object(const TypedWritable *obj)
Writes a single object to the Bam file, so that the BamReader::read_object() can later correctly rest...
Definition: bamWriter.cxx:152
bool register_pta(Datagram &packet, const void *ptr)
Prepares to write a PointerToArray to the Bam file, unifying references to the same pointer across th...
Definition: bamWriter.cxx:437
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:181
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
int get_num_parent_classes(TypedObject *object=(TypedObject *) NULL) const
Returns the number of parent classes that this type is known to have.
Definition: typeHandle.I:173
const CycleData * read(Thread *current_thread) const
Returns a const CycleData pointer, filled with the data for the current stage of the pipeline as seen...
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:192
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:29
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
void flush()
Ensures that all data written thus far is manifested on the output stream.
Definition: bamWriter.cxx:220
void write_file_data(SubfileInfo &result, const Filename &filename)
Writes a block of auxiliary file data from the indicated file (within the vfs).
Definition: bamWriter.cxx:329
void consider_update(const TypedWritable *obj)
Should be called from TypedWritable::update_bam_nested() to recursively check the entire hiererachy o...
Definition: bamWriter.cxx:237
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
Definition: bamWriter.cxx:98
void release_read(const CycleData *pointer) const
Releases a pointer previously obtained via a call to read().
TypeHandle get_parent_class(int index) const
Returns the nth parent class of this type.
Definition: typeHandle.I:185
TypeHandle find_registered_type(TypeHandle handle)
Returns the TypeHandle given, if it is a registered type, or if it is not registered, searches for the nearest ancestor of the indicated type that is registered and returns it.
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:279