Merge pull request #2803 from ZappoMan/modelserver

Model Server Bug Fixes/Bandwidth improvements
This commit is contained in:
Clément Brisset 2014-05-06 17:18:25 -07:00
commit 72e6ff3649
12 changed files with 406 additions and 384 deletions

View file

@ -2032,7 +2032,7 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
// if this node is fully OUTSIDE the view, but previously intersected and/or was inside the last view, then
// we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse
// the children and simply mark them as hidden
args->tree->recurseNodeWithOperation(voxel, hideAllSubTreeOperation, args );
args->tree->recurseElementWithOperation(voxel, hideAllSubTreeOperation, args );
return false;
} break;
@ -2049,7 +2049,7 @@ bool VoxelSystem::hideOutOfViewOperation(OctreeElement* element, void* extraData
// if this node is fully INSIDE the view, but previously INTERSECTED and/or was OUTSIDE the last view, then
// we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse
// the children and simply mark them as visible (as appropriate based on LOD)
args->tree->recurseNodeWithOperation(voxel, showAllSubTreeOperation, args);
args->tree->recurseElementWithOperation(voxel, showAllSubTreeOperation, args);
return false;
} break;
case ViewFrustum::INTERSECT: {

View file

@ -12,7 +12,7 @@
#include "ModelTree.h"
ModelTree::ModelTree(bool shouldReaverage) : Octree(shouldReaverage) {
_rootNode = createNewElement();
_rootElement = createNewElement();
}
ModelTreeElement* ModelTree::createNewElement(unsigned char * octalCode) {
@ -74,31 +74,48 @@ bool ModelTree::findAndDeleteOperation(OctreeElement* element, void* extraData)
return true;
}
class FindAndUpdateModelArgs {
class FindAndUpdateModelOperator : public RecurseOctreeOperator {
public:
const ModelItem& searchModel;
bool found;
FindAndUpdateModelOperator(const ModelItem& searchModel);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
bool wasFound() const { return _found; }
private:
const ModelItem& _searchModel;
bool _found;
};
bool ModelTree::findAndUpdateOperation(OctreeElement* element, void* extraData) {
FindAndUpdateModelArgs* args = static_cast<FindAndUpdateModelArgs*>(extraData);
FindAndUpdateModelOperator::FindAndUpdateModelOperator(const ModelItem& searchModel) :
_searchModel(searchModel),
_found(false) {
};
bool FindAndUpdateModelOperator::PreRecursion(OctreeElement* element) {
ModelTreeElement* modelTreeElement = static_cast<ModelTreeElement*>(element);
// Note: updateModel() will only operate on correctly found models
if (modelTreeElement->updateModel(args->searchModel)) {
args->found = true;
if (modelTreeElement->updateModel(_searchModel)) {
_found = true;
return false; // stop searching
}
return true;
return !_found; // if we haven't yet found it, keep looking
}
bool FindAndUpdateModelOperator::PostRecursion(OctreeElement* element) {
if (_found) {
element->markWithChangedTime();
}
return !_found; // if we haven't yet found it, keep looking
}
void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& senderNode) {
// First, look for the existing model in the tree..
FindAndUpdateModelArgs args = { model, false };
recurseTreeWithOperation(findAndUpdateOperation, &args);
FindAndUpdateModelOperator theOperator(model);
recurseTreeWithOperator(&theOperator);
// if we didn't find it in the tree, then store it...
if (!args.found) {
if (!theOperator.wasFound()) {
glm::vec3 position = model.getPosition();
float size = std::max(MINIMUM_MODEL_ELEMENT_SIZE, model.getRadius());
@ -109,36 +126,49 @@ void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& send
_isDirty = true;
}
class FindAndUpdateModelWithIDandPropertiesArgs {
class FindAndUpdateModelWithIDandPropertiesOperator : public RecurseOctreeOperator {
public:
const ModelItemID& modelID;
const ModelItemProperties& properties;
bool found;
FindAndUpdateModelWithIDandPropertiesOperator(const ModelItemID& modelID, const ModelItemProperties& properties);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
bool wasFound() const { return _found; }
private:
const ModelItemID& _modelID;
const ModelItemProperties& _properties;
bool _found;
};
bool ModelTree::findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData) {
FindAndUpdateModelWithIDandPropertiesArgs* args = static_cast<FindAndUpdateModelWithIDandPropertiesArgs*>(extraData);
FindAndUpdateModelWithIDandPropertiesOperator::FindAndUpdateModelWithIDandPropertiesOperator(const ModelItemID& modelID,
const ModelItemProperties& properties) :
_modelID(modelID),
_properties(properties),
_found(false) {
};
bool FindAndUpdateModelWithIDandPropertiesOperator::PreRecursion(OctreeElement* element) {
ModelTreeElement* modelTreeElement = static_cast<ModelTreeElement*>(element);
// Note: updateModel() will only operate on correctly found models
if (modelTreeElement->updateModel(args->modelID, args->properties)) {
args->found = true;
if (modelTreeElement->updateModel(_modelID, _properties)) {
_found = true;
return false; // stop searching
}
return !_found; // if we haven't yet found it, keep looking
}
// if we've found our model stop searching
if (args->found) {
return false;
bool FindAndUpdateModelWithIDandPropertiesOperator::PostRecursion(OctreeElement* element) {
if (_found) {
element->markWithChangedTime();
}
return true;
return !_found; // if we haven't yet found it, keep looking
}
void ModelTree::updateModel(const ModelItemID& modelID, const ModelItemProperties& properties) {
// First, look for the existing model in the tree..
FindAndUpdateModelWithIDandPropertiesArgs args = { modelID, properties, false };
recurseTreeWithOperation(findAndUpdateWithIDandPropertiesOperation, &args);
// if we found it in the tree, then mark the tree as dirty
if (args.found) {
// Look for the existing model in the tree..
FindAndUpdateModelWithIDandPropertiesOperator theOperator(modelID, properties);
recurseTreeWithOperator(&theOperator);
if (theOperator.wasFound()) {
_isDirty = true;
}
}
@ -464,29 +494,12 @@ void ModelTree::update() {
lockForWrite();
_isDirty = true;
ModelTreeUpdateArgs args = { };
recurseTreeWithOperation(updateOperation, &args);
// TODO: we don't need to update models yet, but when we do, for example
// when we add animation support, we will revisit this code.
//ModelTreeUpdateArgs args = { };
//recurseTreeWithOperation(updateOperation, &args);
// now add back any of the models that moved elements....
int movingModels = args._movingModels.size();
for (int i = 0; i < movingModels; i++) {
bool shouldDie = args._movingModels[i].getShouldDie();
// if the model is still inside our total bounds, then re-add it
AABox treeBounds = getRoot()->getAABox();
if (!shouldDie && treeBounds.contains(args._movingModels[i].getPosition())) {
storeModel(args._movingModels[i]);
} else {
uint32_t modelID = args._movingModels[i].getID();
quint64 deletedAt = usecTimestampNow();
_recentlyDeletedModelsLock.lockForWrite();
_recentlyDeletedModelItemIDs.insert(deletedAt, modelID);
_recentlyDeletedModelsLock.unlock();
}
}
// prune the tree...
// Now is a reasonable time to prune the tree...
recurseTreeWithOperation(pruneOperation, NULL);
unlock();
}

View file

@ -29,7 +29,7 @@ public:
virtual ModelTreeElement* createNewElement(unsigned char * octalCode = NULL);
/// Type safe version of getRoot()
ModelTreeElement* getRoot() { return (ModelTreeElement*)_rootNode; }
ModelTreeElement* getRoot() { return static_cast<ModelTreeElement*>(_rootElement); }
// These methods will allow the OctreeServer to send your tree inbound edit packets of your

View file

@ -137,6 +137,7 @@ bool ModelTreeElement::updateModel(const ModelItem& model) {
difference, debug::valueOf(model.isNewlyCreated()) );
}
thisModel.copyChangedProperties(model);
markWithChangedTime();
} else {
if (wantDebug) {
qDebug(">>> IGNORING SERVER!!! Would've caused jutter! <<< "
@ -167,7 +168,7 @@ bool ModelTreeElement::updateModel(const ModelItemID& modelID, const ModelItemPr
}
if (found) {
thisModel.setProperties(properties);
markWithChangedTime(); // mark our element as changed..
const bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();

File diff suppressed because it is too large Load diff

View file

@ -36,8 +36,15 @@ class Shape;
#include <QObject>
#include <QReadWriteLock>
/// derive from this class to use the Octree::recurseTreeWithOperator() method
class RecurseOctreeOperator {
public:
virtual bool PreRecursion(OctreeElement* element) = 0;
virtual bool PostRecursion(OctreeElement* element) = 0;
};
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseOctreeOperation)(OctreeElement* node, void* extraData);
typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData);
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
const bool NO_EXISTS_BITS = false;
@ -159,7 +166,7 @@ class ReadBitstreamToTreeParams {
public:
bool includeColor;
bool includeExistsBits;
OctreeElement* destinationNode;
OctreeElement* destinationElement;
QUuid sourceUUID;
SharedNodePointer sourceNode;
bool wantImportProgress;
@ -167,13 +174,13 @@ public:
ReadBitstreamToTreeParams(
bool includeColor = WANT_COLOR,
bool includeExistsBits = WANT_EXISTS_BITS,
OctreeElement* destinationNode = NULL,
OctreeElement* destinationElement = NULL,
QUuid sourceUUID = QUuid(),
SharedNodePointer sourceNode = SharedNodePointer(),
bool wantImportProgress = false) :
includeColor(includeColor),
includeExistsBits(includeExistsBits),
destinationNode(destinationNode),
destinationElement(destinationElement),
sourceUUID(sourceUUID),
sourceNode(sourceNode),
wantImportProgress(wantImportProgress)
@ -200,14 +207,14 @@ public:
virtual void update() { }; // nothing to do by default
OctreeElement* getRoot() { return _rootNode; }
OctreeElement* getRoot() { return _rootElement; }
void eraseAllOctreeElements();
void processRemoveOctreeElementsBitstream(const unsigned char* bitstream, int bufferSizeBytes);
void readBitstreamToTree(const unsigned char* bitstream, unsigned long int bufferSizeBytes, ReadBitstreamToTreeParams& args);
void deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
void reaverageOctreeElements(OctreeElement* startNode = NULL);
void reaverageOctreeElements(OctreeElement* startElement = NULL);
void deleteOctreeElementAt(float x, float y, float z, float s);
@ -222,13 +229,14 @@ public:
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL);
void recurseTreeWithPostOperation(RecurseOctreeOperation operation, void* extraData = NULL);
void recurseTreeWithOperationDistanceSorted(RecurseOctreeOperation operation,
const glm::vec3& point, void* extraData = NULL);
int encodeTreeBitstream(OctreeElement* node, OctreePacketData* packetData, OctreeElementBag& bag,
void recurseTreeWithOperator(RecurseOctreeOperator* operatorObject);
int encodeTreeBitstream(OctreeElement* element, OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params) ;
bool isDirty() const { return _isDirty; }
@ -268,28 +276,30 @@ public:
void loadOctreeFile(const char* fileName, bool wantColorRandomizer);
// these will read/write files that match the wireformat, excluding the 'V' leading
void writeToSVOFile(const char* filename, OctreeElement* node = NULL);
void writeToSVOFile(const char* filename, OctreeElement* element = NULL);
bool readFromSVOFile(const char* filename);
unsigned long getOctreeElementsCount();
void copySubTreeIntoNewTree(OctreeElement* startNode, Octree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinationNode);
void copySubTreeIntoNewTree(OctreeElement* startElement, Octree* destinationTree, bool rebaseToRoot);
void copyFromTreeIntoSubTree(Octree* sourceTree, OctreeElement* destinationElement);
bool getShouldReaverage() const { return _shouldReaverage; }
void recurseNodeWithOperation(OctreeElement* node, RecurseOctreeOperation operation,
void recurseElementWithOperation(OctreeElement* element, RecurseOctreeOperation operation,
void* extraData, int recursionCount = 0);
/// Traverse child nodes of node applying operation in post-fix order
///
void recurseNodeWithPostOperation(OctreeElement* node, RecurseOctreeOperation operation,
void recurseElementWithPostOperation(OctreeElement* element, RecurseOctreeOperation operation,
void* extraData, int recursionCount = 0);
void recurseNodeWithOperationDistanceSorted(OctreeElement* node, RecurseOctreeOperation operation,
void recurseElementWithOperationDistanceSorted(OctreeElement* element, RecurseOctreeOperation operation,
const glm::vec3& point, void* extraData, int recursionCount = 0);
bool recurseElementWithOperator(OctreeElement* element, RecurseOctreeOperator* operatorObject, int recursionCount = 0);
bool getIsViewing() const { return _isViewing; }
void setIsViewing(bool isViewing) { _isViewing = isViewing; }
@ -302,21 +312,21 @@ public slots:
protected:
void deleteOctalCodeFromTreeRecursion(OctreeElement* node, void* extraData);
void deleteOctalCodeFromTreeRecursion(OctreeElement* element, void* extraData);
int encodeTreeBitstreamRecursion(OctreeElement* node,
int encodeTreeBitstreamRecursion(OctreeElement* element,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel,
const ViewFrustum::location& parentLocationThisView) const;
static bool countOctreeElementsOperation(OctreeElement* node, void* extraData);
static bool countOctreeElementsOperation(OctreeElement* element, void* extraData);
OctreeElement* nodeForOctalCode(OctreeElement* ancestorNode, const unsigned char* needleCode, OctreeElement** parentOfFoundNode) const;
OctreeElement* createMissingNode(OctreeElement* lastParentNode, const unsigned char* codeToReach);
int readNodeData(OctreeElement *destinationNode, const unsigned char* nodeData,
OctreeElement* nodeForOctalCode(OctreeElement* ancestorElement, const unsigned char* needleCode, OctreeElement** parentOfFoundElement) const;
OctreeElement* createMissingElement(OctreeElement* lastParentElement, const unsigned char* codeToReach);
int readElementData(OctreeElement *destinationElement, const unsigned char* nodeData,
int bufferSizeBytes, ReadBitstreamToTreeParams& args);
OctreeElement* _rootNode;
OctreeElement* _rootElement;
bool _isDirty;
bool _shouldReaverage;

View file

@ -28,6 +28,7 @@
typedef unsigned char OCTREE_PACKET_FLAGS;
typedef uint16_t OCTREE_PACKET_SEQUENCE;
const uint16_t MAX_OCTREE_PACKET_SEQUENCE = 65535;
typedef quint64 OCTREE_PACKET_SENT_TIME;
typedef uint16_t OCTREE_PACKET_INTERNAL_SECTION_SIZE;
const int MAX_OCTREE_PACKET_SIZE = MAX_PACKET_SIZE;

View file

@ -875,10 +875,13 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
return; // ignore any packets that are unreasonable
}
// determine our expected sequence number... handle rollover appropriately
OCTREE_PACKET_SEQUENCE expected = _incomingPacket > 0 ? _incomingLastSequence + 1 : sequence;
// Guard against possible corrupted packets... with bad sequence numbers
const int MAX_RESONABLE_SEQUENCE_OFFSET = 2000;
const int MIN_RESONABLE_SEQUENCE_OFFSET = -2000;
int sequenceOffset = (sequence - _incomingLastSequence);
int sequenceOffset = (sequence - expected);
if (sequenceOffset > MAX_RESONABLE_SEQUENCE_OFFSET || sequenceOffset < MIN_RESONABLE_SEQUENCE_OFFSET) {
qDebug() << "ignoring unreasonable packet... sequence:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
return; // ignore any packets that are unreasonable
@ -901,7 +904,6 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
qDebug() << "last packet duplicate got:" << sequence << "_incomingLastSequence:" << _incomingLastSequence;
}
} else {
OCTREE_PACKET_SEQUENCE expected = _incomingLastSequence+1;
if (sequence != expected) {
if (wantExtraDebugging) {
qDebug() << "out of order... got:" << sequence << "expected:" << expected;
@ -958,9 +960,9 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
}
}
// only bump the last sequence if it was greater than our previous last sequence, this will keep us from
// only bump the last sequence if it was greater than our expected sequence, this will keep us from
// accidentally going backwards when an out of order (recovered) packet comes in
if (sequence > _incomingLastSequence) {
if (sequence >= expected) {
_incomingLastSequence = sequence;
}

View file

@ -12,7 +12,7 @@
#include "ParticleTree.h"
ParticleTree::ParticleTree(bool shouldReaverage) : Octree(shouldReaverage) {
_rootNode = createNewElement();
_rootElement = createNewElement();
}
ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) {

View file

@ -29,7 +29,7 @@ public:
virtual ParticleTreeElement* createNewElement(unsigned char * octalCode = NULL);
/// Type safe version of getRoot()
ParticleTreeElement* getRoot() { return (ParticleTreeElement*)_rootNode; }
ParticleTreeElement* getRoot() { return static_cast<ParticleTreeElement*>(_rootElement); }
// These methods will allow the OctreeServer to send your tree inbound edit packets of your

View file

@ -23,13 +23,13 @@
VoxelTree::VoxelTree(bool shouldReaverage) : Octree(shouldReaverage)
{
_rootNode = createNewElement();
_rootElement = createNewElement();
}
VoxelTreeElement* VoxelTree::createNewElement(unsigned char * octalCode) {
VoxelSystem* voxelSystem = NULL;
if (_rootNode) {
voxelSystem = ((VoxelTreeElement*)_rootNode)->getVoxelSystem();
if (_rootElement) {
voxelSystem = (static_cast<VoxelTreeElement*>(_rootElement))->getVoxelSystem();
}
VoxelTreeElement* newElement = new VoxelTreeElement(octalCode);
newElement->setVoxelSystem(voxelSystem);

View file

@ -26,7 +26,7 @@ public:
VoxelTree(bool shouldReaverage = false);
virtual VoxelTreeElement* createNewElement(unsigned char * octalCode = NULL);
VoxelTreeElement* getRoot() { return (VoxelTreeElement*)_rootNode; }
VoxelTreeElement* getRoot() { return static_cast<VoxelTreeElement*>(_rootElement); }
void deleteVoxelAt(float x, float y, float z, float s);