601 lines
13 KiB
C++
601 lines
13 KiB
C++
/*
|
|
Bullet Continuous Collision Detection and Physics Library
|
|
Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.org
|
|
|
|
This software is provided 'as-is', without any express or implied warranty.
|
|
In no event will the authors be held liable for any damages arising from the use of this software.
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it freely,
|
|
subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
*/
|
|
|
|
#ifndef B3_SERIALIZER_H
|
|
#define B3_SERIALIZER_H
|
|
|
|
#include "Bullet3Common/b3Scalar.h" // has definitions like B3_FORCE_INLINE
|
|
#include "Bullet3Common/b3StackAlloc.h"
|
|
#include "Bullet3Common/b3HashMap.h"
|
|
|
|
#if !defined(__CELLOS_LV2__) && !defined(__MWERKS__)
|
|
#include <memory.h>
|
|
#endif
|
|
#include <string.h>
|
|
|
|
extern char b3s_bulletDNAstr[];
|
|
extern int b3s_bulletDNAlen;
|
|
extern char b3s_bulletDNAstr64[];
|
|
extern int b3s_bulletDNAlen64;
|
|
|
|
B3_FORCE_INLINE int b3StrLen(const char* str)
|
|
{
|
|
if (!str)
|
|
return (0);
|
|
int len = 0;
|
|
|
|
while (*str != 0)
|
|
{
|
|
str++;
|
|
len++;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
class b3Chunk
|
|
{
|
|
public:
|
|
int m_chunkCode;
|
|
int m_length;
|
|
void* m_oldPtr;
|
|
int m_dna_nr;
|
|
int m_number;
|
|
};
|
|
|
|
enum b3SerializationFlags
|
|
{
|
|
B3_SERIALIZE_NO_BVH = 1,
|
|
B3_SERIALIZE_NO_TRIANGLEINFOMAP = 2,
|
|
B3_SERIALIZE_NO_DUPLICATE_ASSERT = 4
|
|
};
|
|
|
|
class b3Serializer
|
|
{
|
|
public:
|
|
virtual ~b3Serializer() {}
|
|
|
|
virtual const unsigned char* getBufferPointer() const = 0;
|
|
|
|
virtual int getCurrentBufferSize() const = 0;
|
|
|
|
virtual b3Chunk* allocate(size_t size, int numElements) = 0;
|
|
|
|
virtual void finalizeChunk(b3Chunk* chunk, const char* structType, int chunkCode, void* oldPtr) = 0;
|
|
|
|
virtual void* findPointer(void* oldPtr) = 0;
|
|
|
|
virtual void* getUniquePointer(void* oldPtr) = 0;
|
|
|
|
virtual void startSerialization() = 0;
|
|
|
|
virtual void finishSerialization() = 0;
|
|
|
|
virtual const char* findNameForPointer(const void* ptr) const = 0;
|
|
|
|
virtual void registerNameForPointer(const void* ptr, const char* name) = 0;
|
|
|
|
virtual void serializeName(const char* ptr) = 0;
|
|
|
|
virtual int getSerializationFlags() const = 0;
|
|
|
|
virtual void setSerializationFlags(int flags) = 0;
|
|
};
|
|
|
|
#define B3_HEADER_LENGTH 12
|
|
#if defined(__sgi) || defined(__sparc) || defined(__sparc__) || defined(__PPC__) || defined(__ppc__) || defined(__BIG_ENDIAN__)
|
|
#define B3_MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
|
|
#else
|
|
#define B3_MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
|
|
#endif
|
|
|
|
#define B3_SOFTBODY_CODE B3_MAKE_ID('S', 'B', 'D', 'Y')
|
|
#define B3_COLLISIONOBJECT_CODE B3_MAKE_ID('C', 'O', 'B', 'J')
|
|
#define B3_RIGIDBODY_CODE B3_MAKE_ID('R', 'B', 'D', 'Y')
|
|
#define B3_CONSTRAINT_CODE B3_MAKE_ID('C', 'O', 'N', 'S')
|
|
#define B3_BOXSHAPE_CODE B3_MAKE_ID('B', 'O', 'X', 'S')
|
|
#define B3_QUANTIZED_BVH_CODE B3_MAKE_ID('Q', 'B', 'V', 'H')
|
|
#define B3_TRIANLGE_INFO_MAP B3_MAKE_ID('T', 'M', 'A', 'P')
|
|
#define B3_SHAPE_CODE B3_MAKE_ID('S', 'H', 'A', 'P')
|
|
#define B3_ARRAY_CODE B3_MAKE_ID('A', 'R', 'A', 'Y')
|
|
#define B3_SBMATERIAL_CODE B3_MAKE_ID('S', 'B', 'M', 'T')
|
|
#define B3_SBNODE_CODE B3_MAKE_ID('S', 'B', 'N', 'D')
|
|
#define B3_DYNAMICSWORLD_CODE B3_MAKE_ID('D', 'W', 'L', 'D')
|
|
#define B3_DNA_CODE B3_MAKE_ID('D', 'N', 'A', '1')
|
|
|
|
struct b3PointerUid
|
|
{
|
|
union {
|
|
void* m_ptr;
|
|
int m_uniqueIds[2];
|
|
};
|
|
};
|
|
|
|
///The b3DefaultSerializer is the main Bullet serialization class.
|
|
///The constructor takes an optional argument for backwards compatibility, it is recommended to leave this empty/zero.
|
|
class b3DefaultSerializer : public b3Serializer
|
|
{
|
|
b3AlignedObjectArray<char*> mTypes;
|
|
b3AlignedObjectArray<short*> mStructs;
|
|
b3AlignedObjectArray<short> mTlens;
|
|
b3HashMap<b3HashInt, int> mStructReverse;
|
|
b3HashMap<b3HashString, int> mTypeLookup;
|
|
|
|
b3HashMap<b3HashPtr, void*> m_chunkP;
|
|
|
|
b3HashMap<b3HashPtr, const char*> m_nameMap;
|
|
|
|
b3HashMap<b3HashPtr, b3PointerUid> m_uniquePointers;
|
|
int m_uniqueIdGenerator;
|
|
|
|
int m_totalSize;
|
|
unsigned char* m_buffer;
|
|
int m_currentSize;
|
|
void* m_dna;
|
|
int m_dnaLength;
|
|
|
|
int m_serializationFlags;
|
|
|
|
b3AlignedObjectArray<b3Chunk*> m_chunkPtrs;
|
|
|
|
protected:
|
|
virtual void* findPointer(void* oldPtr)
|
|
{
|
|
void** ptr = m_chunkP.find(oldPtr);
|
|
if (ptr && *ptr)
|
|
return *ptr;
|
|
return 0;
|
|
}
|
|
|
|
void writeDNA()
|
|
{
|
|
b3Chunk* dnaChunk = allocate(m_dnaLength, 1);
|
|
memcpy(dnaChunk->m_oldPtr, m_dna, m_dnaLength);
|
|
finalizeChunk(dnaChunk, "DNA1", B3_DNA_CODE, m_dna);
|
|
}
|
|
|
|
int getReverseType(const char* type) const
|
|
{
|
|
b3HashString key(type);
|
|
const int* valuePtr = mTypeLookup.find(key);
|
|
if (valuePtr)
|
|
return *valuePtr;
|
|
|
|
return -1;
|
|
}
|
|
|
|
void initDNA(const char* bdnaOrg, int dnalen)
|
|
{
|
|
///was already initialized
|
|
if (m_dna)
|
|
return;
|
|
|
|
int littleEndian = 1;
|
|
littleEndian = ((char*)&littleEndian)[0];
|
|
|
|
m_dna = b3AlignedAlloc(dnalen, 16);
|
|
memcpy(m_dna, bdnaOrg, dnalen);
|
|
m_dnaLength = dnalen;
|
|
|
|
int* intPtr = 0;
|
|
short* shtPtr = 0;
|
|
char* cp = 0;
|
|
int dataLen = 0;
|
|
intPtr = (int*)m_dna;
|
|
|
|
/*
|
|
SDNA (4 bytes) (magic number)
|
|
NAME (4 bytes)
|
|
<nr> (4 bytes) amount of names (int)
|
|
<string>
|
|
<string>
|
|
*/
|
|
|
|
if (strncmp((const char*)m_dna, "SDNA", 4) == 0)
|
|
{
|
|
// skip ++ NAME
|
|
intPtr++;
|
|
intPtr++;
|
|
}
|
|
|
|
// Parse names
|
|
if (!littleEndian)
|
|
*intPtr = b3SwapEndian(*intPtr);
|
|
|
|
dataLen = *intPtr;
|
|
|
|
intPtr++;
|
|
|
|
cp = (char*)intPtr;
|
|
int i;
|
|
for (i = 0; i < dataLen; i++)
|
|
{
|
|
while (*cp) cp++;
|
|
cp++;
|
|
}
|
|
cp = b3AlignPointer(cp, 4);
|
|
|
|
/*
|
|
TYPE (4 bytes)
|
|
<nr> amount of types (int)
|
|
<string>
|
|
<string>
|
|
*/
|
|
|
|
intPtr = (int*)cp;
|
|
b3Assert(strncmp(cp, "TYPE", 4) == 0);
|
|
intPtr++;
|
|
|
|
if (!littleEndian)
|
|
*intPtr = b3SwapEndian(*intPtr);
|
|
|
|
dataLen = *intPtr;
|
|
intPtr++;
|
|
|
|
cp = (char*)intPtr;
|
|
for (i = 0; i < dataLen; i++)
|
|
{
|
|
mTypes.push_back(cp);
|
|
while (*cp) cp++;
|
|
cp++;
|
|
}
|
|
|
|
cp = b3AlignPointer(cp, 4);
|
|
|
|
/*
|
|
TLEN (4 bytes)
|
|
<len> (short) the lengths of types
|
|
<len>
|
|
*/
|
|
|
|
// Parse type lens
|
|
intPtr = (int*)cp;
|
|
b3Assert(strncmp(cp, "TLEN", 4) == 0);
|
|
intPtr++;
|
|
|
|
dataLen = (int)mTypes.size();
|
|
|
|
shtPtr = (short*)intPtr;
|
|
for (i = 0; i < dataLen; i++, shtPtr++)
|
|
{
|
|
if (!littleEndian)
|
|
shtPtr[0] = b3SwapEndian(shtPtr[0]);
|
|
mTlens.push_back(shtPtr[0]);
|
|
}
|
|
|
|
if (dataLen & 1) shtPtr++;
|
|
|
|
/*
|
|
STRC (4 bytes)
|
|
<nr> amount of structs (int)
|
|
<typenr>
|
|
<nr_of_elems>
|
|
<typenr>
|
|
<namenr>
|
|
<typenr>
|
|
<namenr>
|
|
*/
|
|
|
|
intPtr = (int*)shtPtr;
|
|
cp = (char*)intPtr;
|
|
b3Assert(strncmp(cp, "STRC", 4) == 0);
|
|
intPtr++;
|
|
|
|
if (!littleEndian)
|
|
*intPtr = b3SwapEndian(*intPtr);
|
|
dataLen = *intPtr;
|
|
intPtr++;
|
|
|
|
shtPtr = (short*)intPtr;
|
|
for (i = 0; i < dataLen; i++)
|
|
{
|
|
mStructs.push_back(shtPtr);
|
|
|
|
if (!littleEndian)
|
|
{
|
|
shtPtr[0] = b3SwapEndian(shtPtr[0]);
|
|
shtPtr[1] = b3SwapEndian(shtPtr[1]);
|
|
|
|
int len = shtPtr[1];
|
|
shtPtr += 2;
|
|
|
|
for (int a = 0; a < len; a++, shtPtr += 2)
|
|
{
|
|
shtPtr[0] = b3SwapEndian(shtPtr[0]);
|
|
shtPtr[1] = b3SwapEndian(shtPtr[1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shtPtr += (2 * shtPtr[1]) + 2;
|
|
}
|
|
}
|
|
|
|
// build reverse lookups
|
|
for (i = 0; i < (int)mStructs.size(); i++)
|
|
{
|
|
short* strc = mStructs.at(i);
|
|
mStructReverse.insert(strc[0], i);
|
|
mTypeLookup.insert(b3HashString(mTypes[strc[0]]), i);
|
|
}
|
|
}
|
|
|
|
public:
|
|
b3DefaultSerializer(int totalSize = 0)
|
|
: m_totalSize(totalSize),
|
|
m_currentSize(0),
|
|
m_dna(0),
|
|
m_dnaLength(0),
|
|
m_serializationFlags(0)
|
|
{
|
|
m_buffer = m_totalSize ? (unsigned char*)b3AlignedAlloc(totalSize, 16) : 0;
|
|
|
|
const bool VOID_IS_8 = ((sizeof(void*) == 8));
|
|
|
|
#ifdef B3_INTERNAL_UPDATE_SERIALIZATION_STRUCTURES
|
|
if (VOID_IS_8)
|
|
{
|
|
#if _WIN64
|
|
initDNA((const char*)b3s_bulletDNAstr64, b3s_bulletDNAlen64);
|
|
#else
|
|
b3Assert(0);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifndef _WIN64
|
|
initDNA((const char*)b3s_bulletDNAstr, b3s_bulletDNAlen);
|
|
#else
|
|
b3Assert(0);
|
|
#endif
|
|
}
|
|
|
|
#else //B3_INTERNAL_UPDATE_SERIALIZATION_STRUCTURES
|
|
if (VOID_IS_8)
|
|
{
|
|
initDNA((const char*)b3s_bulletDNAstr64, b3s_bulletDNAlen64);
|
|
}
|
|
else
|
|
{
|
|
initDNA((const char*)b3s_bulletDNAstr, b3s_bulletDNAlen);
|
|
}
|
|
#endif //B3_INTERNAL_UPDATE_SERIALIZATION_STRUCTURES
|
|
}
|
|
|
|
virtual ~b3DefaultSerializer()
|
|
{
|
|
if (m_buffer)
|
|
b3AlignedFree(m_buffer);
|
|
if (m_dna)
|
|
b3AlignedFree(m_dna);
|
|
}
|
|
|
|
void writeHeader(unsigned char* buffer) const
|
|
{
|
|
#ifdef B3_USE_DOUBLE_PRECISION
|
|
memcpy(buffer, "BULLETd", 7);
|
|
#else
|
|
memcpy(buffer, "BULLETf", 7);
|
|
#endif //B3_USE_DOUBLE_PRECISION
|
|
|
|
int littleEndian = 1;
|
|
littleEndian = ((char*)&littleEndian)[0];
|
|
|
|
if (sizeof(void*) == 8)
|
|
{
|
|
buffer[7] = '-';
|
|
}
|
|
else
|
|
{
|
|
buffer[7] = '_';
|
|
}
|
|
|
|
if (littleEndian)
|
|
{
|
|
buffer[8] = 'v';
|
|
}
|
|
else
|
|
{
|
|
buffer[8] = 'V';
|
|
}
|
|
|
|
buffer[9] = '2';
|
|
buffer[10] = '8';
|
|
buffer[11] = '1';
|
|
}
|
|
|
|
virtual void startSerialization()
|
|
{
|
|
m_uniqueIdGenerator = 1;
|
|
if (m_totalSize)
|
|
{
|
|
unsigned char* buffer = internalAlloc(B3_HEADER_LENGTH);
|
|
writeHeader(buffer);
|
|
}
|
|
}
|
|
|
|
virtual void finishSerialization()
|
|
{
|
|
writeDNA();
|
|
|
|
//if we didn't pre-allocate a buffer, we need to create a contiguous buffer now
|
|
int mysize = 0;
|
|
if (!m_totalSize)
|
|
{
|
|
if (m_buffer)
|
|
b3AlignedFree(m_buffer);
|
|
|
|
m_currentSize += B3_HEADER_LENGTH;
|
|
m_buffer = (unsigned char*)b3AlignedAlloc(m_currentSize, 16);
|
|
|
|
unsigned char* currentPtr = m_buffer;
|
|
writeHeader(m_buffer);
|
|
currentPtr += B3_HEADER_LENGTH;
|
|
mysize += B3_HEADER_LENGTH;
|
|
for (int i = 0; i < m_chunkPtrs.size(); i++)
|
|
{
|
|
int curLength = sizeof(b3Chunk) + m_chunkPtrs[i]->m_length;
|
|
memcpy(currentPtr, m_chunkPtrs[i], curLength);
|
|
b3AlignedFree(m_chunkPtrs[i]);
|
|
currentPtr += curLength;
|
|
mysize += curLength;
|
|
}
|
|
}
|
|
|
|
mTypes.clear();
|
|
mStructs.clear();
|
|
mTlens.clear();
|
|
mStructReverse.clear();
|
|
mTypeLookup.clear();
|
|
m_chunkP.clear();
|
|
m_nameMap.clear();
|
|
m_uniquePointers.clear();
|
|
m_chunkPtrs.clear();
|
|
}
|
|
|
|
virtual void* getUniquePointer(void* oldPtr)
|
|
{
|
|
if (!oldPtr)
|
|
return 0;
|
|
|
|
b3PointerUid* uptr = (b3PointerUid*)m_uniquePointers.find(oldPtr);
|
|
if (uptr)
|
|
{
|
|
return uptr->m_ptr;
|
|
}
|
|
m_uniqueIdGenerator++;
|
|
|
|
b3PointerUid uid;
|
|
uid.m_uniqueIds[0] = m_uniqueIdGenerator;
|
|
uid.m_uniqueIds[1] = m_uniqueIdGenerator;
|
|
m_uniquePointers.insert(oldPtr, uid);
|
|
return uid.m_ptr;
|
|
}
|
|
|
|
virtual const unsigned char* getBufferPointer() const
|
|
{
|
|
return m_buffer;
|
|
}
|
|
|
|
virtual int getCurrentBufferSize() const
|
|
{
|
|
return m_currentSize;
|
|
}
|
|
|
|
virtual void finalizeChunk(b3Chunk* chunk, const char* structType, int chunkCode, void* oldPtr)
|
|
{
|
|
if (!(m_serializationFlags & B3_SERIALIZE_NO_DUPLICATE_ASSERT))
|
|
{
|
|
b3Assert(!findPointer(oldPtr));
|
|
}
|
|
|
|
chunk->m_dna_nr = getReverseType(structType);
|
|
|
|
chunk->m_chunkCode = chunkCode;
|
|
|
|
void* uniquePtr = getUniquePointer(oldPtr);
|
|
|
|
m_chunkP.insert(oldPtr, uniquePtr); //chunk->m_oldPtr);
|
|
chunk->m_oldPtr = uniquePtr; //oldPtr;
|
|
}
|
|
|
|
virtual unsigned char* internalAlloc(size_t size)
|
|
{
|
|
unsigned char* ptr = 0;
|
|
|
|
if (m_totalSize)
|
|
{
|
|
ptr = m_buffer + m_currentSize;
|
|
m_currentSize += int(size);
|
|
b3Assert(m_currentSize < m_totalSize);
|
|
}
|
|
else
|
|
{
|
|
ptr = (unsigned char*)b3AlignedAlloc(size, 16);
|
|
m_currentSize += int(size);
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
virtual b3Chunk* allocate(size_t size, int numElements)
|
|
{
|
|
unsigned char* ptr = internalAlloc(int(size) * numElements + sizeof(b3Chunk));
|
|
|
|
unsigned char* data = ptr + sizeof(b3Chunk);
|
|
|
|
b3Chunk* chunk = (b3Chunk*)ptr;
|
|
chunk->m_chunkCode = 0;
|
|
chunk->m_oldPtr = data;
|
|
chunk->m_length = int(size) * numElements;
|
|
chunk->m_number = numElements;
|
|
|
|
m_chunkPtrs.push_back(chunk);
|
|
|
|
return chunk;
|
|
}
|
|
|
|
virtual const char* findNameForPointer(const void* ptr) const
|
|
{
|
|
const char* const* namePtr = m_nameMap.find(ptr);
|
|
if (namePtr && *namePtr)
|
|
return *namePtr;
|
|
return 0;
|
|
}
|
|
|
|
virtual void registerNameForPointer(const void* ptr, const char* name)
|
|
{
|
|
m_nameMap.insert(ptr, name);
|
|
}
|
|
|
|
virtual void serializeName(const char* name)
|
|
{
|
|
if (name)
|
|
{
|
|
//don't serialize name twice
|
|
if (findPointer((void*)name))
|
|
return;
|
|
|
|
int len = b3StrLen(name);
|
|
if (len)
|
|
{
|
|
int newLen = len + 1;
|
|
int padding = ((newLen + 3) & ~3) - newLen;
|
|
newLen += padding;
|
|
|
|
//serialize name string now
|
|
b3Chunk* chunk = allocate(sizeof(char), newLen);
|
|
char* destinationName = (char*)chunk->m_oldPtr;
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
destinationName[i] = name[i];
|
|
}
|
|
destinationName[len] = 0;
|
|
finalizeChunk(chunk, "char", B3_ARRAY_CODE, (void*)name);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual int getSerializationFlags() const
|
|
{
|
|
return m_serializationFlags;
|
|
}
|
|
|
|
virtual void setSerializationFlags(int flags)
|
|
{
|
|
m_serializationFlags = flags;
|
|
}
|
|
};
|
|
|
|
#endif //B3_SERIALIZER_H
|