mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 13:43:49 +02:00
support for global collision callbacks in JS
This commit is contained in:
parent
616cfac293
commit
08b06cc59c
21 changed files with 448 additions and 241 deletions
133
examples/gameoflife.js
Normal file
133
examples/gameoflife.js
Normal 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");
|
26
examples/globalCollisionsExample.js
Normal file
26
examples/globalCollisionsExample.js
Normal 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);
|
|
@ -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'); " +
|
||||
" } " +
|
||||
|
|
|
@ -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'); " +
|
||||
" } " +
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(PACKET_TYPE packetType, ParticleID particleID, const ParticleProperties& properties);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <PacketHeaders.h>
|
||||
#include <UUID.h>
|
||||
#include <VoxelConstants.h>
|
||||
#include <VoxelDetail.h>
|
||||
#include <ParticlesScriptingInterface.h>
|
||||
|
||||
#include <Sound.h>
|
||||
|
@ -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<QVector<ParticleID> >(&_engine);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
37
libraries/voxels/src/VoxelDetail.cpp
Normal file
37
libraries/voxels/src/VoxelDetail.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
|
36
libraries/voxels/src/VoxelDetail.h
Normal file
36
libraries/voxels/src/VoxelDetail.h
Normal 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__) */
|
|
@ -14,6 +14,106 @@
|
|||
#include <PacketHeaders.h>
|
||||
#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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -91,5 +91,4 @@ protected:
|
|||
nodeColor _currentColor; /// Client only, false color of this voxel, 4 bytes
|
||||
};
|
||||
|
||||
|
||||
#endif /* defined(__hifi__VoxelTreeElement__) */
|
|
@ -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__) */
|
||||
|
|
Loading…
Reference in a new issue