mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-24 12:53:50 +02:00
Merge pull request #3715 from ZappoMan/entityScriptsAndEvents
Entity Scripts
This commit is contained in:
commit
d3372537c5
14 changed files with 588 additions and 35 deletions
38
examples/entityScripts/changeColorOnHover.js
Normal file
38
examples/entityScripts/changeColorOnHover.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
// changeColorOnHover.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 11/1/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will
|
||||||
|
// change the color of the entity when you hover over it. This script uses the JavaScript prototype/class functionality
|
||||||
|
// to construct an object that has methods for hoverEnterEntity and hoverLeaveEntity;
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
this.oldColor = {};
|
||||||
|
this.oldColorKnown = false;
|
||||||
|
this.storeOldColor = function(entityID) {
|
||||||
|
var oldProperties = Entities.getEntityProperties(entityID);
|
||||||
|
this.oldColor = oldProperties.color;
|
||||||
|
this.oldColorKnown = true;
|
||||||
|
print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
|
||||||
|
};
|
||||||
|
this.hoverEnterEntity = function(entityID, mouseEvent) {
|
||||||
|
if (!this.oldColorKnown) {
|
||||||
|
this.storeOldColor(entityID);
|
||||||
|
}
|
||||||
|
Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} });
|
||||||
|
};
|
||||||
|
this.hoverLeaveEntity = function(entityID, mouseEvent) {
|
||||||
|
if (this.oldColorKnown) {
|
||||||
|
print("leave restoring old color... this.oldColor="
|
||||||
|
+ this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue);
|
||||||
|
Entities.editEntity(entityID, { color: this.oldColor });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
57
examples/entityScripts/crazylegsOnClick.js
Normal file
57
examples/entityScripts/crazylegsOnClick.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
//
|
||||||
|
// crazylegsOnClick.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 11/3/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is an example of an entity script which when assigned to an entity, that entity will make your avatar do the
|
||||||
|
// crazyLegs dance if you click on it.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
var cumulativeTime = 0.0;
|
||||||
|
var FREQUENCY = 5.0;
|
||||||
|
var AMPLITUDE = 45.0;
|
||||||
|
var jointList = MyAvatar.getJointNames();
|
||||||
|
var jointMappings = "\n# Joint list start";
|
||||||
|
for (var i = 0; i < jointList.length; i++) {
|
||||||
|
jointMappings = jointMappings + "\njointIndex = " + jointList[i] + " = " + i;
|
||||||
|
}
|
||||||
|
print(jointMappings + "\n# Joint list end");
|
||||||
|
|
||||||
|
this.crazyLegsUpdate = function(deltaTime) {
|
||||||
|
print("crazyLegsUpdate... deltaTime:" + deltaTime);
|
||||||
|
cumulativeTime += deltaTime;
|
||||||
|
print("crazyLegsUpdate... cumulativeTime:" + cumulativeTime);
|
||||||
|
MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY), 0.0, 0.0));
|
||||||
|
MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees(-AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY), 0.0, 0.0));
|
||||||
|
MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees(
|
||||||
|
AMPLITUDE * (1.0 + Math.sin(cumulativeTime * FREQUENCY)),0.0, 0.0));
|
||||||
|
MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees(
|
||||||
|
AMPLITUDE * (1.0 - Math.sin(cumulativeTime * FREQUENCY)),0.0, 0.0));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stopCrazyLegs = function() {
|
||||||
|
MyAvatar.clearJointData("RightUpLeg");
|
||||||
|
MyAvatar.clearJointData("LeftUpLeg");
|
||||||
|
MyAvatar.clearJointData("RightLeg");
|
||||||
|
MyAvatar.clearJointData("LeftLeg");
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clickDownOnEntity = function(entityID, mouseEvent) {
|
||||||
|
print("clickDownOnEntity()...");
|
||||||
|
cumulativeTime = 0.0;
|
||||||
|
Script.update.connect(this.crazyLegsUpdate);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.clickReleaseOnEntity = function(entityID, mouseEvent) {
|
||||||
|
print("clickReleaseOnEntity()...");
|
||||||
|
this.stopCrazyLegs();
|
||||||
|
Script.update.disconnect(this.crazyLegsUpdate);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
24
examples/entityScripts/playSoundOnClick.js
Normal file
24
examples/entityScripts/playSoundOnClick.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// playSoundOnClick.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 11/3/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is an example of an entity script which when assigned to an entity, that entity will play a sound in world when
|
||||||
|
// you click on it.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw");
|
||||||
|
this.clickDownOnEntity = function(entityID, mouseEvent) {
|
||||||
|
print("clickDownOnEntity()...");
|
||||||
|
var options = new AudioInjectionOptions();
|
||||||
|
var position = MyAvatar.position;
|
||||||
|
options.position = position;
|
||||||
|
options.volume = 0.5;
|
||||||
|
Audio.playSound(bird, options);
|
||||||
|
};
|
||||||
|
})
|
18
examples/entityScripts/teleportOnClick.js
Normal file
18
examples/entityScripts/teleportOnClick.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// teleportOnClick.js
|
||||||
|
// examples/entityScripts
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 11/3/14.
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// This is an example of an entity script which when assigned to an entity, that entity will teleport your avatar if you
|
||||||
|
// click on it the entity.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
(function(){
|
||||||
|
this.clickDownOnEntity = function(entityID, mouseEvent) {
|
||||||
|
MyAvatar.position = Entities.getEntityProperties(entityID).position;
|
||||||
|
};
|
||||||
|
})
|
|
@ -140,6 +140,9 @@ EntityPropertyDialogBox = (function () {
|
||||||
|
|
||||||
array.push({ label: "Visible:", value: properties.visible });
|
array.push({ label: "Visible:", value: properties.visible });
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
|
array.push({ label: "Script:", value: properties.script });
|
||||||
|
index++;
|
||||||
|
|
||||||
if (properties.type == "Box" || properties.type == "Sphere") {
|
if (properties.type == "Box" || properties.type == "Sphere") {
|
||||||
array.push({ label: "Color:", type: "header" });
|
array.push({ label: "Color:", type: "header" });
|
||||||
|
@ -282,6 +285,7 @@ EntityPropertyDialogBox = (function () {
|
||||||
|
|
||||||
properties.lifetime = array[index++].value;
|
properties.lifetime = array[index++].value;
|
||||||
properties.visible = array[index++].value;
|
properties.visible = array[index++].value;
|
||||||
|
properties.script = array[index++].value;
|
||||||
|
|
||||||
if (properties.type == "Box" || properties.type == "Sphere") {
|
if (properties.type == "Box" || properties.type == "Sphere") {
|
||||||
index++; // skip header
|
index++; // skip header
|
||||||
|
|
|
@ -148,7 +148,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_voxelImporter(),
|
_voxelImporter(),
|
||||||
_importSucceded(false),
|
_importSucceded(false),
|
||||||
_sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard),
|
_sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard),
|
||||||
_entityClipboardRenderer(),
|
_entities(true),
|
||||||
|
_entityCollisionSystem(),
|
||||||
|
_entityClipboardRenderer(false),
|
||||||
_entityClipboard(),
|
_entityClipboard(),
|
||||||
_wantToKillLocalVoxels(false),
|
_wantToKillLocalVoxels(false),
|
||||||
_viewFrustum(),
|
_viewFrustum(),
|
||||||
|
@ -1246,6 +1248,8 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
if (_lastMouseMoveWasSimulated) {
|
if (_lastMouseMoveWasSimulated) {
|
||||||
showMouse = false;
|
showMouse = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_entities.mouseMoveEvent(event, deviceID);
|
||||||
|
|
||||||
_controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
|
_controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
|
||||||
|
|
||||||
|
@ -1267,6 +1271,9 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
|
||||||
|
_entities.mousePressEvent(event, deviceID);
|
||||||
|
|
||||||
_controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts
|
_controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts
|
||||||
|
|
||||||
// if one of our scripts have asked to capture this event, then stop processing it
|
// if one of our scripts have asked to capture this event, then stop processing it
|
||||||
|
@ -1304,6 +1311,9 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
|
||||||
|
_entities.mouseReleaseEvent(event, deviceID);
|
||||||
|
|
||||||
_controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts
|
_controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts
|
||||||
|
|
||||||
// if one of our scripts have asked to capture this event, then stop processing it
|
// if one of our scripts have asked to capture this event, then stop processing it
|
||||||
|
@ -1937,6 +1947,10 @@ void Application::init() {
|
||||||
connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithEntity,
|
connect(&_entityCollisionSystem, &EntityCollisionSystem::entityCollisionWithEntity,
|
||||||
ScriptEngine::getEntityScriptingInterface(), &EntityScriptingInterface::entityCollisionWithEntity);
|
ScriptEngine::getEntityScriptingInterface(), &EntityScriptingInterface::entityCollisionWithEntity);
|
||||||
|
|
||||||
|
// connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing
|
||||||
|
// of events related clicking, hovering over, and entering entities
|
||||||
|
_entities.connectSignalsToSlots(ScriptEngine::getEntityScriptingInterface());
|
||||||
|
|
||||||
_entityClipboardRenderer.init();
|
_entityClipboardRenderer.init();
|
||||||
_entityClipboardRenderer.setViewFrustum(getViewFrustum());
|
_entityClipboardRenderer.setViewFrustum(getViewFrustum());
|
||||||
_entityClipboardRenderer.setTree(&_entityClipboard);
|
_entityClipboardRenderer.setTree(&_entityClipboard);
|
||||||
|
@ -3816,35 +3830,7 @@ void joystickFromScriptValue(const QScriptValue &object, Joystick* &out) {
|
||||||
out = qobject_cast<Joystick*>(object.toQObject());
|
out = qobject_cast<Joystick*>(object.toQObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
|
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {
|
||||||
bool loadScriptFromEditor, bool activateMainWindow) {
|
|
||||||
QUrl scriptUrl(scriptFilename);
|
|
||||||
const QString& scriptURLString = scriptUrl.toString();
|
|
||||||
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
|
|
||||||
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
|
|
||||||
|
|
||||||
return _scriptEnginesHash[scriptURLString];
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptEngine* scriptEngine;
|
|
||||||
if (scriptFilename.isNull()) {
|
|
||||||
scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
|
|
||||||
} else {
|
|
||||||
// start the script on a new thread...
|
|
||||||
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
|
|
||||||
|
|
||||||
if (!scriptEngine->hasScript()) {
|
|
||||||
qDebug() << "Application::loadScript(), script failed to load...";
|
|
||||||
QMessageBox::warning(getWindow(), "Error Loading Script", scriptURLString + " failed to load.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_scriptEnginesHash.insertMulti(scriptURLString, scriptEngine);
|
|
||||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
|
||||||
UserActivityLogger::getInstance().loadedScript(scriptURLString);
|
|
||||||
}
|
|
||||||
scriptEngine->setUserLoaded(isUserLoaded);
|
|
||||||
|
|
||||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||||
// we can use the same ones from the application.
|
// we can use the same ones from the application.
|
||||||
scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
|
scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
|
||||||
|
@ -3926,6 +3912,38 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
|
||||||
|
|
||||||
// Starts an event loop, and emits workerThread->started()
|
// Starts an event loop, and emits workerThread->started()
|
||||||
workerThread->start();
|
workerThread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
|
||||||
|
bool loadScriptFromEditor, bool activateMainWindow) {
|
||||||
|
QUrl scriptUrl(scriptFilename);
|
||||||
|
const QString& scriptURLString = scriptUrl.toString();
|
||||||
|
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
|
||||||
|
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
|
||||||
|
|
||||||
|
return _scriptEnginesHash[scriptURLString];
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptEngine* scriptEngine;
|
||||||
|
if (scriptFilename.isNull()) {
|
||||||
|
scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
|
||||||
|
} else {
|
||||||
|
// start the script on a new thread...
|
||||||
|
scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
|
||||||
|
|
||||||
|
if (!scriptEngine->hasScript()) {
|
||||||
|
qDebug() << "Application::loadScript(), script failed to load...";
|
||||||
|
QMessageBox::warning(getWindow(), "Error Loading Script", scriptURLString + " failed to load.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_scriptEnginesHash.insertMulti(scriptURLString, scriptEngine);
|
||||||
|
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||||
|
UserActivityLogger::getInstance().loadedScript(scriptURLString);
|
||||||
|
}
|
||||||
|
scriptEngine->setUserLoaded(isUserLoaded);
|
||||||
|
|
||||||
|
registerScriptEngineWithApplicationServices(scriptEngine);
|
||||||
|
|
||||||
// restore the main window's active state
|
// restore the main window's active state
|
||||||
if (activateMainWindow && !loadScriptFromEditor) {
|
if (activateMainWindow && !loadScriptFromEditor) {
|
||||||
|
|
|
@ -310,6 +310,8 @@ public:
|
||||||
bool isVSyncEditable() const;
|
bool isVSyncEditable() const;
|
||||||
|
|
||||||
|
|
||||||
|
void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
/// Fired when we're simulating; allows external parties to hook in.
|
/// Fired when we're simulating; allows external parties to hook in.
|
||||||
|
|
|
@ -11,19 +11,25 @@
|
||||||
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <QScriptSyntaxCheckResult>
|
||||||
|
|
||||||
#include <FBXReader.h>
|
#include <FBXReader.h>
|
||||||
|
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
|
|
||||||
#include <BoxEntityItem.h>
|
#include <BoxEntityItem.h>
|
||||||
#include <ModelEntityItem.h>
|
#include <ModelEntityItem.h>
|
||||||
|
#include <MouseEvent.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <RenderArgs.h>
|
#include <RenderArgs.h>
|
||||||
|
|
||||||
|
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
|
#include "NetworkAccessManager.h"
|
||||||
#include "EntityTreeRenderer.h"
|
#include "EntityTreeRenderer.h"
|
||||||
|
|
||||||
|
#include "devices/OculusManager.h"
|
||||||
|
|
||||||
#include "RenderableBoxEntityItem.h"
|
#include "RenderableBoxEntityItem.h"
|
||||||
#include "RenderableLightEntityItem.h"
|
#include "RenderableLightEntityItem.h"
|
||||||
#include "RenderableModelEntityItem.h"
|
#include "RenderableModelEntityItem.h"
|
||||||
|
@ -36,15 +42,21 @@ QThread* EntityTreeRenderer::getMainThread() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
EntityTreeRenderer::EntityTreeRenderer() :
|
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts) :
|
||||||
OctreeRenderer() {
|
OctreeRenderer(),
|
||||||
|
_wantScripts(wantScripts),
|
||||||
|
_entitiesScriptEngine(NULL) {
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory)
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::factory)
|
||||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
|
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
|
||||||
|
|
||||||
|
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||||
|
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTreeRenderer::~EntityTreeRenderer() {
|
EntityTreeRenderer::~EntityTreeRenderer() {
|
||||||
|
// do we need to delete the _entitiesScriptEngine?? or is it deleted by default
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::clear() {
|
void EntityTreeRenderer::clear() {
|
||||||
|
@ -54,6 +66,112 @@ void EntityTreeRenderer::clear() {
|
||||||
void EntityTreeRenderer::init() {
|
void EntityTreeRenderer::init() {
|
||||||
OctreeRenderer::init();
|
OctreeRenderer::init();
|
||||||
static_cast<EntityTree*>(_tree)->setFBXService(this);
|
static_cast<EntityTree*>(_tree)->setFBXService(this);
|
||||||
|
|
||||||
|
if (_wantScripts) {
|
||||||
|
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities",
|
||||||
|
Application::getInstance()->getControllerScriptingInterface());
|
||||||
|
Application::getInstance()->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {
|
||||||
|
EntityItem* entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
|
||||||
|
return loadEntityScript(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText) {
|
||||||
|
QUrl url(scriptMaybeURLorText);
|
||||||
|
|
||||||
|
// If the url is not valid, this must be script text...
|
||||||
|
if (!url.isValid()) {
|
||||||
|
return scriptMaybeURLorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString scriptContents; // assume empty
|
||||||
|
|
||||||
|
// if the scheme length is one or lower, maybe they typed in a file, let's try
|
||||||
|
const int WINDOWS_DRIVE_LETTER_SIZE = 1;
|
||||||
|
if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
||||||
|
url = QUrl::fromLocalFile(scriptMaybeURLorText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok, let's see if it's valid... and if so, load it
|
||||||
|
if (url.isValid()) {
|
||||||
|
if (url.scheme() == "file") {
|
||||||
|
QString fileName = url.toLocalFile();
|
||||||
|
QFile scriptFile(fileName);
|
||||||
|
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
|
qDebug() << "Loading file:" << fileName;
|
||||||
|
QTextStream in(&scriptFile);
|
||||||
|
scriptContents = in.readAll();
|
||||||
|
} else {
|
||||||
|
qDebug() << "ERROR Loading file:" << fileName;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
|
||||||
|
qDebug() << "Downloading script at" << url;
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) {
|
||||||
|
scriptContents = reply->readAll();
|
||||||
|
} else {
|
||||||
|
qDebug() << "ERROR Loading file:" << url.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scriptContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
||||||
|
if (!entity) {
|
||||||
|
return QScriptValue(); // no entity...
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityItemID entityID = entity->getEntityItemID();
|
||||||
|
if (_entityScripts.contains(entityID)) {
|
||||||
|
EntityScriptDetails details = _entityScripts[entityID];
|
||||||
|
|
||||||
|
// check to make sure our script text hasn't changed on us since we last loaded it
|
||||||
|
if (details.scriptText == entity->getScript()) {
|
||||||
|
return details.scriptObject; // previously loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got here, then we previously loaded a script, but the entity's script value
|
||||||
|
// has changed and so we need to reload it.
|
||||||
|
_entityScripts.remove(entityID);
|
||||||
|
}
|
||||||
|
if (entity->getScript().isEmpty()) {
|
||||||
|
return QScriptValue(); // no script
|
||||||
|
}
|
||||||
|
|
||||||
|
QString scriptContents = loadScriptContents(entity->getScript());
|
||||||
|
|
||||||
|
if (QScriptEngine::checkSyntax(scriptContents).state() != QScriptSyntaxCheckResult::Valid) {
|
||||||
|
qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
|
||||||
|
qDebug() << " INVALID SYNTAX";
|
||||||
|
qDebug() << " SCRIPT:" << scriptContents;
|
||||||
|
return QScriptValue(); // invalid script
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents);
|
||||||
|
|
||||||
|
if (!entityScriptConstructor.isFunction()) {
|
||||||
|
qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
|
||||||
|
qDebug() << " NOT CONSTRUCTOR";
|
||||||
|
qDebug() << " SCRIPT:" << scriptContents;
|
||||||
|
return QScriptValue(); // invalid script
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue entityScriptObject = entityScriptConstructor.construct();
|
||||||
|
EntityScriptDetails newDetails = { entity->getScript(), entityScriptObject };
|
||||||
|
_entityScripts[entityID] = newDetails;
|
||||||
|
|
||||||
|
return entityScriptObject; // newly constructed
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::setTree(Octree* newTree) {
|
void EntityTreeRenderer::setTree(Octree* newTree) {
|
||||||
|
@ -382,4 +500,198 @@ void EntityTreeRenderer::deleteReleasedModels() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PickRay EntityTreeRenderer::computePickRay(float x, float y) {
|
||||||
|
float screenWidth = Application::getInstance()->getGLWidget()->width();
|
||||||
|
float screenHeight = Application::getInstance()->getGLWidget()->height();
|
||||||
|
PickRay result;
|
||||||
|
if (OculusManager::isConnected()) {
|
||||||
|
Camera* camera = Application::getInstance()->getCamera();
|
||||||
|
result.origin = camera->getPosition();
|
||||||
|
Application::getInstance()->getApplicationOverlay().computeOculusPickRay(x / screenWidth, y / screenHeight, result.direction);
|
||||||
|
} else {
|
||||||
|
ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum();
|
||||||
|
viewFrustum->computePickRay(x / screenWidth, y / screenHeight, result.origin, result.direction);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType) {
|
||||||
|
RayToEntityIntersectionResult result;
|
||||||
|
if (_tree) {
|
||||||
|
EntityTree* entityTree = static_cast<EntityTree*>(_tree);
|
||||||
|
|
||||||
|
OctreeElement* element;
|
||||||
|
EntityItem* intersectedEntity = NULL;
|
||||||
|
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
|
||||||
|
(void**)&intersectedEntity, lockType, &result.accurate);
|
||||||
|
if (result.intersects && intersectedEntity) {
|
||||||
|
result.entityID = intersectedEntity->getEntityItemID();
|
||||||
|
result.properties = intersectedEntity->getProperties();
|
||||||
|
result.intersection = ray.origin + (ray.direction * result.distance);
|
||||||
|
result.entity = intersectedEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
|
||||||
|
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
|
||||||
|
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
|
||||||
|
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
|
||||||
|
|
||||||
|
connect(this, &EntityTreeRenderer::clickDownOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity);
|
||||||
|
connect(this, &EntityTreeRenderer::holdingClickOnEntity, entityScriptingInterface, &EntityScriptingInterface::holdingClickOnEntity);
|
||||||
|
connect(this, &EntityTreeRenderer::clickReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickReleaseOnEntity);
|
||||||
|
|
||||||
|
connect(this, &EntityTreeRenderer::hoverEnterEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity);
|
||||||
|
connect(this, &EntityTreeRenderer::hoverOverEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity);
|
||||||
|
connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
QScriptValueList args;
|
||||||
|
args << entityID.toScriptValue(_entitiesScriptEngine);
|
||||||
|
args << MouseEvent(*event, deviceID).toScriptValue(_entitiesScriptEngine);
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent");
|
||||||
|
PickRay ray = computePickRay(event->x(), event->y());
|
||||||
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock);
|
||||||
|
if (rayPickResult.intersects) {
|
||||||
|
//qDebug() << "mousePressEvent over entity:" << rayPickResult.entityID;
|
||||||
|
emit mousePressOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
||||||
|
|
||||||
|
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
||||||
|
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
||||||
|
if (entityScript.property("mousePressOnEntity").isValid()) {
|
||||||
|
entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentClickingOnEntityID = rayPickResult.entityID;
|
||||||
|
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||||
|
if (entityScript.property("clickDownOnEntity").isValid()) {
|
||||||
|
entityScript.property("clickDownOnEntity").call(entityScript, entityScriptArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent");
|
||||||
|
PickRay ray = computePickRay(event->x(), event->y());
|
||||||
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock);
|
||||||
|
if (rayPickResult.intersects) {
|
||||||
|
//qDebug() << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||||
|
emit mouseReleaseOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
||||||
|
|
||||||
|
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
||||||
|
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
||||||
|
if (entityScript.property("mouseReleaseOnEntity").isValid()) {
|
||||||
|
entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
|
||||||
|
// we're releasing the button, then this is considered a clickOn event
|
||||||
|
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||||
|
emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||||
|
|
||||||
|
QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID);
|
||||||
|
QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID);
|
||||||
|
if (currentClickingEntity.property("clickReleaseOnEntity").isValid()) {
|
||||||
|
currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// makes it the unknown ID, we just released so we can't be clicking on anything
|
||||||
|
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent");
|
||||||
|
PickRay ray = computePickRay(event->x(), event->y());
|
||||||
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock);
|
||||||
|
if (rayPickResult.intersects) {
|
||||||
|
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
||||||
|
|
||||||
|
// load the entity script if needed...
|
||||||
|
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
||||||
|
if (entityScript.property("mouseMoveEvent").isValid()) {
|
||||||
|
entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
//qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID;
|
||||||
|
emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
||||||
|
if (entityScript.property("mouseMoveOnEntity").isValid()) {
|
||||||
|
entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the hover logic...
|
||||||
|
|
||||||
|
// if we were previously hovering over an entity, and this new entity is not the same as our previous entity
|
||||||
|
// then we need to send the hover leave.
|
||||||
|
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||||
|
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
||||||
|
|
||||||
|
QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID);
|
||||||
|
|
||||||
|
QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID);
|
||||||
|
if (currentHoverEntity.property("hoverLeaveEntity").isValid()) {
|
||||||
|
currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the new hover entity does not match the previous hover entity then we are entering the new one
|
||||||
|
// this is true if the _currentHoverOverEntityID is known or unknown
|
||||||
|
if (rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||||
|
emit hoverEnterEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
||||||
|
if (entityScript.property("hoverEnterEntity").isValid()) {
|
||||||
|
entityScript.property("hoverEnterEntity").call(entityScript, entityScriptArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and
|
||||||
|
// we should send our hover over event
|
||||||
|
emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
||||||
|
if (entityScript.property("hoverOverEntity").isValid()) {
|
||||||
|
entityScript.property("hoverOverEntity").call(entityScript, entityScriptArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember what we're hovering over
|
||||||
|
_currentHoverOverEntityID = rayPickResult.entityID;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// handle the hover logic...
|
||||||
|
// if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to
|
||||||
|
// send the hover leave for our previous entity
|
||||||
|
if (!_currentHoverOverEntityID.isInvalidID()) {
|
||||||
|
emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event, deviceID));
|
||||||
|
|
||||||
|
QScriptValueList currentHoverEntityArgs = createMouseEventArgs(_currentHoverOverEntityID, event, deviceID);
|
||||||
|
|
||||||
|
QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID);
|
||||||
|
if (currentHoverEntity.property("hoverLeaveEntity").isValid()) {
|
||||||
|
currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have
|
||||||
|
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
||||||
|
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||||
|
emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||||
|
|
||||||
|
QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID);
|
||||||
|
|
||||||
|
QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID);
|
||||||
|
if (currentClickingEntity.property("holdingClickOnEntity").isValid()) {
|
||||||
|
currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <EntityTree.h>
|
#include <EntityTree.h>
|
||||||
|
#include <EntityScriptingInterface.h> // for RayToEntityIntersectionResult
|
||||||
#include <Octree.h>
|
#include <Octree.h>
|
||||||
#include <OctreePacketData.h>
|
#include <OctreePacketData.h>
|
||||||
#include <OctreeRenderer.h>
|
#include <OctreeRenderer.h>
|
||||||
|
@ -26,11 +27,17 @@
|
||||||
|
|
||||||
#include "renderer/Model.h"
|
#include "renderer/Model.h"
|
||||||
|
|
||||||
|
class EntityScriptDetails {
|
||||||
|
public:
|
||||||
|
QString scriptText;
|
||||||
|
QScriptValue scriptObject;
|
||||||
|
};
|
||||||
|
|
||||||
// Generic client side Octree renderer class.
|
// Generic client side Octree renderer class.
|
||||||
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
|
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
EntityTreeRenderer();
|
EntityTreeRenderer(bool wantScripts);
|
||||||
virtual ~EntityTreeRenderer();
|
virtual ~EntityTreeRenderer();
|
||||||
|
|
||||||
virtual Octree* createTree() { return new EntityTree(true); }
|
virtual Octree* createTree() { return new EntityTree(true); }
|
||||||
|
@ -75,9 +82,47 @@ public:
|
||||||
void releaseModel(Model* model);
|
void releaseModel(Model* model);
|
||||||
|
|
||||||
void deleteReleasedModels();
|
void deleteReleasedModels();
|
||||||
|
|
||||||
|
// event handles which may generate entity related events
|
||||||
|
void mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID);
|
||||||
|
void mousePressEvent(QMouseEvent* event, unsigned int deviceID);
|
||||||
|
void mouseMoveEvent(QMouseEvent* event, unsigned int deviceID);
|
||||||
|
|
||||||
|
/// connect our signals to anEntityScriptingInterface for firing of events related clicking,
|
||||||
|
/// hovering over, and entering entities
|
||||||
|
void connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void mousePressOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void mouseMoveOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void mouseReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
|
||||||
|
void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void clickReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
|
||||||
|
void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<Model*> _releasedModels;
|
QList<Model*> _releasedModels;
|
||||||
void renderProxies(const EntityItem* entity, RenderArgs* args);
|
void renderProxies(const EntityItem* entity, RenderArgs* args);
|
||||||
|
PickRay computePickRay(float x, float y);
|
||||||
|
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType);
|
||||||
|
|
||||||
|
EntityItemID _currentHoverOverEntityID;
|
||||||
|
EntityItemID _currentClickingOnEntityID;
|
||||||
|
|
||||||
|
bool _wantScripts;
|
||||||
|
ScriptEngine* _entitiesScriptEngine;
|
||||||
|
|
||||||
|
QScriptValue loadEntityScript(EntityItem* entity);
|
||||||
|
QScriptValue loadEntityScript(const EntityItemID& entityItemID);
|
||||||
|
QString loadScriptContents(const QString& scriptMaybeURLorText);
|
||||||
|
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID);
|
||||||
|
|
||||||
|
QHash<EntityItemID, EntityScriptDetails> _entityScripts;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityTreeRenderer_h
|
#endif // hifi_EntityTreeRenderer_h
|
||||||
|
|
|
@ -124,6 +124,10 @@ void EntityItemID::handleAddEntityResponse(const QByteArray& packet) {
|
||||||
_tokenIDsToIDs[creatorTokenID] = entityID;
|
_tokenIDsToIDs[creatorTokenID] = entityID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QScriptValue EntityItemID::toScriptValue(QScriptEngine* engine) const {
|
||||||
|
return EntityItemIDtoScriptValue(engine, *this);
|
||||||
|
}
|
||||||
|
|
||||||
QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) {
|
QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) {
|
||||||
QScriptValue obj = engine->newObject();
|
QScriptValue obj = engine->newObject();
|
||||||
obj.setProperty("id", id.id.toString());
|
obj.setProperty("id", id.id.toString());
|
||||||
|
|
|
@ -49,11 +49,16 @@ public:
|
||||||
EntityItemID convertToKnownIDVersion() const;
|
EntityItemID convertToKnownIDVersion() const;
|
||||||
EntityItemID convertToCreatorTokenVersion() const;
|
EntityItemID convertToCreatorTokenVersion() const;
|
||||||
|
|
||||||
|
bool isInvalidID() const { return id == UNKNOWN_ENTITY_ID && creatorTokenID == UNKNOWN_ENTITY_TOKEN && isKnownID == false; }
|
||||||
|
|
||||||
// these methods allow you to create models, and later edit them.
|
// these methods allow you to create models, and later edit them.
|
||||||
|
static EntityItemID createInvalidEntityID() { return EntityItemID(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false); }
|
||||||
static EntityItemID getIDfromCreatorTokenID(uint32_t creatorTokenID);
|
static EntityItemID getIDfromCreatorTokenID(uint32_t creatorTokenID);
|
||||||
static uint32_t getNextCreatorTokenID();
|
static uint32_t getNextCreatorTokenID();
|
||||||
static void handleAddEntityResponse(const QByteArray& packet);
|
static void handleAddEntityResponse(const QByteArray& packet);
|
||||||
static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead);
|
static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead);
|
||||||
|
|
||||||
|
QScriptValue toScriptValue(QScriptEngine* engine) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class EntityTree;
|
friend class EntityTree;
|
||||||
|
@ -68,12 +73,22 @@ inline bool operator<(const EntityItemID& a, const EntityItemID& b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const EntityItemID& a, const EntityItemID& b) {
|
inline bool operator==(const EntityItemID& a, const EntityItemID& b) {
|
||||||
|
if (a.isInvalidID() && b.isInvalidID()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a.isInvalidID() != b.isInvalidID()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (a.id == UNKNOWN_ENTITY_ID || b.id == UNKNOWN_ENTITY_ID) {
|
if (a.id == UNKNOWN_ENTITY_ID || b.id == UNKNOWN_ENTITY_ID) {
|
||||||
return a.creatorTokenID == b.creatorTokenID;
|
return a.creatorTokenID == b.creatorTokenID;
|
||||||
}
|
}
|
||||||
return a.id == b.id;
|
return a.id == b.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(const EntityItemID& a, const EntityItemID& b) {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
inline uint qHash(const EntityItemID& a, uint seed) {
|
inline uint qHash(const EntityItemID& a, uint seed) {
|
||||||
QUuid temp;
|
QUuid temp;
|
||||||
if (a.id == UNKNOWN_ENTITY_ID) {
|
if (a.id == UNKNOWN_ENTITY_ID) {
|
||||||
|
@ -94,5 +109,4 @@ Q_DECLARE_METATYPE(QVector<EntityItemID>);
|
||||||
QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& properties);
|
QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& properties);
|
||||||
void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties);
|
void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties);
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_EntityItemID_h
|
#endif // hifi_EntityItemID_h
|
||||||
|
|
|
@ -217,7 +217,8 @@ RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
|
||||||
entityID(),
|
entityID(),
|
||||||
properties(),
|
properties(),
|
||||||
distance(0),
|
distance(0),
|
||||||
face()
|
face(),
|
||||||
|
entity(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
|
|
||||||
class EntityTree;
|
class EntityTree;
|
||||||
|
class MouseEvent;
|
||||||
|
|
||||||
|
|
||||||
class RayToEntityIntersectionResult {
|
class RayToEntityIntersectionResult {
|
||||||
|
@ -37,6 +38,7 @@ public:
|
||||||
float distance;
|
float distance;
|
||||||
BoxFace face;
|
BoxFace face;
|
||||||
glm::vec3 intersection;
|
glm::vec3 intersection;
|
||||||
|
EntityItem* entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(RayToEntityIntersectionResult)
|
Q_DECLARE_METATYPE(RayToEntityIntersectionResult)
|
||||||
|
@ -101,6 +103,18 @@ signals:
|
||||||
void entityCollisionWithVoxel(const EntityItemID& entityID, const VoxelDetail& voxel, const CollisionInfo& collision);
|
void entityCollisionWithVoxel(const EntityItemID& entityID, const VoxelDetail& voxel, const CollisionInfo& collision);
|
||||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& collision);
|
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& collision);
|
||||||
|
|
||||||
|
void mousePressOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void mouseMoveOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void mouseReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
|
||||||
|
void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void clickReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
|
||||||
|
void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ public:
|
||||||
|
|
||||||
static QScriptValue toScriptValue(QScriptEngine* engine, const MouseEvent& event);
|
static QScriptValue toScriptValue(QScriptEngine* engine, const MouseEvent& event);
|
||||||
static void fromScriptValue(const QScriptValue& object, MouseEvent& event);
|
static void fromScriptValue(const QScriptValue& object, MouseEvent& event);
|
||||||
|
|
||||||
|
QScriptValue toScriptValue(QScriptEngine* engine) const { return MouseEvent::toScriptValue(engine, *this); }
|
||||||
|
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
|
|
Loading…
Reference in a new issue