use ScriptEngine in Particle::collideWithXXX() methods so that we get all script interfaces

This commit is contained in:
Brad Hefta-Gaub 2014-01-04 03:16:47 -08:00
parent 3efc1ea410
commit 0df02618ee
5 changed files with 670 additions and 657 deletions

View file

@ -44,6 +44,7 @@
#include <OctalCode.h>
#include <PacketHeaders.h>
#include <PairingHandler.h>
#include <ParticlesScriptingInterface.h>
#include <PerfStat.h>
#include <UUID.h>
#include <VoxelSceneStats.h>
@ -239,6 +240,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
_voxelEditSender.setVoxelServerJurisdictions(&_voxelServerJurisdictions);
_particleEditSender.setServerJurisdictions(&_particleServerJurisdictions);
Particle::setVoxelEditPacketSender(&_voxelEditSender);
Particle::setParticleEditPacketSender(&_particleEditSender);
// For now we're going to set the PPS for outbound packets to be super high, this is
// probably not the right long term solution. But for now, we're going to do this to
// allow you to move a particle around in your hand
@ -1555,6 +1559,10 @@ void Application::shootParticle() {
" 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.queueVoxelDelete(voxelAt.x, voxelAt.y, voxelAt.z, voxelScale); "
" print('Voxels.queueVoxelDelete(' + voxelAt.x + ', ' + voxelAt.y + ', ' + voxelAt.z + ', ' + voxelScale + ')... \\n'); "
" } "
" Particle.collisionWithVoxel.connect(collisionWithVoxel); " );

View file

