diff --git a/libraries/physics/src/ShapeInfo.cpp b/libraries/physics/src/ShapeInfo.cpp index 03dc162f6b..3d2b8c0f81 100644 --- a/libraries/physics/src/ShapeInfo.cpp +++ b/libraries/physics/src/ShapeInfo.cpp @@ -15,7 +15,20 @@ #include "ShapeInfo.h" -void ShapeInfo::getInfo(const btCollisionShape* shape) { +// prime numbers larger than 1e6 +const int NUM_PRIMES = 64; +const unsigned int PRIMES[] = { + 4194301U, 4194287U, 4194277U, 4194271U, 4194247U, 4194217U, 4194199U, 4194191U, + 4194187U, 4194181U, 4194173U, 4194167U, 4194143U, 4194137U, 4194131U, 4194107U, + 4194103U, 4194023U, 4194011U, 4194007U, 4193977U, 4193971U, 4193963U, 4193957U, + 4193939U, 4193929U, 4193909U, 4193869U, 4193807U, 4193803U, 4193801U, 4193789U, + 4193759U, 4193753U, 4193743U, 4193701U, 4193663U, 4193633U, 4193573U, 4193569U, + 4193551U, 4193549U, 4193531U, 4193513U, 4193507U, 4193459U, 4193447U, 4193443U, + 4193417U, 4193411U, 4193393U, 4193389U, 4193381U, 4193377U, 4193369U, 4193359U, + 4193353U, 4193327U, 4193309U, 4193303U, 4193297U, 4193279U, 4193269U, 4193263U +}; + +void ShapeInfo::collectInfo(const btCollisionShape* shape) { _data.clear(); if (shape) { _type = (unsigned int)(shape->getShapeType()); @@ -80,61 +93,87 @@ void ShapeInfo::setCapsule(float radius, float height) { _data.push_back(btVector3(radius, 0.5f * height, 0.0f)); } +unsigned int ShapeInfo::hashFunction(unsigned int value, int primeIndex) { + unsigned int hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U); + hash += ~(hash << 15); + hash ^= (hash >> 10); + hash += (hash << 3); + hash ^= (hash >> 6); + hash += ~(hash << 11); + return hash ^ (hash >> 16); +} + +unsigned int ShapeInfo::hashFunction2(unsigned int value) { + unsigned hash = 0x811c9dc5U; + for (int i = 0; i < 4; i++ ) { + unsigned int byte = (value << (i * 8)) >> (24 - i * 8); + hash = ( hash ^ byte ) * 0x01000193U; + } + return hash; +} + +const float MILLIMETERS_PER_METER = 1000.0f; + int ShapeInfo::computeHash() const { - // This hash algorithm works well for shapes that have dimensions less than about 256m. - // At larger values the likelihood of hash collision goes up because of the most - // significant bits are pushed off the top and the result could be the same as for smaller - // dimensions (truncation). - // - // The algorithm may produce collisions for shapes whose dimensions differ by less than - // ~1/256 m, however this is by design -- we don't expect collision differences smaller - // than 1 mm to be noticable. - unsigned int key = 0; - btVector3 tempData; + // scramble the bits of the type + // TODO?: provide lookup table for hash of _type? + int primeIndex = 0; + unsigned int hash = ShapeInfo::hashFunction((unsigned int)_type, primeIndex++); + + btVector3 tmpData; int numData = _data.size(); for (int i = 0; i < numData; ++i) { - // Successively multiply components of each vec3 by primes near 512 and convert to U32 - // to spread the data across more bits. Note that all dimensions are at half-value - // (half extents, radius, etc) which is why we multiply by primes near 512 rather - // than 256. - tempData = _data[i]; - key += (unsigned int)(509.0f * (tempData.getZ() + 0.01f)) - + 509 * (unsigned int)(521.0f * (tempData.getY() + 0.01f)) - + (509 * 521) * (unsigned int)(523.0f * (tempData.getX() + 0.01f)); - // avalanch the bits - key += ~(key << 15); - key ^= (key >> 10); - key += (key << 3); - key ^= (key >> 6); - key += ~(key << 11); - key ^= (key >> 16); + tmpData = _data[i]; + for (int j = 0; j < 3; ++j) { + // multiply these mm by a new prime + unsigned int floatHash = ShapeInfo::hashFunction((unsigned int)(tmpData[j] * MILLIMETERS_PER_METER + 0.49f), primeIndex++); + hash ^= floatHash; + } } - // finally XOR with type - return (int)(key ^ _type); + return hash; +} + +int ShapeInfo::computeHash2() const { + // scramble the bits of the type + // TODO?: provide lookup table for hash of _type? + unsigned int hash = ShapeInfo::hashFunction2((unsigned int)_type); + + btVector3 tmpData; + int numData = _data.size(); + for (int i = 0; i < numData; ++i) { + tmpData = _data[i]; + for (int j = 0; j < 3; ++j) { + unsigned int floatHash = ShapeInfo::hashFunction2((unsigned int)(tmpData[j] * MILLIMETERS_PER_METER + 0.49f)); + hash += ~(floatHash << 17); + hash ^= (floatHash >> 11); + hash += (floatHash << 4); + hash ^= (floatHash >> 7); + hash += ~(floatHash << 10); + hash = (hash << 16) | (hash >> 16); + } + } + return hash; } btCollisionShape* ShapeInfo::createShape() const { btCollisionShape* shape = NULL; int numData = _data.size(); switch(_type) { - case BOX_SHAPE_PROXYTYPE: - { + case BOX_SHAPE_PROXYTYPE: { if (numData > 0) { btVector3 halfExtents = _data[0]; shape = new btBoxShape(halfExtents); } } break; - case SPHERE_SHAPE_PROXYTYPE: - { + case SPHERE_SHAPE_PROXYTYPE: { if (numData > 0) { float radius = _data[0].getZ(); shape = new btSphereShape(radius); } } break; - case CYLINDER_SHAPE_PROXYTYPE: - { + case CYLINDER_SHAPE_PROXYTYPE: { if (numData > 0) { btVector3 halfExtents = _data[0]; // NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X @@ -143,8 +182,7 @@ btCollisionShape* ShapeInfo::createShape() const { } } break; - case CAPSULE_SHAPE_PROXYTYPE: - { + case CAPSULE_SHAPE_PROXYTYPE: { if (numData > 0) { float radius = _data[0].getX(); float height = 2.0f * _data[0].getY(); diff --git a/libraries/physics/src/ShapeInfo.h b/libraries/physics/src/ShapeInfo.h index 86063694ca..50f7be7275 100644 --- a/libraries/physics/src/ShapeInfo.h +++ b/libraries/physics/src/ShapeInfo.h @@ -17,167 +17,31 @@ #include #include -const float DEFAULT_MARGIN = 0.04f; - class ShapeInfo { public: ShapeInfo() : _type(INVALID_SHAPE_PROXYTYPE) {} ShapeInfo(const btCollisionShape* shape) : _type(INVALID_SHAPE_PROXYTYPE) { - getInfo(shape); + collectInfo(shape); } - // BOOKMARK -- move ShapeInfo to its own files - void getInfo(const btCollisionShape* shape); - /*{ - _data.clear(); - if (shape) { - _type = (unsigned int)(shape->getShapeType()); - switch(_type) { - case BOX_SHAPE_PROXYTYPE: - { - const btBoxShape* boxShape = static_cast(shape); - _data.push_back(boxShape->getHalfExtentsWithMargin()); - } - break; - case SPHERE_SHAPE_PROXYTYPE: - { - const btSphereShape* sphereShape = static_cast(shape); - _data.push_back(btVector3(0.0f, 0.0f, sphereShape->getRadius())); - } - break; - case CYLINDER_SHAPE_PROXYTYPE: - { - const btCylinderShape* cylinderShape = static_cast(shape); - _data.push_back(cylinderShape->getHalfExtentsWithMargin()); - } - break; - case CAPSULE_SHAPE_PROXYTYPE: - { - const btCapsuleShape* capsuleShape = static_cast(shape); - _data.push_back(btVector3(capsuleShape->getRadius(), capsuleShape->getHalfHeight(), 0.0f)); - // NOTE: we only support capsules with axis along yAxis - } - break; - default: - _type = INVALID_SHAPE_PROXYTYPE; - break; - } - } else { - _type = INVALID_SHAPE_PROXYTYPE; - } - }*/ + void collectInfo(const btCollisionShape* shape); void setBox(const btVector3& halfExtents); - /*{ - _type = BOX_SHAPE_PROXYTYPE; - _data.clear(); - _data.push_back(halfExtents); - }*/ - void setSphere(float radius); - /* { - _type = SPHERE_SHAPE_PROXYTYPE; - _data.clear(); - _data.push_back(btVector3(0.0f, 0.0f, radius)); - }*/ - void setCylinder(float radius, float height); - /*{ - _type = CYLINDER_SHAPE_PROXYTYPE; - _data.clear(); - // NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X - // halfExtents = btVector3(radius, halfHeight, unused) - _data.push_back(btVector3(radius, 0.5f * height, radius)); - }*/ - void setCapsule(float radius, float height); - /*{ - _type = CAPSULE_SHAPE_PROXYTYPE; - _data.clear(); - _data.push_back(btVector3(radius, 0.5f * height, 0.0f)); - }*/ int computeHash() const; - /*{ - // This hash algorithm works well for shapes that have dimensions less than about 256m. - // At larger values the likelihood of hash collision goes up because of the most - // significant bits are pushed off the top and the result could be the same as for smaller - // dimensions (truncation). - // - // The algorithm may produce collisions for shapes whose dimensions differ by less than - // ~1/256 m, however this is by design -- we don't expect collision differences smaller - // than 1 mm to be noticable. - unsigned int key = 0; - btVector3 tempData; - int numData = _data.size(); - for (int i = 0; i < numData; ++i) { - // Successively multiply components of each vec3 by primes near 512 and convert to U32 - // to spread the data across more bits. Note that all dimensions are at half-value - // (half extents, radius, etc) which is why we multiply by primes near 512 rather - // than 256. - tempData = _data[i]; - key += (unsigned int)(509.0f * (tempData.getZ() + 0.01f)) - + 509 * (unsigned int)(521.0f * (tempData.getY() + 0.01f)) - + (509 * 521) * (unsigned int)(523.0f * (tempData.getX() + 0.01f)); - // avalanch the bits - key += ~(key << 15); - key ^= (key >> 10); - key += (key << 3); - key ^= (key >> 6); - key += ~(key << 11); - key ^= (key >> 16); - } - // finally XOR with type - return (int)(key ^ _type); - }*/ + int computeHash2() const; + + static unsigned int hashFunction(unsigned int value, int primeIndex); + static unsigned int hashFunction2(unsigned int value); private: friend class ShapeManager; btCollisionShape* createShape() const; - /*{ - btCollisionShape* shape = NULL; - int numData = _data.size(); - switch(_type) { - case BOX_SHAPE_PROXYTYPE: - { - if (numData > 0) { - btVector3 halfExtents = _data[0]; - shape = new btBoxShape(halfExtents); - } - } - break; - case SPHERE_SHAPE_PROXYTYPE: - { - if (numData > 0) { - float radius = _data[0].getZ(); - shape = new btSphereShape(radius); - } - } - break; - case CYLINDER_SHAPE_PROXYTYPE: - { - if (numData > 0) { - btVector3 halfExtents = _data[0]; - // NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X - // halfExtents = btVector3(radius, halfHeight, unused) - shape = new btCylinderShape(halfExtents); - } - } - break; - case CAPSULE_SHAPE_PROXYTYPE: - { - if (numData > 0) { - float radius = _data[0].getX(); - float height = 2.0f * _data[0].getY(); - shape = new btCapsuleShape(radius, height); - } - } - break; - } - return shape; - }*/ int _type; btAlignedObjectArray _data; diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index 9874f24859..61f0ceabad 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -21,11 +21,12 @@ ShapeManager::~ShapeManager() { ShapeReference* shapeRef = _shapeMap.getAtIndex(i); delete shapeRef->_shape; } + _shapeMap.clear(); } btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { - int key = info.computeHash(); + ShapeKey key(info); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { shapeRef->_refCount++; @@ -43,7 +44,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { } bool ShapeManager::releaseShape(const ShapeInfo& info) { - int key = info.computeHash(); + ShapeKey key(info); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { if (shapeRef->_refCount > 0) { @@ -71,7 +72,7 @@ bool ShapeManager::releaseShape(const ShapeInfo& info) { bool ShapeManager::releaseShape(const btCollisionShape* shape) { // when the number of shapes is high it's probably cheaper to try to construct a ShapeInfo // and then compute the hash rather than walking the list in search of the pointer. - int key = info.computeHash(); + ShapeKey key(info); ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { if (shapeRef->_refCount > 0) { @@ -99,10 +100,9 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) { void ShapeManager::collectGarbage() { int numShapes = _pendingGarbage.size(); for (int i = 0; i < numShapes; ++i) { - int key = _pendingGarbage[i]; + ShapeKey& key = _pendingGarbage[i]; ShapeReference* shapeRef = _shapeMap.find(key); - assert(shapeRef != NULL); - if (shapeRef->_refCount == 0) { + if (shapeRef && shapeRef->_refCount == 0) { delete shapeRef->_shape; _shapeMap.remove(key); } @@ -111,7 +111,7 @@ void ShapeManager::collectGarbage() { } int ShapeManager::getNumReferences(const ShapeInfo& info) const { - int key = info.computeHash(); + ShapeKey key(info); const ShapeReference* shapeRef = _shapeMap.find(key); if (shapeRef) { return shapeRef->_refCount; diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index c683a041a3..6c497dd981 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -19,164 +19,25 @@ #include "ShapeInfo.h" -/* -struct ShapeInfo { - int _type; - btAlignedObjectArray _data; - //btVector3 _scale; - - ShapeInfo() : _type(INVALID_SHAPE_PROXYTYPE) {} - - ShapeInfo(const btCollisionShape* shape) : _type(INVALID_SHAPE_PROXYTYPE) { - getInfo(shape); +class ShapeKey +{ +public: + ShapeKey(const ShapeInfo& info) : _hash(0), _hash2(0) { + _hash = info.computeHash(); + _hash2 = info.computeHash2(); } - // BOOKMARK -- move ShapeInfo to its own files - void getInfo(const btCollisionShape* shape) { - _data.clear(); - if (shape) { - _type = (unsigned int)(shape->getShapeType()); - switch(_type) { - case BOX_SHAPE_PROXYTYPE: - { - const btBoxShape* boxShape = static_cast(shape); - _data.push_back(boxShape->getHalfExtentsWithMargin()); - } - break; - case SPHERE_SHAPE_PROXYTYPE: - { - const btSphereShape* sphereShape = static_cast(shape); - _data.push_back(btVector3(0.0f, 0.0f, sphereShape->getRadius())); - } - break; - case CYLINDER_SHAPE_PROXYTYPE: - { - const btCylinderShape* cylinderShape = static_cast(shape); - _data.push_back(cylinderShape->getHalfExtentsWithMargin()); - } - break; - case CAPSULE_SHAPE_PROXYTYPE: - { - const btCapsuleShape* capsuleShape = static_cast(shape); - _data.push_back(btVector3(capsuleShape->getRadius(), capsuleShape->getHalfHeight(), 0.0f)); - // NOTE: we only support capsules with axis along yAxis - } - break; - default: - _type = INVALID_SHAPE_PROXYTYPE; - break; - } - } else { - _type = INVALID_SHAPE_PROXYTYPE; - } + bool equals(const ShapeKey& other) const { + return _hash == other._hash && _hash2 == other._hash2; } - void setBox(const btVector3& halfExtents) { - _type = BOX_SHAPE_PROXYTYPE; - _data.clear(); - _data.push_back(halfExtents); - } - - void setSphere(float radius) { - _type = SPHERE_SHAPE_PROXYTYPE; - _data.clear(); - _data.push_back(btVector3(0.0f, 0.0f, radius)); - } - - void setCylinder(float radius, float height) { - _type = CYLINDER_SHAPE_PROXYTYPE; - _data.clear(); - // NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X - // halfExtents = btVector3(radius, halfHeight, unused) - _data.push_back(btVector3(radius, 0.5f * height, radius)); - } - - void setCapsule(float radius, float height) { - _type = CAPSULE_SHAPE_PROXYTYPE; - _data.clear(); - _data.push_back(btVector3(radius, 0.5f * height, 0.0f)); - } - - virtual int computeHash() const { - // This hash algorithm works well for shapes that have dimensions less than about 256m. - // At larger values the likelihood of hash collision goes up because of the most - // significant bits are pushed off the top and the result could be the same as for smaller - // dimensions (truncation). - // - // The algorithm may produce collisions for shapes whose dimensions differ by less than - // ~1/256 m, however this is by design -- we don't expect collision differences smaller - // than 1 mm to be noticable. - unsigned int key = 0; - btVector3 tempData; - int numData = _data.size(); - for (int i = 0; i < numData; ++i) { - // Successively multiply components of each vec3 by primes near 512 and convert to U32 - // to spread the data across more bits. Note that all dimensions are at half-value - // (half extents, radius, etc) which is why we multiply by primes near 512 rather - // than 256. - tempData = _data[i]; - key += (unsigned int)(509.0f * (tempData.getZ() + 0.01f)) - + 509 * (unsigned int)(521.0f * (tempData.getY() + 0.01f)) - + (509 * 521) * (unsigned int)(523.0f * (tempData.getX() + 0.01f)); - // avalanch the bits - key += ~(key << 15); - key ^= (key >> 10); - key += (key << 3); - key ^= (key >> 6); - key += ~(key << 11); - key ^= (key >> 16); - } - // finally XOR with type - return (int)(key ^ _type); - } + unsigned int getHash() const { return (unsigned int)_hash; } private: - friend class ShapeManager; - - virtual btCollisionShape* createShape() const { - btCollisionShape* shape = NULL; - int numData = _data.size(); - switch(_type) { - case BOX_SHAPE_PROXYTYPE: - { - if (numData > 0) { - btVector3 halfExtents = _data[0]; - shape = new btBoxShape(halfExtents); - } - } - break; - case SPHERE_SHAPE_PROXYTYPE: - { - if (numData > 0) { - float radius = _data[0].getZ(); - shape = new btSphereShape(radius); - } - } - break; - case CYLINDER_SHAPE_PROXYTYPE: - { - if (numData > 0) { - btVector3 halfExtents = _data[0]; - // NOTE: default cylinder has (UpAxis = 1) axis along yAxis and radius stored in X - // halfExtents = btVector3(radius, halfHeight, unused) - shape = new btCylinderShape(halfExtents); - } - } - break; - case CAPSULE_SHAPE_PROXYTYPE: - { - if (numData > 0) { - float radius = _data[0].getX(); - float height = 2.0f * _data[0].getY(); - shape = new btCapsuleShape(radius, height); - } - } - break; - } - return shape; - } + int _hash; + int _hash2; }; -*/ + class ShapeManager { public: @@ -205,8 +66,8 @@ private: ShapeReference() : _refCount(0), _shape(NULL) {} }; - btHashMap _shapeMap; - btAlignedObjectArray _pendingGarbage; + btHashMap _shapeMap; + btAlignedObjectArray _pendingGarbage; }; #endif // USE_BULLET_PHYSICS