mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 14:47:41 +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 =
|
assignmentServerPort =
|
||||||
argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt();
|
argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION).toString().toUInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
HifiSockAddr assignmentServerSocket(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME, assignmentServerPort);
|
|
||||||
|
|
||||||
// check for an overriden assignment server hostname
|
// check for an overriden assignment server hostname
|
||||||
if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) {
|
if (argumentVariantMap.contains(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION)) {
|
||||||
_assignmentServerHostname = argumentVariantMap.value(CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION).toString();
|
|
||||||
|
|
||||||
// change the hostname for our assignment server
|
// 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);
|
nodeList->setAssignmentServerSocket(assignmentServerSocket);
|
||||||
|
|
||||||
qDebug() << "Assignment server socket is" << assignmentServerSocket;
|
qDebug() << "Assignment server socket is" << assignmentServerSocket;
|
||||||
|
|
|
@ -388,7 +388,7 @@ void DomainServer::setupAutomaticNetworking() {
|
||||||
const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000;
|
const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000;
|
||||||
|
|
||||||
QTimer* dataHeartbeatTimer = new QTimer(this);
|
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);
|
dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,7 +637,7 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowedUsers.count() > 0) {
|
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
|
// it's possible this user can be allowed to connect, but we need to check their username signature
|
||||||
|
|
||||||
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
||||||
|
@ -657,7 +657,7 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
|
||||||
rsaPublicKey, RSA_PKCS1_PADDING);
|
rsaPublicKey, RSA_PKCS1_PADDING);
|
||||||
|
|
||||||
if (decryptResult != -1) {
|
if (decryptResult != -1) {
|
||||||
if (username == decryptedArray) {
|
if (username.toLower() == decryptedArray) {
|
||||||
qDebug() << "Username signature matches for" << username << "- allowing connection.";
|
qDebug() << "Username signature matches for" << username << "- allowing connection.";
|
||||||
|
|
||||||
// free up the public key before we return
|
// 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";
|
const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||||
|
|
||||||
void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) {
|
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 QString DOMAIN_UPDATE = "/api/v1/domains/%1";
|
||||||
const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID();
|
const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID();
|
||||||
|
|
||||||
|
@ -1109,6 +1109,21 @@ void DomainServer::updateDomainInDataServer(const QString& networkAddress) {
|
||||||
|
|
||||||
domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting;
|
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()));
|
QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson()));
|
||||||
|
|
||||||
AccountManager::getInstance().authenticatedRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
|
AccountManager::getInstance().authenticatedRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
|
||||||
|
|
|
@ -66,7 +66,7 @@ private slots:
|
||||||
void requestCurrentPublicSocketViaSTUN();
|
void requestCurrentPublicSocketViaSTUN();
|
||||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||||
void performICEUpdates();
|
void performICEUpdates();
|
||||||
void sendHeartbeatToDataServer() { updateDomainInDataServer(); }
|
void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); }
|
||||||
void sendHeartbeatToIceServer();
|
void sendHeartbeatToIceServer();
|
||||||
void sendICEPingPackets();
|
void sendICEPingPackets();
|
||||||
private:
|
private:
|
||||||
|
@ -77,7 +77,7 @@ private:
|
||||||
bool optionallySetupAssignmentPayment();
|
bool optionallySetupAssignmentPayment();
|
||||||
|
|
||||||
void setupAutomaticNetworking();
|
void setupAutomaticNetworking();
|
||||||
void updateDomainInDataServer(const QString& networkAddress = QString());
|
void sendHeartbeatToDataServer(const QString& networkAddress);
|
||||||
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||||
void processICEHeartbeatResponse(const QByteArray& packet);
|
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 });
|
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
|
||||||
|
|
|
@ -107,9 +107,6 @@ static unsigned STARFIELD_SEED = 1;
|
||||||
|
|
||||||
static const int BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH = 6; // farther dragged clicks are ignored
|
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
|
const unsigned MAXIMUM_CACHE_SIZE = 10737418240; // 10GB
|
||||||
|
|
||||||
static QTimer* idleTimer = NULL;
|
static QTimer* idleTimer = NULL;
|
||||||
|
@ -151,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(),
|
||||||
|
@ -185,9 +184,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_trayIcon(new QSystemTrayIcon(_window)),
|
_trayIcon(new QSystemTrayIcon(_window)),
|
||||||
_lastNackTime(usecTimestampNow()),
|
_lastNackTime(usecTimestampNow()),
|
||||||
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
_lastSendDownstreamAudioStats(usecTimestampNow()),
|
||||||
_renderTargetFramerate(0),
|
_isVSyncOn(true)
|
||||||
_isVSyncOn(true),
|
|
||||||
_renderResolutionScale(1.0f)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||||
|
@ -604,7 +601,7 @@ void Application::paintGL() {
|
||||||
if (OculusManager::isConnected()) {
|
if (OculusManager::isConnected()) {
|
||||||
_textureCache.setFrameBufferSize(OculusManager::getRenderTargetSize());
|
_textureCache.setFrameBufferSize(OculusManager::getRenderTargetSize());
|
||||||
} else {
|
} else {
|
||||||
QSize fbSize = _glWidget->getDeviceSize() * _renderResolutionScale;
|
QSize fbSize = _glWidget->getDeviceSize() * getRenderResolutionScale();
|
||||||
_textureCache.setFrameBufferSize(fbSize);
|
_textureCache.setFrameBufferSize(fbSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1251,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
|
||||||
|
|
||||||
|
@ -1272,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
|
||||||
|
@ -1309,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
|
||||||
|
@ -1474,12 +1479,11 @@ void Application::idle() {
|
||||||
bool showWarnings = getLogger()->extraDebugging();
|
bool showWarnings = getLogger()->extraDebugging();
|
||||||
PerformanceWarning warn(showWarnings, "idle()");
|
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;
|
double targetFramePeriod = 0.0;
|
||||||
if (_renderTargetFramerate > 0) {
|
unsigned int targetFramerate = getRenderTargetFramerate();
|
||||||
targetFramePeriod = 1000.0 / _renderTargetFramerate;
|
if (targetFramerate > 0) {
|
||||||
} else if (_renderTargetFramerate < 0) {
|
targetFramePeriod = 1000.0 / targetFramerate;
|
||||||
targetFramePeriod = IDLE_SIMULATE_MSECS;
|
|
||||||
}
|
}
|
||||||
double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0;
|
double timeSinceLastUpdate = (double)_lastTimeUpdated.nsecsElapsed() / 1000000.0;
|
||||||
if (timeSinceLastUpdate > targetFramePeriod) {
|
if (timeSinceLastUpdate > targetFramePeriod) {
|
||||||
|
@ -1943,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);
|
||||||
|
@ -3179,7 +3187,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
||||||
} else {
|
} else {
|
||||||
// if not rendering the billboard, the region is in device independent coordinates; must convert to device
|
// if not rendering the billboard, the region is in device independent coordinates; must convert to device
|
||||||
QSize size = getTextureCache()->getFrameBufferSize();
|
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;
|
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);
|
glViewport(x, size.height() - y - height, width, height);
|
||||||
glScissor(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());
|
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);
|
||||||
|
@ -3932,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) {
|
||||||
|
@ -4237,37 +4249,56 @@ void Application::takeSnapshot() {
|
||||||
_snapshotShareDialog->show();
|
_snapshotShareDialog->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setRenderTargetFramerate(unsigned int framerate, bool vsyncOn) {
|
void Application::setVSyncEnabled(bool vsyncOn) {
|
||||||
if (vsyncOn != _isVSyncOn) {
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||||
wglSwapIntervalEXT(vsyncOn);
|
wglSwapIntervalEXT(vsyncOn);
|
||||||
int swapInterval = wglGetSwapIntervalEXT();
|
int swapInterval = wglGetSwapIntervalEXT();
|
||||||
_isVSyncOn = swapInterval;
|
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
||||||
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
|
} else {
|
||||||
} 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 {
|
|
||||||
qDebug("V-Sync is FORCED ON on this system\n");
|
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 defined(Q_OS_WIN)
|
||||||
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
if (wglewGetExtension("WGL_EXT_swap_control")) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -4284,6 +4315,33 @@ bool Application::isVSyncEditable() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setRenderResolutionScale(float scale) {
|
unsigned int Application::getRenderTargetFramerate() const {
|
||||||
_renderResolutionScale = scale;
|
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);
|
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:
|
signals:
|
||||||
|
|
||||||
|
@ -367,12 +374,7 @@ public slots:
|
||||||
|
|
||||||
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
void domainSettingsReceived(const QJsonObject& domainSettingsObject);
|
||||||
|
|
||||||
void setRenderTargetFramerate(unsigned int framerate, bool vsyncOn = true);
|
void setVSyncEnabled(bool vsyncOn);
|
||||||
bool isVSyncOn() { return _isVSyncOn; }
|
|
||||||
bool isVSyncEditable();
|
|
||||||
unsigned int getRenderTargetFramerate() const { return _renderTargetFramerate; }
|
|
||||||
|
|
||||||
void setRenderResolutionScale(float scale);
|
|
||||||
|
|
||||||
void resetSensors();
|
void resetSensors();
|
||||||
|
|
||||||
|
@ -624,9 +626,7 @@ private:
|
||||||
quint64 _lastNackTime;
|
quint64 _lastNackTime;
|
||||||
quint64 _lastSendDownstreamAudioStats;
|
quint64 _lastSendDownstreamAudioStats;
|
||||||
|
|
||||||
int _renderTargetFramerate;
|
|
||||||
bool _isVSyncOn;
|
bool _isVSyncOn;
|
||||||
float _renderResolutionScale;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Application_h
|
#endif // hifi_Application_h
|
||||||
|
|
|
@ -377,13 +377,12 @@ Menu::Menu() :
|
||||||
{
|
{
|
||||||
QMenu* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
|
QMenu* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
|
||||||
QActionGroup* framerateGroup = new QActionGroup(framerateMenu);
|
QActionGroup* framerateGroup = new QActionGroup(framerateMenu);
|
||||||
|
framerateGroup->setExclusive(true);
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true));
|
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true));
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false));
|
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false));
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false));
|
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false));
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false));
|
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false));
|
||||||
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false));
|
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false));
|
||||||
connect(framerateMenu, SIGNAL(triggered(QAction*)), this, SLOT(changeRenderTargetFramerate(QAction*)));
|
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
#else
|
#else
|
||||||
|
@ -394,12 +393,12 @@ Menu::Menu() :
|
||||||
|
|
||||||
QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||||
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
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::RenderResolutionTwoThird, 0, false));
|
||||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 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));
|
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, MenuOption::Stars, Qt::Key_Asterisk, true);
|
||||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu,
|
addCheckableActionToQMenuAndActionHash(renderOptionsMenu,
|
||||||
|
@ -1261,46 +1260,7 @@ void Menu::muteEnvironment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::changeVSync() {
|
void Menu::changeVSync() {
|
||||||
Application::getInstance()->setRenderTargetFramerate(
|
Application::getInstance()->setVSyncEnabled(isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::displayNameLocationResponse(const QString& errorString) {
|
void Menu::displayNameLocationResponse(const QString& errorString) {
|
||||||
|
|
|
@ -230,9 +230,7 @@ private slots:
|
||||||
void displayAddressOfflineMessage();
|
void displayAddressOfflineMessage();
|
||||||
void displayAddressNotFoundMessage();
|
void displayAddressNotFoundMessage();
|
||||||
void muteEnvironment();
|
void muteEnvironment();
|
||||||
void changeRenderTargetFramerate(QAction* action);
|
|
||||||
void changeVSync();
|
void changeVSync();
|
||||||
void changeRenderResolution(QAction* action);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Menu* _instance;
|
static Menu* _instance;
|
||||||
|
|
|
@ -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,117 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
void EntityTreeRenderer::setTree(Octree* newTree) {
|
||||||
|
@ -65,6 +188,58 @@ void EntityTreeRenderer::update() {
|
||||||
if (_tree) {
|
if (_tree) {
|
||||||
EntityTree* tree = static_cast<EntityTree*>(_tree);
|
EntityTree* tree = static_cast<EntityTree*>(_tree);
|
||||||
tree->update();
|
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) {
|
void renderElementProxy(EntityTreeElement* entityTreeElement) {
|
||||||
glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float)TREE_SCALE;
|
glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float) TREE_SCALE;
|
||||||
float elementSize = entityTreeElement->getScale() * (float)TREE_SCALE;
|
float elementSize = entityTreeElement->getScale() * (float) TREE_SCALE;
|
||||||
glColor3f(1.0f, 0.0f, 0.0f);
|
glColor3f(1.0f, 0.0f, 0.0f);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z);
|
glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z);
|
||||||
|
@ -175,9 +350,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg
|
||||||
AACube minCube = entity->getMinimumAACube();
|
AACube minCube = entity->getMinimumAACube();
|
||||||
AABox entityBox = entity->getAABox();
|
AABox entityBox = entity->getAABox();
|
||||||
|
|
||||||
maxCube.scale((float)TREE_SCALE);
|
maxCube.scale((float) TREE_SCALE);
|
||||||
minCube.scale((float)TREE_SCALE);
|
minCube.scale((float) TREE_SCALE);
|
||||||
entityBox.scale((float)TREE_SCALE);
|
entityBox.scale((float) TREE_SCALE);
|
||||||
|
|
||||||
glm::vec3 maxCenter = maxCube.calcCenter();
|
glm::vec3 maxCenter = maxCube.calcCenter();
|
||||||
glm::vec3 minCenter = minCube.calcCenter();
|
glm::vec3 minCenter = minCube.calcCenter();
|
||||||
|
@ -207,9 +382,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
|
|
||||||
|
|
||||||
glm::vec3 position = entity->getPosition() * (float)TREE_SCALE;
|
glm::vec3 position = entity->getPosition() * (float) TREE_SCALE;
|
||||||
glm::vec3 center = entity->getCenter() * (float)TREE_SCALE;
|
glm::vec3 center = entity->getCenter() * (float) TREE_SCALE;
|
||||||
glm::vec3 dimensions = entity->getDimensions() * (float)TREE_SCALE;
|
glm::vec3 dimensions = entity->getDimensions() * (float) TREE_SCALE;
|
||||||
glm::quat rotation = entity->getRotation();
|
glm::quat rotation = entity->getRotation();
|
||||||
|
|
||||||
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
|
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 <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,55 @@ 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);
|
||||||
|
|
||||||
|
void enterEntity(const EntityItemID& entityItemID);
|
||||||
|
void leaveEntity(const EntityItemID& entityItemID);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
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
|
#endif // hifi_EntityTreeRenderer_h
|
||||||
|
|
|
@ -253,6 +253,7 @@ public:
|
||||||
|
|
||||||
void applyHardCollision(const CollisionInfo& collisionInfo);
|
void applyHardCollision(const CollisionInfo& collisionInfo);
|
||||||
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
|
virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; }
|
||||||
|
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init
|
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;
|
_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,21 @@ 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);
|
||||||
|
|
||||||
|
void enterEntity(const EntityItemID& entityItemID);
|
||||||
|
void leaveEntity(const EntityItemID& entityItemID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,9 @@ public:
|
||||||
|
|
||||||
virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; }
|
virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; }
|
||||||
|
|
||||||
|
// TODO: implement proper contains for 3D ellipsoid
|
||||||
|
//virtual bool contains(const glm::vec3& point) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void recalculateCollisionShape();
|
virtual void recalculateCollisionShape();
|
||||||
|
|
||||||
|
|
|
@ -130,11 +130,11 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() {
|
||||||
reinterpret_cast<const unsigned char**>(&privateKeyData),
|
reinterpret_cast<const unsigned char**>(&privateKeyData),
|
||||||
_privateKey.size());
|
_privateKey.size());
|
||||||
if (rsaPrivateKey) {
|
if (rsaPrivateKey) {
|
||||||
QByteArray usernameByteArray = _username.toUtf8();
|
QByteArray lowercaseUsername = _username.toLower().toUtf8();
|
||||||
_usernameSignature.resize(RSA_size(rsaPrivateKey));
|
_usernameSignature.resize(RSA_size(rsaPrivateKey));
|
||||||
|
|
||||||
int encryptReturn = RSA_private_encrypt(usernameByteArray.size(),
|
int encryptReturn = RSA_private_encrypt(lowercaseUsername.size(),
|
||||||
reinterpret_cast<const unsigned char*>(usernameByteArray.constData()),
|
reinterpret_cast<const unsigned char*>(lowercaseUsername.constData()),
|
||||||
reinterpret_cast<unsigned char*>(_usernameSignature.data()),
|
reinterpret_cast<unsigned char*>(_usernameSignature.data()),
|
||||||
rsaPrivateKey, RSA_PKCS1_PADDING);
|
rsaPrivateKey, RSA_PKCS1_PADDING);
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <QtCore/QDataStream>
|
#include <qdatastream.h>
|
||||||
#include <QtNetwork/QHostInfo>
|
#include <qhostinfo.h>
|
||||||
#include <QtNetwork/QNetworkInterface>
|
#include <qnetworkinterface.h>
|
||||||
|
|
||||||
#include "HifiSockAddr.h"
|
#include "HifiSockAddr.h"
|
||||||
|
|
||||||
|
@ -36,13 +36,20 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) {
|
||||||
_port = otherSockAddr._port;
|
_port = otherSockAddr._port;
|
||||||
}
|
}
|
||||||
|
|
||||||
HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort) {
|
HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup) :
|
||||||
// lookup the IP by the hostname
|
_address(hostname),
|
||||||
QHostInfo hostInfo = QHostInfo::fromName(hostname);
|
_port(hostOrderPort)
|
||||||
foreach(const QHostAddress& address, hostInfo.addresses()) {
|
{
|
||||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
// if we parsed an IPv4 address out of the hostname, don't look it up
|
||||||
_address = address;
|
if (_address.protocol() != QAbstractSocket::IPv4Protocol) {
|
||||||
_port = hostOrderPort;
|
// 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;
|
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) {
|
QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) {
|
||||||
debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
|
debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
|
||||||
return debug.space();
|
return debug.space();
|
||||||
|
|
|
@ -19,14 +19,15 @@
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QtNetwork/QHostAddress>
|
#include <QtNetwork/QHostInfo>
|
||||||
|
|
||||||
class HifiSockAddr {
|
class HifiSockAddr : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
HifiSockAddr();
|
HifiSockAddr();
|
||||||
HifiSockAddr(const QHostAddress& address, quint16 port);
|
HifiSockAddr(const QHostAddress& address, quint16 port);
|
||||||
HifiSockAddr(const HifiSockAddr& otherSockAddr);
|
HifiSockAddr(const HifiSockAddr& otherSockAddr);
|
||||||
HifiSockAddr(const QString& hostname, quint16 hostOrderPort);
|
HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false);
|
||||||
HifiSockAddr(const sockaddr* sockaddr);
|
HifiSockAddr(const sockaddr* sockaddr);
|
||||||
|
|
||||||
bool isNull() const { return _address.isNull() && _port == 0; }
|
bool isNull() const { return _address.isNull() && _port == 0; }
|
||||||
|
@ -51,6 +52,8 @@ public:
|
||||||
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
|
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
|
||||||
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
|
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
|
||||||
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
|
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
|
||||||
|
private slots:
|
||||||
|
void handleLookupResult(const QHostInfo& hostInfo);
|
||||||
private:
|
private:
|
||||||
QHostAddress _address;
|
QHostAddress _address;
|
||||||
quint16 _port;
|
quint16 _port;
|
||||||
|
|
|
@ -73,6 +73,7 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
|
||||||
_dtlsSocket(NULL),
|
_dtlsSocket(NULL),
|
||||||
_localSockAddr(),
|
_localSockAddr(),
|
||||||
_publicSockAddr(),
|
_publicSockAddr(),
|
||||||
|
_stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT),
|
||||||
_numCollectedPackets(0),
|
_numCollectedPackets(0),
|
||||||
_numCollectedBytes(0),
|
_numCollectedBytes(0),
|
||||||
_packetStatTimer()
|
_packetStatTimer()
|
||||||
|
@ -583,11 +584,8 @@ void LimitedNodeList::sendSTUNRequest() {
|
||||||
QUuid randomUUID = QUuid::createUuid();
|
QUuid randomUUID = QUuid::createUuid();
|
||||||
memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES);
|
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),
|
_nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket),
|
||||||
stunSockAddr.getAddress(), stunSockAddr.getPort());
|
_stunSockAddr.getAddress(), _stunSockAddr.getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimitedNodeList::rebindNodeSocket() {
|
void LimitedNodeList::rebindNodeSocket() {
|
||||||
|
|
|
@ -163,6 +163,7 @@ protected:
|
||||||
QUdpSocket* _dtlsSocket;
|
QUdpSocket* _dtlsSocket;
|
||||||
HifiSockAddr _localSockAddr;
|
HifiSockAddr _localSockAddr;
|
||||||
HifiSockAddr _publicSockAddr;
|
HifiSockAddr _publicSockAddr;
|
||||||
|
HifiSockAddr _stunSockAddr;
|
||||||
int _numCollectedPackets;
|
int _numCollectedPackets;
|
||||||
int _numCollectedBytes;
|
int _numCollectedBytes;
|
||||||
QElapsedTimer _packetStatTimer;
|
QElapsedTimer _packetStatTimer;
|
||||||
|
|
|
@ -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