diff --git a/examples/findParticleExample.js b/examples/findParticleExample.js new file mode 100644 index 0000000000..f582ee6469 --- /dev/null +++ b/examples/findParticleExample.js @@ -0,0 +1,128 @@ +// +// editParticleExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/24/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates "finding" particles +// + +var iteration = 0; + +var particleA = Particles.addParticle( + { + position: { x: 2, y: 0, z: 2 }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + radius : 0.1, + color: { red: 0, green: 255, blue: 0 } + }); + +var particleB = Particles.addParticle( + { + position: { x: 5, y: 0, z: 5 }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + radius : 0.1, + color: { red: 0, green: 255, blue: 255 } + }); + +var searchAt = { x: 0, y: 0, z: 0}; +var moveSearch = { x: 0.1, y: 0, z: 0.1}; +var searchRadius = 1; +var searchRadiusChange = 0; + +print("particleA.creatorTokenID = " + particleA.creatorTokenID); +print("particleB.creatorTokenID = " + particleB.creatorTokenID); + + +function scriptEnding() { + print("calling Particles.deleteParticle()"); + Particles.deleteParticle(particleA); + Particles.deleteParticle(particleB); +} + +function printProperties(properties) { + for (var property in properties) { + if (properties.hasOwnProperty(property)) { + if (property == "position" || + property == "gravity" || + property == "velocity") { + print(property +": " + properties[property].x + ", " + properties[property].y + ", " + properties[property].z); + } else if (property == "color") { + print(property +": " + properties[property].red + ", " + + properties[property].green + ", " + properties[property].blue); + } else { + print(property +": " + properties[property]) + } + } + } +} + +function findParticles() { + + // run for a while, then clean up + // stop it... + if (iteration >= 100) { + print("calling Agent.stop()"); + Agent.stop(); + } + + print("--------------------------"); + print("iteration =" + iteration); + iteration++; + + // Check to see if we've been notified of the actual identity of the particles we created + if (!particleA.isKnownID) { + var identifyA = Particles.identifyParticle(particleA); + if (identifyA.isKnownID) { + particleA = identifyA; + print(">>>> identified particleA.id = " + particleA.id); + } + } + if (!particleB.isKnownID) { + var identifyB = Particles.identifyParticle(particleB); + if (identifyB.isKnownID) { + particleB = identifyB; + print(">>>> identified particleB.id = " + particleB.id); + } + } + + // also check to see if we can "find" particles... + print("searching for particles at:" + searchAt.x + ", " + searchAt.y + ", " + searchAt.z + " radius:" + searchRadius); + var foundParticles = Particles.findParticles(searchAt, searchRadius); + print("found this many particles: "+ foundParticles.length); + for (var i = 0; i < foundParticles.length; i++) { + print(" particle[" + i + "].id:" + foundParticles[i].id); + if (foundParticles[i].id == particleA.id) { + print(">>>> found particleA!!"); + var propertiesA = Particles.getParticleProperties(particleA); + printProperties(propertiesA); + } + if (foundParticles[i].id == particleB.id) { + print(">>>> found particleB!!"); + } + } + // move search + searchAt.x += moveSearch.x; + searchAt.y += moveSearch.y; + searchAt.z += moveSearch.z; + searchRadius += searchRadiusChange; + + // after we've searched for 80 iterations, change our search mechanism to be from the center with expanding radius + if (iteration == 80) { + searchAt = { x: 3.5, y: 0, z: 3.5}; + moveSearch = { x: 0, y: 0, z: 0}; + searchRadius = 0.5; + searchRadiusChange = 0.5; + } + +} + + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(findParticles); + +// register our scriptEnding callback +Agent.scriptEnding.connect(scriptEnding); diff --git a/examples/particleBird.js b/examples/particleBird.js new file mode 100644 index 0000000000..6a4cf79a40 --- /dev/null +++ b/examples/particleBird.js @@ -0,0 +1,174 @@ +// +// particleBird.js +// hifi +// +// This sample script moves a voxel around like a bird and sometimes makes tweeting noises +// +function vLength(v) { + return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); +} +function printVector(v) { + print(v.x + ", " + v.y + ", " + v.z + "\n"); +} +// Create a random vector with individual lengths between a,b +function randVector(a, b) { + var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) }; + return rval; +} +function vMinus(a, b) { + var rval = { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; + return rval; +} +function vPlus(a, b) { + var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; + return rval; +} +function vCopy(a, b) { + a.x = b.x; + a.y = b.y; + a.z = b.z; + return; +} +// Returns a vector which is fraction of the way between a and b +function vInterpolate(a, b, fraction) { + var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction }; + return rval; +} + +// Decide what kind of bird we are +var tweet; +var color; +var size; +var which = Math.random(); +if (which < 0.2) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); + color = { r: 100, g: 50, b: 120 }; + size = 0.08; +} else if (which < 0.4) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/rosyfacedlovebird.raw"); + color = { r: 100, g: 150, b: 75 }; + size = 0.09; +} else if (which < 0.6) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/saysphoebe.raw"); + color = { r: 84, g: 121, b: 36 }; + size = 0.05; +} else if (which < 0.8) { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); + color = { r: 23, g: 197, b: 230 }; + size = 0.12; +} else { + tweet = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/westernscreechowl.raw"); + color = { r: 50, g: 67, b: 144 }; + size = 0.15; +} + +var startTimeInSeconds = new Date().getTime() / 1000; + +var birdLifetime = 20; // lifetime of the bird in seconds! +var position = { x: 0, y: 0, z: 0 }; +var targetPosition = { x: 0, y: 0, z: 0 }; +var range = 1.0; // Over what distance in meters do you want your bird to fly around +var frame = 0; +var moving = false; +var tweeting = 0; +var moved = false; +var CHANCE_OF_MOVING = 0.05; +var CHANCE_OF_TWEETING = 0.05; +var START_HEIGHT_ABOVE_ME = 1.5; +var myPosition = MyAvatar.position; +var properties = { + lifetime: birdLifetime, + position: { x: myPosition.x, y: myPosition.y + START_HEIGHT_ABOVE_ME, z: myPosition.z }, + velocity: { x: 0, y: 0, z: 0 }, + gravity: { x: 0, y: 0, z: 0 }, + radius : 0.1, + color: { red: 0, + green: 255, + blue: 0 } +}; +var range = 1.0; // Distance around avatar where I can move +// Create the actual bird +var particleID = Particles.addParticle(properties); +function moveBird() { + + // check to see if we've been running long enough that our bird is dead + var nowTimeInSeconds = new Date().getTime() / 1000; + if ((nowTimeInSeconds - startTimeInSeconds) >= birdLifetime) { + print("our bird is dying, stop our script"); + Agent.stop(); + return; + } + + myPosition = MyAvatar.position; + frame++; + if (frame % 3 == 0) { + // Tweeting behavior + if (tweeting == 0) { + if (Math.random() < CHANCE_OF_TWEETING) { + //print("tweet!" + "\n"); + var options = new AudioInjectionOptions(); + options.position = position; + options.volume = 0.75; + Audio.playSound(tweet, options); + tweeting = 10; + } + } else { + tweeting -= 1; + } + // Moving behavior + if (moving == false) { + if (Math.random() < CHANCE_OF_MOVING) { + targetPosition = randVector(- range, range); + targetPosition = vPlus(targetPosition, myPosition); + if (targetPosition.x < 0) { + targetPosition.x = 0; + } + if (targetPosition.y < 0) { + targetPosition.y = 0; + } + if (targetPosition.z < 0) { + targetPosition.z = 0; + } + if (targetPosition.x > TREE_SCALE) { + targetPosition.x = TREE_SCALE; + } + if (targetPosition.y > TREE_SCALE) { + targetPosition.y = TREE_SCALE; + } + if (targetPosition.z > TREE_SCALE) { + targetPosition.z = TREE_SCALE; + } + //printVector(position); + moving = true; + } + } + if (moving) { + position = vInterpolate(position, targetPosition, 0.5); + if (vLength(vMinus(position, targetPosition)) < (size / 5.0)) { + moved = false; + moving = false; + } else { + moved = true; + } + } + if (moved || (tweeting > 0)) { + if (tweeting > 0) { + var newProperties = { + position: position, + radius : size * 1.5, + color: { red: Math.random() * 255, green: 0, blue: 0 } + }; + } else { + var newProperties = { + position: position, + radius : size, + color: { red: color.r, green: color.g, blue: color.b } + }; + } + Particles.editParticle(particleID, newProperties); + moved = false; + } + } +} +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(moveBird); diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 5cf5e9248d..ecda364e85 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -292,10 +292,7 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef } -Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree) { - - //qDebug() << "Particle::fromEditPacket() length=" << length; - +Particle Particle::fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid) { Particle newParticle; // id and _lastUpdated will get set here... unsigned char* dataAt = data; processedBytes = 0; @@ -304,9 +301,6 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe int octets = numberOfThreeBitSectionsInCode(data); int lengthOfOctcode = bytesRequiredForCodeLength(octets); - //qDebug() << "Particle::fromEditPacket() lengthOfOctcode=" << lengthOfOctcode; - //printOctalCode(data); - // we don't actually do anything with this octcode... dataAt += lengthOfOctcode; processedBytes += lengthOfOctcode; @@ -323,8 +317,6 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe // special case for handling "new" particles if (isNewParticle) { - //qDebug() << "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; @@ -332,11 +324,8 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe dataAt += sizeof(creatorTokenID); processedBytes += sizeof(creatorTokenID); - //qDebug() << "creatorTokenID:" << creatorTokenID; - newParticle.setCreatorTokenID(creatorTokenID); newParticle._newlyCreated = true; - newParticle.setAge(0); // this guy is new! } else { @@ -346,15 +335,19 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe // copy existing properties before over-writing with new properties if (existingParticle) { newParticle = *existingParticle; - //qDebug() << "newParticle = *existingParticle... calling debugDump()..."; - //existingParticle->debugDump(); + } else { + // the user attempted to edit a particle that doesn't exist + qDebug() << "user attempted to edit a particle that doesn't exist..."; + valid = false; + return newParticle; } - newParticle._id = editID; newParticle._newlyCreated = false; - - } + + // if we got this far, then our result will be valid + valid = true; + // lastEdited memcpy(&newParticle._lastEdited, dataAt, sizeof(newParticle._lastEdited)); @@ -840,6 +833,8 @@ ParticleProperties::ParticleProperties() : _inHand(false), _shouldDie(false), + _id(UNKNOWN_PARTICLE_ID), + _idSet(false), _lastEdited(usecTimestampNow()), _positionChanged(false), _colorChanged(false), @@ -925,6 +920,11 @@ QScriptValue ParticleProperties::copyToScriptValue(QScriptEngine* engine) const properties.setProperty("script", _script); properties.setProperty("inHand", _inHand); properties.setProperty("shouldDie", _shouldDie); + + if (_idSet) { + properties.setProperty("id", _id); + properties.setProperty("isKnownID", (_id == UNKNOWN_PARTICLE_ID)); + } return properties; } @@ -1118,6 +1118,9 @@ void ParticleProperties::copyFromParticle(const Particle& particle) { _inHand = particle.getInHand(); _shouldDie = particle.getShouldDie(); + _id = particle.getID(); + _idSet = true; + _positionChanged = false; _colorChanged = false; _radiusChanged = false; diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index e534c7b418..ebd21a6b52 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -94,6 +94,9 @@ public: void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; _shouldDieChanged = true; } void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; } void setScript(QString updateScript) { _script = updateScript; _scriptChanged = true; } + + /// used by ParticleScriptingInterface to return ParticleProperties for unknown particles + void setIsUnknownID() { _id = UNKNOWN_PARTICLE_ID; _idSet = true; } private: glm::vec3 _position; @@ -107,6 +110,8 @@ private: bool _inHand; bool _shouldDie; + uint32_t _id; + bool _idSet; uint64_t _lastEdited; bool _positionChanged; bool _colorChanged; @@ -145,6 +150,7 @@ public: }; Q_DECLARE_METATYPE(ParticleID); +Q_DECLARE_METATYPE(QVector); QScriptValue ParticleIDtoScriptValue(QScriptEngine* engine, const ParticleID& properties); void ParticleIDfromScriptValue(const QScriptValue &object, ParticleID& properties); @@ -162,7 +168,7 @@ public: bool inHand = NOT_IN_HAND, QString updateScript = DEFAULT_SCRIPT, uint32_t id = NEW_PARTICLE); /// 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, ParticleTree* tree); + static Particle fromEditPacket(unsigned char* data, int length, int& processedBytes, ParticleTree* tree, bool& valid); virtual ~Particle(); virtual void init(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index cd59e0e3a7..7e83c56a42 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -160,6 +160,42 @@ const Particle* ParticleTree::findClosestParticle(glm::vec3 position, float targ return args.closestParticle; } +class FindAllNearPointArgs { +public: + glm::vec3 position; + float targetRadius; + QVector particles; +}; + + +bool ParticleTree::findInSphereOperation(OctreeElement* element, void* extraData) { + FindAllNearPointArgs* args = static_cast(extraData); + ParticleTreeElement* particleTreeElement = static_cast(element); + + glm::vec3 penetration; + bool sphereIntersection = particleTreeElement->getAABox().findSpherePenetration(args->position, + args->targetRadius, penetration); + + // If this particleTreeElement contains the point, then search it... + if (sphereIntersection) { + QVector moreParticles = particleTreeElement->getParticles(args->position, args->targetRadius); + args->particles << moreParticles; + return true; // keep searching in case children have closer particles + } + + // if this element doesn't contain the point, then none of it's children can contain the point, so stop searching + return false; +} + +QVector ParticleTree::findParticles(const glm::vec3& center, float radius) { + QVector result; + FindAllNearPointArgs args = { center, radius }; + lockForRead(); + recurseTreeWithOperation(findInSphereOperation, &args); + unlock(); + return args.particles; +} + class FindByIDArgs { public: uint32_t id; @@ -214,13 +250,14 @@ 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... "; - Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this); - storeParticle(newParticle, senderNode); - if (newParticle.isNewlyCreated()) { - notifyNewlyCreatedParticle(newParticle, senderNode); + bool isValid; + Particle newParticle = Particle::fromEditPacket(editData, maxLength, processedBytes, this, isValid); + if (isValid) { + storeParticle(newParticle, senderNode); + if (newParticle.isNewlyCreated()) { + notifyNewlyCreatedParticle(newParticle, senderNode); + } } - //qDebug() << " DONE... PACKET_TYPE_PARTICLE_ADD_OR_EDIT... "; } break; // TODO: wire in support here for server to get PACKET_TYPE_PARTICLE_ERASE messages diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 2b7efbbe78..5fa25b2c2d 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -42,6 +42,7 @@ public: void storeParticle(const Particle& particle, Node* senderNode = NULL); const Particle* findClosestParticle(glm::vec3 position, float targetRadius); const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false); + QVector findParticles(const glm::vec3& center, float radius); void addNewlyCreatedHook(NewlyCreatedParticleHook* hook); void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook); @@ -58,6 +59,7 @@ private: static bool updateOperation(OctreeElement* element, void* extraData); static bool findAndUpdateOperation(OctreeElement* element, void* extraData); static bool findNearPointOperation(OctreeElement* element, void* extraData); + static bool findInSphereOperation(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); diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index f767b56a49..1454eadcc9 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -185,6 +185,23 @@ const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) cons return closestParticle; } +QVector ParticleTreeElement::getParticles(glm::vec3 searchPosition, float searchRadius) const { + QVector results; + uint16_t numberOfParticles = _particles->size(); + for (uint16_t i = 0; i < numberOfParticles; i++) { + const Particle* particle = &(*_particles)[i]; + glm::vec3 particlePosition = particle->getPosition(); + float particleRadius = particle->getRadius(); + glm::vec3 penetration; + + // check to see that the particle (penetrator) penetrates the search area + if (findSphereSpherePenetration(particlePosition, particleRadius, searchPosition, searchRadius, penetration)) { + results << particle; + } + } + return results; +} + const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const { // NOTE: this lookup is O(N) but maybe we don't care? (guaranteed that num particles per elemen is small?) const Particle* foundParticle = NULL; diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index 6d76594b53..2705ad4292 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -89,6 +89,7 @@ public: bool containsParticle(const Particle& particle) const; bool updateParticle(const Particle& particle); const Particle* getClosestParticle(glm::vec3 position) const; + QVector getParticles(glm::vec3 position, float radius) const; const Particle* getParticleWithID(uint32_t id) const; bool removeParticleWithID(uint32_t id); diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 14388b6ac4..0641cdba7c 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -34,15 +34,44 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr return id; } -void ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) { +ParticleID ParticlesScriptingInterface::identifyParticle(ParticleID particleID) { uint32_t actualID = particleID.id; if (!particleID.isKnownID) { actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID); - // hmmm... we kind of want to bail if someone attempts to edit an unknown if (actualID == UNKNOWN_PARTICLE_ID) { - //qDebug() << "ParticlesScriptingInterface::editParticle()... BAILING!!! particleID.creatorTokenID=" - // << particleID.creatorTokenID; - return; // bailing early + return particleID; // bailing early + } + + // found it! + particleID.id = actualID; + particleID.isKnownID = true; + } + return particleID; +} + +ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID particleID) { + ParticleProperties results; + ParticleID identity = identifyParticle(particleID); + if (!identity.isKnownID) { + results.setIsUnknownID(); + return results; + } + if (_particleTree) { + const Particle* particle = _particleTree->findParticleByID(identity.id); + results.copyFromParticle(*particle); + } + + return results; +} + + + +ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) { + uint32_t actualID = particleID.id; + if (!particleID.isKnownID) { + actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID); + if (actualID == UNKNOWN_PARTICLE_ID) { + return particleID; // bailing early } } @@ -71,6 +100,7 @@ void ParticlesScriptingInterface::editParticle(ParticleID particleID, const Part } } queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + return particleID; } @@ -116,3 +146,17 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen return result; } + +QVector ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const { + QVector result; + if (_particleTree) { + QVector particles = _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE); + + foreach (const Particle* particle, particles) { + ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true); + result << thisParticleID; + } + } + return result; +} + diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h index d61f8289f4..9a3ceb51af 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.h +++ b/libraries/particles/src/ParticlesScriptingInterface.h @@ -28,11 +28,33 @@ public: ParticleTree* getParticleTree(ParticleTree*) { return _particleTree; } public slots: + /// adds a particle with the specific properties ParticleID addParticle(const ParticleProperties& properties); - void editParticle(ParticleID particleID, const ParticleProperties& properties); + + /// identify a recently created particle to determine its true ID + ParticleID identifyParticle(ParticleID particleID); + + /// gets the current particle properties for a specific particle + /// this function will not find return results in script engine contexts which don't have access to particles + ParticleProperties getParticleProperties(ParticleID particleID); + + /// edits a particle updating only the included properties, will return the identified ParticleID in case of + /// successful edit, if the input particleID is for an unknown particle this function will have no effect + ParticleID editParticle(ParticleID particleID, const ParticleProperties& properties); + + /// deletes a particle void deleteParticle(ParticleID particleID); + + /// finds the closest particle to the center point, within the radius + /// will return a ParticleID.isKnownID = false if no particles are in the radius + /// this function will not find any particles in script engine contexts which don't have access to particles ParticleID findClosestParticle(const glm::vec3& center, float radius) const; + /// finds particles within the search sphere specified by the center point and radius + /// this function will not find any particles in script engine contexts which don't have access to particles + QVector findParticles(const glm::vec3& center, float radius) const; + + private: void queueParticleMessage(PACKET_TYPE packetType, ParticleID particleID, const ParticleProperties& properties); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f77b428764..1bf8dbf696 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -118,6 +118,7 @@ void ScriptEngine::init() { registerMetaTypes(&_engine); qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue); + qScriptRegisterSequenceMetaType >(&_engine); QScriptValue agentValue = _engine.newQObject(this); _engine.globalObject().setProperty("Agent", agentValue);