mirror of
https://github.com/lubosz/overte.git
synced 2025-04-23 13:33:38 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
38a04b53d2
27 changed files with 871 additions and 172 deletions
|
@ -96,17 +96,14 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
|
|||
assignmentServerPort =
|
||||
argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt();
|
||||
}
|
||||
|
||||
HifiSockAddr assignmentServerSocket(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, assignmentServerPort);
|
||||
|
||||
// check for an overriden assignment server hostname
|
||||
if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) {
|
||||
_assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString();
|
||||
|
||||
// change the hostname for our assignment server
|
||||
assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerSocket.getPort());
|
||||
_assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString();
|
||||
}
|
||||
|
||||
HifiSockAddr assignmentServerSocket(_assignmentServerHostname, assignmentServerPort, true);
|
||||
nodeList->setAssignmentServerSocket(assignmentServerSocket);
|
||||
|
||||
qDebug() << "Assignment server socket is" << assignmentServerSocket;
|
||||
|
|
|
@ -388,7 +388,7 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000;
|
||||
|
||||
QTimer* dataHeartbeatTimer = new QTimer(this);
|
||||
connect(dataHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToDataServer);
|
||||
connect(dataHeartbeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartbeatToDataServer()));
|
||||
dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS);
|
||||
}
|
||||
|
||||
|
@ -637,7 +637,7 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
|||
}
|
||||
|
||||
if (allowedUsers.count() > 0) {
|
||||
if (allowedUsers.contains(username)) {
|
||||
if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
|
||||
// it's possible this user can be allowed to connect, but we need to check their username signature
|
||||
|
||||
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
||||
|
@ -657,7 +657,7 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
|||
rsaPublicKey, RSA_PKCS1_PADDING);
|
||||
|
||||
if (decryptResult != -1) {
|
||||
if (username == decryptedArray) {
|
||||
if (username.toLower() == decryptedArray) {
|
||||
qDebug() << "Username signature matches for" << username << "- allowing connection.";
|
||||
|
||||
// free up the public key before we return
|
||||
|
@ -1091,10 +1091,10 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) {
|
|||
const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||
|
||||
void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) {
|
||||
updateDomainInDataServer(newPublicSockAddr.getAddress().toString());
|
||||
sendHeartbeatToDataServer(newPublicSockAddr.getAddress().toString());
|
||||
}
|
||||
|
||||
void DomainServer::updateDomainInDataServer(const QString& networkAddress) {
|
||||
void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
|
||||
const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
|
||||
const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID();
|
||||
|
||||
|
@ -1109,6 +1109,21 @@ void DomainServer::updateDomainInDataServer(const QString& networkAddress) {
|
|||
|
||||
domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting;
|
||||
|
||||
// add the number of currently connected agent users
|
||||
int numConnectedAuthedUsers = 0;
|
||||
foreach(const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) {
|
||||
if (node->getLinkedData() && !static_cast<DomainServerNodeData*>(node->getLinkedData())->getUsername().isEmpty()) {
|
||||
++numConnectedAuthedUsers;
|
||||
}
|
||||
}
|
||||
|
||||
const QString DOMAIN_HEARTBEAT_KEY = "heartbeat";
|
||||
const QString HEARTBEAT_NUM_USERS_KEY = "num_users";
|
||||
|
||||
QJsonObject heartbeatObject;
|
||||
heartbeatObject[HEARTBEAT_NUM_USERS_KEY] = numConnectedAuthedUsers;
|
||||
domainObject[DOMAIN_HEARTBEAT_KEY] = heartbeatObject;
|
||||
|
||||
QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson()));
|
||||
|
||||
AccountManager::getInstance().authenticatedRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
|
||||
|
|
|
@ -66,7 +66,7 @@ private slots:
|
|||
void requestCurrentPublicSocketViaSTUN();
|
||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||
void performICEUpdates();
|
||||
void sendHeartbeatToDataServer() { updateDomainInDataServer(); }
|
||||
void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); }
|
||||
void sendHeartbeatToIceServer();
|
||||
void sendICEPingPackets();
|
||||
private:
|
||||
|
@ -77,7 +77,7 @@ private:
|
|||
bool optionallySetupAssignmentPayment();
|
||||
|
||||
void setupAutomaticNetworking();
|
||||
void updateDomainInDataServer(const QString& networkAddress = QString());
|
||||
void sendHeartbeatToDataServer(const QString& networkAddress);
|
||||
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
void processICEHeartbeatResponse(const QByteArray& packet);
|
||||
|
||||
|
|
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);
|
||||
};
|
||||
})
|
32
examples/entityScripts/playSoundOnEnterOrLeave.js
Normal file
32
examples/entityScripts/playSoundOnEnterOrLeave.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// playSoundOnEnterOrLeave.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
|
||||
// your avatar enters or leaves the bounds of 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(){
|
||||
var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw");
|
||||
|
||||
function playSound(entityID) {
|
||||
var options = new AudioInjectionOptions();
|
||||
var position = MyAvatar.position;
|
||||
options.position = position;
|
||||
options.volume = 0.5;
|
||||
Audio.playSound(bird, options);
|
||||
};
|
||||
|
||||
this.enterEntity = function(entityID) {
|
||||
playSound();
|
||||
};
|
||||
|
||||
this.leaveEntity = function(entityID) {
|
||||
playSound();
|
||||
};
|
||||
})
|
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 });
|
||||
index++;
|
||||
|
||||
array.push({ label: "Script:", value: properties.script });
|
||||
index++;
|
||||
|
||||
if (properties.type == "Box" || properties.type == "Sphere") {
|
||||
array.push({ label: "Color:", type: "header" });
|
||||
|
@ -282,6 +285,7 @@ EntityPropertyDialogBox = (function () {
|
|||
|
||||
properties.lifetime = array[index++].value;
|
||||
properties.visible = array[index++].value;
|
||||
properties.script = array[index++].value;
|
||||
|
||||
if (properties.type == "Box" || properties.type == "Sphere") {
|
||||
index++; // skip header
|
||||
|
|
|
@ -107,9 +107,6 @@ static unsigned STARFIELD_SEED = 1;
|
|||
|
||||
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
||||
|
||||
const int IDLE_SIMULATE_MSECS = 16; // How often should call simulate and other stuff
|
||||
// in the idle loop? (60 FPS is default)
|
||||
|
||||
const unsigned MAXIMUM_CACHE_SIZE = 10737418240; // 10GB
|
||||
|
||||
static QTimer* idleTimer = NULL;
|
||||
|
@ -151,7 +148,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_voxelImporter(),
|
||||
_importSucceded(false),
|
||||
_sharedVoxelSystem(TREE_SCALE, DEFAULT_MAX_VOXELS_PER_SYSTEM, &_clipboard),
|
||||
_entityClipboardRenderer(),
|
||||
_entities(true),
|
||||
_entityCollisionSystem(),
|
||||
_entityClipboardRenderer(false),
|
||||
_entityClipboard(),
|
||||
_wantToKillLocalVoxels(false),
|
||||
_viewFrustum(),
|
||||
|
@ -185,9 +184,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
_trayIcon(new QSystemTrayIcon(_window)),
|
||||
_lastNackTime(usecTimestampNow()),
|
||||
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
||||
_renderTargetFramerate(0),
|
||||
_isVSyncOn(true),
|
||||
_renderResolutionScale(1.0f)
|
||||
_isVSyncOn(true)
|
||||
{
|
||||
|
||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||
|
@ -604,7 +601,7 @@ void Application::paintGL() {
|
|||
if (OculusManager::isConnected()) {
|
||||
_textureCache.setFrameBufferSize(OculusManager::getRenderTargetSize());
|
||||
} else {
|
||||
QSize fbSize = _glWidget->getDeviceSize() * _renderResolutionScale;
|
||||
QSize fbSize = _glWidget->getDeviceSize() * getRenderResolutionScale();
|
||||
_textureCache.setFrameBufferSize(fbSize);
|
||||
}
|
||||
|
||||
|
@ -1251,6 +1248,8 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
if (_lastMouseMoveWasSimulated) {
|
||||
showMouse = false;
|
||||
}
|
||||
|
||||
_entities.mouseMoveEvent(event, deviceID);
|
||||
|
||||
_controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
|
||||
|
||||
|
@ -1272,6 +1271,9 @@ void Application::mouseMoveEvent(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
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
|
@ -1309,6 +1311,9 @@ void Application::mousePressEvent(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
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
|
@ -1474,12 +1479,11 @@ void Application::idle() {
|
|||
bool showWarnings = getLogger()->extraDebugging();
|
||||
PerformanceWarning warn(showWarnings, "idle()");
|
||||
|
||||
// Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time we ran
|
||||
// Only run simulation code if more than the targetFramePeriod have passed since last time we ran
|
||||
double targetFramePeriod = 0.0;
|
||||
if (_renderTargetFramerate > 0) {
|
||||
targetFramePeriod = 1000.0 / _renderTargetFramerate;
|
||||
} else if (_renderTargetFramerate < 0) {
|
||||
targetFramePeriod = IDLE_SIMULATE_MSECS;
|
||||
unsigned int targetFramerate = getRenderTargetFramerate();
|
||||
if (targetFramerate > 0) {
|
||||
targetFramePeriod = 1000.0 / targetFramerate;
|
||||
}
|
||||
double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0;
|
||||
if (timeSinceLastUpdate > targetFramePeriod) {
|
||||
|
@ -1943,6 +1947,10 @@ void Application::init() {
|
|||
connect(&_entityCollisionSystem, &EntityCollisionSystem::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.setViewFrustum(getViewFrustum());
|
||||
_entityClipboardRenderer.setTree(&_entityClipboard);
|
||||
|
@ -3179,7 +3187,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
} else {
|
||||
// if not rendering the billboard, the region is in device independent coordinates; must convert to device
|
||||
QSize size = getTextureCache()->getFrameBufferSize();
|
||||
float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * _renderResolutionScale;
|
||||
float ratio = QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale();
|
||||
int x = region.x() * ratio, y = region.y() * ratio, width = region.width() * ratio, height = region.height() * ratio;
|
||||
glViewport(x, size.height() - y - height, width, height);
|
||||
glScissor(x, size.height() - y - height, width, height);
|
||||
|
@ -3822,35 +3830,7 @@ void joystickFromScriptValue(const QScriptValue &object, Joystick* &out) {
|
|||
out = qobject_cast<Joystick*>(object.toQObject());
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {
|
||||
// setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so
|
||||
// we can use the same ones from the application.
|
||||
scriptEngine->getVoxelsScriptingInterface()->setPacketSender(&_voxelEditSender);
|
||||
|
@ -3932,6 +3912,38 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
|
|||
|
||||
// Starts an event loop, and emits workerThread->started()
|
||||
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
|
||||
if (activateMainWindow && !loadScriptFromEditor) {
|
||||
|
@ -4237,37 +4249,56 @@ void Application::takeSnapshot() {
|
|||
_snapshotShareDialog->show();
|
||||
}
|
||||
|
||||
void Application::setRenderTargetFramerate(unsigned int framerate, bool vsyncOn) {
|
||||
if (vsyncOn != _isVSyncOn) {
|
||||
void Application::setVSyncEnabled(bool vsyncOn) {
|
||||
#if defined(Q_OS_WIN)
|
||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||
wglSwapIntervalEXT(vsyncOn);
|
||||
int swapInterval = wglGetSwapIntervalEXT();
|
||||
_isVSyncOn = swapInterval;
|
||||
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
||||
} else {
|
||||
qDebug("V-Sync is FORCED ON on this system\n");
|
||||
}
|
||||
#elif defined(Q_OS_LINUX)
|
||||
// TODO: write the poper code for linux
|
||||
/*
|
||||
if (glQueryExtension.... ("GLX_EXT_swap_control")) {
|
||||
glxSwapIntervalEXT(vsyncOn);
|
||||
int swapInterval = xglGetSwapIntervalEXT();
|
||||
_isVSyncOn = swapInterval;
|
||||
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
||||
} else {
|
||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||
wglSwapIntervalEXT(vsyncOn);
|
||||
int swapInterval = wglGetSwapIntervalEXT();
|
||||
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
||||
} else {
|
||||
qDebug("V-Sync is FORCED ON on this system\n");
|
||||
}
|
||||
*/
|
||||
#else
|
||||
qDebug("V-Sync is FORCED ON on this system\n");
|
||||
#endif
|
||||
}
|
||||
_renderTargetFramerate = framerate;
|
||||
#elif defined(Q_OS_LINUX)
|
||||
// TODO: write the poper code for linux
|
||||
/*
|
||||
if (glQueryExtension.... ("GLX_EXT_swap_control")) {
|
||||
glxSwapIntervalEXT(vsyncOn);
|
||||
int swapInterval = xglGetSwapIntervalEXT();
|
||||
_isVSyncOn = swapInterval;
|
||||
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
||||
} else {
|
||||
qDebug("V-Sync is FORCED ON on this system\n");
|
||||
}
|
||||
*/
|
||||
#else
|
||||
qDebug("V-Sync is FORCED ON on this system\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Application::isVSyncEditable() {
|
||||
bool Application::isVSyncOn() const {
|
||||
#if defined(Q_OS_WIN)
|
||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||
int swapInterval = wglGetSwapIntervalEXT();
|
||||
return (swapInterval > 0);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
#elif defined(Q_OS_LINUX)
|
||||
// TODO: write the poper code for linux
|
||||
/*
|
||||
if (glQueryExtension.... ("GLX_EXT_swap_control")) {
|
||||
int swapInterval = xglGetSwapIntervalEXT();
|
||||
return (swapInterval > 0);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Application::isVSyncEditable() const {
|
||||
#if defined(Q_OS_WIN)
|
||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||
return true;
|
||||
|
@ -4284,6 +4315,33 @@ bool Application::isVSyncEditable() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Application::setRenderResolutionScale(float scale) {
|
||||
_renderResolutionScale = scale;
|
||||
unsigned int Application::getRenderTargetFramerate() const {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateUnlimited)) {
|
||||
return 0;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate60)) {
|
||||
return 60;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate50)) {
|
||||
return 50;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate40)) {
|
||||
return 40;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerate30)) {
|
||||
return 30;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float Application::getRenderResolutionScale() const {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) {
|
||||
return 1.f;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) {
|
||||
return 0.666f;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) {
|
||||
return 0.5f;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionThird)) {
|
||||
return 0.333f;
|
||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) {
|
||||
return 0.25f;
|
||||
} else {
|
||||
return 1.f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -303,7 +303,14 @@ public:
|
|||
|
||||
bool isLookingAtMyAvatar(Avatar* avatar);
|
||||
|
||||
float getRenderResolutionScale() const { return _renderResolutionScale; }
|
||||
float getRenderResolutionScale() const;
|
||||
|
||||
unsigned int getRenderTargetFramerate() const;
|
||||
bool isVSyncOn() const;
|
||||
bool isVSyncEditable() const;
|
||||
|
||||
|
||||
void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -367,12 +374,7 @@ public slots:
|
|||
|
||||
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
||||
|
||||
void setRenderTargetFramerate(unsigned int framerate, bool vsyncOn = true);
|
||||
bool isVSyncOn() { return _isVSyncOn; }
|
||||
bool isVSyncEditable();
|
||||
unsigned int getRenderTargetFramerate() const { return _renderTargetFramerate; }
|
||||
|
||||
void setRenderResolutionScale(float scale);
|
||||
void setVSyncEnabled(bool vsyncOn);
|
||||
|
||||
void resetSensors();
|
||||
|
||||
|
@ -624,9 +626,7 @@ private:
|
|||
quint64 _lastNackTime;
|
||||
quint64 _lastSendDownstreamAudioStats;
|
||||
|
||||
int _renderTargetFramerate;
|
||||
bool _isVSyncOn;
|
||||
float _renderResolutionScale;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -377,13 +377,12 @@ Menu::Menu() :
|
|||
{
|
||||
QMenu* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
|
||||
QActionGroup* framerateGroup = new QActionGroup(framerateMenu);
|
||||
|
||||
framerateGroup->setExclusive(true);
|
||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true));
|
||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false));
|
||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false));
|
||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false));
|
||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false));
|
||||
connect(framerateMenu, SIGNAL(triggered(QAction*)), this, SLOT(changeRenderTargetFramerate(QAction*)));
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#else
|
||||
|
@ -394,12 +393,12 @@ Menu::Menu() :
|
|||
|
||||
QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, false));
|
||||
resolutionGroup->setExclusive(true);
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, true));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
connect(resolutionMenu, SIGNAL(triggered(QAction*)), this, SLOT(changeRenderResolution(QAction*)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu,
|
||||
|
@ -1261,46 +1260,7 @@ void Menu::muteEnvironment() {
|
|||
}
|
||||
|
||||
void Menu::changeVSync() {
|
||||
Application::getInstance()->setRenderTargetFramerate(
|
||||
Application::getInstance()->getRenderTargetFramerate(),
|
||||
isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn));
|
||||
}
|
||||
void Menu::changeRenderTargetFramerate(QAction* action) {
|
||||
bool vsynOn = Application::getInstance()->isVSyncOn();
|
||||
|
||||
QString text = action->text();
|
||||
if (text == MenuOption::RenderTargetFramerateUnlimited) {
|
||||
Application::getInstance()->setRenderTargetFramerate(0, vsynOn);
|
||||
}
|
||||
else if (text == MenuOption::RenderTargetFramerate60) {
|
||||
Application::getInstance()->setRenderTargetFramerate(60, vsynOn);
|
||||
}
|
||||
else if (text == MenuOption::RenderTargetFramerate50) {
|
||||
Application::getInstance()->setRenderTargetFramerate(50, vsynOn);
|
||||
}
|
||||
else if (text == MenuOption::RenderTargetFramerate40) {
|
||||
Application::getInstance()->setRenderTargetFramerate(40, vsynOn);
|
||||
}
|
||||
else if (text == MenuOption::RenderTargetFramerate30) {
|
||||
Application::getInstance()->setRenderTargetFramerate(30, vsynOn);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::changeRenderResolution(QAction* action) {
|
||||
QString text = action->text();
|
||||
if (text == MenuOption::RenderResolutionOne) {
|
||||
Application::getInstance()->setRenderResolutionScale(1.f);
|
||||
} else if (text == MenuOption::RenderResolutionTwoThird) {
|
||||
Application::getInstance()->setRenderResolutionScale(0.666f);
|
||||
} else if (text == MenuOption::RenderResolutionHalf) {
|
||||
Application::getInstance()->setRenderResolutionScale(0.5f);
|
||||
} else if (text == MenuOption::RenderResolutionThird) {
|
||||
Application::getInstance()->setRenderResolutionScale(0.333f);
|
||||
} else if (text == MenuOption::RenderResolutionQuarter) {
|
||||
Application::getInstance()->setRenderResolutionScale(0.25f);
|
||||
} else {
|
||||
Application::getInstance()->setRenderResolutionScale(1.f);
|
||||
}
|
||||
Application::getInstance()->setVSyncEnabled(isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn));
|
||||
}
|
||||
|
||||
void Menu::displayNameLocationResponse(const QString& errorString) {
|
||||
|
|
|
@ -230,9 +230,7 @@ private slots:
|
|||
void displayAddressOfflineMessage();
|
||||
void displayAddressNotFoundMessage();
|
||||
void muteEnvironment();
|
||||
void changeRenderTargetFramerate(QAction* action);
|
||||
void changeVSync();
|
||||
void changeRenderResolution(QAction* action);
|
||||
|
||||
private:
|
||||
static Menu* _instance;
|
||||
|
|
|
@ -11,19 +11,25 @@
|
|||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include <QScriptSyntaxCheckResult>
|
||||
|
||||
#include <FBXReader.h>
|
||||
|
||||
#include "InterfaceConfig.h"
|
||||
|
||||
#include <BoxEntityItem.h>
|
||||
#include <ModelEntityItem.h>
|
||||
#include <MouseEvent.h>
|
||||
#include <PerfStat.h>
|
||||
#include <RenderArgs.h>
|
||||
|
||||
|
||||
#include "Menu.h"
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "EntityTreeRenderer.h"
|
||||
|
||||
#include "devices/OculusManager.h"
|
||||
|
||||
#include "RenderableBoxEntityItem.h"
|
||||
#include "RenderableLightEntityItem.h"
|
||||
#include "RenderableModelEntityItem.h"
|
||||
|
@ -36,15 +42,21 @@ QThread* EntityTreeRenderer::getMainThread() {
|
|||
|
||||
|
||||
|
||||
EntityTreeRenderer::EntityTreeRenderer() :
|
||||
OctreeRenderer() {
|
||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts) :
|
||||
OctreeRenderer(),
|
||||
_wantScripts(wantScripts),
|
||||
_entitiesScriptEngine(NULL) {
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Box, RenderableBoxEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Sphere, RenderableSphereEntityItem::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() {
|
||||
// do we need to delete the _entitiesScriptEngine?? or is it deleted by default
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::clear() {
|
||||
|
@ -54,6 +66,117 @@ void EntityTreeRenderer::clear() {
|
|||
void EntityTreeRenderer::init() {
|
||||
OctreeRenderer::init();
|
||||
static_cast<EntityTree*>(_tree)->setFBXService(this);
|
||||
|
||||
if (_wantScripts) {
|
||||
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities",
|
||||
Application::getInstance()->getControllerScriptingInterface());
|
||||
Application::getInstance()->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
|
||||
}
|
||||
|
||||
// make sure our "last avatar position" is something other than our current position, so that on our
|
||||
// first chance, we'll check for enter/leave entity events.
|
||||
glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition();
|
||||
_lastAvatarPosition = avatarPosition + glm::vec3(1.f, 1.f, 1.f);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -65,6 +188,58 @@ void EntityTreeRenderer::update() {
|
|||
if (_tree) {
|
||||
EntityTree* tree = static_cast<EntityTree*>(_tree);
|
||||
tree->update();
|
||||
|
||||
// check to see if the avatar has moved and if we need to handle enter/leave entity logic
|
||||
checkEnterLeaveEntities();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::checkEnterLeaveEntities() {
|
||||
if (_tree) {
|
||||
glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float) TREE_SCALE;
|
||||
|
||||
if (avatarPosition != _lastAvatarPosition) {
|
||||
float radius = 1.0f / (float) TREE_SCALE; // for now, assume 1 meter radius
|
||||
QVector<const EntityItem*> foundEntities;
|
||||
QVector<EntityItemID> entitiesContainingAvatar;
|
||||
|
||||
// find the entities near us
|
||||
static_cast<EntityTree*>(_tree)->findEntities(avatarPosition, radius, foundEntities);
|
||||
|
||||
// create a list of entities that actually contain the avatar's position
|
||||
foreach(const EntityItem* entity, foundEntities) {
|
||||
if (entity->contains(avatarPosition)) {
|
||||
entitiesContainingAvatar << entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
|
||||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
|
||||
if (!entitiesContainingAvatar.contains(entityID)) {
|
||||
emit leaveEntity(entityID);
|
||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
||||
QScriptValue entityScript = loadEntityScript(entityID);
|
||||
if (entityScript.property("leaveEntity").isValid()) {
|
||||
entityScript.property("leaveEntity").call(entityScript, entityArgs);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// for all of our new containing entities, if they weren't previously containing then send them an enter event
|
||||
foreach(const EntityItemID& entityID, entitiesContainingAvatar) {
|
||||
if (!_currentEntitiesInside.contains(entityID)) {
|
||||
emit enterEntity(entityID);
|
||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
||||
QScriptValue entityScript = loadEntityScript(entityID);
|
||||
if (entityScript.property("enterEntity").isValid()) {
|
||||
entityScript.property("enterEntity").call(entityScript, entityArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
_currentEntitiesInside = entitiesContainingAvatar;
|
||||
_lastAvatarPosition = avatarPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,8 +276,8 @@ const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityI
|
|||
}
|
||||
|
||||
void renderElementProxy(EntityTreeElement* entityTreeElement) {
|
||||
glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float)TREE_SCALE;
|
||||
float elementSize = entityTreeElement->getScale() * (float)TREE_SCALE;
|
||||
glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float) TREE_SCALE;
|
||||
float elementSize = entityTreeElement->getScale() * (float) TREE_SCALE;
|
||||
glColor3f(1.0f, 0.0f, 0.0f);
|
||||
glPushMatrix();
|
||||
glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z);
|
||||
|
@ -175,9 +350,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg
|
|||
AACube minCube = entity->getMinimumAACube();
|
||||
AABox entityBox = entity->getAABox();
|
||||
|
||||
maxCube.scale((float)TREE_SCALE);
|
||||
minCube.scale((float)TREE_SCALE);
|
||||
entityBox.scale((float)TREE_SCALE);
|
||||
maxCube.scale((float) TREE_SCALE);
|
||||
minCube.scale((float) TREE_SCALE);
|
||||
entityBox.scale((float) TREE_SCALE);
|
||||
|
||||
glm::vec3 maxCenter = maxCube.calcCenter();
|
||||
glm::vec3 minCenter = minCube.calcCenter();
|
||||
|
@ -207,9 +382,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg
|
|||
glPopMatrix();
|
||||
|
||||
|
||||
glm::vec3 position = entity->getPosition() * (float)TREE_SCALE;
|
||||
glm::vec3 center = entity->getCenter() * (float)TREE_SCALE;
|
||||
glm::vec3 dimensions = entity->getDimensions() * (float)TREE_SCALE;
|
||||
glm::vec3 position = entity->getPosition() * (float) TREE_SCALE;
|
||||
glm::vec3 center = entity->getCenter() * (float) TREE_SCALE;
|
||||
glm::vec3 dimensions = entity->getDimensions() * (float) TREE_SCALE;
|
||||
glm::quat rotation = entity->getRotation();
|
||||
|
||||
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
|
@ -382,4 +557,207 @@ 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);
|
||||
|
||||
connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity);
|
||||
connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QScriptValueList EntityTreeRenderer::createEntityArgs(const EntityItemID& entityID) {
|
||||
QScriptValueList args;
|
||||
args << entityID.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 <EntityTree.h>
|
||||
#include <EntityScriptingInterface.h> // for RayToEntityIntersectionResult
|
||||
#include <Octree.h>
|
||||
#include <OctreePacketData.h>
|
||||
#include <OctreeRenderer.h>
|
||||
|
@ -26,11 +27,17 @@
|
|||
|
||||
#include "renderer/Model.h"
|
||||
|
||||
class EntityScriptDetails {
|
||||
public:
|
||||
QString scriptText;
|
||||
QScriptValue scriptObject;
|
||||
};
|
||||
|
||||
// Generic client side Octree renderer class.
|
||||
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
|
||||
Q_OBJECT
|
||||
public:
|
||||
EntityTreeRenderer();
|
||||
EntityTreeRenderer(bool wantScripts);
|
||||
virtual ~EntityTreeRenderer();
|
||||
|
||||
virtual Octree* createTree() { return new EntityTree(true); }
|
||||
|
@ -75,9 +82,55 @@ public:
|
|||
void releaseModel(Model* model);
|
||||
|
||||
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);
|
||||
|
||||
void enterEntity(const EntityItemID& entityItemID);
|
||||
void leaveEntity(const EntityItemID& entityItemID);
|
||||
|
||||
private:
|
||||
QList<Model*> _releasedModels;
|
||||
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;
|
||||
|
||||
QScriptValueList createEntityArgs(const EntityItemID& entityID);
|
||||
void checkEnterLeaveEntities();
|
||||
glm::vec3 _lastAvatarPosition;
|
||||
QVector<EntityItemID> _currentEntitiesInside;
|
||||
|
||||
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
|
||||
|
|
|
@ -253,6 +253,7 @@ public:
|
|||
|
||||
void applyHardCollision(const CollisionInfo& collisionInfo);
|
||||
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
|
||||
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
|
||||
|
||||
protected:
|
||||
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
||||
|
|
|
@ -124,6 +124,10 @@ void EntityItemID::handleAddEntityResponse(const QByteArray& packet) {
|
|||
_tokenIDsToIDs[creatorTokenID] = entityID;
|
||||
}
|
||||
|
||||
QScriptValue EntityItemID::toScriptValue(QScriptEngine* engine) const {
|
||||
return EntityItemIDtoScriptValue(engine, *this);
|
||||
}
|
||||
|
||||
QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& id) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("id", id.id.toString());
|
||||
|
|
|
@ -49,11 +49,16 @@ public:
|
|||
EntityItemID convertToKnownIDVersion() 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.
|
||||
static EntityItemID createInvalidEntityID() { return EntityItemID(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false); }
|
||||
static EntityItemID getIDfromCreatorTokenID(uint32_t creatorTokenID);
|
||||
static uint32_t getNextCreatorTokenID();
|
||||
static void handleAddEntityResponse(const QByteArray& packet);
|
||||
static EntityItemID readEntityItemIDFromBuffer(const unsigned char* data, int bytesLeftToRead);
|
||||
|
||||
QScriptValue toScriptValue(QScriptEngine* engine) const;
|
||||
|
||||
private:
|
||||
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) {
|
||||
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) {
|
||||
return a.creatorTokenID == b.creatorTokenID;
|
||||
}
|
||||
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) {
|
||||
QUuid temp;
|
||||
if (a.id == UNKNOWN_ENTITY_ID) {
|
||||
|
@ -94,5 +109,4 @@ Q_DECLARE_METATYPE(QVector<EntityItemID>);
|
|||
QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID& properties);
|
||||
void EntityItemIDfromScriptValue(const QScriptValue &object, EntityItemID& properties);
|
||||
|
||||
|
||||
#endif // hifi_EntityItemID_h
|
||||
|
|
|
@ -217,7 +217,8 @@ RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
|
|||
entityID(),
|
||||
properties(),
|
||||
distance(0),
|
||||
face()
|
||||
face(),
|
||||
entity(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
|
||||
class EntityTree;
|
||||
class MouseEvent;
|
||||
|
||||
|
||||
class RayToEntityIntersectionResult {
|
||||
|
@ -37,6 +38,7 @@ public:
|
|||
float distance;
|
||||
BoxFace face;
|
||||
glm::vec3 intersection;
|
||||
EntityItem* entity;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(RayToEntityIntersectionResult)
|
||||
|
@ -101,6 +103,21 @@ signals:
|
|||
void entityCollisionWithVoxel(const EntityItemID& entityID, const VoxelDetail& voxel, 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);
|
||||
|
||||
void enterEntity(const EntityItemID& entityItemID);
|
||||
void leaveEntity(const EntityItemID& entityItemID);
|
||||
|
||||
private:
|
||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
|
|
@ -53,6 +53,9 @@ public:
|
|||
|
||||
virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; }
|
||||
|
||||
// TODO: implement proper contains for 3D ellipsoid
|
||||
//virtual bool contains(const glm::vec3& point) const;
|
||||
|
||||
protected:
|
||||
virtual void recalculateCollisionShape();
|
||||
|
||||
|
|
|
@ -130,11 +130,11 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() {
|
|||
reinterpret_cast<const unsigned char**>(&privateKeyData),
|
||||
_privateKey.size());
|
||||
if (rsaPrivateKey) {
|
||||
QByteArray usernameByteArray = _username.toUtf8();
|
||||
QByteArray lowercaseUsername = _username.toLower().toUtf8();
|
||||
_usernameSignature.resize(RSA_size(rsaPrivateKey));
|
||||
|
||||
int encryptReturn = RSA_private_encrypt(usernameByteArray.size(),
|
||||
reinterpret_cast<const unsigned char*>(usernameByteArray.constData()),
|
||||
int encryptReturn = RSA_private_encrypt(lowercaseUsername.size(),
|
||||
reinterpret_cast<const unsigned char*>(lowercaseUsername.constData()),
|
||||
reinterpret_cast<unsigned char*>(_usernameSignature.data()),
|
||||
rsaPrivateKey, RSA_PKCS1_PADDING);
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
#include <QtNetwork/QNetworkInterface>
|
||||
#include <qdatastream.h>
|
||||
#include <qhostinfo.h>
|
||||
#include <qnetworkinterface.h>
|
||||
|
||||
#include "HifiSockAddr.h"
|
||||
|
||||
|
@ -36,13 +36,20 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) {
|
|||
_port = otherSockAddr._port;
|
||||
}
|
||||
|
||||
HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) {
|
||||
// lookup the IP by the hostname
|
||||
QHostInfo hostInfo = QHostInfo::fromName(hostname);
|
||||
foreach(const QHostAddress& address, hostInfo.addresses()) {
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_address = address;
|
||||
_port = hostOrderPort;
|
||||
HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup) :
|
||||
_address(hostname),
|
||||
_port(hostOrderPort)
|
||||
{
|
||||
// if we parsed an IPv4 address out of the hostname, don't look it up
|
||||
if (_address.protocol() != QAbstractSocket::IPv4Protocol) {
|
||||
// lookup the IP by the hostname
|
||||
if (shouldBlockForLookup) {
|
||||
qDebug() << "Asynchronously looking up IP address for hostname" << hostname;
|
||||
QHostInfo result = QHostInfo::fromName(hostname);
|
||||
handleLookupResult(result);
|
||||
} else {
|
||||
int lookupID = QHostInfo::lookupHost(hostname, this, SLOT(handleLookupResult(QHostInfo)));
|
||||
qDebug() << "Synchronously looking up IP address for hostname" << hostname << "- lookup ID is" << lookupID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +82,22 @@ bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const {
|
|||
return _address == rhsSockAddr._address && _port == rhsSockAddr._port;
|
||||
}
|
||||
|
||||
void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) {
|
||||
if (hostInfo.error() != QHostInfo::NoError) {
|
||||
qDebug() << "Lookup failed for" << hostInfo.lookupId() << ":" << hostInfo.errorString();
|
||||
}
|
||||
|
||||
foreach(const QHostAddress& address, hostInfo.addresses()) {
|
||||
// just take the first IPv4 address
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
_address = address;
|
||||
qDebug() << "QHostInfo lookup result for"
|
||||
<< hostInfo.hostName() << "with lookup ID" << hostInfo.lookupId() << "is" << address.toString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) {
|
||||
debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
|
||||
return debug.space();
|
||||
|
|
|
@ -19,14 +19,15 @@
|
|||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <QtNetwork/QHostAddress>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
|
||||
class HifiSockAddr {
|
||||
class HifiSockAddr : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
HifiSockAddr();
|
||||
HifiSockAddr(const QHostAddress& address, quint16 port);
|
||||
HifiSockAddr(const HifiSockAddr& otherSockAddr);
|
||||
HifiSockAddr(const QString& hostname, quint16 hostOrderPort);
|
||||
HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false);
|
||||
HifiSockAddr(const sockaddr* sockaddr);
|
||||
|
||||
bool isNull() const { return _address.isNull() && _port == 0; }
|
||||
|
@ -51,6 +52,8 @@ public:
|
|||
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
|
||||
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
|
||||
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
|
||||
private slots:
|
||||
void handleLookupResult(const QHostInfo& hostInfo);
|
||||
private:
|
||||
QHostAddress _address;
|
||||
quint16 _port;
|
||||
|
|
|
@ -73,6 +73,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
|
|||
_dtlsSocket(NULL),
|
||||
_localSockAddr(),
|
||||
_publicSockAddr(),
|
||||
_stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT),
|
||||
_numCollectedPackets(0),
|
||||
_numCollectedBytes(0),
|
||||
_packetStatTimer()
|
||||
|
@ -583,11 +584,8 @@ void LimitedNodeList::sendSTUNRequest() {
|
|||
QUuid randomUUID = QUuid::createUuid();
|
||||
memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES);
|
||||
|
||||
// lookup the IP for the STUN server
|
||||
HifiSockAddr stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT);
|
||||
|
||||
_nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket),
|
||||
stunSockAddr.getAddress(), stunSockAddr.getPort());
|
||||
_stunSockAddr.getAddress(), _stunSockAddr.getPort());
|
||||
}
|
||||
|
||||
void LimitedNodeList::rebindNodeSocket() {
|
||||
|
|
|
@ -163,6 +163,7 @@ protected:
|
|||
QUdpSocket* _dtlsSocket;
|
||||
HifiSockAddr _localSockAddr;
|
||||
HifiSockAddr _publicSockAddr;
|
||||
HifiSockAddr _stunSockAddr;
|
||||
int _numCollectedPackets;
|
||||
int _numCollectedBytes;
|
||||
QElapsedTimer _packetStatTimer;
|
||||
|
|
|
@ -21,6 +21,8 @@ public:
|
|||
|
||||
static QScriptValue toScriptValue(QScriptEngine* engine, const MouseEvent& event);
|
||||
static void fromScriptValue(const QScriptValue& object, MouseEvent& event);
|
||||
|
||||
QScriptValue toScriptValue(QScriptEngine* engine) const { return MouseEvent::toScriptValue(engine, *this); }
|
||||
|
||||
int x;
|
||||
int y;
|
||||
|
|
Loading…
Reference in a new issue