mirror of
https://github.com/lubosz/overte.git
synced 2025-04-25 00:03:16 +02:00
implemented support for PACKET_TYPE_PARTICLE_ERASE
This commit is contained in:
parent
347933f1a5
commit
97b0ed2cc5
17 changed files with 427 additions and 113 deletions
|
@ -4187,6 +4187,7 @@ void Application::processDatagrams() {
|
|||
break;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_DATA:
|
||||
case PACKET_TYPE_PARTICLE_ERASE:
|
||||
case PACKET_TYPE_VOXEL_DATA:
|
||||
case PACKET_TYPE_VOXEL_ERASE:
|
||||
case PACKET_TYPE_OCTREE_STATS:
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include "ParticleTreeRenderer.h"
|
||||
|
||||
ParticleTreeRenderer::ParticleTreeRenderer() :
|
||||
ParticleTreeRenderer::ParticleTreeRenderer() :
|
||||
OctreeRenderer() {
|
||||
}
|
||||
|
||||
|
@ -31,13 +31,13 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
// actually render it here...
|
||||
// we need to iterate the actual particles of the element
|
||||
ParticleTreeElement* particleTreeElement = (ParticleTreeElement*)element;
|
||||
|
||||
|
||||
const QList<Particle>& particles = particleTreeElement->getParticles();
|
||||
|
||||
|
||||
uint16_t numberOfParticles = particles.size();
|
||||
|
||||
|
||||
bool drawAsSphere = true;
|
||||
|
||||
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
const Particle& particle = particles[i];
|
||||
// render particle aspoints
|
||||
|
@ -46,7 +46,7 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
float sphereRadius = particle.getRadius() * (float)TREE_SCALE;
|
||||
|
||||
args->_renderedItems++;
|
||||
|
||||
|
||||
if (drawAsSphere) {
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
|
@ -60,3 +60,8 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleTreeRenderer::processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr,
|
||||
Node* sourceNode) {
|
||||
static_cast<ParticleTree*>(_tree)->processEraseMessage(dataByteArray, senderSockAddr, sourceNode);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ public:
|
|||
|
||||
ParticleTree* getTree() { return (ParticleTree*)_tree; }
|
||||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength) {
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"VoxelPacketProcessor::processPacket()");
|
||||
|
||||
|
||||
const int WAY_BEHIND = 300;
|
||||
|
||||
if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
|
||||
|
@ -27,19 +27,19 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
|
|||
|
||||
Application* app = Application::getInstance();
|
||||
bool wasStatsPacket = false;
|
||||
|
||||
|
||||
|
||||
|
||||
// check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
|
||||
if (app->_wantToKillLocalVoxels) {
|
||||
app->_voxels.killLocalVoxels();
|
||||
app->_wantToKillLocalVoxels = false;
|
||||
}
|
||||
|
||||
|
||||
// note: PACKET_TYPE_OCTREE_STATS can have PACKET_TYPE_VOXEL_DATA
|
||||
// immediately following them inside the same packet. So, we process the PACKET_TYPE_OCTREE_STATS first
|
||||
// then process any remaining bytes as if it was another packet
|
||||
if (packetData[0] == PACKET_TYPE_OCTREE_STATS) {
|
||||
|
||||
|
||||
int statsMessageLength = app->parseOctreeStats(packetData, messageLength, senderSockAddr);
|
||||
wasStatsPacket = true;
|
||||
if (messageLength > statsMessageLength) {
|
||||
|
@ -56,20 +56,25 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
|
|||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
app->trackIncomingVoxelPacket(packetData, messageLength, senderSockAddr, wasStatsPacket);
|
||||
|
||||
|
||||
SharedNodePointer serverNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
|
||||
if (serverNode && serverNode->getActiveSocket() && *serverNode->getActiveSocket() == senderSockAddr) {
|
||||
|
||||
|
||||
switch(packetData[0]) {
|
||||
case PACKET_TYPE_PARTICLE_ERASE: {
|
||||
app->_particles.processEraseMessage(QByteArray((char*) packetData, messageLength),
|
||||
senderSockAddr, serverNode.data());
|
||||
} break;
|
||||
|
||||
case PACKET_TYPE_PARTICLE_DATA: {
|
||||
app->_particles.processDatagram(QByteArray((char*) packetData, messageLength),
|
||||
senderSockAddr, serverNode.data());
|
||||
} break;
|
||||
|
||||
|
||||
case PACKET_TYPE_ENVIRONMENT_DATA: {
|
||||
app->_environment.parseData(senderSockAddr, packetData, messageLength);
|
||||
} break;
|
||||
|
||||
|
||||
default : {
|
||||
app->_voxels.setDataSourceUUID(serverNode->getUUID());
|
||||
app->_voxels.parseData(packetData, messageLength);
|
||||
|
|
|
@ -525,7 +525,8 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b
|
|||
|
||||
// Here's where we can/should allow the server to send other data...
|
||||
// send the environment packet
|
||||
if (_myServer->hasSpecialPacketToSend()) {
|
||||
// TODO: should we turn this into a while loop to better handle sending multiple special packets
|
||||
if (_myServer->hasSpecialPacketToSend(node)) {
|
||||
trueBytesSent += _myServer->sendSpecialPacket(node);
|
||||
truePacketsSent++;
|
||||
packetsSentThisInterval++;
|
||||
|
|
|
@ -100,27 +100,27 @@ void OctreeServer::initHTTPManager(int port) {
|
|||
}
|
||||
|
||||
bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
|
||||
|
||||
|
||||
#ifdef FORCE_CRASH
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation
|
||||
&& path == "/force_crash") {
|
||||
|
||||
|
||||
qDebug() << "About to force a crash!";
|
||||
|
||||
|
||||
int foo;
|
||||
int* forceCrash = &foo;
|
||||
|
||||
|
||||
QString responseString("forcing a crash...");
|
||||
connection->respond(HTTPConnection::StatusCode200, qPrintable(responseString));
|
||||
|
||||
|
||||
delete[] forceCrash;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool showStats = false;
|
||||
|
||||
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
|
||||
if (path == "/") {
|
||||
showStats = true;
|
||||
|
@ -129,28 +129,28 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
showStats = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (showStats) {
|
||||
uint64_t checkSum;
|
||||
// return a 200
|
||||
QString statsString("<html><doc>\r\n<pre>\r\n");
|
||||
statsString += QString("<b>Your %1 Server is running... <a href='/'>[RELOAD]</a></b>\r\n").arg(getMyServerName());
|
||||
|
||||
|
||||
tm* localtm = localtime(&_started);
|
||||
const int MAX_TIME_LENGTH = 128;
|
||||
char buffer[MAX_TIME_LENGTH];
|
||||
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
|
||||
statsString += QString("Running since: %1").arg(buffer);
|
||||
|
||||
|
||||
// Convert now to tm struct for UTC
|
||||
tm* gmtm = gmtime(&_started);
|
||||
if (gmtm) {
|
||||
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", gmtm);
|
||||
statsString += (QString(" [%1 UTM] ").arg(buffer));
|
||||
}
|
||||
|
||||
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
uint64_t now = usecTimestampNow();
|
||||
const int USECS_PER_MSEC = 1000;
|
||||
uint64_t msecsElapsed = (now - _startedUSecs) / USECS_PER_MSEC;
|
||||
|
@ -158,13 +158,13 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
const int SECS_PER_MIN = 60;
|
||||
const int MIN_PER_HOUR = 60;
|
||||
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
|
||||
|
||||
|
||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||
|
||||
|
||||
statsString += "Uptime: ";
|
||||
|
||||
|
||||
if (hours > 0) {
|
||||
statsString += QString("%1 hour%2").arg(hours).arg((hours > 1) ? "s" : "");
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf("%.3f seconds", seconds);
|
||||
}
|
||||
statsString += "\r\n\r\n";
|
||||
|
||||
|
||||
// display voxel file load time
|
||||
if (isInitialLoadComplete()) {
|
||||
if (isPersistEnabled()) {
|
||||
|
@ -183,14 +183,14 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
} else {
|
||||
statsString += QString("%1 File Persist Disabled...\r\n").arg(getMyServerName());
|
||||
}
|
||||
|
||||
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
uint64_t msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;;
|
||||
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
|
||||
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
|
||||
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
|
||||
|
||||
|
||||
statsString += QString("%1 File Load Took").arg(getMyServerName());
|
||||
if (hours > 0) {
|
||||
statsString += QString("%1 hour%2").arg(hours).arg((hours > 1) ? "s" : "");
|
||||
|
@ -202,25 +202,25 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf("%.3f seconds", seconds);
|
||||
}
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
} else {
|
||||
statsString += "Voxels not yet loaded...\r\n";
|
||||
}
|
||||
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
statsString += "<b>Configuration:</b>\r\n";
|
||||
|
||||
|
||||
for (int i = 1; i < _argc; i++) {
|
||||
statsString += _argv[i];
|
||||
}
|
||||
statsString += "\r\n"; //one to end the config line
|
||||
statsString += "\r\n\r\n"; // two more for spacing
|
||||
|
||||
|
||||
// display scene stats
|
||||
unsigned long nodeCount = OctreeElement::getNodeCount();
|
||||
unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
|
||||
unsigned long leafNodeCount = OctreeElement::getLeafNodeCount();
|
||||
|
||||
|
||||
QLocale locale(QLocale::English);
|
||||
const float AS_PERCENT = 100.0;
|
||||
statsString += "<b>Current Nodes in scene:</b>\r\n";
|
||||
|
@ -234,7 +234,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
|
||||
statsString += "\r\n";
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
// display outbound packet stats
|
||||
statsString += QString("<b>%1 Outbound Packet Statistics...</b>\r\n").arg(getMyServerName());
|
||||
uint64_t totalOutboundPackets = OctreeSendThread::_totalPackets;
|
||||
|
@ -243,7 +243,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
uint64_t totalBytesOfOctalCodes = OctreePacketData::getTotalBytesOfOctalCodes();
|
||||
uint64_t totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
|
||||
uint64_t totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
|
||||
|
||||
|
||||
const int COLUMN_WIDTH = 10;
|
||||
statsString += QString(" Total Outbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
@ -260,10 +260,10 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf(" Total Color Bytes: %s bytes (%5.2f%%)\r\n",
|
||||
locale.toString((uint)totalBytesOfColor).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
|
||||
((float)totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT);
|
||||
|
||||
|
||||
statsString += "\r\n";
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
// display inbound packet stats
|
||||
statsString += QString().sprintf("<b>%s Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n",
|
||||
getMyServerName());
|
||||
|
@ -274,9 +274,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
uint64_t averageLockWaitTimePerElement = _octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
|
||||
uint64_t totalElementsProcessed = _octreeInboundPacketProcessor->getTotalElementsProcessed();
|
||||
uint64_t totalPacketsProcessed = _octreeInboundPacketProcessor->getTotalPacketsProcessed();
|
||||
|
||||
|
||||
float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
|
||||
|
||||
|
||||
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Inbound Elements: %1 elements\r\n")
|
||||
|
@ -292,18 +292,18 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
.arg(locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
||||
|
||||
|
||||
int senderNumber = 0;
|
||||
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
|
||||
for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
|
||||
senderNumber++;
|
||||
QUuid senderID = i->first;
|
||||
SingleSenderStats& senderStats = i->second;
|
||||
|
||||
|
||||
statsString += QString("\r\n Stats for sender %1 uuid: %2\r\n")
|
||||
.arg(senderNumber).arg(senderID.toString());
|
||||
|
||||
|
||||
averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
|
||||
averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
|
||||
averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
|
||||
|
@ -311,9 +311,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
averageLockWaitTimePerElement = senderStats.getAverageLockWaitTimePerElement();
|
||||
totalElementsProcessed = senderStats.getTotalElementsProcessed();
|
||||
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
|
||||
|
||||
|
||||
averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / totalPacketsProcessed;
|
||||
|
||||
|
||||
statsString += QString(" Total Inbound Packets: %1 packets\r\n")
|
||||
.arg(locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Total Inbound Elements: %1 elements\r\n")
|
||||
|
@ -330,16 +330,16 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
.arg(locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString(" Average Wait Lock Time/Element: %1 usecs\r\n")
|
||||
.arg(locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' '));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
|
||||
|
||||
// display memory usage stats
|
||||
statsString += "<b>Current Memory Usage Statistics</b>\r\n";
|
||||
statsString += QString().sprintf("\r\nOctreeElement size... %ld bytes\r\n", sizeof(OctreeElement));
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
const char* memoryScaleLabel;
|
||||
const float MEGABYTES = 1000000.f;
|
||||
const float GIGABYTES = 1000000000.f;
|
||||
|
@ -351,7 +351,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
memoryScaleLabel = "GB";
|
||||
memoryScale = GIGABYTES;
|
||||
}
|
||||
|
||||
|
||||
statsString += QString().sprintf("Element Node Memory Usage: %8.2f %s\r\n",
|
||||
OctreeElement::getVoxelMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
statsString += QString().sprintf("Octcode Memory Usage: %8.2f %s\r\n",
|
||||
|
@ -362,7 +362,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf(" Total: %8.2f %s\r\n",
|
||||
OctreeElement::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
|
||||
statsString += "\r\n";
|
||||
|
||||
|
||||
statsString += "OctreeElement Children Population Statistics...\r\n";
|
||||
checkSum = 0;
|
||||
for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
|
||||
|
@ -374,11 +374,11 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += " ----------------------\r\n";
|
||||
statsString += QString(" Total: %1 nodes\r\n")
|
||||
.arg(locale.toString((uint)checkSum).rightJustified(16, ' '));
|
||||
|
||||
|
||||
#ifdef BLENDED_UNION_CHILDREN
|
||||
statsString += "\r\n";
|
||||
statsString += "OctreeElement Children Encoding Statistics...\r\n";
|
||||
|
||||
|
||||
statsString += QString().sprintf(" Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getSingleChildrenCount(),
|
||||
((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT));
|
||||
|
@ -397,31 +397,31 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString&
|
|||
statsString += QString().sprintf(" Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
|
||||
OctreeElement::getExternalChildrenCount(),
|
||||
((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
|
||||
|
||||
|
||||
checkSum = OctreeElement::getSingleChildrenCount() +
|
||||
OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() +
|
||||
OctreeElement::getThreeChildrenOffsetCount() + OctreeElement::getThreeChildrenExternalCount() +
|
||||
OctreeElement::getExternalChildrenCount();
|
||||
|
||||
|
||||
statsString += " ----------------\r\n";
|
||||
statsString += QString().sprintf(" Total: %10.llu nodes\r\n", checkSum);
|
||||
statsString += QString().sprintf(" Expected: %10.lu nodes\r\n", nodeCount);
|
||||
|
||||
|
||||
statsString += "\r\n";
|
||||
statsString += "In other news....\r\n";
|
||||
|
||||
|
||||
statsString += QString().sprintf("could store 4 children internally: %10.llu nodes\r\n",
|
||||
OctreeElement::getCouldStoreFourChildrenInternally());
|
||||
statsString += QString().sprintf("could NOT store 4 children internally: %10.llu nodes\r\n",
|
||||
OctreeElement::getCouldNotStoreFourChildrenInternally());
|
||||
#endif
|
||||
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
statsString += "</pre>\r\n";
|
||||
statsString += "</doc></html>";
|
||||
|
||||
|
||||
connection->respond(HTTPConnection::StatusCode200, qPrintable(statsString), "text/html");
|
||||
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// have HTTPManager attempt to process this request from the document_root
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
/// Handles assignments of type OctreeServer - sending octrees to various clients.
|
||||
class OctreeServer : public ThreadedAssignment, public HTTPRequestHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
OctreeServer(const unsigned char* dataBuffer, int numBytes);
|
||||
~OctreeServer();
|
||||
|
||||
|
@ -58,27 +58,27 @@ public:
|
|||
|
||||
// subclass may implement these method
|
||||
virtual void beforeRun() { };
|
||||
virtual bool hasSpecialPacketToSend() { return false; }
|
||||
virtual bool hasSpecialPacketToSend(Node* node) { return false; }
|
||||
virtual int sendSpecialPacket(Node* node) { return 0; }
|
||||
|
||||
static void attachQueryNodeToNode(Node* newNode);
|
||||
|
||||
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
|
||||
public slots:
|
||||
/// runs the voxel server assignment
|
||||
void run();
|
||||
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
|
||||
protected:
|
||||
void parsePayload();
|
||||
void initHTTPManager(int port);
|
||||
|
||||
|
||||
int _argc;
|
||||
const char** _argv;
|
||||
char** _parsedArgV;
|
||||
|
||||
|
||||
HTTPManager* _httpManager;
|
||||
|
||||
char _persistFilename[MAX_FILENAME_LENGTH];
|
||||
|
@ -94,9 +94,9 @@ protected:
|
|||
OctreePersistThread* _persistThread;
|
||||
|
||||
static OctreeServer* _instance;
|
||||
|
||||
|
||||
time_t _started;
|
||||
uint64_t _startedUSecs;
|
||||
uint64_t _startedUSecs;
|
||||
};
|
||||
|
||||
#endif // __octree_server__OctreeServer__
|
||||
|
|
|
@ -1367,7 +1367,7 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* node) {
|
|||
PACKET_TYPE expectedType = expectedDataPacketType();
|
||||
PACKET_VERSION expectedVersion = versionForPacketType(expectedType);
|
||||
file.write(&expectedType, sizeof(expectedType));
|
||||
file.write(&expectedVersion, sizeof(expectedType));
|
||||
file.write(&expectedVersion, sizeof(expectedVersion));
|
||||
}
|
||||
|
||||
OctreeElementBag nodeBag;
|
||||
|
|
|
@ -15,8 +15,17 @@
|
|||
|
||||
class ParticleNodeData : public OctreeQueryNode {
|
||||
public:
|
||||
ParticleNodeData(Node* owningNode) : OctreeQueryNode(owningNode) { };
|
||||
ParticleNodeData(Node* owningNode) :
|
||||
OctreeQueryNode(owningNode),
|
||||
_lastDeletedParticlesSentAt(0) { };
|
||||
|
||||
virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }
|
||||
|
||||
uint64_t getLastDeletedParticlesSentAt() const { return _lastDeletedParticlesSentAt; }
|
||||
void setLastDeletedParticlesSentAt(uint64_t sentAt) { _lastDeletedParticlesSentAt = sentAt; }
|
||||
|
||||
private:
|
||||
uint64_t _lastDeletedParticlesSentAt;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleNodeData__) */
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#include <QTimer>
|
||||
#include <ParticleTree.h>
|
||||
|
||||
#include "ParticleServer.h"
|
||||
|
@ -36,7 +37,10 @@ Octree* ParticleServer::createTree() {
|
|||
}
|
||||
|
||||
void ParticleServer::beforeRun() {
|
||||
// nothing special to do...
|
||||
QTimer* pruneDeletedParticlesTimer = new QTimer(this);
|
||||
connect(pruneDeletedParticlesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedParticles()));
|
||||
const int PRUNE_DELETED_PARTICLES_INTERVAL_MSECS = 1 * 1000; // once every second
|
||||
pruneDeletedParticlesTimer->start(PRUNE_DELETED_PARTICLES_INTERVAL_MSECS);
|
||||
}
|
||||
|
||||
void ParticleServer::particleCreated(const Particle& newParticle, Node* node) {
|
||||
|
@ -46,20 +50,89 @@ void ParticleServer::particleCreated(const Particle& newParticle, Node* node) {
|
|||
int numBytesPacketHeader = populateTypeAndVersion(outputBuffer, PACKET_TYPE_PARTICLE_ADD_RESPONSE);
|
||||
int packetLength = numBytesPacketHeader;
|
||||
copyAt += numBytesPacketHeader;
|
||||
|
||||
|
||||
// encode the creatorTokenID
|
||||
uint32_t creatorTokenID = newParticle.getCreatorTokenID();
|
||||
memcpy(copyAt, &creatorTokenID, sizeof(creatorTokenID));
|
||||
copyAt += sizeof(creatorTokenID);
|
||||
packetLength += sizeof(creatorTokenID);
|
||||
|
||||
|
||||
// encode the particle ID
|
||||
uint32_t particleID = newParticle.getID();
|
||||
memcpy(copyAt, &particleID, sizeof(particleID));
|
||||
copyAt += sizeof(particleID);
|
||||
packetLength += sizeof(particleID);
|
||||
|
||||
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) outputBuffer, packetLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
}
|
||||
|
||||
|
||||
// ParticleServer will use the "special packets" to send list of recently deleted particles
|
||||
bool ParticleServer::hasSpecialPacketToSend(Node* node) {
|
||||
bool shouldSendDeletedParticles = false;
|
||||
|
||||
// check to see if any new particles have been added since we last sent to this node...
|
||||
ParticleNodeData* nodeData = static_cast<ParticleNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
uint64_t deletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
|
||||
|
||||
ParticleTree* tree = static_cast<ParticleTree*>(_tree);
|
||||
shouldSendDeletedParticles = tree->hasParitclesDeletedSince(deletedParticlesSentAt);
|
||||
}
|
||||
|
||||
return shouldSendDeletedParticles;
|
||||
}
|
||||
|
||||
int ParticleServer::sendSpecialPacket(Node* node) {
|
||||
unsigned char outputBuffer[MAX_PACKET_SIZE];
|
||||
size_t packetLength = 0;
|
||||
|
||||
ParticleNodeData* nodeData = static_cast<ParticleNodeData*>(node->getLinkedData());
|
||||
if (nodeData) {
|
||||
uint64_t deletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
|
||||
uint64_t deletePacketSentAt = usecTimestampNow();
|
||||
|
||||
ParticleTree* tree = static_cast<ParticleTree*>(_tree);
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 particles?
|
||||
while (hasMoreToSend) {
|
||||
hasMoreToSend = tree->encodeParitclesDeletedSince(deletedParticlesSentAt,
|
||||
outputBuffer, MAX_PACKET_SIZE, packetLength);
|
||||
|
||||
//qDebug() << "sending PACKET_TYPE_PARTICLE_ERASE packetLength:" << packetLength;
|
||||
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) outputBuffer, packetLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
}
|
||||
|
||||
nodeData->setLastDeletedParticlesSentAt(deletePacketSentAt);
|
||||
}
|
||||
|
||||
// TODO: caller is expecting a packetLength, what if we send more than one packet??
|
||||
return packetLength;
|
||||
}
|
||||
|
||||
void ParticleServer::pruneDeletedParticles() {
|
||||
ParticleTree* tree = static_cast<ParticleTree*>(_tree);
|
||||
if (tree->hasAnyDeletedParitcles()) {
|
||||
|
||||
//qDebug() << "there are some deleted particles to consider...";
|
||||
uint64_t earliestLastDeletedParticlesSent = usecTimestampNow() + 1; // in the future
|
||||
foreach (const SharedNodePointer& otherNode, NodeList::getInstance()->getNodeHash()) {
|
||||
if (otherNode->getLinkedData()) {
|
||||
ParticleNodeData* nodeData = static_cast<ParticleNodeData*>(otherNode->getLinkedData());
|
||||
uint64_t nodeLastDeletedParticlesSentAt = nodeData->getLastDeletedParticlesSentAt();
|
||||
if (nodeLastDeletedParticlesSentAt < earliestLastDeletedParticlesSent) {
|
||||
earliestLastDeletedParticlesSent = nodeLastDeletedParticlesSentAt;
|
||||
}
|
||||
}
|
||||
}
|
||||
//qDebug() << "earliestLastDeletedParticlesSent=" << earliestLastDeletedParticlesSent;
|
||||
tree->forgetParitclesDeletedBefore(earliestLastDeletedParticlesSent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
|
||||
/// Handles assignments of type ParticleServer - sending particles to various clients.
|
||||
class ParticleServer : public OctreeServer, public NewlyCreatedParticleHook {
|
||||
public:
|
||||
Q_OBJECT
|
||||
public:
|
||||
ParticleServer(const unsigned char* dataBuffer, int numBytes);
|
||||
~ParticleServer();
|
||||
|
||||
// Subclasses must implement these methods
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
|
||||
virtual Octree* createTree();
|
||||
virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
|
||||
|
@ -30,12 +31,17 @@ public:
|
|||
virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; }
|
||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_PARTICLES_PERSIST_FILE; }
|
||||
|
||||
|
||||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketToSend(Node* node);
|
||||
virtual int sendSpecialPacket(Node* node);
|
||||
|
||||
virtual void particleCreated(const Particle& newParticle, Node* senderNode);
|
||||
|
||||
public slots:
|
||||
void pruneDeletedParticles();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,48 @@ bool ParticleTree::handlesEditPacketType(PACKET_TYPE packetType) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
class FindAndDeleteParticlesArgs {
|
||||
public:
|
||||
QList<uint32_t> _idsToDelete;
|
||||
};
|
||||
|
||||
bool ParticleTree::findAndDeleteOperation(OctreeElement* element, void* extraData) {
|
||||
//qDebug() << "findAndDeleteOperation()";
|
||||
|
||||
FindAndDeleteParticlesArgs* args = static_cast< FindAndDeleteParticlesArgs*>(extraData);
|
||||
|
||||
// if we've found and deleted all our target particles, then we can stop looking
|
||||
if (args->_idsToDelete.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
|
||||
|
||||
//qDebug() << "findAndDeleteOperation() args->_idsToDelete.size():" << args->_idsToDelete.size();
|
||||
|
||||
for (QList<uint32_t>::iterator it = args->_idsToDelete.begin(); it != args->_idsToDelete.end(); it++) {
|
||||
uint32_t particleID = *it;
|
||||
//qDebug() << "findAndDeleteOperation() particleID:" << particleID;
|
||||
|
||||
if (particleTreeElement->removeParticleWithID(particleID)) {
|
||||
// if the particle was in this element, then remove it from our search list.
|
||||
//qDebug() << "findAndDeleteOperation() it = args->_idsToDelete.erase(it)";
|
||||
it = args->_idsToDelete.erase(it);
|
||||
}
|
||||
|
||||
if (it == args->_idsToDelete.end()) {
|
||||
//qDebug() << "findAndDeleteOperation() breaking";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we've found and deleted all our target particles, then we can stop looking
|
||||
if (args->_idsToDelete.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class FindAndUpdateParticleArgs {
|
||||
public:
|
||||
|
@ -172,21 +214,17 @@ int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* p
|
|||
// we handle these types of "edit" packets
|
||||
switch (packetType) {
|
||||
case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: {
|
||||
|
||||
qDebug() << " got PACKET_TYPE_PARTICLE_ADD_OR_EDIT... ";
|
||||
|
||||
//qDebug() << " got PACKET_TYPE_PARTICLE_ADD_OR_EDIT... ";
|
||||
Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this);
|
||||
qDebug() << "PACKET_TYPE_PARTICLE_ADD_OR_EDIT... calling storeParticle() ";
|
||||
storeParticle(newParticle, senderNode);
|
||||
qDebug() << "PACKET_TYPE_PARTICLE_ADD_OR_EDIT... AFTER storeParticle() newParticle.getShouldDie()=" << newParticle.getShouldDie();
|
||||
if (newParticle.isNewlyCreated()) {
|
||||
qDebug() << "PACKET_TYPE_PARTICLE_ADD_OR_EDIT... calling notifyNewlyCreatedParticle() ";
|
||||
notifyNewlyCreatedParticle(newParticle, senderNode);
|
||||
qDebug() << "PACKET_TYPE_PARTICLE_ADD_OR_EDIT... AFTER notifyNewlyCreatedParticle() ";
|
||||
}
|
||||
qDebug() << " DONE... PACKET_TYPE_PARTICLE_ADD_OR_EDIT... ";
|
||||
//qDebug() << " DONE... PACKET_TYPE_PARTICLE_ADD_OR_EDIT... ";
|
||||
} break;
|
||||
|
||||
// TODO: wire in support here for server to get PACKET_TYPE_PARTICLE_ERASE messages
|
||||
// instead of using PACKET_TYPE_PARTICLE_ADD_OR_EDIT messages to delete particles
|
||||
case PACKET_TYPE_PARTICLE_ERASE: {
|
||||
processedBytes = 0;
|
||||
} break;
|
||||
|
@ -254,6 +292,12 @@ void ParticleTree::update() {
|
|||
|
||||
if (!shouldDie && treeBounds.contains(args._movingParticles[i].getPosition())) {
|
||||
storeParticle(args._movingParticles[i]);
|
||||
} else {
|
||||
uint32_t particleID = args._movingParticles[i].getID();
|
||||
uint64_t deletedAt = usecTimestampNow();
|
||||
_recentlyDeletedParticlesLock.lockForWrite();
|
||||
_recentlyDeletedParticleIDs.insert(deletedAt, particleID);
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,3 +306,143 @@ void ParticleTree::update() {
|
|||
}
|
||||
|
||||
|
||||
bool ParticleTree::hasParitclesDeletedSince(uint64_t sinceTime) {
|
||||
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
|
||||
bool hasSomethingNewer = false;
|
||||
|
||||
_recentlyDeletedParticlesLock.lockForRead();
|
||||
QMultiMap<uint64_t, uint32_t>::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
|
||||
while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
|
||||
//qDebug() << "considering... time/key:" << iterator.key();
|
||||
if (iterator.key() > sinceTime) {
|
||||
//qDebug() << "YES newer... time/key:" << iterator.key();
|
||||
hasSomethingNewer = true;
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
return hasSomethingNewer;
|
||||
}
|
||||
|
||||
// sinceTime is an in/out parameter - it will be side effected with the last time sent out
|
||||
bool ParticleTree::encodeParitclesDeletedSince(uint64_t& sinceTime, unsigned char* outputBuffer, size_t maxLength,
|
||||
size_t& outputLength) {
|
||||
|
||||
bool hasMoreToSend = true;
|
||||
|
||||
unsigned char* copyAt = outputBuffer;
|
||||
size_t numBytesPacketHeader = populateTypeAndVersion(outputBuffer, PACKET_TYPE_PARTICLE_ERASE);
|
||||
copyAt += numBytesPacketHeader;
|
||||
outputLength = numBytesPacketHeader;
|
||||
|
||||
uint16_t numberOfIds = 0; // placeholder for now
|
||||
unsigned char* numberOfIDsAt = copyAt;
|
||||
memcpy(copyAt, &numberOfIds, sizeof(numberOfIds));
|
||||
copyAt += sizeof(numberOfIds);
|
||||
outputLength += sizeof(numberOfIds);
|
||||
|
||||
// we keep a multi map of particle IDs to timestamps, we only want to include the particle IDs that have been
|
||||
// deleted since we last sent to this node
|
||||
_recentlyDeletedParticlesLock.lockForRead();
|
||||
QMultiMap<uint64_t, uint32_t>::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
|
||||
while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
|
||||
QList<uint32_t> values = _recentlyDeletedParticleIDs.values(iterator.key());
|
||||
for (int valueItem = 0; valueItem < values.size(); ++valueItem) {
|
||||
//qDebug() << "considering... " << iterator.key() << ": " << values.at(valueItem);
|
||||
|
||||
// if the timestamp is more recent then out last sent time, include it
|
||||
if (iterator.key() > sinceTime) {
|
||||
//qDebug() << "including... " << iterator.key() << ": " << values.at(valueItem);
|
||||
uint32_t particleID = values.at(valueItem);
|
||||
memcpy(copyAt, &particleID, sizeof(particleID));
|
||||
copyAt += sizeof(particleID);
|
||||
outputLength += sizeof(particleID);
|
||||
numberOfIds++;
|
||||
|
||||
// check to make sure we have room for one more id...
|
||||
if (outputLength + sizeof(uint32_t) > maxLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check to make sure we have room for one more id...
|
||||
if (outputLength + sizeof(uint32_t) > maxLength) {
|
||||
|
||||
// let our caller know how far we got
|
||||
sinceTime = iterator.key();
|
||||
break;
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
|
||||
// if we got to the end, then we're done sending
|
||||
if (iterator == _recentlyDeletedParticleIDs.constEnd()) {
|
||||
hasMoreToSend = false;
|
||||
}
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
|
||||
// replace the correct count for ids included
|
||||
memcpy(numberOfIDsAt, &numberOfIds, sizeof(numberOfIds));
|
||||
|
||||
return hasMoreToSend;
|
||||
}
|
||||
|
||||
// called by the server when it knows all nodes have been sent deleted packets
|
||||
void ParticleTree::forgetParitclesDeletedBefore(uint64_t sinceTime) {
|
||||
_recentlyDeletedParticlesLock.lockForWrite();
|
||||
QMultiMap<uint64_t, uint32_t>::const_iterator iterator = _recentlyDeletedParticleIDs.constBegin();
|
||||
while (iterator != _recentlyDeletedParticleIDs.constEnd()) {
|
||||
//qDebug() << "considering... time/key:" << iterator.key();
|
||||
if (iterator.key() <= sinceTime) {
|
||||
//qDebug() << "YES older... time/key:" << iterator.key();
|
||||
_recentlyDeletedParticleIDs.remove(iterator.key());
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
_recentlyDeletedParticlesLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr,
|
||||
Node* sourceNode) {
|
||||
//qDebug() << "ParticleTree::processEraseMessage()...";
|
||||
|
||||
const unsigned char* packetData = (const unsigned char*)dataByteArray.constData();
|
||||
const unsigned char* dataAt = packetData;
|
||||
size_t packetLength = dataByteArray.size();
|
||||
|
||||
size_t numBytesPacketHeader = numBytesForPacketHeader(packetData);
|
||||
size_t processedBytes = numBytesPacketHeader;
|
||||
dataAt += numBytesPacketHeader;
|
||||
|
||||
uint16_t numberOfIds = 0; // placeholder for now
|
||||
memcpy(&numberOfIds, dataAt, sizeof(numberOfIds));
|
||||
dataAt += sizeof(numberOfIds);
|
||||
processedBytes += sizeof(numberOfIds);
|
||||
|
||||
//qDebug() << "got erase message for numberOfIds:" << numberOfIds;
|
||||
|
||||
if (numberOfIds > 0) {
|
||||
FindAndDeleteParticlesArgs args;
|
||||
|
||||
for (size_t i = 0; i < numberOfIds; i++) {
|
||||
if (processedBytes + sizeof(uint32_t) > packetLength) {
|
||||
//qDebug() << "bailing?? processedBytes:" << processedBytes << " packetLength:" << packetLength;
|
||||
break; // bail to prevent buffer overflow
|
||||
}
|
||||
|
||||
uint32_t particleID = 0; // placeholder for now
|
||||
memcpy(&particleID, dataAt, sizeof(particleID));
|
||||
dataAt += sizeof(particleID);
|
||||
processedBytes += sizeof(particleID);
|
||||
|
||||
//qDebug() << "got erase message for particleID:" << particleID;
|
||||
args._idsToDelete.push_back(particleID);
|
||||
}
|
||||
|
||||
// calling recurse to actually delete the particles
|
||||
//qDebug() << "calling recurse to actually delete the particles";
|
||||
recurseTreeWithOperation(findAndDeleteOperation, &args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,13 @@ public:
|
|||
void addNewlyCreatedHook(NewlyCreatedParticleHook* hook);
|
||||
void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook);
|
||||
|
||||
bool hasAnyDeletedParitcles() const { return _recentlyDeletedParticleIDs.size() > 0; }
|
||||
bool hasParitclesDeletedSince(uint64_t sinceTime);
|
||||
bool encodeParitclesDeletedSince(uint64_t& sinceTime, unsigned char* packetData, size_t maxLength, size_t& outputLength);
|
||||
void forgetParitclesDeletedBefore(uint64_t sinceTime);
|
||||
|
||||
void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode);
|
||||
|
||||
private:
|
||||
|
||||
static bool updateOperation(OctreeElement* element, void* extraData);
|
||||
|
@ -53,11 +60,16 @@ private:
|
|||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
static bool pruneOperation(OctreeElement* element, void* extraData);
|
||||
static bool findByIDOperation(OctreeElement* element, void* extraData);
|
||||
static bool findAndDeleteOperation(OctreeElement* element, void* extraData);
|
||||
|
||||
void notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode);
|
||||
|
||||
QReadWriteLock _newlyCreatedHooksLock;
|
||||
std::vector<NewlyCreatedParticleHook*> _newlyCreatedHooks;
|
||||
|
||||
|
||||
QReadWriteLock _recentlyDeletedParticlesLock;
|
||||
QMultiMap<uint64_t, uint32_t> _recentlyDeletedParticleIDs;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__ParticleTree__) */
|
||||
|
|
|
@ -200,6 +200,20 @@ const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
|
|||
return foundParticle;
|
||||
}
|
||||
|
||||
bool ParticleTreeElement::removeParticleWithID(uint32_t id) {
|
||||
bool foundParticle = false;
|
||||
uint16_t numberOfParticles = _particles->size();
|
||||
for (uint16_t i = 0; i < numberOfParticles; i++) {
|
||||
if ((*_particles)[i].getID() == id) {
|
||||
foundParticle = true;
|
||||
_particles->removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return foundParticle;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args) {
|
||||
|
|
|
@ -28,11 +28,11 @@ public:
|
|||
|
||||
class ParticleTreeElement : public OctreeElement {
|
||||
friend class ParticleTree; // to allow createElement to new us...
|
||||
|
||||
|
||||
ParticleTreeElement(unsigned char* octalCode = NULL);
|
||||
|
||||
virtual OctreeElement* createNewElement(unsigned char* octalCode = NULL) const;
|
||||
|
||||
|
||||
public:
|
||||
virtual ~ParticleTreeElement();
|
||||
|
||||
|
@ -40,31 +40,31 @@ public:
|
|||
ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); }
|
||||
|
||||
// methods you can and should override to implement your tree functionality
|
||||
|
||||
|
||||
/// Adds a child to the current element. Override this if there is additional child initialization your class needs.
|
||||
virtual ParticleTreeElement* addChildAtIndex(int index);
|
||||
|
||||
/// Override this to implement LOD averaging on changes to the tree.
|
||||
/// Override this to implement LOD averaging on changes to the tree.
|
||||
virtual void calculateAverageFromChildren();
|
||||
|
||||
/// Override this to implement LOD collapsing and identical child pruning on changes to the tree.
|
||||
/// Override this to implement LOD collapsing and identical child pruning on changes to the tree.
|
||||
virtual bool collapseChildren();
|
||||
|
||||
/// Should this element be considered to have content in it. This will be used in collision and ray casting methods.
|
||||
/// By default we assume that only leaves are actual content, but some octrees may have different semantics.
|
||||
virtual bool hasContent() const { return isLeaf(); }
|
||||
|
||||
|
||||
/// Override this to break up large octree elements when an edit operation is performed on a smaller octree element.
|
||||
/// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the
|
||||
/// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the
|
||||
/// meaningful split would be to break the larger cube into smaller cubes of the same color/texture.
|
||||
virtual void splitChildren() { }
|
||||
|
||||
|
||||
/// Override to indicate that this element requires a split before editing lower elements in the octree
|
||||
virtual bool requiresSplit() const { return false; }
|
||||
|
||||
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
|
||||
virtual bool appendElementData(OctreePacketData* packetData) const;
|
||||
|
||||
|
||||
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
|
||||
/// from the network.
|
||||
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
|
||||
|
@ -76,21 +76,23 @@ public:
|
|||
virtual bool isRendered() const { return getShouldRender(); }
|
||||
virtual bool deleteApproved() const { return !hasParticles(); }
|
||||
|
||||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const;
|
||||
|
||||
const QList<Particle>& getParticles() const { return *_particles; }
|
||||
QList<Particle>& getParticles() { return *_particles; }
|
||||
bool hasParticles() const { return _particles->size() > 0; }
|
||||
|
||||
|
||||
void update(ParticleTreeUpdateArgs& args);
|
||||
void setTree(ParticleTree* tree) { _myTree = tree; }
|
||||
|
||||
|
||||
bool containsParticle(const Particle& particle) const;
|
||||
bool updateParticle(const Particle& particle);
|
||||
const Particle* getClosestParticle(glm::vec3 position) const;
|
||||
const Particle* getParticleWithID(uint32_t id) const;
|
||||
|
||||
|
||||
bool removeParticleWithID(uint32_t id);
|
||||
|
||||
|
||||
protected:
|
||||
virtual void init(unsigned char * octalCode);
|
||||
|
|
|
@ -32,7 +32,7 @@ Octree* VoxelServer::createTree() {
|
|||
return new VoxelTree(true);
|
||||
}
|
||||
|
||||
bool VoxelServer::hasSpecialPacketToSend() {
|
||||
bool VoxelServer::hasSpecialPacketToSend(Node* node) {
|
||||
bool shouldSendEnvironments = _sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, OCTREE_SEND_INTERVAL_USECS);
|
||||
return shouldSendEnvironments;
|
||||
}
|
||||
|
@ -41,11 +41,11 @@ int VoxelServer::sendSpecialPacket(Node* node) {
|
|||
int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
|
||||
int envPacketLength = numBytesPacketHeader;
|
||||
int environmentsToSend = getSendMinimalEnvironment() ? 1 : getEnvironmentDataCount();
|
||||
|
||||
|
||||
for (int i = 0; i < environmentsToSend; i++) {
|
||||
envPacketLength += getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength);
|
||||
}
|
||||
|
||||
|
||||
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength,
|
||||
node->getActiveSocket()->getAddress(),
|
||||
node->getActiveSocket()->getPort());
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
/// Handles assignments of type VoxelServer - sending voxels to various clients.
|
||||
class VoxelServer : public OctreeServer {
|
||||
public:
|
||||
public:
|
||||
VoxelServer(const unsigned char* dataBuffer, int numBytes);
|
||||
~VoxelServer();
|
||||
|
||||
|
@ -33,7 +33,7 @@ public:
|
|||
EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; }
|
||||
int getEnvironmentDataCount() const { return sizeof(_environmentData)/sizeof(EnvironmentData); }
|
||||
|
||||
// Subclasses must implement these methods
|
||||
// Subclasses must implement these methods
|
||||
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
|
||||
virtual Octree* createTree();
|
||||
virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
|
||||
|
@ -41,10 +41,10 @@ public:
|
|||
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
|
||||
virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; }
|
||||
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; }
|
||||
|
||||
|
||||
// subclass may implement these method
|
||||
virtual void beforeRun();
|
||||
virtual bool hasSpecialPacketToSend();
|
||||
virtual bool hasSpecialPacketToSend(Node* node);
|
||||
virtual int sendSpecialPacket(Node* node);
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue