diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8a75902463..9075e235a9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -235,6 +235,11 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // Tell our voxel edit sender about our known jurisdictions _voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions); _particleEditSender.setServerJurisdictions(&_particleServerJurisdictions); + + // For now we're going to set the PPS for outbound packets to be super high, this is + // probably not the right long term solution. But for now, we're going to do this to + // allow you to move a particle around in your hand + _particleEditSender.setPacketsPerSecond(3000); // super high!! } Application::~Application() { @@ -1508,23 +1513,23 @@ void Application::shootParticle() { float damping = DEFAULT_DAMPING * 0.01f; QString updateScript(""); - makeParticle(position / (float)TREE_SCALE, radius, color, - velocity / (float)TREE_SCALE, gravity, damping, updateScript); - + ParticleEditHandle* particleEditHandle = makeParticle(position / (float)TREE_SCALE, radius, color, + velocity / (float)TREE_SCALE, gravity, damping, updateScript); + + // If we wanted to be able to edit this particle after shooting, then we could store this value + // and use it for editing later. But we don't care about that for "shooting" and therefore we just + // clean up our memory now. deleting a ParticleEditHandle does not effect the underlying particle, + // it just removes your ability to edit that particle later. + delete particleEditHandle; } -void Application::makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, +// Caller is responsible for managing this EditableParticle +ParticleEditHandle* Application::makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, QString updateScript) { - // setup a ParticleDetail struct with the data - ParticleDetail addParticleDetail = { position, radius, {color.red, color.green, color.blue }, - velocity, gravity, damping, updateScript }; - - // queue the packet - _particleEditSender.queueParticleEditMessages(PACKET_TYPE_PARTICLE_ADD, 1, &addParticleDetail); - - // release them - _particleEditSender.releaseQueuedMessages(); + ParticleEditHandle* particleEditHandle = new ParticleEditHandle(&_particleEditSender, _particles.getTree()); + particleEditHandle->createParticle(position, radius, color, velocity, gravity, damping, updateScript); + return particleEditHandle; } @@ -4297,6 +4302,11 @@ void* Application::networkReceive(void* args) { Q_ARG(QByteArray, QByteArray((char*) app->_incomingPacket, bytesReceived))); break; + case PACKET_TYPE_PARTICLE_ADD_RESPONSE: + // look up our ParticleEditHanders.... + ParticleEditHandle::handleAddResponse(app->_incomingPacket, bytesReceived); + break; + case PACKET_TYPE_PARTICLE_DATA: case PACKET_TYPE_VOXEL_DATA: case PACKET_TYPE_VOXEL_ERASE: diff --git a/interface/src/Application.h b/interface/src/Application.h index 329d50e830..38c8ea4f64 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -65,6 +65,7 @@ #include "ui/RearMirrorTools.h" #include "ui/LodToolsDialog.h" #include "ParticleTreeRenderer.h" +#include "ParticleEditHandle.h" class QAction; class QActionGroup; @@ -120,7 +121,7 @@ public: void wheelEvent(QWheelEvent* event); void shootParticle(); // shoots a particle in the direction you're looking - void makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, + ParticleEditHandle* makeParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, QString updateScript); void makeVoxel(glm::vec3 position, diff --git a/interface/src/ParticleTreeRenderer.h b/interface/src/ParticleTreeRenderer.h index d835a27f52..ba11799776 100644 --- a/interface/src/ParticleTreeRenderer.h +++ b/interface/src/ParticleTreeRenderer.h @@ -34,6 +34,9 @@ public: virtual void renderElement(OctreeElement* element, RenderArgs* args); void update(); + + ParticleTree* getTree() { return (ParticleTree*)_tree; } + protected: }; diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp index 6576b0c2fc..9e0eecb2e0 100644 --- a/interface/src/VoxelPacketProcessor.cpp +++ b/interface/src/VoxelPacketProcessor.cpp @@ -58,15 +58,21 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns Node* voxelServer = NodeList::getInstance()->nodeWithAddress(senderSockAddr); if (voxelServer && *voxelServer->getActiveSocket() == senderSockAddr) { - if (packetData[0] == PACKET_TYPE_PARTICLE_DATA) { - //printf("VoxelPacketProcessor::processPacket().... got PACKET_TYPE_PARTICLE_DATA\n"); - app->_particles.processDatagram(QByteArray((char*) packetData, messageLength), senderSockAddr); - } else if (packetData[0] == PACKET_TYPE_ENVIRONMENT_DATA) { - app->_environment.parseData(senderSockAddr, packetData, messageLength); - } else { - app->_voxels.setDataSourceUUID(voxelServer->getUUID()); - app->_voxels.parseData(packetData, messageLength); - app->_voxels.setDataSourceUUID(QUuid()); + + switch(packetData[0]) { + case PACKET_TYPE_PARTICLE_DATA: { + app->_particles.processDatagram(QByteArray((char*) packetData, messageLength), senderSockAddr); + } break; + + case PACKET_TYPE_ENVIRONMENT_DATA: { + app->_environment.parseData(senderSockAddr, packetData, messageLength); + } break; + + default : { + app->_voxels.setDataSourceUUID(voxelServer->getUUID()); + app->_voxels.parseData(packetData, messageLength); + app->_voxels.setDataSourceUUID(QUuid()); + } break; } } } diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index e3a45d8711..e0170d4be5 100755 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -22,8 +22,9 @@ using namespace std; const float FINGERTIP_VOXEL_SIZE = 0.05; const int TOY_BALL_HAND = 1; const float TOY_BALL_RADIUS = 0.05f; -const glm::vec3 TOYBALL_GRAVITY (0, -1, 0); -float TOYBALL_DAMPING = 1.f; +const float TOY_BALL_DAMPING = 0.99f; +const glm::vec3 TOY_BALL_GRAVITY = glm::vec3(0,-1,0); +const QString TOY_BALL_UPDATE_SCRIPT(""); const float PALM_COLLISION_RADIUS = 0.03f; Hand::Hand(Avatar* owningAvatar) : @@ -38,8 +39,10 @@ Hand::Hand(Avatar* owningAvatar) : _toyBallPosition(0), _toyBallVelocity(0), _toyBallInHand(false), + _hasToyBall(false), + _ballParticleEditHandle(NULL), _pitchUpdate(0) - { +{ } void Hand::init() { @@ -56,55 +59,92 @@ void Hand::reset() { } void Hand::simulateToyBall(PalmData& palm, const glm::vec3& fingerTipPosition, float deltaTime) { - if (palm.getControllerButtons() & BUTTON_FWD) { - // If grabbing toy ball, add forces to it - if (!_toyBallInHand) { - // Test for whether close enough to catch and catch - bool isCaught = true; - if (isCaught) { - _toyBallInHand = true; - } - } - if (_toyBallInHand) { - // Ball is in hand - _toyBallPosition = fingerTipPosition; - _toyBallVelocity = glm::vec3(0); - - } - } else { - // If toy ball just released, add velocity to it! - if (_toyBallInHand) { - _toyBallInHand = false; - glm::vec3 handVelocity = palm.getRawVelocity(); - glm::vec3 fingerTipVelocity = palm.getTipVelocity(); - glm::quat avatarRotation = _owningAvatar->getOrientation(); - //printVector(avatarRotation * handVelocity); - _toyBallVelocity += avatarRotation * fingerTipVelocity; + // Is the controller button being held down.... + if (palm.getControllerButtons() & BUTTON_FWD) { + // If grabbing toy ball, add forces to it. + + // If we don't currently have a ball in hand, then create it... + if (!_toyBallInHand) { + // Test for whether close enough to catch and catch.... + + // isCaught is also used as "creating" a new ball... for now, this section is the + // create new ball portion of the code... + bool isCaught = true; + if (isCaught) { + _toyBallInHand = true; + _hasToyBall = true; + // create the ball, call MakeParticle, and use the resulting ParticleEditHandle to + // manage the newly created particle. // Create a particle on the particle server xColor color = { 255, 255, 0}; // Slightly different color on server - glm::vec3 gravity = glm::vec3(0, -1, 0); - float damping = 0.99f; - QString updateScript(""); - Application::getInstance()->makeParticle(fingerTipPosition / (float)TREE_SCALE, + + _ballParticleEditHandle = Application::getInstance()->makeParticle(fingerTipPosition / (float)TREE_SCALE, TOY_BALL_RADIUS / (float) TREE_SCALE, color, _toyBallVelocity / (float)TREE_SCALE, - gravity / (float) TREE_SCALE, damping, updateScript); + TOY_BALL_GRAVITY / (float) TREE_SCALE, + TOY_BALL_DAMPING, + TOY_BALL_UPDATE_SCRIPT); } } - // Simulate toy ball - _toyBallPosition += _toyBallVelocity * deltaTime; + if (_toyBallInHand) { + // Ball is in hand + _toyBallPosition = fingerTipPosition; + _toyBallVelocity = glm::vec3(0); + + xColor color = { 255, 255, 0}; // Slightly different color on server + + _ballParticleEditHandle->updateParticle(fingerTipPosition / (float)TREE_SCALE, + TOY_BALL_RADIUS / (float) TREE_SCALE, + color, + _toyBallVelocity / (float)TREE_SCALE, + TOY_BALL_GRAVITY / (float) TREE_SCALE, + TOY_BALL_DAMPING, + TOY_BALL_UPDATE_SCRIPT); + } + } else { + // If toy ball just released, add velocity to it! + if (_toyBallInHand) { - if (!_toyBallInHand) { - _toyBallVelocity += TOYBALL_GRAVITY * deltaTime; + _toyBallInHand = false; + glm::vec3 handVelocity = palm.getRawVelocity(); + glm::vec3 fingerTipVelocity = palm.getTipVelocity(); + glm::quat avatarRotation = _owningAvatar->getOrientation(); + //printVector(avatarRotation * handVelocity); + _toyBallVelocity += avatarRotation * fingerTipVelocity; + + xColor color = { 255, 255, 0}; // Slightly different color on server + _ballParticleEditHandle->updateParticle(fingerTipPosition / (float)TREE_SCALE, + TOY_BALL_RADIUS / (float) TREE_SCALE, + color, + _toyBallVelocity / (float)TREE_SCALE, + TOY_BALL_GRAVITY / (float) TREE_SCALE, + TOY_BALL_DAMPING, + TOY_BALL_UPDATE_SCRIPT); + + // after releasing the ball, we free our ParticleEditHandle so we can't edit it further + // note: deleting the edit handle doesn't effect the actual particle + delete _ballParticleEditHandle; + _ballParticleEditHandle = NULL; + } - if (_toyBallPosition.y < 0.f) { - _toyBallPosition.y = 0.f; - _toyBallVelocity.y *= -1.f; - } - _toyBallVelocity -= (_toyBallVelocity * TOYBALL_DAMPING) * deltaTime; + } + // Simulate toy ball + _toyBallPosition += _toyBallVelocity * deltaTime; + if (!_toyBallInHand) { + _toyBallVelocity += TOY_BALL_GRAVITY * deltaTime; + } + if (_toyBallPosition.y < 0.f) { + _toyBallPosition.y = 0.f; + _toyBallVelocity.y *= -1.f; + } + + if (_hasToyBall) { + _toyBallVelocity -= (_toyBallVelocity * TOY_BALL_DAMPING) * deltaTime; + //printf("applying damping to TOY_BALL deltaTime=%f\n",deltaTime); + } } void Hand::simulate(float deltaTime, bool isMine) { diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index a8a6b6106e..1d3dc7b724 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -18,6 +18,7 @@ #include #include +#include #include "Balls.h" #include "InterfaceConfig.h" @@ -26,9 +27,11 @@ #include "devices/SerialInterface.h" #include "VoxelSystem.h" + class Avatar; class ProgramObject; + class Hand : public HandData { public: Hand(Avatar* owningAvatar); @@ -95,7 +98,9 @@ private: glm::vec3 _toyBallPosition; glm::vec3 _toyBallVelocity; - bool _toyBallInHand; + bool _toyBallInHand; + bool _hasToyBall; + ParticleEditHandle* _ballParticleEditHandle; float _pitchUpdate; diff --git a/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp index fb8c3b92a8..6e194fc982 100644 --- a/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp +++ b/libraries/octree-server/src/OctreeInboundPacketProcessor.cpp @@ -42,7 +42,7 @@ void OctreeInboundPacketProcessor::resetStats() { void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, unsigned char* packetData, ssize_t packetLength) { - bool debugProcessPacket = true; //_myServer->wantsVerboseDebug(); + bool debugProcessPacket = _myServer->wantsVerboseDebug(); if (debugProcessPacket) { printf("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength); @@ -56,6 +56,8 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA if (_myServer->getOctree()->handlesEditPacketType(packetType)) { PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE",debugProcessPacket); _receivedPacketCount++; + + Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader))); uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence)))); @@ -85,7 +87,7 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA _myServer->getOctree()->lockForWrite(); uint64_t startProcess = usecTimestampNow(); int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType, - packetData, packetLength, editData, maxSize); + packetData, packetLength, editData, maxSize, senderNode); _myServer->getOctree()->unlock(); uint64_t endProcess = usecTimestampNow(); @@ -107,7 +109,6 @@ void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockA } // Make sure our Node and NodeList knows we've heard from this node. - Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr); QUuid& nodeUUID = DEFAULT_NODE_ID_REF; if (senderNode) { senderNode->setLastHeardMicrostamp(usecTimestampNow()); diff --git a/libraries/octree-server/src/OctreeSendThread.cpp b/libraries/octree-server/src/OctreeSendThread.cpp index eb53f5a3ad..4b770fbc43 100644 --- a/libraries/octree-server/src/OctreeSendThread.cpp +++ b/libraries/octree-server/src/OctreeSendThread.cpp @@ -506,23 +506,6 @@ int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, b trueBytesSent += _myServer->sendSpecialPacket(node); truePacketsSent++; packetsSentThisInterval++; - - /** - int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA); - int envPacketLength = numBytesPacketHeader; - int environmentsToSend = _myServer->getSendMinimalEnvironment() ? 1 : _myServer->getEnvironmentDataCount(); - - for (int i = 0; i < environmentsToSend; i++) { - envPacketLength += _myServer->getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength); - } - - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength, - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - trueBytesSent += envPacketLength; - truePacketsSent++; - packetsSentThisInterval++; - **/ } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 920fe04be7..1b4cee866a 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -185,7 +185,7 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool handlesEditPacketType(PACKET_TYPE packetType) const { return false; } virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, - unsigned char* editData, int maxLength) { return 0; } + unsigned char* editData, int maxLength, Node* senderNode) { return 0; } virtual void update() { }; // nothing to do by default diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 823ccbe05b..aa436fb690 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -27,9 +27,6 @@ OctreeRenderer::~OctreeRenderer() { } void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) { - - - bool showTimingDetails = false; // Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool extraDebugging = false; // Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging) PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram()",showTimingDetails); @@ -38,6 +35,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi int packetLength = dataByteArray.size(); unsigned char command = *packetData; + int numBytesPacketHeader = numBytesForPacketHeader(packetData); PACKET_TYPE expectedType = getExpectedPacketType(); diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 7340dd0c2f..767ae48691 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -43,7 +43,7 @@ public: virtual void renderElement(OctreeElement* element, RenderArgs* args) = 0; /// process incoming data - void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); + virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr); /// initialize and GPU/rendering related resources void init(); diff --git a/libraries/particle-server/src/ParticleServer.cpp b/libraries/particle-server/src/ParticleServer.cpp index bc6c60f712..b1b46b3133 100644 --- a/libraries/particle-server/src/ParticleServer.cpp +++ b/libraries/particle-server/src/ParticleServer.cpp @@ -21,7 +21,8 @@ ParticleServer::ParticleServer(const unsigned char* dataBuffer, int numBytes) : } ParticleServer::~ParticleServer() { - // nothing special to do here... + ParticleTree* tree = (ParticleTree*)_tree; + tree->removeNewlyCreatedHook(this); } OctreeQueryNode* ParticleServer::createOctreeQueryNode(Node* newNode) { @@ -29,9 +30,36 @@ OctreeQueryNode* ParticleServer::createOctreeQueryNode(Node* newNode) { } Octree* ParticleServer::createTree() { - return new ParticleTree(true); + ParticleTree* tree = new ParticleTree(true); + tree->addNewlyCreatedHook(this); + return tree; } void ParticleServer::beforeRun() { // nothing special to do... } + +void ParticleServer::particleCreated(const Particle& newParticle, Node* node) { + unsigned char outputBuffer[MAX_PACKET_SIZE]; + unsigned char* copyAt = outputBuffer; + + 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()); +} diff --git a/libraries/particle-server/src/ParticleServer.h b/libraries/particle-server/src/ParticleServer.h index 4655f697bb..5ace2ef7fc 100644 --- a/libraries/particle-server/src/ParticleServer.h +++ b/libraries/particle-server/src/ParticleServer.h @@ -12,10 +12,12 @@ #include +#include "Particle.h" #include "ParticleServerConsts.h" +#include "ParticleTree.h" /// Handles assignments of type ParticleServer - sending particles to various clients. -class ParticleServer : public OctreeServer { +class ParticleServer : public OctreeServer, public NewlyCreatedParticleHook { public: ParticleServer(const unsigned char* dataBuffer, int numBytes); ~ParticleServer(); @@ -32,6 +34,8 @@ public: // subclass may implement these method virtual void beforeRun(); + virtual void particleCreated(const Particle& newParticle, Node* senderNode); + private: }; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index f662f18c67..e0b24ad4dc 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -19,23 +19,27 @@ uint32_t Particle::_nextID = 0; Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - float damping, glm::vec3 gravity, QString updateScript) { + float damping, glm::vec3 gravity, QString updateScript, uint32_t id) { - init(position, radius, color, velocity, damping, gravity, updateScript); + init(position, radius, color, velocity, damping, gravity, updateScript, id); } Particle::Particle() { rgbColor noColor = { 0, 0, 0 }; - init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0), DEFAULT_DAMPING, DEFAULT_GRAVITY, DEFAULT_SCRIPT); + init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0), DEFAULT_DAMPING, DEFAULT_GRAVITY, DEFAULT_SCRIPT, NEW_PARTICLE); } Particle::~Particle() { } void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - float damping, glm::vec3 gravity, QString updateScript) { - _id = _nextID; - _nextID++; + float damping, glm::vec3 gravity, QString updateScript, uint32_t id) { + if (id == NEW_PARTICLE) { + _id = _nextID; + _nextID++; + } else { + _id = id; + } _lastUpdated = usecTimestampNow(); _position = position; @@ -146,6 +150,8 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef _updateScript = tempString; dataAt += scriptLength; bytesRead += scriptLength; + + //printf("Particle::readParticleDataFromBuffer()... "); debugDump(); } return bytesRead; } @@ -163,6 +169,32 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe // 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); + + // special case for handling "new" particles + if (editID == NEW_PARTICLE) { + // If this is a NEW_PARTICLE, 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); + newParticle.setCreatorTokenID(creatorTokenID); + newParticle._newlyCreated = true; + } else { + newParticle._id = editID; + newParticle._newlyCreated = false; + } + + // lastUpdated + memcpy(&newParticle._lastUpdated, dataAt, sizeof(newParticle._lastUpdated)); + dataAt += sizeof(newParticle._lastUpdated); + processedBytes += sizeof(newParticle._lastUpdated); // radius memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius)); @@ -204,9 +236,20 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe dataAt += scriptLength; processedBytes += scriptLength; + const bool wantDebugging = false; + if (wantDebugging) { + printf("Particle::fromEditPacket()...\n"); + printf(" Particle id in packet:%u\n", editID); + newParticle.debugDump(); + } + return newParticle; } +void Particle::debugDump() const { + printf("Particle id :%u\n", _id); + printf(" last updated:%llu\n", _lastUpdated); +} bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, const ParticleDetail* details, unsigned char* bufferOut, int sizeIn, int& sizeOut) { @@ -217,7 +260,8 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, for (int i = 0; i < count && success; i++) { // get the octal code for the particle - unsigned char* octcode = pointToOctalCode(details[i].position.x, details[i].position.y, details[i].position.z, details[i].radius); + 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); @@ -233,6 +277,24 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, sizeOut += lengthOfOctcode; // Now add our edit content details... + + // id + memcpy(copyAt, &details[i].id, sizeof(details[i].id)); + copyAt += sizeof(details[i].id); + sizeOut += sizeof(details[i].id); + // special case for handling "new" particles + if (details[i].id == NEW_PARTICLE) { + // If this is a NEW_PARTICLE, 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, &details[i].creatorTokenID, sizeof(details[i].creatorTokenID)); + copyAt += sizeof(details[i].creatorTokenID); + sizeOut += sizeof(details[i].creatorTokenID); + } + + // lastUpdated + memcpy(copyAt, &details[i].lastUpdated, sizeof(details[i].lastUpdated)); + copyAt += sizeof(details[i].lastUpdated); + sizeOut += sizeof(details[i].lastUpdated); // radius memcpy(copyAt, &details[i].radius, sizeof(details[i].radius)); @@ -272,6 +334,14 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, int count, memcpy(copyAt, qPrintable(details[i].updateScript), scriptLength); copyAt += scriptLength; sizeOut += scriptLength; + + bool wantDebugging = false; + if (wantDebugging) { + printf("encodeParticleEditMessageDetails()....\n"); + printf("Particle id :%u\n", details[i].id); + printf(" last updated:%llu\n", details[i].lastUpdated); + printf(" nextID:%u\n", _nextID); + } } // cleanup delete[] octcode; @@ -308,6 +378,7 @@ void Particle::update() { // handle damping glm::vec3 dampingResistance = _velocity * _damping; _velocity -= dampingResistance * timeElapsed; + //printf("applying damping to Particle timeElapsed=%f\n",timeElapsed); _lastUpdated = now; } diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index e56697465e..764f10c87d 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -19,8 +19,11 @@ #include #include +const uint32_t NEW_PARTICLE = 0xFFFFFFFF; class ParticleDetail { public: + uint32_t id; + uint64_t lastUpdated; glm::vec3 position; float radius; rgbColor color; @@ -28,6 +31,7 @@ public: glm::vec3 gravity; float damping; QString updateScript; + uint32_t creatorTokenID; }; const float DEFAULT_DAMPING = 0.99f; @@ -39,14 +43,15 @@ class Particle { public: Particle(); Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - float damping = DEFAULT_DAMPING, glm::vec3 gravity = DEFAULT_GRAVITY, QString updateScript = DEFAULT_SCRIPT); + float damping = DEFAULT_DAMPING, glm::vec3 gravity = DEFAULT_GRAVITY, QString updateScript = DEFAULT_SCRIPT, + uint32_t id = NEW_PARTICLE); - /// creates an NEW particle from an PACKET_TYPE_PARTICLE_ADD edit data buffer + /// creates an NEW particle from an PACKET_TYPE_PARTICLE_ADD_OR_EDIT edit data buffer static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes); virtual ~Particle(); virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - float damping, glm::vec3 gravity, QString updateScript); + float damping, glm::vec3 gravity, QString updateScript, uint32_t id); const glm::vec3& getPosition() const { return _position; } const rgbColor& getColor() const { return _color; } @@ -59,6 +64,8 @@ public: uint32_t getID() const { return _id; } bool getShouldDie() const { return _shouldDie; } QString getUpdateScript() const { return _updateScript; } + uint32_t getCreatorTokenID() const { return _creatorTokenID; } + bool isNewlyCreated() const { return _newlyCreated; } void setPosition(const glm::vec3& value) { _position = value; } void setVelocity(const glm::vec3& value) { _velocity = value; } @@ -73,6 +80,7 @@ public: void setDamping(float value) { _damping = value; } void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; } void setUpdateScript(QString updateScript) { _updateScript = updateScript; } + void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; } bool appendParticleData(OctreePacketData* packetData) const; int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); @@ -83,6 +91,7 @@ public: void update(); + void debugDump() const; protected: void runScript(); static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3); @@ -101,6 +110,9 @@ protected: glm::vec3 _gravity; float _damping; QString _updateScript; + + uint32_t _creatorTokenID; + bool _newlyCreated; }; class ParticleScriptObject : public QObject { @@ -115,16 +127,19 @@ public slots: glm::vec3 getGravity() const { return _particle->getGravity(); } float getDamping() const { return _particle->getDamping(); } float getRadius() const { return _particle->getRadius(); } + bool setShouldDie() { return _particle->getShouldDie(); } - void setPosition(glm::vec3 value) { return _particle->setPosition(value); } - void setVelocity(glm::vec3 value) { return _particle->setVelocity(value); } - void setGravity(glm::vec3 value) { return _particle->setGravity(value); } - void setDamping(float value) { return _particle->setDamping(value); } - void setColor(xColor value) { return _particle->setColor(value); } - void setRadius(float value) { return _particle->setRadius(value); } + void setPosition(glm::vec3 value) { _particle->setPosition(value); } + void setVelocity(glm::vec3 value) { _particle->setVelocity(value); } + void setGravity(glm::vec3 value) { _particle->setGravity(value); } + void setDamping(float value) { _particle->setDamping(value); } + void setColor(xColor value) { _particle->setColor(value); } + void setRadius(float value) { _particle->setRadius(value); } + void setShouldDie(bool value) { _particle->setShouldDie(value); } private: Particle* _particle; }; + #endif /* defined(__hifi__Particle__) */ \ No newline at end of file diff --git a/libraries/particles/src/ParticleEditHandle.cpp b/libraries/particles/src/ParticleEditHandle.cpp new file mode 100644 index 0000000000..27182b24e0 --- /dev/null +++ b/libraries/particles/src/ParticleEditHandle.cpp @@ -0,0 +1,103 @@ +// +// ParticleEditHandle.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#include "Particle.h" +#include "ParticleEditHandle.h" +#include "ParticleEditPacketSender.h" +#include "ParticleTree.h" + +std::map ParticleEditHandle::_allHandles; +uint32_t ParticleEditHandle::_nextCreatorTokenID = 0; + + +ParticleEditHandle::ParticleEditHandle(ParticleEditPacketSender* packetSender, ParticleTree* localTree) { + _creatorTokenID = _nextCreatorTokenID; + _nextCreatorTokenID++; + _id = NEW_PARTICLE; + _isKnownID = false; + _packetSender = packetSender; + _localTree = localTree; + + _allHandles[_creatorTokenID] = this; +} + +ParticleEditHandle::~ParticleEditHandle() { + // remove us from our _allHandles map + _allHandles.erase(_allHandles.find(_creatorTokenID)); +} + +void ParticleEditHandle::createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, + glm::vec3 gravity, float damping, QString updateScript) { + + // setup a ParticleDetail struct with the data + ParticleDetail addParticleDetail = { NEW_PARTICLE, usecTimestampNow(), + position, radius, {color.red, color.green, color.blue }, + velocity, gravity, damping, updateScript, _creatorTokenID }; + + // queue the packet + _packetSender->queueParticleEditMessages(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 1, &addParticleDetail); + + // release them + _packetSender->releaseQueuedMessages(); + + // if we have a local tree, also update it... + if (_localTree) { + // we can't really do this here, because if we create a particle locally, we'll get a ghost particle + // because we can't really handle updating/deleting it locally + } +} + +bool ParticleEditHandle::updateParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, + glm::vec3 gravity, float damping, QString updateScript) { + + if (!isKnownID()) { + return false; // not allowed until we know the id + } + + // setup a ParticleDetail struct with the data + ParticleDetail newParticleDetail = { _id, usecTimestampNow(), + position, radius, {color.red, color.green, color.blue }, + velocity, gravity, damping, updateScript, _creatorTokenID }; + + // queue the packet + _packetSender->queueParticleEditMessages(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, 1, &newParticleDetail); + + // release them + _packetSender->releaseQueuedMessages(); + + // if we have a local tree, also update it... + if (_localTree) { + rgbColor rcolor = {color.red, color.green, color.blue }; + Particle tempParticle(position, radius, rcolor, velocity, damping, gravity, updateScript, _id); + _localTree->storeParticle(tempParticle); + } + + return true; +} + +void ParticleEditHandle::handleAddResponse(unsigned char* packetData , int packetLength) { + unsigned char* dataAt = packetData; + int numBytesPacketHeader = numBytesForPacketHeader(packetData); + dataAt += numBytesPacketHeader; + + uint32_t creatorTokenID; + memcpy(&creatorTokenID, dataAt, sizeof(creatorTokenID)); + dataAt += sizeof(creatorTokenID); + + uint32_t particleID; + memcpy(&particleID, dataAt, sizeof(particleID)); + dataAt += sizeof(particleID); + + // find this particle in the _allHandles map + if (_allHandles.find(creatorTokenID) != _allHandles.end()) { + ParticleEditHandle* theHandle = _allHandles[creatorTokenID]; + theHandle->_id = particleID; + theHandle->_isKnownID = true; + } +} diff --git a/libraries/particles/src/ParticleEditHandle.h b/libraries/particles/src/ParticleEditHandle.h new file mode 100644 index 0000000000..2e81f35abf --- /dev/null +++ b/libraries/particles/src/ParticleEditHandle.h @@ -0,0 +1,52 @@ +// +// ParticleEditHandle.h +// hifi +// +// Created by Brad Hefta-Gaub on 12/4/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__ParticleEditHandle__ +#define __hifi__ParticleEditHandle__ + +#include +#include + +#include +#include + +#include +#include + +class ParticleEditPacketSender; +class ParticleTree; + +class ParticleEditHandle { +public: + ParticleEditHandle(ParticleEditPacketSender* packetSender, ParticleTree* localTree); + ~ParticleEditHandle(); + + uint32_t getCreatorTokenID() const { return _creatorTokenID; } + uint32_t getID() const { return _id; } + + bool isKnownID() const { return _isKnownID; } + + void createParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, + glm::vec3 gravity, float damping, QString updateScript); + + bool updateParticle(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, + glm::vec3 gravity, float damping, QString updateScript); + + static void handleAddResponse(unsigned char* packetData , int packetLength); +private: + uint32_t _creatorTokenID; + uint32_t _id; + bool _isKnownID; + static uint32_t _nextCreatorTokenID; + static std::map _allHandles; + ParticleEditPacketSender* _packetSender; + ParticleTree* _localTree; +}; + +#endif /* defined(__hifi__ParticleEditHandle__) */ \ No newline at end of file diff --git a/libraries/particles/src/ParticleScriptingInterface.cpp b/libraries/particles/src/ParticleScriptingInterface.cpp index 22fe83b85e..bedfab73a0 100644 --- a/libraries/particles/src/ParticleScriptingInterface.cpp +++ b/libraries/particles/src/ParticleScriptingInterface.cpp @@ -9,7 +9,8 @@ #include "ParticleScriptingInterface.h" ParticleScriptingInterface::ParticleScriptingInterface() : - _jurisdictionListener(NODE_TYPE_PARTICLE_SERVER) + _jurisdictionListener(NODE_TYPE_PARTICLE_SERVER), + _nextCreatorTokenID(0) { _jurisdictionListener.initialize(true); _particlePacketSender.setServerJurisdictions(_jurisdictionListener.getJurisdictions()); @@ -19,12 +20,20 @@ void ParticleScriptingInterface::queueParticleAdd(PACKET_TYPE addPacketType, Par _particlePacketSender.queueParticleEditMessages(addPacketType, 1, &addParticleDetails); } -void ParticleScriptingInterface::queueParticleAdd(glm::vec3 position, float radius, +uint32_t ParticleScriptingInterface::queueParticleAdd(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, QString updateScript) { + + // The application will keep track of creatorTokenID + uint32_t creatorTokenID = _nextCreatorTokenID; + _nextCreatorTokenID++; // setup a ParticleDetail struct with the data - ParticleDetail addParticleDetail = { position, radius, {color.red, color.green, color.blue }, velocity, gravity, damping, updateScript }; + ParticleDetail addParticleDetail = { NEW_PARTICLE, usecTimestampNow(), + position, radius, {color.red, color.green, color.blue }, velocity, + gravity, damping, updateScript, creatorTokenID }; // queue the packet - queueParticleAdd(PACKET_TYPE_PARTICLE_ADD, addParticleDetail); + queueParticleAdd(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, addParticleDetail); + + return creatorTokenID; } diff --git a/libraries/particles/src/ParticleScriptingInterface.h b/libraries/particles/src/ParticleScriptingInterface.h index 4300fdc2f1..76a422bd46 100644 --- a/libraries/particles/src/ParticleScriptingInterface.h +++ b/libraries/particles/src/ParticleScriptingInterface.h @@ -24,7 +24,8 @@ public: JurisdictionListener* getJurisdictionListener() { return &_jurisdictionListener; } public slots: /// queues the creation of a Particle which will be sent by calling process on the PacketSender - void queueParticleAdd(glm::vec3 position, float radius, + /// returns the creatorTokenID for the newly created particle + uint32_t queueParticleAdd(glm::vec3 position, float radius, xColor color, glm::vec3 velocity, glm::vec3 gravity, float damping, QString updateScript); /// Set the desired max packet size in bytes that should be created @@ -84,6 +85,8 @@ private: JurisdictionListener _jurisdictionListener; void queueParticleAdd(PACKET_TYPE addPacketType, ParticleDetail& addParticleDetails); + + uint32_t _nextCreatorTokenID; }; #endif /* defined(__hifi__ParticleScriptingInterface__) */ diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index 9648b50c30..9d36c72f4a 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -22,7 +22,7 @@ ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) c bool ParticleTree::handlesEditPacketType(PACKET_TYPE packetType) const { // we handle these types of "edit" packets switch (packetType) { - case PACKET_TYPE_PARTICLE_ADD: + case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: case PACKET_TYPE_PARTICLE_ERASE: return true; } @@ -65,17 +65,19 @@ void ParticleTree::storeParticle(const Particle& particle) { } int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, - unsigned char* editData, int maxLength) { + unsigned char* editData, int maxLength, Node* senderNode) { int processedBytes = 0; // we handle these types of "edit" packets switch (packetType) { - case PACKET_TYPE_PARTICLE_ADD: { + case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: { Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes); storeParticle(newParticle); - // It seems like we need some way to send the ID back to the creator?? + if (newParticle.isNewlyCreated()) { + notifyNewlyCreatedParticle(newParticle, senderNode); + } } break; - + case PACKET_TYPE_PARTICLE_ERASE: { processedBytes = 0; } break; @@ -83,6 +85,31 @@ int ParticleTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* p return processedBytes; } +void ParticleTree::notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode) { + _newlyCreatedHooksLock.lockForRead(); + for (int i = 0; i < _newlyCreatedHooks.size(); i++) { + _newlyCreatedHooks[i]->particleCreated(newParticle, senderNode); + } + _newlyCreatedHooksLock.unlock(); +} + +void ParticleTree::addNewlyCreatedHook(NewlyCreatedParticleHook* hook) { + _newlyCreatedHooksLock.lockForWrite(); + _newlyCreatedHooks.push_back(hook); + _newlyCreatedHooksLock.unlock(); +} + +void ParticleTree::removeNewlyCreatedHook(NewlyCreatedParticleHook* hook) { + _newlyCreatedHooksLock.lockForWrite(); + for (int i = 0; i < _newlyCreatedHooks.size(); i++) { + if (_newlyCreatedHooks[i] == hook) { + _newlyCreatedHooks.erase(_newlyCreatedHooks.begin() + i); + break; + } + } + _newlyCreatedHooksLock.unlock(); +} + bool ParticleTree::updateOperation(OctreeElement* element, void* extraData) { ParticleTreeUpdateArgs* args = static_cast(extraData); diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 176ac16e2b..cde0bb60f7 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -17,7 +17,10 @@ public: std::vector _movingParticles; }; - +class NewlyCreatedParticleHook { +public: + virtual void particleCreated(const Particle& newParticle, Node* senderNode) = 0; +}; class ParticleTree : public Octree { Q_OBJECT @@ -35,18 +38,24 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool handlesEditPacketType(PACKET_TYPE packetType) const; virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, - unsigned char* editData, int maxLength); + unsigned char* editData, int maxLength, Node* senderNode); virtual void update(); void storeParticle(const Particle& particle); + + void addNewlyCreatedHook(NewlyCreatedParticleHook* hook); + void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook); + private: static bool updateOperation(OctreeElement* element, void* extraData); static bool findAndUpdateOperation(OctreeElement* element, void* extraData); - - - + + void notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode); + + QReadWriteLock _newlyCreatedHooksLock; + std::vector _newlyCreatedHooks; }; #endif /* defined(__hifi__ParticleTree__) */ diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index a5194b06cd..9b5961b12f 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -49,6 +49,9 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_JURISDICTION: return 1; + + case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: + return 1; default: return 0; diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h index ade4292918..a59ae5e5e5 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -48,8 +48,9 @@ const PACKET_TYPE PACKET_TYPE_JURISDICTION = 'J'; const PACKET_TYPE PACKET_TYPE_JURISDICTION_REQUEST = 'j'; const PACKET_TYPE PACKET_TYPE_PARTICLE_QUERY = 'Q'; const PACKET_TYPE PACKET_TYPE_PARTICLE_DATA = 'v'; -const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD = 'a'; +const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD_OR_EDIT = 'a'; const PACKET_TYPE PACKET_TYPE_PARTICLE_ERASE = 'x'; +const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD_RESPONSE = 'b'; typedef char PACKET_VERSION; diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 986d70665e..ae61beeb4e 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -651,7 +651,7 @@ bool VoxelTree::handlesEditPacketType(PACKET_TYPE packetType) const { } int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, - unsigned char* editData, int maxLength) { + unsigned char* editData, int maxLength, Node* senderNode) { int processedBytes = 0; // we handle these types of "edit" packets diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index eae47885b2..ea841ebf90 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -55,7 +55,7 @@ public: virtual bool handlesEditPacketType(PACKET_TYPE packetType) const; virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength, - unsigned char* editData, int maxLength); + unsigned char* editData, int maxLength, Node* senderNode); void processSetVoxelsBitstream(const unsigned char* bitstream, int bufferSizeBytes); /**