diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index 274a776abe..a628806ca6 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -64,7 +64,6 @@ ALLOC_DELETED_CHAIN_DEF(GeomVertexArrayDataHandle); GeomVertexArrayData:: GeomVertexArrayData() : SimpleLruPage(0) { _contexts = NULL; - _endian_reversed = false; // Can't put it in the LRU until it has been read in and made valid. } @@ -97,7 +96,6 @@ GeomVertexArrayData(const GeomVertexArrayFormat *array_format, CLOSE_ITERATE_ALL_STAGES(_cycler); _contexts = NULL; - _endian_reversed = false; set_lru_size(0); nassertv(_array_format->is_registered()); @@ -116,7 +114,6 @@ GeomVertexArrayData(const GeomVertexArrayData ©) : _cycler(copy._cycler) { _contexts = NULL; - _endian_reversed = false; copy.mark_used_lru(); @@ -511,11 +508,14 @@ finalize(BamReader *manager) { manager->change_pointer(_array_format, new_array_format); _array_format = new_array_format; - if (_endian_reversed) { - // Now is the time to endian-reverse the data. - VertexDataBuffer new_buffer(cdata->_buffer.get_size()); - reverse_data_endianness(new_buffer.get_write_pointer(), cdata->_buffer.get_read_pointer(true), cdata->_buffer.get_size()); - cdata->_buffer.swap(new_buffer); + PT(BamAuxData) aux_data = (BamAuxData *)manager->get_aux_data(this, ""); + if (aux_data != (BamAuxData *)NULL) { + if (aux_data->_endian_reversed) { + // Now is the time to endian-reverse the data. + VertexDataBuffer new_buffer(cdata->_buffer.get_size()); + reverse_data_endianness(new_buffer.get_write_pointer(), cdata->_buffer.get_read_pointer(true), cdata->_buffer.get_size()); + cdata->_buffer.swap(new_buffer); + } } set_lru_size(cdata->_buffer.get_size()); @@ -632,13 +632,15 @@ fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) { scan.skip_bytes(size); } + bool endian_reversed = false; + if (manager->get_file_endian() != BE_native) { // For non-native endian files, we have to convert the data. if (array_data->_array_format == (GeomVertexArrayFormat *)NULL) { // But we can't do that until we've completed the _array_format // pointer, which tells us how to convert it. - array_data->_endian_reversed = true; + endian_reversed = true; } else { // Since we have the _array_format pointer now, we can reverse // it immediately (and we should, to support threaded CData @@ -649,6 +651,12 @@ fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) { } } + if (endian_reversed) { + PT(BamAuxData) aux_data = new BamAuxData; + aux_data->_endian_reversed = endian_reversed; + manager->set_aux_data(array_data, "", aux_data); + } + array_data->set_lru_size(_buffer.get_size()); _modified = Geom::get_next_modified(); diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index 04a4a68809..5a66829dcd 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -36,6 +36,7 @@ #include "simpleLru.h" #include "vertexDataBuffer.h" #include "config_gobj.h" +#include "bamReader.h" class PreparedGraphicsObjects; class VertexBufferContext; @@ -135,9 +136,13 @@ private: typedef pmap Contexts; Contexts *_contexts; - // This is only used when reading from a bam file. It is set true - // to indicate the data must be endian-reversed in finalize(). - bool _endian_reversed; + // This data is only needed when reading from a bam file. + class BamAuxData : public BamReader::AuxData { + public: + // set true to indicate the data must be endian-reversed in + // finalize(). + bool _endian_reversed; + }; // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA CData : public CycleData { diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index 95fe3c012d..412bf07a64 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -17,6 +17,7 @@ alphaTestAttrib.I alphaTestAttrib.h \ ambientLight.I ambientLight.h \ antialiasAttrib.I antialiasAttrib.h \ + attribNodeRegistry.I attribNodeRegistry.h \ attribSlots.h attribSlots.I \ audioVolumeAttrib.I audioVolumeAttrib.h \ auxSceneData.I auxSceneData.h \ @@ -127,6 +128,7 @@ alphaTestAttrib.cxx \ ambientLight.cxx \ antialiasAttrib.cxx \ + attribNodeRegistry.cxx \ attribSlots.cxx \ audioVolumeAttrib.cxx \ auxSceneData.cxx \ @@ -232,6 +234,7 @@ alphaTestAttrib.I alphaTestAttrib.h \ ambientLight.I ambientLight.h \ antialiasAttrib.I antialiasAttrib.h \ + attribNodeRegistry.I attribNodeRegistry.h \ attribSlots.h attribSlots.I \ audioVolumeAttrib.I audioVolumeAttrib.h \ auxSceneData.I auxSceneData.h \ diff --git a/panda/src/pgraph/attribNodeRegistry.I b/panda/src/pgraph/attribNodeRegistry.I new file mode 100644 index 0000000000..fe97495e15 --- /dev/null +++ b/panda/src/pgraph/attribNodeRegistry.I @@ -0,0 +1,69 @@ +// Filename: attribNodeRegistry.I +// Created by: drose (07Jul07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::get_global_ptr +// Access: Published, Static +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AttribNodeRegistry *AttribNodeRegistry:: +get_global_ptr() { + if (_global_ptr == (AttribNodeRegistry *)NULL) { + make_global_ptr(); + } + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::Entry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AttribNodeRegistry::Entry:: +Entry(const NodePath &node) : + _type(node.node()->get_type()), + _name(node.get_name()), + _node(node) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::Entry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AttribNodeRegistry::Entry:: +Entry(TypeHandle type, const string &name) : + _type(type), + _name(name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::Entry::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool AttribNodeRegistry::Entry:: +operator < (const Entry &other) const { + if (_type != other._type) { + return _type < other._type; + } + return _name < other._name; +} diff --git a/panda/src/pgraph/attribNodeRegistry.cxx b/panda/src/pgraph/attribNodeRegistry.cxx new file mode 100644 index 0000000000..fcf94fa6fe --- /dev/null +++ b/panda/src/pgraph/attribNodeRegistry.cxx @@ -0,0 +1,275 @@ +// Filename: attribNodeRegistry.cxx +// Created by: drose (07Jul07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "attribNodeRegistry.h" +#include "mutexHolder.h" + +AttribNodeRegistry * TVOLATILE AttribNodeRegistry::_global_ptr; + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +AttribNodeRegistry:: +AttribNodeRegistry() { +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::add_node +// Access: Published +// Description: Adds the indicated NodePath to the registry. The +// name and type of the node are noted at the time of +// this call; if the name changes later, it will not +// update the registry index. +// +// The NodePath must reference some kind of an attribute +// node, such as a LightNode or a PlaneNode. When bam +// files that reference an attribute node of the same +// type and the same name are loaded, they will quietly +// be redirected to reference this NodePath. +//////////////////////////////////////////////////////////////////// +void AttribNodeRegistry:: +add_node(const NodePath &attrib_node) { + nassertv(!attrib_node.is_empty()); + MutexHolder holder(_lock); + _entries.insert(Entry(attrib_node)); +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::remove_node +// Access: Published +// Description: Removes the indicated NodePath from the registry. +// The name of the node must not have changed since the +// matching call to add_node(), or it will not be +// successfully removed. +// +// Returns true if the NodePath is found and removed, +// false if it is not found (for instance, because the +// name has changed). +//////////////////////////////////////////////////////////////////// +bool AttribNodeRegistry:: +remove_node(const NodePath &attrib_node) { + nassertr(!attrib_node.is_empty(), false); + MutexHolder holder(_lock); + Entries::iterator ei = _entries.find(Entry(attrib_node)); + if (ei != _entries.end()) { + _entries.erase(ei); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::lookup_node +// Access: Published +// Description: Looks up the indicated NodePath in the registry. If +// there is a node already in the registry with the +// matching name and type, returns that NodePath +// instead; otherwise, returns the original NodePath. +//////////////////////////////////////////////////////////////////// +NodePath AttribNodeRegistry:: +lookup_node(const NodePath &orig_node) const { + nassertr(!orig_node.is_empty(), orig_node); + + MutexHolder holder(_lock); + Entries::const_iterator ei = _entries.find(Entry(orig_node)); + if (ei != _entries.end()) { + return (*ei)._node; + } + return orig_node; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::get_num_nodes +// Access: Published +// Description: Returns the total number of nodes in the registry. +//////////////////////////////////////////////////////////////////// +int AttribNodeRegistry:: +get_num_nodes() const { + MutexHolder holder(_lock); + return _entries.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::get_node +// Access: Published +// Description: Returns the nth NodePath recorded in the registry. +//////////////////////////////////////////////////////////////////// +NodePath AttribNodeRegistry:: +get_node(int n) const { + MutexHolder holder(_lock); + nassertr(n >= 0 && n < (int)_entries.size(), NodePath()); + return _entries[n]._node; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::get_node_type +// Access: Published +// Description: Returns the type of the nth node, as recorded in the +// registry. +//////////////////////////////////////////////////////////////////// +TypeHandle AttribNodeRegistry:: +get_node_type(int n) const { + MutexHolder holder(_lock); + nassertr(n >= 0 && n < (int)_entries.size(), TypeHandle::none()); + return _entries[n]._type; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::get_node_name +// Access: Published +// Description: Returns the name of the nth node, as recorded in the +// registry. This will be the node name as it was at +// the time the node was recorded; if the node has +// changed names since then, this will still return the +// original name. +//////////////////////////////////////////////////////////////////// +string AttribNodeRegistry:: +get_node_name(int n) const { + MutexHolder holder(_lock); + nassertr(n >= 0 && n < (int)_entries.size(), string()); + return _entries[n]._name; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::find_node +// Access: Published +// Description: Returns the index number of the indicated NodePath in +// the registry (assuming its name hasn't changed since +// it was recorded in the registry), or -1 if the +// NodePath cannot be found (for instance, because its +// name has changed). +//////////////////////////////////////////////////////////////////// +int AttribNodeRegistry:: +find_node(const NodePath &attrib_node) const { + nassertr(!attrib_node.is_empty(), -1); + MutexHolder holder(_lock); + Entries::const_iterator ei = _entries.find(Entry(attrib_node)); + if (ei != _entries.end()) { + return ei - _entries.begin(); + } + return -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::find_node +// Access: Published +// Description: Returns the index number of the node with the +// indicated type and name in the registry, or -1 if +// there is no such node in the registry. +//////////////////////////////////////////////////////////////////// +int AttribNodeRegistry:: +find_node(TypeHandle type, const string &name) const { + MutexHolder holder(_lock); + Entries::const_iterator ei = _entries.find(Entry(type, name)); + if (ei != _entries.end()) { + return ei - _entries.begin(); + } + return -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::remove_node +// Access: Published +// Description: Removes the nth node from the registry. +//////////////////////////////////////////////////////////////////// +void AttribNodeRegistry:: +remove_node(int n) { + MutexHolder holder(_lock); + nassertv(n >= 0 && n < (int)_entries.size()); + _entries.erase(_entries.begin() + n); +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::clear +// Access: Published +// Description: Removes all nodes from the registry. +//////////////////////////////////////////////////////////////////// +void AttribNodeRegistry:: +clear() { + MutexHolder holder(_lock); + _entries.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void AttribNodeRegistry:: +output(ostream &out) const { + MutexHolder holder(_lock); + + typedef pmap Counts; + Counts counts; + + Entries::const_iterator ei; + for (ei = _entries.begin(); ei != _entries.end(); ++ei) { + TypeHandle type = (*ei)._type; + Counts::iterator ci = counts.insert(Counts::value_type(type, 0)).first; + ++((*ci).second); + } + + out << _entries.size() << " entries"; + + if (!counts.empty()) { + Counts::iterator ci = counts.begin(); + out << " (" << (*ci).first << ":" << (*ci).second; + ++ci; + while (ci != counts.end()) { + out << ", " << (*ci).first << ":" << (*ci).second; + ++ci; + } + out << ")"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::write +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void AttribNodeRegistry:: +write(ostream &out) const { + MutexHolder holder(_lock); + + Entries::const_iterator ei; + for (ei = _entries.begin(); ei != _entries.end(); ++ei) { + const Entry &entry = (*ei); + out << entry._type << ", \"" << entry._name << "\": " << entry._node + << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AttribNodeRegistry::make_global_ptr +// Access: Private, Static +// Description: +//////////////////////////////////////////////////////////////////// +void AttribNodeRegistry:: +make_global_ptr() { + AttribNodeRegistry *ptr = new AttribNodeRegistry; + void *result = AtomicAdjust::compare_and_exchange_ptr + ((void * TVOLATILE &)_global_ptr, (void *)NULL, (void *)ptr); + if (result != NULL) { + // Someone else got there first. + delete ptr; + } + assert(_global_ptr != (AttribNodeRegistry *)NULL); +} diff --git a/panda/src/pgraph/attribNodeRegistry.h b/panda/src/pgraph/attribNodeRegistry.h new file mode 100644 index 0000000000..3f0ab27f9b --- /dev/null +++ b/panda/src/pgraph/attribNodeRegistry.h @@ -0,0 +1,91 @@ +// Filename: attribNodeRegistry.h +// Created by: drose (07Jul07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef ATTRIBNODEREGISTRY_H +#define ATTRIBNODEREGISTRY_H + +#include "pandabase.h" +#include "nodePath.h" +#include "ordered_vector.h" +#include "pmutex.h" + +//////////////////////////////////////////////////////////////////// +// Class : AttribNodeRegistry +// Description : This global object records NodePaths that are +// referenced by scene graph attribs, such as +// ClipPlaneAttribs and LightAttribs. +// +// Its primary purpose is to unify attribs that are +// loaded in from bam files. Attrib nodes are +// identified by name and type; when a bam file that +// contains references to some attrib nodes is loaded, +// those nodes are first looked up here in the +// AttribNodeRegistry. If there is a match (by name and +// node type), the identified node is used instead of +// the node referenced within the bam file itself. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA AttribNodeRegistry { +protected: + AttribNodeRegistry(); + +PUBLISHED: + void add_node(const NodePath &attrib_node); + bool remove_node(const NodePath &attrib_node); + NodePath lookup_node(const NodePath &orig_node) const; + + int get_num_nodes() const; + NodePath get_node(int n) const; + TypeHandle get_node_type(int n) const; + string get_node_name(int n) const; + + int find_node(const NodePath &attrib_node) const; + int find_node(TypeHandle type, const string &name) const; + void remove_node(int n); + void clear(); + + void output(ostream &out) const; + void write(ostream &out) const; + + INLINE static AttribNodeRegistry *get_global_ptr(); + +private: + static void make_global_ptr(); + + class Entry { + public: + INLINE Entry(const NodePath &node); + INLINE Entry(TypeHandle type, const string &name); + INLINE bool operator < (const Entry &other) const; + + TypeHandle _type; + string _name; + NodePath _node; + }; + + typedef ov_set Entries; + Entries _entries; + + Mutex _lock; + + static AttribNodeRegistry * TVOLATILE _global_ptr; +}; + +#include "attribNodeRegistry.I" + +#endif + diff --git a/panda/src/pgraph/clipPlaneAttrib.cxx b/panda/src/pgraph/clipPlaneAttrib.cxx index c0912124dc..a456309a01 100644 --- a/panda/src/pgraph/clipPlaneAttrib.cxx +++ b/panda/src/pgraph/clipPlaneAttrib.cxx @@ -25,6 +25,7 @@ #include "datagram.h" #include "datagramIterator.h" #include "config_pgraph.h" +#include "attribNodeRegistry.h" CPT(RenderAttrib) ClipPlaneAttrib::_empty_attrib; CPT(RenderAttrib) ClipPlaneAttrib::_all_off_attrib; @@ -937,7 +938,9 @@ write_datagram(BamWriter *manager, Datagram &dg) { for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) { NodePath plane = (*fi); - // Whoops, we don't have a way to write out a NodePath right now. + // Since we can't write out a NodePath, we write out just the + // plain PandaNode. The user can use the AttribNodeRegistry on + // re-read if there is any ambiguity that needs to be resolved. manager->write_pointer(dg, plane.node()); } @@ -961,28 +964,46 @@ write_datagram(BamWriter *manager, Datagram &dg) { int ClipPlaneAttrib:: complete_pointers(TypedWritable **p_list, BamReader *manager) { int pi = RenderAttrib::complete_pointers(p_list, manager); + AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr(); Planes::iterator ci = _off_planes.begin(); while (ci != _off_planes.end()) { PandaNode *node; DCAST_INTO_R(node, p_list[pi++], pi); NodePath np(node); - (*ci) = np; + (*ci) = areg->lookup_node(np); ++ci; } + _off_planes.sort(); ci = _on_planes.begin(); while (ci != _on_planes.end()) { PandaNode *node; DCAST_INTO_R(node, p_list[pi++], pi); NodePath np(node); - (*ci) = np; + (*ci) = areg->lookup_node(np); ++ci; } + _on_planes.sort(); return pi; } +//////////////////////////////////////////////////////////////////// +// Function: ClipPlaneAttrib::require_fully_complete +// Access: Public, Virtual +// Description: Some objects require all of their nested pointers to +// have been completed before the objects themselves can +// be completed. If this is the case, override this +// method to return true, and be careful with circular +// references (which would make the object unreadable +// from a bam file). +//////////////////////////////////////////////////////////////////// +bool ClipPlaneAttrib:: +require_fully_complete() const { + return true; +} + //////////////////////////////////////////////////////////////////// // Function: ClipPlaneAttrib::make_from_bam // Access: Protected, Static @@ -1026,19 +1047,19 @@ fillin(DatagramIterator &scan, BamReader *manager) { int num_off_planes = scan.get_uint16(); - // Push back a NULL pointer for each off Plane for now, until - // we get the actual list of pointers later in complete_pointers(). + // Push back an empty NodePath for each off Plane for now, until we + // get the actual list of pointers later in complete_pointers(). _off_planes.reserve(num_off_planes); int i; for (i = 0; i < num_off_planes; i++) { manager->read_pointer(scan); - _off_planes.push_back(NULL); + _off_planes.push_back(NodePath()); } int num_on_planes = scan.get_uint16(); _on_planes.reserve(num_on_planes); for (i = 0; i < num_on_planes; i++) { manager->read_pointer(scan); - _on_planes.push_back(NULL); + _on_planes.push_back(NodePath()); } } diff --git a/panda/src/pgraph/clipPlaneAttrib.h b/panda/src/pgraph/clipPlaneAttrib.h index ec37b5cdd8..8b7033f078 100644 --- a/panda/src/pgraph/clipPlaneAttrib.h +++ b/panda/src/pgraph/clipPlaneAttrib.h @@ -127,6 +127,7 @@ public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); virtual int complete_pointers(TypedWritable **plist, BamReader *manager); + virtual bool require_fully_complete() const; protected: static TypedWritable *make_from_bam(const FactoryParams ¶ms); diff --git a/panda/src/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index 2b73ec4c35..55040dcfae 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/panda/src/pgraph/lightAttrib.cxx @@ -27,6 +27,7 @@ #include "datagram.h" #include "datagramIterator.h" #include "config_pgraph.h" +#include "attribNodeRegistry.h" CPT(RenderAttrib) LightAttrib::_empty_attrib; CPT(RenderAttrib) LightAttrib::_all_off_attrib; @@ -916,7 +917,9 @@ write_datagram(BamWriter *manager, Datagram &dg) { for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) { NodePath light = (*fi); - // Whoops, we don't have a way to write out a NodePath right now. + // Since we can't write out a NodePath, we write out just the + // plain PandaNode. The user can use the AttribNodeRegistry on + // re-read if there is any ambiguity that needs to be resolved. manager->write_pointer(dg, light.node()); } @@ -940,28 +943,46 @@ write_datagram(BamWriter *manager, Datagram &dg) { int LightAttrib:: complete_pointers(TypedWritable **p_list, BamReader *manager) { int pi = RenderAttrib::complete_pointers(p_list, manager); + AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr(); Lights::iterator ci = _off_lights.begin(); while (ci != _off_lights.end()) { PandaNode *node; DCAST_INTO_R(node, p_list[pi++], pi); NodePath np(node); - (*ci) = np; + (*ci) = areg->lookup_node(np); ++ci; } + _off_lights.sort(); ci = _on_lights.begin(); while (ci != _on_lights.end()) { PandaNode *node; DCAST_INTO_R(node, p_list[pi++], pi); NodePath np(node); - (*ci) = np; + (*ci) = areg->lookup_node(np); ++ci; } + _on_lights.sort(); return pi; } +//////////////////////////////////////////////////////////////////// +// Function: LightAttrib::require_fully_complete +// Access: Public, Virtual +// Description: Some objects require all of their nested pointers to +// have been completed before the objects themselves can +// be completed. If this is the case, override this +// method to return true, and be careful with circular +// references (which would make the object unreadable +// from a bam file). +//////////////////////////////////////////////////////////////////// +bool LightAttrib:: +require_fully_complete() const { + return true; +} + //////////////////////////////////////////////////////////////////// // Function: LightAttrib::make_from_bam // Access: Protected, Static @@ -1005,19 +1026,19 @@ fillin(DatagramIterator &scan, BamReader *manager) { int num_off_lights = scan.get_uint16(); - // Push back a NULL pointer for each off Light for now, until - // we get the actual list of pointers later in complete_pointers(). + // Push back an empty NodePath for each off Light for now, until we + // get the actual list of pointers later in complete_pointers(). _off_lights.reserve(num_off_lights); int i; for (i = 0; i < num_off_lights; i++) { manager->read_pointer(scan); - _off_lights.push_back(NULL); + _off_lights.push_back(NodePath()); } int num_on_lights = scan.get_uint16(); _on_lights.reserve(num_on_lights); for (i = 0; i < num_on_lights; i++) { manager->read_pointer(scan); - _on_lights.push_back(NULL); + _on_lights.push_back(NodePath()); } } diff --git a/panda/src/pgraph/lightAttrib.h b/panda/src/pgraph/lightAttrib.h index 87de5d5ef7..eb9789405b 100644 --- a/panda/src/pgraph/lightAttrib.h +++ b/panda/src/pgraph/lightAttrib.h @@ -125,6 +125,7 @@ public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); virtual int complete_pointers(TypedWritable **plist, BamReader *manager); + virtual bool require_fully_complete() const; protected: static TypedWritable *make_from_bam(const FactoryParams ¶ms); diff --git a/panda/src/pgraph/pgraph_composite1.cxx b/panda/src/pgraph/pgraph_composite1.cxx index 7a0052ed6d..207ab8f2ce 100644 --- a/panda/src/pgraph/pgraph_composite1.cxx +++ b/panda/src/pgraph/pgraph_composite1.cxx @@ -1,6 +1,7 @@ #include "accumulatedAttribs.cxx" #include "ambientLight.cxx" #include "antialiasAttrib.cxx" +#include "attribNodeRegistry.cxx" #include "audioVolumeAttrib.cxx" #include "auxSceneData.cxx" #include "attribSlots.cxx" diff --git a/panda/src/putil/bamReader.I b/panda/src/putil/bamReader.I index 44365e4853..6b6eb7de61 100644 --- a/panda/src/putil/bamReader.I +++ b/panda/src/putil/bamReader.I @@ -173,6 +173,15 @@ get_datagram(Datagram &datagram) { return _source->get_datagram(datagram); } +//////////////////////////////////////////////////////////////////// +// Function: BamReader::AuxData::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE BamReader::AuxData:: +AuxData() { +} + //////////////////////////////////////////////////////////////////// // Function: parse_params // Access: Private, Static diff --git a/panda/src/putil/bamReader.cxx b/panda/src/putil/bamReader.cxx index a1dd33341e..c2e62d7949 100644 --- a/panda/src/putil/bamReader.cxx +++ b/panda/src/putil/bamReader.cxx @@ -135,20 +135,44 @@ init() { //////////////////////////////////////////////////////////////////// // Function: BamReader::set_aux_data // Access: Public -// Description: Associates an arbitrary pointer to the bam reader -// with the indicated name. The name is an arbitrary -// user-defined key to access the data later. This data -// is typically queried by objects reading themselves -// from the bam file; this is intended to provide some -// context information to objects in the bam file. Set -// the aux data to NULL to remove it. +// Description: Associates an arbitrary block of data with the +// indicated object (or NULL), and the indicated name. +// +// This is intended to provide a place for temporary +// storage for objects reading themselves from the bam +// file. To use it, inherit from BamReader::AuxData and +// store whatever data you like there. Then associate +// your AuxData with the object as it is being read with +// set_aux_data(). You may later set the aux data to +// NULL to remove it; or it will automatically be +// removed (and deleted) after finalize() is called for +// the object in question. +// +// If the TypedWritable pointer is NULL, the the aux +// data is stored globally for the BamReader in general. +// This pointer is available to any bam objects, and +// will not be automatically removed until the BamReader +// itself destructs. +// +// In either case, the name is just an arbitrary +// user-defined key. If there is already a data pointer +// stored for the obj/name pair, that data pointer will +// be replaced (and deleted). //////////////////////////////////////////////////////////////////// void BamReader:: -set_aux_data(const string &name, void *data) { +set_aux_data(TypedWritable *obj, const string &name, BamReader::AuxData *data) { if (data == (void *)NULL) { - _aux_data.erase(name); + AuxDataTable::iterator ti = _aux_data.find(obj); + if (ti != _aux_data.end()) { + AuxDataNames &names = (*ti).second; + names.erase(name); + if (names.empty()) { + _aux_data.erase(ti); + } + } + } else { - _aux_data[name] = data; + _aux_data[obj][name] = data; } } @@ -157,15 +181,19 @@ set_aux_data(const string &name, void *data) { // Access: Public // Description: Returns the pointer previously associated with the // bam reader by a previous call to set_aux_data(), or -// NULL if the data with the indicated key has not been -// set. +// NULL if data with the indicated key has not been set. //////////////////////////////////////////////////////////////////// -void *BamReader:: -get_aux_data(const string &name) const { - AuxData::const_iterator di = _aux_data.find(name); - if (di != _aux_data.end()) { - return (*di).second; +BamReader::AuxData *BamReader:: +get_aux_data(TypedWritable *obj, const string &name) const { + AuxDataTable::const_iterator ti = _aux_data.find(obj); + if (ti != _aux_data.end()) { + const AuxDataNames &names = (*ti).second; + AuxDataNames::const_iterator ni = names.find(name); + if (ni != names.end()) { + return (*ni).second; + } } + return NULL; } @@ -272,8 +300,31 @@ resolve() { do { all_completed = true; any_completed_this_pass = false; - - // Walk through all the objects that still have outstanding pointers. + + // First do the PipelineCycler objects. + CyclerPointers::iterator ci; + ci = _cycler_pointers.begin(); + while (ci != _cycler_pointers.end()) { + PipelineCyclerBase *cycler = (*ci).first; + const vector_int &pointer_ids = (*ci).second; + + if (resolve_cycler_pointers(cycler, pointer_ids)) { + // Now remove this cycler from the list of things that need + // completion. We have to be a bit careful when deleting things + // from the STL container while we are traversing it. + CyclerPointers::iterator old = ci; + ++ci; + _cycler_pointers.erase(old); + any_completed_this_pass = true; + + } else { + // Couldn't complete this cycler yet; it'll wait for next time. + ++ci; + all_completed = false; + } + } + + // Now do the main objects. ObjectPointers::iterator oi; oi = _object_pointers.begin(); while (oi != _object_pointers.end()) { @@ -320,32 +371,9 @@ resolve() { all_completed = false; } } + } while (!all_completed && any_completed_this_pass); - // Also do the PipelineCycler objects. We only need to try these - // once, since they don't depend on each other. - - CyclerPointers::iterator ci; - ci = _cycler_pointers.begin(); - while (ci != _cycler_pointers.end()) { - PipelineCyclerBase *cycler = (*ci).first; - const vector_int &pointer_ids = (*ci).second; - - if (resolve_cycler_pointers(cycler, pointer_ids)) { - // Now remove this cycler from the list of things that need - // completion. We have to be a bit careful when deleting things - // from the STL container while we are traversing it. - CyclerPointers::iterator old = ci; - ++ci; - _cycler_pointers.erase(old); - - } else { - // Couldn't complete this cycler yet; it'll wait for next time. - ++ci; - all_completed = false; - } - } - if (all_completed) { finalize(); } else { @@ -396,6 +424,12 @@ change_pointer(const TypedWritable *orig_pointer, const TypedWritable *new_point return false; } + if (bam_cat.is_spam()) { + bam_cat.spam() + << "change_pointer(" << (void *)orig_pointer << ", " + << (void *)new_pointer << ") (" << new_pointer->get_type() << ")\n"; + } + const vector_int &old_refs = (*ci).second; vector_int &new_refs = _created_objs_by_pointer[new_pointer]; @@ -638,10 +672,14 @@ read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler, //////////////////////////////////////////////////////////////////// void BamReader:: register_finalize(TypedWritable *whom) { - if (whom == TypedWritable::Null) { - bam_cat.error() << "Can't register a null pointer to finalize!" << endl; - return; + nassertv(whom != (TypedWritable *)NULL); + + if (bam_cat.is_spam()) { + bam_cat.spam() + << "register_finalize(" << (void *)whom << ") (" << whom->get_type() + << ")\n"; } + _finalize_list.insert(whom); } @@ -698,6 +736,11 @@ finalize_now(TypedWritable *whom) { Finalize::iterator fi = _finalize_list.find(whom); if (fi != _finalize_list.end()) { _finalize_list.erase(fi); + if (bam_cat.is_spam()) { + bam_cat.spam() + << "finalizing " << (void *)whom << " (" << whom->get_type() + << ")\n"; + } whom->finalize(this); } } @@ -994,7 +1037,7 @@ p_read_object() { } else { if (bam_cat.is_spam()) { bam_cat.spam() - << "Read a " << object->get_type() << "\n"; + << "Read a " << object->get_type() << ": " << (void *)object << "\n"; } } } @@ -1018,12 +1061,17 @@ resolve_object_pointers(TypedWritable *object, const vector_int &pointer_ids) { // given object until we have *all* outstanding pointers for // that object. bool is_complete = true; + + // Some objects further require all of their nested objects to have + // been completed (i.e. complete_pointers has been called on each + // nested object) before they can themselves be completed. + bool require_fully_complete = object->require_fully_complete(); + vector_typedWritable references; - + vector_int::const_iterator pi; for (pi = pointer_ids.begin(); pi != pointer_ids.end() && is_complete; ++pi) { int child_id = (*pi); - if (child_id == 0) { // A NULL pointer is a NULL pointer. references.push_back((TypedWritable *)NULL); @@ -1042,8 +1090,15 @@ resolve_object_pointers(TypedWritable *object, const vector_int &pointer_ids) { is_complete = false; } else { - // Yes, it's ready. - references.push_back(child_obj._ptr); + if (require_fully_complete && + _object_pointers.find(child_id) != _object_pointers.end()) { + // It's not yet complete itself. + is_complete = false; + + } else { + // Yes, it's ready. + references.push_back(child_obj._ptr); + } } } } @@ -1051,6 +1106,14 @@ resolve_object_pointers(TypedWritable *object, const vector_int &pointer_ids) { if (is_complete) { // Okay, here's the complete list of pointers for you! + nassertr(references.size() == pointer_ids.size(), false); + + if (bam_cat.is_spam()) { + bam_cat.spam() + << "complete_pointers for " << (void *)object + << " (" << object->get_type() << "), " << references.size() + << " pointers.\n"; + } int num_completed = object->complete_pointers(&references[0], this); if (num_completed != (int)references.size()) { bam_cat.warning() @@ -1058,6 +1121,13 @@ resolve_object_pointers(TypedWritable *object, const vector_int &pointer_ids) { << " of " << references.size() << " pointers.\n"; } return true; + + } else { + if (bam_cat.is_spam()) { + bam_cat.spam() + << "not ready: complete_pointers for " << (void *)object + << " (" << object->get_type() << ")\n"; + } } return false; @@ -1115,6 +1185,11 @@ resolve_cycler_pointers(PipelineCyclerBase *cycler, if (is_complete) { // Okay, here's the complete list of pointers for you! CycleData *cdata = cycler->write(Thread::get_current_thread()); + if (bam_cat.is_spam()) { + bam_cat.spam() + << "complete_pointers for CycleData object " << (void *)cdata + << "\n"; + } int num_completed = cdata->complete_pointers(&references[0], this); cycler->release_write(cdata); if (num_completed != (int)references.size()) { @@ -1147,8 +1222,42 @@ finalize() { TypedWritable *object = (*fi); nassertv(object != (TypedWritable *)NULL); _finalize_list.erase(fi); + if (bam_cat.is_spam()) { + bam_cat.spam() + << "finalizing " << (void *)object << " (" << object->get_type() + << ")\n"; + } object->finalize(this); - + _aux_data.erase(object); fi = _finalize_list.begin(); } + + // Now clear the aux data of all objects, except the NULL object. + if (!_aux_data.empty()) { + AuxDataTable::iterator ti = _aux_data.find((TypedWritable *)NULL); + + if (ti != _aux_data.end()) { + if (_aux_data.size() > 1) { + // Move the NULL data to the new table; remove the rest. + AuxDataTable new_aux_data; + AuxDataTable::iterator nti = + new_aux_data.insert(AuxDataTable::value_type(NULL, AuxDataNames())).first; + (*nti).second.swap((*ti).second); + _aux_data.swap(new_aux_data); + } + } else { + // There's no NULL data; clear the whole table. + _aux_data.clear(); + } + } } + +//////////////////////////////////////////////////////////////////// +// Function: BamReader::AuxData::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +BamReader::AuxData:: +~AuxData() { +} + diff --git a/panda/src/putil/bamReader.h b/panda/src/putil/bamReader.h index 5dbd0090e5..bd617b2c68 100644 --- a/panda/src/putil/bamReader.h +++ b/panda/src/putil/bamReader.h @@ -98,8 +98,10 @@ public: bool init(); - void set_aux_data(const string &name, void *data); - void *get_aux_data(const string &name) const; + class AuxData; + void set_aux_data(TypedWritable *obj, const string &name, AuxData *data); + AuxData *get_aux_data(TypedWritable *obj, const string &name) const; + INLINE const Filename &get_filename() const; TypedWritable *read_object(); @@ -115,10 +117,6 @@ public: INLINE int get_current_major_ver() const; INLINE int get_current_minor_ver() const; - // This special TypeHandle is written to the bam file to indicate an - // object id is no longer needed. - static TypeHandle _remove_flag; - public: // Functions to support classes that read themselves from the Bam. @@ -161,6 +159,20 @@ private: INLINE bool get_datagram(Datagram &datagram); +public: + // This special TypeHandle is written to the bam file to indicate an + // object id is no longer needed. + static TypeHandle _remove_flag; + + // Inherit from this class to piggyback additional temporary data on + // the bamReader (via set_aux_data() and get_aux_data()) for any + // particular objects during the bam reading process. + class AuxData : public ReferenceCount { + public: + INLINE AuxData(); + virtual ~AuxData(); + }; + private: static WritableFactory *_factory; @@ -234,8 +246,9 @@ private: static NewTypes _new_types; // This is used in support of set_aux_data() and get_aux_data(). - typedef phash_map AuxData; - AuxData _aux_data; + typedef pmap AuxDataNames; + typedef phash_map AuxDataTable; + AuxDataTable _aux_data; int _file_major, _file_minor; BamEndian _file_endian; diff --git a/panda/src/putil/typedWritable.cxx b/panda/src/putil/typedWritable.cxx index 7e1421f9ff..1772a0b243 100644 --- a/panda/src/putil/typedWritable.cxx +++ b/panda/src/putil/typedWritable.cxx @@ -85,7 +85,21 @@ int TypedWritable:: complete_pointers(TypedWritable **, BamReader *) { return 0; } - + +//////////////////////////////////////////////////////////////////// +// Function: TypedWritable::require_fully_complete +// Access: Public, Virtual +// Description: Some objects require all of their nested pointers to +// have been completed before the objects themselves can +// be completed. If this is the case, override this +// method to return true, and be careful with circular +// references (which would make the object unreadable +// from a bam file). +//////////////////////////////////////////////////////////////////// +bool TypedWritable:: +require_fully_complete() const { + return false; +} //////////////////////////////////////////////////////////////////// // Function: TypedWritable::finalize diff --git a/panda/src/putil/typedWritable.h b/panda/src/putil/typedWritable.h index be6e3d2d33..eaa5b58db8 100644 --- a/panda/src/putil/typedWritable.h +++ b/panda/src/putil/typedWritable.h @@ -50,6 +50,7 @@ public: virtual void write_datagram(BamWriter *, Datagram &); virtual int complete_pointers(TypedWritable **p_list, BamReader *manager); + virtual bool require_fully_complete() const; virtual void finalize(BamReader *manager);