From faf31f268ec197201b4f05e3308ecb9e57c23735 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 4 Sep 2014 15:20:07 -0700 Subject: [PATCH] use QHash, not QVector, for Octree content query --- interface/src/avatar/VoxelShapeManager.cpp | 94 +++++++--------------- interface/src/avatar/VoxelShapeManager.h | 7 +- libraries/octree/src/Octree.cpp | 16 +++- libraries/octree/src/Octree.h | 4 +- 4 files changed, 53 insertions(+), 68 deletions(-) diff --git a/interface/src/avatar/VoxelShapeManager.cpp b/interface/src/avatar/VoxelShapeManager.cpp index 782fa3fa71..a73679a2c4 100644 --- a/interface/src/avatar/VoxelShapeManager.cpp +++ b/interface/src/avatar/VoxelShapeManager.cpp @@ -29,11 +29,12 @@ void VoxelShapeManager::stepForward(float deltaTime) { if (simulation) { glm::vec3 simulationOrigin = simulation->getTranslation(); if (glm::distance2(_lastSimulationTranslation, simulationOrigin) > EPSILON) { - int numVoxels = _voxels.size(); - for (int i = 0; i < numVoxels; ++i) { + VoxelPool::const_iterator voxelItr = _voxels.constBegin(); + while (voxelItr != _voxels.constEnd()) { // the shape's position is stored in the simulation-frame - glm::vec3 cubeCenter = _voxels[i]._cube.calcCenter(); - _voxels[i]._shape->setTranslation(cubeCenter - simulationOrigin); + const VoxelInfo& voxel = voxelItr.value(); + voxel._shape->setTranslation(voxel._cube.calcCenter() - simulationOrigin); + ++voxelItr; } _lastSimulationTranslation = simulationOrigin; } @@ -44,9 +45,10 @@ void VoxelShapeManager::buildShapes() { // the shapes are owned by the elements of _voxels, // so _shapes is constructed by harvesting them from _voxels _shapes.clear(); - int numVoxels = _voxels.size(); - for (int i = 0; i < numVoxels; ++i) { - _shapes.push_back(_voxels[i]._shape); + VoxelPool::const_iterator voxelItr = _voxels.constBegin(); + while (voxelItr != _voxels.constEnd()) { + _shapes.push_back(voxelItr.value()._shape); + ++voxelItr; } } @@ -60,72 +62,38 @@ void VoxelShapeManager::updateVoxels(CubeList& cubes) { if (!simulation) { return; } - // sort incoming cubes - qSort(cubes); - // Some of the cubes are new, others already exist, and some old voxels no longer exist. - // Since both lists are sorted we walk them simultaneously looking for matches. - QVector cubesToAdd; - QVector voxelsToRemove; - int numCubes = cubes.size(); - int numVoxels = _voxels.size(); - int j = 0; - for (int i = 0; i < numCubes; ++i) { - while (j < numVoxels && _voxels[j]._cube < cubes[i]) { - // remove non-matching voxels not found in cubes - voxelsToRemove.push_back(j++); - } - if (j < numVoxels) { - if (glm::distance2(cubes[i].getCorner(), _voxels[j]._cube.getCorner()) < EPSILON) { - if (cubes[i].getScale() != _voxels[j]._cube.getScale()) { - // the voxel changed scale so we replace - voxelsToRemove.push_back(j++); - cubesToAdd.push_back(i); - } else { - // the voxel already exists - ++j; - } - } else { - // the voxel doesn't exist yet - cubesToAdd.push_back(i); - } + int numChanges = 0; + VoxelPool::iterator voxelItr = _voxels.begin(); + while (voxelItr != _voxels.end()) { + // look for this voxel in cubes + CubeList::iterator cubeItr = cubes.find(voxelItr.key()); + if (cubeItr == cubes.end()) { + // did not find it --> remove the voxel + simulation->removeShape(voxelItr.value()._shape); + voxelItr = _voxels.erase(voxelItr); + ++numChanges; } else { - // all existing voxels have already been processed, so this one is new - cubesToAdd.push_back(i); + // found it --> remove the cube + cubes.erase(cubeItr); + voxelItr++; } } - while (j < numVoxels) { - // remove non-matching voxels at the end - voxelsToRemove.push_back(j++); - } - // remove voxels identified as old, from back to front - for (int i = voxelsToRemove.size() - 1; i >= 0; --i) { - int k = voxelsToRemove[i]; - simulation->removeShape(_voxels[k]._shape); - if (k < numVoxels - 1) { - // copy the last voxel into this spot - _voxels[k] = _voxels[numVoxels - 1]; - } - _voxels.pop_back(); - --numVoxels; - } - - // add new voxels + // add remaining cubes to _voxels glm::vec3 simulationOrigin = simulation->getTranslation(); - for (int i = 0; i < cubesToAdd.size(); ++i) { - const AACube& cube = cubes[cubesToAdd[i]]; - // NOTE: shape's position is in simulation frame + CubeList::const_iterator cubeItr = cubes.constBegin(); + while (cubeItr != cubes.constEnd()) { + AACube cube = cubeItr.value(); AACubeShape* shape = new AACubeShape(cube.getScale(), cube.calcCenter() - simulationOrigin); shape->setEntity(this); - VoxelInfo voxel = { cube, shape }; - _voxels.push_back(voxel); - ++numVoxels; + VoxelInfo voxel = {cube, shape }; + _voxels.insert(cubeItr.key(), voxel); + ++numChanges; + ++cubeItr; } - if (cubesToAdd.size() > 0 || voxelsToRemove.size() > 0) { - // keep _voxels sorted - qSort(_voxels); + if (numChanges > 0) { buildShapes(); } } diff --git a/interface/src/avatar/VoxelShapeManager.h b/interface/src/avatar/VoxelShapeManager.h index d5642801b9..1b7179788d 100644 --- a/interface/src/avatar/VoxelShapeManager.h +++ b/interface/src/avatar/VoxelShapeManager.h @@ -12,6 +12,8 @@ #ifndef hifi_VoxelShapeManager_h #define hifi_VoxelShapeManager_h +#include + #include #include #include @@ -22,11 +24,12 @@ class AACubeShape; class VoxelInfo{ public: - bool operator<(const VoxelInfo& otherVoxel) const { return _cube < otherVoxel._cube; } AACube _cube; AACubeShape* _shape; }; +typedef QHash VoxelPool; + class VoxelShapeManager : public PhysicsEntity { public: VoxelShapeManager(); @@ -42,7 +45,7 @@ public: private: glm::vec3 _lastSimulationTranslation; - QVector _voxels; + VoxelPool _voxels; }; #endif // hifi_VoxelShapeManager_h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 58e5871aca..3611605515 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -793,6 +793,19 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { return false; } +quint64 cubeListHashKey(const glm::vec3& point) { + // NOTE: TREE_SCALE = 16384 (15 bits) and multiplier is 1024 (11 bits), + // so each component (26 bits) uses more than its alloted 21 bits. + // however we don't expect to span huge cubes so it is ok if we wrap + // (every 2^21 / 2^10 = 2048 meters). + const uint BITS_PER_COMPONENT = 21; + const quint64 MAX_SCALED_COMPONENT = 2097152; // 2^21 + const float RESOLUTION_PER_METER = 1024.0f; // 2^10 + return (quint64)(point.x * RESOLUTION_PER_METER) % MAX_SCALED_COMPONENT + + (((quint64)(point.y * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << BITS_PER_COMPONENT) + + (((quint64)(point.z * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << 2 * BITS_PER_COMPONENT); +} + bool findContentInCubeOp(OctreeElement* element, void* extraData) { ContentArgs* args = static_cast(extraData); @@ -806,7 +819,8 @@ bool findContentInCubeOp(OctreeElement* element, void* extraData) { return true; // recurse on children } if (element->hasContent()) { - args->cubes->push_back(cube); + // NOTE: the voxel's center is unique so we use it as the input for the key + args->cubes->insert(cubeListHashKey(cube.calcCenter()), cube); return true; } return false; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index d877e68737..f58b5f0cbd 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -33,9 +33,9 @@ class Shape; #include +#include #include #include -#include /// derive from this class to use the Octree::recurseTreeWithOperator() method class RecurseOctreeOperator { @@ -48,7 +48,7 @@ public: // Callback function, for recuseTreeWithOperation typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData); typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; -typedef QVector CubeList; +typedef QHash CubeList; const bool NO_EXISTS_BITS = false; const bool WANT_EXISTS_BITS = true;