Hi Erwin,
Thanks for the prompt response.
But how far down the rabbit hole to go, asked Alice!
Ok, so a set of criteria:
1) No RTTI
2) No STL
3) No Boost
4) Member serialization functions, not globals
5) 32/64 bit support
6) little/big endian support
7) Allows for custom archives, so as allowing for different file formats, or to serialize to/from clipboard instead of a file etc.
Versioning support / support forward and backward compatibility
You are a Task Master, Sir!
Taking these in turn:
1) No RTTI
As you can see from the BasicDemoPersist program, it does not use rtti to serialize the btCollisionObject hierarchy. Runtime type information (whether provided by rtti or some other mechanism) is necessary as far as I can see in order to reconnect/reinstantiate via base class pointers. In order to gain (non-rtti) runtime type information, one must therefore make some kind of assumption about the class. At the moment the serialization code within BasicDemoPersist assumes that an ‘int getInternalType()’ member function is present. This seems to work for the btCollisionObject class hierarchy. However, I have just started to look at the code for the btTypedConstraint (derived from btTypedObject); a separate ‘int getObjectType()’ function is used.
Suggestion
Derive btCollisionObject (and all other class hierarchies, if there are any others that Ive not found yet) from btTypedObject, so that you have a single universal system for providing type information. A good idea also would be to provide a single ‘typeinfo enum’ for all of the classes, to avoid uniqueid duplication, eg:
Currently you have:
enum btTypedConstraintType
{
POINT2POINT_CONSTRAINT_TYPE=MAX_CONTACT_MANIFOLD_TYPE+1,
HINGE_CONSTRAINT_TYPE,
CONETWIST_CONSTRAINT_TYPE,
D6_CONSTRAINT_TYPE,
SLIDER_CONSTRAINT_TYPE,
CONTACT_CONSTRAINT_TYPE
};
and
enum CollisionObjectTypes
{
CO_COLLISION_OBJECT =1,
CO_RIGID_BODY,
///CO_GHOST_OBJECT keeps track of all objects overlapping its AABB and that pass its collision filter
///It is useful for collision sensors, explosion objects, character controller etc.
CO_GHOST_OBJECT,
CO_SOFT_BODY,
CO_HF_FLUID
};
and
enum BroadphaseNativeTypes
{
// polyhedral convex shapes
BOX_SHAPE_PROXYTYPE,
TRIANGLE_SHAPE_PROXYTYPE,
TETRAHEDRAL_SHAPE_PROXYTYPE,
CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE,
CONVEX_HULL_SHAPE_PROXYTYPE,
CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE,
CUSTOM_POLYHEDRAL_SHAPE_TYPE,
//implicit convex shapes
IMPLICIT_CONVEX_SHAPES_START_HERE,
...
You could have:
Enum ObjectTypes
{
CO_COLLISION_OBJECT =1,
CO_RIGID_BODY =2,
CO_GHOST_OBJECT =3,
CO_SOFT_BODY =4,
CO_HF_FLUID =5,
POINT2POINT_CONSTRAINT_TYPE=6,
HINGE_CONSTRAINT_TYPE =7,
CONETWIST_CONSTRAINT_TYPE =8,
D6_CONSTRAINT_TYPE =9,
SLIDER_CONSTRAINT_TYPE =10,
CONTACT_CONSTRAINT_TYPE =11
BOX_SHAPE_PROXYTYPE =12,
... plus all other types listed here, with explicit, non-reorderable identifiers
};
By making type identifiers unique and explicit, this goes some way to satisfying criteria
above – forward and backward compatibility.
If rtti is not used, mechanism of providing a unique class identifier is needed. At the moment, as far as I can see, Bullet does not provide this. For example,
TETRAHEDRAL_SHAPE_PROXYTYPE
and CO_RIGID_BODY
both currently evaluate to 2.
What do you think?
2) No STL
As I understand it, this means not using any of the std:: classes or functionality. To be honest, I think you are missing out on a good thing. However, that said, I believe the serialization code only uses the std::map structure, and the for_each() function. These are easy enough to implement ‘bullet-bespoke’ (see “Algorithms”, R. Sedgewick (1988)).
3) No Boost
Again, missing out on a good thing in my opinion, since, like STL, the code is tested on many different platforms, and as far as I am aware is completely open-source.
That said however, the serialization code provided has no dependency on Boost; it is based upon the Boost serialization framework, but does not use it. Ie the problem is NULL and
void (sorry for the pun!)
4) Member serialization functions, not globals
This is not really an issue. The global functions could be made members. It is also possible to provide either-or (member or global), and for the serialization code to deduce at compile time which is provided for that particular class, and use the appropriate one (see the Chooser<>). This has the advantage non-intrusion if necessary.
5) 32/64 bit support
My knowledge of this issue is somewhat scant Im afraid. Could you enlighten me, or suggest a good source (book or url) please. If it simply means that, for example the btScalar type will be declared as a float or a double depending upon which architecture is used, I don’t really see this as a big problem – the system will work with all primitive types.
If you mean that, depending upon the architecture (32 or 64 bit) different primitive types will have different sizeof(type)s, eg is sizeof(int) on a 32 bit machine the same as on a 64 bit?
Suggestion
Include in the archive whether it is a 64 or 32 bit archive. That way the correct sizes for the data types can be assumed. But how do you interrogate the system to find out whether the system is 64 or 32 bit? Is there some kind of universally agreed function? Or do you just do a sizeof(int), and if it == 4, => 32bit ; sizeof(int) == 8 => 64 bit ??
6) little/big endian support
Again, little knowledge of this Im afraid. Could a good source be pointed out please. At a superficial level I understand this to determine the bit ordering of the types, so an int stored in big-endian fashion will have its most-significant bit at the other end of the block of memory to a little-endian implementation. Is this correct?
Suggestion
Again, the ‘endian-ness’ of the archive could be stored within it. But again, like the ‘bit-ness’, how do you gather this information (platform-independently) from the system? Is there a magic function?
7) Allows for custom archives
As I see it this is a matter of deriving from the Archive class different implementation, and reading/writing the data accordingly.
Again, the type of the archive could be stored within it, and a check be done to make sure that the correct archive class type is being used to read it.
Versioning support / support forward and backward compatibility
Do you version at the whole archive level, or at the individual class level? Either way, this means writing out the version number to the archive (either once for the whole archive, or once for each class instance). This information is read in and provided as a variable to the serialization function. The serialization function then modifies its loading behaviour depending upon the version number.
Thinking about it, an overall archive version number could be stored within the archive. This could be used to alter the behaviour of the archive system itself according to its value (ie so you can version the archive system itself). Then, if necessary, each serialization function could implement its own versioning (by reading in its own, class-level version number). This would then cater for both classes that have different versions, but also have the efficiency of not making it a prerequisite, and hence not serializing a version number for each class instance if it is superfluous.
Problems with Glut
Indeed, re the window-closing issue: I have implemented a non-glut OpenGL window and do not get these issues; the problem seems to be with glut. The bigger problem with glut is regarding checking for memory leaks - because the glutmain function does not return, this proves to be an issue when debugging for them. Is there a way around this? By the way, I also heard that the licence for it is not strictly open-source – a guy called Mark Kilgard retains it (see
http://www.opengl.org/resources/libraries/glut/).
I’m afraid there are quite a few questions and suggestions there. However, with this additional information I feel that the serialization system could be hammer into something shipshape.
Chris Simson