bool sendVoxelsOperation(OctreeElement* element, void* extraData) { VoxelTreeElement* voxel = static_cast<VoxelTreeElement*>(element); SendVoxelsOperationArgs* args = static_cast<SendVoxelsOperationArgs*>(extraData); if (voxel->isColored()) { const unsigned char* nodeOctalCode = voxel->getOctalCode(); unsigned char* codeColorBuffer = NULL; int codeLength = 0; int bytesInCode = 0; int codeAndColorLength; // If the newBase is NULL, then don't rebase if (args->newBaseOctCode) { codeColorBuffer = rebaseOctalCode(nodeOctalCode, args->newBaseOctCode, true); codeLength = numberOfThreeBitSectionsInCode(codeColorBuffer); bytesInCode = bytesRequiredForCodeLength(codeLength); codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA; } else { codeLength = numberOfThreeBitSectionsInCode(nodeOctalCode); bytesInCode = bytesRequiredForCodeLength(codeLength); codeAndColorLength = bytesInCode + SIZE_OF_COLOR_DATA; codeColorBuffer = new unsigned char[codeAndColorLength]; memcpy(codeColorBuffer, nodeOctalCode, bytesInCode); } // copy the colors over codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX]; codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX]; codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX]; args->packetSender->queueVoxelEditMessage(PacketTypeVoxelSetDestructive, codeColorBuffer, codeAndColorLength); delete[] codeColorBuffer; } return true; // keep going }
int OctreeSceneStats::packIntoPacket() { _statsPacket->reset(); _statsPacket->writePrimitive(_start); _statsPacket->writePrimitive(_end); _statsPacket->writePrimitive(_elapsed); _statsPacket->writePrimitive(_totalEncodeTime); _statsPacket->writePrimitive(_isFullScene); _statsPacket->writePrimitive(_isMoving); _statsPacket->writePrimitive(_packets); _statsPacket->writePrimitive(_bytes); _statsPacket->writePrimitive(_totalInternal); _statsPacket->writePrimitive(_totalLeaves); _statsPacket->writePrimitive(_internal); _statsPacket->writePrimitive(_leaves); _statsPacket->writePrimitive(_internalSkippedDistance); _statsPacket->writePrimitive(_leavesSkippedDistance); _statsPacket->writePrimitive(_internalSkippedOutOfView); _statsPacket->writePrimitive(_leavesSkippedOutOfView); _statsPacket->writePrimitive(_internalSkippedWasInView); _statsPacket->writePrimitive(_leavesSkippedWasInView); _statsPacket->writePrimitive(_internalSkippedNoChange); _statsPacket->writePrimitive(_leavesSkippedNoChange); _statsPacket->writePrimitive(_internalSkippedOccluded); _statsPacket->writePrimitive(_leavesSkippedOccluded); _statsPacket->writePrimitive(_internalColorSent); _statsPacket->writePrimitive(_leavesColorSent); _statsPacket->writePrimitive(_internalDidntFit); _statsPacket->writePrimitive(_leavesDidntFit); _statsPacket->writePrimitive(_colorBitsWritten); _statsPacket->writePrimitive(_existsBitsWritten); _statsPacket->writePrimitive(_existsInPacketBitsWritten); _statsPacket->writePrimitive(_treesRemoved); // add the root jurisdiction if (_jurisdictionRoot) { // copy the int bytes = (int)bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_jurisdictionRoot)); _statsPacket->writePrimitive(bytes); _statsPacket->write(reinterpret_cast<char*>(_jurisdictionRoot), bytes); // if and only if there's a root jurisdiction, also include the end elements int endNodeCount = (int)_jurisdictionEndNodes.size(); _statsPacket->writePrimitive(endNodeCount); for (int i=0; i < endNodeCount; i++) { unsigned char* endNodeCode = _jurisdictionEndNodes[i]; auto bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode)); _statsPacket->writePrimitive(bytes); _statsPacket->write(reinterpret_cast<char*>(endNodeCode), bytes); } } else { int bytes = 0; _statsPacket->writePrimitive(bytes); } return _statsPacket->getPayloadSize(); }
// adjust any internal timestamps to fix clock skew for this server void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { unsigned char* dataAt = codeColorBuffer; int octets = numberOfThreeBitSectionsInCode(dataAt); int lengthOfOctcode = bytesRequiredForCodeLength(octets); dataAt += lengthOfOctcode; // id uint32_t id; memcpy(&id, dataAt, sizeof(id)); dataAt += sizeof(id); // special case for handling "new" modelItems if (id == NEW_MODEL) { // If this is a NEW_MODEL, then we assume that there's an additional uint32_t creatorToken, that // we want to send back to the creator as an map to the actual id dataAt += sizeof(uint32_t); } // lastEdited quint64 lastEditedInLocalTime; memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime)); quint64 lastEditedInServerTime = lastEditedInLocalTime + clockSkew; memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime)); const bool wantDebug = false; if (wantDebug) { qDebug("ModelItem::adjustEditPacketForClockSkew()..."); qDebug() << " lastEditedInLocalTime: " << lastEditedInLocalTime; qDebug() << " clockSkew: " << clockSkew; qDebug() << " lastEditedInServerTime: " << lastEditedInServerTime; } }
void OctreeSceneStats::sceneStarted(bool isFullScene, bool isMoving, OctreeElementPointer root, JurisdictionMap* jurisdictionMap) { reset(); // resets packet and octree stats _isStarted = true; _start = usecTimestampNow(); _totalElements = OctreeElement::getNodeCount(); _totalInternal = OctreeElement::getInternalNodeCount(); _totalLeaves = OctreeElement::getLeafNodeCount(); _isFullScene = isFullScene; _isMoving = isMoving; if (_jurisdictionRoot) { delete[] _jurisdictionRoot; _jurisdictionRoot = NULL; } // clear existing endNodes before copying new ones... for (size_t i=0; i < _jurisdictionEndNodes.size(); i++) { if (_jurisdictionEndNodes[i]) { delete[] _jurisdictionEndNodes[i]; } } _jurisdictionEndNodes.clear(); // setup jurisdictions if (jurisdictionMap) { unsigned char* jurisdictionRoot = jurisdictionMap->getRootOctalCode(); if (jurisdictionRoot) { auto bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(jurisdictionRoot)); _jurisdictionRoot = new unsigned char[bytes]; memcpy(_jurisdictionRoot, jurisdictionRoot, bytes); } // copy new endNodes... for (int i = 0; i < jurisdictionMap->getEndNodeCount(); i++) { unsigned char* endNodeCode = jurisdictionMap->getEndNodeOctalCode(i); if (endNodeCode) { auto bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode)); unsigned char* endNodeCodeCopy = new unsigned char[bytes]; memcpy(endNodeCodeCopy, endNodeCode, bytes); _jurisdictionEndNodes.push_back(endNodeCodeCopy); } } } }
void VoxelTree::readCodeColorBufferToTree(const unsigned char* codeColorBuffer, bool destructive) { ReadCodeColorBufferToTreeArgs args; args.codeColorBuffer = codeColorBuffer; args.lengthOfCode = numberOfThreeBitSectionsInCode(codeColorBuffer); args.destructive = destructive; args.pathChanged = false; VoxelTreeElement* node = getRoot(); readCodeColorBufferToTreeRecursion(node, args); }
void OctreeElement::calculateAACube() { // copy corner into cube glm::vec3 corner; copyFirstVertexForCode(getOctalCode(), (float*)&corner); // this tells you the "size" of the voxel float voxelScale = (float)TREE_SCALE / powf(2.0f, numberOfThreeBitSectionsInCode(getOctalCode())); corner *= (float)TREE_SCALE; _cube.setBox(corner, voxelScale); }
void JurisdictionMap::copyContents(unsigned char* rootCodeIn, const std::vector<unsigned char*>& endNodesIn) { unsigned char* rootCode; std::vector<unsigned char*> endNodes; if (rootCodeIn) { int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(rootCodeIn)); rootCode = new unsigned char[bytes]; memcpy(rootCode, rootCodeIn, bytes); } else { rootCode = new unsigned char[1]; *rootCode = 0; } for (int i = 0; i < endNodesIn.size(); i++) { if (endNodesIn[i]) { int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodesIn[i])); unsigned char* endNodeCode = new unsigned char[bytes]; memcpy(endNodeCode, endNodesIn[i], bytes); endNodes.push_back(endNodeCode); } } init(rootCode, endNodes); }
OctreeElement::~OctreeElement() { notifyDeleteHooks(); _voxelNodeCount--; if (isLeaf()) { _voxelNodeLeafCount--; } if (_octcodePointer) { _octcodeMemoryUsage -= bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(getOctalCode())); delete[] _octalCode.pointer; } // delete all of this node's children, this also takes care of all population tracking data deleteAllChildren(); }
void OctreeElement::init(unsigned char * octalCode) { if (!octalCode) { octalCode = new unsigned char[1]; *octalCode = 0; } _voxelNodeCount++; _voxelNodeLeafCount++; // all nodes start as leaf nodes size_t octalCodeLength = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); if (octalCodeLength > sizeof(_octalCode)) { _octalCode.pointer = octalCode; _octcodePointer = true; _octcodeMemoryUsage += octalCodeLength; } else { _octcodePointer = false; memcpy(_octalCode.buffer, octalCode, octalCodeLength); delete[] octalCode; } // set up the _children union _childBitmask = 0; _childrenExternal = false; _childrenCount[0]++; // default pointers to child nodes to NULL #ifdef SIMPLE_CHILD_ARRAY for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { _simpleChildArray[i] = NULL; } #endif #ifdef SIMPLE_EXTERNAL_CHILDREN _childrenSingle.reset(); #endif for (int i = 0; i < NUMBER_OF_CHILDREN; i ++) { _externalChildren[i].reset(); } _isDirty = true; _shouldRender = false; _sourceUUIDKey = 0; calculateAACube(); markWithChangedTime(); }
OctreeElement::~OctreeElement() { // We can't call notifyDeleteHooks from here: // notifyDeleteHooks(); // see comment in EntityTreeElement::createNewElement. assert(_deleteHooksNotified); _voxelNodeCount--; if (isLeaf()) { _voxelNodeLeafCount--; } if (_octcodePointer) { _octcodeMemoryUsage -= bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(getOctalCode())); delete[] _octalCode.pointer; } // delete all of this node's children, this also takes care of all population tracking data deleteAllChildren(); }
// adjust any internal timestamps to fix clock skew for this server void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, size_t length, int clockSkew) { unsigned char* dataAt = editPacketBuffer; int octets = numberOfThreeBitSectionsInCode(dataAt); int lengthOfOctcode = bytesRequiredForCodeLength(octets); dataAt += lengthOfOctcode; // lastEdited quint64 lastEditedInLocalTime; memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime)); quint64 lastEditedInServerTime = lastEditedInLocalTime + clockSkew; memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime)); const bool wantDebug = false; if (wantDebug) { qDebug("EntityItem::adjustEditPacketForClockSkew()..."); qDebug() << " lastEditedInLocalTime: " << lastEditedInLocalTime; qDebug() << " clockSkew: " << clockSkew; qDebug() << " lastEditedInServerTime: " << lastEditedInServerTime; } }
int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, unsigned char* editData, int maxLength, Node* senderNode) { int processedBytes = 0; // we handle these types of "edit" packets switch (packetType) { case PACKET_TYPE_VOXEL_SET: case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE: { bool destructive = (packetType == PACKET_TYPE_VOXEL_SET_DESTRUCTIVE); int octets = numberOfThreeBitSectionsInCode(editData, maxLength); if (octets == OVERFLOWED_OCTCODE_BUFFER) { printf("WARNING! Got voxel edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), "); printf("bailing processing of packet!\n"); return processedBytes; } const int COLOR_SIZE_IN_BYTES = 3; int voxelCodeSize = bytesRequiredForCodeLength(octets); int voxelDataSize = voxelCodeSize + COLOR_SIZE_IN_BYTES; if (voxelDataSize > maxLength) { printf("WARNING! Got voxel edit record that would overflow buffer, bailing processing of packet!\n"); printf("bailing processing of packet!\n"); return processedBytes; } readCodeColorBufferToTree(editData, destructive); return voxelDataSize; } break; case PACKET_TYPE_VOXEL_ERASE: processRemoveOctreeElementsBitstream((unsigned char*)packetData, packetLength); return maxLength; } return processedBytes; }
bool OctreePacketData::startSubTree(const unsigned char* octcode) { _bytesOfOctalCodesCurrentSubTree = _bytesOfOctalCodes; bool success = false; int possibleStartAt = _bytesInUse; int length = 0; if (octcode) { length = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octcode)); success = append(octcode, length); // handles checking compression } else { // NULL case, means root node, which is 0 unsigned char byte = 0; length = 1; success = append(byte); // handles checking compression } if (success) { _subTreeAt = possibleStartAt; } if (success) { _bytesOfOctalCodes += length; _totalBytesOfOctalCodes += length; } return success; }
ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ModelTree* tree, bool& valid) { ModelItem newModelItem; // id and _lastUpdated will get set here... const unsigned char* dataAt = data; processedBytes = 0; // the first part of the data is our octcode... int octets = numberOfThreeBitSectionsInCode(data); int lengthOfOctcode = bytesRequiredForCodeLength(octets); // we don't actually do anything with this octcode... dataAt += lengthOfOctcode; processedBytes += lengthOfOctcode; // id uint32_t editID; memcpy(&editID, dataAt, sizeof(editID)); dataAt += sizeof(editID); processedBytes += sizeof(editID); bool isNewModelItem = (editID == NEW_MODEL); // special case for handling "new" modelItems if (isNewModelItem) { // If this is a NEW_MODEL, then we assume that there's an additional uint32_t creatorToken, that // we want to send back to the creator as an map to the actual id uint32_t creatorTokenID; memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID)); dataAt += sizeof(creatorTokenID); processedBytes += sizeof(creatorTokenID); newModelItem.setCreatorTokenID(creatorTokenID); newModelItem._newlyCreated = true; valid = true; } else { // look up the existing modelItem const ModelItem* existingModelItem = tree->findModelByID(editID, true); // copy existing properties before over-writing with new properties if (existingModelItem) { newModelItem = *existingModelItem; valid = true; } else { // the user attempted to edit a modelItem that doesn't exist qDebug() << "user attempted to edit a modelItem that doesn't exist... editID=" << editID; // NOTE: even though this is a bad editID, we have to consume the edit details, so that // the buffer doesn't get corrupted for further processing... valid = false; } newModelItem._id = editID; newModelItem._newlyCreated = false; } // lastEdited memcpy(&newModelItem._lastEdited, dataAt, sizeof(newModelItem._lastEdited)); dataAt += sizeof(newModelItem._lastEdited); processedBytes += sizeof(newModelItem._lastEdited); // All of the remaining items are optional, and may or may not be included based on their included values in the // properties included bits uint16_t packetContainsBits = 0; if (!isNewModelItem) { memcpy(&packetContainsBits, dataAt, sizeof(packetContainsBits)); dataAt += sizeof(packetContainsBits); processedBytes += sizeof(packetContainsBits); } // radius if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_RADIUS) == MODEL_PACKET_CONTAINS_RADIUS)) { memcpy(&newModelItem._radius, dataAt, sizeof(newModelItem._radius)); dataAt += sizeof(newModelItem._radius); processedBytes += sizeof(newModelItem._radius); } // position if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_POSITION) == MODEL_PACKET_CONTAINS_POSITION)) { memcpy(&newModelItem._position, dataAt, sizeof(newModelItem._position)); dataAt += sizeof(newModelItem._position); processedBytes += sizeof(newModelItem._position); } // color if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_COLOR) == MODEL_PACKET_CONTAINS_COLOR)) { memcpy(newModelItem._color, dataAt, sizeof(newModelItem._color)); dataAt += sizeof(newModelItem._color); processedBytes += sizeof(newModelItem._color); } // shouldDie if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_SHOULDDIE) == MODEL_PACKET_CONTAINS_SHOULDDIE)) { memcpy(&newModelItem._shouldDie, dataAt, sizeof(newModelItem._shouldDie)); dataAt += sizeof(newModelItem._shouldDie); processedBytes += sizeof(newModelItem._shouldDie); } // modelURL if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_MODEL_URL) == MODEL_PACKET_CONTAINS_MODEL_URL)) { uint16_t modelURLLength; memcpy(&modelURLLength, dataAt, sizeof(modelURLLength)); dataAt += sizeof(modelURLLength); processedBytes += sizeof(modelURLLength); QString tempString((const char*)dataAt); newModelItem._modelURL = tempString; dataAt += modelURLLength; processedBytes += modelURLLength; } // modelRotation if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_MODEL_ROTATION) == MODEL_PACKET_CONTAINS_MODEL_ROTATION)) { int bytes = unpackOrientationQuatFromBytes(dataAt, newModelItem._modelRotation); dataAt += bytes; processedBytes += bytes; } // animationURL if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_URL) == MODEL_PACKET_CONTAINS_ANIMATION_URL)) { uint16_t animationURLLength; memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); dataAt += sizeof(animationURLLength); processedBytes += sizeof(animationURLLength); QString tempString((const char*)dataAt); newModelItem._animationURL = tempString; dataAt += animationURLLength; processedBytes += animationURLLength; } // animationIsPlaying if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { memcpy(&newModelItem._animationIsPlaying, dataAt, sizeof(newModelItem._animationIsPlaying)); dataAt += sizeof(newModelItem._animationIsPlaying); processedBytes += sizeof(newModelItem._animationIsPlaying); } // animationFrameIndex if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { memcpy(&newModelItem._animationFrameIndex, dataAt, sizeof(newModelItem._animationFrameIndex)); dataAt += sizeof(newModelItem._animationFrameIndex); processedBytes += sizeof(newModelItem._animationFrameIndex); } // animationFPS if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FPS) == MODEL_PACKET_CONTAINS_ANIMATION_FPS)) { memcpy(&newModelItem._animationFPS, dataAt, sizeof(newModelItem._animationFPS)); dataAt += sizeof(newModelItem._animationFPS); processedBytes += sizeof(newModelItem._animationFPS); } const bool wantDebugging = false; if (wantDebugging) { qDebug("ModelItem::fromEditPacket()..."); qDebug() << " ModelItem id in packet:" << editID; newModelItem.debugDump(); } return newModelItem; }
void OctreeSceneStats::copyFromOther(const OctreeSceneStats& other) { _totalEncodeTime = other._totalEncodeTime; _elapsed = other._elapsed; _lastFullElapsed = other._lastFullElapsed; _lastFullTotalEncodeTime = other._lastFullTotalEncodeTime; _lastFullTotalPackets = other._lastFullTotalPackets; _lastFullTotalBytes = other._lastFullTotalBytes; _encodeStart = other._encodeStart; _packets = other._packets; _bytes = other._bytes; _passes = other._passes; _totalElements = other._totalElements; _totalInternal = other._totalInternal; _totalLeaves = other._totalLeaves; _traversed = other._traversed; _internal = other._internal; _leaves = other._leaves; _skippedDistance = other._skippedDistance; _internalSkippedDistance = other._internalSkippedDistance; _leavesSkippedDistance = other._leavesSkippedDistance; _skippedOutOfView = other._skippedOutOfView; _internalSkippedOutOfView = other._internalSkippedOutOfView; _leavesSkippedOutOfView = other._leavesSkippedOutOfView; _skippedWasInView = other._skippedWasInView; _internalSkippedWasInView = other._internalSkippedWasInView; _leavesSkippedWasInView = other._leavesSkippedWasInView; _skippedNoChange = other._skippedNoChange; _internalSkippedNoChange = other._internalSkippedNoChange; _leavesSkippedNoChange = other._leavesSkippedNoChange; _skippedOccluded = other._skippedOccluded; _internalSkippedOccluded = other._internalSkippedOccluded; _leavesSkippedOccluded = other._leavesSkippedOccluded; _colorSent = other._colorSent; _internalColorSent = other._internalColorSent; _leavesColorSent = other._leavesColorSent; _didntFit = other._didntFit; _internalDidntFit = other._internalDidntFit; _leavesDidntFit = other._leavesDidntFit; _colorBitsWritten = other._colorBitsWritten; _existsBitsWritten = other._existsBitsWritten; _existsInPacketBitsWritten = other._existsInPacketBitsWritten; _treesRemoved = other._treesRemoved; // before copying the jurisdictions, delete any current values... if (_jurisdictionRoot) { delete[] _jurisdictionRoot; _jurisdictionRoot = NULL; } for (size_t i = 0; i < _jurisdictionEndNodes.size(); i++) { if (_jurisdictionEndNodes[i]) { delete[] _jurisdictionEndNodes[i]; } } _jurisdictionEndNodes.clear(); // Now copy the values from the other if (other._jurisdictionRoot) { int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(other._jurisdictionRoot)); _jurisdictionRoot = new unsigned char[bytes]; memcpy(_jurisdictionRoot, other._jurisdictionRoot, bytes); } for (size_t i = 0; i < other._jurisdictionEndNodes.size(); i++) { unsigned char* endNodeCode = other._jurisdictionEndNodes[i]; if (endNodeCode) { int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode)); unsigned char* endNodeCodeCopy = new unsigned char[bytes]; memcpy(endNodeCodeCopy, endNodeCode, bytes); _jurisdictionEndNodes.push_back(endNodeCodeCopy); } } _incomingPacket = other._incomingPacket; _incomingBytes = other._incomingBytes; _incomingWastedBytes = other._incomingWastedBytes; _incomingLastSequence = other._incomingLastSequence; _incomingLikelyLost = other._incomingLikelyLost; _incomingRecovered = other._incomingRecovered; _incomingEarly = other._incomingEarly; _incomingLate = other._incomingLate; _incomingReallyLate = other._incomingReallyLate; _incomingPossibleDuplicate = other._incomingPossibleDuplicate; _missingSequenceNumbers = other._missingSequenceNumbers; }
int OctreeSceneStats::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) { unsigned char* bufferStart = destinationBuffer; int headerLength = populatePacketHeader(reinterpret_cast<char*>(destinationBuffer), PacketTypeOctreeStats); destinationBuffer += headerLength; memcpy(destinationBuffer, &_start, sizeof(_start)); destinationBuffer += sizeof(_start); memcpy(destinationBuffer, &_end, sizeof(_end)); destinationBuffer += sizeof(_end); memcpy(destinationBuffer, &_elapsed, sizeof(_elapsed)); destinationBuffer += sizeof(_elapsed); memcpy(destinationBuffer, &_totalEncodeTime, sizeof(_totalEncodeTime)); destinationBuffer += sizeof(_totalEncodeTime); memcpy(destinationBuffer, &_isFullScene, sizeof(_isFullScene)); destinationBuffer += sizeof(_isFullScene); memcpy(destinationBuffer, &_isMoving, sizeof(_isMoving)); destinationBuffer += sizeof(_isMoving); memcpy(destinationBuffer, &_packets, sizeof(_packets)); destinationBuffer += sizeof(_packets); memcpy(destinationBuffer, &_bytes, sizeof(_bytes)); destinationBuffer += sizeof(_bytes); memcpy(destinationBuffer, &_totalInternal, sizeof(_totalInternal)); destinationBuffer += sizeof(_totalInternal); memcpy(destinationBuffer, &_totalLeaves, sizeof(_totalLeaves)); destinationBuffer += sizeof(_totalLeaves); memcpy(destinationBuffer, &_internal, sizeof(_internal)); destinationBuffer += sizeof(_internal); memcpy(destinationBuffer, &_leaves, sizeof(_leaves)); destinationBuffer += sizeof(_leaves); memcpy(destinationBuffer, &_internalSkippedDistance, sizeof(_internalSkippedDistance)); destinationBuffer += sizeof(_internalSkippedDistance); memcpy(destinationBuffer, &_leavesSkippedDistance, sizeof(_leavesSkippedDistance)); destinationBuffer += sizeof(_leavesSkippedDistance); memcpy(destinationBuffer, &_internalSkippedOutOfView, sizeof(_internalSkippedOutOfView)); destinationBuffer += sizeof(_internalSkippedOutOfView); memcpy(destinationBuffer, &_leavesSkippedOutOfView, sizeof(_leavesSkippedOutOfView)); destinationBuffer += sizeof(_leavesSkippedOutOfView); memcpy(destinationBuffer, &_internalSkippedWasInView, sizeof(_internalSkippedWasInView)); destinationBuffer += sizeof(_internalSkippedWasInView); memcpy(destinationBuffer, &_leavesSkippedWasInView, sizeof(_leavesSkippedWasInView)); destinationBuffer += sizeof(_leavesSkippedWasInView); memcpy(destinationBuffer, &_internalSkippedNoChange, sizeof(_internalSkippedNoChange)); destinationBuffer += sizeof(_internalSkippedNoChange); memcpy(destinationBuffer, &_leavesSkippedNoChange, sizeof(_leavesSkippedNoChange)); destinationBuffer += sizeof(_leavesSkippedNoChange); memcpy(destinationBuffer, &_internalSkippedOccluded, sizeof(_internalSkippedOccluded)); destinationBuffer += sizeof(_internalSkippedOccluded); memcpy(destinationBuffer, &_leavesSkippedOccluded, sizeof(_leavesSkippedOccluded)); destinationBuffer += sizeof(_leavesSkippedOccluded); memcpy(destinationBuffer, &_internalColorSent, sizeof(_internalColorSent)); destinationBuffer += sizeof(_internalColorSent); memcpy(destinationBuffer, &_leavesColorSent, sizeof(_leavesColorSent)); destinationBuffer += sizeof(_leavesColorSent); memcpy(destinationBuffer, &_internalDidntFit, sizeof(_internalDidntFit)); destinationBuffer += sizeof(_internalDidntFit); memcpy(destinationBuffer, &_leavesDidntFit, sizeof(_leavesDidntFit)); destinationBuffer += sizeof(_leavesDidntFit); memcpy(destinationBuffer, &_colorBitsWritten, sizeof(_colorBitsWritten)); destinationBuffer += sizeof(_colorBitsWritten); memcpy(destinationBuffer, &_existsBitsWritten, sizeof(_existsBitsWritten)); destinationBuffer += sizeof(_existsBitsWritten); memcpy(destinationBuffer, &_existsInPacketBitsWritten, sizeof(_existsInPacketBitsWritten)); destinationBuffer += sizeof(_existsInPacketBitsWritten); memcpy(destinationBuffer, &_treesRemoved, sizeof(_treesRemoved)); destinationBuffer += sizeof(_treesRemoved); // add the root jurisdiction if (_jurisdictionRoot) { // copy the int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(_jurisdictionRoot)); memcpy(destinationBuffer, &bytes, sizeof(bytes)); destinationBuffer += sizeof(bytes); memcpy(destinationBuffer, _jurisdictionRoot, bytes); destinationBuffer += bytes; // if and only if there's a root jurisdiction, also include the end elements int endNodeCount = _jurisdictionEndNodes.size(); memcpy(destinationBuffer, &endNodeCount, sizeof(endNodeCount)); destinationBuffer += sizeof(endNodeCount); for (int i=0; i < endNodeCount; i++) { unsigned char* endNodeCode = _jurisdictionEndNodes[i]; int bytes = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(endNodeCode)); memcpy(destinationBuffer, &bytes, sizeof(bytes)); destinationBuffer += sizeof(bytes); memcpy(destinationBuffer, endNodeCode, bytes); destinationBuffer += bytes; } } else { int bytes = 0; memcpy(destinationBuffer, &bytes, sizeof(bytes)); destinationBuffer += sizeof(bytes); } return destinationBuffer - bufferStart; // includes header! }
bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id, const ModelItemProperties& properties, unsigned char* bufferOut, int sizeIn, int& sizeOut) { bool success = true; // assume the best unsigned char* copyAt = bufferOut; sizeOut = 0; // get the octal code for the modelItem // this could be a problem if the caller doesn't include position.... glm::vec3 rootPosition(0); float rootScale = 0.5f; unsigned char* octcode = pointToOctalCode(rootPosition.x, rootPosition.y, rootPosition.z, rootScale); // TODO: Consider this old code... including the correct octree for where the modelItem will go matters for // modelItem servers with different jurisdictions, but for now, we'll send everything to the root, since the // tree does the right thing... // //unsigned char* octcode = pointToOctalCode(details[i].position.x, details[i].position.y, // details[i].position.z, details[i].radius); int octets = numberOfThreeBitSectionsInCode(octcode); int lengthOfOctcode = bytesRequiredForCodeLength(octets); // add it to our message memcpy(copyAt, octcode, lengthOfOctcode); copyAt += lengthOfOctcode; sizeOut += lengthOfOctcode; // Now add our edit content details... bool isNewModelItem = (id.id == NEW_MODEL); // id memcpy(copyAt, &id.id, sizeof(id.id)); copyAt += sizeof(id.id); sizeOut += sizeof(id.id); // special case for handling "new" modelItems if (isNewModelItem) { // If this is a NEW_MODEL, then we assume that there's an additional uint32_t creatorToken, that // we want to send back to the creator as an map to the actual id memcpy(copyAt, &id.creatorTokenID, sizeof(id.creatorTokenID)); copyAt += sizeof(id.creatorTokenID); sizeOut += sizeof(id.creatorTokenID); } // lastEdited quint64 lastEdited = properties.getLastEdited(); memcpy(copyAt, &lastEdited, sizeof(lastEdited)); copyAt += sizeof(lastEdited); sizeOut += sizeof(lastEdited); // For new modelItems, all remaining items are mandatory, for an edited modelItem, All of the remaining items are // optional, and may or may not be included based on their included values in the properties included bits uint16_t packetContainsBits = properties.getChangedBits(); if (!isNewModelItem) { memcpy(copyAt, &packetContainsBits, sizeof(packetContainsBits)); copyAt += sizeof(packetContainsBits); sizeOut += sizeof(packetContainsBits); } // radius if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_RADIUS) == MODEL_PACKET_CONTAINS_RADIUS)) { float radius = properties.getRadius() / (float) TREE_SCALE; memcpy(copyAt, &radius, sizeof(radius)); copyAt += sizeof(radius); sizeOut += sizeof(radius); } // position if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_POSITION) == MODEL_PACKET_CONTAINS_POSITION)) { glm::vec3 position = properties.getPosition() / (float)TREE_SCALE; memcpy(copyAt, &position, sizeof(position)); copyAt += sizeof(position); sizeOut += sizeof(position); } // color if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_COLOR) == MODEL_PACKET_CONTAINS_COLOR)) { rgbColor color = { properties.getColor().red, properties.getColor().green, properties.getColor().blue }; memcpy(copyAt, color, sizeof(color)); copyAt += sizeof(color); sizeOut += sizeof(color); } // shoulDie if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_SHOULDDIE) == MODEL_PACKET_CONTAINS_SHOULDDIE)) { bool shouldDie = properties.getShouldDie(); memcpy(copyAt, &shouldDie, sizeof(shouldDie)); copyAt += sizeof(shouldDie); sizeOut += sizeof(shouldDie); } // modelURL if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_MODEL_URL) == MODEL_PACKET_CONTAINS_MODEL_URL)) { uint16_t urlLength = properties.getModelURL().size() + 1; memcpy(copyAt, &urlLength, sizeof(urlLength)); copyAt += sizeof(urlLength); sizeOut += sizeof(urlLength); memcpy(copyAt, qPrintable(properties.getModelURL()), urlLength); copyAt += urlLength; sizeOut += urlLength; } // modelRotation if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_MODEL_ROTATION) == MODEL_PACKET_CONTAINS_MODEL_ROTATION)) { int bytes = packOrientationQuatToBytes(copyAt, properties.getModelRotation()); copyAt += bytes; sizeOut += bytes; } // animationURL if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_URL) == MODEL_PACKET_CONTAINS_ANIMATION_URL)) { uint16_t urlLength = properties.getAnimationURL().size() + 1; memcpy(copyAt, &urlLength, sizeof(urlLength)); copyAt += sizeof(urlLength); sizeOut += sizeof(urlLength); memcpy(copyAt, qPrintable(properties.getAnimationURL()), urlLength); copyAt += urlLength; sizeOut += urlLength; } // animationIsPlaying if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { bool animationIsPlaying = properties.getAnimationIsPlaying(); memcpy(copyAt, &animationIsPlaying, sizeof(animationIsPlaying)); copyAt += sizeof(animationIsPlaying); sizeOut += sizeof(animationIsPlaying); } // animationFrameIndex if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { float animationFrameIndex = properties.getAnimationFrameIndex(); memcpy(copyAt, &animationFrameIndex, sizeof(animationFrameIndex)); copyAt += sizeof(animationFrameIndex); sizeOut += sizeof(animationFrameIndex); } // animationFPS if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FPS) == MODEL_PACKET_CONTAINS_ANIMATION_FPS)) { float animationFPS = properties.getAnimationFPS(); memcpy(copyAt, &animationFPS, sizeof(animationFPS)); copyAt += sizeof(animationFPS); sizeOut += sizeof(animationFPS); } bool wantDebugging = false; if (wantDebugging) { qDebug("encodeModelItemEditMessageDetails()...."); qDebug("ModelItem id :%u", id.id); qDebug(" nextID:%u", _nextID); } // cleanup delete[] octcode; return success; }
void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelTreeElement* node, ReadCodeColorBufferToTreeArgs& args) { int lengthOfNodeCode = numberOfThreeBitSectionsInCode(node->getOctalCode()); // Since we traverse the tree in code order, we know that if our code // matches, then we've reached our target node. if (lengthOfNodeCode == args.lengthOfCode) { // we've reached our target -- we might have found our node, but that node might have children. // in this case, we only allow you to set the color if you explicitly asked for a destructive // write. if (!node->isLeaf() && args.destructive) { // if it does exist, make sure it has no children for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { node->deleteChildAtIndex(i); } } else { if (!node->isLeaf()) { qDebug("WARNING! operation would require deleting children, add Voxel ignored!\n "); } } // If we get here, then it means, we either had a true leaf to begin with, or we were in // destructive mode and we deleted all the child trees. So we can color. if (node->isLeaf()) { // give this node its color int octalCodeBytes = bytesRequiredForCodeLength(args.lengthOfCode); nodeColor newColor; memcpy(newColor, args.codeColorBuffer + octalCodeBytes, SIZE_OF_COLOR_DATA); newColor[SIZE_OF_COLOR_DATA] = 1; node->setColor(newColor); // It's possible we just reset the node to it's exact same color, in // which case we don't consider this to be dirty... if (node->isDirty()) { // track our tree dirtiness _isDirty = true; // track that path has changed args.pathChanged = true; } } return; } // Ok, we know we haven't reached our target node yet, so keep looking //printOctalCode(args.codeColorBuffer); int childIndex = branchIndexWithDescendant(node->getOctalCode(), args.codeColorBuffer); VoxelTreeElement* childNode = node->getChildAtIndex(childIndex); // If the branch we need to traverse does not exist, then create it on the way down... if (!childNode) { childNode = node->addChildAtIndex(childIndex); } // recurse... readCodeColorBufferToTreeRecursion(childNode, args); // Unwinding... // If the lower level did some work, then we need to let this node know, so it can // do any bookkeeping it wants to, like color re-averaging, time stamp marking, etc if (args.pathChanged) { node->handleSubtreeChanged(this); } }
// TODO: // how to handle lastEdited? // how to handle lastUpdated? // consider handling case where no properties are included... we should just ignore this packet... // // TODO: Right now, all possible properties for all subclasses are handled here. Ideally we'd prefer // to handle this in a more generic way. Allowing subclasses of EntityItem to register their properties // // TODO: There's a lot of repeated patterns in the code below to handle each property. It would be nice if the property // registration mechanism allowed us to collapse these repeated sections of code into a single implementation that // utilized the registration table to shorten up and simplify this code. // // TODO: Implement support for paged properties, spanning MTU, and custom properties // // TODO: Implement support for script and visible properties. // bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, EntityItemID& entityID, EntityItemProperties& properties) { bool valid = false; const unsigned char* dataAt = data; processedBytes = 0; // the first part of the data is an octcode, this is a required element of the edit packet format, but we don't // actually use it, we do need to skip it and read to the actual data we care about. int octets = numberOfThreeBitSectionsInCode(data); int bytesToReadOfOctcode = bytesRequiredForCodeLength(octets); // we don't actually do anything with this octcode... dataAt += bytesToReadOfOctcode; processedBytes += bytesToReadOfOctcode; // Edit packets have a last edited time stamp immediately following the octcode. // NOTE: the edit times have been set by the editor to match out clock, so we don't need to adjust // these times for clock skew at this point. quint64 lastEdited; memcpy(&lastEdited, dataAt, sizeof(lastEdited)); dataAt += sizeof(lastEdited); processedBytes += sizeof(lastEdited); properties.setLastEdited(lastEdited); // NOTE: We intentionally do not send "created" times in edit messages. This is because: // 1) if the edit is to an existing entity, the created time can not be changed // 2) if the edit is to a new entity, the created time is the last edited time // encoded id QByteArray encodedID((const char*)dataAt, NUM_BYTES_RFC4122_UUID); // maximum possible size QUuid editID = QUuid::fromRfc4122(encodedID); dataAt += encodedID.size(); processedBytes += encodedID.size(); bool isNewEntityItem = (editID == NEW_ENTITY); if (isNewEntityItem) { // If this is a NEW_ENTITY, then we assume that there's an additional uint32_t creatorToken, that // we want to send back to the creator as an map to the actual id QByteArray encodedToken((const char*)dataAt, (bytesToRead - processedBytes)); ByteCountCoded<quint32> tokenCoder = encodedToken; quint32 creatorTokenID = tokenCoder; encodedToken = tokenCoder; // determine true bytesToRead dataAt += encodedToken.size(); processedBytes += encodedToken.size(); //newEntityItem.setCreatorTokenID(creatorTokenID); //newEntityItem._newlyCreated = true; entityID.id = NEW_ENTITY; entityID.creatorTokenID = creatorTokenID; entityID.isKnownID = false; valid = true; // created time is lastEdited time properties.setCreated(lastEdited); } else { entityID.id = editID; entityID.creatorTokenID = UNKNOWN_ENTITY_TOKEN; entityID.isKnownID = true; valid = true; // created time is lastEdited time properties.setCreated(USE_EXISTING_CREATED_TIME); } // Entity Type... QByteArray encodedType((const char*)dataAt, (bytesToRead - processedBytes)); ByteCountCoded<quint32> typeCoder = encodedType; quint32 entityTypeCode = typeCoder; properties.setType((EntityTypes::EntityType)entityTypeCode); encodedType = typeCoder; // determine true bytesToRead dataAt += encodedType.size(); processedBytes += encodedType.size(); // Update Delta - when was this item updated relative to last edit... this really should be 0 // TODO: Should we get rid of this in this in edit packets, since this has to always be 0? // TODO: do properties need to handle lastupdated??? // last updated is stored as ByteCountCoded delta from lastEdited QByteArray encodedUpdateDelta((const char*)dataAt, (bytesToRead - processedBytes)); ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta; encodedUpdateDelta = updateDeltaCoder; // determine true bytesToRead dataAt += encodedUpdateDelta.size(); processedBytes += encodedUpdateDelta.size(); // TODO: Do we need this lastUpdated?? We don't seem to use it. //quint64 updateDelta = updateDeltaCoder; //quint64 lastUpdated = lastEdited + updateDelta; // don't adjust for clock skew since we already did that for lastEdited // Property Flags... QByteArray encodedPropertyFlags((const char*)dataAt, (bytesToRead - processedBytes)); EntityPropertyFlags propertyFlags = encodedPropertyFlags; dataAt += propertyFlags.getEncodedLength(); processedBytes += propertyFlags.getEncodedLength(); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); // NOTE: PROP_RADIUS obsolete READ_ENTITY_PROPERTY_QUAT_TO_PROPERTIES(PROP_ROTATION, setRotation); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MASS, float, setMass); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, glm::vec3, setGravity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_SCRIPT,setScript); READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_COLOR, setColor); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); return valid; }