From 08b06cc59c048fa8553bab91067dce77cc049bc4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 29 Jan 2014 17:52:17 -0800 Subject: [PATCH] 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__) */