The PhysX serialization system (Serialization) is extendable to custom types. If an application were to require a new joint type, for example, the serialization system could be extended to add support for serialization of that new joint type.
The following document contains some recipes and example code that show how PhysX serialization may be extended to custom types. It doesn't cover all aspects of the extension mechanisms. It is therefore advisable to look into the following implementation example for more details:
Both binary and RepX serialization can be extended for custom types. To prepare the custom type for serialization it must first inherit from PxBase. This allows instances of the custom type to be added to a PxCollection, which is a pre-requisite for serialization. The core serialization functionality needs to be provided by implementing the PxSerializer interface. The template PxSerializerDefaultAdapter provides a default implementation and can be specialized for the custom type as required. In order to support RepX serialization an additional PxRepXSerializer interface needs to be implemented. RepX serialization relies on automatic code generation using clang. Scripts to run the code generation for the examples can be found in (Tools/PhysXMetaDataGenerator).
Serialization and deserialization of a custom class can be achieved with the following steps:
For pointer members the following needs to be done (Note that reference members are currently not supported):
Note
In checked builds (PX_CHECKED defined as 1) metadata definitions are verified against serialized data. If metadata definitions are missing warnings are output on the error stream during re-targeting (PxBinaryConverter::convert). To avoid false warnings, all unused memory in custom serialized class instances should be marked with a 0xcd pattern. This can be done with Cm::markSerializedMem from CmUtils.h.
Note
The memory of a deserialized class instance should not be deallocated. The memory is embedded in the memory buffer containing the serialized data. The flag PxBaseFlag::eOWNS_MEMORY can used to decide whether the object memory needs be deallocated or not.
Example for a custom class:
#include "extensions/PxSerialization.h"
#include "common/PxTypeInfo.h"
#include "common/PxMetaData.h"
#include "common/PxSerializer.h"
#include "common/PxSerialFramework.h
using namespace physx;
const PxType customClassType = PxConcreteType::eFIRST_USER_EXTENSION;
PX_DEFINE_TYPEINFO(CustomClass, customClassType);
class CustomClass : public PxBase
{
friend class PxSerializerDefaultAdapter<CustomClass>;
public:
// constructor setting up PxBase object
CustomClass()
: PxBase(customClassType, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
{}
// constructor called on deserialization
CustomClass(PxBaseFlags baseFlags) : PxBase(baseFlags) {}
virtual ~CustomClass() {}
//PxBase
virtual const char* getConcreteTypeName() const { return "CustomClass"; }
virtual bool isKindOf(const char* name) const
{
return !strcmp("CustomClass", name) || PxBase::isKindOf(name);
}
//~PxBase
//PxSerializationRegistry::registerBinaryMetaDataCallback
static void getBinaryMetaData(PxOutputStream& stream)
{
PX_DEF_BIN_METADATA_VCLASS(stream, CustomClass)
PX_DEF_BIN_METADATA_BASE_CLASS(stream, CustomClass, PxBase)
PX_DEF_BIN_METADATA_ITEM(stream, CustomClass, PxRigidDynamic, mActor,
PxMetaDataFlag::ePTR)
PX_DEF_BIN_METADATA_ITEM(stream, CustomClass, char, mBuf, PxMetaDataFlag::ePTR)
PX_DEF_BIN_METADATA_ITEM(stream, CustomClass, PxU32, mSize, 0)
PX_DEF_BIN_METADATA_EXTRA_ITEMS(stream, CustomClass, char, mBuf, mSize, 0, 0)
}
//~PxSerializationRegistry::registerBinaryMetaDataCallback
private:
PxRigidDynamic* mActor; //add in requires
char* mBuf; //extra data
PxU32 mSize; //size of mBuf
};
//PxSerializerDefaultAdapter
template<>
void PxSerializerDefaultAdapter<CustomClass>::requires(PxBase& obj,
PxProcessPxBaseCallback& c) const
{
CustomClass* custom = obj.is<CustomClass>();
PX_ASSERT(custom);
c.process(*custom->mActor);
}
template<>
void PxSerializerDefaultAdapter<CustomClass>::registerReferences(PxBase& obj,
PxSerializationContext& s) const
{
CustomClass* custom = obj.is<CustomClass>();
PX_ASSERT(custom);
s.registerReference(obj, PX_SERIAL_REF_KIND_PXBASE, size_t(&obj));
s.registerReference(*custom->mActor, PX_SERIAL_REF_KIND_PXBASE, size_t(custom->mActor));
}
template<>
void PxSerializerDefaultAdapter<CustomClass>::exportExtraData(PxBase& obj,
PxSerializationContext& s) const
{
CustomClass* custom = obj.is<CustomClass>();
PX_ASSERT(custom);
s.alignData(PX_SERIAL_ALIGN);
s.writeData(custom->mBuf, custom->mSize);
}
template<>
PxBase* PxSerializerDefaultAdapter<CustomClass>::createObject(PxU8*& address,
PxDeserializationContext& context)
const
{
CustomClass* custom = new (address) CustomClass(PxBaseFlag::eIS_RELEASABLE);
address += sizeof(CustomClass);
// resolve references
context.translatePtr(custom->mActor);
// import extra data
custom->mBuf = context.readExtraData<char*, PX_SERIAL_ALIGN>();
// return deserialized object
return custom;
}
//~PxSerializerDefaultAdapter
void registerCustomClassBinarySerializer(PxSerializationRegistry& registry)
{
registry.registerSerializer(customClassType, PX_NEW_SERIALIZER_ADAPTER(CustomClass));
registry.registerBinaryMetaDataCallback(CustomClass::getBinaryMetaData);
}
void unregisterCustomClassBinarySerializer(PxSerializationRegistry& registry)
{
PX_DELETE_SERIALIZER_ADAPTER(registry.unregisterSerializer(customClassType));
}
Serialization and deserialization of a custom class can be achieved with the following steps:
Example for a custom class:
#include "SnRepXSerializerImpl.h"
const PxType customClassType = PxConcreteType::eFIRST_USER_EXTENSION;
PX_DEFINE_TYPEINFO(CustomClass, customClassType);
struct CustomClassRepXSerializer : public RepXSerializerImpl<CustomClass>
{
CustomClassRepXSerializer(PxAllocatorCallback& inCallback)
: RepXSerializerImpl<CustomClass>(inCallback)
{}
virtual PxRepXObject fileToObject(XmlReader& inReader, XmlMemoryAllocator& inAllocator,
PxRepXInstantiationArgs& inArgs, PxCollection* inCollection)
{
// factory for CustomClass instance provided by application
CustomClass* object = createCustomClass();
// when using the PhysX API metadata system readAllProperties(...) can be used to read
// all properties automatically
readAllProperties(inArgs, inReader, object, inAllocator, *inCollection);
return PxCreateRepXObject(object);
}
virtual void objectToFileImpl(const CustomClass* obj, PxCollection* inCollection,
XmlWriter& inWriter, MemoryBuffer& inTempBuffer,
PxRepXInstantiationArgs&)
{
// when using the PhysX API metadata system writeAllProperties(...) can be used to save
// all properties automatically
writeAllProperties(obj, inWriter, inTempBuffer, *inCollection);
}
// this can return NULL if fileToObject(...) is overwritten with a custom implementation.
virtual CustomClass* allocateObject(PxRepXInstantiationArgs&) { return NULL; }
};
void registerCustomClassRepXSerializer(PxSerializationRegistry& registry)
{
registry.registerSerializer(customClassType,
PX_NEW_SERIALIZER_ADAPTER(CustomClass));
registry.registerRepXSerializer(customClassType,
PX_NEW_REPX_SERIALIZER<CustomClassRepXSerializer>));
}
void unregisterCustomClassRepXSerializer(PxSerializationRegistry& registry)
{
PX_DELETE_SERIALIZER_ADAPTER(registry.unregisterSerializer(customClassType));
PX_DELETE_REPX_SERIALIZER(registry.unregisterRepXSerializer(customClassType));
}
Note
Implementing a PxRepXSerializer is currently not practical without including the internal PhysXExtension header "SnRepXSerializerImpl.h".
This system produces a set of objects that are analogues of the interfaces and of descriptors in the PhysX system, all based on the public interface. The generator heuristically finds functions that start with get/set and, through a series of cascading rules, combines those into several types of properties.
Currently the generator supports the following property types:
In order to make use of the generator the following files need to be created with the following recipe:
CustomTypeExtensionAPI.h
runClang_[windows|osx|linux].[bat|sh] (e.g. runClang_windows.bat)
Set definition folder for these autogenerated files and set the source file in here.
Specify the filename of autogenerated files. Then it will generate the following files:
include/CustomTypeAutoGeneratedMetaDataObjectNames.h
include/CustomTypeAutoGeneratedMetaDataObjects.h
src/CustomTypeAutoGeneratedMetaDataObjects.cpp
CustomTypeMetaDataObjects.h
CustomTypeMetaDataObjects.cpp
PxVehicle serialization is a useful example. With Source/PhysXVehicle as the root folder the structure of the files is as follows:
src/PhysXMetaData/include/PxVehicleMetaDataObjects.h
src/PhysXMetaData/src/PxVehicleMetaDataObjects.cpp
../../Tools/PhysXMetaDataGenerator/PxVehicleExtensionAPI.h
../../Tools/PhysXMetaDataGenerator/generateMetaData.py
Running the script will auto-generate the following files:
src/PhysXMetaData/include/PxVehicleAutoGeneratedMetaDataObjectNames.h
src/PhysXMetaData/include/PxVehicleAutoGeneratedMetaDataObjects.h
src/PhysXMetaData/src/PxVehicleAutoGeneratedMetaDataObjects.cpp
Note
The properties defined in PxVehicleAutoGeneratedMetaDataObjects.h are written to the RepX file automatically if PxVehicleMetaDataObjects.h is included for the custom RepX serializer.