// // ShapeInfoTests.cpp // tests/physics/src // // Created by Andrew Meadows on 2014.11.02 // 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 // #include "ShapeInfoTests.h" #include #include #include #include #include #include #include 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 hashes; 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] = 1UL << i; } 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 i = 1; i < numSteps && testCount < maxTests; ++i) { float radiusX = (float)i * deltaLength; int32_t* hashPtr; // test sphere info.setSphere(radiusX); ++testCount; HashKey hashKey(info.getHash()); hashPtr = hashes.find(hashKey); if (hashPtr) { std::cout << testCount << " hash collision sphere radius = " << radiusX << " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr << std::dec << std::endl; ++numCollisions; assert(false); } else { hashes.insert(hashKey, hashKey.getHash()); } // track bit distribution counts to evaluate hash function randomness for (int j = 0; j < NUM_HASH_BITS; ++j) { if (masks[j] & hashKey.getHash()) { ++bits[j]; } } for (int y = 1; y < numSteps && testCount < maxTests; ++y) { float radiusY = (float)y * deltaLength; for (int z = 1; z < numSteps && testCount < maxTests; ++z) { float radiusZ = (float)z * deltaLength; // test box info.setBox(glm::vec3(radiusX, radiusY, radiusZ)); ++testCount; HashKey hashKey(info.getHash()); hashPtr = hashes.find(hashKey); if (hashPtr) { std::cout << testCount << " hash collision box dimensions = < " << radiusX << ", " << radiusY << ", " << radiusZ << " >" << " h = 0x" << std::hex << hashKey.getHash() << " : 0x" << *hashPtr << " : 0x" << hashKey.getHash64() << std::dec << std::endl; ++numCollisions; assert(false); } else { hashes.insert(hashKey, hashKey.getHash()); } // track bit distribution counts to evaluate hash function randomness for (int k = 0; k < NUM_HASH_BITS; ++k) { if (masks[k] & hashKey.getHash()) { ++bits[k]; } } } } } uint64_t msec = timer.getTimeMilliseconds(); std::cout << msec << " msec with " << numCollisions << " collisions out of " << testCount << " hashes" << std::endl; // print out distribution of bits // 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); #endif // MANUAL_TEST } void ShapeInfoTests::testBoxShape() { ShapeInfo info; glm::vec3 halfExtents(1.23f, 4.56f, 7.89f); info.setBox(halfExtents); HashKey hashKey(info.getHash()); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; } void ShapeInfoTests::testSphereShape() { ShapeInfo info; float radius = 1.23f; info.setSphere(radius); HashKey hashKey = info.getHash(); const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; } void ShapeInfoTests::testCylinderShape() { /* TODO: reimplement Cylinder shape ShapeInfo info; float radius = 1.23f; float height = 4.56f; info.setCylinder(radius, height); HashKey hashKey(info.getHash()); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; */ } void ShapeInfoTests::testCapsuleShape() { /* TODO: reimplement Capsule shape ShapeInfo info; float radius = 1.23f; float height = 4.56f; info.setCapsule(radius, height); HashKey hashKey(info.getHash()); btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info); QCOMPARE(shape != nullptr, true); ShapeInfo otherInfo = info; HashKey otherKey = otherInfo.getHash(); QCOMPARE(hashKey.getHash(), otherKey.getHash()); delete shape; */ }