mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 17:17:58 +02:00
Merge pull request #14887 from AndrewMeadows/shape-garbage-collector
Case 21066: restore shape caching in ShapeManager
This commit is contained in:
commit
3b5b077a3e
8 changed files with 141 additions and 93 deletions
|
@ -6228,27 +6228,40 @@ void Application::update(float deltaTime) {
|
||||||
PROFILE_RANGE(simulation_physics, "PrePhysics");
|
PROFILE_RANGE(simulation_physics, "PrePhysics");
|
||||||
PerformanceTimer perfTimer("prePhysics)");
|
PerformanceTimer perfTimer("prePhysics)");
|
||||||
{
|
{
|
||||||
|
PROFILE_RANGE(simulation_physics, "RemoveEntities");
|
||||||
const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics();
|
const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics();
|
||||||
|
{
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
|
||||||
_physicsEngine->removeObjects(motionStates);
|
_physicsEngine->removeObjects(motionStates);
|
||||||
|
}
|
||||||
_entitySimulation->deleteObjectsRemovedFromPhysics();
|
_entitySimulation->deleteObjectsRemovedFromPhysics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(simulation_physics, "AddEntities");
|
||||||
VectorOfMotionStates motionStates;
|
VectorOfMotionStates motionStates;
|
||||||
getEntities()->getTree()->withReadLock([&] {
|
getEntities()->getTree()->withReadLock([&] {
|
||||||
_entitySimulation->getObjectsToAddToPhysics(motionStates);
|
_entitySimulation->getObjectsToAddToPhysics(motionStates);
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "NumObjs", 0xffff0000, (uint64_t)motionStates.size());
|
||||||
_physicsEngine->addObjects(motionStates);
|
_physicsEngine->addObjects(motionStates);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
VectorOfMotionStates motionStates;
|
||||||
|
PROFILE_RANGE(simulation_physics, "ChangeEntities");
|
||||||
getEntities()->getTree()->withReadLock([&] {
|
getEntities()->getTree()->withReadLock([&] {
|
||||||
_entitySimulation->getObjectsToChange(motionStates);
|
_entitySimulation->getObjectsToChange(motionStates);
|
||||||
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
|
||||||
_entitySimulation->setObjectsToChange(stillNeedChange);
|
_entitySimulation->setObjectsToChange(stillNeedChange);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_entitySimulation->applyDynamicChanges();
|
_entitySimulation->applyDynamicChanges();
|
||||||
|
|
||||||
t1 = std::chrono::high_resolution_clock::now();
|
t1 = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(simulation_physics, "Avatars");
|
||||||
PhysicsEngine::Transaction transaction;
|
PhysicsEngine::Transaction transaction;
|
||||||
avatarManager->buildPhysicsTransaction(transaction);
|
avatarManager->buildPhysicsTransaction(transaction);
|
||||||
_physicsEngine->processTransaction(transaction);
|
_physicsEngine->processTransaction(transaction);
|
||||||
|
@ -6258,11 +6271,15 @@ void Application::update(float deltaTime) {
|
||||||
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
|
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
|
||||||
myAvatar->prepareForPhysicsSimulation();
|
myAvatar->prepareForPhysicsSimulation();
|
||||||
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
|
_physicsEngine->enableGlobalContactAddedCallback(myAvatar->isFlying());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(simulation_physics, "PrepareActions");
|
||||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||||
dynamic->prepareForPhysicsSimulation();
|
dynamic->prepareForPhysicsSimulation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
auto t2 = std::chrono::high_resolution_clock::now();
|
auto t2 = std::chrono::high_resolution_clock::now();
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "StepPhysics");
|
PROFILE_RANGE(simulation_physics, "StepPhysics");
|
||||||
|
|
|
@ -90,7 +90,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
||||||
PROFILE_RANGE(app, __FUNCTION__);
|
PROFILE_RANGE(render, __FUNCTION__);
|
||||||
|
|
||||||
if (!_uiTexture) {
|
if (!_uiTexture) {
|
||||||
_uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
|
_uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
|
||||||
|
@ -119,7 +119,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
|
void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
|
||||||
PROFILE_RANGE(app, __FUNCTION__);
|
PROFILE_RANGE(render, __FUNCTION__);
|
||||||
|
|
||||||
gpu::Batch& batch = *renderArgs->_batch;
|
gpu::Batch& batch = *renderArgs->_batch;
|
||||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
@ -176,7 +176,7 @@ static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LI
|
||||||
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
|
static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);
|
||||||
|
|
||||||
void ApplicationOverlay::buildFramebufferObject() {
|
void ApplicationOverlay::buildFramebufferObject() {
|
||||||
PROFILE_RANGE(app, __FUNCTION__);
|
PROFILE_RANGE(render, __FUNCTION__);
|
||||||
|
|
||||||
auto uiSize = glm::uvec2(qApp->getUiSize());
|
auto uiSize = glm::uvec2(qApp->getUiSize());
|
||||||
if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) {
|
if (!_overlayFramebuffer || uiSize != _overlayFramebuffer->getSize()) {
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
|
|
||||||
#include "ShapeFactory.h"
|
#include "ShapeFactory.h"
|
||||||
|
|
||||||
|
const int MAX_RING_SIZE = 256;
|
||||||
|
|
||||||
ShapeManager::ShapeManager() {
|
ShapeManager::ShapeManager() {
|
||||||
|
_garbageRing.reserve(MAX_RING_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeManager::~ShapeManager() {
|
ShapeManager::~ShapeManager() {
|
||||||
|
@ -33,8 +36,8 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
if (info.getType() == SHAPE_TYPE_NONE) {
|
if (info.getType() == SHAPE_TYPE_NONE) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
HashKey key = info.getHash();
|
HashKey hashKey(info.getHash());
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
ShapeReference* shapeRef = _shapeMap.find(hashKey);
|
||||||
if (shapeRef) {
|
if (shapeRef) {
|
||||||
shapeRef->refCount++;
|
shapeRef->refCount++;
|
||||||
return shapeRef->shape;
|
return shapeRef->shape;
|
||||||
|
@ -44,23 +47,43 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||||
ShapeReference newRef;
|
ShapeReference newRef;
|
||||||
newRef.refCount = 1;
|
newRef.refCount = 1;
|
||||||
newRef.shape = shape;
|
newRef.shape = shape;
|
||||||
newRef.key = key;
|
newRef.key = info.getHash();
|
||||||
_shapeMap.insert(key, newRef);
|
_shapeMap.insert(hashKey, newRef);
|
||||||
}
|
}
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private helper method
|
// private helper method
|
||||||
bool ShapeManager::releaseShapeByKey(const HashKey& key) {
|
bool ShapeManager::releaseShapeByKey(uint64_t key) {
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
HashKey hashKey(key);
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(hashKey);
|
||||||
if (shapeRef) {
|
if (shapeRef) {
|
||||||
if (shapeRef->refCount > 0) {
|
if (shapeRef->refCount > 0) {
|
||||||
shapeRef->refCount--;
|
shapeRef->refCount--;
|
||||||
if (shapeRef->refCount == 0) {
|
if (shapeRef->refCount == 0) {
|
||||||
_pendingGarbage.push_back(key);
|
// look for existing entry in _garbageRing
|
||||||
const int MAX_SHAPE_GARBAGE_CAPACITY = 255;
|
int32_t ringSize = (int32_t)(_garbageRing.size());
|
||||||
if (_pendingGarbage.size() > MAX_SHAPE_GARBAGE_CAPACITY) {
|
for (int32_t i = 0; i < ringSize; ++i) {
|
||||||
collectGarbage();
|
int32_t j = (_ringIndex + ringSize) % ringSize;
|
||||||
|
if (_garbageRing[j] == key) {
|
||||||
|
// already on the list, don't add it again
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ringSize == MAX_RING_SIZE) {
|
||||||
|
// remove one
|
||||||
|
HashKey hashKeyToRemove(_garbageRing[_ringIndex]);
|
||||||
|
ShapeReference* shapeRef = _shapeMap.find(hashKeyToRemove);
|
||||||
|
if (shapeRef && shapeRef->refCount == 0) {
|
||||||
|
ShapeFactory::deleteShape(shapeRef->shape);
|
||||||
|
_shapeMap.remove(hashKeyToRemove);
|
||||||
|
}
|
||||||
|
// replace at _ringIndex and advance
|
||||||
|
_garbageRing[_ringIndex] = key;
|
||||||
|
_ringIndex = (_ringIndex + 1) % ringSize;
|
||||||
|
} else {
|
||||||
|
// add one
|
||||||
|
_garbageRing.push_back(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -87,21 +110,22 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeManager::collectGarbage() {
|
void ShapeManager::collectGarbage() {
|
||||||
int numShapes = _pendingGarbage.size();
|
int numShapes = (int32_t)(_garbageRing.size());
|
||||||
for (int i = 0; i < numShapes; ++i) {
|
for (int i = 0; i < numShapes; ++i) {
|
||||||
HashKey& key = _pendingGarbage[i];
|
HashKey key(_garbageRing[i]);
|
||||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||||
if (shapeRef && shapeRef->refCount == 0) {
|
if (shapeRef && shapeRef->refCount == 0) {
|
||||||
ShapeFactory::deleteShape(shapeRef->shape);
|
ShapeFactory::deleteShape(shapeRef->shape);
|
||||||
_shapeMap.remove(key);
|
_shapeMap.remove(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_pendingGarbage.clear();
|
_ringIndex = 0;
|
||||||
|
_garbageRing.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
|
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
|
||||||
HashKey key = info.getHash();
|
HashKey hashKey(info.getHash());
|
||||||
const ShapeReference* shapeRef = _shapeMap.find(key);
|
const ShapeReference* shapeRef = _shapeMap.find(hashKey);
|
||||||
if (shapeRef) {
|
if (shapeRef) {
|
||||||
return shapeRef->refCount;
|
return shapeRef->refCount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#ifndef hifi_ShapeManager_h
|
#ifndef hifi_ShapeManager_h
|
||||||
#define hifi_ShapeManager_h
|
#define hifi_ShapeManager_h
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <btBulletDynamicsCommon.h>
|
#include <btBulletDynamicsCommon.h>
|
||||||
#include <LinearMath/btHashMap.h>
|
#include <LinearMath/btHashMap.h>
|
||||||
|
|
||||||
|
@ -41,6 +43,7 @@
|
||||||
// later. When that list grows big enough the ShapeManager will remove any matching
|
// later. When that list grows big enough the ShapeManager will remove any matching
|
||||||
// entries that still have zero ref-count.
|
// entries that still have zero ref-count.
|
||||||
|
|
||||||
|
|
||||||
class ShapeManager {
|
class ShapeManager {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -63,19 +66,20 @@ public:
|
||||||
bool hasShape(const btCollisionShape* shape) const;
|
bool hasShape(const btCollisionShape* shape) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool releaseShapeByKey(const HashKey& key);
|
bool releaseShapeByKey(uint64_t key);
|
||||||
|
|
||||||
class ShapeReference {
|
class ShapeReference {
|
||||||
public:
|
public:
|
||||||
int refCount;
|
int refCount;
|
||||||
const btCollisionShape* shape;
|
const btCollisionShape* shape;
|
||||||
HashKey key;
|
uint64_t key { 0 };
|
||||||
ShapeReference() : refCount(0), shape(nullptr) {}
|
ShapeReference() : refCount(0), shape(nullptr) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// btHashMap is required because it supports memory alignment of the btCollisionShapes
|
// btHashMap is required because it supports memory alignment of the btCollisionShapes
|
||||||
btHashMap<HashKey, ShapeReference> _shapeMap;
|
btHashMap<HashKey, ShapeReference> _shapeMap;
|
||||||
btAlignedObjectArray<HashKey> _pendingGarbage;
|
std::vector<uint64_t> _garbageRing;
|
||||||
|
uint32_t _ringIndex { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ShapeManager_h
|
#endif // hifi_ShapeManager_h
|
||||||
|
|
|
@ -32,17 +32,19 @@ class HashKey {
|
||||||
public:
|
public:
|
||||||
static float getNumQuantizedValuesPerMeter();
|
static float getNumQuantizedValuesPerMeter();
|
||||||
|
|
||||||
|
HashKey() {}
|
||||||
|
HashKey(uint64_t hash) : _hash(hash) {}
|
||||||
|
|
||||||
// These two methods are required by btHashMap.
|
// These two methods are required by btHashMap.
|
||||||
bool equals(const HashKey& other) const { return _hash == other._hash; }
|
bool equals(const HashKey& other) const { return _hash == other._hash; }
|
||||||
int32_t getHash() const { return (int32_t)((uint32_t)_hash); }
|
int32_t getHash() const { return (int32_t)((uint32_t)_hash); }
|
||||||
|
|
||||||
void clear() { _hash = _hashCount = 0; }
|
// These methods for accumulating a hash.
|
||||||
bool isNull() const { return _hash == 0 && _hashCount == 0; }
|
|
||||||
void hashUint64(uint64_t data);
|
void hashUint64(uint64_t data);
|
||||||
void hashFloat(float data);
|
void hashFloat(float data);
|
||||||
void hashVec3(const glm::vec3& data);
|
void hashVec3(const glm::vec3& data);
|
||||||
|
|
||||||
uint64_t getHash64() const { return _hash; } // for debug/test purposes
|
uint64_t getHash64() const { return _hash; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _hash { 0 };
|
uint64_t _hash { 0 };
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "HashKey.h"
|
||||||
#include "NumericalConstants.h" // for MILLIMETERS_PER_METER
|
#include "NumericalConstants.h" // for MILLIMETERS_PER_METER
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
|
@ -96,7 +97,7 @@ void ShapeInfo::clear() {
|
||||||
_sphereCollection.clear();
|
_sphereCollection.clear();
|
||||||
_halfExtents = glm::vec3(0.0f);
|
_halfExtents = glm::vec3(0.0f);
|
||||||
_offset = glm::vec3(0.0f);
|
_offset = glm::vec3(0.0f);
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
_type = SHAPE_TYPE_NONE;
|
_type = SHAPE_TYPE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,14 +132,14 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
||||||
_url = "";
|
_url = "";
|
||||||
_type = SHAPE_TYPE_BOX;
|
_type = SHAPE_TYPE_BOX;
|
||||||
setHalfExtents(halfExtents);
|
setHalfExtents(halfExtents);
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setSphere(float radius) {
|
void ShapeInfo::setSphere(float radius) {
|
||||||
|
@ -146,7 +147,7 @@ void ShapeInfo::setSphere(float radius) {
|
||||||
_type = SHAPE_TYPE_SPHERE;
|
_type = SHAPE_TYPE_SPHERE;
|
||||||
radius = glm::max(radius, MIN_HALF_EXTENT);
|
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||||
_halfExtents = glm::vec3(radius);
|
_halfExtents = glm::vec3(radius);
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setMultiSphere(const std::vector<glm::vec3>& centers, const std::vector<float>& radiuses) {
|
void ShapeInfo::setMultiSphere(const std::vector<glm::vec3>& centers, const std::vector<float>& radiuses) {
|
||||||
|
@ -158,12 +159,12 @@ void ShapeInfo::setMultiSphere(const std::vector<glm::vec3>& centers, const std:
|
||||||
SphereData sphere = SphereData(centers[i], radiuses[i]);
|
SphereData sphere = SphereData(centers[i], radiuses[i]);
|
||||||
_sphereCollection.push_back(sphere);
|
_sphereCollection.push_back(sphere);
|
||||||
}
|
}
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
|
void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
|
||||||
_pointCollection = pointCollection;
|
_pointCollection = pointCollection;
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) {
|
void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) {
|
||||||
|
@ -172,12 +173,12 @@ void ShapeInfo::setCapsuleY(float radius, float cylinderHalfHeight) {
|
||||||
radius = glm::max(radius, MIN_HALF_EXTENT);
|
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||||
cylinderHalfHeight = glm::max(cylinderHalfHeight, 0.0f);
|
cylinderHalfHeight = glm::max(cylinderHalfHeight, 0.0f);
|
||||||
_halfExtents = glm::vec3(radius, cylinderHalfHeight + radius, radius);
|
_halfExtents = glm::vec3(radius, cylinderHalfHeight + radius, radius);
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setOffset(const glm::vec3& offset) {
|
void ShapeInfo::setOffset(const glm::vec3& offset) {
|
||||||
_offset = offset;
|
_offset = offset;
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ShapeInfo::getNumSubShapes() const {
|
uint32_t ShapeInfo::getNumSubShapes() const {
|
||||||
|
@ -269,20 +270,21 @@ float ShapeInfo::computeVolume() const {
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HashKey& ShapeInfo::getHash() const {
|
uint64_t ShapeInfo::getHash() const {
|
||||||
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
|
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
|
||||||
if (_hashKey.isNull() && _type != SHAPE_TYPE_NONE) {
|
if (_hash64 == 0 && _type != SHAPE_TYPE_NONE) {
|
||||||
|
HashKey hashKey;
|
||||||
// The key is not yet cached therefore we must compute it.
|
// The key is not yet cached therefore we must compute it.
|
||||||
|
|
||||||
_hashKey.hashUint64((uint64_t)_type);
|
hashKey.hashUint64((uint64_t)_type);
|
||||||
if (_type == SHAPE_TYPE_MULTISPHERE) {
|
if (_type == SHAPE_TYPE_MULTISPHERE) {
|
||||||
for (auto &sphereData : _sphereCollection) {
|
for (auto &sphereData : _sphereCollection) {
|
||||||
_hashKey.hashVec3(glm::vec3(sphereData));
|
hashKey.hashVec3(glm::vec3(sphereData));
|
||||||
_hashKey.hashFloat(sphereData.w);
|
hashKey.hashFloat(sphereData.w);
|
||||||
}
|
}
|
||||||
} else if (_type != SHAPE_TYPE_SIMPLE_HULL) {
|
} else if (_type != SHAPE_TYPE_SIMPLE_HULL) {
|
||||||
_hashKey.hashVec3(_halfExtents);
|
hashKey.hashVec3(_halfExtents);
|
||||||
_hashKey.hashVec3(_offset);
|
hashKey.hashVec3(_offset);
|
||||||
} else {
|
} else {
|
||||||
// TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique
|
// TODO: we could avoid hashing all of these points if we were to supply the ShapeInfo with a unique
|
||||||
// descriptive string. Shapes that are uniquely described by their type and URL could just put their
|
// descriptive string. Shapes that are uniquely described by their type and URL could just put their
|
||||||
|
@ -292,7 +294,7 @@ const HashKey& ShapeInfo::getHash() const {
|
||||||
const int numPoints = (int)points.size();
|
const int numPoints = (int)points.size();
|
||||||
|
|
||||||
for (int i = 0; i < numPoints; ++i) {
|
for (int i = 0; i < numPoints; ++i) {
|
||||||
_hashKey.hashVec3(points[i]);
|
hashKey.hashVec3(points[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,23 +302,24 @@ const HashKey& ShapeInfo::getHash() const {
|
||||||
if (!url.isEmpty()) {
|
if (!url.isEmpty()) {
|
||||||
QByteArray baUrl = url.toLocal8Bit();
|
QByteArray baUrl = url.toLocal8Bit();
|
||||||
uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size());
|
uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size());
|
||||||
_hashKey.hashUint64((uint64_t)urlHash);
|
hashKey.hashUint64((uint64_t)urlHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||||
uint64_t numHulls = (uint64_t)_pointCollection.size();
|
uint64_t numHulls = (uint64_t)_pointCollection.size();
|
||||||
_hashKey.hashUint64(numHulls);
|
hashKey.hashUint64(numHulls);
|
||||||
} else if (_type == SHAPE_TYPE_MULTISPHERE) {
|
} else if (_type == SHAPE_TYPE_MULTISPHERE) {
|
||||||
uint64_t numSpheres = (uint64_t)_sphereCollection.size();
|
uint64_t numSpheres = (uint64_t)_sphereCollection.size();
|
||||||
_hashKey.hashUint64(numSpheres);
|
hashKey.hashUint64(numSpheres);
|
||||||
} else if (_type == SHAPE_TYPE_SIMPLE_HULL) {
|
} else if (_type == SHAPE_TYPE_SIMPLE_HULL) {
|
||||||
_hashKey.hashUint64(1);
|
hashKey.hashUint64(1);
|
||||||
}
|
}
|
||||||
|
_hash64 = hashKey.getHash64();
|
||||||
}
|
}
|
||||||
return _hashKey;
|
return _hash64;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) {
|
void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) {
|
||||||
_halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT));
|
_halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT));
|
||||||
_hashKey.clear();
|
_hash64 = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,6 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
#include "HashKey.h"
|
|
||||||
|
|
||||||
const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored
|
const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored
|
||||||
|
|
||||||
// Bullet has a mesh generation util for convex shapes that we used to
|
// Bullet has a mesh generation util for convex shapes that we used to
|
||||||
|
@ -91,7 +89,7 @@ public:
|
||||||
|
|
||||||
float computeVolume() const;
|
float computeVolume() const;
|
||||||
|
|
||||||
const HashKey& getHash() const;
|
uint64_t getHash() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setHalfExtents(const glm::vec3& halfExtents);
|
void setHalfExtents(const glm::vec3& halfExtents);
|
||||||
|
@ -102,7 +100,7 @@ protected:
|
||||||
TriangleIndices _triangleIndices;
|
TriangleIndices _triangleIndices;
|
||||||
glm::vec3 _halfExtents = glm::vec3(0.0f);
|
glm::vec3 _halfExtents = glm::vec3(0.0f);
|
||||||
glm::vec3 _offset = glm::vec3(0.0f);
|
glm::vec3 _offset = glm::vec3(0.0f);
|
||||||
mutable HashKey _hashKey;
|
mutable uint64_t _hash64;
|
||||||
ShapeType _type = SHAPE_TYPE_NONE;
|
ShapeType _type = SHAPE_TYPE_NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -55,20 +55,20 @@ void ShapeInfoTests::testHashFunctions() {
|
||||||
// test sphere
|
// test sphere
|
||||||
info.setSphere(radiusX);
|
info.setSphere(radiusX);
|
||||||
++testCount;
|
++testCount;
|
||||||
HashKey key = info.getHash();
|
HashKey hashKey(info.getHash());
|
||||||
hashPtr = hashes.find(key);
|
hashPtr = hashes.find(hashKey);
|
||||||
if (hashPtr) {
|
if (hashPtr) {
|
||||||
std::cout << testCount << " hash collision sphere radius = " << radiusX
|
std::cout << testCount << " hash collision sphere radius = " << radiusX
|
||||||
<< " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr
|
<< " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr
|
||||||
<< std::dec << std::endl;
|
<< std::dec << std::endl;
|
||||||
++numCollisions;
|
++numCollisions;
|
||||||
assert(false);
|
assert(false);
|
||||||
} else {
|
} else {
|
||||||
hashes.insert(key, key.getHash());
|
hashes.insert(hashKey, hashKey.getHash());
|
||||||
}
|
}
|
||||||
// track bit distribution counts to evaluate hash function randomness
|
// track bit distribution counts to evaluate hash function randomness
|
||||||
for (int j = 0; j < NUM_HASH_BITS; ++j) {
|
for (int j = 0; j < NUM_HASH_BITS; ++j) {
|
||||||
if (masks[j] & key.getHash()) {
|
if (masks[j] & hashKey.getHash()) {
|
||||||
++bits[j];
|
++bits[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,21 +80,21 @@ void ShapeInfoTests::testHashFunctions() {
|
||||||
// test box
|
// test box
|
||||||
info.setBox(glm::vec3(radiusX, radiusY, radiusZ));
|
info.setBox(glm::vec3(radiusX, radiusY, radiusZ));
|
||||||
++testCount;
|
++testCount;
|
||||||
HashKey key = info.getHash();
|
HashKey hashKey(info.getHash());
|
||||||
hashPtr = hashes.find(key);
|
hashPtr = hashes.find(hashKey);
|
||||||
if (hashPtr) {
|
if (hashPtr) {
|
||||||
std::cout << testCount << " hash collision box dimensions = < " << radiusX
|
std::cout << testCount << " hash collision box dimensions = < " << radiusX
|
||||||
<< ", " << radiusY << ", " << radiusZ << " >"
|
<< ", " << radiusY << ", " << radiusZ << " >"
|
||||||
<< " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr << " : 0x" << key.getHash64()
|
<< " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr << " : 0x" << hashKey.getHash64()
|
||||||
<< std::dec << std::endl;
|
<< std::dec << std::endl;
|
||||||
++numCollisions;
|
++numCollisions;
|
||||||
assert(false);
|
assert(false);
|
||||||
} else {
|
} else {
|
||||||
hashes.insert(key, key.getHash());
|
hashes.insert(hashKey, hashKey.getHash());
|
||||||
}
|
}
|
||||||
// track bit distribution counts to evaluate hash function randomness
|
// track bit distribution counts to evaluate hash function randomness
|
||||||
for (int k = 0; k < NUM_HASH_BITS; ++k) {
|
for (int k = 0; k < NUM_HASH_BITS; ++k) {
|
||||||
if (masks[k] & key.getHash()) {
|
if (masks[k] & hashKey.getHash()) {
|
||||||
++bits[k];
|
++bits[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,14 +117,14 @@ void ShapeInfoTests::testBoxShape() {
|
||||||
ShapeInfo info;
|
ShapeInfo info;
|
||||||
glm::vec3 halfExtents(1.23f, 4.56f, 7.89f);
|
glm::vec3 halfExtents(1.23f, 4.56f, 7.89f);
|
||||||
info.setBox(halfExtents);
|
info.setBox(halfExtents);
|
||||||
HashKey key = info.getHash();
|
HashKey hashKey(info.getHash());
|
||||||
|
|
||||||
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
QCOMPARE(shape != nullptr, true);
|
QCOMPARE(shape != nullptr, true);
|
||||||
|
|
||||||
ShapeInfo otherInfo = info;
|
ShapeInfo otherInfo = info;
|
||||||
HashKey otherKey = otherInfo.getHash();
|
HashKey otherKey = otherInfo.getHash();
|
||||||
QCOMPARE(key.getHash(), otherKey.getHash());
|
QCOMPARE(hashKey.getHash(), otherKey.getHash());
|
||||||
|
|
||||||
delete shape;
|
delete shape;
|
||||||
}
|
}
|
||||||
|
@ -133,14 +133,14 @@ void ShapeInfoTests::testSphereShape() {
|
||||||
ShapeInfo info;
|
ShapeInfo info;
|
||||||
float radius = 1.23f;
|
float radius = 1.23f;
|
||||||
info.setSphere(radius);
|
info.setSphere(radius);
|
||||||
HashKey key = info.getHash();
|
HashKey hashKey = info.getHash();
|
||||||
|
|
||||||
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
QCOMPARE(shape != nullptr, true);
|
QCOMPARE(shape != nullptr, true);
|
||||||
|
|
||||||
ShapeInfo otherInfo = info;
|
ShapeInfo otherInfo = info;
|
||||||
HashKey otherKey = otherInfo.getHash();
|
HashKey otherKey = otherInfo.getHash();
|
||||||
QCOMPARE(key.getHash(), otherKey.getHash());
|
QCOMPARE(hashKey.getHash(), otherKey.getHash());
|
||||||
|
|
||||||
delete shape;
|
delete shape;
|
||||||
}
|
}
|
||||||
|
@ -151,14 +151,14 @@ void ShapeInfoTests::testCylinderShape() {
|
||||||
float radius = 1.23f;
|
float radius = 1.23f;
|
||||||
float height = 4.56f;
|
float height = 4.56f;
|
||||||
info.setCylinder(radius, height);
|
info.setCylinder(radius, height);
|
||||||
HashKey key = info.getHash();
|
HashKey hashKey(info.getHash());
|
||||||
|
|
||||||
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
QCOMPARE(shape != nullptr, true);
|
QCOMPARE(shape != nullptr, true);
|
||||||
|
|
||||||
ShapeInfo otherInfo = info;
|
ShapeInfo otherInfo = info;
|
||||||
HashKey otherKey = otherInfo.getHash();
|
HashKey otherKey = otherInfo.getHash();
|
||||||
QCOMPARE(key.getHash(), otherKey.getHash());
|
QCOMPARE(hashKey.getHash(), otherKey.getHash());
|
||||||
|
|
||||||
delete shape;
|
delete shape;
|
||||||
*/
|
*/
|
||||||
|
@ -170,14 +170,14 @@ void ShapeInfoTests::testCapsuleShape() {
|
||||||
float radius = 1.23f;
|
float radius = 1.23f;
|
||||||
float height = 4.56f;
|
float height = 4.56f;
|
||||||
info.setCapsule(radius, height);
|
info.setCapsule(radius, height);
|
||||||
HashKey key = info.getHash();
|
HashKey hashKey(info.getHash());
|
||||||
|
|
||||||
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||||
QCOMPARE(shape != nullptr, true);
|
QCOMPARE(shape != nullptr, true);
|
||||||
|
|
||||||
ShapeInfo otherInfo = info;
|
ShapeInfo otherInfo = info;
|
||||||
HashKey otherKey = otherInfo.getHash();
|
HashKey otherKey = otherInfo.getHash();
|
||||||
QCOMPARE(key.getHash(), otherKey.getHash());
|
QCOMPARE(hashKey.getHash(), otherKey.getHash());
|
||||||
|
|
||||||
delete shape;
|
delete shape;
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue