mirror of
https://github.com/lubosz/overte.git
synced 2025-04-07 09:22:42 +02:00
cleanup ShapeInfo::getHash()
This commit is contained in:
parent
f21a284324
commit
45e571dd02
8 changed files with 202 additions and 193 deletions
|
@ -32,7 +32,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
|||
if (info.getType() == SHAPE_TYPE_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
DoubleHashKey key = info.getHash();
|
||||
HashKey key = info.getHash();
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef) {
|
||||
shapeRef->refCount++;
|
||||
|
@ -50,7 +50,7 @@ const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
|||
}
|
||||
|
||||
// private helper method
|
||||
bool ShapeManager::releaseShapeByKey(const DoubleHashKey& key) {
|
||||
bool ShapeManager::releaseShapeByKey(const HashKey& key) {
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef) {
|
||||
if (shapeRef->refCount > 0) {
|
||||
|
@ -88,7 +88,7 @@ bool ShapeManager::releaseShape(const btCollisionShape* shape) {
|
|||
void ShapeManager::collectGarbage() {
|
||||
int numShapes = _pendingGarbage.size();
|
||||
for (int i = 0; i < numShapes; ++i) {
|
||||
DoubleHashKey& key = _pendingGarbage[i];
|
||||
HashKey& key = _pendingGarbage[i];
|
||||
ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef && shapeRef->refCount == 0) {
|
||||
ShapeFactory::deleteShape(shapeRef->shape);
|
||||
|
@ -99,7 +99,7 @@ void ShapeManager::collectGarbage() {
|
|||
}
|
||||
|
||||
int ShapeManager::getNumReferences(const ShapeInfo& info) const {
|
||||
DoubleHashKey key = info.getHash();
|
||||
HashKey key = info.getHash();
|
||||
const ShapeReference* shapeRef = _shapeMap.find(key);
|
||||
if (shapeRef) {
|
||||
return shapeRef->refCount;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include <ShapeInfo.h>
|
||||
|
||||
#include "DoubleHashKey.h"
|
||||
#include "HashKey.h"
|
||||
|
||||
class ShapeManager {
|
||||
public:
|
||||
|
@ -41,18 +41,18 @@ public:
|
|||
bool hasShape(const btCollisionShape* shape) const;
|
||||
|
||||
private:
|
||||
bool releaseShapeByKey(const DoubleHashKey& key);
|
||||
bool releaseShapeByKey(const HashKey& key);
|
||||
|
||||
class ShapeReference {
|
||||
public:
|
||||
int refCount;
|
||||
const btCollisionShape* shape;
|
||||
DoubleHashKey key;
|
||||
HashKey key;
|
||||
ShapeReference() : refCount(0), shape(nullptr) {}
|
||||
};
|
||||
|
||||
btHashMap<DoubleHashKey, ShapeReference> _shapeMap;
|
||||
btAlignedObjectArray<DoubleHashKey> _pendingGarbage;
|
||||
btHashMap<HashKey, ShapeReference> _shapeMap;
|
||||
btAlignedObjectArray<HashKey> _pendingGarbage;
|
||||
};
|
||||
|
||||
#endif // hifi_ShapeManager_h
|
||||
|
|
67
libraries/shared/src/HashKey.cpp
Normal file
67
libraries/shared/src/HashKey.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// HashKey.cpp
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Andrew Meadows 2017.10.25
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
#include "HashKey.h"
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
|
||||
|
||||
const uint8_t NUM_PRIMES = 64;
|
||||
const uint64_t PRIMES[] = {
|
||||
4194301UL, 4194287UL, 4194277UL, 4194271UL, 4194247UL, 4194217UL, 4194199UL, 4194191UL,
|
||||
4194187UL, 4194181UL, 4194173UL, 4194167UL, 4194143UL, 4194137UL, 4194131UL, 4194107UL,
|
||||
4194103UL, 4194023UL, 4194011UL, 4194007UL, 4193977UL, 4193971UL, 4193963UL, 4193957UL,
|
||||
4193939UL, 4193929UL, 4193909UL, 4193869UL, 4193807UL, 4193803UL, 4193801UL, 4193789UL,
|
||||
4193759UL, 4193753UL, 4193743UL, 4193701UL, 4193663UL, 4193633UL, 4193573UL, 4193569UL,
|
||||
4193551UL, 4193549UL, 4193531UL, 4193513UL, 4193507UL, 4193459UL, 4193447UL, 4193443UL,
|
||||
4193417UL, 4193411UL, 4193393UL, 4193389UL, 4193381UL, 4193377UL, 4193369UL, 4193359UL,
|
||||
4193353UL, 4193327UL, 4193309UL, 4193303UL, 4193297UL, 4193279UL, 4193269UL, 4193263UL
|
||||
};
|
||||
|
||||
|
||||
// this hash function inspired by Squirrel Eiserloh's GDC2017 talk: "Noise-Based RNG"
|
||||
uint64_t squirrel3_64(uint64_t data, uint8_t primeIndex) {
|
||||
constexpr uint64_t BIT_NOISE1 = 2760725261486592643UL;
|
||||
constexpr uint64_t BIT_NOISE2 = 6774464464027632833UL;
|
||||
constexpr uint64_t BIT_NOISE3 = 5545331650366059883UL;
|
||||
|
||||
// blend prime numbers into the hash to prevent dupes
|
||||
// when hashing the same set of numbers in a different order
|
||||
uint64_t hash = PRIMES[primeIndex % NUM_PRIMES] * data;
|
||||
hash *= BIT_NOISE1;
|
||||
hash ^= (hash >> 16);
|
||||
hash += BIT_NOISE2;
|
||||
hash ^= (hash << 16);
|
||||
hash *= BIT_NOISE3;
|
||||
return hash ^ (hash >> 16);
|
||||
}
|
||||
|
||||
constexpr float QUANTIZED_VALUES_PER_METER = 250.0f;
|
||||
|
||||
// static
|
||||
float HashKey::getNumQuantizedValuesPerMeter() {
|
||||
return QUANTIZED_VALUES_PER_METER;
|
||||
}
|
||||
|
||||
void HashKey::hashUint64(uint64_t data) {
|
||||
_hash += squirrel3_64(data, ++_hashCount);
|
||||
}
|
||||
|
||||
void HashKey::hashFloat(float data) {
|
||||
_hash += squirrel3_64((uint64_t)((int64_t)(data * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
||||
}
|
||||
|
||||
void HashKey::hashVec3(const glm::vec3& data) {
|
||||
_hash += squirrel3_64((uint64_t)((int64_t)(data[0] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
||||
_hash *= squirrel3_64((uint64_t)((int64_t)(data[1] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
||||
_hash ^= squirrel3_64((uint64_t)((int64_t)(data[2] * QUANTIZED_VALUES_PER_METER)), ++_hashCount);
|
||||
}
|
||||
|
52
libraries/shared/src/HashKey.h
Normal file
52
libraries/shared/src/HashKey.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// HashKey.h
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Andrew Meadows 2017.10.25
|
||||
// Copyright 2017 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_HashKey_h
|
||||
#define hifi_HashKey_h
|
||||
|
||||
#include <cstddef>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
|
||||
// HashKey for use with btHashMap which requires a particular API for its keys. In particular it
|
||||
// requires its Key to implement these methods:
|
||||
//
|
||||
// bool Key::equals()
|
||||
// int32_t Key::getHash()
|
||||
//
|
||||
// The important thing about the HashKey implementation is that while getHash() returns 32-bits,
|
||||
// internally HashKey stores a 64-bit hash which is used for the equals() comparison. This allows
|
||||
// btHashMap to insert "dupe" 32-bit keys to different "values".
|
||||
|
||||
class HashKey {
|
||||
public:
|
||||
static float getNumQuantizedValuesPerMeter();
|
||||
|
||||
// These two methods are required by btHashMap.
|
||||
bool equals(const HashKey& other) const { return _hash == other._hash; }
|
||||
int32_t getHash() const { return (int32_t)((uint32_t)_hash); }
|
||||
|
||||
void clear() { _hash = _hashCount = 0; }
|
||||
bool isNull() const { return _hash == 0 && _hashCount == 0; }
|
||||
void hashUint64(uint64_t data);
|
||||
void hashFloat(float data);
|
||||
void hashVec3(const glm::vec3& data);
|
||||
|
||||
uint64_t getHash64() const { return _hash; } // for debug/test purposes
|
||||
|
||||
private:
|
||||
uint64_t _hash { 0 };
|
||||
uint8_t _hashCount { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_HashKey_h
|
|
@ -53,7 +53,7 @@ void ShapeInfo::clear() {
|
|||
_triangleIndices.clear();
|
||||
_halfExtents = glm::vec3(0.0f);
|
||||
_offset = glm::vec3(0.0f);
|
||||
_doubleHashKey.clear();
|
||||
_hashKey.clear();
|
||||
_type = SHAPE_TYPE_NONE;
|
||||
}
|
||||
|
||||
|
@ -87,14 +87,14 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
|
|||
default:
|
||||
break;
|
||||
}
|
||||
_doubleHashKey.clear();
|
||||
_hashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
|
||||
_url = "";
|
||||
_type = SHAPE_TYPE_BOX;
|
||||
setHalfExtents(halfExtents);
|
||||
_doubleHashKey.clear();
|
||||
_hashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setSphere(float radius) {
|
||||
|
@ -102,12 +102,12 @@ void ShapeInfo::setSphere(float radius) {
|
|||
_type = SHAPE_TYPE_SPHERE;
|
||||
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||
_halfExtents = glm::vec3(radius);
|
||||
_doubleHashKey.clear();
|
||||
_hashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
|
||||
_pointCollection = pointCollection;
|
||||
_doubleHashKey.clear();
|
||||
_hashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
||||
|
@ -116,12 +116,12 @@ void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
|
|||
radius = glm::max(radius, MIN_HALF_EXTENT);
|
||||
halfHeight = glm::max(halfHeight, 0.0f);
|
||||
_halfExtents = glm::vec3(radius, halfHeight, radius);
|
||||
_doubleHashKey.clear();
|
||||
_hashKey.clear();
|
||||
}
|
||||
|
||||
void ShapeInfo::setOffset(const glm::vec3& offset) {
|
||||
_offset = offset;
|
||||
_doubleHashKey.clear();
|
||||
_hashKey.clear();
|
||||
}
|
||||
|
||||
uint32_t ShapeInfo::getNumSubShapes() const {
|
||||
|
@ -256,119 +256,46 @@ bool ShapeInfo::contains(const glm::vec3& point) const {
|
|||
}
|
||||
}
|
||||
|
||||
const DoubleHashKey& ShapeInfo::getHash() const {
|
||||
const HashKey& ShapeInfo::getHash() const {
|
||||
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
|
||||
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {
|
||||
bool useOffset = glm::length2(_offset) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET;
|
||||
if (_hashKey.isNull() && _type != SHAPE_TYPE_NONE) {
|
||||
// The key is not yet cached therefore we must compute it.
|
||||
|
||||
// compute hash1
|
||||
// TODO?: provide lookup table for hash/hash2 of _type rather than recompute?
|
||||
uint32_t primeIndex = 0;
|
||||
_doubleHashKey.computeHash((uint32_t)_type, primeIndex++);
|
||||
|
||||
_hashKey.hashUint64((uint64_t)_type);
|
||||
if (_type != SHAPE_TYPE_SIMPLE_HULL) {
|
||||
// compute hash1
|
||||
uint32_t hash = _doubleHashKey.getHash();
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||
// so the cast to int produces a round() effect rather than a floor()
|
||||
hash ^= DoubleHashKey::hashFunction(
|
||||
(uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f),
|
||||
primeIndex++);
|
||||
if (useOffset) {
|
||||
hash ^= DoubleHashKey::hashFunction(
|
||||
(uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f),
|
||||
primeIndex++);
|
||||
}
|
||||
}
|
||||
_doubleHashKey.setHash(hash);
|
||||
|
||||
// compute hash2
|
||||
hash = _doubleHashKey.getHash2();
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||
// so the cast to int produces a round() effect rather than a floor()
|
||||
uint32_t floatHash = DoubleHashKey::hashFunction2(
|
||||
(uint32_t)(_halfExtents[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _halfExtents[j]) * 0.49f));
|
||||
if (useOffset) {
|
||||
floatHash ^= DoubleHashKey::hashFunction2(
|
||||
(uint32_t)(_offset[j] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[j]) * 0.49f));
|
||||
}
|
||||
hash += ~(floatHash << 17);
|
||||
hash ^= (floatHash >> 11);
|
||||
hash += (floatHash << 4);
|
||||
hash ^= (floatHash >> 7);
|
||||
hash += ~(floatHash << 10);
|
||||
hash = (hash << 16) | (hash >> 16);
|
||||
}
|
||||
_doubleHashKey.setHash2(hash);
|
||||
_hashKey.hashVec3(_halfExtents);
|
||||
_hashKey.hashVec3(_offset);
|
||||
} else {
|
||||
|
||||
// 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
|
||||
// url in the description.
|
||||
assert(_pointCollection.size() == (size_t)1);
|
||||
const PointList & points = _pointCollection.back();
|
||||
const int numPoints = (int)points.size();
|
||||
uint32_t hash = _doubleHashKey.getHash();
|
||||
uint32_t hash2 = _doubleHashKey.getHash2();
|
||||
|
||||
for (int pointIndex = 0; pointIndex < numPoints; ++pointIndex) {
|
||||
// compute hash1 & 2
|
||||
const glm::vec3 &curPoint = points[pointIndex];
|
||||
for (int vecCompIndex = 0; vecCompIndex < 3; ++vecCompIndex) {
|
||||
|
||||
// NOTE: 0.49f is used to bump the float up almost half a millimeter
|
||||
// so the cast to int produces a round() effect rather than a floor()
|
||||
uint32_t valueToHash = (uint32_t)(curPoint[vecCompIndex] * MILLIMETERS_PER_METER + copysignf(1.0f, curPoint[vecCompIndex]) * 0.49f);
|
||||
|
||||
hash ^= DoubleHashKey::hashFunction(valueToHash, primeIndex++);
|
||||
uint32_t floatHash = DoubleHashKey::hashFunction2(valueToHash);
|
||||
|
||||
if (useOffset) {
|
||||
|
||||
const uint32_t offsetValToHash = (uint32_t)(_offset[vecCompIndex] * MILLIMETERS_PER_METER + copysignf(1.0f, _offset[vecCompIndex])* 0.49f);
|
||||
|
||||
hash ^= DoubleHashKey::hashFunction(offsetValToHash, primeIndex++);
|
||||
floatHash ^= DoubleHashKey::hashFunction2(offsetValToHash);
|
||||
}
|
||||
|
||||
hash2 += ~(floatHash << 17);
|
||||
hash2 ^= (floatHash >> 11);
|
||||
hash2 += (floatHash << 4);
|
||||
hash2 ^= (floatHash >> 7);
|
||||
hash2 += ~(floatHash << 10);
|
||||
hash2 = (hash2 << 16) | (hash2 >> 16);
|
||||
}
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
_hashKey.hashVec3(points[i]);
|
||||
}
|
||||
|
||||
_doubleHashKey.setHash(hash);
|
||||
_doubleHashKey.setHash2(hash2);
|
||||
}
|
||||
|
||||
QString url = _url.toString();
|
||||
if (!url.isEmpty()) {
|
||||
// fold the urlHash into both parts
|
||||
QByteArray baUrl = url.toLocal8Bit();
|
||||
uint32_t urlHash = qChecksum(baUrl.data(), baUrl.size());
|
||||
_doubleHashKey.setHash(_doubleHashKey.getHash() ^ urlHash);
|
||||
_doubleHashKey.setHash2(_doubleHashKey.getHash2() ^ urlHash);
|
||||
_hashKey.hashUint64((uint64_t)urlHash);
|
||||
}
|
||||
|
||||
uint32_t numHulls = 0;
|
||||
if (_type == SHAPE_TYPE_COMPOUND || _type == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||
numHulls = (uint32_t)_pointCollection.size();
|
||||
uint64_t numHulls = (uint64_t)_pointCollection.size();
|
||||
_hashKey.hashUint64(numHulls);
|
||||
} else if (_type == SHAPE_TYPE_SIMPLE_HULL) {
|
||||
numHulls = 1;
|
||||
}
|
||||
if (numHulls > 0) {
|
||||
uint32_t hash = DoubleHashKey::hashFunction(numHulls, primeIndex++);
|
||||
_doubleHashKey.setHash(_doubleHashKey.getHash() ^ hash);
|
||||
hash = DoubleHashKey::hashFunction2(numHulls);
|
||||
_doubleHashKey.setHash2(_doubleHashKey.getHash2() ^ hash);
|
||||
_hashKey.hashUint64(1);
|
||||
}
|
||||
}
|
||||
return _doubleHashKey;
|
||||
return _hashKey;
|
||||
}
|
||||
|
||||
void ShapeInfo::setHalfExtents(const glm::vec3& halfExtents) {
|
||||
_halfExtents = glm::max(halfExtents, glm::vec3(MIN_HALF_EXTENT));
|
||||
_hashKey.clear();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "DoubleHashKey.h"
|
||||
#include "HashKey.h"
|
||||
|
||||
const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored
|
||||
|
||||
|
@ -89,7 +89,7 @@ public:
|
|||
/// For compound shapes it will only return whether it is inside the bounding box
|
||||
bool contains(const glm::vec3& point) const;
|
||||
|
||||
const DoubleHashKey& getHash() const;
|
||||
const HashKey& getHash() const;
|
||||
|
||||
protected:
|
||||
void setHalfExtents(const glm::vec3& halfExtents);
|
||||
|
@ -99,7 +99,7 @@ protected:
|
|||
TriangleIndices _triangleIndices;
|
||||
glm::vec3 _halfExtents = glm::vec3(0.0f);
|
||||
glm::vec3 _offset = glm::vec3(0.0f);
|
||||
mutable DoubleHashKey _doubleHashKey;
|
||||
mutable HashKey _hashKey;
|
||||
ShapeType _type = SHAPE_TYPE_NONE;
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <btBulletDynamicsCommon.h>
|
||||
#include <LinearMath/btHashMap.h>
|
||||
|
||||
#include <DoubleHashKey.h>
|
||||
#include <HashKey.h>
|
||||
#include <ShapeInfo.h>
|
||||
#include <ShapeFactory.h>
|
||||
#include <StreamUtils.h>
|
||||
|
@ -23,108 +23,78 @@
|
|||
|
||||
QTEST_MAIN(ShapeInfoTests)
|
||||
|
||||
// Enable this to manually run testHashCollisions
|
||||
// (NOT a regular unit test; takes ~40 secs to run on an i7)
|
||||
//#define MANUAL_TEST true
|
||||
|
||||
void ShapeInfoTests::testHashFunctions() {
|
||||
#if MANUAL_TEST
|
||||
int maxTests = 10000000;
|
||||
ShapeInfo info;
|
||||
btHashMap<btHashInt, uint32_t> hashes;
|
||||
btHashMap<HashKey, int32_t> hashes;
|
||||
|
||||
uint32_t bits[32];
|
||||
uint32_t masks[32];
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
const int32_t NUM_HASH_BITS = 32;
|
||||
uint32_t bits[NUM_HASH_BITS];
|
||||
uint32_t masks[NUM_HASH_BITS];
|
||||
for (int i = 0; i < NUM_HASH_BITS; ++i) {
|
||||
bits[i] = 0;
|
||||
masks[i] = 1U << i;
|
||||
masks[i] = 1UL << i;
|
||||
}
|
||||
|
||||
float deltaLength = 0.002f;
|
||||
float endLength = 100.0f;
|
||||
float deltaLength = 1.0f / (HashKey::getNumQuantizedValuesPerMeter() - 3.0f);
|
||||
float endLength = 2000.0f * deltaLength;
|
||||
int numSteps = (int)(endLength / deltaLength);
|
||||
|
||||
int testCount = 0;
|
||||
int numCollisions = 0;
|
||||
|
||||
btClock timer;
|
||||
for (int x = 1; x < numSteps && testCount < maxTests; ++x) {
|
||||
float radiusX = (float)x * deltaLength;
|
||||
for (int i = 1; i < numSteps && testCount < maxTests; ++i) {
|
||||
float radiusX = (float)i * deltaLength;
|
||||
int32_t* hashPtr;
|
||||
// test sphere
|
||||
info.setSphere(radiusX);
|
||||
++testCount;
|
||||
DoubleHashKey key = info.getHash();
|
||||
uint32_t* hashPtr = hashes.find(key.getHash());
|
||||
if (hashPtr && *hashPtr == key.getHash2()) {
|
||||
std::cout << testCount << " hash collision radiusX = " << radiusX
|
||||
<< " h1 = 0x" << std::hex << key.getHash()
|
||||
<< " h2 = 0x" << std::hex << key.getHash2()
|
||||
<< std::endl;
|
||||
HashKey key = info.getHash();
|
||||
hashPtr = hashes.find(key);
|
||||
if (hashPtr) {
|
||||
std::cout << testCount << " hash collision sphere radius = " << radiusX
|
||||
<< " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr
|
||||
<< std::dec << std::endl;
|
||||
++numCollisions;
|
||||
assert(false);
|
||||
} else {
|
||||
hashes.insert(key.getHash(), key.getHash2());
|
||||
hashes.insert(key, key.getHash());
|
||||
}
|
||||
for (int k = 0; k < 32; ++k) {
|
||||
if (masks[k] & key.getHash2()) {
|
||||
++bits[k];
|
||||
// track bit distribution counts to evaluate hash function randomness
|
||||
for (int j = 0; j < NUM_HASH_BITS; ++j) {
|
||||
if (masks[j] & key.getHash()) {
|
||||
++bits[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 1; y < numSteps && testCount < maxTests; ++y) {
|
||||
float radiusY = (float)y * deltaLength;
|
||||
/* TODO: reimplement Cylinder and Capsule shapes
|
||||
// test cylinder and capsule
|
||||
int types[] = { CYLINDER_SHAPE_PROXYTYPE, CAPSULE_SHAPE_PROXYTYPE };
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
switch(types[i]) {
|
||||
case CYLINDER_SHAPE_PROXYTYPE: {
|
||||
info.setCylinder(radiusX, radiusY);
|
||||
break;
|
||||
}
|
||||
case CAPSULE_SHAPE_PROXYTYPE: {
|
||||
info.setCapsuleY(radiusX, radiusY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++testCount;
|
||||
key = info.getHash();
|
||||
hashPtr = hashes.find(key.getHash());
|
||||
if (hashPtr && *hashPtr == key.getHash2()) {
|
||||
std::cout << testCount << " hash collision radiusX = " << radiusX << " radiusY = " << radiusY
|
||||
<< " h1 = 0x" << std::hex << key.getHash()
|
||||
<< " h2 = 0x" << std::hex << key.getHash2()
|
||||
<< std::endl;
|
||||
++numCollisions;
|
||||
assert(false);
|
||||
} else {
|
||||
hashes.insert(key.getHash(), key.getHash2());
|
||||
}
|
||||
for (int k = 0; k < 32; ++k) {
|
||||
if (masks[k] & key.getHash2()) {
|
||||
++bits[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for (int z = 1; z < numSteps && testCount < maxTests; ++z) {
|
||||
float radiusZ = (float)z * deltaLength;
|
||||
// test box
|
||||
info.setBox(glm::vec3(radiusX, radiusY, radiusZ));
|
||||
++testCount;
|
||||
DoubleHashKey key = info.getHash();
|
||||
hashPtr = hashes.find(key.getHash());
|
||||
if (hashPtr && *hashPtr == key.getHash2()) {
|
||||
std::cout << testCount << " hash collision radiusX = " << radiusX
|
||||
<< " radiusY = " << radiusY << " radiusZ = " << radiusZ
|
||||
<< " h1 = 0x" << std::hex << key.getHash()
|
||||
<< " h2 = 0x" << std::hex << key.getHash2()
|
||||
<< std::endl;
|
||||
HashKey key = info.getHash();
|
||||
hashPtr = hashes.find(key);
|
||||
if (hashPtr) {
|
||||
std::cout << testCount << " hash collision box dimensions = < " << radiusX
|
||||
<< ", " << radiusY << ", " << radiusZ << " >"
|
||||
<< " h = 0x" << std::hex << key.getHash() << " : 0x" << *hashPtr << " : 0x" << key.getHash64()
|
||||
<< std::dec << std::endl;
|
||||
++numCollisions;
|
||||
assert(false);
|
||||
} else {
|
||||
hashes.insert(key.getHash(), key.getHash2());
|
||||
hashes.insert(key, key.getHash());
|
||||
}
|
||||
for (int k = 0; k < 32; ++k) {
|
||||
if (masks[k] & key.getHash2()) {
|
||||
// track bit distribution counts to evaluate hash function randomness
|
||||
for (int k = 0; k < NUM_HASH_BITS; ++k) {
|
||||
if (masks[k] & key.getHash()) {
|
||||
++bits[k];
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +105,8 @@ void ShapeInfoTests::testHashFunctions() {
|
|||
std::cout << msec << " msec with " << numCollisions << " collisions out of " << testCount << " hashes" << std::endl;
|
||||
|
||||
// print out distribution of bits
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
// ideally the numbers in each bin will be about the same
|
||||
for (int i = 0; i < NUM_HASH_BITS; ++i) {
|
||||
std::cout << "bit 0x" << std::hex << masks[i] << std::dec << " = " << bits[i] << std::endl;
|
||||
}
|
||||
QCOMPARE(numCollisions, 0);
|
||||
|
@ -146,15 +117,14 @@ void ShapeInfoTests::testBoxShape() {
|
|||
ShapeInfo info;
|
||||
glm::vec3 halfExtents(1.23f, 4.56f, 7.89f);
|
||||
info.setBox(halfExtents);
|
||||
DoubleHashKey key = info.getHash();
|
||||
HashKey key = info.getHash();
|
||||
|
||||
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||
QCOMPARE(shape != nullptr, true);
|
||||
|
||||
ShapeInfo otherInfo = info;
|
||||
DoubleHashKey otherKey = otherInfo.getHash();
|
||||
HashKey otherKey = otherInfo.getHash();
|
||||
QCOMPARE(key.getHash(), otherKey.getHash());
|
||||
QCOMPARE(key.getHash2(), otherKey.getHash2());
|
||||
|
||||
delete shape;
|
||||
}
|
||||
|
@ -163,15 +133,14 @@ void ShapeInfoTests::testSphereShape() {
|
|||
ShapeInfo info;
|
||||
float radius = 1.23f;
|
||||
info.setSphere(radius);
|
||||
DoubleHashKey key = info.getHash();
|
||||
HashKey key = info.getHash();
|
||||
|
||||
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||
QCOMPARE(shape != nullptr, true);
|
||||
|
||||
ShapeInfo otherInfo = info;
|
||||
DoubleHashKey otherKey = otherInfo.getHash();
|
||||
HashKey otherKey = otherInfo.getHash();
|
||||
QCOMPARE(key.getHash(), otherKey.getHash());
|
||||
QCOMPARE(key.getHash2(), otherKey.getHash2());
|
||||
|
||||
delete shape;
|
||||
}
|
||||
|
@ -182,15 +151,14 @@ void ShapeInfoTests::testCylinderShape() {
|
|||
float radius = 1.23f;
|
||||
float height = 4.56f;
|
||||
info.setCylinder(radius, height);
|
||||
DoubleHashKey key = info.getHash();
|
||||
HashKey key = info.getHash();
|
||||
|
||||
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||
QCOMPARE(shape != nullptr, true);
|
||||
|
||||
ShapeInfo otherInfo = info;
|
||||
DoubleHashKey otherKey = otherInfo.getHash();
|
||||
HashKey otherKey = otherInfo.getHash();
|
||||
QCOMPARE(key.getHash(), otherKey.getHash());
|
||||
QCOMPARE(key.getHash2(), otherKey.getHash2());
|
||||
|
||||
delete shape;
|
||||
*/
|
||||
|
@ -202,15 +170,14 @@ void ShapeInfoTests::testCapsuleShape() {
|
|||
float radius = 1.23f;
|
||||
float height = 4.56f;
|
||||
info.setCapsule(radius, height);
|
||||
DoubleHashKey key = info.getHash();
|
||||
HashKey key = info.getHash();
|
||||
|
||||
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||
QCOMPARE(shape != nullptr, true);
|
||||
|
||||
ShapeInfo otherInfo = info;
|
||||
DoubleHashKey otherKey = otherInfo.getHash();
|
||||
HashKey otherKey = otherInfo.getHash();
|
||||
QCOMPARE(key.getHash(), otherKey.getHash());
|
||||
QCOMPARE(key.getHash2(), otherKey.getHash2());
|
||||
|
||||
delete shape;
|
||||
*/
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
//#include "BulletTestUtils.h"
|
||||
//#include "../QTestExtensions.h"
|
||||
|
||||
// Enable this to manually run testHashCollisions
|
||||
// (NOT a regular unit test; takes ~17 secs to run on an i7)
|
||||
#define MANUAL_TEST false
|
||||
|
||||
class ShapeInfoTests : public QObject {
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
|
|
Loading…
Reference in a new issue