use QHash, not QVector, for Octree content query

This commit is contained in:
Andrew Meadows 2014-09-04 15:20:07 -07:00
parent e120697a9b
commit faf31f268e
4 changed files with 53 additions and 68 deletions

View file

@ -29,11 +29,12 @@ void VoxelShapeManager::stepForward(float deltaTime) {
if (simulation) { if (simulation) {
glm::vec3 simulationOrigin = simulation->getTranslation(); glm::vec3 simulationOrigin = simulation->getTranslation();
if (glm::distance2(_lastSimulationTranslation, simulationOrigin) > EPSILON) { if (glm::distance2(_lastSimulationTranslation, simulationOrigin) > EPSILON) {
int numVoxels = _voxels.size(); VoxelPool::const_iterator voxelItr = _voxels.constBegin();
for (int i = 0; i < numVoxels; ++i) { while (voxelItr != _voxels.constEnd()) {
// the shape's position is stored in the simulation-frame // the shape's position is stored in the simulation-frame
glm::vec3 cubeCenter = _voxels[i]._cube.calcCenter(); const VoxelInfo& voxel = voxelItr.value();
_voxels[i]._shape->setTranslation(cubeCenter - simulationOrigin); voxel._shape->setTranslation(voxel._cube.calcCenter() - simulationOrigin);
++voxelItr;
} }
_lastSimulationTranslation = simulationOrigin; _lastSimulationTranslation = simulationOrigin;
} }
@ -44,9 +45,10 @@ void VoxelShapeManager::buildShapes() {
// the shapes are owned by the elements of _voxels, // the shapes are owned by the elements of _voxels,
// so _shapes is constructed by harvesting them from _voxels // so _shapes is constructed by harvesting them from _voxels
_shapes.clear(); _shapes.clear();
int numVoxels = _voxels.size(); VoxelPool::const_iterator voxelItr = _voxels.constBegin();
for (int i = 0; i < numVoxels; ++i) { while (voxelItr != _voxels.constEnd()) {
_shapes.push_back(_voxels[i]._shape); _shapes.push_back(voxelItr.value()._shape);
++voxelItr;
} }
} }
@ -60,72 +62,38 @@ void VoxelShapeManager::updateVoxels(CubeList& cubes) {
if (!simulation) { if (!simulation) {
return; return;
} }
// sort incoming cubes
qSort(cubes);
// Some of the cubes are new, others already exist, and some old voxels no longer exist. int numChanges = 0;
// Since both lists are sorted we walk them simultaneously looking for matches. VoxelPool::iterator voxelItr = _voxels.begin();
QVector<int> cubesToAdd; while (voxelItr != _voxels.end()) {
QVector<int> voxelsToRemove; // look for this voxel in cubes
int numCubes = cubes.size(); CubeList::iterator cubeItr = cubes.find(voxelItr.key());
int numVoxels = _voxels.size(); if (cubeItr == cubes.end()) {
int j = 0; // did not find it --> remove the voxel
for (int i = 0; i < numCubes; ++i) { simulation->removeShape(voxelItr.value()._shape);
while (j < numVoxels && _voxels[j]._cube < cubes[i]) { voxelItr = _voxels.erase(voxelItr);
// remove non-matching voxels not found in cubes ++numChanges;
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);
}
} else { } else {
// all existing voxels have already been processed, so this one is new // found it --> remove the cube
cubesToAdd.push_back(i); 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 // add remaining cubes to _voxels
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
glm::vec3 simulationOrigin = simulation->getTranslation(); glm::vec3 simulationOrigin = simulation->getTranslation();
for (int i = 0; i < cubesToAdd.size(); ++i) { CubeList::const_iterator cubeItr = cubes.constBegin();
const AACube& cube = cubes[cubesToAdd[i]]; while (cubeItr != cubes.constEnd()) {
// NOTE: shape's position is in simulation frame AACube cube = cubeItr.value();
AACubeShape* shape = new AACubeShape(cube.getScale(), cube.calcCenter() - simulationOrigin); AACubeShape* shape = new AACubeShape(cube.getScale(), cube.calcCenter() - simulationOrigin);
shape->setEntity(this); shape->setEntity(this);
VoxelInfo voxel = { cube, shape }; VoxelInfo voxel = {cube, shape };
_voxels.push_back(voxel); _voxels.insert(cubeItr.key(), voxel);
++numVoxels; ++numChanges;
++cubeItr;
} }
if (cubesToAdd.size() > 0 || voxelsToRemove.size() > 0) { if (numChanges > 0) {
// keep _voxels sorted
qSort(_voxels);
buildShapes(); buildShapes();
} }
} }

View file

@ -12,6 +12,8 @@
#ifndef hifi_VoxelShapeManager_h #ifndef hifi_VoxelShapeManager_h
#define hifi_VoxelShapeManager_h #define hifi_VoxelShapeManager_h
#include <QHash>
#include <AACube.h> #include <AACube.h>
#include <PhysicsEntity.h> #include <PhysicsEntity.h>
#include <Octree.h> #include <Octree.h>
@ -22,11 +24,12 @@ class AACubeShape;
class VoxelInfo{ class VoxelInfo{
public: public:
bool operator<(const VoxelInfo& otherVoxel) const { return _cube < otherVoxel._cube; }
AACube _cube; AACube _cube;
AACubeShape* _shape; AACubeShape* _shape;
}; };
typedef QHash<quint64, VoxelInfo> VoxelPool;
class VoxelShapeManager : public PhysicsEntity { class VoxelShapeManager : public PhysicsEntity {
public: public:
VoxelShapeManager(); VoxelShapeManager();
@ -42,7 +45,7 @@ public:
private: private:
glm::vec3 _lastSimulationTranslation; glm::vec3 _lastSimulationTranslation;
QVector<VoxelInfo> _voxels; VoxelPool _voxels;
}; };
#endif // hifi_VoxelShapeManager_h #endif // hifi_VoxelShapeManager_h

View file

@ -793,6 +793,19 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) {
return false; 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) { bool findContentInCubeOp(OctreeElement* element, void* extraData) {
ContentArgs* args = static_cast<ContentArgs*>(extraData); ContentArgs* args = static_cast<ContentArgs*>(extraData);
@ -806,7 +819,8 @@ bool findContentInCubeOp(OctreeElement* element, void* extraData) {
return true; // recurse on children return true; // recurse on children
} }
if (element->hasContent()) { 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 true;
} }
return false; return false;

View file

@ -33,9 +33,9 @@ class Shape;
#include <CollisionInfo.h> #include <CollisionInfo.h>
#include <QHash>
#include <QObject> #include <QObject>
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QVector>
/// derive from this class to use the Octree::recurseTreeWithOperator() method /// derive from this class to use the Octree::recurseTreeWithOperator() method
class RecurseOctreeOperator { class RecurseOctreeOperator {
@ -48,7 +48,7 @@ public:
// Callback function, for recuseTreeWithOperation // Callback function, for recuseTreeWithOperation
typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData); typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData);
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
typedef QVector<AACube> CubeList; typedef QHash<quint64, AACube> CubeList;
const bool NO_EXISTS_BITS = false; const bool NO_EXISTS_BITS = false;
const bool WANT_EXISTS_BITS = true; const bool WANT_EXISTS_BITS = true;