@ -7,21 +7,25 @@
//
//
#include <QtScript/QScriptEngine>
#include <QtCore/QObject>
#include <Octree.h>
#include <RegisteredMetaTypes.h>
#include <SharedUtil.h> // usecTimestampNow()
#include <Octree.h>
#include <VoxelsScriptingInterface.h>
#include "ParticlesScriptingInterface.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
// headers, but not link to each other, this is essentially what this construct is doing, but would be
// better to add includes to the include path, but not link
#include "../../script-engine/src/ScriptEngine.h"
#include "ParticlesScriptingInterface.h"
#include "Particle.h"
uint32_t Particle::_nextID = 0;
VoxelsScriptingInterface* Particle::_voxelsScriptingInterface = NULL;
ParticlesScriptingInterface* Particle::_particlesScriptingInterface = NULL;
VoxelEditPacketSender* Particle::_voxelEditSender = NULL;
ParticleEditPacketSender* Particle::_particleEditSender = NULL;
Particle::Particle(glm::vec3 position, float radius, rgbColor color, glm::vec3 velocity, glm::vec3 gravity,
@ -532,45 +536,30 @@ void Particle::runUpdateScript() {
void Particle::collisionWithParticle(Particle* other) {
if (!_script.isEmpty()) {
QScriptEngine engine;
ScriptEngine engine(_script); // no menu or controller interface...
// register meta-type for glm::vec3 and rgbColor conversions
registerMetaTypes(&engine);
if (_voxelEditSender) {
engine.getVoxelsScriptingInterface()->setPacketSender(_voxelEditSender);
}
if (_particleEditSender) {
engine.getParticlesScriptingInterface()->setPacketSender(_particleEditSender);
}
// Add the Particle object
ParticleScriptObject particleScriptable(this);
QScriptValue particleValue = engine.newQObject(&particleScriptable);
engine.globalObject().setProperty("Particle", particleValue);
engine.registerGlobalObject("Particle", &particleScriptable);
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE);
if (getVoxelsScriptingInterface()) {
QScriptValue voxelScripterValue = engine.newQObject(getVoxelsScriptingInterface());
engine.globalObject().setProperty("Voxels", voxelScripterValue);
}
if (getParticlesScriptingInterface()) {
QScriptValue particleScripterValue = engine.newQObject(getParticlesScriptingInterface());
engine.globalObject().setProperty("Particles", particleScripterValue);
}
QScriptValue result = engine.evaluate(_script);
// init and evaluate the script, but return so we can emit the collision
engine.evaluate();
ParticleScriptObject otherParticleScriptable(other);
particleScriptable.emitCollisionWithParticle(&otherParticleScriptable);
if (getVoxelsScriptingInterface()) {
getVoxelsScriptingInterface()->getPacketSender()->releaseQueuedMessages();
if (_voxelEditSender) {
_voxelEditSender->releaseQueuedMessages();
}
if (getParticlesScriptingInterface()) {
getParticlesScriptingInterface()->getPacketSender()->releaseQueuedMessages();
}
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
if (_particleEditSender) {
_particleEditSender->releaseQueuedMessages();
}
}
}
@ -578,45 +567,32 @@ void Particle::collisionWithParticle(Particle* other) {
void Particle::collisionWithVoxel(VoxelDetail* voxelDetails) {
if (!_script.isEmpty()) {
QScriptEngine engine;
ScriptEngine engine(_script); // no menu or controller interface...
// register meta-type for glm::vec3 and rgbColor conversions
registerMetaTypes(&engine);
// 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
ParticleScriptObject particleScriptable(this);
QScriptValue particleValue = engine.newQObject(&particleScriptable);
engine.globalObject().setProperty("Particle", particleValue);
engine.registerGlobalObject("Particle", &particleScriptable);
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", TREE_SCALE);
if (getVoxelsScriptingInterface()) {
QScriptValue voxelScripterValue = engine.newQObject(getVoxelsScriptingInterface());
engine.globalObject().setProperty("Voxels", voxelScripterValue);
}
if (getParticlesScriptingInterface()) {
QScriptValue particleScripterValue = engine.newQObject(getParticlesScriptingInterface());
engine.globalObject().setProperty("Particles", particleScripterValue);
}
QScriptValue result = engine.evaluate(_script);
// init and evaluate the script, but return so we can emit the collision
engine.evaluate();
VoxelDetailScriptObject voxelDetailsScriptable(voxelDetails);
particleScriptable.emitCollisionWithVoxel(&voxelDetailsScriptable);
if (getVoxelsScriptingInterface()) {
getVoxelsScriptingInterface()->getPacketSender()->releaseQueuedMessages();
if (_voxelEditSender) {
_voxelEditSender->releaseQueuedMessages();
}
if (getParticlesScriptingInterface()) {
getParticlesScriptingInterface()->getPacketSender()->releaseQueuedMessages();
}
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
if (_particleEditSender) {
_particleEditSender->releaseQueuedMessages();
}
}
}

View file

@ -21,6 +21,8 @@
class VoxelsScriptingInterface;
class ParticlesScriptingInterface;
class VoxelEditPacketSender;
class ParticleEditPacketSender;
const uint32_t NEW_PARTICLE = 0xFFFFFFFF;
@ -122,18 +124,19 @@ public:
// similar to assignment/copy, but it handles keeping lifetime accurate
void copyChangedProperties(const Particle& other);
static VoxelsScriptingInterface* getVoxelsScriptingInterface() { return _voxelsScriptingInterface; }
static ParticlesScriptingInterface* getParticlesScriptingInterface() { return _particlesScriptingInterface; }
static VoxelEditPacketSender* getVoxelEditPacketSender() { return _voxelEditSender; }
static ParticleEditPacketSender* getParticleEditPacketSender() { return _particleEditSender; }
static void setVoxelsScriptingInterface(VoxelsScriptingInterface* interface)
{ _voxelsScriptingInterface = interface; }
static void setVoxelEditPacketSender(VoxelEditPacketSender* interface)
{ _voxelEditSender = interface; }
static void setParticleEditPacketSender(ParticleEditPacketSender* interface)
{ _particleEditSender = interface; }
static void setParticlesScriptingInterface(ParticlesScriptingInterface* interface)
{ _particlesScriptingInterface = interface; }
protected:
static VoxelsScriptingInterface* _voxelsScriptingInterface;
static ParticlesScriptingInterface* _particlesScriptingInterface;
static VoxelEditPacketSender* _voxelEditSender;
static ParticleEditPacketSender* _particleEditSender;
void runUpdateScript();
static QScriptValue vec3toScriptValue(QScriptEngine *engine, const glm::vec3 &vec3);

View file

@ -19,11 +19,14 @@
#include <PacketHeaders.h>
#include <UUID.h>
#include <VoxelConstants.h>
#include <ParticlesScriptingInterface.h>
#include <Sound.h>
#include "ScriptEngine.h"
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
int ScriptEngine::_scriptNumber = 1;
VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface;
ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface;
@ -41,6 +44,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems,
_scriptContents = scriptContents;
_isFinished = false;
_isRunning = false;
_isInitialized = false;
// some clients will use these menu features
_wantMenuItems = wantMenuItems;
@ -54,15 +58,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems,
}
_menu = menu;
_controllerScriptingInterface = controllerScriptingInterface;
// hook up our interfaces
if (!Particle::getVoxelsScriptingInterface()) {
Particle::setVoxelsScriptingInterface(getVoxelsScriptingInterface());
}
if (!Particle::getParticlesScriptingInterface()) {
Particle::setParticlesScriptingInterface(getParticlesScriptingInterface());
}
}
ScriptEngine::~ScriptEngine() {
@ -92,57 +87,83 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents) {
Q_SCRIPT_DECLARE_QMETAOBJECT(AudioInjectorOptions, QObject*)
void ScriptEngine::run() {
_isRunning = true;
QScriptEngine engine;
void ScriptEngine::init() {
if (_isInitialized) {
return; // only initialize once
}
_isInitialized = true;
_voxelsScriptingInterface.init();
_particlesScriptingInterface.init();
// register meta-type for glm::vec3 conversions
registerMetaTypes(&engine);
registerMetaTypes(&_engine);
QScriptValue agentValue = engine.newQObject(this);
engine.globalObject().setProperty("Agent", agentValue);
QScriptValue agentValue = _engine.newQObject(this);
_engine.globalObject().setProperty("Agent", agentValue);
QScriptValue voxelScripterValue = engine.newQObject(&_voxelsScriptingInterface);
engine.globalObject().setProperty("Voxels", voxelScripterValue);
QScriptValue voxelScripterValue = _engine.newQObject(&_voxelsScriptingInterface);
_engine.globalObject().setProperty("Voxels", voxelScripterValue);
QScriptValue particleScripterValue = engine.newQObject(&_particlesScriptingInterface);
engine.globalObject().setProperty("Particles", particleScripterValue);
QScriptValue particleScripterValue = _engine.newQObject(&_particlesScriptingInterface);
_engine.globalObject().setProperty("Particles", particleScripterValue);
QScriptValue soundConstructorValue = _engine.newFunction(soundConstructor);
QScriptValue soundMetaObject = _engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue);
_engine.globalObject().setProperty("Sound", soundMetaObject);
QScriptValue soundConstructorValue = engine.newFunction(soundConstructor);
QScriptValue soundMetaObject = engine.newQMetaObject(&Sound::staticMetaObject, soundConstructorValue);
engine.globalObject().setProperty("Sound", soundMetaObject);
QScriptValue injectionOptionValue = _engine.scriptValueFromQMetaObject<AudioInjectorOptions>();
_engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue);
QScriptValue injectionOptionValue = engine.scriptValueFromQMetaObject<AudioInjectorOptions>();
engine.globalObject().setProperty("AudioInjectionOptions", injectionOptionValue);
QScriptValue audioScriptingInterfaceValue = engine.newQObject(&_audioScriptingInterface);
engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue);
QScriptValue audioScriptingInterfaceValue = _engine.newQObject(&_audioScriptingInterface);
_engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue);
if (_controllerScriptingInterface) {
QScriptValue controllerScripterValue = engine.newQObject(_controllerScriptingInterface);
engine.globalObject().setProperty("Controller", controllerScripterValue);
QScriptValue controllerScripterValue = _engine.newQObject(_controllerScriptingInterface);
_engine.globalObject().setProperty("Controller", controllerScripterValue);
}
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
QScriptValue treeScaleValue = _engine.newVariant(QVariant(TREE_SCALE));
_engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
// let the VoxelPacketSender know how frequently we plan to call it
_voxelsScriptingInterface.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
_particlesScriptingInterface.getParticlePacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
//qDebug() << "Script:\n" << _scriptContents << "\n";
}
QScriptValue result = engine.evaluate(_scriptContents);
void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
QScriptValue value = _engine.newQObject(object);
_engine.globalObject().setProperty(name, value);
}
void ScriptEngine::evaluate() {
if (!_isInitialized) {
init();
}
QScriptValue result = _engine.evaluate(_scriptContents);
qDebug() << "Evaluated script.\n";
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
if (_engine.hasUncaughtException()) {
int line = _engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
}
}
void ScriptEngine::run() {
if (!_isInitialized) {
init();
}
_isRunning = true;
QScriptValue result = _engine.evaluate(_scriptContents);
qDebug() << "Evaluated script.\n";
if (_engine.hasUncaughtException()) {
int line = _engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n";
}
@ -198,9 +219,9 @@ void ScriptEngine::run() {
emit willSendVisualDataCallback();
}
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
if (_engine.hasUncaughtException()) {
int line = _engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << _engine.uncaughtException().toString() << "\n";
}
}
cleanMenuItems();
@ -213,7 +234,6 @@ void ScriptEngine::run() {
emit finished();
_isRunning = false;
}
void ScriptEngine::stop() {
_isFinished = true;
}

View file

@ -17,9 +17,10 @@
#include <AbstractMenuInterface.h>
#include <AudioScriptingInterface.h>
#include <ParticlesScriptingInterface.h>
#include <VoxelsScriptingInterface.h>
class ParticlesScriptingInterface;
#include "AbstractControllerScriptingInterface.h"
const QString NO_SCRIPT("");
@ -45,9 +46,13 @@ public:
void setupMenuItems();
void cleanMenuItems();
void registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name
public slots:
void run();
void init();
void run(); /// runs continuously until Agent.stop() is called
void stop();
void evaluate(); /// initializes the engine, and evaluates the script, but then returns control to caller
signals:
void willSendAudioDataCallback();
@ -57,7 +62,8 @@ protected:
QString _scriptContents;
bool _isFinished;
bool _isRunning;
bool _isInitialized;
QScriptEngine _engine;
private:
static VoxelsScriptingInterface _voxelsScriptingInterface;