Merge pull request #1745 from ZappoMan/scripting_work

support for global collision callbacks in JS
This commit is contained in:
AndrewMeadows 2014-01-31 10:42:44 -08:00
commit bed63513a9
26 changed files with 661 additions and 145 deletions

View file

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

View file

@ -1,5 +1,5 @@
//
// editParticleExample.js
// findParticleExample.js
// hifi
//
// Created by Brad Hefta-Gaub on 1/24/14.

133
examples/gameoflife.js Normal file
View file

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

View file

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

View file

@ -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'); " +
" } " +

View file

@ -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'); " +
" } " +

View file

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

View file

@ -1878,6 +1878,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);

View file

@ -13,6 +13,8 @@
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h> // usecTimestampNow()
#include <VoxelsScriptingInterface.h>
#include <VoxelDetail.h>
// 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
@ -830,7 +832,7 @@ void Particle::update(const quint64& 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) {
@ -852,106 +854,62 @@ void Particle::update(const quint64& 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) {
quint64 ageInUsecs = age * USECS_PER_SECOND;
_created = usecTimestampNow() - ageInUsecs;

View file

@ -20,13 +20,16 @@
#include <SharedUtil.h>
#include <OctreePacketData.h>
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:

View file

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

View file

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

View file

@ -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<ParticleID> 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(PacketType packetType, ParticleID particleID, const ParticleProperties& properties);

View file

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

View file

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

View file

@ -19,6 +19,7 @@
#include <PacketHeaders.h>
#include <UUID.h>
#include <VoxelConstants.h>
#include <VoxelDetail.h>
#include <ParticlesScriptingInterface.h>
#include <Sound.h>
@ -115,10 +116,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<QVector<ParticleID> >(&_engine);

View file

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

View file

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

View file

@ -231,7 +231,6 @@ void sharedMessageHandler(QtMsgType type, const QMessageLogContext& context, con
fprintf(stdout, "%s", message.toLocal8Bit().constData());
}
unsigned char* pointToOctalCode(float x, float y, float z, float s) {
return pointToVoxel(x, y, z, s);
}

View file

@ -96,20 +96,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);
#ifdef _WIN32
void usleep(int waitTime);
#endif

View file

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

View file

@ -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 <QtScript/QScriptEngine>
#include <SharedUtil.h>
#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__) */

View file

@ -70,14 +70,14 @@ bool createVoxelEditMessage(PacketType command, short int sequence,
// 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;
}

View file

@ -12,6 +12,7 @@
#define __shared__VoxelEditPacketSender__
#include <OctreeEditPacketSender.h>
#include "VoxelDetail.h"
/// Utility for processing, packing, queueing and sending of outbound edit voxel messages.
class VoxelEditPacketSender : public OctreeEditPacketSender {

View file

@ -91,5 +91,4 @@ protected:
nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes
};
#endif /* defined(__hifi__VoxelTreeElement__) */

View file

@ -57,20 +57,4 @@ private:
void queueVoxelAdd(PacketType 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__) */