mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 12:08:54 +02:00
ShapeManager now under unit test
This commit is contained in:
parent
50a97849bb
commit
d26540b029
8 changed files with 544 additions and 45 deletions
|
@ -4,16 +4,9 @@ set(TARGET_NAME physics)
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
|
|
||||||
include_glm()
|
include_glm()
|
||||||
|
include_bullet()
|
||||||
|
|
||||||
link_hifi_libraries(shared)
|
link_hifi_libraries(shared)
|
||||||
|
|
||||||
# Note: we rely on the default FindBullet.cmake moduld that comes with cmake
|
|
||||||
find_package(Bullet)
|
|
||||||
if (BULLET_FOUND)
|
|
||||||
include_directories(SYSTEM "${BULLET_INCLUDE_DIRS}")
|
|
||||||
list(APPEND ${TARGET_NAME}_LIBRARIES_TO_LINK "${BULLET_LIBRARIES}")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_BULLET_PHYSICS")
|
|
||||||
endif (BULLET_FOUND)
|
|
||||||
|
|
||||||
# call macro to link our dependencies and bubble them up via a property on our target
|
# call macro to link our dependencies and bubble them up via a property on our target
|
||||||
link_shared_dependencies()
|
link_shared_dependencies()
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
#include "PhysicsWorld.h"
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
|
||||||
#include "CollisionInfo.h"
|
#include "CollisionInfo.h"
|
||||||
#include "RayIntersectionInfo.h"
|
#include "RayIntersectionInfo.h"
|
||||||
|
|
||||||
|
|
159
libraries/physics/src/ShapeInfo.cpp
Normal file
159
libraries/physics/src/ShapeInfo.cpp
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
//
|
||||||
|
// ShapeInfo.cpp
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.10.29
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
|
||||||
|
#include "ShapeInfo.h"
|
||||||
|
|
||||||
|
void ShapeInfo::getInfo(const btCollisionShape* shape) {
|
||||||
|
_data.clear();
|
||||||
|
if (shape) {
|
||||||
|
_type = (unsigned int)(shape->getShapeType());
|
||||||
|
switch(_type) {
|
||||||
|
case BOX_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btBoxShape* boxShape = static_cast<const btBoxShape*>(shape);
|
||||||
|
_data.push_back(boxShape->getHalfExtentsWithMargin());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SPHERE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btSphereShape* sphereShape = static_cast<const btSphereShape*>(shape);
|
||||||
|
_data.push_back(btVector3(0.0f, 0.0f, sphereShape->getRadius()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CYLINDER_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btCylinderShape* cylinderShape = static_cast<const btCylinderShape*>(shape);
|
||||||
|
_data.push_back(cylinderShape->getHalfExtentsWithMargin());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CAPSULE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btCapsuleShape* capsuleShape = static_cast<const btCapsuleShape*>(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 ShapeInfo::setBox(const btVector3& halfExtents) {
|
||||||
|
_type = BOX_SHAPE_PROXYTYPE;
|
||||||
|
_data.clear();
|
||||||
|
_data.push_back(halfExtents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeInfo::setSphere(float radius) {
|
||||||
|
_type = SPHERE_SHAPE_PROXYTYPE;
|
||||||
|
_data.clear();
|
||||||
|
_data.push_back(btVector3(0.0f, 0.0f, radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShapeInfo::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 ShapeInfo::setCapsule(float radius, float height) {
|
||||||
|
_type = CAPSULE_SHAPE_PROXYTYPE;
|
||||||
|
_data.clear();
|
||||||
|
_data.push_back(btVector3(radius, 0.5f * height, 0.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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
btCollisionShape* ShapeInfo::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
187
libraries/physics/src/ShapeInfo.h
Normal file
187
libraries/physics/src/ShapeInfo.h
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
//
|
||||||
|
// ShapeInfo.h
|
||||||
|
// libraries/physcis/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2014.10.29
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ShapeInfo_h
|
||||||
|
#define hifi_ShapeInfo_h
|
||||||
|
|
||||||
|
#ifdef USE_BULLET_PHYSICS
|
||||||
|
|
||||||
|
#include <btBulletDynamicsCommon.h>
|
||||||
|
#include <LinearMath/btHashMap.h>
|
||||||
|
|
||||||
|
const float DEFAULT_MARGIN = 0.04f;
|
||||||
|
|
||||||
|
class ShapeInfo {
|
||||||
|
public:
|
||||||
|
ShapeInfo() : _type(INVALID_SHAPE_PROXYTYPE) {}
|
||||||
|
|
||||||
|
ShapeInfo(const btCollisionShape* shape) : _type(INVALID_SHAPE_PROXYTYPE) {
|
||||||
|
getInfo(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<const btBoxShape*>(shape);
|
||||||
|
_data.push_back(boxShape->getHalfExtentsWithMargin());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SPHERE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btSphereShape* sphereShape = static_cast<const btSphereShape*>(shape);
|
||||||
|
_data.push_back(btVector3(0.0f, 0.0f, sphereShape->getRadius()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CYLINDER_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btCylinderShape* cylinderShape = static_cast<const btCylinderShape*>(shape);
|
||||||
|
_data.push_back(cylinderShape->getHalfExtentsWithMargin());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CAPSULE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btCapsuleShape* capsuleShape = static_cast<const btCapsuleShape*>(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 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);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
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<btVector3> _data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // USE_BULLET_PHYSICS
|
||||||
|
#endif // hifi_ShapeInfo_h
|
|
@ -25,7 +25,7 @@ ShapeManager::~ShapeManager() {
|
||||||
|
|
||||||
|
|
||||||
btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
int key = info.getHash();
|
int key = info.computeHash();
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
if (shapeRef) {
|
if (shapeRef) {
|
||||||
shapeRef->_refCount++;
|
shapeRef->_refCount++;
|
||||||
|
@ -43,7 +43,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShapeManager::releaseShape(const ShapeInfo& info) {
|
bool ShapeManager::releaseShape(const ShapeInfo& info) {
|
||||||
int key = info.getHash();
|
int key = info.computeHash();
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
if (shapeRef) {
|
if (shapeRef) {
|
||||||
if (shapeRef->_refCount > 0) {
|
if (shapeRef->_refCount > 0) {
|
||||||
|
@ -67,6 +67,35 @@ bool ShapeManager::releaseShape(const ShapeInfo& info) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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();
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
|
if (shapeRef) {
|
||||||
|
if (shapeRef->_refCount > 0) {
|
||||||
|
shapeRef->_refCount--;
|
||||||
|
if (shapeRef->_refCount == 0) {
|
||||||
|
_pendingGarbage.push_back(key);
|
||||||
|
const int MAX_GARBAGE_CAPACITY = 127;
|
||||||
|
if (_pendingGarbage.size() > MAX_GARBAGE_CAPACITY) {
|
||||||
|
collectGarbage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// attempt to remove shape that has no refs
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// attempt to remove unmanaged shape
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void ShapeManager::collectGarbage() {
|
void ShapeManager::collectGarbage() {
|
||||||
int numShapes = _pendingGarbage.size();
|
int numShapes = _pendingGarbage.size();
|
||||||
for (int i = 0; i < numShapes; ++i) {
|
for (int i = 0; i < numShapes; ++i) {
|
||||||
|
@ -82,7 +111,7 @@ void ShapeManager::collectGarbage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
|
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
|
||||||
int key = info.getHash();
|
int key = info.computeHash();
|
||||||
const ShapeReference* shapeRef = _shapeMap.find(key);
|
const ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
if (shapeRef) {
|
if (shapeRef) {
|
||||||
return shapeRef->_refCount;
|
return shapeRef->_refCount;
|
||||||
|
|
|
@ -17,48 +17,166 @@
|
||||||
#include <btBulletDynamicsCommon.h>
|
#include <btBulletDynamicsCommon.h>
|
||||||
#include <LinearMath/btHashMap.h>
|
#include <LinearMath/btHashMap.h>
|
||||||
|
|
||||||
|
#include "ShapeInfo.h"
|
||||||
|
|
||||||
|
/*
|
||||||
struct ShapeInfo {
|
struct ShapeInfo {
|
||||||
int _type;
|
int _type;
|
||||||
btVector3 _size;
|
btAlignedObjectArray<btVector3> _data;
|
||||||
|
//btVector3 _scale;
|
||||||
|
|
||||||
ShapeInfo() : _type(BOX_SHAPE_PROXYTYPE), _size(1.0f, 1.0f, 1.0f) {}
|
ShapeInfo() : _type(INVALID_SHAPE_PROXYTYPE) {}
|
||||||
|
|
||||||
virtual int getHash() const {
|
ShapeInfo(const btCollisionShape* shape) : _type(INVALID_SHAPE_PROXYTYPE) {
|
||||||
// successfully multiply components of size by primes near 256 and convert to U32
|
getInfo(shape);
|
||||||
unsigned int key = (unsigned int)(241.0f * _size.getX())
|
|
||||||
+ 241 * (unsigned int)(251.0f * _size.getY())
|
|
||||||
+ (241 * 251) * (unsigned int)(257.0f * _size.getZ());
|
|
||||||
// then scramble the results
|
|
||||||
key += ~(key << 15);
|
|
||||||
key ^= (key >> 10);
|
|
||||||
key += (key << 3);
|
|
||||||
key ^= (key >> 6);
|
|
||||||
key += ~(key << 11);
|
|
||||||
key ^= (key >> 16);
|
|
||||||
// finally XOR with type
|
|
||||||
key ^= _type;
|
|
||||||
return key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<const btBoxShape*>(shape);
|
||||||
|
_data.push_back(boxShape->getHalfExtentsWithMargin());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SPHERE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btSphereShape* sphereShape = static_cast<const btSphereShape*>(shape);
|
||||||
|
_data.push_back(btVector3(0.0f, 0.0f, sphereShape->getRadius()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CYLINDER_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btCylinderShape* cylinderShape = static_cast<const btCylinderShape*>(shape);
|
||||||
|
_data.push_back(cylinderShape->getHalfExtentsWithMargin());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CAPSULE_SHAPE_PROXYTYPE:
|
||||||
|
{
|
||||||
|
const btCapsuleShape* capsuleShape = static_cast<const btCapsuleShape*>(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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ShapeManager;
|
||||||
|
|
||||||
virtual btCollisionShape* createShape() const {
|
virtual btCollisionShape* createShape() const {
|
||||||
const float MAX_SHAPE_DIMENSION = 100.0f;
|
btCollisionShape* shape = NULL;
|
||||||
const float MIN_SHAPE_DIMENSION = 0.01f;
|
int numData = _data.size();
|
||||||
for (int i = 0; i < 3; ++i) {
|
switch(_type) {
|
||||||
float side = _size[i];
|
case BOX_SHAPE_PROXYTYPE:
|
||||||
if (side > MAX_SHAPE_DIMENSION || side < MIN_SHAPE_DIMENSION) {
|
{
|
||||||
return NULL;
|
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;
|
||||||
}
|
}
|
||||||
// default behavior is to create a btBoxShape
|
return shape;
|
||||||
return new btBoxShape(0.5f * _size);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
struct ShapeReference {
|
|
||||||
int _refCount;
|
|
||||||
btCollisionShape* _shape;
|
|
||||||
ShapeReference() : _refCount(0), _shape(NULL) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ShapeManager {
|
class ShapeManager {
|
||||||
public:
|
public:
|
||||||
|
@ -71,6 +189,7 @@ public:
|
||||||
|
|
||||||
/// \return true if shape was found and released
|
/// \return true if shape was found and released
|
||||||
bool releaseShape(const ShapeInfo& info);
|
bool releaseShape(const ShapeInfo& info);
|
||||||
|
// bool removeReference(const btCollisionShape*);
|
||||||
|
|
||||||
/// delete shapes that have zero references
|
/// delete shapes that have zero references
|
||||||
void collectGarbage();
|
void collectGarbage();
|
||||||
|
@ -80,6 +199,12 @@ public:
|
||||||
int getNumReferences(const ShapeInfo& info) const;
|
int getNumReferences(const ShapeInfo& info) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct ShapeReference {
|
||||||
|
int _refCount;
|
||||||
|
btCollisionShape* _shape;
|
||||||
|
ShapeReference() : _refCount(0), _shape(NULL) {}
|
||||||
|
};
|
||||||
|
|
||||||
btHashMap<btHashInt, ShapeReference> _shapeMap;
|
btHashMap<btHashInt, ShapeReference> _shapeMap;
|
||||||
btAlignedObjectArray<int> _pendingGarbage;
|
btAlignedObjectArray<int> _pendingGarbage;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,8 +3,8 @@ set(TARGET_NAME physics-tests)
|
||||||
setup_hifi_project()
|
setup_hifi_project()
|
||||||
|
|
||||||
include_glm()
|
include_glm()
|
||||||
|
include_bullet()
|
||||||
|
|
||||||
# link in the shared libraries
|
|
||||||
link_hifi_libraries(shared physics)
|
link_hifi_libraries(shared physics)
|
||||||
|
|
||||||
link_shared_dependencies()
|
link_shared_dependencies()
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
|
|
||||||
#include "ShapeColliderTests.h"
|
#include "ShapeColliderTests.h"
|
||||||
#include "VerletShapeTests.h"
|
#include "VerletShapeTests.h"
|
||||||
|
#include "ShapeManagerTests.h"
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
ShapeColliderTests::runAllTests();
|
//ShapeColliderTests::runAllTests();
|
||||||
VerletShapeTests::runAllTests();
|
//VerletShapeTests::runAllTests();
|
||||||
|
ShapeManagerTests::runAllTests();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue