Merge pull request #1357 from ZappoMan/edit_particles

Edit Particles
This commit is contained in:
Philip Rosedale 2013-12-12 11:23:40 -08:00
commit c8c6165356
25 changed files with 499 additions and 127 deletions

View file

@ -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:

View file

@ -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,

View file

@ -34,6 +34,9 @@ public:
virtual void renderElement(OctreeElement* element, RenderArgs* args);
void update();
ParticleTree* getTree() { return (ParticleTree*)_tree; }
protected:
};

View file

@ -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;
}
}
}

View file

@ -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) {

View file

@ -18,6 +18,7 @@
#include <AvatarData.h>
#include <HandData.h>
#include <ParticleEditHandle.h>
#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;

View file

@ -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());

View file

@ -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++;
**/
}

View file

@ -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

View file

@ -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();

View file

@ -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();

View file

@ -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());
}

View file

@ -12,10 +12,12 @@
#include <OctreeServer.h>
#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:
};

View file

@ -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;
}

View file

@ -19,8 +19,11 @@
#include <SharedUtil.h>
#include <OctreePacketData.h>
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__) */

View file

@ -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<uint32_t,ParticleEditHandle*> 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;
}
}

View file

@ -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 <glm/glm.hpp>
#include <stdint.h>
#include <QtScript/QScriptEngine>
#include <QtCore/QObject>
#include <SharedUtil.h>
#include <OctreePacketData.h>
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<uint32_t,ParticleEditHandle*> _allHandles;
ParticleEditPacketSender* _packetSender;
ParticleTree* _localTree;
};
#endif /* defined(__hifi__ParticleEditHandle__) */

View file

@ -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;
}

View file

@ -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__) */

View file

@ -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<ParticleTreeUpdateArgs*>(extraData);

View file

@ -17,7 +17,10 @@ public:
std::vector<Particle> _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<NewlyCreatedParticleHook*> _newlyCreatedHooks;
};
#endif /* defined(__hifi__ParticleTree__) */

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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);
/**