cleanup ShapeInfo::getHash()

This commit is contained in:
Andrew Meadows 2017-10-26 08:16:41 -07:00
parent f21a284324
commit 45e571dd02
8 changed files with 202 additions and 193 deletions

View file

@ -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;

View file

@ -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

View 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);
}

View 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

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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;
*/

View file

@ -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: