From 62b7e6e58d38670b036562d2926df06614b1c406 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 27 Jan 2014 09:00:34 -0800 Subject: [PATCH 01/20] implement local edits in ParticleScriptingInterface --- interface/src/Application.cpp | 8 ++-- libraries/particles/src/Particle.cpp | 22 ++++++++++ libraries/particles/src/Particle.h | 1 + libraries/particles/src/ParticleTree.cpp | 41 +++++++++++++++++- libraries/particles/src/ParticleTree.h | 3 ++ .../particles/src/ParticleTreeElement.cpp | 40 +++++++++++------ libraries/particles/src/ParticleTreeElement.h | 3 +- .../src/ParticlesScriptingInterface.cpp | 43 +++++++++---------- 8 files changed, 120 insertions(+), 41 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 285a395c07..94c1d42b71 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -110,6 +110,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), + _statsExpanded(false), _nodeThread(new QThread(this)), _datagramProcessor(), _frameCount(0), @@ -153,8 +154,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _swatch(NULL), _pasteMode(false), _logger(new FileLogger()), - _persistThread(NULL), - _statsExpanded(false) + _persistThread(NULL) { _applicationStartupTime = startup_time; @@ -252,7 +252,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _window->setCentralWidget(_glWidget); restoreSizeAndPosition(); - loadScripts(); QFontDatabase fontDatabase; fontDatabase.addApplicationFont("resources/styles/Inconsolata.otf"); @@ -282,6 +281,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); checkVersion(); + + // do this as late as possible so that all required subsystems are inialized + loadScripts(); } Application::~Application() { diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index ecda364e85..6e13749aac 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -1065,44 +1065,66 @@ void ParticleProperties::copyFromScriptValue(const QScriptValue &object) { } void ParticleProperties::copyToParticle(Particle& particle) const { + bool somethingChanged = false; if (_positionChanged) { particle.setPosition(_position / (float) TREE_SCALE); + somethingChanged = true; } if (_colorChanged) { particle.setColor(_color); + somethingChanged = true; } if (_radiusChanged) { particle.setRadius(_radius / (float) TREE_SCALE); + somethingChanged = true; } if (_velocityChanged) { particle.setVelocity(_velocity / (float) TREE_SCALE); + somethingChanged = true; } if (_gravityChanged) { particle.setGravity(_gravity / (float) TREE_SCALE); + somethingChanged = true; } if (_dampingChanged) { particle.setDamping(_damping); + somethingChanged = true; } if (_lifetimeChanged) { particle.setLifetime(_lifetime); + somethingChanged = true; } if (_scriptChanged) { particle.setScript(_script); + somethingChanged = true; } if (_inHandChanged) { particle.setInHand(_inHand); + somethingChanged = true; } if (_shouldDieChanged) { particle.setShouldDie(_shouldDie); + somethingChanged = true; + } + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - _lastEdited; + qDebug() << "ParticleProperties::copyToParticle() AFTER update... edited AGO=" << elapsed << + "now=" << now << " _lastEdited=" << _lastEdited; + } + particle.setLastEdited(_lastEdited); } } diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index ebd21a6b52..ed865c670d 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -201,6 +201,7 @@ public: /// The last edited time of this particle from the time perspective of the authoritative server/source uint64_t getLastEdited() const { return _lastEdited; } + void setLastEdited(uint64_t lastEdited) { _lastEdited = lastEdited; } /// lifetime of the particle in seconds float getAge() const { return static_cast(usecTimestampNow() - _created) / static_cast(USECS_PER_SECOND); } diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index 7e83c56a42..a6f04a5174 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -81,8 +81,8 @@ public: bool ParticleTree::findAndUpdateOperation(OctreeElement* element, void* extraData) { FindAndUpdateParticleArgs* args = static_cast(extraData); ParticleTreeElement* particleTreeElement = static_cast(element); - if (particleTreeElement->containsParticle(args->searchParticle)) { - particleTreeElement->updateParticle(args->searchParticle); + // Note: updateParticle() will only operate on correctly found particles + if (particleTreeElement->updateParticle(args->searchParticle)) { args->found = true; return false; // stop searching } @@ -106,6 +106,43 @@ void ParticleTree::storeParticle(const Particle& particle, Node* senderNode) { _isDirty = true; } +class FindAndUpdateParticleWithIDandPropertiesArgs { +public: + const ParticleID& particleID; + const ParticleProperties& properties; + bool found; +}; + +bool ParticleTree::findAndUpdateWithIDandPropertiesOperation(OctreeElement* element, void* extraData) { + FindAndUpdateParticleWithIDandPropertiesArgs* args = static_cast(extraData); + ParticleTreeElement* particleTreeElement = static_cast(element); + // Note: updateParticle() will only operate on correctly found particles + if (particleTreeElement->updateParticle(args->particleID, args->properties)) { + args->found = true; + return false; // stop searching + } + return true; +} + +void ParticleTree::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) { + // First, look for the existing particle in the tree.. + FindAndUpdateParticleWithIDandPropertiesArgs args = { particleID, properties, false }; + recurseTreeWithOperation(findAndUpdateWithIDandPropertiesOperation, &args); + // if we found it in the tree, then mark the tree as dirty + if (args.found) { + _isDirty = true; + } +} + +void ParticleTree::deleteParticle(const ParticleID& particleID) { + if (particleID.isKnownID) { + FindAndDeleteParticlesArgs args; + args._idsToDelete.push_back(particleID.id); + recurseTreeWithOperation(findAndDeleteOperation, &args); + } +} + + class FindNearPointArgs { public: glm::vec3 position; diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 5fa25b2c2d..3fd6c3eca3 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -40,6 +40,8 @@ public: virtual void update(); void storeParticle(const Particle& particle, Node* senderNode = NULL); + void updateParticle(const ParticleID& particleID, const ParticleProperties& properties); + void deleteParticle(const ParticleID& particleID); 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); @@ -58,6 +60,7 @@ private: static bool updateOperation(OctreeElement* element, void* extraData); static bool findAndUpdateOperation(OctreeElement* element, void* extraData); + static bool findAndUpdateWithIDandPropertiesOperation(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); diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 1454eadcc9..43b40f8f9f 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -125,18 +125,6 @@ bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float r return false; } -bool ParticleTreeElement::containsParticle(const Particle& particle) const { - // TODO: remove this method and force callers to use getParticleWithID() instead - uint16_t numberOfParticles = _particles->size(); - uint32_t particleID = particle.getID(); - for (uint16_t i = 0; i < numberOfParticles; i++) { - if ((*_particles)[i].getID() == particleID) { - return true; - } - } - return false; -} - bool ParticleTreeElement::updateParticle(const Particle& particle) { // NOTE: this method must first lookup the particle by ID, hence it is O(N) // and "particle is not found" is worst-case (full N) but maybe we don't care? @@ -172,6 +160,34 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) { return false; } +bool ParticleTreeElement::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) { + // currently only known IDs are in the tree... in the future we might support local storage of creatorTokenID particles + if (particleID.isKnownID) { + uint16_t numberOfParticles = _particles->size(); + uint32_t searchID = particleID.id; + for (uint16_t i = 0; i < numberOfParticles; i++) { + // note: unlike storeParticle() which is called from inbound packets, this is only called by local editors + // and therefore we can be confident that this change is higher priority and should be honored + Particle& thisParticle = (*_particles)[i]; + if (thisParticle.getID() == searchID) { + thisParticle.setProperties(properties); + + const bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - thisParticle.getLastEdited(); + + qDebug() << "ParticleTreeElement::updateParticle() AFTER update... edited AGO=" << elapsed << + "now=" << now << " thisParticle.getLastEdited()=" << thisParticle.getLastEdited(); + } + return true; + } + } + } + return false; +} + + const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const { const Particle* closestParticle = NULL; float closestParticleDistance = FLT_MAX; diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index 2705ad4292..f8ac341400 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -86,8 +86,9 @@ public: void update(ParticleTreeUpdateArgs& args); void setTree(ParticleTree* tree) { _myTree = tree; } - bool containsParticle(const Particle& particle) const; bool updateParticle(const Particle& particle); + bool updateParticle(const ParticleID& particleID, const ParticleProperties& properties); + const Particle* getClosestParticle(glm::vec3 position) const; QVector getParticles(glm::vec3 position, float radius) const; const Particle* getParticleWithID(uint32_t id) const; diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 0641cdba7c..73c316de37 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -57,8 +57,10 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID return results; } if (_particleTree) { + _particleTree->lockForRead(); const Particle* particle = _particleTree->findParticleByID(identity.id); results.copyFromParticle(*particle); + _particleTree->unlock(); } return results; @@ -77,29 +79,14 @@ ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, cons particleID.id = actualID; particleID.isKnownID = true; - //qDebug() << "ParticlesScriptingInterface::editParticle()... FOUND IT!!! actualID=" << actualID; - - bool wantDebugging = false; - if (wantDebugging) { - uint16_t containsBits = properties.getChangedBits(); - qDebug() << "ParticlesScriptingInterface::editParticle()... containsBits=" << containsBits; - if ((containsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION) { - qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getPositon()=" - << properties.getPosition().x << ", " - << properties.getPosition().y << ", " - << properties.getPosition().z << "..."; - } - if ((containsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY) { - qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getVelocity()=" - << properties.getVelocity().x << ", " - << properties.getVelocity().y << ", " - << properties.getVelocity().z << "..."; - } - if ((containsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND) { - qDebug() << "ParticlesScriptingInterface::editParticle()... properties.getInHand()=" << properties.getInHand(); - } - } queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + + // If we have a local particle tree set, then also update it. + if (_particleTree) { + _particleTree->lockForWrite(); + _particleTree->updateParticle(particleID, properties); + _particleTree->unlock(); + } return particleID; } @@ -130,14 +117,22 @@ void ParticlesScriptingInterface::deleteParticle(ParticleID particleID) { //qDebug() << "ParticlesScriptingInterface::deleteParticle(), queueParticleMessage......"; queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + + // If we have a local particle tree set, then also update it. + if (_particleTree) { + _particleTree->lockForWrite(); + _particleTree->deleteParticle(particleID); + _particleTree->unlock(); + } } ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& center, float radius) const { ParticleID result(UNKNOWN_PARTICLE_ID, UNKNOWN_TOKEN, false); if (_particleTree) { + _particleTree->lockForRead(); const Particle* closestParticle = _particleTree->findClosestParticle(center/(float)TREE_SCALE, radius/(float)TREE_SCALE); - + _particleTree->unlock(); if (closestParticle) { result.id = closestParticle->getID(); result.isKnownID = true; @@ -150,7 +145,9 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen QVector ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const { QVector result; if (_particleTree) { + _particleTree->lockForRead(); QVector particles = _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE); + _particleTree->unlock(); foreach (const Particle* particle, particles) { ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true); From c5ba92d73e15a4ff34cdadc2dfeeb30d16556f64 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 27 Jan 2014 11:10:59 -0800 Subject: [PATCH 02/20] first part of getting local particle tree in agent --- assignment-client/src/Agent.cpp | 6 ++++++ assignment-client/src/Agent.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 4f81c42046..85a2fd3a65 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -43,6 +43,9 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& dataByteArray.size()); break; } + } else if (dataByteArray[0] == PACKET_TYPE_PARTICLE_ADD_RESPONSE) { + // this will keep creatorTokenIDs to IDs mapped correctly + Particle::handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size()); } else { NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); } @@ -88,6 +91,9 @@ void Agent::run() { connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); + // tell our script engine about our local particle tree + _scriptEngine.getParticlesScriptingInterface()->setParticleTree(&_particleTree); + // setup an Avatar for the script to use AvatarData scriptedAvatar; diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 980885fcf3..6ab2924ada 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -32,6 +33,7 @@ signals: void willSendVisualDataCallback(); private: ScriptEngine _scriptEngine; + ParticleTree _particleTree; }; #endif /* defined(__hifi__Agent__) */ From 13a92bc3c9dc9c47da0102fa697806b04cb394dc Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 27 Jan 2014 11:11:20 -0800 Subject: [PATCH 03/20] tweaks to editParticle example --- examples/editParticleExample.js | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/editParticleExample.js b/examples/editParticleExample.js index 4eb5dfe907..61e32c4d55 100644 --- a/examples/editParticleExample.js +++ b/examples/editParticleExample.js @@ -9,6 +9,9 @@ // var count = 0; +var moveUntil = 2000; +var stopAfter = moveUntil + 100; +var expectedLifetime = (moveUntil/60) + 1; // 1 second after done moving... var originalProperties = { position: { x: 10, @@ -28,7 +31,9 @@ var originalProperties = { color: { red: 0, green: 255, - blue: 0 } + blue: 0 }, + + lifetime: expectedLifetime }; @@ -38,17 +43,17 @@ var positionDelta = { x: 0.05, y: 0, z: 0 }; var particleID = Particles.addParticle(originalProperties); function moveParticle() { - if (count >= 100) { + if (count >= moveUntil) { //Agent.stop(); // delete it... - if (count == 100) { + if (count == moveUntil) { print("calling Particles.deleteParticle()"); Particles.deleteParticle(particleID); } // stop it... - if (count >= 200) { + if (count >= stopAfter) { print("calling Agent.stop()"); Agent.stop(); } @@ -77,16 +82,6 @@ function moveParticle() { print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z); Particles.editParticle(particleID, newProperties); - - // also check to see if we can "find" particles... - var searchAt = { x: 9, y: 0, z: 0}; - var searchRadius = 2; - var foundParticle = Particles.findClosestParticle(searchAt, searchRadius); - if (foundParticle.isKnownID) { - print("found particle:" + foundParticle.id); - } else { - print("could not find particle in or around x=9 to x=11:"); - } } From 5e4813ab1165a220dbfdcfc50f31cc8c5f7b6067 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 27 Jan 2014 13:50:05 -0800 Subject: [PATCH 04/20] more work on allowing for local particle trees --- assignment-client/src/Agent.cpp | 3 + interface/src/DatagramProcessor.cpp | 1 + libraries/octree/src/Octree.cpp | 1 + libraries/octree/src/Octree.h | 6 ++ libraries/octree/src/OctreeRenderer.cpp | 4 + libraries/particles/src/Particle.cpp | 29 ++++++- libraries/particles/src/Particle.h | 3 + libraries/particles/src/ParticleTree.cpp | 76 +++++++++++++++++++ libraries/particles/src/ParticleTree.h | 3 + .../particles/src/ParticleTreeElement.cpp | 67 +++++++++++----- libraries/particles/src/ParticleTreeElement.h | 12 +++ .../src/ParticlesScriptingInterface.cpp | 24 ++++-- 12 files changed, 201 insertions(+), 28 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 85a2fd3a65..db200c415c 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -46,6 +46,9 @@ void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& } else if (dataByteArray[0] == PACKET_TYPE_PARTICLE_ADD_RESPONSE) { // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size()); + + // also give our local particle tree a chance to remap any internal locally created particles + _particleTree.handleAddParticleResponse((unsigned char*) dataByteArray.data(), dataByteArray.size()); } else { NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); } diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index b78bd0c309..29c073fdba 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -57,6 +57,7 @@ void DatagramProcessor::processDatagrams() { case PACKET_TYPE_PARTICLE_ADD_RESPONSE: // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(incomingPacket, bytesReceived); + application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket, bytesReceived); break; case PACKET_TYPE_PARTICLE_DATA: diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 5d25f0c164..1fa583b2be 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -43,6 +43,7 @@ Octree::Octree(bool shouldReaverage) : _shouldReaverage(shouldReaverage), _stopImport(false) { _rootNode = NULL; + _isViewing = false; } Octree::~Octree() { diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 959c9272cf..b01a8b1d63 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -258,6 +258,9 @@ public: void recurseNodeWithOperationDistanceSorted(OctreeElement* node, RecurseOctreeOperation operation, const glm::vec3& point, void* extraData, int recursionCount = 0); + bool getIsViewing() const { return _isViewing; } + void setIsViewing(bool isViewing) { _isViewing = isViewing; } + signals: void importSize(float x, float y, float z); void importProgress(int progress); @@ -321,6 +324,9 @@ protected: void emptyDeleteQueue(); QReadWriteLock lock; + + /// This tree is receiving inbound viewer datagrams. + bool _isViewing; }; float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale); diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 7bbeb065d6..de0d09ef74 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -42,6 +42,10 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Hifi if(command == expectedType) { PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PACKET_TYPE",showTimingDetails); + + // if we are getting inbound packets, then our tree is also viewing, and we should remember that fact. + _tree->setIsViewing(true); + const unsigned char* dataAt = packetData + numBytesPacketHeader; diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 6e13749aac..0d111c4e01 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -78,6 +78,33 @@ Particle::Particle() { DEFAULT_GRAVITY, DEFAULT_DAMPING, DEFAULT_LIFETIME, NOT_IN_HAND, DEFAULT_SCRIPT, NEW_PARTICLE); } +Particle::Particle(const ParticleID& particleID, const ParticleProperties& properties) { + _id = particleID.id; + _creatorTokenID = particleID.creatorTokenID; + + // init values with defaults before calling setProperties + uint64_t now = usecTimestampNow(); + _lastEdited = now; + _lastUpdated = now; + _created = now; // will get updated as appropriate in setAge() + + _position = glm::vec3(0,0,0); + _radius = 0; + _mass = 1.0f; + rgbColor noColor = { 0, 0, 0 }; + memcpy(_color, noColor, sizeof(_color)); + _velocity = glm::vec3(0,0,0); + _damping = DEFAULT_DAMPING; + _lifetime = DEFAULT_LIFETIME; + _gravity = DEFAULT_GRAVITY; + _script = DEFAULT_SCRIPT; + _inHand = NOT_IN_HAND; + _shouldDie = false; + + setProperties(properties); +} + + Particle::~Particle() { } @@ -86,10 +113,8 @@ void Particle::init(glm::vec3 position, float radius, rgbColor color, glm::vec3 if (id == NEW_PARTICLE) { _id = _nextID; _nextID++; - //qDebug() << "Particle::init()... assigning new id... _id=" << _id; } else { _id = id; - //qDebug() << "Particle::init()... assigning id from init... _id=" << _id; } uint64_t now = usecTimestampNow(); _lastEdited = now; diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index ed865c670d..e37db04a06 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -161,6 +161,8 @@ class Particle { public: Particle(); + + Particle(const ParticleID& particleID, const ParticleProperties& properties); /// all position, velocity, gravity, radius units are in domain units (0.0 to 1.0) Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, @@ -207,6 +209,7 @@ public: float getAge() const { return static_cast(usecTimestampNow() - _created) / static_cast(USECS_PER_SECOND); } float getEditedAgo() const { return static_cast(usecTimestampNow() - _lastEdited) / static_cast(USECS_PER_SECOND); } uint32_t getID() const { return _id; } + void setID(uint32_t id) { _id = id; } bool getShouldDie() const { return _shouldDie; } QString getScript() const { return _script; } uint32_t getCreatorTokenID() const { return _creatorTokenID; } diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index a6f04a5174..7f8f168e5d 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -121,6 +121,12 @@ bool ParticleTree::findAndUpdateWithIDandPropertiesOperation(OctreeElement* elem args->found = true; return false; // stop searching } + + // if we've found our particle stop searching + if (args->found) { + return false; + } + return true; } @@ -134,6 +140,21 @@ void ParticleTree::updateParticle(const ParticleID& particleID, const ParticlePr } } +void ParticleTree::addParticle(const ParticleID& particleID, const ParticleProperties& properties) { + // This only operates on locally created particles + if (particleID.isKnownID) { + return; // not allowed + } + Particle particle(particleID, properties); + glm::vec3 position = particle.getPosition(); + float size = std::max(MINIMUM_PARTICLE_ELEMENT_SIZE, particle.getRadius()); + ParticleTreeElement* element = (ParticleTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size); + + element->storeParticle(particle); + + _isDirty = true; +} + void ParticleTree::deleteParticle(const ParticleID& particleID) { if (particleID.isKnownID) { FindAndDeleteParticlesArgs args; @@ -142,6 +163,61 @@ void ParticleTree::deleteParticle(const ParticleID& particleID) { } } +// scans the tree and handles mapping locally created particles to know IDs. +// in the event that this tree is also viewing the scene, then we need to also +// search the tree to make sure we don't have a duplicate particle from the viewing +// operation. +bool ParticleTree::findAndUpdateParticleIDOperation(OctreeElement* element, void* extraData) { + bool keepSearching = true; + + FindAndUpdateParticleIDArgs* args = static_cast(extraData); + ParticleTreeElement* particleTreeElement = static_cast(element); + + // Note: updateParticleID() will only operate on correctly found particles + particleTreeElement->updateParticleID(args); + + // if we've found and replaced both the creatorTokenID and the viewedParticle, then we + // can stop looking, otherwise we will keep looking + if (args->creatorTokenFound && args->viewedParticleFound) { + keepSearching = false; + } + + return keepSearching; +} + +void ParticleTree::handleAddParticleResponse(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); + + // update particles in our tree + bool assumeParticleFound = !getIsViewing(); // if we're not a viewing tree, then we don't have to find the actual particle + FindAndUpdateParticleIDArgs args = { + particleID, + creatorTokenID, + false, + assumeParticleFound, + getIsViewing() + }; + + const bool wantDebug = false; + if (wantDebug) { + qDebug() << "looking for creatorTokenID=" << creatorTokenID << " particleID=" << particleID + << " getIsViewing()=" << getIsViewing(); + } + lockForWrite(); + recurseTreeWithOperation(findAndUpdateParticleIDOperation, &args); + unlock(); +} + class FindNearPointArgs { public: diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 3fd6c3eca3..54286e7c4f 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -41,6 +41,7 @@ public: void storeParticle(const Particle& particle, Node* senderNode = NULL); void updateParticle(const ParticleID& particleID, const ParticleProperties& properties); + void addParticle(const ParticleID& particleID, const ParticleProperties& properties); void deleteParticle(const ParticleID& particleID); const Particle* findClosestParticle(glm::vec3 position, float targetRadius); const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false); @@ -55,6 +56,7 @@ public: void forgetParticlesDeletedBefore(uint64_t sinceTime); void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); + void handleAddParticleResponse(unsigned char* packetData , int packetLength); private: @@ -66,6 +68,7 @@ private: static bool pruneOperation(OctreeElement* element, void* extraData); static bool findByIDOperation(OctreeElement* element, void* extraData); static bool findAndDeleteOperation(OctreeElement* element, void* extraData); + static bool findAndUpdateParticleIDOperation(OctreeElement* element, void* extraData); void notifyNewlyCreatedParticle(const Particle& newParticle, Node* senderNode); diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 43b40f8f9f..7e3f769daa 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -161,32 +161,61 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) { } bool ParticleTreeElement::updateParticle(const ParticleID& particleID, const ParticleProperties& properties) { - // currently only known IDs are in the tree... in the future we might support local storage of creatorTokenID particles - if (particleID.isKnownID) { - uint16_t numberOfParticles = _particles->size(); - uint32_t searchID = particleID.id; - for (uint16_t i = 0; i < numberOfParticles; i++) { - // note: unlike storeParticle() which is called from inbound packets, this is only called by local editors - // and therefore we can be confident that this change is higher priority and should be honored - Particle& thisParticle = (*_particles)[i]; - if (thisParticle.getID() == searchID) { - thisParticle.setProperties(properties); + uint16_t numberOfParticles = _particles->size(); + for (uint16_t i = 0; i < numberOfParticles; i++) { + // note: unlike storeParticle() which is called from inbound packets, this is only called by local editors + // and therefore we can be confident that this change is higher priority and should be honored + Particle& thisParticle = (*_particles)[i]; + + bool found = false; + if (particleID.isKnownID) { + found = thisParticle.getID() == particleID.id; + } else { + found = thisParticle.getCreatorTokenID() == particleID.creatorTokenID; + } + if (found) { + thisParticle.setProperties(properties); - const bool wantDebug = false; - if (wantDebug) { - uint64_t now = usecTimestampNow(); - int elapsed = now - thisParticle.getLastEdited(); + const bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - thisParticle.getLastEdited(); - qDebug() << "ParticleTreeElement::updateParticle() AFTER update... edited AGO=" << elapsed << - "now=" << now << " thisParticle.getLastEdited()=" << thisParticle.getLastEdited(); - } - return true; - } + qDebug() << "ParticleTreeElement::updateParticle() AFTER update... edited AGO=" << elapsed << + "now=" << now << " thisParticle.getLastEdited()=" << thisParticle.getLastEdited(); + } + return true; } } return false; } +void ParticleTreeElement::updateParticleID(FindAndUpdateParticleIDArgs* args) { + uint16_t numberOfParticles = _particles->size(); + for (uint16_t i = 0; i < numberOfParticles; i++) { + Particle& thisParticle = (*_particles)[i]; + + if (!args->creatorTokenFound) { + // first, we're looking for matching creatorTokenIDs, if we find that, then we fix it to know the actual ID + if (thisParticle.getCreatorTokenID() == args->creatorTokenID) { + thisParticle.setID(args->particleID); + args->creatorTokenFound = true; + } + } + + // if we're in an isViewing tree, we also need to look for an kill any viewed particles + if (!args->viewedParticleFound && args->isViewing) { + if (thisParticle.getCreatorTokenID() == UNKNOWN_TOKEN && thisParticle.getID() == args->particleID) { + _particles->removeAt(i); // remove the particle at this index + numberOfParticles--; // this means we have 1 fewer particle in this list + i--; // and we actually want to back up i as well. + args->viewedParticleFound = true; + } + } + } +} + + const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const { const Particle* closestParticle = NULL; diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index f8ac341400..b810efef97 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -26,6 +26,17 @@ public: QList _movingParticles; }; +class FindAndUpdateParticleIDArgs { +public: + uint32_t particleID; + uint32_t creatorTokenID; + bool creatorTokenFound; + bool viewedParticleFound; + bool isViewing; +}; + + + class ParticleTreeElement : public OctreeElement { friend class ParticleTree; // to allow createElement to new us... @@ -88,6 +99,7 @@ public: bool updateParticle(const Particle& particle); bool updateParticle(const ParticleID& particleID, const ParticleProperties& properties); + void updateParticleID(FindAndUpdateParticleIDArgs* args); const Particle* getClosestParticle(glm::vec3 position) const; QVector getParticles(glm::vec3 position, float radius) const; diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 73c316de37..d2f5edce26 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -31,6 +31,13 @@ ParticleID ParticlesScriptingInterface::addParticle(const ParticleProperties& pr // queue the packet queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, id, properties); + // If we have a local particle tree set, then also update it. + if (_particleTree) { + _particleTree->lockForWrite(); + _particleTree->addParticle(id, properties); + _particleTree->unlock(); + } + return id; } @@ -70,18 +77,21 @@ ParticleProperties ParticlesScriptingInterface::getParticleProperties(ParticleID ParticleID ParticlesScriptingInterface::editParticle(ParticleID particleID, const ParticleProperties& properties) { uint32_t actualID = particleID.id; + + // if the particle is unknown, attempt to look it up if (!particleID.isKnownID) { actualID = Particle::getIDfromCreatorTokenID(particleID.creatorTokenID); - if (actualID == UNKNOWN_PARTICLE_ID) { - return particleID; // bailing early - } } - particleID.id = actualID; - particleID.isKnownID = true; - queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + // if at this point, we know the id, send the update to the particle server + if (actualID != UNKNOWN_PARTICLE_ID) { + particleID.id = actualID; + particleID.isKnownID = true; + queueParticleMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); + } - // If we have a local particle tree set, then also update it. + // If we have a local particle tree set, then also update it. We can do this even if we don't know + // the actual id, because we can edit out local particles just with creatorTokenID if (_particleTree) { _particleTree->lockForWrite(); _particleTree->updateParticle(particleID, properties); From 568d5bc05de5f493875fe3e85446601fc0203a50 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 09:26:47 -0800 Subject: [PATCH 05/20] first cut at particle meshes --- interface/src/ParticleTreeRenderer.cpp | 82 ++++++++++++++-- interface/src/ParticleTreeRenderer.h | 7 ++ libraries/octree/src/OctreeRenderer.h | 2 +- libraries/particles/src/Particle.cpp | 99 +++++++++++++------- libraries/particles/src/Particle.h | 71 +++++++++++--- libraries/shared/src/RegisteredMetaTypes.cpp | 16 ++++ libraries/shared/src/RegisteredMetaTypes.h | 4 + 7 files changed, 229 insertions(+), 52 deletions(-) diff --git a/interface/src/ParticleTreeRenderer.cpp b/interface/src/ParticleTreeRenderer.cpp index aa4978891f..e0229bddaa 100644 --- a/interface/src/ParticleTreeRenderer.cpp +++ b/interface/src/ParticleTreeRenderer.cpp @@ -7,6 +7,8 @@ // // +#include + #include "InterfaceConfig.h" #include "ParticleTreeRenderer.h" @@ -16,8 +18,18 @@ ParticleTreeRenderer::ParticleTreeRenderer() : } ParticleTreeRenderer::~ParticleTreeRenderer() { + // delete the models in _particleModels + foreach(Model* model, _particleModels) { + delete model; + } + _particleModels.clear(); } +void ParticleTreeRenderer::init() { + OctreeRenderer::init(); +} + + void ParticleTreeRenderer::update() { if (_tree) { ParticleTree* tree = (ParticleTree*)_tree; @@ -27,6 +39,39 @@ void ParticleTreeRenderer::update() { } } +void ParticleTreeRenderer::render() { + OctreeRenderer::render(); +} + +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/lotus.fbx")); +//_testModel->setURL(QUrl("http://www.fungibleinsight.com/faces/tie.fbx")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Angie1.fbx")); +//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/orb_model.fbx")); +//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/space_frigate_6.FBX")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/soccer_ball.fbx")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/top%20fbx.FBX")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/golfball_FBX2010.fbx")); +//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/Combat_tank_V01.FBX")); + +//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/orc.fbx")); +//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX")); + +Model* ParticleTreeRenderer::getModel(const QString& url) { + Model* model = NULL; + + // if we don't already have this model then create it and initialize it + if (_particleModels.find(url) == _particleModels.end()) { + model = new Model(); + model->init(); + qDebug() << "calling model->setURL()"; + model->setURL(QUrl(url)); + qDebug() << "after calling setURL()"; + } else { + model = _particleModels[url]; + } + return model; +} + void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) { // actually render it here... // we need to iterate the actual particles of the element @@ -36,8 +81,6 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg uint16_t numberOfParticles = particles.size(); - bool drawAsSphere = true; - for (uint16_t i = 0; i < numberOfParticles; i++) { const Particle& particle = particles[i]; // render particle aspoints @@ -45,18 +88,41 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg glColor3ub(particle.getColor()[RED_INDEX],particle.getColor()[GREEN_INDEX],particle.getColor()[BLUE_INDEX]); float sphereRadius = particle.getRadius() * (float)TREE_SCALE; + bool drawAsModel = particle.hasModel(); + args->_renderedItems++; - if (drawAsSphere) { + if (drawAsModel) { + glPushMatrix(); + const float alpha = 1.0f; + + Model* model = getModel(particle.getModelURL()); + + glm::vec3 translationAdjustment = particle.getModelTranslation(); + + // set the position + glm::vec3 translation(position.x, position.y, position.z); + model->setTranslation(translation + translationAdjustment); + + // glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f) + // set the rotation + glm::quat rotation = particle.getModelRotation(); + model->setRotation(rotation); + + // scale + const float MODEL_SCALE = 0.0006f; // need to figure out correct scale adjust + glm::vec3 scale(1.0f,1.0f,1.0f); + model->setScale(scale * MODEL_SCALE); + + model->simulate(0.0f); + model->render(alpha); + //qDebug() << "called _testModel->render(alpha);"; + glPopMatrix(); + } else { glPushMatrix(); glTranslatef(position.x, position.y, position.z); glutSolidSphere(sphereRadius, 15, 15); glPopMatrix(); - } else { - glPointSize(sphereRadius); - glBegin(GL_POINTS); - glVertex3f(position.x, position.y, position.z); - glEnd(); } } } diff --git a/interface/src/ParticleTreeRenderer.h b/interface/src/ParticleTreeRenderer.h index 1c76cf623d..608d701f23 100644 --- a/interface/src/ParticleTreeRenderer.h +++ b/interface/src/ParticleTreeRenderer.h @@ -20,6 +20,7 @@ #include #include #include +#include "renderer/Model.h" // Generic client side Octree renderer class. class ParticleTreeRenderer : public OctreeRenderer { @@ -39,7 +40,13 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); + virtual void init(); + virtual void render(); + protected: + Model* getModel(const QString& url); + + QMap _particleModels; }; #endif /* defined(__hifi__ParticleTreeRenderer__) */ \ No newline at end of file diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index f321ed2e3e..10d655ba17 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -46,7 +46,7 @@ public: virtual void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); /// initialize and GPU/rendering related resources - void init(); + virtual void init(); /// render the content of the octree virtual void render(); diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 0d111c4e01..cb3d70ac27 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -391,49 +391,49 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe // radius - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_RADIUS) == PACKET_CONTAINS_RADIUS)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { memcpy(&newParticle._radius, dataAt, sizeof(newParticle._radius)); dataAt += sizeof(newParticle._radius); processedBytes += sizeof(newParticle._radius); } // position - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { memcpy(&newParticle._position, dataAt, sizeof(newParticle._position)); dataAt += sizeof(newParticle._position); processedBytes += sizeof(newParticle._position); } // color - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_COLOR) == PACKET_CONTAINS_COLOR)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) { memcpy(newParticle._color, dataAt, sizeof(newParticle._color)); dataAt += sizeof(newParticle._color); processedBytes += sizeof(newParticle._color); } // velocity - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { memcpy(&newParticle._velocity, dataAt, sizeof(newParticle._velocity)); dataAt += sizeof(newParticle._velocity); processedBytes += sizeof(newParticle._velocity); } // gravity - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_GRAVITY) == PACKET_CONTAINS_GRAVITY)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { memcpy(&newParticle._gravity, dataAt, sizeof(newParticle._gravity)); dataAt += sizeof(newParticle._gravity); processedBytes += sizeof(newParticle._gravity); } // damping - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_DAMPING) == PACKET_CONTAINS_DAMPING)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { memcpy(&newParticle._damping, dataAt, sizeof(newParticle._damping)); dataAt += sizeof(newParticle._damping); processedBytes += sizeof(newParticle._damping); } // lifetime - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_LIFETIME) == PACKET_CONTAINS_LIFETIME)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { memcpy(&newParticle._lifetime, dataAt, sizeof(newParticle._lifetime)); dataAt += sizeof(newParticle._lifetime); processedBytes += sizeof(newParticle._lifetime); @@ -441,21 +441,21 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe // TODO: make inHand and shouldDie into single bits // inHand - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { memcpy(&newParticle._inHand, dataAt, sizeof(newParticle._inHand)); dataAt += sizeof(newParticle._inHand); processedBytes += sizeof(newParticle._inHand); } // shouldDie - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SHOULDDIE) == PACKET_CONTAINS_SHOULDDIE)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { memcpy(&newParticle._shouldDie, dataAt, sizeof(newParticle._shouldDie)); dataAt += sizeof(newParticle._shouldDie); processedBytes += sizeof(newParticle._shouldDie); } // script - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SCRIPT) == PACKET_CONTAINS_SCRIPT)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { uint16_t scriptLength; memcpy(&scriptLength, dataAt, sizeof(scriptLength)); dataAt += sizeof(scriptLength); @@ -556,7 +556,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // radius - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_RADIUS) == PACKET_CONTAINS_RADIUS)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { float radius = properties.getRadius() / (float) TREE_SCALE; memcpy(copyAt, &radius, sizeof(radius)); copyAt += sizeof(radius); @@ -564,7 +564,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // position - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_POSITION) == PACKET_CONTAINS_POSITION)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { glm::vec3 position = properties.getPosition() / (float)TREE_SCALE; memcpy(copyAt, &position, sizeof(position)); copyAt += sizeof(position); @@ -572,7 +572,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // color - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_COLOR) == PACKET_CONTAINS_COLOR)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) { rgbColor color = { properties.getColor().red, properties.getColor().green, properties.getColor().blue }; memcpy(copyAt, color, sizeof(color)); copyAt += sizeof(color); @@ -580,7 +580,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // velocity - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_VELOCITY) == PACKET_CONTAINS_VELOCITY)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { glm::vec3 velocity = properties.getVelocity() / (float)TREE_SCALE; memcpy(copyAt, &velocity, sizeof(velocity)); copyAt += sizeof(velocity); @@ -588,7 +588,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // gravity - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_GRAVITY) == PACKET_CONTAINS_GRAVITY)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { glm::vec3 gravity = properties.getGravity() / (float)TREE_SCALE; memcpy(copyAt, &gravity, sizeof(gravity)); copyAt += sizeof(gravity); @@ -596,7 +596,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // damping - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_DAMPING) == PACKET_CONTAINS_DAMPING)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { float damping = properties.getDamping(); memcpy(copyAt, &damping, sizeof(damping)); copyAt += sizeof(damping); @@ -604,7 +604,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // lifetime - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_LIFETIME) == PACKET_CONTAINS_LIFETIME)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { float lifetime = properties.getLifetime(); memcpy(copyAt, &lifetime, sizeof(lifetime)); copyAt += sizeof(lifetime); @@ -612,7 +612,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // inHand - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_INHAND) == PACKET_CONTAINS_INHAND)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { bool inHand = properties.getInHand(); memcpy(copyAt, &inHand, sizeof(inHand)); copyAt += sizeof(inHand); @@ -620,7 +620,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // shoulDie - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SHOULDDIE) == PACKET_CONTAINS_SHOULDDIE)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { bool shouldDie = properties.getShouldDie(); memcpy(copyAt, &shouldDie, sizeof(shouldDie)); copyAt += sizeof(shouldDie); @@ -628,7 +628,7 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID } // script - if (isNewParticle || ((packetContainsBits & PACKET_CONTAINS_SCRIPT) == PACKET_CONTAINS_SCRIPT)) { + if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { uint16_t scriptLength = properties.getScript().size() + 1; memcpy(copyAt, &scriptLength, sizeof(scriptLength)); copyAt += sizeof(scriptLength); @@ -857,10 +857,15 @@ ParticleProperties::ParticleProperties() : _script(""), _inHand(false), _shouldDie(false), + _modelURL(""), + _modelTranslation(DEFAULT_MODEL_TRANSLATION), + _modelRotation(DEFAULT_MODEL_ROTATION), + _modelScale(DEFAULT_MODEL_SCALE), _id(UNKNOWN_PARTICLE_ID), _idSet(false), _lastEdited(usecTimestampNow()), + _positionChanged(false), _colorChanged(false), _radiusChanged(false), @@ -871,6 +876,10 @@ ParticleProperties::ParticleProperties() : _scriptChanged(false), _inHandChanged(false), _shouldDieChanged(false), + _modelURLChanged(false), + _modelTranslationChanged(false), + _modelRotationChanged(false), + _modelScaleChanged(false), _defaultSettings(true) { } @@ -879,44 +888,59 @@ ParticleProperties::ParticleProperties() : uint16_t ParticleProperties::getChangedBits() const { uint16_t changedBits = 0; if (_radiusChanged) { - changedBits += PACKET_CONTAINS_RADIUS; + changedBits += CONTAINS_RADIUS; } if (_positionChanged) { - changedBits += PACKET_CONTAINS_POSITION; + changedBits += CONTAINS_POSITION; } if (_colorChanged) { - changedBits += PACKET_CONTAINS_COLOR; + changedBits += CONTAINS_COLOR; } if (_velocityChanged) { - changedBits += PACKET_CONTAINS_VELOCITY; + changedBits += CONTAINS_VELOCITY; } if (_gravityChanged) { - changedBits += PACKET_CONTAINS_GRAVITY; + changedBits += CONTAINS_GRAVITY; } if (_dampingChanged) { - changedBits += PACKET_CONTAINS_DAMPING; + changedBits += CONTAINS_DAMPING; } if (_lifetimeChanged) { - changedBits += PACKET_CONTAINS_LIFETIME; + changedBits += CONTAINS_LIFETIME; } if (_inHandChanged) { - changedBits += PACKET_CONTAINS_INHAND; + changedBits += CONTAINS_INHAND; } if (_scriptChanged) { - changedBits += PACKET_CONTAINS_SCRIPT; + changedBits += CONTAINS_SCRIPT; } - // how do we want to handle this? if (_shouldDieChanged) { - changedBits += PACKET_CONTAINS_SHOULDDIE; + changedBits += CONTAINS_SHOULDDIE; + } + + if (_modelURLChanged) { + changedBits += CONTAINS_MODEL_URL; + } + + if (_modelTranslationChanged) { + changedBits += CONTAINS_MODEL_TRANSLATION; + } + + if (_modelRotationChanged) { + changedBits += CONTAINS_MODEL_ROTATION; + } + + if (_modelScaleChanged) { + changedBits += CONTAINS_MODEL_SCALE; } return changedBits; @@ -945,7 +969,18 @@ QScriptValue ParticleProperties::copyToScriptValue(QScriptEngine* engine) const properties.setProperty("script", _script); properties.setProperty("inHand", _inHand); properties.setProperty("shouldDie", _shouldDie); - + + properties.setProperty("modelURL", _modelURL); + + QScriptValue modelTranslation = vec3toScriptValue(engine, _modelTranslation); + properties.setProperty("modelTranslation", modelTranslation); + + QScriptValue modelRotation = quatToScriptValue(engine, _modelRotation); + properties.setProperty("modelRotation", modelRotation); + + properties.setProperty("modelScale", _modelScale); + + if (_idSet) { properties.setProperty("id", _id); properties.setProperty("isKnownID", (_id == UNKNOWN_PARTICLE_ID)); diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index e37db04a06..535d77d6af 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -31,16 +31,20 @@ const uint32_t NEW_PARTICLE = 0xFFFFFFFF; const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF; const uint32_t UNKNOWN_PARTICLE_ID = 0xFFFFFFFF; -const uint16_t PACKET_CONTAINS_RADIUS = 1; -const uint16_t PACKET_CONTAINS_POSITION = 2; -const uint16_t PACKET_CONTAINS_COLOR = 4; -const uint16_t PACKET_CONTAINS_VELOCITY = 8; -const uint16_t PACKET_CONTAINS_GRAVITY = 16; -const uint16_t PACKET_CONTAINS_DAMPING = 32; -const uint16_t PACKET_CONTAINS_LIFETIME = 64; -const uint16_t PACKET_CONTAINS_INHAND = 128; -const uint16_t PACKET_CONTAINS_SCRIPT = 256; -const uint16_t PACKET_CONTAINS_SHOULDDIE = 512; +const uint16_t CONTAINS_RADIUS = 1; +const uint16_t CONTAINS_POSITION = 2; +const uint16_t CONTAINS_COLOR = 4; +const uint16_t CONTAINS_VELOCITY = 8; +const uint16_t CONTAINS_GRAVITY = 16; +const uint16_t CONTAINS_DAMPING = 32; +const uint16_t CONTAINS_LIFETIME = 64; +const uint16_t CONTAINS_INHAND = 128; +const uint16_t CONTAINS_SCRIPT = 256; +const uint16_t CONTAINS_SHOULDDIE = 512; +const uint16_t CONTAINS_MODEL_URL = 1024; +const uint16_t CONTAINS_MODEL_TRANSLATION = 1024; +const uint16_t CONTAINS_MODEL_ROTATION = 2048; +const uint16_t CONTAINS_MODEL_SCALE = 4096; const float DEFAULT_LIFETIME = 10.0f; // particles live for 10 seconds by default const float DEFAULT_DAMPING = 0.99f; @@ -48,9 +52,13 @@ const float DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_PARTICLE_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container const glm::vec3 DEFAULT_GRAVITY(0, (-9.8f / TREE_SCALE), 0); const QString DEFAULT_SCRIPT(""); +const glm::vec3 DEFAULT_MODEL_TRANSLATION(0, 0, 0); +const glm::quat DEFAULT_MODEL_ROTATION(0, 0, 0, 0); +const float DEFAULT_MODEL_SCALE = 1.0f; const bool IN_HAND = true; // it's in a hand const bool NOT_IN_HAND = !IN_HAND; // it's not in a hand + /// A collection of properties of a particle used in the scripting API. Translates between the actual properties of a particle /// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of /// particle properties via JavaScript hashes/QScriptValues @@ -75,6 +83,10 @@ public: const QString& getScript() const { return _script; } bool getInHand() const { return _inHand; } bool getShouldDie() const { return _shouldDie; } + const QString& getModelURL() const { return _modelURL; } + const glm::vec3& getModelTranslation() const { return _modelTranslation; } + const glm::quat& getModelRotation() const { return _modelRotation; } + float getModelScale() const { return _modelScale; } uint64_t getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -93,7 +105,14 @@ public: void setDamping(float value) { _damping = value; _dampingChanged = true; } void setShouldDie(bool shouldDie) { _shouldDie = shouldDie; _shouldDieChanged = true; } void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; } - void setScript(QString updateScript) { _script = updateScript; _scriptChanged = true; } + void setScript(const QString& updateScript) { _script = updateScript; _scriptChanged = true; } + + // model related properties + void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } + void setModelTranslation(const glm::vec3& translation) { _modelTranslation = translation; + _modelTranslationChanged = true; } + void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } + void setModelScale(float scale) { _modelScale = scale; _modelScaleChanged = true; } /// used by ParticleScriptingInterface to return ParticleProperties for unknown particles void setIsUnknownID() { _id = UNKNOWN_PARTICLE_ID; _idSet = true; } @@ -109,10 +128,15 @@ private: QString _script; bool _inHand; bool _shouldDie; + QString _modelURL; + glm::vec3 _modelTranslation; + glm::quat _modelRotation; + float _modelScale; uint32_t _id; bool _idSet; uint64_t _lastEdited; + bool _positionChanged; bool _colorChanged; bool _radiusChanged; @@ -123,6 +147,10 @@ private: bool _scriptChanged; bool _inHandChanged; bool _shouldDieChanged; + bool _modelURLChanged; + bool _modelTranslationChanged; + bool _modelRotationChanged; + bool _modelScaleChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ParticleProperties); @@ -196,6 +224,14 @@ public: bool getInHand() const { return _inHand; } float getDamping() const { return _damping; } float getLifetime() const { return _lifetime; } + + // model related properties + bool hasModel() const { return !_modelURL.isEmpty(); } + const QString& getModelURL() const { return _modelURL; } + const glm::vec3& getModelTranslation() const { return _modelTranslation; } + const glm::quat& getModelRotation() const { return _modelRotation; } + float getModelScale() const { return _modelScale; } + ParticleProperties getProperties() const; /// The last updated/simulated time of this particle from the time perspective of the authoritative server/source @@ -238,6 +274,13 @@ public: void setLifetime(float value) { _lifetime = value; } void setScript(QString updateScript) { _script = updateScript; } void setCreatorTokenID(uint32_t creatorTokenID) { _creatorTokenID = creatorTokenID; } + + // model related properties + void setModelURL(const QString& url) { _modelURL = url; } + void setModelTranslation(const glm::vec3& translation) { _modelTranslation = translation; } + void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } + void setModelScale(float scale) { _modelScale = scale; } + void setProperties(const ParticleProperties& properties); bool appendParticleData(OctreePacketData* packetData) const; @@ -300,6 +343,12 @@ protected: QString _script; bool _inHand; + // model related items + QString _modelURL; + glm::vec3 _modelTranslation; + glm::quat _modelRotation; + float _modelScale; + uint32_t _creatorTokenID; bool _newlyCreated; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 9d2eec6b40..4b0f2c403e 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -13,6 +13,7 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); + qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue); } @@ -42,6 +43,21 @@ void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2) { vec2.y = object.property("y").toVariant().toFloat(); } +QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", quat.x); + obj.setProperty("y", quat.y); + obj.setProperty("z", quat.z); + obj.setProperty("w", quat.w); + return obj; +} + +void quatFromScriptValue(const QScriptValue &object, glm::quat& quat) { + quat.x = object.property("x").toVariant().toFloat(); + quat.y = object.property("y").toVariant().toFloat(); + quat.z = object.property("z").toVariant().toFloat(); + quat.w = object.property("w").toVariant().toFloat(); +} QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { QScriptValue obj = engine->newObject(); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 4d91a21be7..de667c9ed8 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -19,6 +19,7 @@ Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec2) +Q_DECLARE_METATYPE(glm::quat) Q_DECLARE_METATYPE(xColor) void registerMetaTypes(QScriptEngine* engine); @@ -29,6 +30,9 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2); void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2); +QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat); +void quatFromScriptValue(const QScriptValue &object, glm::quat& quat); + QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color); void xColorFromScriptValue(const QScriptValue &object, xColor& color); From 427abd450963a3df7b604f73f9e0776215d6cf6b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 11:18:37 -0800 Subject: [PATCH 06/20] add model properties to particles --- libraries/octree/src/OctreePacketData.cpp | 13 + libraries/octree/src/OctreePacketData.h | 3 + libraries/particles/src/Particle.cpp | 446 +++++++++++++++------- libraries/particles/src/Particle.h | 6 - libraries/shared/src/PacketHeaders.cpp | 4 +- libraries/shared/src/SharedUtil.cpp | 2 +- libraries/shared/src/SharedUtil.h | 4 +- 7 files changed, 329 insertions(+), 149 deletions(-) diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index 9e7d88416b..59216918b1 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -305,6 +305,19 @@ bool OctreePacketData::appendValue(const glm::vec3& value) { return success; } +bool OctreePacketData::appendValue(const glm::quat& value) { + const size_t VALUES_PER_QUAT = 4; + const size_t PACKED_QUAT_SIZE = sizeof(uint16_t) * VALUES_PER_QUAT; + unsigned char data[PACKED_QUAT_SIZE]; + int length = packOrientationQuatToBytes(data, value); + bool success = append(data, length); + if (success) { + _bytesOfValues += length; + _totalBytesOfValues += length; + } + return success; +} + bool OctreePacketData::appendValue(bool value) { bool success = append((uint8_t)value); // used unsigned char version if (success) { diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 87b55ace96..27f2ee5090 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -130,6 +130,9 @@ public: /// appends a non-position vector to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::vec3& value); + /// appends a packed quat to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const glm::quat& value); + /// appends a bool value to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(bool value); diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index cb3d70ac27..2dbf59efbe 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -65,13 +65,6 @@ void Particle::handleAddParticleResponse(unsigned char* packetData , int packetL } - -Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity, - float damping, float lifetime, bool inHand, QString updateScript, uint32_t id) { - - init(position, radius, color, velocity, gravity, damping, lifetime, inHand, updateScript, id); -} - Particle::Particle() { rgbColor noColor = { 0, 0, 0 }; init(glm::vec3(0,0,0), 0, noColor, glm::vec3(0,0,0), @@ -182,7 +175,6 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const { if (success) { success = packetData->appendValue(getShouldDie()); } - if (success) { uint16_t scriptLength = _script.size() + 1; // include NULL success = packetData->appendValue(scriptLength); @@ -190,6 +182,28 @@ bool Particle::appendParticleData(OctreePacketData* packetData) const { success = packetData->appendRawData((const unsigned char*)qPrintable(_script), scriptLength); } } + + // modelURL + if (success) { + uint16_t modelURLLength = _modelURL.size() + 1; // include NULL + success = packetData->appendValue(modelURLLength); + if (success) { + success = packetData->appendRawData((const unsigned char*)qPrintable(_modelURL), modelURLLength); + } + } + // modelTranslation + if (success) { + success = packetData->appendValue(getModelTranslation()); + } + // modelRotation + if (success) { + success = packetData->appendValue(getModelRotation()); + } + // modelScale + if (success) { + success = packetData->appendValue(getModelScale()); + } + return success; } @@ -210,21 +224,6 @@ int Particle::expectedBytes() { return expectedBytes; } -int Particle::expectedEditMessageBytes() { - int expectedBytes = sizeof(uint32_t) // id - + sizeof(uint64_t) // lasted edited - + sizeof(float) // radius - + sizeof(glm::vec3) // position - + sizeof(rgbColor) // color - + sizeof(glm::vec3) // velocity - + sizeof(glm::vec3) // gravity - + sizeof(float) // damping - + sizeof(float) // lifetime - + sizeof(bool); // inhand - // potentially more... - return expectedBytes; -} - int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { int bytesRead = 0; if (bytesLeftToRead >= expectedBytes()) { @@ -311,6 +310,31 @@ int Particle::readParticleDataFromBuffer(const unsigned char* data, int bytesLef dataAt += scriptLength; bytesRead += scriptLength; + // modelURL + uint16_t modelURLLength; + memcpy(&modelURLLength, dataAt, sizeof(modelURLLength)); + dataAt += sizeof(modelURLLength); + bytesRead += sizeof(modelURLLength); + QString modelURLString((const char*)dataAt); + _modelURL = modelURLString; + dataAt += modelURLLength; + bytesRead += modelURLLength; + + // modelTranslation + memcpy(&_modelTranslation, dataAt, sizeof(_modelTranslation)); + dataAt += sizeof(_modelTranslation); + bytesRead += sizeof(_modelTranslation); + + // modelRotation + int bytes = unpackOrientationQuatFromBytes(dataAt, _modelRotation); + dataAt += bytes; + bytesRead += bytes; + + // modelScale + memcpy(&_modelScale, dataAt, sizeof(_modelScale)); + dataAt += sizeof(_modelScale); + bytesRead += sizeof(_modelScale); + //printf("Particle::readParticleDataFromBuffer()... "); debugDump(); } return bytesRead; @@ -466,6 +490,39 @@ Particle Particle::fromEditPacket(unsigned char* data, int length, int& processe processedBytes += scriptLength; } + // modelURL + if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) { + uint16_t modelURLLength; + memcpy(&modelURLLength, dataAt, sizeof(modelURLLength)); + dataAt += sizeof(modelURLLength); + processedBytes += sizeof(modelURLLength); + QString tempString((const char*)dataAt); + newParticle._modelURL = tempString; + dataAt += modelURLLength; + processedBytes += modelURLLength; + } + + // modelTranslation + if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) { + memcpy(&newParticle._modelTranslation, dataAt, sizeof(newParticle._modelTranslation)); + dataAt += sizeof(newParticle._modelTranslation); + processedBytes += sizeof(newParticle._modelTranslation); + } + + // modelRotation + if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) { + int bytes = unpackOrientationQuatFromBytes(dataAt, newParticle._modelRotation); + dataAt += bytes; + processedBytes += bytes; + } + + // modelScale + if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) { + memcpy(&newParticle._modelScale, dataAt, sizeof(newParticle._modelScale)); + dataAt += sizeof(newParticle._modelScale); + processedBytes += sizeof(newParticle._modelScale); + } + const bool wantDebugging = false; if (wantDebugging) { qDebug("Particle::fromEditPacket()..."); @@ -512,138 +569,166 @@ bool Particle::encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID int octets = numberOfThreeBitSectionsInCode(octcode); int lengthOfOctcode = bytesRequiredForCodeLength(octets); - int lenfthOfEditData = lengthOfOctcode + expectedEditMessageBytes(); - // make sure we have room to copy this particle - if (sizeOut + lenfthOfEditData > sizeIn) { - success = false; - } else { - // add it to our message - memcpy(copyAt, octcode, lengthOfOctcode); - copyAt += lengthOfOctcode; - sizeOut += lengthOfOctcode; + // add it to our message + memcpy(copyAt, octcode, lengthOfOctcode); + copyAt += lengthOfOctcode; + sizeOut += lengthOfOctcode; - // Now add our edit content details... - bool isNewParticle = (id.id == NEW_PARTICLE); + // Now add our edit content details... + bool isNewParticle = (id.id == NEW_PARTICLE); - // id - memcpy(copyAt, &id.id, sizeof(id.id)); - copyAt += sizeof(id.id); - sizeOut += sizeof(id.id); + // id + memcpy(copyAt, &id.id, sizeof(id.id)); + copyAt += sizeof(id.id); + sizeOut += sizeof(id.id); - // special case for handling "new" particles - if (isNewParticle) { - // 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, &id.creatorTokenID, sizeof(id.creatorTokenID)); - copyAt += sizeof(id.creatorTokenID); - sizeOut += sizeof(id.creatorTokenID); - } + // special case for handling "new" particles + if (isNewParticle) { + // 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, &id.creatorTokenID, sizeof(id.creatorTokenID)); + copyAt += sizeof(id.creatorTokenID); + sizeOut += sizeof(id.creatorTokenID); + } - // lastEdited - uint64_t lastEdited = properties.getLastEdited(); - memcpy(copyAt, &lastEdited, sizeof(lastEdited)); - copyAt += sizeof(lastEdited); - sizeOut += sizeof(lastEdited); + // lastEdited + uint64_t lastEdited = properties.getLastEdited(); + memcpy(copyAt, &lastEdited, sizeof(lastEdited)); + copyAt += sizeof(lastEdited); + sizeOut += sizeof(lastEdited); - // For new particles, all remaining items are mandatory, for an edited particle, All of the remaining items are - // optional, and may or may not be included based on their included values in the properties included bits - uint16_t packetContainsBits = properties.getChangedBits(); - if (!isNewParticle) { - memcpy(copyAt, &packetContainsBits, sizeof(packetContainsBits)); - copyAt += sizeof(packetContainsBits); - sizeOut += sizeof(packetContainsBits); - } + // For new particles, all remaining items are mandatory, for an edited particle, All of the remaining items are + // optional, and may or may not be included based on their included values in the properties included bits + uint16_t packetContainsBits = properties.getChangedBits(); + if (!isNewParticle) { + memcpy(copyAt, &packetContainsBits, sizeof(packetContainsBits)); + copyAt += sizeof(packetContainsBits); + sizeOut += sizeof(packetContainsBits); + } - // radius - if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { - float radius = properties.getRadius() / (float) TREE_SCALE; - memcpy(copyAt, &radius, sizeof(radius)); - copyAt += sizeof(radius); - sizeOut += sizeof(radius); - } + // radius + if (isNewParticle || ((packetContainsBits & CONTAINS_RADIUS) == CONTAINS_RADIUS)) { + float radius = properties.getRadius() / (float) TREE_SCALE; + memcpy(copyAt, &radius, sizeof(radius)); + copyAt += sizeof(radius); + sizeOut += sizeof(radius); + } - // position - if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { - glm::vec3 position = properties.getPosition() / (float)TREE_SCALE; - memcpy(copyAt, &position, sizeof(position)); - copyAt += sizeof(position); - sizeOut += sizeof(position); - } + // position + if (isNewParticle || ((packetContainsBits & CONTAINS_POSITION) == CONTAINS_POSITION)) { + glm::vec3 position = properties.getPosition() / (float)TREE_SCALE; + memcpy(copyAt, &position, sizeof(position)); + copyAt += sizeof(position); + sizeOut += sizeof(position); + } - // color - if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) { - rgbColor color = { properties.getColor().red, properties.getColor().green, properties.getColor().blue }; - memcpy(copyAt, color, sizeof(color)); - copyAt += sizeof(color); - sizeOut += sizeof(color); - } + // color + if (isNewParticle || ((packetContainsBits & CONTAINS_COLOR) == CONTAINS_COLOR)) { + rgbColor color = { properties.getColor().red, properties.getColor().green, properties.getColor().blue }; + memcpy(copyAt, color, sizeof(color)); + copyAt += sizeof(color); + sizeOut += sizeof(color); + } - // velocity - if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { - glm::vec3 velocity = properties.getVelocity() / (float)TREE_SCALE; - memcpy(copyAt, &velocity, sizeof(velocity)); - copyAt += sizeof(velocity); - sizeOut += sizeof(velocity); - } + // velocity + if (isNewParticle || ((packetContainsBits & CONTAINS_VELOCITY) == CONTAINS_VELOCITY)) { + glm::vec3 velocity = properties.getVelocity() / (float)TREE_SCALE; + memcpy(copyAt, &velocity, sizeof(velocity)); + copyAt += sizeof(velocity); + sizeOut += sizeof(velocity); + } - // gravity - if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { - glm::vec3 gravity = properties.getGravity() / (float)TREE_SCALE; - memcpy(copyAt, &gravity, sizeof(gravity)); - copyAt += sizeof(gravity); - sizeOut += sizeof(gravity); - } + // gravity + if (isNewParticle || ((packetContainsBits & CONTAINS_GRAVITY) == CONTAINS_GRAVITY)) { + glm::vec3 gravity = properties.getGravity() / (float)TREE_SCALE; + memcpy(copyAt, &gravity, sizeof(gravity)); + copyAt += sizeof(gravity); + sizeOut += sizeof(gravity); + } - // damping - if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { - float damping = properties.getDamping(); - memcpy(copyAt, &damping, sizeof(damping)); - copyAt += sizeof(damping); - sizeOut += sizeof(damping); - } + // damping + if (isNewParticle || ((packetContainsBits & CONTAINS_DAMPING) == CONTAINS_DAMPING)) { + float damping = properties.getDamping(); + memcpy(copyAt, &damping, sizeof(damping)); + copyAt += sizeof(damping); + sizeOut += sizeof(damping); + } - // lifetime - if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { - float lifetime = properties.getLifetime(); - memcpy(copyAt, &lifetime, sizeof(lifetime)); - copyAt += sizeof(lifetime); - sizeOut += sizeof(lifetime); - } + // lifetime + if (isNewParticle || ((packetContainsBits & CONTAINS_LIFETIME) == CONTAINS_LIFETIME)) { + float lifetime = properties.getLifetime(); + memcpy(copyAt, &lifetime, sizeof(lifetime)); + copyAt += sizeof(lifetime); + sizeOut += sizeof(lifetime); + } - // inHand - if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { - bool inHand = properties.getInHand(); - memcpy(copyAt, &inHand, sizeof(inHand)); - copyAt += sizeof(inHand); - sizeOut += sizeof(inHand); - } + // inHand + if (isNewParticle || ((packetContainsBits & CONTAINS_INHAND) == CONTAINS_INHAND)) { + bool inHand = properties.getInHand(); + memcpy(copyAt, &inHand, sizeof(inHand)); + copyAt += sizeof(inHand); + sizeOut += sizeof(inHand); + } - // shoulDie - if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { - bool shouldDie = properties.getShouldDie(); - memcpy(copyAt, &shouldDie, sizeof(shouldDie)); - copyAt += sizeof(shouldDie); - sizeOut += sizeof(shouldDie); - } + // shoulDie + if (isNewParticle || ((packetContainsBits & CONTAINS_SHOULDDIE) == CONTAINS_SHOULDDIE)) { + bool shouldDie = properties.getShouldDie(); + memcpy(copyAt, &shouldDie, sizeof(shouldDie)); + copyAt += sizeof(shouldDie); + sizeOut += sizeof(shouldDie); + } - // script - if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { - uint16_t scriptLength = properties.getScript().size() + 1; - memcpy(copyAt, &scriptLength, sizeof(scriptLength)); - copyAt += sizeof(scriptLength); - sizeOut += sizeof(scriptLength); - memcpy(copyAt, qPrintable(properties.getScript()), scriptLength); - copyAt += scriptLength; - sizeOut += scriptLength; - } + // script + if (isNewParticle || ((packetContainsBits & CONTAINS_SCRIPT) == CONTAINS_SCRIPT)) { + uint16_t scriptLength = properties.getScript().size() + 1; + memcpy(copyAt, &scriptLength, sizeof(scriptLength)); + copyAt += sizeof(scriptLength); + sizeOut += sizeof(scriptLength); + memcpy(copyAt, qPrintable(properties.getScript()), scriptLength); + copyAt += scriptLength; + sizeOut += scriptLength; + } - bool wantDebugging = false; - if (wantDebugging) { - printf("encodeParticleEditMessageDetails()....\n"); - printf("Particle id :%u\n", id.id); - printf(" nextID:%u\n", _nextID); - } + // modelURL + if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_URL) == CONTAINS_MODEL_URL)) { + uint16_t urlLength = properties.getModelURL().size() + 1; + memcpy(copyAt, &urlLength, sizeof(urlLength)); + copyAt += sizeof(urlLength); + sizeOut += sizeof(urlLength); + memcpy(copyAt, qPrintable(properties.getModelURL()), urlLength); + copyAt += urlLength; + sizeOut += urlLength; + } + + // modelTranslation + if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_TRANSLATION) == CONTAINS_MODEL_TRANSLATION)) { + glm::vec3 modelTranslation = properties.getModelTranslation(); // should this be relative to TREE_SCALE?? + memcpy(copyAt, &modelTranslation, sizeof(modelTranslation)); + copyAt += sizeof(modelTranslation); + sizeOut += sizeof(modelTranslation); + } + + // modelRotation + if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_ROTATION) == CONTAINS_MODEL_ROTATION)) { + int bytes = packOrientationQuatToBytes(copyAt, properties.getModelRotation()); + copyAt += bytes; + sizeOut += bytes; + } + + // modelScale + if (isNewParticle || ((packetContainsBits & CONTAINS_MODEL_SCALE) == CONTAINS_MODEL_SCALE)) { + float modelScale = properties.getModelScale(); + memcpy(copyAt, &modelScale, sizeof(modelScale)); + copyAt += sizeof(modelScale); + sizeOut += sizeof(modelScale); + } + + bool wantDebugging = false; + if (wantDebugging) { + printf("encodeParticleEditMessageDetails()....\n"); + printf("Particle id :%u\n", id.id); + printf(" nextID:%u\n", _nextID); } // cleanup @@ -1121,6 +1206,63 @@ void ParticleProperties::copyFromScriptValue(const QScriptValue &object) { } } + QScriptValue modelURL = object.property("modelURL"); + if (modelURL.isValid()) { + QString newModelURL; + newModelURL = modelURL.toVariant().toString(); + if (_defaultSettings || newModelURL != _modelURL) { + _modelURL = newModelURL; + _modelURLChanged = true; + } + } + + QScriptValue modelTranslation = object.property("modelTranslation"); + if (modelTranslation.isValid()) { + QScriptValue x = modelTranslation.property("x"); + QScriptValue y = modelTranslation.property("y"); + QScriptValue z = modelTranslation.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newModelTranslation; + newModelTranslation.x = x.toVariant().toFloat(); + newModelTranslation.y = y.toVariant().toFloat(); + newModelTranslation.z = z.toVariant().toFloat(); + if (_defaultSettings || newModelTranslation != _modelTranslation) { + _modelTranslation = newModelTranslation; + _modelTranslationChanged = true; + } + } + } + + + QScriptValue modelRotation = object.property("modelRotation"); + if (modelRotation.isValid()) { + QScriptValue x = modelRotation.property("x"); + QScriptValue y = modelRotation.property("y"); + QScriptValue z = modelRotation.property("z"); + QScriptValue w = modelRotation.property("w"); + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + glm::quat newModelRotation; + newModelRotation.x = x.toVariant().toFloat(); + newModelRotation.y = y.toVariant().toFloat(); + newModelRotation.z = z.toVariant().toFloat(); + newModelRotation.w = w.toVariant().toFloat(); + if (_defaultSettings || newModelRotation != _modelRotation) { + _modelRotation = newModelRotation; + _modelRotationChanged = true; + } + } + } + + QScriptValue modelScale = object.property("modelScale"); + if (modelScale.isValid()) { + float newModelScale; + newModelScale = modelScale.toVariant().toFloat(); + if (_defaultSettings || newModelScale != _modelScale) { + _modelScale = newModelScale; + _modelScaleChanged = true; + } + } + _lastEdited = usecTimestampNow(); } @@ -1175,6 +1317,26 @@ void ParticleProperties::copyToParticle(Particle& particle) const { particle.setShouldDie(_shouldDie); somethingChanged = true; } + + if (_modelURLChanged) { + particle.setModelURL(_modelURL); + somethingChanged = true; + } + + if (_modelTranslationChanged) { + particle.setModelTranslation(_modelTranslation); + somethingChanged = true; + } + + if (_modelRotationChanged) { + particle.setModelRotation(_modelRotation); + somethingChanged = true; + } + + if (_modelScaleChanged) { + particle.setModelScale(_modelScale); + somethingChanged = true; + } if (somethingChanged) { bool wantDebug = false; @@ -1199,6 +1361,10 @@ void ParticleProperties::copyFromParticle(const Particle& particle) { _script = particle.getScript(); _inHand = particle.getInHand(); _shouldDie = particle.getShouldDie(); + _modelURL = particle.getModelURL(); + _modelTranslation = particle.getModelTranslation(); + _modelRotation = particle.getModelRotation(); + _modelScale = particle.getModelScale(); _id = particle.getID(); _idSet = true; @@ -1213,6 +1379,10 @@ void ParticleProperties::copyFromParticle(const Particle& particle) { _scriptChanged = false; _inHandChanged = false; _shouldDieChanged = false; + _modelURLChanged = false; + _modelTranslationChanged = false; + _modelRotationChanged = false; + _modelScaleChanged = false; _defaultSettings = false; } diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index 535d77d6af..fa9ea14d0e 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -192,11 +192,6 @@ public: Particle(const ParticleID& particleID, const ParticleProperties& properties); - /// all position, velocity, gravity, radius units are in domain units (0.0 to 1.0) - Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, - glm::vec3 gravity = DEFAULT_GRAVITY, float damping = DEFAULT_DAMPING, float lifetime = DEFAULT_LIFETIME, - 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, bool& valid); @@ -286,7 +281,6 @@ public: bool appendParticleData(OctreePacketData* packetData) const; int readParticleDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); static int expectedBytes(); - static int expectedEditMessageBytes(); static bool encodeParticleEditMessageDetails(PACKET_TYPE command, ParticleID id, const ParticleProperties& details, unsigned char* bufferOut, int sizeIn, int& sizeOut); diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 24a34f618e..0f81110d01 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -45,10 +45,10 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { return 1; case PACKET_TYPE_PARTICLE_ADD_OR_EDIT: - return 4; + return 5; case PACKET_TYPE_PARTICLE_DATA: - return 8; + return 9; case PACKET_TYPE_PING_REPLY: return 1; diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 9433fe1236..00e11e2b19 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -607,7 +607,7 @@ int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput return sizeof(quatParts); } -int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput) { +int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput) { uint16_t quatParts[4]; memcpy(&quatParts, buffer, sizeof(quatParts)); diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index a77663bbda..174742fcf3 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -152,7 +152,7 @@ int unpackFloatAngleFromTwoByte(uint16_t* byteAnglePointer, float* destinationPo // Orientation Quats are known to have 4 normalized components be between -1.0 and 1.0 // this allows us to encode each component in 16bits with great accuracy int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput); -int unpackOrientationQuatFromBytes(unsigned char* buffer, glm::quat& quatOutput); +int unpackOrientationQuatFromBytes(const unsigned char* buffer, glm::quat& quatOutput); // Ratios need the be highly accurate when less than 10, but not very accurate above 10, and they // are never greater than 1000 to 1, this allows us to encode each component in 16bits @@ -172,7 +172,7 @@ int unpackFloatFromByte(unsigned char* buffer, float& value, float scaleBy); int packFloatScalarToSignedTwoByteFixed(unsigned char* buffer, float scalar, int radix); int unpackFloatScalarFromSignedTwoByteFixed(int16_t* byteFixedPointer, float* destinationPointer, int radix); -// A convenience for sending vec3's as fixed-poimt floats +// A convenience for sending vec3's as fixed-point floats int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix); int unpackFloatVec3FromSignedTwoByteFixed(unsigned char* sourceBuffer, glm::vec3& destination, int radix); From bd0631838dfeb5f5e8086cfbab4fcc7b9ec3fee0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 11:19:04 -0800 Subject: [PATCH 07/20] example of setting particle model with JS --- examples/particleModelExample.js | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/particleModelExample.js diff --git a/examples/particleModelExample.js b/examples/particleModelExample.js new file mode 100644 index 0000000000..cc4bc517fb --- /dev/null +++ b/examples/particleModelExample.js @@ -0,0 +1,49 @@ +// +// particleModelExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates creating and editing a particle +// + +var count = 0; +var stopAfter = 100; + +var modelProperties = { + position: { x: 0, y: 1, z: 0 }, + velocity: { x: 0.5, y: 0, z: 0.5 }, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius : 0.1, + modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", +}; + +var ballProperties = { + position: { x: 0, y: 0.75, z: 0 }, + velocity: { x: 0.5, y: 0, z: 0.5 }, + gravity: { x: 0, y: 0, z: 0 }, + damping: 0, + radius : 0.1, + color: { red: 255, green: 0, blue: 0 }, +}; + +var modelParticleID = Particles.addParticle(modelProperties); +var ballParticleID = Particles.addParticle(ballProperties); + +function endAfterAWhile() { + // stop it... + if (count >= stopAfter) { + print("calling Agent.stop()"); + Agent.stop(); + } + + print("count =" + count); + count++; +} + + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(endAfterAWhile); + From dc258684033b230c675a2bf98c481feeb45d7779 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 11:26:55 -0800 Subject: [PATCH 08/20] cleanup --- interface/src/ParticleTreeRenderer.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/interface/src/ParticleTreeRenderer.cpp b/interface/src/ParticleTreeRenderer.cpp index e0229bddaa..11ba832d07 100644 --- a/interface/src/ParticleTreeRenderer.cpp +++ b/interface/src/ParticleTreeRenderer.cpp @@ -43,19 +43,6 @@ void ParticleTreeRenderer::render() { OctreeRenderer::render(); } -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/lotus.fbx")); -//_testModel->setURL(QUrl("http://www.fungibleinsight.com/faces/tie.fbx")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Angie1.fbx")); -//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/orb_model.fbx")); -//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/space_frigate_6.FBX")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/soccer_ball.fbx")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/top%20fbx.FBX")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/golfball_FBX2010.fbx")); -//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/Combat_tank_V01.FBX")); - -//_testModel->setURL(QUrl("http://public.highfidelity.io/meshes/orc.fbx")); -//_testModel->setURL(QUrl("http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX")); - Model* ParticleTreeRenderer::getModel(const QString& url) { Model* model = NULL; @@ -63,9 +50,8 @@ Model* ParticleTreeRenderer::getModel(const QString& url) { if (_particleModels.find(url) == _particleModels.end()) { model = new Model(); model->init(); - qDebug() << "calling model->setURL()"; model->setURL(QUrl(url)); - qDebug() << "after calling setURL()"; + _particleModels[url] = model; } else { model = _particleModels[url]; } From 64afd17d147d8279ffb552b05bf3b587c701b85c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 11:49:11 -0800 Subject: [PATCH 09/20] include particle radius in model scaling --- examples/particleModelExample.js | 10 ++++++---- interface/src/ParticleTreeRenderer.cpp | 14 +++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/particleModelExample.js b/examples/particleModelExample.js index cc4bc517fb..9f19069ee9 100644 --- a/examples/particleModelExample.js +++ b/examples/particleModelExample.js @@ -12,21 +12,23 @@ var count = 0; var stopAfter = 100; var modelProperties = { - position: { x: 0, y: 1, z: 0 }, + position: { x: 1, y: 1, z: 1 }, velocity: { x: 0.5, y: 0, z: 0.5 }, gravity: { x: 0, y: 0, z: 0 }, damping: 0, - radius : 0.1, + radius : 0.25, modelURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", + lifetime: 20 }; var ballProperties = { - position: { x: 0, y: 0.75, z: 0 }, + position: { x: 1, y: 0.5, z: 1 }, velocity: { x: 0.5, y: 0, z: 0.5 }, gravity: { x: 0, y: 0, z: 0 }, damping: 0, - radius : 0.1, + radius : 0.25, color: { red: 255, green: 0, blue: 0 }, + lifetime: 20 }; var modelParticleID = Particles.addParticle(modelProperties); diff --git a/interface/src/ParticleTreeRenderer.cpp b/interface/src/ParticleTreeRenderer.cpp index 11ba832d07..7c82f76aab 100644 --- a/interface/src/ParticleTreeRenderer.cpp +++ b/interface/src/ParticleTreeRenderer.cpp @@ -72,7 +72,7 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg // render particle aspoints glm::vec3 position = particle.getPosition() * (float)TREE_SCALE; glColor3ub(particle.getColor()[RED_INDEX],particle.getColor()[GREEN_INDEX],particle.getColor()[BLUE_INDEX]); - float sphereRadius = particle.getRadius() * (float)TREE_SCALE; + float radius = particle.getRadius() * (float)TREE_SCALE; bool drawAsModel = particle.hasModel(); @@ -90,24 +90,24 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg glm::vec3 translation(position.x, position.y, position.z); model->setTranslation(translation + translationAdjustment); - // glm::angleAxis(-90.0f, 1.0f, 0.0f, 0.0f) // set the rotation glm::quat rotation = particle.getModelRotation(); model->setRotation(rotation); // scale - const float MODEL_SCALE = 0.0006f; // need to figure out correct scale adjust + // TODO: need to figure out correct scale adjust, this was arbitrarily set to make a couple models work + const float MODEL_SCALE = 0.00575f; glm::vec3 scale(1.0f,1.0f,1.0f); - model->setScale(scale * MODEL_SCALE); + model->setScale(scale * MODEL_SCALE * radius * particle.getModelScale()); model->simulate(0.0f); - model->render(alpha); - //qDebug() << "called _testModel->render(alpha);"; + model->render(alpha); // TODO: should we allow particles to have alpha on their models? + glPopMatrix(); } else { glPushMatrix(); glTranslatef(position.x, position.y, position.z); - glutSolidSphere(sphereRadius, 15, 15); + glutSolidSphere(radius, 15, 15); glPopMatrix(); } } From 5addc35cd8a7f68e82229c5b2635e7aaa8a3575f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 14:58:08 -0800 Subject: [PATCH 10/20] hacking on controller events --- interface/src/Application.cpp | 17 +++++++++++++ interface/src/ControllerScriptingInterface.h | 25 +++++++++++++++++++ .../AbstractControllerScriptingInterface.h | 19 ++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 228a8bdd2f..fd365d67e8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -681,6 +681,9 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ } void Application::keyPressEvent(QKeyEvent* event) { + + _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts + if (activeWindow() == _window) { if (_chatEntryOn) { if (_chatEntry.keyPressEvent(event)) { @@ -1092,6 +1095,9 @@ void Application::keyPressEvent(QKeyEvent* event) { } void Application::keyReleaseEvent(QKeyEvent* event) { + + _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts + if (activeWindow() == _window) { if (_chatEntryOn) { _myAvatar.setKeyState(NO_KEY_DOWN); @@ -1154,6 +1160,8 @@ void Application::keyReleaseEvent(QKeyEvent* event) { } void Application::mouseMoveEvent(QMouseEvent* event) { + _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts + _lastMouseMove = usecTimestampNow(); if (_mouseHidden) { getGLWidget()->setCursor(Qt::ArrowCursor); @@ -1200,6 +1208,7 @@ const float HOVER_VOXEL_FREQUENCY = 7040.f; const float HOVER_VOXEL_DECAY = 0.999f; void Application::mousePressEvent(QMouseEvent* event) { + _controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts if (activeWindow() == _window) { if (event->button() == Qt::LeftButton) { _mouseX = event->x(); @@ -1267,6 +1276,7 @@ void Application::mousePressEvent(QMouseEvent* event) { } void Application::mouseReleaseEvent(QMouseEvent* event) { + _controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts if (activeWindow() == _window) { if (event->button() == Qt::LeftButton) { _mouseX = event->x(); @@ -1283,6 +1293,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { } void Application::touchUpdateEvent(QTouchEvent* event) { + _controllerScriptingInterface.emitTouchUpdateEvent(event); // send events to any registered scripts bool validTouch = false; if (activeWindow() == _window) { const QList& tPoints = event->touchPoints(); @@ -1307,12 +1318,15 @@ void Application::touchUpdateEvent(QTouchEvent* event) { } void Application::touchBeginEvent(QTouchEvent* event) { + _controllerScriptingInterface.emitTouchBeginEvent(event); // send events to any registered scripts + touchUpdateEvent(event); _lastTouchAvgX = _touchAvgX; _lastTouchAvgY = _touchAvgY; } void Application::touchEndEvent(QTouchEvent* event) { + _controllerScriptingInterface.emitTouchEndEvent(event); // send events to any registered scripts _touchDragStartedAvgX = _touchAvgX; _touchDragStartedAvgY = _touchAvgY; _isTouchPressed = false; @@ -1320,6 +1334,9 @@ void Application::touchEndEvent(QTouchEvent* event) { const bool USE_MOUSEWHEEL = false; void Application::wheelEvent(QWheelEvent* event) { + + _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts + // Wheel Events disabled for now because they are also activated by touch look pitch up/down. if (USE_MOUSEWHEEL && (activeWindow() == _window)) { if (!Menu::getInstance()->isVoxelModeActionChecked()) { diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h index 69daefa3fb..ca14729151 100644 --- a/interface/src/ControllerScriptingInterface.h +++ b/interface/src/ControllerScriptingInterface.h @@ -18,6 +18,25 @@ class PalmData; class ControllerScriptingInterface : public AbstractControllerScriptingInterface { Q_OBJECT +public: + void emitKeyPressEvent(QKeyEvent* x) { + KeyEvent event(x); + emit keyPressEvent(event); + } + + /** + void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(*event); } + + void emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(*event); } + void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(*event); } + void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(*event); } + + void emitTouchBeginEvent(QTouchEvent* event) { emit touchBeginEvent(event); } + void emitTouchEndEvent(QTouchEvent* event) { emit touchEndEvent(event); } + void emitTouchUpdateEvent(QTouchEvent* event) { emit touchUpdateEvent(event); } + void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(event); } + **/ + public slots: virtual bool isPrimaryButtonPressed() const; virtual glm::vec2 getPrimaryJoystickPosition() const; @@ -36,6 +55,12 @@ public slots: virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const; virtual glm::vec3 getSpatialControlNormal(int controlIndex) const; +// The following signals are defined by AbstractControllerScriptingInterface +// +// signals: +// void keyPressEvent(); +// void keyPressEvent(); + private: const PalmData* getPrimaryPalm() const; const PalmData* getPalm(int palmIndex) const; diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h index 5c791af0a4..b3f482c74e 100644 --- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h +++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h @@ -10,8 +10,12 @@ #define __hifi__AbstractControllerScriptingInterface__ #include + #include +#include "EventTypes.h" + + /// handles scripting of input controller commands from JS class AbstractControllerScriptingInterface : public QObject { Q_OBJECT @@ -33,6 +37,21 @@ public slots: virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0; virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0; virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0; + +signals: + void keyPressEvent(const KeyEvent& event); + void keyReleaseEvent(const KeyEvent& event); + + void mouseMoveEvent(const MouseEvent& event); + void mousePressEvent(const MouseEvent& event); + void mouseReleaseEvent(const MouseEvent& event); + + void touchBeginEvent(const TouchEvent& event); + void touchEndEvent(const TouchEvent& event); + void touchUpdateEvent(const TouchEvent& event); + + void wheelEvent(const WheelEvent& event); + }; #endif /* defined(__hifi__AbstractControllerScriptingInterface__) */ From 7882d50c5fc81aa17860263e03e9724a89f294a5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 14:58:20 -0800 Subject: [PATCH 11/20] hacking on controller events --- libraries/script-engine/src/EventTypes.cpp | 86 ++++++++++++++++++++++ libraries/script-engine/src/EventTypes.h | 62 ++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 libraries/script-engine/src/EventTypes.cpp create mode 100644 libraries/script-engine/src/EventTypes.h diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp new file mode 100644 index 0000000000..e11fa72495 --- /dev/null +++ b/libraries/script-engine/src/EventTypes.cpp @@ -0,0 +1,86 @@ +// +// EventTypes.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// Used to register meta-types with Qt for very various event types so that they can be exposed to our +// scripting engine + +#include "EventTypes.h" + +void registerEventTypes(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, keyEventToScriptValue, keyEventFromScriptValue); + qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue); + qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue); + qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue); +} + +QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) { + QScriptValue obj = engine->newObject(); + /* + obj.setProperty("key", event.key()); + obj.setProperty("isAutoRepeat", event.isAutoRepeat()); + + bool isShifted = event.modifiers().testFlag(Qt::ShiftModifier); + bool isMeta = event.modifiers().testFlag(Qt::ControlModifier); + + obj.setProperty("isShifted", isShifted); + obj.setProperty("isMeta", isMeta); + */ + + return obj; +} + +void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) { + // nothing for now... +} + +QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", event.x()); + obj.setProperty("y", event.y()); + return obj; +} + +void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event) { + // nothing for now... +} + +QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event) { + QScriptValue obj = engine->newObject(); + + // convert the touch points into an average + const QList& tPoints = event.touchPoints(); + float touchAvgX = 0.0f; + float touchAvgY = 0.0f; + int numTouches = tPoints.count(); + if (numTouches > 1) { + for (int i = 0; i < numTouches; ++i) { + touchAvgX += tPoints[i].pos().x(); + touchAvgY += tPoints[i].pos().y(); + } + touchAvgX /= (float)(numTouches); + touchAvgY /= (float)(numTouches); + } + + obj.setProperty("averageX", touchAvgX); + obj.setProperty("averageY", touchAvgY); + return obj; +} + +void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event) { + // nothing for now... +} + +QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", event.x()); + obj.setProperty("y", event.y()); + return obj; +} + +void wheelEventFromScriptValue(const QScriptValue &object, WheelEvent& event) { + // nothing for now... +} diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h new file mode 100644 index 0000000000..5ed014f09a --- /dev/null +++ b/libraries/script-engine/src/EventTypes.h @@ -0,0 +1,62 @@ +// +// EventTypes.h +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi_EventTypes_h__ +#define __hifi_EventTypes_h__ + +#include + +#include + +#include +#include +#include +#include + +class KeyEvent { +public: + KeyEvent() { }; + KeyEvent(QKeyEvent* other) { }; +}; + +class MouseEvent { +public: + MouseEvent() { }; +}; + +class TouchEvent { +public: + TouchEvent() { }; +}; + +class WheelEvent { +public: + WheelEvent() { }; +}; + + +Q_DECLARE_METATYPE(KeyEvent) +Q_DECLARE_METATYPE(MouseEvent) +Q_DECLARE_METATYPE(TouchEvent) +Q_DECLARE_METATYPE(WheelEvent) + +void registerEventTypes(QScriptEngine* engine); + +QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event); +void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event); + +QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event); +void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event); + +QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event); +void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event); + +QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event); +void wheelEventFromScriptValue(const QScriptValue &object, WheelEvent& event); + +#endif // __hifi_EventTypes_h__ From febb306a629fb7484eb2c9b7a18c74feb56b4032 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 19:46:59 -0800 Subject: [PATCH 12/20] cleaned up some registration of global objects --- libraries/script-engine/src/ScriptEngine.cpp | 30 +++++++------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 1bf8dbf696..6ca59137be 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -116,19 +116,12 @@ void ScriptEngine::init() { // register meta-type for glm::vec3 conversions registerMetaTypes(&_engine); + + registerEventTypes(&_engine); qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue); qScriptRegisterSequenceMetaType >(&_engine); - QScriptValue agentValue = _engine.newQObject(this); - _engine.globalObject().setProperty("Agent", agentValue); - - QScriptValue voxelScripterValue = _engine.newQObject(&_voxelsScriptingInterface); - _engine.globalObject().setProperty("Voxels", voxelScripterValue); - - QScriptValue particleScripterValue = _engine.newQObject(&_particlesScriptingInterface); - _engine.globalObject().setProperty("Particles", particleScripterValue); - QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor); QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue); _engine.globalObject().setProperty("Sound", soundMetaObject); @@ -136,15 +129,12 @@ void ScriptEngine::init() { QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject(); _engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue); - QScriptValue audioScriptingInterfaceValue = _engine.newQObject(&_audioScriptingInterface); - _engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue); - + registerGlobalObject("Agent", this); + registerGlobalObject("Audio", &_audioScriptingInterface); + registerGlobalObject("Controller", _controllerScriptingInterface); registerGlobalObject("Data", &_dataServerScriptingInterface); - - if (_controllerScriptingInterface) { - QScriptValue controllerScripterValue = _engine.newQObject(_controllerScriptingInterface); - _engine.globalObject().setProperty("Controller", controllerScripterValue); - } + registerGlobalObject("Particles", &_particlesScriptingInterface); + registerGlobalObject("Voxels", &_voxelsScriptingInterface); QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE)); _engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); @@ -157,8 +147,10 @@ void ScriptEngine::init() { } void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { - QScriptValue value = _engine.newQObject(object); - _engine.globalObject().setProperty(name, value); + if (object) { + QScriptValue value = _engine.newQObject(object); + _engine.globalObject().setProperty(name, value); + } } void ScriptEngine::evaluate() { From 305e69def64d3648ad15f89aa68c1ec0990e8ca4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 19:47:45 -0800 Subject: [PATCH 13/20] really get controller key, mouse, etc scripting working --- interface/src/Application.cpp | 57 ++++++++++ .../src/ControllerScriptingInterface.cpp | 35 ++++++ interface/src/ControllerScriptingInterface.h | 49 ++++++--- libraries/particles/src/ParticleTree.cpp | 14 +-- .../AbstractControllerScriptingInterface.h | 13 +++ libraries/script-engine/src/EventTypes.cpp | 103 +++++++++++------- libraries/script-engine/src/EventTypes.h | 27 ++++- 7 files changed, 226 insertions(+), 72 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fd365d67e8..1a673da70c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -683,6 +683,11 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ void Application::keyPressEvent(QKeyEvent* event) { _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isKeyCaptured(event)) { + return; + } if (activeWindow() == _window) { if (_chatEntryOn) { @@ -1098,6 +1103,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) { _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isKeyCaptured(event)) { + return; + } + + if (activeWindow() == _window) { if (_chatEntryOn) { _myAvatar.setKeyState(NO_KEY_DOWN); @@ -1162,6 +1173,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) { void Application::mouseMoveEvent(QMouseEvent* event) { _controllerScriptingInterface.emitMouseMoveEvent(event); // send events to any registered scripts + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isMouseCaptured()) { + return; + } + + _lastMouseMove = usecTimestampNow(); if (_mouseHidden) { getGLWidget()->setCursor(Qt::ArrowCursor); @@ -1209,6 +1226,13 @@ const float HOVER_VOXEL_DECAY = 0.999f; void Application::mousePressEvent(QMouseEvent* event) { _controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isMouseCaptured()) { + return; + } + + if (activeWindow() == _window) { if (event->button() == Qt::LeftButton) { _mouseX = event->x(); @@ -1277,6 +1301,12 @@ void Application::mousePressEvent(QMouseEvent* event) { void Application::mouseReleaseEvent(QMouseEvent* event) { _controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isMouseCaptured()) { + return; + } + if (activeWindow() == _window) { if (event->button() == Qt::LeftButton) { _mouseX = event->x(); @@ -1294,6 +1324,12 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { void Application::touchUpdateEvent(QTouchEvent* event) { _controllerScriptingInterface.emitTouchUpdateEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isTouchCaptured()) { + return; + } + bool validTouch = false; if (activeWindow() == _window) { const QList& tPoints = event->touchPoints(); @@ -1321,21 +1357,42 @@ void Application::touchBeginEvent(QTouchEvent* event) { _controllerScriptingInterface.emitTouchBeginEvent(event); // send events to any registered scripts touchUpdateEvent(event); + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isTouchCaptured()) { + return; + } + + // put any application specific touch behavior below here.. _lastTouchAvgX = _touchAvgX; _lastTouchAvgY = _touchAvgY; + } void Application::touchEndEvent(QTouchEvent* event) { _controllerScriptingInterface.emitTouchEndEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isTouchCaptured()) { + return; + } + + // put any application specific touch behavior below here.. _touchDragStartedAvgX = _touchAvgX; _touchDragStartedAvgY = _touchAvgY; _isTouchPressed = false; + } const bool USE_MOUSEWHEEL = false; void Application::wheelEvent(QWheelEvent* event) { _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts + + // if one of our scripts have asked to capture this event, then stop processing it + if (_controllerScriptingInterface.isWheelCaptured()) { + return; + } // Wheel Events disabled for now because they are also activated by touch look pitch up/down. if (USE_MOUSEWHEEL && (activeWindow() == _window)) { diff --git a/interface/src/ControllerScriptingInterface.cpp b/interface/src/ControllerScriptingInterface.cpp index fd27eb2428..491c38a9de 100644 --- a/interface/src/ControllerScriptingInterface.cpp +++ b/interface/src/ControllerScriptingInterface.cpp @@ -179,5 +179,40 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex return glm::vec3(0); // bad index } +bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { +qDebug() << "ControllerScriptingInterface::isKeyCaptured() event=" << event; + return isKeyCaptured(KeyEvent(*event)); +} +bool ControllerScriptingInterface::isKeyCaptured(const KeyEvent& event) const { + +qDebug() << "ControllerScriptingInterface::isKeyCaptured() event.key=" << event.key; + + // if we've captured some combination of this key it will be in the map + if (_capturedKeys.contains(event.key, event)) { +qDebug() << "ControllerScriptingInterface::isKeyCaptured() event.key=" << event.key << " returning TRUE"; + return true; + } + return false; +} + +void ControllerScriptingInterface::captureKeyEvents(const KeyEvent& event) { + // if it's valid + if (event.isValid) { + // and not already captured + if (!isKeyCaptured(event)) { + // then add this KeyEvent record to the captured combos for this key + _capturedKeys.insert(event.key, event); + } + } +} + +void ControllerScriptingInterface::releaseKeyEvents(const KeyEvent& event) { + if (event.isValid) { + // and not already captured + if (isKeyCaptured(event)) { + _capturedKeys.remove(event.key, event); + } + } +} diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h index ca14729151..6b09d34909 100644 --- a/interface/src/ControllerScriptingInterface.h +++ b/interface/src/ControllerScriptingInterface.h @@ -1,3 +1,4 @@ + // // ControllerScriptingInterface.h // hifi @@ -19,23 +20,24 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface Q_OBJECT public: - void emitKeyPressEvent(QKeyEvent* x) { - KeyEvent event(x); - emit keyPressEvent(event); - } - - /** - void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(*event); } + void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } + void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } - void emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(*event); } - void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(*event); } - void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(*event); } + void emitMouseMoveEvent(QMouseEvent* event) { emit mouseMoveEvent(MouseEvent(*event)); } + void emitMousePressEvent(QMouseEvent* event) { emit mousePressEvent(MouseEvent(*event)); } + void emitMouseReleaseEvent(QMouseEvent* event) { emit mouseReleaseEvent(MouseEvent(*event)); } + + void emitTouchBeginEvent(QTouchEvent* event) { emit touchBeginEvent(*event); } + void emitTouchEndEvent(QTouchEvent* event) { emit touchEndEvent(*event); } + void emitTouchUpdateEvent(QTouchEvent* event) { emit touchUpdateEvent(*event); } + void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); } + + bool isKeyCaptured(QKeyEvent* event) const; + bool isKeyCaptured(const KeyEvent& event) const; + bool isMouseCaptured() const { return _mouseCaptured; } + bool isTouchCaptured() const { return _touchCaptured; } + bool isWheelCaptured() const { return _wheelCaptured; } - void emitTouchBeginEvent(QTouchEvent* event) { emit touchBeginEvent(event); } - void emitTouchEndEvent(QTouchEvent* event) { emit touchEndEvent(event); } - void emitTouchUpdateEvent(QTouchEvent* event) { emit touchUpdateEvent(event); } - void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(event); } - **/ public slots: virtual bool isPrimaryButtonPressed() const; @@ -55,6 +57,18 @@ public slots: virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const; virtual glm::vec3 getSpatialControlNormal(int controlIndex) const; + virtual void captureKeyEvents(const KeyEvent& event); + virtual void releaseKeyEvents(const KeyEvent& event); + + virtual void captureMouseEvents() { _mouseCaptured = true; } + virtual void releaseMouseEvents() { _mouseCaptured = false; } + + virtual void captureTouchEvents() { _touchCaptured = true; } + virtual void releaseTouchEvents() { _touchCaptured = false; } + + virtual void captureWheelEvents() { _wheelCaptured = true; } + virtual void releaseWheelEvents() { _wheelCaptured = false; } + // The following signals are defined by AbstractControllerScriptingInterface // // signals: @@ -66,6 +80,11 @@ private: const PalmData* getPalm(int palmIndex) const; int getNumberOfActivePalms() const; const PalmData* getActivePalm(int palmIndex) const; + + bool _mouseCaptured; + bool _touchCaptured; + bool _wheelCaptured; + QMultiMap _capturedKeys; }; const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index 7f8f168e5d..6cb46b3340 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -498,11 +498,9 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha while (iterator != _recentlyDeletedParticleIDs.constEnd()) { QList values = _recentlyDeletedParticleIDs.values(iterator.key()); for (int valueItem = 0; valueItem < values.size(); ++valueItem) { - //qDebug() << "considering... " << iterator.key() << ": " << values.at(valueItem); // if the timestamp is more recent then out last sent time, include it if (iterator.key() > sinceTime) { - //qDebug() << "including... " << iterator.key() << ": " << values.at(valueItem); uint32_t particleID = values.at(valueItem); memcpy(copyAt, &particleID, sizeof(particleID)); copyAt += sizeof(particleID); @@ -540,16 +538,14 @@ bool ParticleTree::encodeParticlesDeletedSince(uint64_t& sinceTime, unsigned cha // called by the server when it knows all nodes have been sent deleted packets void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) { - //qDebug() << "forgetParticlesDeletedBefore()"; QSet keysToRemove; _recentlyDeletedParticlesLock.lockForWrite(); QMultiMap::iterator iterator = _recentlyDeletedParticleIDs.begin(); + // First find all the keys in the map that are older and need to be deleted while (iterator != _recentlyDeletedParticleIDs.end()) { - //qDebug() << "considering... time/key:" << iterator.key(); if (iterator.key() <= sinceTime) { - //qDebug() << "YES older... time/key:" << iterator.key(); keysToRemove << iterator.key(); } ++iterator; @@ -557,18 +553,15 @@ void ParticleTree::forgetParticlesDeletedBefore(uint64_t sinceTime) { // Now run through the keysToRemove and remove them foreach (uint64_t value, keysToRemove) { - //qDebug() << "removing the key, _recentlyDeletedParticleIDs.remove(value); time/key:" << value; _recentlyDeletedParticleIDs.remove(value); } _recentlyDeletedParticlesLock.unlock(); - //qDebug() << "DONE forgetParticlesDeletedBefore()"; } void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode) { - //qDebug() << "ParticleTree::processEraseMessage()..."; const unsigned char* packetData = (const unsigned char*)dataByteArray.constData(); const unsigned char* dataAt = packetData; @@ -583,14 +576,11 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi dataAt += sizeof(numberOfIds); processedBytes += sizeof(numberOfIds); - //qDebug() << "got erase message for numberOfIds:" << numberOfIds; - if (numberOfIds > 0) { FindAndDeleteParticlesArgs args; for (size_t i = 0; i < numberOfIds; i++) { if (processedBytes + sizeof(uint32_t) > packetLength) { - //qDebug() << "bailing?? processedBytes:" << processedBytes << " packetLength:" << packetLength; break; // bail to prevent buffer overflow } @@ -599,12 +589,10 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi dataAt += sizeof(particleID); processedBytes += sizeof(particleID); - //qDebug() << "got erase message for particleID:" << particleID; args._idsToDelete.push_back(particleID); } // calling recurse to actually delete the particles - //qDebug() << "calling recurse to actually delete the particles"; recurseTreeWithOperation(findAndDeleteOperation, &args); } } diff --git a/libraries/script-engine/src/AbstractControllerScriptingInterface.h b/libraries/script-engine/src/AbstractControllerScriptingInterface.h index b3f482c74e..4fad5f6edc 100644 --- a/libraries/script-engine/src/AbstractControllerScriptingInterface.h +++ b/libraries/script-engine/src/AbstractControllerScriptingInterface.h @@ -38,6 +38,19 @@ public slots: virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0; virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0; + virtual void captureKeyEvents(const KeyEvent& event) = 0; + virtual void releaseKeyEvents(const KeyEvent& event) = 0; + + virtual void captureMouseEvents() = 0; + virtual void releaseMouseEvents() = 0; + + virtual void captureTouchEvents() = 0; + virtual void releaseTouchEvents() = 0; + + virtual void captureWheelEvents() = 0; + virtual void releaseWheelEvents() = 0; + + signals: void keyPressEvent(const KeyEvent& event); void keyReleaseEvent(const KeyEvent& event); diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp index e11fa72495..5c4ab7f2a7 100644 --- a/libraries/script-engine/src/EventTypes.cpp +++ b/libraries/script-engine/src/EventTypes.cpp @@ -10,47 +10,28 @@ #include "EventTypes.h" -void registerEventTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, keyEventToScriptValue, keyEventFromScriptValue); - qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue); - qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue); - qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue); + +KeyEvent::KeyEvent() { + key = 0; + isShifted = false; + isMeta = false; + isValid = false; } -QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) { - QScriptValue obj = engine->newObject(); - /* - obj.setProperty("key", event.key()); - obj.setProperty("isAutoRepeat", event.isAutoRepeat()); - bool isShifted = event.modifiers().testFlag(Qt::ShiftModifier); - bool isMeta = event.modifiers().testFlag(Qt::ControlModifier); - - obj.setProperty("isShifted", isShifted); - obj.setProperty("isMeta", isMeta); - */ - - return obj; +KeyEvent::KeyEvent(const QKeyEvent& event) { + key = event.key(); + isShifted = event.modifiers().testFlag(Qt::ShiftModifier); + isMeta = event.modifiers().testFlag(Qt::ControlModifier); + isValid = true; } -void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) { - // nothing for now... +MouseEvent::MouseEvent(const QMouseEvent& event) { + x = event.x(); + y = event.y(); } -QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", event.x()); - obj.setProperty("y", event.y()); - return obj; -} - -void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event) { - // nothing for now... -} - -QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event) { - QScriptValue obj = engine->newObject(); - +TouchEvent::TouchEvent(const QTouchEvent& event) { // convert the touch points into an average const QList& tPoints = event.touchPoints(); float touchAvgX = 0.0f; @@ -64,9 +45,53 @@ QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& ev touchAvgX /= (float)(numTouches); touchAvgY /= (float)(numTouches); } - - obj.setProperty("averageX", touchAvgX); - obj.setProperty("averageY", touchAvgY); + x = touchAvgX; + y = touchAvgY; +} + +WheelEvent::WheelEvent(const QWheelEvent& event) { + x = event.x(); + y = event.y(); +} + + +void registerEventTypes(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, keyEventToScriptValue, keyEventFromScriptValue); + qScriptRegisterMetaType(engine, mouseEventToScriptValue, mouseEventFromScriptValue); + qScriptRegisterMetaType(engine, touchEventToScriptValue, touchEventFromScriptValue); + qScriptRegisterMetaType(engine, wheelEventToScriptValue, wheelEventFromScriptValue); +} + +QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) { + QScriptValue obj = engine->newObject(); + obj.setProperty("key", event.key); + obj.setProperty("isShifted", event.isShifted); + obj.setProperty("isMeta", event.isMeta); + return obj; +} + +void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) { + event.key = object.property("key").toVariant().toInt(); + event.isShifted = object.property("isShifted").toVariant().toBool(); + event.isMeta = object.property("isMeta").toVariant().toBool(); + event.isValid = object.property("key").isValid(); +} + +QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", event.x); + obj.setProperty("y", event.y); + return obj; +} + +void mouseEventFromScriptValue(const QScriptValue &object, MouseEvent& event) { + // nothing for now... +} + +QScriptValue touchEventToScriptValue(QScriptEngine* engine, const TouchEvent& event) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", event.x); + obj.setProperty("y", event.y); return obj; } @@ -76,8 +101,8 @@ void touchEventFromScriptValue(const QScriptValue &object, TouchEvent& event) { QScriptValue wheelEventToScriptValue(QScriptEngine* engine, const WheelEvent& event) { QScriptValue obj = engine->newObject(); - obj.setProperty("x", event.x()); - obj.setProperty("y", event.y()); + obj.setProperty("x", event.x); + obj.setProperty("y", event.y); return obj; } diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h index 5ed014f09a..c3764b2619 100644 --- a/libraries/script-engine/src/EventTypes.h +++ b/libraries/script-engine/src/EventTypes.h @@ -18,25 +18,42 @@ #include #include + class KeyEvent { public: - KeyEvent() { }; - KeyEvent(QKeyEvent* other) { }; + KeyEvent(); + KeyEvent(const QKeyEvent& event); + inline bool operator==(const KeyEvent& other) const { + return other.key == key && other.isShifted == isShifted && other.isMeta == isMeta; } + int key; + bool isShifted; + bool isMeta; + bool isValid; }; + class MouseEvent { public: - MouseEvent() { }; + MouseEvent() : x(0), y(0) { }; + MouseEvent(const QMouseEvent& event); + int x; + int y; }; class TouchEvent { public: - TouchEvent() { }; + TouchEvent() : x(0), y(0) { }; + TouchEvent(const QTouchEvent& event); + float x; + float y; }; class WheelEvent { public: - WheelEvent() { }; + WheelEvent() : x(0), y(0) { }; + WheelEvent(const QWheelEvent& event); + int x; + int y; }; From a73733e57bfcf287535fbc92c9ac8f38995a5596 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 19:48:06 -0800 Subject: [PATCH 14/20] controller scripting example --- examples/controllerExample.js | 105 ++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 examples/controllerExample.js diff --git a/examples/controllerExample.js b/examples/controllerExample.js new file mode 100644 index 0000000000..c1b33b24a5 --- /dev/null +++ b/examples/controllerExample.js @@ -0,0 +1,105 @@ +// +// controllerExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Controller class +// +// + +// initialize our triggers +var triggerPulled = new Array(); +var numberOfTriggers = Controller.getNumberOfTriggers(); +for (t = 0; t < numberOfTriggers; t++) { + triggerPulled[t] = false; +} + +function checkController() { + var numberOfTriggers = Controller.getNumberOfTriggers(); + var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); + var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; + + // this is expected for hydras + if (numberOfTriggers == 2 && controllersPerTrigger == 2) { + for (var t = 0; t < numberOfTriggers; t++) { + var triggerValue = Controller.getTriggerValue(t); + + if (triggerPulled[t]) { + // must release to at least 0.1 + if (triggerValue < 0.1) { + triggerPulled[t] = false; // unpulled + } + } else { + // must pull to at least 0.9 + if (triggerValue > 0.9) { + triggerPulled[t] = true; // pulled + triggerToggled = true; + } + } + + if (triggerToggled) { + print("a trigger was toggled"); + } + } + } +} + +function keyPressEvent(event) { + print("keyPressEvent event.key=" + event.key); + if (event.key == "A".charCodeAt(0)) { + print("the A key was pressed"); + } + if (event.key == " ".charCodeAt(0)) { + print("the key was pressed"); + } +} + +function mouseMoveEvent(event) { + print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y); +} + +function touchBeginEvent(event) { + print("touchBeginEvent event.x,y=" + event.x + ", " + event.y); +} + +function touchUpdateEvent(event) { + print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y); +} + +function touchEndEvent(event) { + print("touchEndEvent event.x,y=" + event.x + ", " + event.y); +} + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(checkController); + +// Map keyPress and mouse move events to our callbacks +Controller.keyPressEvent.connect(keyPressEvent); +var AKeyEvent = { + key: "A".charCodeAt(0), + isShifted: false, + isMeta: false +}; + +// prevent the A key from going through to the application +Controller.captureKeyEvents(AKeyEvent); + + +Controller.mouseMoveEvent.connect(mouseMoveEvent); + +// Map touch events to our callbacks +Controller.touchBeginEvent.connect(touchBeginEvent); +Controller.touchUpdateEvent.connect(touchUpdateEvent); +Controller.touchEndEvent.connect(touchEndEvent); + +// disable the standard application for touch events +Controller.captureTouchEvents(); + +function scriptEnding() { + // re-enabled the standard application for touch events + Controller.releaseTouchEvents(); +} + +Agent.scriptEnding.connect(scriptEnding); From bcc7cfb15ec6e21abaf1fee507b4dd46934a2e5f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 20:38:47 -0800 Subject: [PATCH 15/20] removed some debug --- interface/src/ControllerScriptingInterface.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/interface/src/ControllerScriptingInterface.cpp b/interface/src/ControllerScriptingInterface.cpp index 491c38a9de..799a57b951 100644 --- a/interface/src/ControllerScriptingInterface.cpp +++ b/interface/src/ControllerScriptingInterface.cpp @@ -180,17 +180,12 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex } bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { -qDebug() << "ControllerScriptingInterface::isKeyCaptured() event=" << event; return isKeyCaptured(KeyEvent(*event)); } bool ControllerScriptingInterface::isKeyCaptured(const KeyEvent& event) const { - -qDebug() << "ControllerScriptingInterface::isKeyCaptured() event.key=" << event.key; - // if we've captured some combination of this key it will be in the map if (_capturedKeys.contains(event.key, event)) { -qDebug() << "ControllerScriptingInterface::isKeyCaptured() event.key=" << event.key << " returning TRUE"; return true; } return false; From 434756a40f19528b40386c056658f98137b1ef41 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Jan 2014 20:40:00 -0800 Subject: [PATCH 16/20] removed some dead comments --- interface/src/ControllerScriptingInterface.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h index 6b09d34909..b6d5c81a05 100644 --- a/interface/src/ControllerScriptingInterface.h +++ b/interface/src/ControllerScriptingInterface.h @@ -69,12 +69,6 @@ public slots: virtual void captureWheelEvents() { _wheelCaptured = true; } virtual void releaseWheelEvents() { _wheelCaptured = false; } -// The following signals are defined by AbstractControllerScriptingInterface -// -// signals: -// void keyPressEvent(); -// void keyPressEvent(); - private: const PalmData* getPrimaryPalm() const; const PalmData* getPalm(int palmIndex) const; From 98cdc656c44f01286af1011fc3bb3aae9d1e0800 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 29 Jan 2014 00:56:08 -0800 Subject: [PATCH 17/20] more scripting support, including adding a Quat helper class that makes JS quat math easier --- interface/src/Util.cpp | 28 ------------------- interface/src/Util.h | 2 -- interface/src/world.h | 3 +- libraries/avatars/src/AvatarData.cpp | 7 +++++ libraries/avatars/src/AvatarData.h | 4 +++ libraries/script-engine/src/Quat.cpp | 20 ++++++++++++++ libraries/script-engine/src/Quat.h | 29 ++++++++++++++++++++ libraries/script-engine/src/ScriptEngine.cpp | 2 ++ libraries/script-engine/src/ScriptEngine.h | 2 ++ libraries/shared/src/SharedUtil.cpp | 28 +++++++++++++++++++ libraries/shared/src/SharedUtil.h | 6 ++++ 11 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 libraries/script-engine/src/Quat.cpp create mode 100644 libraries/script-engine/src/Quat.h diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index a2e518b2d7..ef7e049d75 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -105,34 +105,6 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) { return glm::angleAxis(angle, axis); } -// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's -// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde, -// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) -glm::vec3 safeEulerAngles(const glm::quat& q) { - float sy = 2.0f * (q.y * q.w - q.x * q.z); - if (sy < 1.0f - EPSILON) { - if (sy > -1.0f + EPSILON) { - return glm::degrees(glm::vec3( - atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)), - asinf(sy), - atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)))); - - } else { - // not a unique solution; x + z = atan2(-m21, m11) - return glm::degrees(glm::vec3( - 0.0f, - PIf * -0.5f, - atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); - } - } else { - // not a unique solution; x - z = atan2(-m21, m11) - return glm::degrees(glm::vec3( - 0.0f, - PIf * 0.5f, - -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); - } -} - // Safe version of glm::mix; based on the code in Nick Bobick's article, // http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, // https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) diff --git a/interface/src/Util.h b/interface/src/Util.h index 2a812120f0..09d1fa0484 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -54,8 +54,6 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2); glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2); -glm::vec3 safeEulerAngles(const glm::quat& q); - glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); glm::vec3 extractTranslation(const glm::mat4& matrix); diff --git a/interface/src/world.h b/interface/src/world.h index a226dc228e..8d3bd7322e 100644 --- a/interface/src/world.h +++ b/interface/src/world.h @@ -9,8 +9,9 @@ #ifndef __interface__world__ #define __interface__world__ - +#ifndef PIf #define PIf 3.14159265f +#endif const float GRAVITY_EARTH = 9.80665f; const float EDGE_SIZE_GROUND_PLANE = 20.f; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 8f548869ec..44f71c7aec 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -276,3 +276,10 @@ void AvatarData::setClampedTargetScale(float targetScale) { _targetScale = targetScale; qDebug() << "Changed scale to " << _targetScale; } + +void AvatarData::setOrientation(const glm::quat& orientation) { + glm::vec3 eulerAngles = safeEulerAngles(orientation); + _bodyPitch = eulerAngles.x; + _bodyYaw = eulerAngles.y; + _bodyRoll = eulerAngles.z; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fc9bad7f02..7300da041e 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -69,6 +69,9 @@ class AvatarData : public NodeData { Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage) + + Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) + public: AvatarData(); ~AvatarData(); @@ -91,6 +94,7 @@ public: void setBodyRoll(float bodyRoll) { _bodyRoll = bodyRoll; } glm::quat getOrientation() const { return glm::quat(glm::radians(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll))); } + void setOrientation(const glm::quat& orientation); // Scale float getTargetScale() const { return _targetScale; } diff --git a/libraries/script-engine/src/Quat.cpp b/libraries/script-engine/src/Quat.cpp new file mode 100644 index 0000000000..12900c29c7 --- /dev/null +++ b/libraries/script-engine/src/Quat.cpp @@ -0,0 +1,20 @@ +// +// Quat.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 1/29/14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// Scriptable Quaternion class library. +// +// + +#include "Quat.h" + +glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) { + return q1 * q2; +} + +glm::quat Quat::fromVec3(const glm::vec3& vec3) { + return glm::quat(vec3); +} diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h new file mode 100644 index 0000000000..6bcd121808 --- /dev/null +++ b/libraries/script-engine/src/Quat.h @@ -0,0 +1,29 @@ +// +// Quat.h +// hifi +// +// Created by Brad Hefta-Gaub on 1/29/14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// Scriptable Quaternion class library. +// +// + +#ifndef __hifi__Quat__ +#define __hifi__Quat__ + +#include +#include + +/// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API +class Quat : public QObject { + Q_OBJECT + +public slots: + glm::quat multiply(const glm::quat& q1, const glm::quat& q2); + glm::quat fromVec3(const glm::vec3& vec3); +}; + + + +#endif /* defined(__hifi__Quat__) */ diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 6ca59137be..28cb49f6f7 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -134,6 +134,8 @@ void ScriptEngine::init() { registerGlobalObject("Controller", _controllerScriptingInterface); registerGlobalObject("Data", &_dataServerScriptingInterface); registerGlobalObject("Particles", &_particlesScriptingInterface); + registerGlobalObject("Quat", &_quatLibrary); + registerGlobalObject("Voxels", &_voxelsScriptingInterface); QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE)); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index b36e2425fe..ef55e14109 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -25,6 +25,7 @@ class ParticlesScriptingInterface; #include "AbstractControllerScriptingInterface.h" #include "DataServerScriptingInterface.h" +#include "Quat.h" const QString NO_SCRIPT(""); @@ -94,6 +95,7 @@ private: QString _fileNameString; AbstractMenuInterface* _menu; static int _scriptNumber; + Quat _quatLibrary; }; #endif /* defined(__hifi__ScriptEngine__) */ diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 00e11e2b19..54a4291c25 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -715,3 +715,31 @@ void debug::checkDeadBeef(void* memoryVoid, int size) { assert(memcmp(memoryAt, DEADBEEF, std::min(size, DEADBEEF_SIZE)) != 0); } +// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's +// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde, +// https://github.com/threerings/clyde/blob/master/src/main/java/com/threerings/math/Quaternion.java) +glm::vec3 safeEulerAngles(const glm::quat& q) { + float sy = 2.0f * (q.y * q.w - q.x * q.z); + if (sy < 1.0f - EPSILON) { + if (sy > -1.0f + EPSILON) { + return glm::degrees(glm::vec3( + atan2f(q.y * q.z + q.x * q.w, 0.5f - (q.x * q.x + q.y * q.y)), + asinf(sy), + atan2f(q.x * q.y + q.z * q.w, 0.5f - (q.y * q.y + q.z * q.z)))); + + } else { + // not a unique solution; x + z = atan2(-m21, m11) + return glm::degrees(glm::vec3( + 0.0f, + PIf * -0.5f, + atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); + } + } else { + // not a unique solution; x - z = atan2(-m21, m11) + return glm::degrees(glm::vec3( + 0.0f, + PIf * 0.5f, + -atan2f(q.x * q.w - q.y * q.z, 0.5f - (q.x * q.x + q.z * q.z)))); + } +} + diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 174742fcf3..399bf40204 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -176,4 +176,10 @@ int unpackFloatScalarFromSignedTwoByteFixed(int16_t* byteFixedPointer, float* de int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix); int unpackFloatVec3FromSignedTwoByteFixed(unsigned char* sourceBuffer, glm::vec3& destination, int radix); +#ifndef PIf +#define PIf 3.14159265f +#endif + +glm::vec3 safeEulerAngles(const glm::quat& q); + #endif /* defined(__hifi__SharedUtil__) */ From ecd2477007c9b5c6d8cc5f41d6b7784ca6a4e209 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 29 Jan 2014 01:01:41 -0800 Subject: [PATCH 18/20] adding lookWithMouse.js example that uses mouse instead of multi-touch to add the look around feature --- examples/lookWithMouse.js | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 examples/lookWithMouse.js diff --git a/examples/lookWithMouse.js b/examples/lookWithMouse.js new file mode 100644 index 0000000000..90ea81bf8c --- /dev/null +++ b/examples/lookWithMouse.js @@ -0,0 +1,84 @@ +// +// lookWithMouse.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/28/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Controller class +// +// + +var isMouseDown = false; +var lastX = 0; +var lastY = 0; +var yawFromMouse = 0; +var pitchFromMouse = 0; + +function mousePressEvent(event) { + print("mousePressEvent event.x,y=" + event.x + ", " + event.y); + isMouseDown = true; + lastX = event.x; + lastY = event.y; +} + +function mouseReleaseEvent(event) { + print("mouseReleaseEvent event.x,y=" + event.x + ", " + event.y); + isMouseDown = false; +} + +function mouseMoveEvent(event) { + print("mouseMoveEvent event.x,y=" + event.x + ", " + event.y); + + if (isMouseDown) { + print("isMouseDown... attempting to change pitch..."); + var MOUSE_YAW_SCALE = -0.25; + var MOUSE_PITCH_SCALE = -12.5; + var FIXED_MOUSE_TIMESTEP = 0.016; + yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); + pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); + lastX = event.x; + lastY = event.y; + } +} + +function update() { + + print("isMouseDown... attempting to change pitch..."); + + // rotate body yaw for yaw received from mouse + MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } )); + yawFromMouse = 0; + + // apply pitch from mouse + /** + _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() + + _myAvatar.getHand().getPitchUpdate() + + pitchFromMouse); + **/ + + //_myAvatar.getHand().setPitchUpdate(0.f); + + pitchFromMouse = 0; +} + +// Map the mouse events to our functions +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + +// disable the standard application for mouse events +Controller.captureMouseEvents(); + +function scriptEnding() { + // re-enabled the standard application for mouse events + Controller.releaseMouseEvents(); +} + +MyAvatar.bodyYaw = 0; +MyAvatar.bodyPitch = 0; +MyAvatar.bodyRoll = 0; + +// would be nice to change to update +Agent.willSendVisualDataCallback.connect(update); +Agent.scriptEnding.connect(scriptEnding); From b36cc0bcf9bdfcf4952e6e895f647ebd4f75c964 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 29 Jan 2014 02:09:49 -0800 Subject: [PATCH 19/20] removed old cruft around mousePitch in Head and unified pitch --- examples/lookWithMouse.js | 12 +----------- interface/src/Application.cpp | 5 +---- interface/src/avatar/Head.cpp | 12 +----------- interface/src/avatar/Head.h | 5 ----- interface/src/avatar/MyAvatar.cpp | 17 ++++++++--------- libraries/avatars/src/AvatarData.h | 5 +++++ 6 files changed, 16 insertions(+), 40 deletions(-) diff --git a/examples/lookWithMouse.js b/examples/lookWithMouse.js index 90ea81bf8c..79fad76a1b 100644 --- a/examples/lookWithMouse.js +++ b/examples/lookWithMouse.js @@ -43,22 +43,12 @@ function mouseMoveEvent(event) { } function update() { - - print("isMouseDown... attempting to change pitch..."); - // rotate body yaw for yaw received from mouse MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } )); yawFromMouse = 0; // apply pitch from mouse - /** - _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() + - _myAvatar.getHand().getPitchUpdate() + - pitchFromMouse); - **/ - - //_myAvatar.getHand().setPitchUpdate(0.f); - + MyAvatar.headPitch = MyAvatar.headPitch + pitchFromMouse; pitchFromMouse = 0; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a0d9c9617e..6578618e5f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2404,10 +2404,7 @@ void Application::updateAvatar(float deltaTime) { _yawFromTouch = 0.f; // apply pitch from touch - _myAvatar.getHead().setMousePitch(_myAvatar.getHead().getMousePitch() + - _myAvatar.getHand().getPitchUpdate() + - _pitchFromTouch); - _myAvatar.getHand().setPitchUpdate(0.f); + _myAvatar.getHead().setPitch(_myAvatar.getHead().getPitch() + _pitchFromTouch); _pitchFromTouch = 0.0f; // Update my avatar's state from gyros diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 78e93996c8..2269d1d4a6 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -37,8 +37,6 @@ Head::Head(Avatar* owningAvatar) : _leftEyeBlinkVelocity(0.0f), _rightEyeBlinkVelocity(0.0f), _timeWithoutTalking(0.0f), - _cameraPitch(_pitch), - _mousePitch(0.f), _cameraYaw(_yaw), _isCameraMoving(false), _faceModel(this) @@ -52,7 +50,6 @@ void Head::init() { void Head::reset() { _yaw = _pitch = _roll = 0.0f; - _mousePitch = 0.0f; _leanForward = _leanSideways = 0.0f; _faceModel.reset(); } @@ -186,13 +183,6 @@ void Head::setScale (float scale) { _scale = scale; } -void Head::setMousePitch(float mousePitch) { - const float MAX_PITCH = 90.0f; - _mousePitch = glm::clamp(mousePitch, -MAX_PITCH, MAX_PITCH); -} - - - glm::quat Head::getOrientation() const { return glm::quat(glm::radians(_bodyRotation)) * glm::quat(glm::radians(glm::vec3(_pitch, _yaw, _roll))); } @@ -200,7 +190,7 @@ glm::quat Head::getOrientation() const { glm::quat Head::getCameraOrientation () const { Avatar* owningAvatar = static_cast(_owningAvatar); return owningAvatar->getWorldAlignedOrientation() - * glm::quat(glm::radians(glm::vec3(_cameraPitch + _mousePitch, _cameraYaw, 0.0f))); + * glm::quat(glm::radians(glm::vec3(_pitch, _cameraYaw, 0.0f))); } glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 5d9f9d9bbd..94b8bd3dc7 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -46,9 +46,6 @@ public: void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; } - float getMousePitch() const { return _mousePitch; } - void setMousePitch(float mousePitch); - glm::quat getOrientation() const; glm::quat getCameraOrientation () const; const glm::vec3& getAngularVelocity() const { return _angularVelocity; } @@ -99,8 +96,6 @@ private: float _leftEyeBlinkVelocity; float _rightEyeBlinkVelocity; float _timeWithoutTalking; - float _cameraPitch; // Used to position the camera differently from the head - float _mousePitch; float _cameraYaw; bool _isCameraMoving; FaceModel _faceModel; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6910826524..7448d33415 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -238,7 +238,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { // Adjust body yaw by yaw from controller setOrientation(glm::angleAxis(-euler.y, glm::vec3(0, 1, 0)) * getOrientation()); // Adjust head pitch from controller - getHead().setMousePitch(getHead().getMousePitch() - euler.x); + getHead().setPitch(getHead().getPitch() - euler.x); _position += _velocity * deltaTime; @@ -284,8 +284,6 @@ void MyAvatar::updateFromGyros(bool turnWithHead) { } } } else { - _head.setPitch(_head.getMousePitch()); - // restore rotation, lean to neutral positions const float RESTORE_RATE = 0.05f; _head.setYaw(glm::mix(_head.getYaw(), 0.0f, RESTORE_RATE)); @@ -434,7 +432,7 @@ void MyAvatar::saveData(QSettings* settings) { settings->setValue("bodyPitch", _bodyPitch); settings->setValue("bodyRoll", _bodyRoll); - settings->setValue("mousePitch", _head.getMousePitch()); + settings->setValue("headPitch", _head.getPitch()); settings->setValue("position_x", _position.x); settings->setValue("position_y", _position.y); @@ -456,7 +454,7 @@ void MyAvatar::loadData(QSettings* settings) { _bodyPitch = loadSetting(settings, "bodyPitch", 0.0f); _bodyRoll = loadSetting(settings, "bodyRoll", 0.0f); - _head.setMousePitch(loadSetting(settings, "mousePitch", 0.0f)); + _head.setPitch(loadSetting(settings, "headPitch", 0.0f)); _position.x = loadSetting(settings, "position_x", 0.0f); _position.y = loadSetting(settings, "position_y", 0.0f); @@ -497,9 +495,10 @@ void MyAvatar::orbit(const glm::vec3& position, int deltaX, int deltaY) { setOrientation(orientation); // then vertically - float oldMousePitch = _head.getMousePitch(); - _head.setMousePitch(oldMousePitch + deltaY * -ANGULAR_SCALE); - rotation = glm::angleAxis(_head.getMousePitch() - oldMousePitch, orientation * IDENTITY_RIGHT); + float oldPitch = _head.getPitch(); + _head.setPitch(oldPitch + deltaY * -ANGULAR_SCALE); + rotation = glm::angleAxis(_head.getPitch() - oldPitch, orientation * IDENTITY_RIGHT); + setPosition(position + rotation * (getPosition() - position)); } @@ -549,7 +548,7 @@ void MyAvatar::updateThrust(float deltaTime, Transmitter * transmitter) { _thrust -= _driveKeys[DOWN] * _scale * THRUST_MAG_DOWN * _thrustMultiplier * deltaTime * up; _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_MAG * deltaTime; _bodyYawDelta += _driveKeys[ROT_LEFT] * YAW_MAG * deltaTime; - _head.setMousePitch(_head.getMousePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime); + _head.setPitch(_head.getPitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_MAG * deltaTime); // If thrust keys are being held down, slowly increase thrust to allow reaching great speeds if (_driveKeys[FWD] || _driveKeys[BACK] || _driveKeys[RIGHT] || _driveKeys[LEFT] || _driveKeys[UP] || _driveKeys[DOWN]) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 7300da041e..e3bb8f08d5 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -71,6 +71,7 @@ class AvatarData : public NodeData { Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) + Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) public: AvatarData(); @@ -96,6 +97,10 @@ public: glm::quat getOrientation() const { return glm::quat(glm::radians(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll))); } void setOrientation(const glm::quat& orientation); + // access to Head().set/getMousePitch + float getHeadPitch() const { return _headData->getPitch(); } + void setHeadPitch(float value) { _headData->setPitch(value); }; + // Scale float getTargetScale() const { return _targetScale; } void setTargetScale(float targetScale) { _targetScale = targetScale; } From 616cfac2939789d164db6a657c347d482ba998a3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 29 Jan 2014 11:21:45 -0800 Subject: [PATCH 20/20] fix init bug --- interface/src/ControllerScriptingInterface.cpp | 8 ++++++++ interface/src/ControllerScriptingInterface.h | 1 + 2 files changed, 9 insertions(+) diff --git a/interface/src/ControllerScriptingInterface.cpp b/interface/src/ControllerScriptingInterface.cpp index 799a57b951..4d7540c06c 100644 --- a/interface/src/ControllerScriptingInterface.cpp +++ b/interface/src/ControllerScriptingInterface.cpp @@ -10,6 +10,14 @@ #include "Application.h" #include "ControllerScriptingInterface.h" +ControllerScriptingInterface::ControllerScriptingInterface() : + _mouseCaptured(false), + _touchCaptured(false), + _wheelCaptured(false) +{ +} + + const PalmData* ControllerScriptingInterface::getPrimaryPalm() const { int leftPalmIndex, rightPalmIndex; diff --git a/interface/src/ControllerScriptingInterface.h b/interface/src/ControllerScriptingInterface.h index b6d5c81a05..e84039bcb0 100644 --- a/interface/src/ControllerScriptingInterface.h +++ b/interface/src/ControllerScriptingInterface.h @@ -20,6 +20,7 @@ class ControllerScriptingInterface : public AbstractControllerScriptingInterface Q_OBJECT public: + ControllerScriptingInterface(); void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }