From 08b06cc59c048fa8553bab91067dce77cc049bc4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 29 Jan 2014 17:52:17 -0800 Subject: [PATCH 1/4] support for global collision callbacks in JS --- examples/gameoflife.js | 133 ++++++++++++++++++ examples/globalCollisionsExample.js | 26 ++++ examples/gun.js | 8 +- examples/paintGun.js | 6 +- interface/src/Application.cpp | 11 ++ libraries/particles/src/Particle.cpp | 120 +++++----------- libraries/particles/src/Particle.h | 27 ++-- .../particles/src/ParticleCollisionSystem.cpp | 15 ++ .../particles/src/ParticleCollisionSystem.h | 10 +- .../src/ParticlesScriptingInterface.h | 12 ++ libraries/script-engine/src/ScriptEngine.cpp | 7 +- libraries/script-engine/src/ScriptEngine.h | 4 +- libraries/shared/src/RegisteredMetaTypes.h | 1 + libraries/shared/src/SharedUtil.cpp | 100 ------------- libraries/shared/src/SharedUtil.h | 18 --- libraries/voxels/src/VoxelDetail.cpp | 37 +++++ libraries/voxels/src/VoxelDetail.h | 36 +++++ .../voxels/src/VoxelEditPacketSender.cpp | 100 +++++++++++++ libraries/voxels/src/VoxelEditPacketSender.h | 1 + libraries/voxels/src/VoxelTreeElement.h | 1 - .../voxels/src/VoxelsScriptingInterface.h | 16 --- 21 files changed, 448 insertions(+), 241 deletions(-) create mode 100644 examples/gameoflife.js create mode 100644 examples/globalCollisionsExample.js create mode 100644 libraries/voxels/src/VoxelDetail.cpp create mode 100644 libraries/voxels/src/VoxelDetail.h diff --git a/examples/gameoflife.js b/examples/gameoflife.js new file mode 100644 index 0000000000..09fae07204 --- /dev/null +++ b/examples/gameoflife.js @@ -0,0 +1,133 @@ +// Add your JavaScript for assignment below this line + +// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) + +var NUMBER_OF_CELLS_EACH_DIMENSION = 64; +var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; + +var currentCells = []; +var nextCells = []; + +var METER_LENGTH = 1; +var cellScale = (NUMBER_OF_CELLS_EACH_DIMENSION * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION; + +// randomly populate the cell start values +for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + // create the array to hold this row + currentCells[i] = []; + + // create the array to hold this row in the nextCells array + nextCells[i] = []; + + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + currentCells[i][j] = Math.floor(Math.random() * 2); + + // put the same value in the nextCells array for first board draw + nextCells[i][j] = currentCells[i][j]; + } +} + +function isNeighbourAlive(i, j) { + if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION + || i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) { + return 0; + } else { + return currentCells[i][j]; + } +} + +function updateCells() { + var i = 0; + var j = 0; + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + // figure out the number of live neighbours for the i-j cell + var liveNeighbours = + isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) + + isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) + + isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1); + + if (currentCells[i][j]) { + // live cell + + if (liveNeighbours < 2) { + // rule #1 - under-population - this cell will die + // mark it zero to mark the change + nextCells[i][j] = 0; + } else if (liveNeighbours < 4) { + // rule #2 - this cell lives + // mark it -1 to mark no change + nextCells[i][j] = -1; + } else { + // rule #3 - overcrowding - this cell dies + // mark it zero to mark the change + nextCells[i][j] = 0; + } + } else { + // dead cell + if (liveNeighbours == 3) { + // rule #4 - reproduction - this cell revives + // mark it one to mark the change + nextCells[i][j] = 1; + } else { + // this cell stays dead + // mark it -1 for no change + nextCells[i][j] = -1; + } + } + + if (Math.random() < 0.001) { + // Random mutation to keep things interesting in there. + nextCells[i][j] = 1; + } + } + } + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to this cell, change the value in the currentCells array + currentCells[i][j] = nextCells[i][j]; + } + } + } +} + +function sendNextCells() { + for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to the state of this cell, send it + + // find the x and y position for this voxel, z = 0 + var x = j * cellScale; + var y = i * cellScale; + + // queue a packet to add a voxel for the new cell + var color = (nextCells[i][j] == 1) ? 255 : 1; + Voxels.setVoxel(x, y, 0, cellScale, color, color, color); + } + } + } +} + +var sentFirstBoard = false; + +function step() { +print("step()..."); + if (sentFirstBoard) { + // we've already sent the first full board, perform a step in time + updateCells(); + } else { + // this will be our first board send + sentFirstBoard = true; + } + + sendNextCells(); +} + +print("here"); +Agent.willSendVisualDataCallback.connect(step); +Voxels.setPacketsPerSecond(200); +print("now here"); diff --git a/examples/globalCollisionsExample.js b/examples/globalCollisionsExample.js new file mode 100644 index 0000000000..4db4c808e5 --- /dev/null +++ b/examples/globalCollisionsExample.js @@ -0,0 +1,26 @@ +// +// globalCollisionsExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/29/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Controller class +// +// + + +function particleCollisionWithVoxel(particle, voxel) { + print("particleCollisionWithVoxel().."); + print(" particle.getID()=" + particle.id); + print(" voxel color...=" + voxel.red + ", " + voxel.green + ", " + voxel.blue); +} + +function particleCollisionWithParticle(particleA, particleB) { + print("particleCollisionWithParticle().."); + print(" particleA.getID()=" + particleA.id); + print(" particleB.getID()=" + particleB.id); +} + +Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel); +Particles.particleCollisionWithParticle.connect(particleCollisionWithParticle); diff --git a/examples/gun.js b/examples/gun.js index 30d2b41449..3f8eefe3e2 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -72,13 +72,13 @@ function checkController() { " function collisionWithVoxel(voxel) { " + " print('collisionWithVoxel(voxel)... '); " + " print('myID=' + Particle.getID() + '\\n'); " + - " var voxelColor = voxel.getColor();" + - " print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " + + " var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" + + " var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" + + " var voxelScale = voxel.s;" + + " print('voxelColor=' + voxel.red + ', ' + voxel.green + ', ' + voxel.blue + '\\n'); " + " var myColor = Particle.getColor();" + " print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " + " Particle.setColor(voxelColor); " + - " var voxelAt = voxel.getPosition();" + - " var voxelScale = voxel.getScale();" + " Voxels.eraseVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); " + " print('Voxels.eraseVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " + " } " + diff --git a/examples/paintGun.js b/examples/paintGun.js index 0cbe3ff366..b78e6abb0b 100644 --- a/examples/paintGun.js +++ b/examples/paintGun.js @@ -65,13 +65,13 @@ function checkController() { " function collisionWithVoxel(voxel) { " + " print('collisionWithVoxel(voxel)... '); " + " print('myID=' + Particle.getID() + '\\n'); " + - " var voxelColor = voxel.getColor();" + + " var voxelColor = { red: voxel.red, green: voxel.green, blue: voxel.blue };" + + " var voxelAt = { x: voxel.x, y: voxel.y, z: voxel.z };" + + " var voxelScale = voxel.s;" + " print('voxelColor=' + voxelColor.red + ', ' + voxelColor.green + ', ' + voxelColor.blue + '\\n'); " + " var myColor = Particle.getColor();" + " print('myColor=' + myColor.red + ', ' + myColor.green + ', ' + myColor.blue + '\\n'); " + " Particle.setColor(voxelColor); " + - " var voxelAt = voxel.getPosition();" + - " var voxelScale = voxel.getScale();" + " Voxels.setVoxel(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale, 255, 255, 0); " + " print('Voxels.setVoxel(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); " + " } " + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6578618e5f..f300c79c67 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1879,6 +1879,17 @@ void Application::init() { _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager); + // connect the _particleCollisionSystem to our script engine's ParticleScriptingInterface + connect(&_particleCollisionSystem, + SIGNAL(particleCollisionWithVoxel(const ParticleID&, const VoxelDetail&)), + ScriptEngine::getParticlesScriptingInterface(), + SLOT(forwardParticleCollisionWithVoxel(const ParticleID&, const VoxelDetail&))); + + connect(&_particleCollisionSystem, + SIGNAL(particleCollisionWithParticle(const ParticleID&, const ParticleID&)), + ScriptEngine::getParticlesScriptingInterface(), + SLOT(forwardParticleCollisionWithParticle(const ParticleID&, const ParticleID&))); + _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1); diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 933bf36830..557d4ca87f 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -13,6 +13,8 @@ #include #include // usecTimestampNow() #include +#include + // This is not ideal, but adding script-engine as a linked library, will cause a circular reference // I'm open to other potential solutions. Could we change cmake to allow libraries to reference each others @@ -831,7 +833,7 @@ void Particle::update(const uint64_t& now) { bool shouldDie = (getAge() > getLifetime()) || getShouldDie() || (!isInHand && isStopped && isReallyOld); setShouldDie(shouldDie); - runUpdateScript(); // allow the javascript to alter our state + executeUpdateScripts(); // allow the javascript to alter our state // If the ball is in hand, it doesn't move or have gravity effect it if (!isInHand) { @@ -853,106 +855,62 @@ void Particle::update(const uint64_t& now) { } } -void Particle::runUpdateScript() { +void Particle::startParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable) { + if (_voxelEditSender) { + engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); + } + if (_particleEditSender) { + engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); + } + + // Add the "this" Particle object + engine.registerGlobalObject("Particle", &particleScriptable); + engine.evaluate(); +} + +void Particle::endParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable) { + if (_voxelEditSender) { + _voxelEditSender->releaseQueuedMessages(); + } + if (_particleEditSender) { + _particleEditSender->releaseQueuedMessages(); + } +} + +void Particle::executeUpdateScripts() { + // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script); // no menu or controller interface... - - if (_voxelEditSender) { - engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); - } - if (_particleEditSender) { - engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); - } - - // Add the Particle object + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); - engine.registerGlobalObject("Particle", &particleScriptable); - - // init and evaluate the script, but return so we can emit the collision - engine.evaluate(); - + startParticleScriptContext(engine, particleScriptable); particleScriptable.emitUpdate(); - - // it seems like we may need to send out particle edits if the state of our particle was changed. - - if (_voxelEditSender) { - _voxelEditSender->releaseQueuedMessages(); - } - if (_particleEditSender) { - _particleEditSender->releaseQueuedMessages(); - } + endParticleScriptContext(engine, particleScriptable); } } void Particle::collisionWithParticle(Particle* other) { + // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - ScriptEngine engine(_script); // no menu or controller interface... - - if (_voxelEditSender) { - engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); - } - if (_particleEditSender) { - engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); - } - - // Add the Particle object + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); - engine.registerGlobalObject("Particle", &particleScriptable); - - // init and evaluate the script, but return so we can emit the collision - engine.evaluate(); - + startParticleScriptContext(engine, particleScriptable); ParticleScriptObject otherParticleScriptable(other); particleScriptable.emitCollisionWithParticle(&otherParticleScriptable); - - // it seems like we may need to send out particle edits if the state of our particle was changed. - - if (_voxelEditSender) { - _voxelEditSender->releaseQueuedMessages(); - } - if (_particleEditSender) { - _particleEditSender->releaseQueuedMessages(); - } + endParticleScriptContext(engine, particleScriptable); } } void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) { + // Only run this particle script if there's a script attached directly to the particle. if (!_script.isEmpty()) { - - ScriptEngine engine(_script); // no menu or controller interface... - - // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so - // we can use the same ones as our context. - if (_voxelEditSender) { - engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender); - } - if (_particleEditSender) { - engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender); - } - - // Add the Particle object + ScriptEngine engine(_script); ParticleScriptObject particleScriptable(this); - engine.registerGlobalObject("Particle", &particleScriptable); - - // init and evaluate the script, but return so we can emit the collision - engine.evaluate(); - - VoxelDetailScriptObject voxelDetailsScriptable(voxelDetails); - particleScriptable.emitCollisionWithVoxel(&voxelDetailsScriptable); - - // it seems like we may need to send out particle edits if the state of our particle was changed. - - if (_voxelEditSender) { - _voxelEditSender->releaseQueuedMessages(); - } - if (_particleEditSender) { - _particleEditSender->releaseQueuedMessages(); - } + startParticleScriptContext(engine, particleScriptable); + particleScriptable.emitCollisionWithVoxel(*voxelDetails); + endParticleScriptContext(engine, particleScriptable); } } - - void Particle::setAge(float age) { uint64_t ageInUsecs = age * USECS_PER_SECOND; _created = usecTimestampNow() - ageInUsecs; diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index 456eb7ef4f..ec2e389cb5 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -20,13 +20,16 @@ #include #include -class VoxelsScriptingInterface; -class ParticlesScriptingInterface; -class VoxelEditPacketSender; +class Particle; class ParticleEditPacketSender; class ParticleProperties; -class Particle; +class ParticlesScriptingInterface; +class ParticleScriptObject; class ParticleTree; +class ScriptEngine; +class VoxelEditPacketSender; +class VoxelsScriptingInterface; +struct VoxelDetail; const uint32_t NEW_PARTICLE = 0xFFFFFFFF; const uint32_t UNKNOWN_TOKEN = 0xFFFFFFFF; @@ -227,7 +230,8 @@ public: const glm::vec3& getModelTranslation() const { return _modelTranslation; } const glm::quat& getModelRotation() const { return _modelRotation; } float getModelScale() const { return _modelScale; } - + + ParticleID getParticleID() const { return ParticleID(getID(), getCreatorTokenID(), getID() != UNKNOWN_PARTICLE_ID); } ParticleProperties getProperties() const; /// The last updated/simulated time of this particle from the time perspective of the authoritative server/source @@ -318,11 +322,9 @@ protected: static VoxelEditPacketSender* _voxelEditSender; static ParticleEditPacketSender* _particleEditSender; - void runUpdateScript(); - static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3); - static void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); - static QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color); - static void xColorFromScriptValue(const QScriptValue &object, xColor& color); + void startParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable); + void endParticleScriptContext(ScriptEngine& engine, ParticleScriptObject& particleScriptable); + void executeUpdateScripts(); void setAge(float age); @@ -366,10 +368,11 @@ class ParticleScriptObject : public QObject { Q_OBJECT public: ParticleScriptObject(Particle* particle) { _particle = particle; } + //~ParticleScriptObject() { qDebug() << "~ParticleScriptObject() this=" << this; } void emitUpdate() { emit update(); } void emitCollisionWithParticle(QObject* other) { emit collisionWithParticle(other); } - void emitCollisionWithVoxel(QObject* voxel) { emit collisionWithVoxel(voxel); } + void emitCollisionWithVoxel(const VoxelDetail& voxel) { emit collisionWithVoxel(voxel); } public slots: unsigned int getID() const { return _particle->getID(); } @@ -414,7 +417,7 @@ public slots: signals: void update(); - void collisionWithVoxel(QObject* voxel); + void collisionWithVoxel(const VoxelDetail& voxel); void collisionWithParticle(QObject* other); private: diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index a2394935b1..143b1ddcbd 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -68,6 +68,17 @@ void ParticleCollisionSystem::checkParticle(Particle* particle) { updateCollisionWithAvatars(particle); } +void ParticleCollisionSystem::emitGlobalParicleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails) { + ParticleID particleID = particle->getParticleID(); + emit particleCollisionWithVoxel(particleID, *voxelDetails); +} + +void ParticleCollisionSystem::emitGlobalParicleCollisionWithParticle(Particle* particleA, Particle* particleB) { + ParticleID idA = particleA->getParticleID(); + ParticleID idB = particleB->getParticleID(); + emit particleCollisionWithParticle(idA, idB); +} + void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE); float radius = particle->getRadius() * (float)(TREE_SCALE); @@ -83,6 +94,9 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { // let the particles run their collision scripts if they have them particle->collisionWithVoxel(voxelDetails); + // let the global script run their collision scripts for particles if they have them + emitGlobalParicleCollisionWithVoxel(particle, voxelDetails); + updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); collisionInfo._penetration /= (float)(TREE_SCALE); particle->applyHardCollision(collisionInfo); @@ -110,6 +124,7 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) if (glm::dot(relativeVelocity, penetration) > 0.0f) { particleA->collisionWithParticle(particleB); particleB->collisionWithParticle(particleA); + emitGlobalParicleCollisionWithParticle(particleA, particleB); glm::vec3 axis = glm::normalize(penetration); glm::vec3 axialVelocity = glm::dot(relativeVelocity, axis) * axis; diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index 4a61693fa6..9baf9bfd05 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -31,7 +31,8 @@ class VoxelTree; const glm::vec3 NO_ADDED_VELOCITY = glm::vec3(0); -class ParticleCollisionSystem { +class ParticleCollisionSystem : public QObject { +Q_OBJECT public: ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL, VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL, @@ -51,9 +52,14 @@ public: void queueParticlePropertiesUpdate(Particle* particle); void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency); +signals: + void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel); + void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB); + private: static bool updateOperation(OctreeElement* element, void* extraData); - + void emitGlobalParicleCollisionWithVoxel(Particle* particle, VoxelDetail* voxelDetails); + void emitGlobalParicleCollisionWithParticle(Particle* particleA, Particle* particleB); ParticleEditPacketSender* _packetSender; ParticleTree* _particles; diff --git a/libraries/particles/src/ParticlesScriptingInterface.h b/libraries/particles/src/ParticlesScriptingInterface.h index 9a3ceb51af..2a6a5e24f0 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.h +++ b/libraries/particles/src/ParticlesScriptingInterface.h @@ -53,7 +53,19 @@ public slots: /// finds particles within the search sphere specified by the center point and radius /// this function will not find any particles in script engine contexts which don't have access to particles QVector findParticles(const glm::vec3& center, float radius) const; + + /// inbound slots for external collision systems + void forwardParticleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel) { + emit particleCollisionWithVoxel(particleID, voxel); + } + + void forwardParticleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB) { + emit particleCollisionWithParticle(idA, idB); + } +signals: + void particleCollisionWithVoxel(const ParticleID& particleID, const VoxelDetail& voxel); + void particleCollisionWithParticle(const ParticleID& idA, const ParticleID& idB); private: void queueParticleMessage(PACKET_TYPE packetType, ParticleID particleID, const ParticleProperties& properties); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 28cb49f6f7..9db1254f80 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -114,10 +115,12 @@ void ScriptEngine::init() { _voxelsScriptingInterface.init(); _particlesScriptingInterface.init(); - // register meta-type for glm::vec3 conversions + // register various meta-types registerMetaTypes(&_engine); - + registerVoxelMetaTypes(&_engine); + //registerParticleMetaTypes(&_engine); registerEventTypes(&_engine); + qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue); qScriptRegisterSequenceMetaType >(&_engine); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index ef55e14109..c2188cca63 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -41,10 +41,10 @@ public: ~ScriptEngine(); /// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener - VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; } + static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return &_voxelsScriptingInterface; } /// Access the ParticlesScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener - ParticlesScriptingInterface* getParticlesScriptingInterface() { return &_particlesScriptingInterface; } + static ParticlesScriptingInterface* getParticlesScriptingInterface() { return &_particlesScriptingInterface; } /// Access the DataServerScriptingInterface for access to its underlying UUID const DataServerScriptingInterface& getDataServerScriptingInterface() { return _dataServerScriptingInterface; } diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index de667c9ed8..b198add7c2 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -22,6 +22,7 @@ Q_DECLARE_METATYPE(glm::vec2) Q_DECLARE_METATYPE(glm::quat) Q_DECLARE_METATYPE(xColor) + void registerMetaTypes(QScriptEngine* engine); QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 54a4291c25..e2c5b912c0 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -231,106 +231,6 @@ void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, con fprintf(stdout, "%s", message.toLocal8Bit().constData()); } -////////////////////////////////////////////////////////////////////////////////////////// -// Function: createVoxelEditMessage() -// Description: creates an "insert" or "remove" voxel message for a voxel code -// corresponding to the closest voxel which encloses a cube with -// lower corners at x,y,z, having side of length S. -// The input values x,y,z range 0.0 <= v < 1.0 -// message should be either 'S' for SET or 'E' for ERASE -// -// IMPORTANT: The buffer is returned to you a buffer which you MUST delete when you are -// done with it. -// -// HACK ATTACK: Well, what if this is larger than the MTU? That's the caller's problem, we -// just truncate the message -// -// Complaints: Brad :) -#define GUESS_OF_VOXELCODE_SIZE 10 -#define MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE 1500 -#define SIZE_OF_COLOR_DATA sizeof(rgbColor) -bool createVoxelEditMessage(unsigned char command, short int sequence, - int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) { - - bool success = true; // assume the best - int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now - unsigned char* messageBuffer = new unsigned char[messageSize]; - - int numBytesPacketHeader = populateTypeAndVersion(messageBuffer, command); - unsigned short int* sequenceAt = (unsigned short int*) &messageBuffer[numBytesPacketHeader]; - *sequenceAt = sequence; - - // pack in timestamp - uint64_t now = usecTimestampNow(); - uint64_t* timeAt = (uint64_t*)&messageBuffer[numBytesPacketHeader + sizeof(sequence)]; - *timeAt = now; - - unsigned char* copyAt = &messageBuffer[numBytesPacketHeader + sizeof(sequence) + sizeof(now)]; - int actualMessageSize = numBytesPacketHeader + sizeof(sequence) + sizeof(now); - - for (int i = 0; i < voxelCount && success; i++) { - // get the coded voxel - unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z, - voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue); - - int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA; - - // make sure we have room to copy this voxel - if (actualMessageSize + lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) { - success = false; - } else { - // add it to our message - memcpy(copyAt, voxelData, lengthOfVoxelData); - copyAt += lengthOfVoxelData; - actualMessageSize += lengthOfVoxelData; - } - // cleanup - delete[] voxelData; - } - - if (success) { - // finally, copy the result to the output - bufferOut = new unsigned char[actualMessageSize]; - sizeOut = actualMessageSize; - memcpy(bufferOut, messageBuffer, actualMessageSize); - } - - delete[] messageBuffer; // clean up our temporary buffer - return success; -} - -/// encodes the voxel details portion of a voxel edit message -bool encodeVoxelEditMessageDetails(unsigned char command, int voxelCount, VoxelDetail* voxelDetails, - unsigned char* bufferOut, int sizeIn, int& sizeOut) { - - bool success = true; // assume the best - unsigned char* copyAt = bufferOut; - sizeOut = 0; - - for (int i = 0; i < voxelCount && success; i++) { - // get the coded voxel - unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z, - voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue); - - int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA; - - // make sure we have room to copy this voxel - if (sizeOut + lengthOfVoxelData > sizeIn) { - success = false; - } else { - // add it to our message - memcpy(copyAt, voxelData, lengthOfVoxelData); - copyAt += lengthOfVoxelData; - sizeOut += lengthOfVoxelData; - } - // cleanup - delete[] voxelData; - } - - return success; -} - - unsigned char* pointToOctalCode(float x, float y, float z, float s) { return pointToVoxel(x, y, z, s); } diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 399bf40204..c25d2c5f0d 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -94,27 +94,9 @@ bool cmdOptionExists(int argc, const char * argv[],const char* option); void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message); -struct VoxelDetail { - float x; - float y; - float z; - float s; - unsigned char red; - unsigned char green; - unsigned char blue; -}; - unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r = 0, unsigned char g = 0, unsigned char b = 0); unsigned char* pointToOctalCode(float x, float y, float z, float s); -// Creates a full Voxel edit message, including command header, sequence, and details -bool createVoxelEditMessage(unsigned char command, short int sequence, - int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut); - -/// encodes the voxel details portion of a voxel edit message -bool encodeVoxelEditMessageDetails(unsigned char command, int voxelCount, VoxelDetail* voxelDetails, - unsigned char* bufferOut, int sizeIn, int& sizeOut); - #ifdef _WIN32 void usleep(int waitTime); #endif diff --git a/libraries/voxels/src/VoxelDetail.cpp b/libraries/voxels/src/VoxelDetail.cpp new file mode 100644 index 0000000000..d4ab5cafcb --- /dev/null +++ b/libraries/voxels/src/VoxelDetail.cpp @@ -0,0 +1,37 @@ +// +// VoxelDetail.cpp +// hifi +// +// Created by Brad Hefta-Gaub on 1/29/2014 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +#include "VoxelDetail.h" + +void registerVoxelMetaTypes(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, voxelDetailToScriptValue, voxelDetailFromScriptValue); +} + +QScriptValue voxelDetailToScriptValue(QScriptEngine* engine, const VoxelDetail& voxelDetail) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", voxelDetail.x * (float)TREE_SCALE); + obj.setProperty("y", voxelDetail.y * (float)TREE_SCALE); + obj.setProperty("z", voxelDetail.z * (float)TREE_SCALE); + obj.setProperty("s", voxelDetail.s * (float)TREE_SCALE); + obj.setProperty("red", voxelDetail.red); + obj.setProperty("green", voxelDetail.green); + obj.setProperty("blue", voxelDetail.blue); + return obj; +} + +void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& voxelDetail) { + voxelDetail.x = object.property("x").toVariant().toFloat() / (float)TREE_SCALE; + voxelDetail.y = object.property("y").toVariant().toFloat() / (float)TREE_SCALE; + voxelDetail.z = object.property("z").toVariant().toFloat() / (float)TREE_SCALE; + voxelDetail.s = object.property("s").toVariant().toFloat() / (float)TREE_SCALE; + voxelDetail.red = object.property("red").toVariant().toInt(); + voxelDetail.green = object.property("green").toVariant().toInt(); + voxelDetail.blue = object.property("blue").toVariant().toInt(); +} + + + diff --git a/libraries/voxels/src/VoxelDetail.h b/libraries/voxels/src/VoxelDetail.h new file mode 100644 index 0000000000..ca1ff3940b --- /dev/null +++ b/libraries/voxels/src/VoxelDetail.h @@ -0,0 +1,36 @@ +// +// VoxelDetail.h +// hifi +// +// Created by Brad Hefta-Gaub on 1/29/2014 +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// +// + +#ifndef __hifi__VoxelDetail__ +#define __hifi__VoxelDetail__ + +#include + +#include +#include "VoxelConstants.h" + +struct VoxelDetail { + float x; + float y; + float z; + float s; + unsigned char red; + unsigned char green; + unsigned char blue; +}; + +Q_DECLARE_METATYPE(VoxelDetail) + +void registerVoxelMetaTypes(QScriptEngine* engine); + +QScriptValue voxelDetailToScriptValue(QScriptEngine* engine, const VoxelDetail& color); +void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& color); + + +#endif /* defined(__hifi__VoxelDetail__) */ \ No newline at end of file diff --git a/libraries/voxels/src/VoxelEditPacketSender.cpp b/libraries/voxels/src/VoxelEditPacketSender.cpp index af3ae63377..e776a8f665 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.cpp +++ b/libraries/voxels/src/VoxelEditPacketSender.cpp @@ -14,6 +14,106 @@ #include #include "VoxelEditPacketSender.h" +////////////////////////////////////////////////////////////////////////////////////////// +// Function: createVoxelEditMessage() +// Description: creates an "insert" or "remove" voxel message for a voxel code +// corresponding to the closest voxel which encloses a cube with +// lower corners at x,y,z, having side of length S. +// The input values x,y,z range 0.0 <= v < 1.0 +// message should be either 'S' for SET or 'E' for ERASE +// +// IMPORTANT: The buffer is returned to you a buffer which you MUST delete when you are +// done with it. +// +// HACK ATTACK: Well, what if this is larger than the MTU? That's the caller's problem, we +// just truncate the message +// +// Complaints: Brad :) +#define GUESS_OF_VOXELCODE_SIZE 10 +#define MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE 1500 +#define SIZE_OF_COLOR_DATA sizeof(rgbColor) +bool createVoxelEditMessage(unsigned char command, short int sequence, + int voxelCount, VoxelDetail* voxelDetails, unsigned char*& bufferOut, int& sizeOut) { + + bool success = true; // assume the best + int messageSize = MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE; // just a guess for now + unsigned char* messageBuffer = new unsigned char[messageSize]; + + int numBytesPacketHeader = populateTypeAndVersion(messageBuffer, command); + unsigned short int* sequenceAt = (unsigned short int*) &messageBuffer[numBytesPacketHeader]; + *sequenceAt = sequence; + + // pack in timestamp + uint64_t now = usecTimestampNow(); + uint64_t* timeAt = (uint64_t*)&messageBuffer[numBytesPacketHeader + sizeof(sequence)]; + *timeAt = now; + + unsigned char* copyAt = &messageBuffer[numBytesPacketHeader + sizeof(sequence) + sizeof(now)]; + int actualMessageSize = numBytesPacketHeader + sizeof(sequence) + sizeof(now); + + for (int i = 0; i < voxelCount && success; i++) { + // get the coded voxel + unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z, + voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue); + + int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA; + + // make sure we have room to copy this voxel + if (actualMessageSize + lengthOfVoxelData > MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE) { + success = false; + } else { + // add it to our message + memcpy(copyAt, voxelData, lengthOfVoxelData); + copyAt += lengthOfVoxelData; + actualMessageSize += lengthOfVoxelData; + } + // cleanup + delete[] voxelData; + } + + if (success) { + // finally, copy the result to the output + bufferOut = new unsigned char[actualMessageSize]; + sizeOut = actualMessageSize; + memcpy(bufferOut, messageBuffer, actualMessageSize); + } + + delete[] messageBuffer; // clean up our temporary buffer + return success; +} + +/// encodes the voxel details portion of a voxel edit message +bool encodeVoxelEditMessageDetails(unsigned char command, int voxelCount, VoxelDetail* voxelDetails, + unsigned char* bufferOut, int sizeIn, int& sizeOut) { + + bool success = true; // assume the best + unsigned char* copyAt = bufferOut; + sizeOut = 0; + + for (int i = 0; i < voxelCount && success; i++) { + // get the coded voxel + unsigned char* voxelData = pointToVoxel(voxelDetails[i].x,voxelDetails[i].y,voxelDetails[i].z, + voxelDetails[i].s,voxelDetails[i].red,voxelDetails[i].green,voxelDetails[i].blue); + + int lengthOfVoxelData = bytesRequiredForCodeLength(*voxelData)+SIZE_OF_COLOR_DATA; + + // make sure we have room to copy this voxel + if (sizeOut + lengthOfVoxelData > sizeIn) { + success = false; + } else { + // add it to our message + memcpy(copyAt, voxelData, lengthOfVoxelData); + copyAt += lengthOfVoxelData; + sizeOut += lengthOfVoxelData; + } + // cleanup + delete[] voxelData; + } + + return success; +} + + void VoxelEditPacketSender::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) { // allows app to disable sending if for example voxels have been disabled diff --git a/libraries/voxels/src/VoxelEditPacketSender.h b/libraries/voxels/src/VoxelEditPacketSender.h index ec9b74dff8..c09c3b533a 100644 --- a/libraries/voxels/src/VoxelEditPacketSender.h +++ b/libraries/voxels/src/VoxelEditPacketSender.h @@ -12,6 +12,7 @@ #define __shared__VoxelEditPacketSender__ #include +#include "VoxelDetail.h" /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class VoxelEditPacketSender : public OctreeEditPacketSender { diff --git a/libraries/voxels/src/VoxelTreeElement.h b/libraries/voxels/src/VoxelTreeElement.h index 86732f4b66..028d2456eb 100644 --- a/libraries/voxels/src/VoxelTreeElement.h +++ b/libraries/voxels/src/VoxelTreeElement.h @@ -91,5 +91,4 @@ protected: nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes }; - #endif /* defined(__hifi__VoxelTreeElement__) */ \ No newline at end of file diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index 97bdfb2c59..f5f5114154 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -57,20 +57,4 @@ private: void queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails); }; -class VoxelDetailScriptObject : public QObject { - Q_OBJECT -public: - VoxelDetailScriptObject(VoxelDetail* voxelDetail) { _voxelDetail = voxelDetail; } - -public slots: - /// position in meter units - glm::vec3 getPosition() const { return glm::vec3(_voxelDetail->x, _voxelDetail->y, _voxelDetail->z) * (float)TREE_SCALE; } - xColor getColor() const { xColor color = { _voxelDetail->red, _voxelDetail->green, _voxelDetail->blue }; return color; } - /// scale in meter units - float getScale() const { return _voxelDetail->s * (float)TREE_SCALE; } - -private: - VoxelDetail* _voxelDetail; -}; - #endif /* defined(__hifi__VoxelsScriptingInterface__) */ From a04879936735013c40b7ac983805541159b7d0f3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 29 Jan 2014 20:54:22 -0800 Subject: [PATCH 2/4] fix header comment --- examples/findParticleExample.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/findParticleExample.js b/examples/findParticleExample.js index f582ee6469..bd20e6ded7 100644 --- a/examples/findParticleExample.js +++ b/examples/findParticleExample.js @@ -1,5 +1,5 @@ // -// editParticleExample.js +// findParticleExample.js // hifi // // Created by Brad Hefta-Gaub on 1/24/14. From 01c369716eadc8ad55cb13b909dfedb621ecdd40 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 29 Jan 2014 20:55:12 -0800 Subject: [PATCH 3/4] implement a basic exampple of ride along with a particle --- examples/rideAlongWithAParticleExample.js | 50 +++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 examples/rideAlongWithAParticleExample.js diff --git a/examples/rideAlongWithAParticleExample.js b/examples/rideAlongWithAParticleExample.js new file mode 100644 index 0000000000..eca9fe7f4c --- /dev/null +++ b/examples/rideAlongWithAParticleExample.js @@ -0,0 +1,50 @@ +// +// rideAlongWithAParticleExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 1/24/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates "finding" particles +// + +var iteration = 0; +var lengthOfRide = 2000; // in iterations + +var particleA = Particles.addParticle( + { + position: { x: 10, y: 0, z: 10 }, + velocity: { x: 5, y: 0, z: 5 }, + gravity: { x: 0, y: 0, z: 0 }, + radius : 0.1, + color: { red: 0, green: 255, blue: 0 }, + damping: 0, + lifetime: (lengthOfRide * 60) + 1 + }); + +function rideWithParticle() { + + if (iteration <= lengthOfRide) { + + // Check to see if we've been notified of the actual identity of the particles we created + if (!particleA.isKnownID) { + particleA = Particles.identifyParticle(particleA); + } + + var propertiesA = Particles.getParticleProperties(particleA); + var newPosition = propertiesA.position; + MyAvatar.position = { x: propertiesA.position.x, + y: propertiesA.position.y + 2, + z: propertiesA.position.z }; + } else { + Agent.stop(); + } + + iteration++; + print("iteration="+iteration); +} + + +// register the call back so it fires before each data send +Agent.willSendVisualDataCallback.connect(rideWithParticle); + From da5e0f55b6d8bf4ed9b53860599f03928f8cebd5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 30 Jan 2014 12:26:50 -0800 Subject: [PATCH 4/4] add easier support for key codes in JS by adding text property and auto-detecting isShifted --- examples/controllerExample.js | 101 +++++++++++++- libraries/script-engine/src/EventTypes.cpp | 155 ++++++++++++++++++++- libraries/script-engine/src/EventTypes.h | 11 +- 3 files changed, 260 insertions(+), 7 deletions(-) diff --git a/examples/controllerExample.js b/examples/controllerExample.js index c1b33b24a5..95561dc9dc 100644 --- a/examples/controllerExample.js +++ b/examples/controllerExample.js @@ -48,6 +48,15 @@ function checkController() { function keyPressEvent(event) { print("keyPressEvent event.key=" + event.key); + print("keyPressEvent event.text=" + event.text); + + print("keyPressEvent event.isShifted=" + event.isShifted); + print("keyPressEvent event.isControl=" + event.isControl); + print("keyPressEvent event.isMeta=" + event.isMeta); + print("keyPressEvent event.isAlt=" + event.isAlt); + print("keyPressEvent event.isKeypad=" + event.isKeypad); + + if (event.key == "A".charCodeAt(0)) { print("the A key was pressed"); } @@ -77,14 +86,102 @@ Agent.willSendVisualDataCallback.connect(checkController); // Map keyPress and mouse move events to our callbacks Controller.keyPressEvent.connect(keyPressEvent); -var AKeyEvent = { +var KeyEvent_A = { key: "A".charCodeAt(0), + text: "A", isShifted: false, isMeta: false }; +var KeyEvent_a = { + text: "a", + isShifted: false, + isMeta: false +}; + +var KeyEvent_a2 = { + key: "a".charCodeAt(0), + isShifted: false, + isMeta: false +}; + +var KeyEvent_a3 = { + text: "a" +}; + +var KeyEvent_A2 = { + text: "A" +}; + + +var KeyEvent_9 = { + text: "9" +}; + +var KeyEvent_Num = { + text: "#" +}; + +var KeyEvent_At = { + text: "@" +}; + +var KeyEvent_MetaAt = { + text: "@", + isMeta: true +}; + +var KeyEvent_Up = { + text: "up" +}; +var KeyEvent_Down = { + text: "down" +}; +var KeyEvent_Left = { + text: "left" +}; +var KeyEvent_Right = { + text: "right" +}; + // prevent the A key from going through to the application -Controller.captureKeyEvents(AKeyEvent); +print("KeyEvent_A"); +Controller.captureKeyEvents(KeyEvent_A); + +print("KeyEvent_A2"); +Controller.captureKeyEvents(KeyEvent_A2); + +print("KeyEvent_a"); +Controller.captureKeyEvents(KeyEvent_a); + +print("KeyEvent_a2"); +Controller.captureKeyEvents(KeyEvent_a2); + +print("KeyEvent_a3"); +Controller.captureKeyEvents(KeyEvent_a3); + +print("KeyEvent_9"); +Controller.captureKeyEvents(KeyEvent_9); + +print("KeyEvent_Num"); +Controller.captureKeyEvents(KeyEvent_Num); + +print("KeyEvent_At"); +Controller.captureKeyEvents(KeyEvent_At); + +print("KeyEvent_MetaAt"); +Controller.captureKeyEvents(KeyEvent_MetaAt); + +print("KeyEvent_Up"); +Controller.captureKeyEvents(KeyEvent_Up); +print("KeyEvent_Down"); +Controller.captureKeyEvents(KeyEvent_Down); +print("KeyEvent_Left"); +Controller.captureKeyEvents(KeyEvent_Left); +print("KeyEvent_Right"); +Controller.captureKeyEvents(KeyEvent_Right); + + Controller.mouseMoveEvent.connect(mouseMoveEvent); diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp index 5c4ab7f2a7..ff27282b73 100644 --- a/libraries/script-engine/src/EventTypes.cpp +++ b/libraries/script-engine/src/EventTypes.cpp @@ -8,22 +8,72 @@ // Used to register meta-types with Qt for very various event types so that they can be exposed to our // scripting engine +#include #include "EventTypes.h" KeyEvent::KeyEvent() { key = 0; + text = QString(""); isShifted = false; isMeta = false; + isControl = false; isValid = false; } KeyEvent::KeyEvent(const QKeyEvent& event) { key = event.key(); + text = event.text(); isShifted = event.modifiers().testFlag(Qt::ShiftModifier); - isMeta = event.modifiers().testFlag(Qt::ControlModifier); + isMeta = event.modifiers().testFlag(Qt::MetaModifier); + isControl = event.modifiers().testFlag(Qt::ControlModifier); + isAlt = event.modifiers().testFlag(Qt::AltModifier); + isKeypad = event.modifiers().testFlag(Qt::KeypadModifier); isValid = true; + + // handle special text for special characters... + if (key == Qt::Key_F1) { + text = "F1"; + } else if (key == Qt::Key_F2) { + text = "F2"; + } else if (key == Qt::Key_F3) { + text = "F3"; + } else if (key == Qt::Key_F4) { + text = "F4"; + } else if (key == Qt::Key_F5) { + text = "F5"; + } else if (key == Qt::Key_F6) { + text = "F6"; + } else if (key == Qt::Key_F7) { + text = "F7"; + } else if (key == Qt::Key_F8) { + text = "F8"; + } else if (key == Qt::Key_F9) { + text = "F9"; + } else if (key == Qt::Key_F10) { + text = "F10"; + } else if (key == Qt::Key_F11) { + text = "F11"; + } else if (key == Qt::Key_F12) { + text = "F12"; + } else if (key == Qt::Key_Up) { + text = "UP"; + } else if (key == Qt::Key_Down) { + text = "DOWN"; + } else if (key == Qt::Key_Left) { + text = "LEFT"; + } else if (key == Qt::Key_Right) { + text = "RIGHT"; + } else if (key == Qt::Key_Escape) { + text = "ESC"; + } else if (key == Qt::Key_Tab) { + text = "TAB"; + } else if (key == Qt::Key_Delete) { + text = "DELETE"; + } else if (key == Qt::Key_Backspace) { + text = "BACKSPACE"; + } } MouseEvent::MouseEvent(const QMouseEvent& event) { @@ -65,16 +115,113 @@ void registerEventTypes(QScriptEngine* engine) { QScriptValue keyEventToScriptValue(QScriptEngine* engine, const KeyEvent& event) { QScriptValue obj = engine->newObject(); obj.setProperty("key", event.key); + obj.setProperty("text", event.text); obj.setProperty("isShifted", event.isShifted); obj.setProperty("isMeta", event.isMeta); + obj.setProperty("isControl", event.isControl); + obj.setProperty("isAlt", event.isAlt); + obj.setProperty("isKeypad", event.isKeypad); return obj; } void keyEventFromScriptValue(const QScriptValue &object, KeyEvent& event) { - event.key = object.property("key").toVariant().toInt(); - event.isShifted = object.property("isShifted").toVariant().toBool(); + + event.isValid = false; // assume the worst event.isMeta = object.property("isMeta").toVariant().toBool(); - event.isValid = object.property("key").isValid(); + event.isControl = object.property("isControl").toVariant().toBool(); + event.isAlt = object.property("isAlt").toVariant().toBool(); + event.isKeypad = object.property("isKeypad").toVariant().toBool(); + + QScriptValue key = object.property("key"); + if (key.isValid()) { + event.key = key.toVariant().toInt(); + event.text = QString(QChar(event.key)); + event.isValid = true; + } else { + QScriptValue text = object.property("text"); + if (text.isValid()) { + event.text = object.property("text").toVariant().toString(); + + // if the text is a special command, then map it here... + // TODO: come up with more elegant solution here, a map? is there a Qt function that gives nice names for keys? + if (event.text.toUpper() == "F1") { + event.key = Qt::Key_F1; + } else if (event.text.toUpper() == "F2") { + event.key = Qt::Key_F2; + } else if (event.text.toUpper() == "F3") { + event.key = Qt::Key_F3; + } else if (event.text.toUpper() == "F4") { + event.key = Qt::Key_F4; + } else if (event.text.toUpper() == "F5") { + event.key = Qt::Key_F5; + } else if (event.text.toUpper() == "F6") { + event.key = Qt::Key_F6; + } else if (event.text.toUpper() == "F7") { + event.key = Qt::Key_F7; + } else if (event.text.toUpper() == "F8") { + event.key = Qt::Key_F8; + } else if (event.text.toUpper() == "F9") { + event.key = Qt::Key_F9; + } else if (event.text.toUpper() == "F10") { + event.key = Qt::Key_F10; + } else if (event.text.toUpper() == "F11") { + event.key = Qt::Key_F11; + } else if (event.text.toUpper() == "F12") { + event.key = Qt::Key_F12; + } else if (event.text.toUpper() == "UP") { + event.key = Qt::Key_Up; + event.isKeypad = true; + } else if (event.text.toUpper() == "DOWN") { + event.key = Qt::Key_Down; + event.isKeypad = true; + } else if (event.text.toUpper() == "LEFT") { + event.key = Qt::Key_Left; + event.isKeypad = true; + } else if (event.text.toUpper() == "RIGHT") { + event.key = Qt::Key_Right; + event.isKeypad = true; + } else if (event.text.toUpper() == "ESC") { + event.key = Qt::Key_Escape; + } else if (event.text.toUpper() == "TAB") { + event.key = Qt::Key_Tab; + } else if (event.text.toUpper() == "DELETE") { + event.key = Qt::Key_Delete; + } else if (event.text.toUpper() == "BACKSPACE") { + event.key = Qt::Key_Backspace; + } else { + event.key = event.text.at(0).unicode(); + } + event.isValid = true; + } + } + + QScriptValue isShifted = object.property("isShifted"); + if (isShifted.isValid()) { + event.isShifted = isShifted.toVariant().toBool(); + } else { + // if no isShifted was included, get it from the text + QChar character = event.text.at(0); + if (character.isLetter() && character.isUpper()) { + event.isShifted = true; + } else { + // if it's a symbol, then attempt to detect shifted-ness + if (QString("~!@#$%^&*()_+{}|:\"<>?").contains(character)) { + event.isShifted = true; + } + } + } + + + const bool wantDebug = false; + if (wantDebug) { + qDebug() << "event.key=" << event.key + << " event.text=" << event.text + << " event.isShifted=" << event.isShifted + << " event.isControl=" << event.isControl + << " event.isMeta=" << event.isMeta + << " event.isAlt=" << event.isAlt + << " event.isKeypad=" << event.isKeypad; + } } QScriptValue mouseEventToScriptValue(QScriptEngine* engine, const MouseEvent& event) { diff --git a/libraries/script-engine/src/EventTypes.h b/libraries/script-engine/src/EventTypes.h index c3764b2619..81cba03cea 100644 --- a/libraries/script-engine/src/EventTypes.h +++ b/libraries/script-engine/src/EventTypes.h @@ -24,10 +24,19 @@ public: KeyEvent(); KeyEvent(const QKeyEvent& event); inline bool operator==(const KeyEvent& other) const { - return other.key == key && other.isShifted == isShifted && other.isMeta == isMeta; } + return other.key == key + && other.isShifted == isShifted + && other.isControl == isControl + && other.isMeta == isMeta + && other.isAlt == isAlt + && other.isKeypad == isKeypad; } int key; + QString text; bool isShifted; + bool isControl; bool isMeta; + bool isAlt; + bool isKeypad; bool isValid; };