mirror of
https://github.com/overte-org/overte.git
synced 2025-08-05 05:20:00 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
This commit is contained in:
commit
558aa52d30
73 changed files with 5000 additions and 1036 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;
|
||||
|
|
|
@ -50,6 +50,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_hostname(),
|
||||
_webAuthenticationStateSet(),
|
||||
_cookieSessionHash(),
|
||||
_automaticNetworkingSetting(),
|
||||
_settingsManager()
|
||||
{
|
||||
LogUtils::init();
|
||||
|
@ -327,17 +328,17 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
return;
|
||||
}
|
||||
|
||||
QString automaticNetworkValue =
|
||||
_automaticNetworkingSetting =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
|
||||
|
||||
if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
|
||||
automaticNetworkValue == FULL_AUTOMATIC_NETWORKING_VALUE) {
|
||||
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE ||
|
||||
_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
|
||||
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
const QUuid& domainID = nodeList->getSessionUUID();
|
||||
|
||||
if (!domainID.isNull()) {
|
||||
qDebug() << "domain-server" << automaticNetworkValue << "automatic networking enabled for ID"
|
||||
qDebug() << "domain-server" << _automaticNetworkingSetting << "automatic networking enabled for ID"
|
||||
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
|
||||
|
||||
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
|
||||
|
@ -347,7 +348,7 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
QTimer* dynamicIPTimer = new QTimer(this);
|
||||
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN);
|
||||
|
||||
if (automaticNetworkValue == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
|
||||
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
|
||||
dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS);
|
||||
|
||||
// send public socket changes to the data server so nodes can find us at our new IP
|
||||
|
@ -361,11 +362,11 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
|
||||
|
||||
// call our sendHeartbeaToIceServer immediately anytime a local or public socket changes
|
||||
connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer);
|
||||
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHearbeatToIceServer);
|
||||
connect(nodeList, &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer);
|
||||
connect(nodeList, &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer);
|
||||
|
||||
// tell the data server which type of automatic networking we are using
|
||||
updateNetworkingInfoWithDataServer(automaticNetworkValue);
|
||||
// send our heartbeat to data server so it knows what our network settings are
|
||||
sendHeartbeatToDataServer();
|
||||
}
|
||||
|
||||
// attempt to update our sockets now
|
||||
|
@ -378,8 +379,17 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
updateNetworkingInfoWithDataServer(automaticNetworkValue);
|
||||
sendHeartbeatToDataServer();
|
||||
}
|
||||
|
||||
qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting;
|
||||
|
||||
// no matter the auto networking settings we should heartbeat to the data-server every 15s
|
||||
const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000;
|
||||
|
||||
QTimer* dataHeartbeatTimer = new QTimer(this);
|
||||
connect(dataHeartbeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartbeatToDataServer()));
|
||||
dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS);
|
||||
}
|
||||
|
||||
void DomainServer::loginFailed() {
|
||||
|
@ -627,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);
|
||||
|
@ -647,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
|
||||
|
@ -1081,10 +1091,10 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) {
|
|||
const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking";
|
||||
|
||||
void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) {
|
||||
updateNetworkingInfoWithDataServer(IP_ONLY_AUTOMATIC_NETWORKING_VALUE, newPublicSockAddr.getAddress().toString());
|
||||
sendHeartbeatToDataServer(newPublicSockAddr.getAddress().toString());
|
||||
}
|
||||
|
||||
void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress) {
|
||||
void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
|
||||
const QString DOMAIN_UPDATE = "/api/v1/domains/%1";
|
||||
const QUuid& domainID = LimitedNodeList::getInstance()->getSessionUUID();
|
||||
|
||||
|
@ -1097,9 +1107,30 @@ void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting,
|
|||
domainObject[PUBLIC_NETWORK_ADDRESS_KEY] = networkAddress;
|
||||
}
|
||||
|
||||
qDebug() << "Updating automatic networking setting in domain-server to" << newSetting;
|
||||
domainObject[AUTOMATIC_NETWORKING_KEY] = _automaticNetworkingSetting;
|
||||
|
||||
domainObject[AUTOMATIC_NETWORKING_KEY] = newSetting;
|
||||
// add a flag to indicate if this domain uses restricted access - for now that will exclude it from listings
|
||||
const QString RESTRICTED_ACCESS_FLAG = "restricted";
|
||||
|
||||
const QVariant* allowedUsersVariant = valueForKeyPath(_settingsManager.getSettingsMap(),
|
||||
ALLOWED_USERS_SETTINGS_KEYPATH);
|
||||
QStringList allowedUsers = allowedUsersVariant ? allowedUsersVariant->toStringList() : QStringList();
|
||||
domainObject[RESTRICTED_ACCESS_FLAG] = (allowedUsers.size() > 0);
|
||||
|
||||
// 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()));
|
||||
|
||||
|
@ -1112,11 +1143,11 @@ void DomainServer::updateNetworkingInfoWithDataServer(const QString& newSetting,
|
|||
// todo: have data-web respond with ice-server hostname to use
|
||||
|
||||
void DomainServer::performICEUpdates() {
|
||||
sendHearbeatToIceServer();
|
||||
sendHeartbeatToIceServer();
|
||||
sendICEPingPackets();
|
||||
}
|
||||
|
||||
void DomainServer::sendHearbeatToIceServer() {
|
||||
void DomainServer::sendHeartbeatToIceServer() {
|
||||
const HifiSockAddr ICE_SERVER_SOCK_ADDR = HifiSockAddr("ice.highfidelity.io", ICE_SERVER_DEFAULT_PORT);
|
||||
LimitedNodeList::getInstance()->sendHeartbeatToIceServer(ICE_SERVER_SOCK_ADDR);
|
||||
}
|
||||
|
|
|
@ -66,7 +66,8 @@ private slots:
|
|||
void requestCurrentPublicSocketViaSTUN();
|
||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||
void performICEUpdates();
|
||||
void sendHearbeatToIceServer();
|
||||
void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); }
|
||||
void sendHeartbeatToIceServer();
|
||||
void sendICEPingPackets();
|
||||
private:
|
||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||
|
@ -76,7 +77,7 @@ private:
|
|||
bool optionallySetupAssignmentPayment();
|
||||
|
||||
void setupAutomaticNetworking();
|
||||
void updateNetworkingInfoWithDataServer(const QString& newSetting, const QString& networkAddress = QString());
|
||||
void sendHeartbeatToDataServer(const QString& networkAddress);
|
||||
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
void processICEHeartbeatResponse(const QByteArray& packet);
|
||||
|
||||
|
@ -150,6 +151,8 @@ private:
|
|||
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
||||
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
||||
|
||||
QString _automaticNetworkingSetting;
|
||||
|
||||
DomainServerSettingsManager _settingsManager;
|
||||
};
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ var THRUST_CONTROLLER = 0;
|
|||
var VIEW_CONTROLLER = 1;
|
||||
|
||||
function checkCamera(deltaTime) {
|
||||
if (Camera.getMode() == "independent") {
|
||||
if (Camera.mode == "independent") {
|
||||
var THRUST_MAG_UP = 800.0;
|
||||
var THRUST_MAG_DOWN = 300.0;
|
||||
var THRUST_MAG_FWD = 500.0;
|
||||
|
@ -102,19 +102,19 @@ function keyPressEvent(event) {
|
|||
}
|
||||
|
||||
if (event.text == "1") {
|
||||
Camera.setMode("first person");
|
||||
Camera.mode = "first person";
|
||||
}
|
||||
|
||||
if (event.text == "2") {
|
||||
Camera.setMode("mirror");
|
||||
Camera.mode = "mirror";
|
||||
}
|
||||
|
||||
if (event.text == "3") {
|
||||
Camera.setMode("third person");
|
||||
Camera.mode = "third person";
|
||||
}
|
||||
|
||||
if (event.text == "4") {
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
joysticksCaptured = true;
|
||||
Controller.captureJoystick(THRUST_CONTROLLER);
|
||||
Controller.captureJoystick(VIEW_CONTROLLER);
|
||||
|
|
|
@ -20,15 +20,15 @@ var cameraLocations = [ {x: 7971.9, y: 241.3, z: 7304.1}, {x: 7973.0, y: 241.3,
|
|||
var cameraLookAts = [ {x: 7971.1, y: 241.3, z: 7304.1}, {x: 7972.1, y: 241.3, z: 7304.1}, {x: 7972.1, y: 241.3, z: 7304.1}, {x: 7972.1, y: 241.3, z: 7304.1}, {x: 7972.1, y: 241.3, z: 7304.1}, {x: 7971.3, y: 241.3, z: 7304.2} ];
|
||||
|
||||
function saveCameraState() {
|
||||
oldMode = Camera.getMode();
|
||||
oldMode = Camera.mode;
|
||||
avatarPosition = MyAvatar.position;
|
||||
Camera.setModeShiftPeriod(0.0);
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
}
|
||||
|
||||
function restoreCameraState() {
|
||||
Camera.stopLooking();
|
||||
Camera.setMode(oldMode);
|
||||
Camera.mode = oldMode;
|
||||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
|
@ -52,7 +52,7 @@ function keyPressEvent(event) {
|
|||
saveCameraState();
|
||||
freeCamera = true;
|
||||
}
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
Camera.setPosition(cameraLocations[choice - 1]);
|
||||
Camera.keepLookingAt(cameraLookAts[choice - 1]);
|
||||
}
|
||||
|
|
|
@ -20,15 +20,15 @@ var cameraLocations = [ {x: 8027.5, y: 237.5, z: 7305.7}, {x: 8027.5, y: 237.5,
|
|||
var cameraLookAts = [ {x: 8027.5, y: 237.5, z: 7304.0}, {x: 8027.5, y: 237.5, z: 7305.7}, {x: 8027.5, y: 237.5, z: 7304.0}, {x: 8027.5, y: 237.5, z: 7304.0}, {x: 8027.5, y: 237.5, z: 7304.0}, {x: 8027.5, y: 237.5, z: 7304.0} ];
|
||||
|
||||
function saveCameraState() {
|
||||
oldMode = Camera.getMode();
|
||||
oldMode = Camera.mode;
|
||||
avatarPosition = MyAvatar.position;
|
||||
Camera.setModeShiftPeriod(0.0);
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
}
|
||||
|
||||
function restoreCameraState() {
|
||||
Camera.stopLooking();
|
||||
Camera.setMode(oldMode);
|
||||
Camera.mode = oldMode;
|
||||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
|
@ -52,7 +52,7 @@ function keyPressEvent(event) {
|
|||
saveCameraState();
|
||||
freeCamera = true;
|
||||
}
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
Camera.setPosition(cameraLocations[choice - 1]);
|
||||
Camera.keepLookingAt(cameraLookAts[choice - 1]);
|
||||
}
|
||||
|
|
|
@ -20,15 +20,15 @@ var cameraLocations = [ {x: 2921.5, y: 251.3, z: 8254.8}, {x: 2921.5, y: 251.3,
|
|||
var cameraLookAts = [ {x: 2921.5, y: 251.3, z: 8255.7}, {x: 2921.5, y: 251.3, z: 8255.7}, {x: 2921.5, y: 251.3, z: 8255.7}, {x: 2921.5, y: 251.3, z: 8255.7}, {x: 2921.4 , y: 251.3, z: 8255.1} ];
|
||||
|
||||
function saveCameraState() {
|
||||
oldMode = Camera.getMode();
|
||||
oldMode = Camera.mode;
|
||||
avatarPosition = MyAvatar.position;
|
||||
Camera.setModeShiftPeriod(0.0);
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
}
|
||||
|
||||
function restoreCameraState() {
|
||||
Camera.stopLooking();
|
||||
Camera.setMode(oldMode);
|
||||
Camera.mode = oldMode;
|
||||
}
|
||||
|
||||
function update(deltaTime) {
|
||||
|
@ -52,7 +52,7 @@ function keyPressEvent(event) {
|
|||
saveCameraState();
|
||||
freeCamera = true;
|
||||
}
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
Camera.setPosition(cameraLocations[choice - 1]);
|
||||
Camera.keepLookingAt(cameraLookAts[choice - 1]);
|
||||
}
|
||||
|
|
|
@ -50,14 +50,14 @@ var SPAWN_DISTANCE = 1;
|
|||
var DEFAULT_DIMENSION = 0.20;
|
||||
|
||||
var modelURLs = [
|
||||
HIFI_PUBLIC_BUCKET + "meshes/Feisar_Ship.FBX",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/birarda/birarda_head.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/pug.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush1.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush6.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/newInvader16x16-large-purple.svo",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/minotaur/mino_full.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/Combat_tank_V01.FBX",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/orc.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/slimer.fbx"
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed2.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed4.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed7.fbx"
|
||||
];
|
||||
|
||||
var jointList = MyAvatar.getJointNames();
|
||||
|
@ -2787,13 +2787,13 @@ function setupModelMenus() {
|
|||
print("delete exists... don't add ours");
|
||||
}
|
||||
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Paste Models", isCheckable: true });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S",
|
||||
afterItem: "Allow Select Large Models", isCheckable: true });
|
||||
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
||||
|
@ -2809,6 +2809,7 @@ function cleanupModelMenus() {
|
|||
Menu.removeMenuItem("Edit", "Delete");
|
||||
}
|
||||
|
||||
Menu.removeMenuItem("Edit", "Model List...");
|
||||
Menu.removeMenuItem("Edit", "Paste Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Select Large Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Select Small Models");
|
||||
|
@ -2883,16 +2884,21 @@ function handeMenuEvent(menuItem) {
|
|||
} else {
|
||||
print(" Delete Entity.... not holding...");
|
||||
}
|
||||
} else if (menuItem == "Model List") {
|
||||
} else if (menuItem == "Model List...") {
|
||||
var models = new Array();
|
||||
models = Entities.findEntities(MyAvatar.position, Number.MAX_VALUE);
|
||||
for (var i = 0; i < models.length; i++) {
|
||||
models[i].properties = Entities.getEntityProperties(models[i]);
|
||||
models[i].toString = function() {
|
||||
var modelname = decodeURIComponent(
|
||||
this.properties.modelURL.indexOf("/") != -1 ?
|
||||
this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) :
|
||||
this.properties.modelURL);
|
||||
var modelname;
|
||||
if (this.properties.type == "Model") {
|
||||
modelname = decodeURIComponent(
|
||||
this.properties.modelURL.indexOf("/") != -1 ?
|
||||
this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) :
|
||||
this.properties.modelURL);
|
||||
} else {
|
||||
modelname = this.properties.id;
|
||||
}
|
||||
return "[" + this.properties.type + "] " + modelname;
|
||||
};
|
||||
}
|
||||
|
|
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;
|
||||
};
|
||||
})
|
986
examples/followThroughOverlappingAction.js
Normal file
986
examples/followThroughOverlappingAction.js
Normal file
|
@ -0,0 +1,986 @@
|
|||
//
|
||||
// follow-through-and-overlapping-action.js
|
||||
//
|
||||
// Simple demonstration showing the visual effect of adding the Disney
|
||||
// follow through and overlapping action animation technique to avatar movement.
|
||||
//
|
||||
// Designed and created by David Wooldridge and Ozan Serim, August 2014
|
||||
//
|
||||
// Version 1.001
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// location of overlay images
|
||||
var pathToOverlays = "https://s3.amazonaws.com/hifi-public/ArmSwingScript/ArmSwingOverlays/";
|
||||
|
||||
// animation
|
||||
var LEFT = 1;
|
||||
var RIGHT = 2;
|
||||
var DIRECTION = 0;
|
||||
|
||||
// min max
|
||||
var weightMin = 1;
|
||||
var weightMax = 20;
|
||||
var jointEffectMax = 5;
|
||||
|
||||
// animation effect variables
|
||||
var handEffect = 3.4; // 0 to jointEffectMax
|
||||
var forearmEffect = 2.5; // 0 to jointEffectMax
|
||||
var limbWeight = 8; // will only use nearest integer, as defines an array length
|
||||
var effectStrength = 1; // 0 to 1 - overall effect strength
|
||||
// Overshoot: false uses upper arm as driver for forearm and hand
|
||||
// true uses upper arm for forearm and lower arm as driver for hand.
|
||||
var overShoot = false;
|
||||
|
||||
// animate self (tap the 'r' key)
|
||||
var animateSelf = false;
|
||||
var selfAnimateFrequency = 7.5;
|
||||
|
||||
// overlay values
|
||||
var handleValue = 0;
|
||||
var controlPosX = Window.innerWidth / 2 - 500;
|
||||
var controlPosY = 0;
|
||||
var minSliderX = controlPosX + 18;
|
||||
var sliderRangeX = 190;
|
||||
var minHandleX = controlPosX - 50;
|
||||
var handleRangeX = 350 / 2;
|
||||
var handlePosition = 0;
|
||||
|
||||
// background overlay
|
||||
var controllerBackground = Overlays.addOverlay("image", {
|
||||
bounds: {x: controlPosX, y: controlPosY, width: 250, height: 380},
|
||||
imageURL: pathToOverlays + "flourish-augmentation-control-overlay.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
var controllerRadioSelectedBackground = Overlays.addOverlay("image", {
|
||||
bounds: {x: controlPosX, y: controlPosY, width: 250, height: 380},
|
||||
imageURL: pathToOverlays + "flourish-augmentation-control-radio-selected-overlay.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1,
|
||||
visible: false
|
||||
});
|
||||
// handle overlay
|
||||
var applyMotionHandle = Overlays.addOverlay("image", {
|
||||
bounds: {x: minHandleX+handleRangeX-39, y: controlPosY+232,
|
||||
width: 79, height: 100},
|
||||
imageURL: pathToOverlays + "flourish-augmentation-handle-overlay.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
// slider overlays
|
||||
var handEffectSlider = Overlays.addOverlay("image", {
|
||||
bounds: {x: minSliderX + (handEffect / jointEffectMax * sliderRangeX),
|
||||
y: controlPosY + 46, width: 25, height: 25},
|
||||
imageURL: pathToOverlays + "ddao-slider-handle.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
var forearmEffectSlider = Overlays.addOverlay("image", {
|
||||
bounds: {x: minSliderX + (forearmEffect / jointEffectMax * sliderRangeX), y: controlPosY + 86,
|
||||
width: 25, height: 25},
|
||||
imageURL: pathToOverlays + "ddao-slider-handle.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
var limbWeightSlider = Overlays.addOverlay("image", {
|
||||
bounds: {x: minSliderX + (limbWeight / weightMax * sliderRangeX), y: controlPosY+126,
|
||||
width: 25, height: 25},
|
||||
imageURL: pathToOverlays + "ddao-slider-handle.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
var effectStrengthSlider = Overlays.addOverlay("image", {
|
||||
bounds: {x: minSliderX + (effectStrength * sliderRangeX), y: controlPosY+206,
|
||||
width: 25, height: 25},
|
||||
imageURL: pathToOverlays + "ddao-slider-handle.png",
|
||||
color: {red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
// main loop - using averaging filters to add limb element follow-through
|
||||
var upperArmPDampingFilter = [];
|
||||
upperArmPDampingFilter.length = parseInt(limbWeight); // sets amount of damping for upper arm pitch
|
||||
var forearmPDampingFilter = [];
|
||||
forearmPDampingFilter.length = parseInt(limbWeight) + 2; // sets amount of damping for lower arm pitch
|
||||
|
||||
var cumulativeTime = 0;
|
||||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
|
||||
// used for self animating (press r to invoke)
|
||||
cumulativeTime += deltaTime;
|
||||
|
||||
// blend three keyframes using handle position to determine progress between keyframes
|
||||
var animationProgress = handleValue;
|
||||
|
||||
if(animateSelf) {
|
||||
animationProgress = Math.sin(cumulativeTime * selfAnimateFrequency);
|
||||
animationProgress++;
|
||||
animationProgress /= 2;
|
||||
}
|
||||
|
||||
var keyframeOneWeight = 0;
|
||||
var keyframeTwoWeight = 0;
|
||||
var keyframeThreeWeight = 0;
|
||||
|
||||
if(movingHandle || animateSelf) {
|
||||
keyframeOneWeight = 0;
|
||||
keyframeTwoWeight = animationProgress;
|
||||
keyframeThreeWeight = 1 - animationProgress;
|
||||
}
|
||||
else if(!movingHandle) {
|
||||
// idle
|
||||
keyframeOneWeight = 1;
|
||||
keyframeTwoWeight = 0;
|
||||
keyframeThreeWeight = 0;
|
||||
}
|
||||
|
||||
var shoulderPitch =
|
||||
keyframeOneWeight * keyFrameOne.joints[8].pitchOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[8].pitchOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[8].pitchOffset;
|
||||
|
||||
var upperArmPitch =
|
||||
keyframeOneWeight * keyFrameOne.joints[9].pitchOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[9].pitchOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[9].pitchOffset;
|
||||
|
||||
// get the change in upper arm pitch and use to add weight effect to forearm (always) and hand (only for overShoot)
|
||||
var deltaUpperArmPitch = effectStrength *
|
||||
(upperArmPDampingFilter[upperArmPDampingFilter.length - 1] -
|
||||
upperArmPDampingFilter[0]);
|
||||
|
||||
var forearmPitch =
|
||||
keyframeOneWeight * keyFrameOne.joints[10].pitchOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[10].pitchOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[10].pitchOffset -
|
||||
(deltaUpperArmPitch/(jointEffectMax - forearmEffect));
|
||||
|
||||
// there are two methods for calculating the hand follow through
|
||||
var handPitch = 0;
|
||||
if(overShoot) {
|
||||
|
||||
// get the change in forearm pitch and use to add weight effect to hand
|
||||
var deltaForearmPitch = effectStrength *
|
||||
(forearmPDampingFilter[forearmPDampingFilter.length - 1] -
|
||||
forearmPDampingFilter[0]);
|
||||
handPitch =
|
||||
keyframeOneWeight * keyFrameOne.joints[11].pitchOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[11].pitchOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[11].pitchOffset +
|
||||
(deltaForearmPitch /(jointEffectMax - handEffect)); // hand driven by forearm
|
||||
|
||||
} else {
|
||||
|
||||
handPitch =
|
||||
keyframeOneWeight * keyFrameOne.joints[11].pitchOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[11].pitchOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[11].pitchOffset -
|
||||
(deltaUpperArmPitch /(jointEffectMax - handEffect)); // hand driven by upper arm
|
||||
}
|
||||
|
||||
var shoulderYaw =
|
||||
keyframeOneWeight * keyFrameOne.joints[8].yawOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[8].yawOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[8].yawOffset;
|
||||
|
||||
var upperArmYaw =
|
||||
keyframeOneWeight * keyFrameOne.joints[9].yawOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[9].yawOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[9].yawOffset;
|
||||
|
||||
var lowerArmYaw =
|
||||
keyframeOneWeight * keyFrameOne.joints[10].yawOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[10].yawOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[10].yawOffset;
|
||||
|
||||
var handYaw =
|
||||
keyframeOneWeight * keyFrameOne.joints[11].yawOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[11].yawOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[11].yawOffset;
|
||||
|
||||
var shoulderRoll =
|
||||
keyframeOneWeight * keyFrameOne.joints[8].rollOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[8].rollOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[8].rollOffset;
|
||||
|
||||
var upperArmRoll =
|
||||
keyframeOneWeight * keyFrameOne.joints[9].rollOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[9].rollOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[9].rollOffset;
|
||||
|
||||
var lowerArmRoll =
|
||||
keyframeOneWeight * keyFrameOne.joints[10].rollOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[10].rollOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[10].rollOffset;
|
||||
|
||||
var handRoll =
|
||||
keyframeOneWeight * keyFrameOne.joints[11].rollOffset +
|
||||
keyframeTwoWeight * keyFrameTwo.joints[11].rollOffset +
|
||||
keyframeThreeWeight * keyFrameThree.joints[11].rollOffset;
|
||||
|
||||
// filter upper arm pitch
|
||||
upperArmPDampingFilter.push(upperArmPitch);
|
||||
upperArmPDampingFilter.shift();
|
||||
var upperArmPitchFiltered = 0;
|
||||
for(ea in upperArmPDampingFilter) upperArmPitchFiltered += upperArmPDampingFilter[ea];
|
||||
upperArmPitchFiltered /= upperArmPDampingFilter.length;
|
||||
upperArmPitch = (effectStrength * upperArmPitchFiltered) + ((1 - effectStrength) * upperArmPitch);
|
||||
|
||||
// filter forearm pitch only if using for hand follow-though
|
||||
if(overShoot) {
|
||||
|
||||
forearmPDampingFilter.push(forearmPitch);
|
||||
forearmPDampingFilter.shift();
|
||||
var forearmPitchFiltered = 0;
|
||||
for(ea in forearmPDampingFilter) forearmPitchFiltered += forearmPDampingFilter[ea];
|
||||
forearmPitchFiltered /= forearmPDampingFilter.length;
|
||||
forearmPitch = (effectStrength*forearmPitchFiltered) + ((1-effectStrength) * forearmPitch);
|
||||
}
|
||||
|
||||
// apply the new rotation data to the joints
|
||||
MyAvatar.setJointData("RightShoulder", Quat.fromPitchYawRollDegrees(shoulderPitch, shoulderYaw, shoulderRoll));
|
||||
MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(upperArmPitch, -upperArmYaw, upperArmRoll));
|
||||
MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(forearmPitch, lowerArmYaw, lowerArmRoll));
|
||||
MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollDegrees(handPitch, handYaw, handRoll));
|
||||
});
|
||||
|
||||
|
||||
// mouse handling
|
||||
var movingHandEffectSlider = false;
|
||||
var movingForearmEffectSlider = false;
|
||||
var movingLimbWeightSlider = false;
|
||||
var movingDampingSlider = false;
|
||||
var movingEffectStrengthSlider = false;
|
||||
var movingHandle = false;
|
||||
|
||||
function mousePressEvent(event) {
|
||||
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
|
||||
if(clickedOverlay === applyMotionHandle) movingHandle = true;
|
||||
else if(clickedOverlay === handEffectSlider) movingHandEffectSlider = true;
|
||||
else if(clickedOverlay === forearmEffectSlider) movingForearmEffectSlider = true;
|
||||
else if(clickedOverlay === limbWeightSlider) movingLimbWeightSlider = true;
|
||||
else if(clickedOverlay === effectStrengthSlider) movingEffectStrengthSlider = true;
|
||||
else if(clickedOverlay === controllerRadioSelectedBackground &&
|
||||
event.x > 477 && event.x < 497 && event.y > 338 && event.y < 360) {
|
||||
|
||||
overShoot = false;
|
||||
Overlays.editOverlay(controllerBackground, {visible: true});
|
||||
Overlays.editOverlay(controllerRadioSelectedBackground, {visible: false});
|
||||
}
|
||||
else if(clickedOverlay === controllerBackground &&
|
||||
event.x > 477 && event.x < 497 && event.y > 338 && event.y < 360){
|
||||
|
||||
overShoot = true;
|
||||
Overlays.editOverlay(controllerBackground, {visible: false});
|
||||
Overlays.editOverlay(controllerRadioSelectedBackground, {visible: true});
|
||||
}
|
||||
}
|
||||
function mouseMoveEvent(event) {
|
||||
|
||||
if(movingHandle) {
|
||||
|
||||
var thumbClickOffsetX = event.x - minHandleX;
|
||||
var thumbPositionNormalised = (thumbClickOffsetX - handleRangeX) / handleRangeX;
|
||||
if(thumbPositionNormalised <= -1) thumbPositionNormalised = -1;
|
||||
else if(thumbPositionNormalised > 1) thumbPositionNormalised = 1;
|
||||
|
||||
if(thumbPositionNormalised < 0) DIRECTION = LEFT;
|
||||
else DIRECTION = RIGHT;
|
||||
|
||||
handleValue = (thumbPositionNormalised + 1) / 2;
|
||||
var handleX = (thumbPositionNormalised * handleRangeX) + handleRangeX - 39;
|
||||
Overlays.editOverlay(applyMotionHandle, {x: handleX + minHandleX});
|
||||
return;
|
||||
}
|
||||
else if(movingHandEffectSlider) {
|
||||
|
||||
var thumbClickOffsetX = event.x - minSliderX;
|
||||
var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
|
||||
if(thumbPositionNormalised < 0) thumbPositionNormalised = 0;
|
||||
if(thumbPositionNormalised > 1) thumbPositionNormalised = 1;
|
||||
handEffect = (thumbPositionNormalised - 0.08) * jointEffectMax;
|
||||
var sliderX = thumbPositionNormalised * sliderRangeX ;
|
||||
Overlays.editOverlay(handEffectSlider, {x: sliderX + minSliderX});
|
||||
}
|
||||
else if(movingForearmEffectSlider) {
|
||||
|
||||
var thumbClickOffsetX = event.x - minSliderX;
|
||||
var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
|
||||
if(thumbPositionNormalised < 0) thumbPositionNormalised = 0;
|
||||
if(thumbPositionNormalised > 1) thumbPositionNormalised = 1;
|
||||
forearmEffect = (thumbPositionNormalised - 0.1) * jointEffectMax;
|
||||
var sliderX = thumbPositionNormalised * sliderRangeX ;
|
||||
Overlays.editOverlay(forearmEffectSlider, {x: sliderX + minSliderX});
|
||||
}
|
||||
else if(movingLimbWeightSlider) {
|
||||
|
||||
var thumbClickOffsetX = event.x - minSliderX;
|
||||
var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
|
||||
if(thumbPositionNormalised<0) thumbPositionNormalised = 0;
|
||||
if(thumbPositionNormalised>1) thumbPositionNormalised = 1;
|
||||
limbWeight = thumbPositionNormalised * weightMax;
|
||||
if(limbWeight < weightMin) limbWeight = weightMin;
|
||||
upperArmPDampingFilter.length = parseInt(limbWeight);
|
||||
var sliderX = thumbPositionNormalised * sliderRangeX ;
|
||||
Overlays.editOverlay(limbWeightSlider, {x: sliderX + minSliderX});
|
||||
}
|
||||
else if(movingEffectStrengthSlider) {
|
||||
|
||||
var thumbClickOffsetX = event.x - minSliderX;
|
||||
var thumbPositionNormalised = thumbClickOffsetX / sliderRangeX;
|
||||
if(thumbPositionNormalised < 0) thumbPositionNormalised = 0;
|
||||
if(thumbPositionNormalised > 1) thumbPositionNormalised = 1;
|
||||
effectStrength = thumbPositionNormalised;
|
||||
var sliderX = thumbPositionNormalised * sliderRangeX ;
|
||||
Overlays.editOverlay(effectStrengthSlider, {x: sliderX + minSliderX});
|
||||
return;
|
||||
}
|
||||
}
|
||||
function mouseReleaseEvent(event) {
|
||||
|
||||
if(movingHandle) {
|
||||
|
||||
movingHandle = false;
|
||||
handleValue = 0;
|
||||
Overlays.editOverlay(applyMotionHandle, {x: minHandleX+handleRangeX - 39});
|
||||
}
|
||||
else if(movingHandEffectSlider) movingHandEffectSlider = false;
|
||||
else if(movingForearmEffectSlider) movingForearmEffectSlider = false;
|
||||
else if(movingLimbWeightSlider) movingLimbWeightSlider = false;
|
||||
else if(movingEffectStrengthSlider) movingEffectStrengthSlider = false;
|
||||
else if(movingDampingSlider) movingDampingSlider = false;
|
||||
}
|
||||
|
||||
// set up mouse and keyboard callbacks
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
|
||||
// keyboard command
|
||||
function keyPressEvent(event) {
|
||||
|
||||
if (event.text == "q") {
|
||||
print('hand effect = ' + handEffect + '\n');
|
||||
print('forearmEffect = ' + forearmEffect + '\n');
|
||||
print('limbWeight = ' + limbWeight + '\n');
|
||||
print('effectStrength = ' + effectStrength + '\n');
|
||||
}
|
||||
else if (event.text == "r") {
|
||||
animateSelf = !animateSelf;
|
||||
}
|
||||
else if (event.text == "[") {
|
||||
selfAnimateFrequency += 0.5;
|
||||
print('selfAnimateFrequency = '+selfAnimateFrequency);
|
||||
}
|
||||
else if (event.text == "]") {
|
||||
selfAnimateFrequency -= 0.5;
|
||||
print('selfAnimateFrequency = '+selfAnimateFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// zero out all joints
|
||||
function resetJoints() {
|
||||
|
||||
var avatarJointNames = MyAvatar.getJointNames();
|
||||
for (var i = 0; i < avatarJointNames.length; i++)
|
||||
MyAvatar.clearJointData(avatarJointNames[i]);
|
||||
}
|
||||
|
||||
// Script ending
|
||||
Script.scriptEnding.connect(function() {
|
||||
|
||||
// delete the overlays
|
||||
Overlays.deleteOverlay(controllerBackground);
|
||||
Overlays.deleteOverlay(controllerRadioSelectedBackground);
|
||||
Overlays.deleteOverlay(handEffectSlider);
|
||||
Overlays.deleteOverlay(forearmEffectSlider);
|
||||
Overlays.deleteOverlay(limbWeightSlider);
|
||||
Overlays.deleteOverlay(effectStrengthSlider);
|
||||
Overlays.deleteOverlay(applyMotionHandle);
|
||||
|
||||
// leave the avi in zeroed out stance
|
||||
resetJoints();
|
||||
});
|
||||
|
||||
|
||||
// animation data. animation keyframes produced using walk.js
|
||||
MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(80,0,0));
|
||||
var keyFrameOne =
|
||||
{
|
||||
"name":"FemaleStandingOne",
|
||||
"settings":{
|
||||
"baseFrequency":70,
|
||||
"flyingHipsPitch":60,
|
||||
"takeFlightVelocity":40,
|
||||
"maxBankingAngle":40
|
||||
},
|
||||
"adjusters":{
|
||||
"legsSeparation":{
|
||||
"strength":-0.03679245283018867,
|
||||
"separationAngle":50
|
||||
},
|
||||
"stride":{
|
||||
"strength":0,
|
||||
"upperLegsPitch":30,
|
||||
"lowerLegsPitch":15,
|
||||
"upperLegsPitchOffset":0.2,
|
||||
"lowerLegsPitchOffset":1.5
|
||||
}
|
||||
},
|
||||
"joints":[
|
||||
{
|
||||
"name":"hips",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0,
|
||||
"thrust":0,
|
||||
"bob":0,
|
||||
"sway":0,
|
||||
"thrustPhase":180,
|
||||
"bobPhase":0,
|
||||
"swayPhase":-90,
|
||||
"thrustOffset":0,
|
||||
"bobOffset":0,
|
||||
"swayOffset":0
|
||||
},
|
||||
{
|
||||
"name":"upperLegs",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"lowerLegs",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"feet",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"toes",
|
||||
"pitch":2.0377358490566038,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":4.415094339622641,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine",
|
||||
"pitch":1.660377358490566,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":-180,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine1",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine2",
|
||||
"pitch":2.1132075471698113,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":-0.6792452830188722,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"shoulders",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0.6792452830188678,
|
||||
"yawOffset":-5.20754716981132,
|
||||
"rollOffset":-2.9433962264150937
|
||||
},
|
||||
{
|
||||
"name":"upperArms",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":77.77358490566039,
|
||||
"yawOffset":9.169811320754715,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"lowerArms",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"hands",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":1.6981132075471694,
|
||||
"yawOffset":-1.0188679245283017,
|
||||
"rollOffset":1.0188679245283017
|
||||
},
|
||||
{
|
||||
"name":"head",
|
||||
"pitch":0,
|
||||
"yaw":1.7358490566037734,
|
||||
"roll":1.5094339622641508,
|
||||
"pitchPhase":-90.33962264150944,
|
||||
"yawPhase":94.41509433962267,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":1.6981132075471694,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
}
|
||||
]
|
||||
};
|
||||
var keyFrameTwo =
|
||||
{
|
||||
"name":"FemaleStandingOne",
|
||||
"settings":{
|
||||
"baseFrequency":70,
|
||||
"flyingHipsPitch":60,
|
||||
"takeFlightVelocity":40,
|
||||
"maxBankingAngle":40
|
||||
},
|
||||
"adjusters":{
|
||||
"legsSeparation":{
|
||||
"strength":-0.03679245283018867,
|
||||
"separationAngle":50
|
||||
},
|
||||
"stride":{
|
||||
"strength":0,
|
||||
"upperLegsPitch":30,
|
||||
"lowerLegsPitch":15,
|
||||
"upperLegsPitchOffset":0.2,
|
||||
"lowerLegsPitchOffset":1.5
|
||||
}
|
||||
},
|
||||
"joints":[
|
||||
{
|
||||
"name":"hips",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0,
|
||||
"thrust":0,
|
||||
"bob":0,
|
||||
"sway":0,
|
||||
"thrustPhase":180,
|
||||
"bobPhase":0,
|
||||
"swayPhase":-90,
|
||||
"thrustOffset":0,
|
||||
"bobOffset":0,
|
||||
"swayOffset":0
|
||||
},
|
||||
{
|
||||
"name":"upperLegs",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"lowerLegs",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"feet",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"toes",
|
||||
"pitch":2.0377358490566038,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":4.415094339622641,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine",
|
||||
"pitch":1.660377358490566,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":-180,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine1",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine2",
|
||||
"pitch":2.1132075471698113,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":-0.6792452830188722,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"shoulders",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0.6792452830188678,
|
||||
"yawOffset":-5.20754716981132,
|
||||
"rollOffset":-2.9433962264150937
|
||||
},
|
||||
{
|
||||
"name":"upperArms",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":49.584905660377345,
|
||||
"yawOffset":9.169811320754715,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"lowerArms",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"hands",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":-13,
|
||||
"yawOffset":-1.0188679245283017,
|
||||
"rollOffset":1.0188679245283017
|
||||
},
|
||||
{
|
||||
"name":"head",
|
||||
"pitch":0,
|
||||
"yaw":1.7358490566037734,
|
||||
"roll":1.5094339622641508,
|
||||
"pitchPhase":-90.33962264150944,
|
||||
"yawPhase":94.41509433962267,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":1.6981132075471694,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
}
|
||||
]
|
||||
};
|
||||
var keyFrameThree =
|
||||
{
|
||||
"name":"FemaleStandingOne",
|
||||
"settings":{
|
||||
"baseFrequency":70,
|
||||
"flyingHipsPitch":60,
|
||||
"takeFlightVelocity":40,
|
||||
"maxBankingAngle":40
|
||||
},
|
||||
"adjusters":{
|
||||
"legsSeparation":{
|
||||
"strength":-0.03679245283018867,
|
||||
"separationAngle":50
|
||||
},
|
||||
"stride":{
|
||||
"strength":0,
|
||||
"upperLegsPitch":30,
|
||||
"lowerLegsPitch":15,
|
||||
"upperLegsPitchOffset":0.2,
|
||||
"lowerLegsPitchOffset":1.5
|
||||
}
|
||||
},
|
||||
"joints":[
|
||||
{
|
||||
"name":"hips",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0,
|
||||
"thrust":0,
|
||||
"bob":0,
|
||||
"sway":0,
|
||||
"thrustPhase":180,
|
||||
"bobPhase":0,
|
||||
"swayPhase":-90,
|
||||
"thrustOffset":0,
|
||||
"bobOffset":0,
|
||||
"swayOffset":0
|
||||
},
|
||||
{
|
||||
"name":"upperLegs",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"lowerLegs",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"feet",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"toes",
|
||||
"pitch":2.0377358490566038,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":4.415094339622641,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine",
|
||||
"pitch":1.660377358490566,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":-180,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine1",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"spine2",
|
||||
"pitch":2.1132075471698113,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":-0.6792452830188722,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"shoulders",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":-21.0566037735849,
|
||||
"yawOffset":-5.20754716981132,
|
||||
"rollOffset":-2.9433962264150937
|
||||
},
|
||||
{
|
||||
"name":"upperArms",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":-33.28301886792452,
|
||||
"yawOffset":9.169811320754715,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"lowerArms",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":0,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
},
|
||||
{
|
||||
"name":"hands",
|
||||
"pitch":0,
|
||||
"yaw":0,
|
||||
"roll":0,
|
||||
"pitchPhase":0,
|
||||
"yawPhase":0,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":-13,
|
||||
"yawOffset":-1.0188679245283017,
|
||||
"rollOffset":1.0188679245283017
|
||||
},
|
||||
{
|
||||
"name":"head",
|
||||
"pitch":0,
|
||||
"yaw":1.7358490566037734,
|
||||
"roll":1.5094339622641508,
|
||||
"pitchPhase":-90.33962264150944,
|
||||
"yawPhase":94.41509433962267,
|
||||
"rollPhase":0,
|
||||
"pitchOffset":1.6981132075471694,
|
||||
"yawOffset":0,
|
||||
"rollOffset":0
|
||||
}
|
||||
]
|
||||
};
|
|
@ -53,12 +53,12 @@ var lastYawTurned = 0.0;
|
|||
var startPullbackPosition;
|
||||
|
||||
function saveCameraState() {
|
||||
oldMode = Camera.getMode();
|
||||
Camera.setMode("independent");
|
||||
oldMode = Camera.mode;
|
||||
Camera.mode = "independent";
|
||||
}
|
||||
|
||||
function restoreCameraState() {
|
||||
Camera.setMode(oldMode);
|
||||
Camera.mode = oldMode;
|
||||
}
|
||||
|
||||
function activateWarp() {
|
||||
|
|
|
@ -17,13 +17,17 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var PI = 3.14 // No need for something more precise
|
||||
var PI = Math.PI;
|
||||
var RAD_TO_DEG = 180.0 / PI;
|
||||
|
||||
var AZIMUTH_RATE = 90.0;
|
||||
var ALTITUDE_RATE = 200.0;
|
||||
var RADIUS_RATE = 1.0 / 100.0;
|
||||
var PAN_RATE = 50.0;
|
||||
|
||||
var Y_AXIS = { x: 0, y: 1, z: 0 };
|
||||
var X_AXIS = { x: 1, y: 0, z: 0 };
|
||||
|
||||
var alt = false;
|
||||
var shift = false;
|
||||
var control = false;
|
||||
|
@ -53,6 +57,18 @@ var avatarPosition;
|
|||
var avatarOrientation;
|
||||
|
||||
|
||||
function orientationOf(vector) {
|
||||
var direction,
|
||||
yaw,
|
||||
pitch;
|
||||
|
||||
direction = Vec3.normalize(vector);
|
||||
yaw = Quat.angleAxis(Math.atan2(direction.x, direction.z) * RAD_TO_DEG, Y_AXIS);
|
||||
pitch = Quat.angleAxis(Math.asin(-direction.y) * RAD_TO_DEG, X_AXIS);
|
||||
return Quat.multiply(yaw, pitch);
|
||||
}
|
||||
|
||||
|
||||
function handleRadialMode(dx, dy) {
|
||||
azimuth += dx / AZIMUTH_RATE;
|
||||
radius += radius * dy * RADIUS_RATE;
|
||||
|
@ -65,6 +81,7 @@ function handleRadialMode(dx, dy) {
|
|||
z: (Math.cos(altitude) * Math.sin(azimuth)) * radius };
|
||||
position = Vec3.sum(center, vector);
|
||||
Camera.setPosition(position);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
}
|
||||
|
||||
function handleOrbitMode(dx, dy) {
|
||||
|
@ -82,6 +99,7 @@ function handleOrbitMode(dx, dy) {
|
|||
z:(Math.cos(altitude) * Math.sin(azimuth)) * radius };
|
||||
position = Vec3.sum(center, vector);
|
||||
Camera.setPosition(position);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
}
|
||||
|
||||
|
||||
|
@ -96,19 +114,18 @@ function handlePanMode(dx, dy) {
|
|||
position = Vec3.sum(position, dv);
|
||||
|
||||
Camera.setPosition(position);
|
||||
Camera.keepLookingAt(center);
|
||||
Camera.setOrientation(orientationOf(vector));
|
||||
}
|
||||
|
||||
function saveCameraState() {
|
||||
oldMode = Camera.getMode();
|
||||
oldMode = Camera.mode;
|
||||
var oldPosition = Camera.getPosition();
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
Camera.setPosition(oldPosition);
|
||||
}
|
||||
|
||||
function restoreCameraState() {
|
||||
Camera.stopLooking();
|
||||
Camera.setMode(oldMode);
|
||||
Camera.mode = oldMode;
|
||||
}
|
||||
|
||||
function handleModes() {
|
||||
|
@ -245,7 +262,6 @@ function mousePressEvent(event) {
|
|||
azimuth = Math.atan2(vector.z, vector.x);
|
||||
altitude = Math.asin(vector.y / Vec3.length(vector));
|
||||
|
||||
Camera.keepLookingAt(center);
|
||||
print(string);
|
||||
isActive = true;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ var leapHands = (function () {
|
|||
var isOnHMD,
|
||||
LEAP_ON_HMD_MENU_ITEM = "Leap Motion on HMD",
|
||||
LEAP_OFFSET = 0.019, // Thickness of Leap Motion plus HMD clip
|
||||
HMD_OFFSET = 0.100, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief
|
||||
HMD_OFFSET = 0.070, // Eyeballs to front surface of Oculus DK2 TODO: Confirm and make depend on device and eye relief
|
||||
hands,
|
||||
wrists,
|
||||
NUM_HANDS = 2, // 0 = left; 1 = right
|
||||
|
|
|
@ -34,6 +34,13 @@ var EASING_MULTIPLIER = 8;
|
|||
var INITIAL_ZOOM_DISTANCE = 2;
|
||||
var INITIAL_ZOOM_DISTANCE_FIRST_PERSON = 3;
|
||||
|
||||
var easeOutCubic = function(t) {
|
||||
t--;
|
||||
return t * t * t + 1;
|
||||
};
|
||||
|
||||
EASE_TIME = 0.5;
|
||||
|
||||
CameraManager = function() {
|
||||
var that = {};
|
||||
|
||||
|
@ -51,6 +58,10 @@ CameraManager = function() {
|
|||
that.focalPoint = { x: 0, y: 0, z: 0 };
|
||||
that.targetFocalPoint = { x: 0, y: 0, z: 0 };
|
||||
|
||||
easing = false;
|
||||
easingTime = 0;
|
||||
startOrientation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
|
||||
that.previousCameraMode = null;
|
||||
|
||||
that.lastMousePosition = { x: 0, y: 0 };
|
||||
|
@ -66,10 +77,6 @@ CameraManager = function() {
|
|||
var focalPoint = Vec3.sum(Camera.getPosition(),
|
||||
Vec3.multiply(that.zoomDistance, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
if (Camera.getMode() == 'first person') {
|
||||
that.targetZoomDistance = INITIAL_ZOOM_DISTANCE_FIRST_PERSON;
|
||||
}
|
||||
|
||||
// Determine the correct yaw and pitch to keep the camera in the same location
|
||||
var dPos = Vec3.subtract(focalPoint, Camera.getPosition());
|
||||
var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z);
|
||||
|
@ -81,12 +88,12 @@ CameraManager = function() {
|
|||
|
||||
that.focalPoint = focalPoint;
|
||||
that.setFocalPoint(focalPoint);
|
||||
that.previousCameraMode = Camera.getMode();
|
||||
Camera.setMode("independent");
|
||||
that.previousCameraMode = Camera.mode;
|
||||
Camera.mode = "independent";
|
||||
|
||||
that.updateCamera();
|
||||
|
||||
cameraTool.setVisible(true);
|
||||
cameraTool.setVisible(false);
|
||||
}
|
||||
|
||||
that.disable = function(ignoreCamera) {
|
||||
|
@ -95,18 +102,40 @@ CameraManager = function() {
|
|||
that.mode = MODE_INACTIVE;
|
||||
|
||||
if (!ignoreCamera) {
|
||||
Camera.setMode(that.previousCameraMode);
|
||||
Camera.mode = that.previousCameraMode;
|
||||
}
|
||||
cameraTool.setVisible(false);
|
||||
}
|
||||
|
||||
that.focus = function(entityProperties) {
|
||||
var dim = SelectionManager.worldDimensions;
|
||||
var size = Math.max(dim.x, Math.max(dim.y, dim.z));
|
||||
that.focus = function(position, dimensions, easeOrientation) {
|
||||
if (dimensions) {
|
||||
var size = Math.max(dimensions.x, Math.max(dimensions.y, dimensions.z));
|
||||
that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM);
|
||||
} else {
|
||||
that.targetZoomDistance = Vec3.length(Vec3.subtract(Camera.getPosition(), position));
|
||||
}
|
||||
|
||||
that.targetZoomDistance = Math.max(size * FOCUS_ZOOM_SCALE, FOCUS_MIN_ZOOM);
|
||||
if (easeOrientation) {
|
||||
// Do eased turning towards target
|
||||
that.focalPoint = that.targetFocalPoint = position;
|
||||
|
||||
that.setFocalPoint(SelectionManager.worldPosition);
|
||||
that.zoomDistance = that.targetZoomDistance = Vec3.length(Vec3.subtract(Camera.getPosition(), position));
|
||||
|
||||
var dPos = Vec3.subtract(that.focalPoint, Camera.getPosition());
|
||||
var xzDist = Math.sqrt(dPos.x * dPos.x + dPos.z * dPos.z);
|
||||
|
||||
that.targetPitch = -Math.atan2(dPos.y, xzDist) * 180 / Math.PI;
|
||||
that.targetYaw = Math.atan2(dPos.x, dPos.z) * 180 / Math.PI;
|
||||
that.pitch = that.targetPitch;
|
||||
that.yaw = that.targetYaw;
|
||||
|
||||
startOrientation = Camera.getOrientation();
|
||||
|
||||
easing = true;
|
||||
easingTime = 0;
|
||||
} else {
|
||||
that.setFocalPoint(position);
|
||||
}
|
||||
|
||||
that.updateCamera();
|
||||
}
|
||||
|
@ -242,7 +271,7 @@ CameraManager = function() {
|
|||
}
|
||||
|
||||
that.updateCamera = function() {
|
||||
if (!that.enabled || Camera.getMode() != "independent") return;
|
||||
if (!that.enabled || Camera.mode != "independent") return;
|
||||
|
||||
var yRot = Quat.angleAxis(that.yaw, { x: 0, y: 1, z: 0 });
|
||||
var xRot = Quat.angleAxis(that.pitch, { x: 1, y: 0, z: 0 });
|
||||
|
@ -255,6 +284,11 @@ CameraManager = function() {
|
|||
xRot = Quat.angleAxis(-that.pitch, { x: 1, y: 0, z: 0 });
|
||||
q = Quat.multiply(yRot, xRot);
|
||||
|
||||
if (easing) {
|
||||
var t = easeOutCubic(easingTime / EASE_TIME);
|
||||
q = Quat.slerp(startOrientation, q, t);
|
||||
}
|
||||
|
||||
Camera.setOrientation(q);
|
||||
}
|
||||
|
||||
|
@ -266,10 +300,14 @@ CameraManager = function() {
|
|||
|
||||
// Ease the position and orbit of the camera
|
||||
that.update = function(dt) {
|
||||
if (Camera.getMode() != "independent") {
|
||||
if (Camera.mode != "independent") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (easing) {
|
||||
easingTime = Math.min(EASE_TIME, easingTime + dt);
|
||||
}
|
||||
|
||||
var scale = Math.min(dt * EASING_MULTIPLIER, 1.0);
|
||||
|
||||
var dYaw = that.targetYaw - that.yaw;
|
||||
|
@ -292,12 +330,15 @@ CameraManager = function() {
|
|||
that.zoomDistance += scale * dZoom;
|
||||
|
||||
that.updateCamera();
|
||||
|
||||
if (easingTime >= 1) {
|
||||
easing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Last mode that was first or third person
|
||||
var lastAvatarCameraMode = "first person";
|
||||
Camera.modeUpdated.connect(function(newMode) {
|
||||
print("Camera mode has been updated: " + newMode);
|
||||
if (newMode == "first person" || newMode == "third person") {
|
||||
lastAvatarCameraMode = newMode;
|
||||
that.disable(true);
|
||||
|
@ -308,7 +349,7 @@ CameraManager = function() {
|
|||
|
||||
Controller.keyReleaseEvent.connect(function (event) {
|
||||
if (event.text == "ESC" && that.enabled) {
|
||||
Camera.setMode(lastAvatarCameraMode);
|
||||
Camera.mode = lastAvatarCameraMode;
|
||||
cameraManager.disable(true);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -209,12 +209,6 @@ SelectionDisplay = (function () {
|
|||
var lastCameraOrientation = Camera.getOrientation();
|
||||
var lastPlaneIntersection;
|
||||
|
||||
var currentSelection = { id: -1, isKnownID: false };
|
||||
var entitySelected = false;
|
||||
var selectedEntityProperties;
|
||||
var selectedEntityPropertiesOriginalPosition;
|
||||
var selectedEntityPropertiesOriginalDimensions;
|
||||
|
||||
var handleHoverColor = { red: 224, green: 67, blue: 36 };
|
||||
var handleHoverAlpha = 1.0;
|
||||
|
||||
|
@ -656,20 +650,12 @@ SelectionDisplay = (function () {
|
|||
};
|
||||
|
||||
that.select = function(entityID, event) {
|
||||
var properties = Entities.getEntityProperties(entityID);
|
||||
// if (currentSelection.isKnownID == true) {
|
||||
// that.unselect(currentSelection);
|
||||
// }
|
||||
currentSelection = entityID;
|
||||
entitySelected = true;
|
||||
var properties = Entities.getEntityProperties(SelectionManager.selections[0]);
|
||||
|
||||
// lastCameraPosition = Camera.getPosition();
|
||||
lastCameraPosition = Camera.getPosition();
|
||||
lastCameraOrientation = Camera.getOrientation();
|
||||
|
||||
if (event !== false) {
|
||||
selectedEntityProperties = properties;
|
||||
selectedEntityPropertiesOriginalPosition = properties.position;
|
||||
selectedEntityPropertiesOriginalDimensions = properties.dimensions;
|
||||
pickRay = Camera.computePickRay(event.x, event.y);
|
||||
lastPlaneIntersection = rayPlaneIntersection(pickRay, properties.position, Quat.getFront(lastCameraOrientation));
|
||||
|
||||
|
@ -678,8 +664,6 @@ SelectionDisplay = (function () {
|
|||
print("select() with EVENT...... ");
|
||||
print(" event.y:" + event.y);
|
||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
||||
Vec3.print(" originalPosition:", selectedEntityPropertiesOriginalPosition);
|
||||
Vec3.print(" originalDimensions:", selectedEntityPropertiesOriginalDimensions);
|
||||
Vec3.print(" current position:", properties.position);
|
||||
}
|
||||
|
||||
|
@ -939,11 +923,6 @@ SelectionDisplay = (function () {
|
|||
};
|
||||
|
||||
that.unselectAll = function () {
|
||||
if (currentSelection.isKnownID == true) {
|
||||
that.unselect(currentSelection);
|
||||
}
|
||||
currentSelection = { id: -1, isKnownID: false };
|
||||
entitySelected = false;
|
||||
};
|
||||
|
||||
that.updateHandles = function() {
|
||||
|
@ -952,7 +931,9 @@ SelectionDisplay = (function () {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
that.updateRotationHandles();
|
||||
that.highlightSelectable();
|
||||
|
||||
var rotation, dimensions, position;
|
||||
|
||||
|
@ -1113,12 +1094,6 @@ SelectionDisplay = (function () {
|
|||
};
|
||||
|
||||
that.unselect = function (entityID) {
|
||||
that.setOverlaysVisible(false);
|
||||
|
||||
Entities.editEntity(entityID, { localRenderAlpha: 1.0 });
|
||||
|
||||
currentSelection = { id: -1, isKnownID: false };
|
||||
entitySelected = false;
|
||||
};
|
||||
|
||||
var initialXZPick = null;
|
||||
|
@ -1207,15 +1182,16 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
}
|
||||
|
||||
tooltip.updateText(selectedEntityProperties);
|
||||
that.select(currentSelection, false); // TODO: this should be more than highlighted
|
||||
SelectionManager._update();
|
||||
}
|
||||
};
|
||||
|
||||
var lastXYPick = null
|
||||
addGrabberTool(grabberMoveUp, {
|
||||
mode: "TRANSLATE_UP_DOWN",
|
||||
onBegin: function(event) {
|
||||
lastXYPick = rayPlaneIntersection(pickRay, SelectionManager.worldPosition, Quat.getFront(lastCameraOrientation));
|
||||
|
||||
SelectionManager.saveProperties();
|
||||
|
||||
// Duplicate entities if alt is pressed. This will make a
|
||||
|
@ -1243,7 +1219,7 @@ SelectionDisplay = (function () {
|
|||
|
||||
// translate mode left/right based on view toward entity
|
||||
var newIntersection = rayPlaneIntersection(pickRay,
|
||||
selectedEntityPropertiesOriginalPosition,
|
||||
SelectionManager.worldPosition,
|
||||
Quat.getFront(lastCameraOrientation));
|
||||
|
||||
var vector = Vec3.subtract(newIntersection, lastPlaneIntersection);
|
||||
|
@ -1253,8 +1229,6 @@ SelectionDisplay = (function () {
|
|||
vector.x = 0;
|
||||
vector.z = 0;
|
||||
|
||||
// newPosition = Vec3.sum(selectedEntityPropertiesOriginalPosition, vector);
|
||||
|
||||
var wantDebug = false;
|
||||
if (wantDebug) {
|
||||
print("translateUpDown... ");
|
||||
|
@ -1262,8 +1236,6 @@ SelectionDisplay = (function () {
|
|||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
||||
Vec3.print(" newIntersection:", newIntersection);
|
||||
Vec3.print(" vector:", vector);
|
||||
Vec3.print(" originalPosition:", selectedEntityPropertiesOriginalPosition);
|
||||
Vec3.print(" recentPosition:", selectedEntityProperties.position);
|
||||
Vec3.print(" newPosition:", newPosition);
|
||||
}
|
||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
|
@ -1273,8 +1245,6 @@ SelectionDisplay = (function () {
|
|||
Entities.editEntity(SelectionManager.selections[i], properties);
|
||||
}
|
||||
|
||||
tooltip.updateText(selectedEntityProperties);
|
||||
that.select(currentSelection, false); // TODO: this should be more than highlighted
|
||||
SelectionManager._update();
|
||||
},
|
||||
});
|
||||
|
@ -1306,7 +1276,7 @@ SelectionDisplay = (function () {
|
|||
var rotation = null;
|
||||
|
||||
var onBegin = function(event) {
|
||||
var properties = Entities.getEntityProperties(currentSelection);
|
||||
var properties = Entities.getEntityProperties(SelectionManager.selections[0]);
|
||||
initialProperties = properties;
|
||||
rotation = spaceMode == SPACE_LOCAL ? properties.rotation : Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
|
||||
|
@ -1445,11 +1415,11 @@ SelectionDisplay = (function () {
|
|||
var changeInPosition = Vec3.multiplyQbyV(rotation, vec3Mult(p, changeInDimensions));
|
||||
var newPosition = Vec3.sum(initialPosition, changeInPosition);
|
||||
|
||||
|
||||
selectedEntityProperties.position = newPosition;
|
||||
selectedEntityProperties.dimensions = newDimensions;
|
||||
for (var i = 0; i < SelectionManager.selections.length; i++) {
|
||||
Entities.editEntity(SelectionManager.selections[i], selectedEntityProperties);
|
||||
Entities.editEntity(SelectionManager.selections[i], {
|
||||
position: newPosition,
|
||||
dimensions: newDimensions,
|
||||
});
|
||||
}
|
||||
|
||||
var wantDebug = false;
|
||||
|
@ -1460,18 +1430,14 @@ SelectionDisplay = (function () {
|
|||
Vec3.print(" vector:", vector);
|
||||
Vec3.print(" oldPOS:", oldPOS);
|
||||
Vec3.print(" newPOS:", newPOS);
|
||||
Vec3.print(" oldDimensions:", selectedEntityPropertiesOriginalDimensions);
|
||||
Vec3.print(" changeInDimensions:", changeInDimensions);
|
||||
Vec3.print(" newDimensions:", newDimensions);
|
||||
|
||||
Vec3.print(" oldPosition:", selectedEntityPropertiesOriginalPosition);
|
||||
Vec3.print(" changeInPosition:", changeInPosition);
|
||||
Vec3.print(" newPosition:", newPosition);
|
||||
}
|
||||
|
||||
tooltip.updateText(selectedEntityProperties);
|
||||
SelectionManager._update();
|
||||
that.select(currentSelection, false); // TODO: this should be more than highlighted
|
||||
|
||||
};
|
||||
|
||||
|
@ -1590,7 +1556,6 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
|
||||
if (result.intersects) {
|
||||
var properties = Entities.getEntityProperties(currentSelection);
|
||||
var center = yawCenter;
|
||||
var zero = yawZero;
|
||||
var centerToZero = Vec3.subtract(center, zero);
|
||||
|
@ -1664,8 +1629,8 @@ SelectionDisplay = (function () {
|
|||
// Size the overlays to the current selection size
|
||||
var diagonal = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1;
|
||||
var halfDimensions = Vec3.multiply(selectionManager.worldDimensions, 0.5);
|
||||
var innerRadius = diagonal;
|
||||
var outerRadius = diagonal * 1.15;
|
||||
innerRadius = diagonal;
|
||||
outerRadius = diagonal * 1.15;
|
||||
var innerAlpha = 0.2;
|
||||
var outerAlpha = 0.2;
|
||||
Overlays.editOverlay(rotateOverlayInner,
|
||||
|
@ -1721,7 +1686,7 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
|
||||
if (result.intersects) {
|
||||
var properties = Entities.getEntityProperties(currentSelection);
|
||||
var properties = Entities.getEntityProperties(selectionManager.selections[0]);
|
||||
var center = pitchCenter;
|
||||
var zero = pitchZero;
|
||||
var centerToZero = Vec3.subtract(center, zero);
|
||||
|
@ -1794,8 +1759,8 @@ SelectionDisplay = (function () {
|
|||
// Size the overlays to the current selection size
|
||||
var diagonal = (Vec3.length(selectionManager.worldDimensions) / 2) * 1.1;
|
||||
var halfDimensions = Vec3.multiply(selectionManager.worldDimensions, 0.5);
|
||||
var innerRadius = diagonal;
|
||||
var outerRadius = diagonal * 1.15;
|
||||
innerRadius = diagonal;
|
||||
outerRadius = diagonal * 1.15;
|
||||
var innerAlpha = 0.2;
|
||||
var outerAlpha = 0.2;
|
||||
Overlays.editOverlay(rotateOverlayInner,
|
||||
|
@ -1851,7 +1816,7 @@ SelectionDisplay = (function () {
|
|||
}
|
||||
|
||||
if (result.intersects) {
|
||||
var properties = Entities.getEntityProperties(currentSelection);
|
||||
var properties = Entities.getEntityProperties(selectionManager.selections[0]);
|
||||
var center = rollCenter;
|
||||
var zero = rollZero;
|
||||
var centerToZero = Vec3.subtract(center, zero);
|
||||
|
@ -1915,9 +1880,9 @@ SelectionDisplay = (function () {
|
|||
});
|
||||
|
||||
that.checkMove = function() {
|
||||
if (currentSelection.isKnownID &&
|
||||
if (SelectionManager.hasSelection() &&
|
||||
(!Vec3.equal(Camera.getPosition(), lastCameraPosition) || !Quat.equal(Camera.getOrientation(), lastCameraOrientation))){
|
||||
that.select(currentSelection, false, false);
|
||||
that.select(selectionManager.selections[0], false, false);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2033,7 +1998,7 @@ SelectionDisplay = (function () {
|
|||
var overlayOrientation;
|
||||
var overlayCenter;
|
||||
|
||||
var properties = Entities.getEntityProperties(currentSelection);
|
||||
var properties = Entities.getEntityProperties(selectionManager.selections[0]);
|
||||
var angles = Quat.safeEulerAngles(properties.rotation);
|
||||
var pitch = angles.x;
|
||||
var yaw = angles.y;
|
||||
|
@ -2169,12 +2134,11 @@ SelectionDisplay = (function () {
|
|||
|
||||
if (somethingClicked) {
|
||||
pickRay = Camera.computePickRay(event.x, event.y);
|
||||
lastPlaneIntersection = rayPlaneIntersection(pickRay, selectedEntityPropertiesOriginalPosition,
|
||||
lastPlaneIntersection = rayPlaneIntersection(pickRay, selectionManager.worldPosition,
|
||||
Quat.getFront(lastCameraOrientation));
|
||||
if (wantDebug) {
|
||||
print("mousePressEvent()...... " + overlayNames[result.overlayID]);
|
||||
Vec3.print(" lastPlaneIntersection:", lastPlaneIntersection);
|
||||
Vec3.print(" originalPosition:", selectedEntityPropertiesOriginalPosition);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2289,8 +2253,8 @@ SelectionDisplay = (function () {
|
|||
};
|
||||
|
||||
that.updateHandleSizes = function() {
|
||||
if (selectedEntityProperties) {
|
||||
var diff = Vec3.subtract(selectedEntityProperties.position, Camera.getPosition());
|
||||
if (selectionManager.hasSelection()) {
|
||||
var diff = Vec3.subtract(selectionManager.worldPosition, Camera.getPosition());
|
||||
var grabberSize = Vec3.length(diff) * GRABBER_DISTANCE_TO_SIZE_RATIO;
|
||||
for (var i = 0; i < stretchHandles.length; i++) {
|
||||
Overlays.editOverlay(stretchHandles[i], {
|
||||
|
@ -2336,15 +2300,10 @@ SelectionDisplay = (function () {
|
|||
mode = "UNKNOWN";
|
||||
|
||||
// if something is selected, then reset the "original" properties for any potential next click+move operation
|
||||
if (entitySelected) {
|
||||
|
||||
if (SelectionManager.hasSelection()) {
|
||||
if (showHandles) {
|
||||
that.select(currentSelection, event);
|
||||
that.select(SelectionManager.selections[0], event);
|
||||
}
|
||||
|
||||
selectedEntityProperties = Entities.getEntityProperties(currentSelection);
|
||||
selectedEntityPropertiesOriginalPosition = properties.position;
|
||||
selectedEntityPropertiesOriginalDimensions = properties.dimensions;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -40,20 +40,18 @@ var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8};
|
|||
var HELMET_ATTACHMENT_URL = "https://hifi-public.s3.amazonaws.com/models/attachments/IronManMaskOnly.fbx"
|
||||
|
||||
function reticlePosition() {
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
var reticleRay = Camera.computePickRay(screenSize.x / 2, screenSize.y / 2);
|
||||
var RETICLE_DISTANCE = 1;
|
||||
return Vec3.sum(reticleRay.origin, Vec3.multiply(reticleRay.direction, RETICLE_DISTANCE));
|
||||
return Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), RETICLE_DISTANCE));
|
||||
}
|
||||
|
||||
function drawLobby() {
|
||||
if (!panelWall) {
|
||||
print("Adding overlays for the lobby panel wall and orb shell.");
|
||||
|
||||
var cameraEuler = Quat.safeEulerAngles(Camera.getOrientation());
|
||||
var cameraEuler = Quat.safeEulerAngles(Camera.orientation);
|
||||
var towardsMe = Quat.angleAxis(cameraEuler.y + 180, { x: 0, y: 1, z: 0});
|
||||
|
||||
var orbPosition = Vec3.sum(Camera.getPosition(), Vec3.multiplyQbyV(towardsMe, ORB_SHIFT));
|
||||
var orbPosition = Vec3.sum(Camera.position, Vec3.multiplyQbyV(towardsMe, ORB_SHIFT));
|
||||
|
||||
var panelWallProps = {
|
||||
url: HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyPrototype/Lobby5_PanelsWithFrames.fbx",
|
||||
|
@ -76,14 +74,15 @@ function drawLobby() {
|
|||
orbShell = Overlays.addOverlay("model", orbShellProps);
|
||||
|
||||
// for HMD wearers, create a reticle in center of screen
|
||||
var RETICLE_SPHERE_SIZE = 0.025;
|
||||
var CURSOR_SCALE = 0.025;
|
||||
|
||||
reticle = Overlays.addOverlay("sphere", {
|
||||
reticle = Overlays.addOverlay("billboard", {
|
||||
url: HIFI_PUBLIC_BUCKET + "images/cursor.svg",
|
||||
position: reticlePosition(),
|
||||
size: RETICLE_SPHERE_SIZE,
|
||||
color: { red: 0, green: 255, blue: 0 },
|
||||
ignoreRayIntersection: true,
|
||||
isFacingAvatar: true,
|
||||
alpha: 1.0,
|
||||
solid: true
|
||||
scale: CURSOR_SCALE
|
||||
});
|
||||
|
||||
// add an attachment on this avatar so other people see them in the lobby
|
||||
|
@ -130,12 +129,13 @@ function cleanupLobby() {
|
|||
|
||||
function actionStartEvent(event) {
|
||||
if (panelWall) {
|
||||
|
||||
// we've got an action event and our panel wall is up
|
||||
// check if we hit a panel and if we should jump there
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var result = Overlays.findRayIntersection(pickRay);
|
||||
var result = Overlays.findRayIntersection(event.actionRay);
|
||||
|
||||
if (result.intersects && result.overlayID == panelWall) {
|
||||
|
||||
var panelName = result.extraInfo;
|
||||
var panelStringIndex = panelName.indexOf("Panel");
|
||||
if (panelStringIndex != -1) {
|
||||
|
@ -174,7 +174,7 @@ function maybeCleanupLobby() {
|
|||
|
||||
function toggleEnvironmentRendering(shouldRender) {
|
||||
Menu.setIsOptionChecked("Voxels", shouldRender);
|
||||
Menu.setIsOptionChecked("Models", shouldRender);
|
||||
Menu.setIsOptionChecked("Entities", shouldRender);
|
||||
Menu.setIsOptionChecked("Metavoxels", shouldRender);
|
||||
Menu.setIsOptionChecked("Avatars", shouldRender);
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@
|
|||
//
|
||||
|
||||
var lookingAtSomething = false;
|
||||
var oldMode = Camera.getMode();
|
||||
var oldMode = Camera.mode;
|
||||
|
||||
function cancelLookAt() {
|
||||
if (lookingAtSomething) {
|
||||
lookingAtSomething = false;
|
||||
Camera.stopLooking();
|
||||
Camera.setMode(oldMode);
|
||||
Camera.mode = oldMode;
|
||||
releaseMovementKeys();
|
||||
}
|
||||
}
|
||||
|
@ -65,13 +65,13 @@ function mousePressEvent(event) {
|
|||
if (intersection.intersects) {
|
||||
|
||||
// remember the old mode we were in
|
||||
oldMode = Camera.getMode();
|
||||
oldMode = Camera.mode;
|
||||
|
||||
print("looking at intersection point: " + intersection.intersection.x + ", "
|
||||
+ intersection.intersection.y + ", " + intersection.intersection.z);
|
||||
|
||||
// switch to independent mode
|
||||
Camera.setMode("independent");
|
||||
Camera.mode = "independent";
|
||||
|
||||
// tell the camera to fix it's look at on the point we clicked
|
||||
Camera.keepLookingAt(intersection.intersection);
|
||||
|
|
|
@ -51,15 +51,21 @@ var wantEntityGlow = false;
|
|||
var SPAWN_DISTANCE = 1;
|
||||
var DEFAULT_DIMENSION = 0.20;
|
||||
|
||||
var MENU_INSPECT_TOOL_ENABLED = "Inspect Tool";
|
||||
var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus";
|
||||
|
||||
var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled";
|
||||
var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";
|
||||
|
||||
var modelURLs = [
|
||||
HIFI_PUBLIC_BUCKET + "meshes/Feisar_Ship.FBX",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/birarda/birarda_head.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/pug.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush1.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush6.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/newInvader16x16-large-purple.svo",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/minotaur/mino_full.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/Combat_tank_V01.FBX",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/orc.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "meshes/slimer.fbx"
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed2.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed4.fbx",
|
||||
HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed7.fbx"
|
||||
];
|
||||
|
||||
var mode = 0;
|
||||
|
@ -256,7 +262,7 @@ var toolBar = (function () {
|
|||
if (activeButton === toolBar.clicked(clickedOverlay)) {
|
||||
isActive = !isActive;
|
||||
if (!isActive) {
|
||||
selectionDisplay.unselectAll();
|
||||
selectionManager.clearSelections();
|
||||
cameraManager.disable();
|
||||
} else {
|
||||
cameraManager.enable();
|
||||
|
@ -386,10 +392,7 @@ function isLocked(properties) {
|
|||
}
|
||||
|
||||
|
||||
var entitySelected = false;
|
||||
var selectedEntityID;
|
||||
var selectedEntityProperties;
|
||||
var mouseLastPosition;
|
||||
var orientation;
|
||||
var intersection;
|
||||
|
||||
|
@ -403,160 +406,158 @@ function rayPlaneIntersection(pickRay, point, normal) {
|
|||
return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t));
|
||||
}
|
||||
|
||||
function findClickedEntity(event) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
var foundIntersection = Entities.findRayIntersection(pickRay);
|
||||
|
||||
if (!foundIntersection.accurate) {
|
||||
return null;
|
||||
}
|
||||
var foundEntity = foundIntersection.entityID;
|
||||
|
||||
if (!foundEntity.isKnownID) {
|
||||
var identify = Entities.identifyEntity(foundEntity);
|
||||
if (!identify.isKnownID) {
|
||||
print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")");
|
||||
selectionManager.clearSelections();
|
||||
return null;
|
||||
}
|
||||
foundEntity = identify;
|
||||
}
|
||||
|
||||
return { pickRay: pickRay, entityID: foundEntity };
|
||||
}
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
mouseLastPosition = { x: event.x, y: event.y };
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
|
||||
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)
|
||||
|| cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) {
|
||||
// Event handled; do nothing.
|
||||
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) {
|
||||
return;
|
||||
} else {
|
||||
entitySelected = false;
|
||||
selectionDisplay.unselectAll();
|
||||
|
||||
// If we aren't active and didn't click on an overlay: quit
|
||||
if (!isActive) {
|
||||
}
|
||||
if (isActive) {
|
||||
var entitySelected = false;
|
||||
if (cameraManager.mousePressEvent(event) || selectionDisplay.mousePressEvent(event)) {
|
||||
// Event handled; do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
Vec3.print("[Mouse] Looking at: ", pickRay.origin);
|
||||
var foundIntersection = Entities.findRayIntersection(pickRay);
|
||||
|
||||
if(!foundIntersection.accurate) {
|
||||
return;
|
||||
}
|
||||
var foundEntity = foundIntersection.entityID;
|
||||
|
||||
if (!foundEntity.isKnownID) {
|
||||
var identify = Entities.identifyEntity(foundEntity);
|
||||
if (!identify.isKnownID) {
|
||||
print("Unknown ID " + identify.id + " (update loop " + foundEntity.id + ")");
|
||||
} else {
|
||||
var result = findClickedEntity(event);
|
||||
if (result === null) {
|
||||
selectionManager.clearSelections();
|
||||
return;
|
||||
}
|
||||
foundEntity = identify;
|
||||
}
|
||||
var pickRay = result.pickRay;
|
||||
var foundEntity = result.entityID;
|
||||
|
||||
var properties = Entities.getEntityProperties(foundEntity);
|
||||
if (isLocked(properties)) {
|
||||
print("Model locked " + properties.id);
|
||||
} else {
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
var properties = Entities.getEntityProperties(foundEntity);
|
||||
if (isLocked(properties)) {
|
||||
print("Model locked " + properties.id);
|
||||
} else {
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal);
|
||||
// P P - Model
|
||||
// /| A - Palm
|
||||
// / | d B - unit vector toward tip
|
||||
// / | X - base of the perpendicular line
|
||||
// A---X----->B d - distance fom axis
|
||||
// x x - distance from A
|
||||
//
|
||||
// |X-A| = (P-A).B
|
||||
// X == A + ((P-A).B)B
|
||||
// d = |P-X|
|
||||
print("Checking properties: " + properties.id + " " + properties.isKnownID + " - Half Diagonal:" + halfDiagonal);
|
||||
// P P - Model
|
||||
// /| A - Palm
|
||||
// / | d B - unit vector toward tip
|
||||
// / | X - base of the perpendicular line
|
||||
// A---X----->B d - distance fom axis
|
||||
// x x - distance from A
|
||||
//
|
||||
// |X-A| = (P-A).B
|
||||
// X == A + ((P-A).B)B
|
||||
// d = |P-X|
|
||||
|
||||
var A = pickRay.origin;
|
||||
var B = Vec3.normalize(pickRay.direction);
|
||||
var P = properties.position;
|
||||
var A = pickRay.origin;
|
||||
var B = Vec3.normalize(pickRay.direction);
|
||||
var P = properties.position;
|
||||
|
||||
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||
var X = Vec3.sum(A, Vec3.multiply(B, x));
|
||||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
var x = Vec3.dot(Vec3.subtract(P, A), B);
|
||||
var X = Vec3.sum(A, Vec3.multiply(B, x));
|
||||
var d = Vec3.length(Vec3.subtract(P, X));
|
||||
var halfDiagonal = Vec3.length(properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (0 < x && sizeOK) {
|
||||
entitySelected = true;
|
||||
selectedEntityID = foundEntity;
|
||||
selectedEntityProperties = properties;
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
if (0 < x && sizeOK) {
|
||||
entitySelected = true;
|
||||
selectedEntityID = foundEntity;
|
||||
orientation = MyAvatar.orientation;
|
||||
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
|
||||
|
||||
if (!event.isShifted) {
|
||||
selectionManager.clearSelections();
|
||||
if (!event.isShifted) {
|
||||
selectionManager.clearSelections();
|
||||
}
|
||||
selectionManager.addEntity(foundEntity);
|
||||
|
||||
print("Model selected: " + foundEntity.id);
|
||||
}
|
||||
selectionManager.addEntity(foundEntity);
|
||||
|
||||
print("Model selected selectedEntityID:" + selectedEntityID.id);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entitySelected) {
|
||||
selectedEntityProperties.oldDimensions = selectedEntityProperties.dimensions;
|
||||
selectedEntityProperties.oldPosition = {
|
||||
x: selectedEntityProperties.position.x,
|
||||
y: selectedEntityProperties.position.y,
|
||||
z: selectedEntityProperties.position.z,
|
||||
};
|
||||
selectedEntityProperties.oldRotation = {
|
||||
x: selectedEntityProperties.rotation.x,
|
||||
y: selectedEntityProperties.rotation.y,
|
||||
z: selectedEntityProperties.rotation.z,
|
||||
w: selectedEntityProperties.rotation.w,
|
||||
};
|
||||
selectedEntityProperties.glowLevel = 0.0;
|
||||
|
||||
print("Clicked on " + selectedEntityID.id + " " + entitySelected);
|
||||
tooltip.updateText(selectedEntityProperties);
|
||||
tooltip.show(true);
|
||||
selectionDisplay.select(selectedEntityID, event);
|
||||
if (entitySelected) {
|
||||
selectionDisplay.select(selectedEntityID, event);
|
||||
}
|
||||
} else if (Menu.isOptionChecked(MENU_INSPECT_TOOL_ENABLED)) {
|
||||
var result = findClickedEntity(event);
|
||||
if (event.isRightButton) {
|
||||
if (result !== null) {
|
||||
var currentProperties = Entities.getEntityProperties(result.entityID);
|
||||
cameraManager.enable();
|
||||
cameraManager.focus(currentProperties.position, null, Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
cameraManager.mousePressEvent(event);
|
||||
}
|
||||
} else {
|
||||
cameraManager.mousePressEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var highlightedEntityID = { isKnownID: false };
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing
|
||||
if (selectionDisplay.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var entityIntersection = Entities.findRayIntersection(pickRay);
|
||||
if (entityIntersection.accurate) {
|
||||
if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) {
|
||||
selectionDisplay.unhighlightSelectable(highlightedEntityID);
|
||||
highlightedEntityID = { id: -1, isKnownID: false };
|
||||
if (isActive) {
|
||||
// allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing
|
||||
if (selectionDisplay.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(),
|
||||
entityIntersection.properties.position)) * 180 / 3.14;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (entityIntersection.entityID.isKnownID && sizeOK) {
|
||||
if (wantEntityGlow) {
|
||||
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var entityIntersection = Entities.findRayIntersection(pickRay);
|
||||
if (entityIntersection.accurate) {
|
||||
if(highlightedEntityID.isKnownID && highlightedEntityID.id != entityIntersection.entityID.id) {
|
||||
selectionDisplay.unhighlightSelectable(highlightedEntityID);
|
||||
highlightedEntityID = { id: -1, isKnownID: false };
|
||||
}
|
||||
highlightedEntityID = entityIntersection.entityID;
|
||||
selectionDisplay.highlightSelectable(entityIntersection.entityID);
|
||||
}
|
||||
|
||||
var halfDiagonal = Vec3.length(entityIntersection.properties.dimensions) / 2.0;
|
||||
|
||||
var angularSize = 2 * Math.atan(halfDiagonal / Vec3.distance(Camera.getPosition(),
|
||||
entityIntersection.properties.position)) * 180 / 3.14;
|
||||
|
||||
var sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
|
||||
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
|
||||
|
||||
if (entityIntersection.entityID.isKnownID && sizeOK) {
|
||||
if (wantEntityGlow) {
|
||||
Entities.editEntity(entityIntersection.entityID, { glowLevel: 0.25 });
|
||||
}
|
||||
highlightedEntityID = entityIntersection.entityID;
|
||||
selectionDisplay.highlightSelectable(entityIntersection.entityID);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
cameraManager.mouseMoveEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (!isActive) {
|
||||
return;
|
||||
}
|
||||
if (entitySelected) {
|
||||
if (isActive && selectionManager.hasSelection()) {
|
||||
tooltip.show(false);
|
||||
}
|
||||
|
||||
cameraManager.mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
|
@ -584,6 +585,7 @@ function setupModelMenus() {
|
|||
print("delete exists... don't add ours");
|
||||
}
|
||||
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." });
|
||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L",
|
||||
afterItem: "Paste Models", isCheckable: true });
|
||||
|
@ -594,6 +596,11 @@ function setupModelMenus() {
|
|||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
||||
Menu.addMenuItem({ menuName: "Developer", menuItemName: "Debug Ryans Rotation Problems", isCheckable: true });
|
||||
|
||||
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_INSPECT_TOOL_ENABLED, afterItem: "Edit Entities Help...",
|
||||
isCheckable: true, isChecked: Settings.getValue(SETTING_INSPECT_TOOL_ENABLED) == "true" });
|
||||
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED,
|
||||
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
|
||||
}
|
||||
|
||||
setupModelMenus(); // do this when first running our script.
|
||||
|
@ -606,6 +613,7 @@ function cleanupModelMenus() {
|
|||
Menu.removeMenuItem("Edit", "Delete");
|
||||
}
|
||||
|
||||
Menu.removeMenuItem("Edit", "Model List...");
|
||||
Menu.removeMenuItem("Edit", "Paste Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Select Large Models");
|
||||
Menu.removeMenuItem("Edit", "Allow Select Small Models");
|
||||
|
@ -614,9 +622,15 @@ function cleanupModelMenus() {
|
|||
Menu.removeMenuItem("File", "Export Models");
|
||||
Menu.removeMenuItem("File", "Import Models");
|
||||
Menu.removeMenuItem("Developer", "Debug Ryans Rotation Problems");
|
||||
|
||||
Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED);
|
||||
Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function() {
|
||||
Settings.setValue(SETTING_INSPECT_TOOL_ENABLED, Menu.isOptionChecked(MENU_INSPECT_TOOL_ENABLED));
|
||||
Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
|
||||
progressDialog.cleanup();
|
||||
toolBar.cleanup();
|
||||
cleanupModelMenus();
|
||||
|
@ -642,7 +656,7 @@ function handeMenuEvent(menuItem) {
|
|||
allowLargeModels = Menu.isOptionChecked("Allow Select Large Models");
|
||||
} else if (menuItem == "Delete") {
|
||||
if (SelectionManager.hasSelection()) {
|
||||
print(" Delete Entity.... selectedEntityID="+ selectedEntityID);
|
||||
print(" Delete Entities");
|
||||
SelectionManager.saveProperties();
|
||||
var savedProperties = [];
|
||||
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||
|
@ -657,11 +671,41 @@ function handeMenuEvent(menuItem) {
|
|||
}
|
||||
SelectionManager.clearSelections();
|
||||
pushCommandForSelections([], savedProperties);
|
||||
selectionDisplay.unselect(selectedEntityID);
|
||||
entitySelected = false;
|
||||
} else {
|
||||
print(" Delete Entity.... not holding...");
|
||||
}
|
||||
} else if (menuItem == "Model List...") {
|
||||
var models = new Array();
|
||||
models = Entities.findEntities(MyAvatar.position, Number.MAX_VALUE);
|
||||
for (var i = 0; i < models.length; i++) {
|
||||
models[i].properties = Entities.getEntityProperties(models[i]);
|
||||
models[i].toString = function() {
|
||||
var modelname;
|
||||
if (this.properties.type == "Model") {
|
||||
modelname = decodeURIComponent(
|
||||
this.properties.modelURL.indexOf("/") != -1 ?
|
||||
this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) :
|
||||
this.properties.modelURL);
|
||||
} else {
|
||||
modelname = this.properties.id;
|
||||
}
|
||||
return "[" + this.properties.type + "] " + modelname;
|
||||
};
|
||||
}
|
||||
var form = [{label: "Model: ", options: models}];
|
||||
form.push({label: "Action: ", options: ["Properties", "Delete", "Teleport"]});
|
||||
form.push({ button: "Cancel" });
|
||||
if (Window.form("Model List", form)) {
|
||||
var selectedModel = form[0].value;
|
||||
if (form[1].value == "Properties") {
|
||||
editModelID = selectedModel;
|
||||
showPropertiesForm(editModelID);
|
||||
} else if (form[1].value == "Delete") {
|
||||
Entities.deleteEntity(selectedModel);
|
||||
} else if (form[1].value == "Teleport") {
|
||||
MyAvatar.position = selectedModel.properties.position;
|
||||
}
|
||||
}
|
||||
} else if (menuItem == "Edit Properties...") {
|
||||
// good place to put the properties dialog
|
||||
|
||||
|
@ -706,10 +750,10 @@ Controller.keyReleaseEvent.connect(function (event) {
|
|||
} else if (event.text == "TAB") {
|
||||
selectionDisplay.toggleSpaceMode();
|
||||
} else if (event.text == "f") {
|
||||
if (entitySelected) {
|
||||
// Get latest properties
|
||||
var properties = Entities.getEntityProperties(selectedEntityID);
|
||||
cameraManager.focus(properties);
|
||||
if (isActive) {
|
||||
cameraManager.focus(selectionManager.worldPosition,
|
||||
selectionManager.worldDimensions,
|
||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||
}
|
||||
} else if (event.text == '[') {
|
||||
if (isActive) {
|
||||
|
@ -788,7 +832,6 @@ function applyEntityProperties(data) {
|
|||
var properties = data.createEntities[i].properties;
|
||||
var newEntityID = Entities.addEntity(properties);
|
||||
DELETED_ENTITY_MAP[entityID.id] = newEntityID;
|
||||
print(newEntityID.isKnownID);
|
||||
if (data.selectCreated) {
|
||||
selectedEntityIDs.push(newEntityID);
|
||||
}
|
||||
|
|
2036
interface/resources/html/edit-entities-commands.html
Normal file
2036
interface/resources/html/edit-entities-commands.html
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 193 KiB |
|
@ -14,9 +14,6 @@
|
|||
// the depth texture
|
||||
uniform sampler2D depthTexture;
|
||||
|
||||
// the normal texture
|
||||
uniform sampler2D normalTexture;
|
||||
|
||||
// the random rotation texture
|
||||
uniform sampler2D rotationTexture;
|
||||
|
||||
|
@ -60,11 +57,10 @@ vec3 texCoordToViewSpace(vec2 texCoord) {
|
|||
}
|
||||
|
||||
void main(void) {
|
||||
vec3 rotationZ = texture2D(normalTexture, gl_TexCoord[0].st).xyz * 2.0 - vec3(1.0, 1.0, 1.0);
|
||||
vec3 rotationY = normalize(cross(rotationZ, texture2D(rotationTexture,
|
||||
gl_TexCoord[0].st * noiseScale).xyz - vec3(0.5, 0.5, 0.5)));
|
||||
mat3 rotation = mat3(cross(rotationY, rotationZ), rotationY, rotationZ);
|
||||
|
||||
vec3 rotationX = texture2D(rotationTexture, gl_TexCoord[0].st * noiseScale).rgb;
|
||||
vec3 rotationY = normalize(cross(rotationX, vec3(0.0, 0.0, 1.0)));
|
||||
mat3 rotation = mat3(rotationX, rotationY, cross(rotationX, rotationY));
|
||||
|
||||
vec3 center = texCoordToViewSpace(gl_TexCoord[0].st);
|
||||
|
||||
vec2 rdenominator = 1.0 / (rightTop - leftBottom);
|
||||
|
|
|
@ -107,8 +107,8 @@ 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;
|
||||
|
||||
const QString CHECK_VERSION_URL = "https://highfidelity.io/latestVersion.xml";
|
||||
|
@ -148,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(),
|
||||
|
@ -182,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
|
||||
|
@ -347,9 +347,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
billboardPacketTimer->start(AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS);
|
||||
|
||||
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
||||
cache->setMaximumCacheSize(MAXIMUM_CACHE_SIZE);
|
||||
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
|
||||
networkAccessManager.setCache(cache);
|
||||
|
||||
|
@ -585,7 +585,7 @@ void Application::initializeGL() {
|
|||
// update before the first render
|
||||
update(1.f / _fps);
|
||||
|
||||
InfoView::showFirstTime();
|
||||
InfoView::showFirstTime(INFO_HELP_PATH);
|
||||
}
|
||||
|
||||
void Application::paintGL() {
|
||||
|
@ -601,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);
|
||||
}
|
||||
|
||||
|
@ -1116,7 +1116,8 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_Space: {
|
||||
if (!event->isAutoRepeat()) {
|
||||
// this starts an HFActionEvent
|
||||
HFActionEvent startActionEvent(HFActionEvent::startType(), getViewportCenter());
|
||||
HFActionEvent startActionEvent(HFActionEvent::startType(),
|
||||
_viewFrustum.computePickRay(0.5f, 0.5f));
|
||||
sendEvent(this, &startActionEvent);
|
||||
}
|
||||
|
||||
|
@ -1207,7 +1208,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
case Qt::Key_Space: {
|
||||
if (!event->isAutoRepeat()) {
|
||||
// this ends the HFActionEvent
|
||||
HFActionEvent endActionEvent(HFActionEvent::endType(), getViewportCenter());
|
||||
HFActionEvent endActionEvent(HFActionEvent::endType(), _viewFrustum.computePickRay(0.5f, 0.5f));
|
||||
sendEvent(this, &endActionEvent);
|
||||
}
|
||||
|
||||
|
@ -1248,6 +1249,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
|
||||
|
||||
|
@ -1269,6 +1272,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
|
||||
|
@ -1296,7 +1302,8 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
}
|
||||
|
||||
// nobody handled this - make it an action event on the _window object
|
||||
HFActionEvent actionEvent(HFActionEvent::startType(), event->localPos());
|
||||
HFActionEvent actionEvent(HFActionEvent::startType(),
|
||||
_myCamera.computePickRay(event->x(), event->y()));
|
||||
sendEvent(this, &actionEvent);
|
||||
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
|
@ -1306,6 +1313,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
|
||||
|
@ -1327,7 +1337,8 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
}
|
||||
|
||||
// fire an action end event
|
||||
HFActionEvent actionEvent(HFActionEvent::endType(), event->localPos());
|
||||
HFActionEvent actionEvent(HFActionEvent::endType(),
|
||||
_myCamera.computePickRay(event->x(), event->y()));
|
||||
sendEvent(this, &actionEvent);
|
||||
}
|
||||
}
|
||||
|
@ -1471,12 +1482,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) {
|
||||
|
@ -1558,12 +1568,9 @@ void Application::setEnableVRMode(bool enableVRMode) {
|
|||
OculusManager::disconnect();
|
||||
OculusManager::connect();
|
||||
}
|
||||
int oculusMaxFPS = Menu::getInstance()->getOculusUIMaxFPS();
|
||||
setRenderTargetFramerate(oculusMaxFPS);
|
||||
OculusManager::recalibrate();
|
||||
} else {
|
||||
OculusManager::abandonCalibration();
|
||||
setRenderTargetFramerate(0);
|
||||
}
|
||||
|
||||
resizeGL(_glWidget->getDeviceWidth(), _glWidget->getDeviceHeight());
|
||||
|
@ -1943,6 +1950,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);
|
||||
|
@ -2338,7 +2349,7 @@ void Application::update(float deltaTime) {
|
|||
if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) {
|
||||
queryOctree(NodeType::VoxelServer, PacketTypeVoxelQuery, _voxelServerJurisdictions);
|
||||
}
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) {
|
||||
queryOctree(NodeType::EntityServer, PacketTypeEntityQuery, _entityServerJurisdictions);
|
||||
}
|
||||
_lastQueriedViewFrustum = _viewFrustum;
|
||||
|
@ -2985,7 +2996,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
|
|||
}
|
||||
|
||||
// render models...
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) {
|
||||
PerformanceTimer perfTimer("entities");
|
||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||
"Application::displaySide() ... entities...");
|
||||
|
@ -3179,7 +3190,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 +3833,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);
|
||||
|
@ -3866,9 +3849,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
|
|||
scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features
|
||||
scriptEngine->setAvatarHashMap(&_avatarManager, "AvatarList");
|
||||
|
||||
CameraScriptableObject* cameraScriptable = new CameraScriptableObject(&_myCamera, &_viewFrustum);
|
||||
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
|
||||
connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater()));
|
||||
scriptEngine->registerGlobalObject("Camera", &_myCamera);
|
||||
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
scriptEngine->registerGlobalObject("SpeechRecognizer", Menu::getInstance()->getSpeechRecognizer());
|
||||
|
@ -3932,6 +3913,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 +4250,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 +4316,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,6 +128,9 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f;
|
|||
|
||||
static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND;
|
||||
|
||||
static const QString INFO_HELP_PATH = "html/interface-welcome-allsvg.html";
|
||||
static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-entities-commands.html";
|
||||
|
||||
class Application : public QApplication {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -284,8 +287,6 @@ public:
|
|||
PointShader& getPointShader() { return _pointShader; }
|
||||
FileLogger* getLogger() { return _logger; }
|
||||
|
||||
QPointF getViewportCenter() const
|
||||
{ return QPointF(_glWidget->getDeviceWidth() / 2.0f, _glWidget->getDeviceHeight() / 2.0f); }
|
||||
glm::vec2 getViewportDimensions() const { return glm::vec2(_glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); }
|
||||
NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; }
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
@ -300,7 +301,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:
|
||||
|
||||
|
@ -364,12 +372,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();
|
||||
|
||||
|
@ -621,9 +624,7 @@ private:
|
|||
quint64 _lastNackTime;
|
||||
quint64 _lastSendDownstreamAudioStats;
|
||||
|
||||
int _renderTargetFramerate;
|
||||
bool _isVSyncOn;
|
||||
float _renderResolutionScale;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -72,9 +72,9 @@ float Camera::getFarClip() const {
|
|||
: std::numeric_limits<int16_t>::max() - 1;
|
||||
}
|
||||
|
||||
void Camera::setMode(CameraMode m) {
|
||||
_mode = m;
|
||||
emit modeUpdated(m);
|
||||
void Camera::setMode(CameraMode mode) {
|
||||
_mode = mode;
|
||||
emit modeUpdated(modeToString(mode));
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,57 +94,45 @@ void Camera::setFarClip(float f) {
|
|||
_farClip = f;
|
||||
}
|
||||
|
||||
CameraScriptableObject::CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum) :
|
||||
_camera(camera), _viewFrustum(viewFrustum)
|
||||
{
|
||||
connect(_camera, &Camera::modeUpdated, this, &CameraScriptableObject::onModeUpdated);
|
||||
}
|
||||
|
||||
PickRay CameraScriptableObject::computePickRay(float x, float y) {
|
||||
PickRay Camera::computePickRay(float x, float y) {
|
||||
float screenWidth = Application::getInstance()->getGLWidget()->width();
|
||||
float screenHeight = Application::getInstance()->getGLWidget()->height();
|
||||
PickRay result;
|
||||
if (OculusManager::isConnected()) {
|
||||
result.origin = _camera->getPosition();
|
||||
result.origin = getPosition();
|
||||
Application::getInstance()->getApplicationOverlay().computeOculusPickRay(x / screenWidth, y / screenHeight, result.direction);
|
||||
} else {
|
||||
_viewFrustum->computePickRay(x / screenWidth, y / screenHeight, result.origin, result.direction);
|
||||
Application::getInstance()->getViewFrustum()->computePickRay(x / screenWidth, y / screenHeight,
|
||||
result.origin, result.direction);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString CameraScriptableObject::getMode() const {
|
||||
return modeToString(_camera->getMode());
|
||||
}
|
||||
|
||||
void CameraScriptableObject::setMode(const QString& mode) {
|
||||
CameraMode currentMode = _camera->getMode();
|
||||
CameraMode targetMode = currentMode;
|
||||
if (mode == "third person") {
|
||||
targetMode = CAMERA_MODE_THIRD_PERSON;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
} else if (mode == "first person") {
|
||||
targetMode = CAMERA_MODE_FIRST_PERSON;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
} else if (mode == "mirror") {
|
||||
targetMode = CAMERA_MODE_MIRROR;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
} else if (mode == "independent") {
|
||||
targetMode = CAMERA_MODE_INDEPENDENT;
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
void Camera::setModeString(const QString& mode) {
|
||||
CameraMode targetMode = stringToMode(mode);
|
||||
|
||||
switch (targetMode) {
|
||||
case CAMERA_MODE_THIRD_PERSON:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
break;
|
||||
case CAMERA_MODE_MIRROR:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
break;
|
||||
case CAMERA_MODE_INDEPENDENT:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (currentMode != targetMode) {
|
||||
_camera->setMode(targetMode);
|
||||
|
||||
if (_mode != targetMode) {
|
||||
setMode(targetMode);
|
||||
}
|
||||
}
|
||||
|
||||
void CameraScriptableObject::onModeUpdated(CameraMode m) {
|
||||
emit modeUpdated(modeToString(m));
|
||||
QString Camera::getModeString() const {
|
||||
return modeToString(_mode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ static int cameraModeId = qRegisterMetaType<CameraMode>();
|
|||
|
||||
class Camera : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
|
||||
Q_PROPERTY(QString mode READ getModeString WRITE setModeString)
|
||||
public:
|
||||
Camera();
|
||||
|
||||
|
@ -39,7 +43,6 @@ public:
|
|||
|
||||
void update( float deltaTime );
|
||||
|
||||
void setPosition(const glm::vec3& p) { _position = p; }
|
||||
void setRotation(const glm::quat& rotation) { _rotation = rotation; };
|
||||
void setHmdPosition(const glm::vec3& hmdPosition) { _hmdPosition = hmdPosition; }
|
||||
void setHmdRotation(const glm::quat& hmdRotation) { _hmdRotation = hmdRotation; };
|
||||
|
@ -66,12 +69,20 @@ public:
|
|||
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
|
||||
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
|
||||
float getScale() const { return _scale; }
|
||||
public slots:
|
||||
QString getModeString() const;
|
||||
void setModeString(const QString& mode);
|
||||
|
||||
signals:
|
||||
void modeUpdated(CameraMode newMode);
|
||||
void setPosition(const glm::vec3& position) { _position = position; }
|
||||
|
||||
private:
|
||||
void setOrientation(const glm::quat& orientation) { setRotation(orientation); }
|
||||
glm::quat getOrientation() const { return getRotation(); }
|
||||
|
||||
PickRay computePickRay(float x, float y);
|
||||
signals:
|
||||
void modeUpdated(const QString& newMode);
|
||||
|
||||
private:
|
||||
CameraMode _mode;
|
||||
glm::vec3 _position;
|
||||
float _fieldOfView; // degrees
|
||||
|
@ -88,32 +99,4 @@ private:
|
|||
float _scale;
|
||||
};
|
||||
|
||||
|
||||
class CameraScriptableObject : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
CameraScriptableObject(Camera* camera, ViewFrustum* viewFrustum);
|
||||
|
||||
public slots:
|
||||
QString getMode() const;
|
||||
void setMode(const QString& mode);
|
||||
void setPosition(const glm::vec3& value) { _camera->setPosition(value);}
|
||||
|
||||
glm::vec3 getPosition() const { return _camera->getPosition(); }
|
||||
|
||||
void setOrientation(const glm::quat& value) { _camera->setRotation(value); }
|
||||
glm::quat getOrientation() const { return _camera->getRotation(); }
|
||||
|
||||
PickRay computePickRay(float x, float y);
|
||||
|
||||
signals:
|
||||
void modeUpdated(const QString& newMode);
|
||||
|
||||
private slots:
|
||||
void onModeUpdated(CameraMode m);
|
||||
|
||||
private:
|
||||
Camera* _camera;
|
||||
ViewFrustum* _viewFrustum;
|
||||
};
|
||||
#endif // hifi_Camera_h
|
||||
|
|
|
@ -41,7 +41,7 @@ void FileLogger::addMessage(QString message) {
|
|||
QFile file(_fileName);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
||||
QTextStream out(&file);
|
||||
out << message;
|
||||
out << message << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,6 @@ Menu::Menu() :
|
|||
_maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM),
|
||||
_voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
|
||||
_oculusUIAngularSize(DEFAULT_OCULUS_UI_ANGULAR_SIZE),
|
||||
_oculusUIMaxFPS(DEFAULT_OCULUS_UI_MAX_FPS),
|
||||
_sixenseReticleMoveSpeed(DEFAULT_SIXENSE_RETICLE_MOVE_SPEED),
|
||||
_invertSixenseButtons(DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS),
|
||||
_automaticAvatarLOD(true),
|
||||
|
@ -359,6 +358,7 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails()));
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, this, SLOT(octreeStatsDetails()));
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::EditEntitiesHelp, 0, this, SLOT(showEditEntitiesHelp()));
|
||||
|
||||
QMenu* developerMenu = addMenu("Developer");
|
||||
|
||||
|
@ -366,7 +366,7 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Avatars, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Models, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Entities, 0, true);
|
||||
|
||||
QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows");
|
||||
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
|
||||
|
@ -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,
|
||||
|
@ -433,14 +432,16 @@ Menu::Menu() :
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
|
||||
|
||||
QMenu* modelDebugMenu = developerMenu->addMenu("Models");
|
||||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelBounds, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementProxy, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelDebugMenu, MenuOption::DisplayModelElementChildProxies, 0, false);
|
||||
QMenu* modelCullingMenu = modelDebugMenu->addMenu("Culling");
|
||||
addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontCullOutOfViewMeshParts, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontCullTooSmallMeshParts, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(modelCullingMenu, MenuOption::DontReduceMaterialSwitches, 0, false);
|
||||
QMenu* entitiesDebugMenu = developerMenu->addMenu("Entities");
|
||||
addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisplayModelBounds, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisplayModelElementProxy, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisplayModelElementChildProxies, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(entitiesDebugMenu, MenuOption::DisableLightEntities, 0, false);
|
||||
|
||||
QMenu* entityCullingMenu = entitiesDebugMenu->addMenu("Culling");
|
||||
addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontCullOutOfViewMeshParts, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontCullTooSmallMeshParts, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(entityCullingMenu, MenuOption::DontReduceMaterialSwitches, 0, false);
|
||||
|
||||
QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxels");
|
||||
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
|
||||
|
@ -783,8 +784,6 @@ void Menu::loadSettings(QSettings* settings) {
|
|||
settings->endGroup();
|
||||
|
||||
_walletPrivateKey = settings->value("privateKey").toByteArray();
|
||||
|
||||
_oculusUIMaxFPS = loadSetting(settings, "oculusUIMaxFPS", 0.0f);
|
||||
|
||||
scanMenuBar(&loadAction, settings);
|
||||
Application::getInstance()->getAvatar()->loadData(settings);
|
||||
|
@ -846,9 +845,6 @@ void Menu::saveSettings(QSettings* settings) {
|
|||
settings->setValue("viewFrustumOffsetUp", _viewFrustumOffset.up);
|
||||
settings->endGroup();
|
||||
settings->setValue("privateKey", _walletPrivateKey);
|
||||
|
||||
// Oculus Rift settings
|
||||
settings->setValue("oculusUIMaxFPS", _oculusUIMaxFPS);
|
||||
|
||||
scanMenuBar(&saveAction, settings);
|
||||
Application::getInstance()->getAvatar()->saveData(settings);
|
||||
|
@ -1117,7 +1113,11 @@ QAction* Menu::getActionForOption(const QString& menuOption) {
|
|||
}
|
||||
|
||||
void Menu::aboutApp() {
|
||||
InfoView::forcedShow();
|
||||
InfoView::forcedShow(INFO_HELP_PATH);
|
||||
}
|
||||
|
||||
void Menu::showEditEntitiesHelp() {
|
||||
InfoView::forcedShow(INFO_EDIT_ENTITIES_PATH);
|
||||
}
|
||||
|
||||
void Menu::bumpSettings() {
|
||||
|
@ -1260,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) {
|
||||
|
|
|
@ -100,8 +100,6 @@ public:
|
|||
void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; bumpSettings(); }
|
||||
float getOculusUIAngularSize() const { return _oculusUIAngularSize; }
|
||||
void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; bumpSettings(); }
|
||||
int getOculusUIMaxFPS() const { return _oculusUIMaxFPS; }
|
||||
void setOculusUIMaxFPS(int oculusUIMaxFPS) { _oculusUIMaxFPS = oculusUIMaxFPS; bumpSettings(); }
|
||||
float getSixenseReticleMoveSpeed() const { return _sixenseReticleMoveSpeed; }
|
||||
void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; bumpSettings(); }
|
||||
bool getInvertSixenseButtons() const { return _invertSixenseButtons; }
|
||||
|
@ -208,6 +206,7 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void aboutApp();
|
||||
void showEditEntitiesHelp();
|
||||
void bumpSettings();
|
||||
void editPreferences();
|
||||
void editAttachments();
|
||||
|
@ -231,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;
|
||||
|
@ -294,7 +291,6 @@ private:
|
|||
int _maxVoxels;
|
||||
float _voxelSizeScale;
|
||||
float _oculusUIAngularSize;
|
||||
int _oculusUIMaxFPS;
|
||||
float _sixenseReticleMoveSpeed;
|
||||
bool _invertSixenseButtons;
|
||||
bool _automaticAvatarLOD;
|
||||
|
@ -386,6 +382,7 @@ namespace MenuOption {
|
|||
const QString DecreaseVoxelSize = "Decrease Voxel Size";
|
||||
const QString DisableActivityLogger = "Disable Activity Logger";
|
||||
const QString DisableAutoAdjustLOD = "Disable Automatically Adjusting LOD";
|
||||
const QString DisableLightEntities = "Disable Light Entities";
|
||||
const QString DisableNackPackets = "Disable NACK Packets";
|
||||
const QString DisplayFrustum = "Display Frustum";
|
||||
const QString DisplayHands = "Show Hand Info";
|
||||
|
@ -398,9 +395,11 @@ namespace MenuOption {
|
|||
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EditEntitiesHelp = "Edit Entities Help...";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)";
|
||||
const QString EnableVRMode = "Enable VR Mode";
|
||||
const QString Entities = "Entities";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
const QString ExpandMyAvatarTiming = "Expand /myAvatar";
|
||||
const QString ExpandOtherAvatarTiming = "Expand /otherAvatar";
|
||||
|
@ -431,8 +430,6 @@ namespace MenuOption {
|
|||
const QString MetavoxelEditor = "Metavoxel Editor...";
|
||||
const QString Metavoxels = "Metavoxels";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString ModelOptions = "Model Options";
|
||||
const QString Models = "Models";
|
||||
const QString MoveWithLean = "Move with Lean";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString MuteEnvironment = "Mute Environment";
|
||||
|
|
|
@ -961,6 +961,7 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
|||
_scale(scale),
|
||||
_heightBounds(translation, translation + glm::vec3(scale, scale, scale)),
|
||||
_colorBounds(_heightBounds),
|
||||
_materialBounds(_heightBounds),
|
||||
_height(height),
|
||||
_color(color),
|
||||
_material(material),
|
||||
|
@ -968,10 +969,12 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
|||
_heightTextureID(0),
|
||||
_colorTextureID(0),
|
||||
_materialTextureID(0),
|
||||
_heightSize(glm::sqrt(float(height.size()))),
|
||||
_heightSize(glm::sqrt((float)height.size())),
|
||||
_heightIncrement(scale / (_heightSize - HEIGHT_EXTENSION)),
|
||||
_colorSize(glm::sqrt(float(color.size() / DataBlock::COLOR_BYTES))),
|
||||
_colorIncrement(scale / (_colorSize - SHARED_EDGE)) {
|
||||
_colorSize(glm::sqrt((float)color.size() / DataBlock::COLOR_BYTES)),
|
||||
_colorIncrement(scale / (_colorSize - SHARED_EDGE)),
|
||||
_materialSize(glm::sqrt((float)material.size())),
|
||||
_materialIncrement(scale / (_materialSize - SHARED_EDGE)) {
|
||||
|
||||
_heightBounds.minimum.x -= _heightIncrement * HEIGHT_BORDER;
|
||||
_heightBounds.minimum.z -= _heightIncrement * HEIGHT_BORDER;
|
||||
|
@ -980,6 +983,9 @@ HeightfieldBuffer::HeightfieldBuffer(const glm::vec3& translation, float scale,
|
|||
|
||||
_colorBounds.maximum.x += _colorIncrement * SHARED_EDGE;
|
||||
_colorBounds.maximum.z += _colorIncrement * SHARED_EDGE;
|
||||
|
||||
_materialBounds.maximum.x += _materialIncrement * SHARED_EDGE;
|
||||
_materialBounds.maximum.z += _materialIncrement * SHARED_EDGE;
|
||||
}
|
||||
|
||||
HeightfieldBuffer::~HeightfieldBuffer() {
|
||||
|
@ -1006,16 +1012,14 @@ QByteArray HeightfieldBuffer::getUnextendedHeight() const {
|
|||
return unextended;
|
||||
}
|
||||
|
||||
QByteArray HeightfieldBuffer::getUnextendedColor() const {
|
||||
int srcSize = glm::sqrt(float(_color.size() / DataBlock::COLOR_BYTES));
|
||||
int destSize = srcSize - 1;
|
||||
QByteArray unextended(destSize * destSize * DataBlock::COLOR_BYTES, 0);
|
||||
const char* src = _color.constData();
|
||||
int srcStride = srcSize * DataBlock::COLOR_BYTES;
|
||||
QByteArray HeightfieldBuffer::getUnextendedColor(int x, int y) const {
|
||||
int unextendedSize = _heightSize - HEIGHT_EXTENSION;
|
||||
QByteArray unextended(unextendedSize * unextendedSize * DataBlock::COLOR_BYTES, 0);
|
||||
char* dest = unextended.data();
|
||||
int destStride = destSize * DataBlock::COLOR_BYTES;
|
||||
for (int z = 0; z < destSize; z++, src += srcStride, dest += destStride) {
|
||||
memcpy(dest, src, destStride);
|
||||
const char* src = _color.constData() + (y * _colorSize + x) * unextendedSize * DataBlock::COLOR_BYTES;
|
||||
for (int z = 0; z < unextendedSize; z++, dest += unextendedSize * DataBlock::COLOR_BYTES,
|
||||
src += _colorSize * DataBlock::COLOR_BYTES) {
|
||||
memcpy(dest, src, unextendedSize * DataBlock::COLOR_BYTES);
|
||||
}
|
||||
return unextended;
|
||||
}
|
||||
|
@ -1702,134 +1706,246 @@ public:
|
|||
|
||||
HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections);
|
||||
|
||||
void init(HeightfieldBuffer* buffer) { _buffer = buffer; }
|
||||
void init(HeightfieldBuffer* buffer);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
virtual bool postVisit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
const QVector<Box>& _intersections;
|
||||
HeightfieldBuffer* _buffer;
|
||||
|
||||
QVector<int> _depthFlags;
|
||||
};
|
||||
|
||||
enum DepthFlags { HEIGHT_FLAG = 0x01, COLOR_FLAG = 0x02, MATERIAL_FLAG = 0x04 };
|
||||
|
||||
HeightfieldFetchVisitor::HeightfieldFetchVisitor(const MetavoxelLOD& lod, const QVector<Box>& intersections) :
|
||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>(), lod),
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>(), lod),
|
||||
_intersections(intersections) {
|
||||
}
|
||||
|
||||
void HeightfieldFetchVisitor::init(HeightfieldBuffer* buffer) {
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
int HeightfieldFetchVisitor::visit(MetavoxelInfo& info) {
|
||||
Box bounds = info.getBounds();
|
||||
const Box& heightBounds = _buffer->getHeightBounds();
|
||||
if (!bounds.intersects(heightBounds)) {
|
||||
if (!info.getBounds().intersects(_buffer->getHeightBounds())) {
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
if (!info.isLeaf && info.size > _buffer->getScale()) {
|
||||
if (_depthFlags.size() > _depth) {
|
||||
_depthFlags[_depth] = 0;
|
||||
} else {
|
||||
_depthFlags.append(0);
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
}
|
||||
postVisit(info);
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
bool HeightfieldFetchVisitor::postVisit(MetavoxelInfo& info) {
|
||||
HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
|
||||
if (!height) {
|
||||
return STOP_RECURSION;
|
||||
int flags = _depthFlags.at(_depth);
|
||||
if (height) {
|
||||
// to handle borders correctly, make sure we only sample nodes with resolution <= ours
|
||||
int heightSize = glm::sqrt((float)height->getContents().size());
|
||||
float heightIncrement = info.size / heightSize;
|
||||
if (heightIncrement < _buffer->getHeightIncrement() || (flags & HEIGHT_FLAG)) {
|
||||
height.reset();
|
||||
} else {
|
||||
flags |= HEIGHT_FLAG;
|
||||
}
|
||||
}
|
||||
HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
||||
if (color) {
|
||||
int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES);
|
||||
float colorIncrement = info.size / colorSize;
|
||||
if (colorIncrement < _buffer->getColorIncrement() || (flags & COLOR_FLAG)) {
|
||||
color.reset();
|
||||
} else {
|
||||
flags |= COLOR_FLAG;
|
||||
}
|
||||
}
|
||||
HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
|
||||
if (material) {
|
||||
int materialSize = glm::sqrt((float)material->getContents().size());
|
||||
float materialIncrement = info.size / materialSize;
|
||||
if (materialIncrement < _buffer->getMaterialIncrement() || (flags & MATERIAL_FLAG)) {
|
||||
material.reset();
|
||||
} else {
|
||||
flags |= MATERIAL_FLAG;
|
||||
}
|
||||
}
|
||||
if (_depth > 0) {
|
||||
_depthFlags[_depth - 1] |= flags;
|
||||
}
|
||||
if (!(height || color || material)) {
|
||||
return false;
|
||||
}
|
||||
Box bounds = info.getBounds();
|
||||
foreach (const Box& intersection, _intersections) {
|
||||
Box overlap = intersection.getIntersection(bounds);
|
||||
if (overlap.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
float heightIncrement = _buffer->getHeightIncrement();
|
||||
int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement;
|
||||
int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement;
|
||||
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement);
|
||||
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement);
|
||||
int heightSize = _buffer->getHeightSize();
|
||||
char* dest = _buffer->getHeight().data() + destY * heightSize + destX;
|
||||
|
||||
const QByteArray& srcHeight = height->getContents();
|
||||
int srcSize = glm::sqrt(float(srcHeight.size()));
|
||||
float srcIncrement = info.size / srcSize;
|
||||
|
||||
if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) {
|
||||
// easy case: same resolution
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
if (height) {
|
||||
float heightIncrement = _buffer->getHeightIncrement();
|
||||
const Box& heightBounds = _buffer->getHeightBounds();
|
||||
int destX = (overlap.minimum.x - heightBounds.minimum.x) / heightIncrement;
|
||||
int destY = (overlap.minimum.z - heightBounds.minimum.z) / heightIncrement;
|
||||
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / heightIncrement);
|
||||
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / heightIncrement);
|
||||
int heightSize = _buffer->getHeightSize();
|
||||
char* dest = _buffer->getHeight().data() + destY * heightSize + destX;
|
||||
|
||||
const char* src = srcHeight.constData() + srcY * srcSize + srcX;
|
||||
for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) {
|
||||
memcpy(dest, src, destWidth);
|
||||
}
|
||||
} else {
|
||||
// more difficult: different resolutions
|
||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
float srcAdvance = heightIncrement / srcIncrement;
|
||||
int shift = 0;
|
||||
float size = _buffer->getScale();
|
||||
while (size < info.size) {
|
||||
shift++;
|
||||
size *= 2.0f;
|
||||
}
|
||||
int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale();
|
||||
for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) {
|
||||
const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize;
|
||||
float lineSrcX = srcX;
|
||||
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
|
||||
*lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM);
|
||||
const QByteArray& srcHeight = height->getContents();
|
||||
int srcSize = glm::sqrt((float)srcHeight.size());
|
||||
float srcIncrement = info.size / srcSize;
|
||||
|
||||
if (info.size == _buffer->getScale() && srcSize == (heightSize - HeightfieldBuffer::HEIGHT_EXTENSION)) {
|
||||
// easy case: same resolution
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
|
||||
const char* src = srcHeight.constData() + srcY * srcSize + srcX;
|
||||
for (int y = 0; y < destHeight; y++, src += srcSize, dest += heightSize) {
|
||||
memcpy(dest, src, destWidth);
|
||||
}
|
||||
} else {
|
||||
// more difficult: different resolutions
|
||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
float srcAdvance = heightIncrement / srcIncrement;
|
||||
int shift = 0;
|
||||
float size = _buffer->getScale();
|
||||
while (size < info.size) {
|
||||
shift++;
|
||||
size *= 2.0f;
|
||||
}
|
||||
int subtract = (_buffer->getTranslation().y - info.minimum.y) * EIGHT_BIT_MAXIMUM / _buffer->getScale();
|
||||
for (int y = 0; y < destHeight; y++, dest += heightSize, srcY += srcAdvance) {
|
||||
const uchar* src = (const uchar*)srcHeight.constData() + (int)srcY * srcSize;
|
||||
float lineSrcX = srcX;
|
||||
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
|
||||
*lineDest = qMin(qMax(0, (src[(int)lineSrcX] << shift) - subtract), EIGHT_BIT_MAXIMUM);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int colorSize = _buffer->getColorSize();
|
||||
if (colorSize == 0) {
|
||||
continue;
|
||||
}
|
||||
HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
||||
if (!color) {
|
||||
continue;
|
||||
}
|
||||
const Box& colorBounds = _buffer->getColorBounds();
|
||||
overlap = colorBounds.getIntersection(overlap);
|
||||
float colorIncrement = _buffer->getColorIncrement();
|
||||
destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement;
|
||||
destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement;
|
||||
destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement);
|
||||
destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement);
|
||||
dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES;
|
||||
int destStride = colorSize * DataBlock::COLOR_BYTES;
|
||||
int destBytes = destWidth * DataBlock::COLOR_BYTES;
|
||||
|
||||
const QByteArray& srcColor = color->getContents();
|
||||
srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES));
|
||||
int srcStride = srcSize * DataBlock::COLOR_BYTES;
|
||||
srcIncrement = info.size / srcSize;
|
||||
|
||||
if (srcIncrement == colorIncrement) {
|
||||
// easy case: same resolution
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
if (color) {
|
||||
const Box& colorBounds = _buffer->getColorBounds();
|
||||
overlap = colorBounds.getIntersection(overlap);
|
||||
float colorIncrement = _buffer->getColorIncrement();
|
||||
int destX = (overlap.minimum.x - colorBounds.minimum.x) / colorIncrement;
|
||||
int destY = (overlap.minimum.z - colorBounds.minimum.z) / colorIncrement;
|
||||
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / colorIncrement);
|
||||
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / colorIncrement);
|
||||
int colorSize = _buffer->getColorSize();
|
||||
char* dest = _buffer->getColor().data() + (destY * colorSize + destX) * DataBlock::COLOR_BYTES;
|
||||
int destStride = colorSize * DataBlock::COLOR_BYTES;
|
||||
int destBytes = destWidth * DataBlock::COLOR_BYTES;
|
||||
|
||||
const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES;
|
||||
for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) {
|
||||
memcpy(dest, src, destBytes);
|
||||
}
|
||||
} else {
|
||||
// more difficult: different resolutions
|
||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
float srcAdvance = colorIncrement / srcIncrement;
|
||||
for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) {
|
||||
const char* src = srcColor.constData() + (int)srcY * srcStride;
|
||||
float lineSrcX = srcX;
|
||||
for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES,
|
||||
lineSrcX += srcAdvance) {
|
||||
const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES;
|
||||
lineDest[0] = lineSrc[0];
|
||||
lineDest[1] = lineSrc[1];
|
||||
lineDest[2] = lineSrc[2];
|
||||
const QByteArray& srcColor = color->getContents();
|
||||
int srcSize = glm::sqrt(float(srcColor.size() / DataBlock::COLOR_BYTES));
|
||||
int srcStride = srcSize * DataBlock::COLOR_BYTES;
|
||||
float srcIncrement = info.size / srcSize;
|
||||
|
||||
if (srcIncrement == colorIncrement) {
|
||||
// easy case: same resolution
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
|
||||
const char* src = srcColor.constData() + (srcY * srcSize + srcX) * DataBlock::COLOR_BYTES;
|
||||
for (int y = 0; y < destHeight; y++, src += srcStride, dest += destStride) {
|
||||
memcpy(dest, src, destBytes);
|
||||
}
|
||||
} else {
|
||||
// more difficult: different resolutions
|
||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
float srcAdvance = colorIncrement / srcIncrement;
|
||||
for (int y = 0; y < destHeight; y++, dest += destStride, srcY += srcAdvance) {
|
||||
const char* src = srcColor.constData() + (int)srcY * srcStride;
|
||||
float lineSrcX = srcX;
|
||||
for (char* lineDest = dest, *end = dest + destBytes; lineDest != end; lineDest += DataBlock::COLOR_BYTES,
|
||||
lineSrcX += srcAdvance) {
|
||||
const char* lineSrc = src + (int)lineSrcX * DataBlock::COLOR_BYTES;
|
||||
lineDest[0] = lineSrc[0];
|
||||
lineDest[1] = lineSrc[1];
|
||||
lineDest[2] = lineSrc[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (material) {
|
||||
const Box& materialBounds = _buffer->getMaterialBounds();
|
||||
overlap = materialBounds.getIntersection(overlap);
|
||||
float materialIncrement = _buffer->getMaterialIncrement();
|
||||
int destX = (overlap.minimum.x - materialBounds.minimum.x) / materialIncrement;
|
||||
int destY = (overlap.minimum.z - materialBounds.minimum.z) / materialIncrement;
|
||||
int destWidth = glm::ceil((overlap.maximum.x - overlap.minimum.x) / materialIncrement);
|
||||
int destHeight = glm::ceil((overlap.maximum.z - overlap.minimum.z) / materialIncrement);
|
||||
int materialSize = _buffer->getMaterialSize();
|
||||
char* dest = _buffer->getMaterial().data() + destY * materialSize + destX;
|
||||
|
||||
const QByteArray& srcMaterial = material->getContents();
|
||||
const QVector<SharedObjectPointer> srcMaterials = material->getMaterials();
|
||||
int srcSize = glm::sqrt((float)srcMaterial.size());
|
||||
float srcIncrement = info.size / srcSize;
|
||||
QHash<int, int> materialMappings;
|
||||
|
||||
if (srcIncrement == materialIncrement) {
|
||||
// easy case: same resolution
|
||||
int srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
int srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
|
||||
const uchar* src = (const uchar*)srcMaterial.constData() + srcY * srcSize + srcX;
|
||||
for (int y = 0; y < destHeight; y++, src += srcSize, dest += materialSize) {
|
||||
const uchar* lineSrc = src;
|
||||
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrc++) {
|
||||
int value = *lineSrc;
|
||||
if (value != 0) {
|
||||
int& mapping = materialMappings[value];
|
||||
if (mapping == 0) {
|
||||
mapping = getMaterialIndex(material->getMaterials().at(value - 1),
|
||||
_buffer->getMaterials(), _buffer->getMaterial());
|
||||
}
|
||||
value = mapping;
|
||||
}
|
||||
*lineDest = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// more difficult: different resolutions
|
||||
float srcX = (overlap.minimum.x - info.minimum.x) / srcIncrement;
|
||||
float srcY = (overlap.minimum.z - info.minimum.z) / srcIncrement;
|
||||
float srcAdvance = materialIncrement / srcIncrement;
|
||||
for (int y = 0; y < destHeight; y++, dest += materialSize, srcY += srcAdvance) {
|
||||
const uchar* src = (const uchar*)srcMaterial.constData() + (int)srcY * srcSize;
|
||||
float lineSrcX = srcX;
|
||||
for (char* lineDest = dest, *end = dest + destWidth; lineDest != end; lineDest++, lineSrcX += srcAdvance) {
|
||||
int value = src[(int)lineSrcX];
|
||||
if (value != 0) {
|
||||
int& mapping = materialMappings[value];
|
||||
if (mapping == 0) {
|
||||
mapping = getMaterialIndex(material->getMaterials().at(value - 1),
|
||||
_buffer->getMaterials(), _buffer->getMaterial());
|
||||
}
|
||||
value = mapping;
|
||||
}
|
||||
*lineDest = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
clearUnusedMaterials(_buffer->getMaterials(), _buffer->getMaterial());
|
||||
}
|
||||
}
|
||||
return STOP_RECURSION;
|
||||
return false;
|
||||
}
|
||||
|
||||
class HeightfieldRegionVisitor : public MetavoxelVisitor {
|
||||
|
@ -1841,11 +1957,22 @@ public:
|
|||
HeightfieldRegionVisitor(const MetavoxelLOD& lod);
|
||||
|
||||
virtual int visit(MetavoxelInfo& info);
|
||||
virtual bool postVisit(MetavoxelInfo& info);
|
||||
|
||||
private:
|
||||
|
||||
void addRegion(const Box& unextended, const Box& extended);
|
||||
|
||||
class DepthInfo {
|
||||
public:
|
||||
float minimumColorIncrement;
|
||||
float minimumMaterialIncrement;
|
||||
|
||||
DepthInfo() : minimumColorIncrement(FLT_MAX), minimumMaterialIncrement(FLT_MAX) { }
|
||||
};
|
||||
|
||||
QVector<DepthInfo> _depthInfo;
|
||||
|
||||
QVector<Box> _intersections;
|
||||
HeightfieldFetchVisitor _fetchVisitor;
|
||||
};
|
||||
|
@ -1861,70 +1988,99 @@ HeightfieldRegionVisitor::HeightfieldRegionVisitor(const MetavoxelLOD& lod) :
|
|||
}
|
||||
|
||||
int HeightfieldRegionVisitor::visit(MetavoxelInfo& info) {
|
||||
DepthInfo depthInfo;
|
||||
HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
||||
if (color) {
|
||||
int colorSize = glm::sqrt((float)color->getContents().size() / DataBlock::COLOR_BYTES);
|
||||
depthInfo.minimumColorIncrement = info.size / colorSize;
|
||||
}
|
||||
HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
|
||||
if (material) {
|
||||
int materialSize = glm::sqrt((float)material->getContents().size());
|
||||
depthInfo.minimumMaterialIncrement = info.size / materialSize;
|
||||
}
|
||||
if (_depth < _depthInfo.size()) {
|
||||
_depthInfo[_depth] = depthInfo;
|
||||
} else {
|
||||
_depthInfo.append(depthInfo);
|
||||
}
|
||||
if (!info.isLeaf) {
|
||||
return DEFAULT_ORDER;
|
||||
return _visitations.at(_depth).isInputLeaf(0) ? (DEFAULT_ORDER | ALL_NODES_REST) : DEFAULT_ORDER;
|
||||
}
|
||||
HeightfieldBuffer* buffer = NULL;
|
||||
HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
|
||||
if (height) {
|
||||
const QByteArray& heightContents = height->getContents();
|
||||
int size = glm::sqrt(float(heightContents.size()));
|
||||
int extendedSize = size + HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||
int heightContentsSize = extendedSize * extendedSize;
|
||||
|
||||
HeightfieldColorDataPointer color = info.inputValues.at(1).getInlineValue<HeightfieldColorDataPointer>();
|
||||
int colorContentsSize = 0;
|
||||
if (color) {
|
||||
const QByteArray& colorContents = color->getContents();
|
||||
int colorSize = glm::sqrt(float(colorContents.size() / DataBlock::COLOR_BYTES));
|
||||
int extendedColorSize = colorSize + HeightfieldBuffer::SHARED_EDGE;
|
||||
colorContentsSize = extendedColorSize * extendedColorSize * DataBlock::COLOR_BYTES;
|
||||
}
|
||||
|
||||
HeightfieldMaterialDataPointer material = info.inputValues.at(2).getInlineValue<HeightfieldMaterialDataPointer>();
|
||||
QByteArray materialContents;
|
||||
QVector<SharedObjectPointer> materials;
|
||||
if (material) {
|
||||
materialContents = material->getContents();
|
||||
materials = material->getMaterials();
|
||||
}
|
||||
|
||||
const HeightfieldBuffer* existingBuffer = static_cast<const HeightfieldBuffer*>(
|
||||
info.inputValues.at(3).getInlineValue<BufferDataPointer>().data());
|
||||
Box bounds = info.getBounds();
|
||||
if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize &&
|
||||
existingBuffer->getColor().size() == colorContentsSize) {
|
||||
// we already have a buffer of the correct resolution
|
||||
addRegion(bounds, existingBuffer->getHeightBounds());
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(),
|
||||
existingBuffer->getColor(), materialContents, materials);
|
||||
|
||||
} else {
|
||||
// we must create a new buffer and update its borders
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0),
|
||||
QByteArray(colorContentsSize, 0), materialContents, materials);
|
||||
const Box& heightBounds = buffer->getHeightBounds();
|
||||
addRegion(bounds, heightBounds);
|
||||
|
||||
_intersections.clear();
|
||||
_intersections.append(Box(heightBounds.minimum,
|
||||
glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z),
|
||||
glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z),
|
||||
heightBounds.maximum));
|
||||
_intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z),
|
||||
glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z)));
|
||||
|
||||
_fetchVisitor.init(buffer);
|
||||
_data->guide(_fetchVisitor);
|
||||
}
|
||||
}
|
||||
BufferDataPointer pointer(buffer);
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
||||
postVisit(info);
|
||||
return STOP_RECURSION;
|
||||
}
|
||||
|
||||
bool HeightfieldRegionVisitor::postVisit(MetavoxelInfo& info) {
|
||||
const DepthInfo& depthInfo = _depthInfo.at(_depth);
|
||||
if (_depth > 0) {
|
||||
DepthInfo& parentDepthInfo = _depthInfo[_depth - 1];
|
||||
parentDepthInfo.minimumColorIncrement = qMin(parentDepthInfo.minimumColorIncrement, depthInfo.minimumColorIncrement);
|
||||
parentDepthInfo.minimumMaterialIncrement = qMin(parentDepthInfo.minimumMaterialIncrement,
|
||||
depthInfo.minimumMaterialIncrement);
|
||||
}
|
||||
if (_visitations.at(_depth).isInputLeaf(0)) {
|
||||
HeightfieldBuffer* buffer = NULL;
|
||||
HeightfieldHeightDataPointer height = info.inputValues.at(0).getInlineValue<HeightfieldHeightDataPointer>();
|
||||
if (height) {
|
||||
int heightSize = glm::sqrt((float)height->getContents().size());
|
||||
int extendedHeightSize = heightSize + HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||
int heightContentsSize = extendedHeightSize * extendedHeightSize;
|
||||
float minimumColorIncrement = depthInfo.minimumColorIncrement;
|
||||
float minimumMaterialIncrement = depthInfo.minimumMaterialIncrement;
|
||||
for (int i = _depth - 1; i >= 0 && qMax(minimumColorIncrement, minimumMaterialIncrement) == FLT_MAX; i--) {
|
||||
const DepthInfo& ancestorDepthInfo = _depthInfo.at(i);
|
||||
minimumColorIncrement = qMin(minimumColorIncrement, ancestorDepthInfo.minimumColorIncrement);
|
||||
minimumMaterialIncrement = qMin(minimumMaterialIncrement, ancestorDepthInfo.minimumMaterialIncrement);
|
||||
}
|
||||
int colorContentsSize = 0;
|
||||
if (minimumColorIncrement != FLT_MAX) {
|
||||
int colorSize = (int)glm::round(info.size / minimumColorIncrement) + HeightfieldBuffer::SHARED_EDGE;
|
||||
colorContentsSize = colorSize * colorSize * DataBlock::COLOR_BYTES;
|
||||
}
|
||||
int materialContentsSize = 0;
|
||||
if (minimumMaterialIncrement != FLT_MAX) {
|
||||
int materialSize = (int)glm::round(info.size / minimumMaterialIncrement) + HeightfieldBuffer::SHARED_EDGE;
|
||||
materialContentsSize = materialSize * materialSize;
|
||||
}
|
||||
const HeightfieldBuffer* existingBuffer = static_cast<const HeightfieldBuffer*>(
|
||||
info.inputValues.at(3).getInlineValue<BufferDataPointer>().data());
|
||||
Box bounds = info.getBounds();
|
||||
if (existingBuffer && existingBuffer->getHeight().size() == heightContentsSize &&
|
||||
existingBuffer->getColor().size() == colorContentsSize &&
|
||||
existingBuffer->getMaterial().size() == materialContentsSize) {
|
||||
// we already have a buffer of the correct resolution
|
||||
addRegion(bounds, existingBuffer->getHeightBounds());
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, existingBuffer->getHeight(),
|
||||
existingBuffer->getColor(), existingBuffer->getMaterial(), existingBuffer->getMaterials());
|
||||
|
||||
} else {
|
||||
// we must create a new buffer and update its borders
|
||||
buffer = new HeightfieldBuffer(info.minimum, info.size, QByteArray(heightContentsSize, 0),
|
||||
QByteArray(colorContentsSize, 0), QByteArray(materialContentsSize, 0));
|
||||
const Box& heightBounds = buffer->getHeightBounds();
|
||||
addRegion(bounds, heightBounds);
|
||||
|
||||
_intersections.clear();
|
||||
_intersections.append(Box(heightBounds.minimum,
|
||||
glm::vec3(bounds.maximum.x, heightBounds.maximum.y, bounds.minimum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.maximum.x, heightBounds.minimum.y, heightBounds.minimum.z),
|
||||
glm::vec3(heightBounds.maximum.x, heightBounds.maximum.y, bounds.maximum.z)));
|
||||
_intersections.append(Box(glm::vec3(bounds.minimum.x, heightBounds.minimum.y, bounds.maximum.z),
|
||||
heightBounds.maximum));
|
||||
_intersections.append(Box(glm::vec3(heightBounds.minimum.x, heightBounds.minimum.y, bounds.minimum.z),
|
||||
glm::vec3(bounds.minimum.x, heightBounds.maximum.y, heightBounds.maximum.z)));
|
||||
|
||||
_fetchVisitor.init(buffer);
|
||||
_data->guide(_fetchVisitor);
|
||||
}
|
||||
}
|
||||
BufferDataPointer pointer(buffer);
|
||||
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline(pointer));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HeightfieldRegionVisitor::addRegion(const Box& unextended, const Box& extended) {
|
||||
regions.append(unextended);
|
||||
regionBounds.add(extended);
|
||||
|
|
|
@ -236,6 +236,7 @@ public:
|
|||
|
||||
const Box& getHeightBounds() const { return _heightBounds; }
|
||||
const Box& getColorBounds() const { return _colorBounds; }
|
||||
const Box& getMaterialBounds() const { return _materialBounds; }
|
||||
|
||||
QByteArray& getHeight() { return _height; }
|
||||
const QByteArray& getHeight() const { return _height; }
|
||||
|
@ -246,10 +247,11 @@ public:
|
|||
QByteArray& getMaterial() { return _material; }
|
||||
const QByteArray& getMaterial() const { return _material; }
|
||||
|
||||
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
|
||||
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
|
||||
|
||||
QByteArray getUnextendedHeight() const;
|
||||
QByteArray getUnextendedColor() const;
|
||||
QByteArray getUnextendedColor(int x = 0, int y = 0) const;
|
||||
|
||||
int getHeightSize() const { return _heightSize; }
|
||||
float getHeightIncrement() const { return _heightIncrement; }
|
||||
|
@ -257,6 +259,9 @@ public:
|
|||
int getColorSize() const { return _colorSize; }
|
||||
float getColorIncrement() const { return _colorIncrement; }
|
||||
|
||||
int getMaterialSize() const { return _materialSize; }
|
||||
float getMaterialIncrement() const { return _materialIncrement; }
|
||||
|
||||
virtual void render(bool cursor = false);
|
||||
|
||||
private:
|
||||
|
@ -265,6 +270,7 @@ private:
|
|||
float _scale;
|
||||
Box _heightBounds;
|
||||
Box _colorBounds;
|
||||
Box _materialBounds;
|
||||
QByteArray _height;
|
||||
QByteArray _color;
|
||||
QByteArray _material;
|
||||
|
@ -277,6 +283,8 @@ private:
|
|||
float _heightIncrement;
|
||||
int _colorSize;
|
||||
float _colorIncrement;
|
||||
int _materialSize;
|
||||
float _materialIncrement;
|
||||
|
||||
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
|
||||
static QHash<int, BufferPair> _bufferPairs;
|
||||
|
|
|
@ -1269,6 +1269,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
_isPushing = false;
|
||||
float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
|
||||
|
||||
glm::vec3 newLocalVelocity = localVelocity;
|
||||
float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +
|
||||
(fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +
|
||||
fabsf(_driveKeys[UP] - _driveKeys[DOWN]);
|
||||
|
@ -1285,31 +1286,47 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
if (directionLength > EPSILON) {
|
||||
direction /= directionLength;
|
||||
|
||||
// Compute the target keyboard velocity (which ramps up slowly, and damps very quickly)
|
||||
// the max magnitude of which depends on what we're doing:
|
||||
float motorSpeed = glm::length(_keyboardMotorVelocity);
|
||||
float finalMaxMotorSpeed = hasFloor ? _scale * MAX_WALKING_SPEED : _scale * MAX_KEYBOARD_MOTOR_SPEED;
|
||||
float speedGrowthTimescale = 2.0f;
|
||||
float speedIncreaseFactor = 1.8f;
|
||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
||||
const float maxBoostSpeed = _scale * MAX_BOOST_SPEED;
|
||||
if (motorSpeed < maxBoostSpeed) {
|
||||
// an active keyboard motor should never be slower than this
|
||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||
motorSpeed += MIN_AVATAR_SPEED * boostCoefficient;
|
||||
motorEfficiency += (1.0f - motorEfficiency) * boostCoefficient;
|
||||
} else if (motorSpeed > finalMaxMotorSpeed) {
|
||||
motorSpeed = finalMaxMotorSpeed;
|
||||
if (hasFloor) {
|
||||
// we're walking --> simple exponential decay toward target walk speed
|
||||
const float WALK_ACCELERATION_TIMESCALE = 0.7f; // seconds to decrease delta to 1/e
|
||||
_keyboardMotorVelocity = MAX_WALKING_SPEED * direction;
|
||||
motorEfficiency = glm::clamp(deltaTime / WALK_ACCELERATION_TIMESCALE, 0.0f, 1.0f);
|
||||
|
||||
} else {
|
||||
// we're flying --> more complex curve
|
||||
float motorSpeed = glm::length(_keyboardMotorVelocity);
|
||||
float finalMaxMotorSpeed = _scale * MAX_KEYBOARD_MOTOR_SPEED;
|
||||
float speedGrowthTimescale = 2.0f;
|
||||
float speedIncreaseFactor = 1.8f;
|
||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
||||
const float maxBoostSpeed = _scale * MAX_BOOST_SPEED;
|
||||
if (motorSpeed < maxBoostSpeed) {
|
||||
// an active keyboard motor should never be slower than this
|
||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||
motorSpeed += MIN_AVATAR_SPEED * boostCoefficient;
|
||||
motorEfficiency += (1.0f - motorEfficiency) * boostCoefficient;
|
||||
} else if (motorSpeed > finalMaxMotorSpeed) {
|
||||
motorSpeed = finalMaxMotorSpeed;
|
||||
}
|
||||
_keyboardMotorVelocity = motorSpeed * direction;
|
||||
}
|
||||
_keyboardMotorVelocity = motorSpeed * direction;
|
||||
_isPushing = true;
|
||||
}
|
||||
newLocalVelocity = localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity);
|
||||
} else {
|
||||
_keyboardMotorVelocity = glm::vec3(0.0f);
|
||||
newLocalVelocity = (1.0f - motorEfficiency) * localVelocity;
|
||||
if (hasFloor && !_wasPushing) {
|
||||
float speed = glm::length(newLocalVelocity);
|
||||
if (speed > MIN_AVATAR_SPEED) {
|
||||
// add small constant friction to help avatar drift to a stop sooner at low speeds
|
||||
const float CONSTANT_FRICTION_DECELERATION = MIN_AVATAR_SPEED / 0.20f;
|
||||
newLocalVelocity *= (speed - timescale * CONSTANT_FRICTION_DECELERATION) / speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply keyboard motor
|
||||
return localVelocity + motorEfficiency * (_keyboardMotorVelocity - localVelocity);
|
||||
return newLocalVelocity;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVelocity) {
|
||||
|
|
|
@ -420,7 +420,7 @@ void OculusManager::endFrameTiming() {
|
|||
//Sets the camera FoV and aspect ratio
|
||||
void OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) {
|
||||
#ifdef HAVE_LIBOVR
|
||||
camera.setAspectRatio(_renderTargetSize.w / _renderTargetSize.h);
|
||||
camera.setAspectRatio((float)_renderTargetSize.w / _renderTargetSize.h);
|
||||
camera.setFieldOfView(atan(_eyeFov[0].UpTan) * DEGREES_PER_RADIAN * 2.0f);
|
||||
#endif
|
||||
}
|
||||
|
@ -511,12 +511,13 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
|
|||
|
||||
_camera->update(1.0f / Application::getInstance()->getFps());
|
||||
|
||||
Matrix4f proj = ovrMatrix4f_Projection(_eyeRenderDesc[eye].Fov, whichCamera.getNearClip(), whichCamera.getFarClip(), true);
|
||||
proj.Transpose();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glLoadMatrixf((GLfloat *)proj.M);
|
||||
|
||||
const ovrFovPort& port = _eyeFov[_activeEyeIndex];
|
||||
float nearClip = whichCamera.getNearClip(), farClip = whichCamera.getFarClip();
|
||||
glFrustum(-nearClip * port.LeftTan, nearClip * port.RightTan, -nearClip * port.DownTan,
|
||||
nearClip * port.UpTan, nearClip, farClip);
|
||||
|
||||
glViewport(_eyeRenderViewport[eye].Pos.x, _eyeRenderViewport[eye].Pos.y,
|
||||
_eyeRenderViewport[eye].Size.w, _eyeRenderViewport[eye].Size.h);
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "ui/overlays/Text3DOverlay.h"
|
||||
|
||||
const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f;
|
||||
const int DEFAULT_OCULUS_UI_MAX_FPS = 75;
|
||||
|
||||
class Camera;
|
||||
class PalmData;
|
||||
|
|
|
@ -91,8 +91,10 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
// left eye portal
|
||||
int portalX = 0;
|
||||
int portalY = 0;
|
||||
int portalW = Application::getInstance()->getGLWidget()->getDeviceWidth() / 2;
|
||||
int portalH = Application::getInstance()->getGLWidget()->getDeviceHeight();
|
||||
QSize deviceSize = Application::getInstance()->getGLWidget()->getDeviceSize() *
|
||||
Application::getInstance()->getRenderResolutionScale();
|
||||
int portalW = deviceSize.width() / 2;
|
||||
int portalH = deviceSize.height();
|
||||
|
||||
ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();
|
||||
|
||||
|
@ -135,7 +137,7 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
// render right side view
|
||||
portalX = Application::getInstance()->getGLWidget()->getDeviceWidth() / 2;
|
||||
portalX = deviceSize.width() / 2;
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
// render left side view
|
||||
glViewport(portalX, portalY, portalW, portalH);
|
||||
|
@ -165,8 +167,7 @@ void TV3DManager::display(Camera& whichCamera) {
|
|||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
// reset the viewport to how we started
|
||||
glViewport(0, 0, Application::getInstance()->getGLWidget()->getDeviceWidth(),
|
||||
Application::getInstance()->getGLWidget()->getDeviceHeight());
|
||||
glViewport(0, 0, deviceSize.width(), deviceSize.height());
|
||||
|
||||
Application::getInstance()->getGlowEffect()->render();
|
||||
}
|
||||
|
|
|
@ -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); }
|
||||
|
@ -57,12 +64,6 @@ public:
|
|||
/// clears the tree
|
||||
virtual void clear();
|
||||
|
||||
//Q_INVOKABLE Model* getModel(const ModelEntityItem* modelEntityItem);
|
||||
|
||||
// renderers for various types of entities
|
||||
void renderEntityTypeBox(EntityItem* entity, RenderArgs* args);
|
||||
void renderEntityTypeModel(EntityItem* entity, RenderArgs* args);
|
||||
|
||||
static QThread* getMainThread();
|
||||
|
||||
/// if a renderable entity item needs a model, we will allocate it for them
|
||||
|
@ -75,9 +76,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
|
||||
|
|
|
@ -61,15 +61,18 @@ void RenderableLightEntityItem::render(RenderArgs* args) {
|
|||
float exponent = getExponent();
|
||||
float cutoff = glm::radians(getCutoff());
|
||||
|
||||
if (_isSpotlight) {
|
||||
Application::getInstance()->getDeferredLightingEffect()->addSpotLight(position, largestDiameter / 2.0f,
|
||||
ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation,
|
||||
direction, exponent, cutoff);
|
||||
} else {
|
||||
Application::getInstance()->getDeferredLightingEffect()->addPointLight(position, largestDiameter / 2.0f,
|
||||
ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation);
|
||||
}
|
||||
bool disableLights = Menu::getInstance()->isOptionChecked(MenuOption::DisableLightEntities);
|
||||
|
||||
if (!disableLights) {
|
||||
if (_isSpotlight) {
|
||||
Application::getInstance()->getDeferredLightingEffect()->addSpotLight(position, largestDiameter / 2.0f,
|
||||
ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation,
|
||||
direction, exponent, cutoff);
|
||||
} else {
|
||||
Application::getInstance()->getDeferredLightingEffect()->addPointLight(position, largestDiameter / 2.0f,
|
||||
ambient, diffuse, specular, constantAttenuation, linearAttenuation, quadraticAttenuation);
|
||||
}
|
||||
}
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
glColor4f(diffuseR, diffuseG, diffuseB, 1.0f);
|
||||
|
|
|
@ -38,12 +38,9 @@ RenderableModelEntityItem::~RenderableModelEntityItem() {
|
|||
|
||||
bool RenderableModelEntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
|
||||
QString oldModelURL = getModelURL();
|
||||
QString oldTextures = getTextures();
|
||||
bool somethingChanged = ModelEntityItem::setProperties(properties, forceCopy);
|
||||
if (somethingChanged) {
|
||||
if ((oldModelURL != getModelURL()) || (oldTextures != getTextures())) {
|
||||
_needsModelReload = true;
|
||||
}
|
||||
if (somethingChanged && oldModelURL != getModelURL()) {
|
||||
_needsModelReload = true;
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
@ -60,6 +57,47 @@ int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned c
|
|||
return bytesRead;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::remapTextures() {
|
||||
if (!_model) {
|
||||
return; // nothing to do if we don't have a model
|
||||
}
|
||||
|
||||
if (_currentTextures == _textures) {
|
||||
return; // nothing to do if our recently mapped textures match our desired textures
|
||||
}
|
||||
qDebug() << "void RenderableModelEntityItem::remapTextures()....";
|
||||
|
||||
// since we're changing here, we need to run through our current texture map
|
||||
// and any textures in the recently mapped texture, that is not in our desired
|
||||
// textures, we need to "unset"
|
||||
QJsonDocument currentTexturesAsJson = QJsonDocument::fromJson(_currentTextures.toUtf8());
|
||||
QJsonObject currentTexturesAsJsonObject = currentTexturesAsJson.object();
|
||||
QVariantMap currentTextureMap = currentTexturesAsJsonObject.toVariantMap();
|
||||
|
||||
QJsonDocument texturesAsJson = QJsonDocument::fromJson(_textures.toUtf8());
|
||||
QJsonObject texturesAsJsonObject = texturesAsJson.object();
|
||||
QVariantMap textureMap = texturesAsJsonObject.toVariantMap();
|
||||
|
||||
foreach(const QString& key, currentTextureMap.keys()) {
|
||||
// if the desired texture map (what we're setting the textures to) doesn't
|
||||
// contain this texture, then remove it by setting the URL to null
|
||||
if (!textureMap.contains(key)) {
|
||||
QUrl noURL;
|
||||
qDebug() << "Removing texture named" << key << "by replacing it with no URL";
|
||||
_model->setTextureWithNameToURL(key, noURL);
|
||||
}
|
||||
}
|
||||
|
||||
// here's where we remap any textures if needed...
|
||||
foreach(const QString& key, textureMap.keys()) {
|
||||
QUrl newTextureURL = textureMap[key].toUrl();
|
||||
qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL;
|
||||
_model->setTextureWithNameToURL(key, newTextureURL);
|
||||
}
|
||||
|
||||
_currentTextures = _textures;
|
||||
}
|
||||
|
||||
|
||||
void RenderableModelEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RMEIrender");
|
||||
|
@ -72,6 +110,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
glm::vec3 dimensions = getDimensions() * (float)TREE_SCALE;
|
||||
|
||||
if (drawAsModel) {
|
||||
remapTextures();
|
||||
glPushMatrix();
|
||||
{
|
||||
float alpha = getLocalRenderAlpha();
|
||||
|
@ -159,6 +198,10 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
|
|||
}
|
||||
assert(_myRenderer == renderer); // you should only ever render on one renderer
|
||||
|
||||
if (QThread::currentThread() != _myRenderer->thread()) {
|
||||
return _model;
|
||||
}
|
||||
|
||||
_needsModelReload = false; // this is the reload
|
||||
|
||||
// if we have a URL, then we will want to end up returning a model...
|
||||
|
@ -183,18 +226,6 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
|
|||
}
|
||||
}
|
||||
|
||||
// here's where we remap any textures if needed...
|
||||
if (!_textures.isEmpty() && _model) {
|
||||
QJsonDocument texturesAsJson = QJsonDocument::fromJson(_textures.toUtf8());
|
||||
QJsonObject texturesAsJsonObject = texturesAsJson.object();
|
||||
QVariantMap textureMap = texturesAsJsonObject.toVariantMap();
|
||||
foreach(const QString& key, textureMap.keys()) {
|
||||
QUrl newTextureURL = textureMap[key].toUrl();
|
||||
qDebug() << "Updating texture named" << key << "to texture at URL" << newTextureURL;
|
||||
_model->setTextureWithNameToURL(key, newTextureURL);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,12 +51,14 @@ public:
|
|||
virtual void render(RenderArgs* args);
|
||||
Model* getModel(EntityTreeRenderer* renderer);
|
||||
private:
|
||||
void remapTextures();
|
||||
bool needsSimulation() const;
|
||||
|
||||
Model* _model;
|
||||
bool _needsInitialSimulation;
|
||||
bool _needsModelReload;
|
||||
EntityTreeRenderer* _myRenderer;
|
||||
QString _currentTextures;
|
||||
};
|
||||
|
||||
#endif // hifi_RenderableModelEntityItem_h
|
||||
|
|
|
@ -51,8 +51,7 @@ void AmbientOcclusionEffect::init() {
|
|||
|
||||
_occlusionProgram->bind();
|
||||
_occlusionProgram->setUniformValue("depthTexture", 0);
|
||||
_occlusionProgram->setUniformValue("normalTexture", 1);
|
||||
_occlusionProgram->setUniformValue("rotationTexture", 2);
|
||||
_occlusionProgram->setUniformValue("rotationTexture", 1);
|
||||
_occlusionProgram->setUniformValueArray("sampleKernel", sampleKernel, SAMPLE_KERNEL_SIZE);
|
||||
_occlusionProgram->setUniformValue("radius", 0.1f);
|
||||
_occlusionProgram->release();
|
||||
|
@ -102,9 +101,6 @@ void AmbientOcclusionEffect::render() {
|
|||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryDepthTextureID());
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryNormalTextureID());
|
||||
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, _rotationTextureID);
|
||||
|
||||
// render with the occlusion shader to the secondary/tertiary buffer
|
||||
|
@ -142,9 +138,6 @@ void AmbientOcclusionEffect::render() {
|
|||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
// now render secondary to primary with 4x4 blur
|
||||
|
|
|
@ -129,9 +129,8 @@ void JoystickScriptingInterface::update() {
|
|||
: HFActionEvent::endType();
|
||||
|
||||
// global action events fire in the center of the screen
|
||||
QPointF centerPoint = Application::getInstance()->getViewportCenter();
|
||||
HFActionEvent actionEvent(actionType, centerPoint);
|
||||
|
||||
HFActionEvent actionEvent(actionType,
|
||||
Application::getInstance()->getViewFrustum()->computePickRay(0.5f, 0.5f));
|
||||
qApp->sendEvent(qApp, &actionEvent);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,24 +20,24 @@
|
|||
#define SETTINGS_VERSION_KEY "info-version"
|
||||
#define MAX_DIALOG_HEIGHT_RATIO 0.9
|
||||
|
||||
InfoView::InfoView(bool forced) :
|
||||
InfoView::InfoView(bool forced, QString path) :
|
||||
_forced(forced)
|
||||
{
|
||||
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
|
||||
|
||||
QString absPath = QFileInfo(Application::resourcesPath() + "html/interface-welcome-allsvg.html").absoluteFilePath();
|
||||
QString absPath = QFileInfo(Application::resourcesPath() + path).absoluteFilePath();
|
||||
QUrl url = QUrl::fromLocalFile(absPath);
|
||||
|
||||
load(url);
|
||||
connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loaded(bool)));
|
||||
}
|
||||
|
||||
void InfoView::showFirstTime() {
|
||||
new InfoView(false);
|
||||
void InfoView::showFirstTime(QString path) {
|
||||
new InfoView(false, path);
|
||||
}
|
||||
|
||||
void InfoView::forcedShow() {
|
||||
new InfoView(true);
|
||||
void InfoView::forcedShow(QString path) {
|
||||
new InfoView(true, path);
|
||||
}
|
||||
|
||||
bool InfoView::shouldShow() {
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
class InfoView : public QWebView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static void showFirstTime();
|
||||
static void forcedShow();
|
||||
static void showFirstTime(QString path);
|
||||
static void forcedShow(QString path);
|
||||
|
||||
private:
|
||||
InfoView(bool forced);
|
||||
InfoView(bool forced, QString path);
|
||||
bool _forced;
|
||||
bool shouldShow();
|
||||
|
||||
|
|
|
@ -1038,26 +1038,42 @@ void ImportHeightfieldTool::apply() {
|
|||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldAttribute(), encodeInline(heightPointer))));
|
||||
|
||||
QByteArray color;
|
||||
if (buffer->getColor().isEmpty()) {
|
||||
const unsigned char WHITE_VALUE = 0xFF;
|
||||
color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE);
|
||||
} else {
|
||||
color = buffer->getUnextendedColor();
|
||||
}
|
||||
HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
||||
|
||||
int size = glm::sqrt(float(height.size()));
|
||||
QByteArray material(size * size, 0);
|
||||
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(AttributeValue(
|
||||
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), encodeInline(materialPointer))));
|
||||
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
|
||||
_translation->getValue() + buffer->getTranslation() * scale, data)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
|
||||
int colorUnits = buffer->getColor().isEmpty() ? 1 : (buffer->getColorSize() - HeightfieldBuffer::SHARED_EDGE) /
|
||||
(buffer->getHeightSize() - HeightfieldBuffer::HEIGHT_EXTENSION);
|
||||
float colorScale = scale / colorUnits;
|
||||
|
||||
for (int y = 0; y < colorUnits; y++) {
|
||||
for (int x = 0; x < colorUnits; x++) {
|
||||
MetavoxelData data;
|
||||
data.setSize(colorScale);
|
||||
|
||||
QByteArray color;
|
||||
if (buffer->getColor().isEmpty()) {
|
||||
const unsigned char WHITE_VALUE = 0xFF;
|
||||
color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE);
|
||||
} else {
|
||||
color = buffer->getUnextendedColor(x, y);
|
||||
}
|
||||
HeightfieldColorDataPointer colorPointer(new HeightfieldColorData(color));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), new MetavoxelNode(
|
||||
AttributeValue(AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
|
||||
encodeInline(colorPointer))));
|
||||
|
||||
QByteArray material(height.size(), 0);
|
||||
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
|
||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(
|
||||
AttributeValue(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(),
|
||||
encodeInline(materialPointer))));
|
||||
|
||||
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
|
||||
_translation->getValue() + buffer->getTranslation() * scale + glm::vec3(x, 0.0f, y) * colorScale, data)) };
|
||||
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1143,7 +1159,9 @@ void ImportHeightfieldTool::updatePreview() {
|
|||
float z = 0.0f;
|
||||
int blockSize = pow(2.0, _blockSize->value());
|
||||
int heightSize = blockSize + HeightfieldBuffer::HEIGHT_EXTENSION;
|
||||
int colorSize = blockSize + HeightfieldBuffer::SHARED_EDGE;
|
||||
int colorScale = glm::round(glm::log(_colorImage.height() / (float)_heightImage.height()) / glm::log(2.0f));
|
||||
int colorBlockSize = blockSize * pow(2.0, qMax(colorScale, 0));
|
||||
int colorSize = colorBlockSize + HeightfieldBuffer::SHARED_EDGE;
|
||||
for (int i = 0; i < _heightImage.height(); i += blockSize, z++) {
|
||||
float x = 0.0f;
|
||||
for (int j = 0; j < _heightImage.width(); j += blockSize, x++) {
|
||||
|
@ -1164,12 +1182,14 @@ void ImportHeightfieldTool::updatePreview() {
|
|||
}
|
||||
QByteArray color;
|
||||
if (!_colorImage.isNull()) {
|
||||
int colorI = (i / blockSize) * colorBlockSize;
|
||||
int colorJ = (j / blockSize) * colorBlockSize;
|
||||
color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0);
|
||||
rows = qMax(0, qMin(colorSize, _colorImage.height() - i));
|
||||
columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
|
||||
rows = qMax(0, qMin(colorSize, _colorImage.height() - colorI));
|
||||
columns = qMax(0, qMin(colorSize, _colorImage.width() - colorJ));
|
||||
for (int y = 0; y < rows; y++) {
|
||||
memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES,
|
||||
_colorImage.scanLine(i + y) + j * DataBlock::COLOR_BYTES,
|
||||
_colorImage.scanLine(colorI + y) + colorJ * DataBlock::COLOR_BYTES,
|
||||
columns * DataBlock::COLOR_BYTES);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,8 +147,6 @@ void PreferencesDialog::loadPreferences() {
|
|||
ui.maxVoxelsPPSSpin->setValue(menuInstance->getMaxVoxelPacketsPerSecond());
|
||||
|
||||
ui.oculusUIAngularSizeSpin->setValue(menuInstance->getOculusUIAngularSize());
|
||||
|
||||
ui.oculusUIMaxFPSSpin->setValue(menuInstance->getOculusUIMaxFPS());
|
||||
|
||||
ui.sixenseReticleMoveSpeedSpin->setValue(menuInstance->getSixenseReticleMoveSpeed());
|
||||
|
||||
|
@ -231,8 +229,6 @@ void PreferencesDialog::savePreferences() {
|
|||
Menu::getInstance()->setMaxVoxelPacketsPerSecond(ui.maxVoxelsPPSSpin->value());
|
||||
|
||||
Menu::getInstance()->setOculusUIAngularSize(ui.oculusUIAngularSizeSpin->value());
|
||||
|
||||
Menu::getInstance()->setOculusUIMaxFPS(ui.oculusUIMaxFPSSpin->value());
|
||||
|
||||
Menu::getInstance()->setSixenseReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
|
||||
|
||||
|
|
|
@ -86,13 +86,13 @@ void OctreePacketProcessor::processPacket(const SharedNodePointer& sendingNode,
|
|||
|
||||
switch(voxelPacketType) {
|
||||
case PacketTypeEntityErase: {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) {
|
||||
app->_entities.processEraseMessage(mutablePacket, sendingNode);
|
||||
}
|
||||
} break;
|
||||
|
||||
case PacketTypeEntityData: {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Models)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Entities)) {
|
||||
app->_entities.processDatagram(mutablePacket, sendingNode);
|
||||
}
|
||||
} break;
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>1459</height>
|
||||
<height>1386</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
|
@ -1772,24 +1772,24 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="faceshiftHostnameEdit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>localhost</string>
|
||||
</property>
|
||||
</widget>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>localhost</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="voxelsTitleLabel">
|
||||
<property name="sizePolicy">
|
||||
|
@ -2084,85 +2084,6 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_17">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Oculus Rift FPS</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>maxVoxelsSpin</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_18">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="oculusUIMaxFPSSpin">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>95</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>75</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sixenseControllersTitleLabel">
|
||||
<property name="sizePolicy">
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -1198,6 +1198,10 @@ bool HeightfieldAttribute::merge(void*& parent, void* children[], bool postRead)
|
|||
return false;
|
||||
}
|
||||
|
||||
AttributeValue HeightfieldAttribute::inherit(const AttributeValue& parentValue) const {
|
||||
return AttributeValue(parentValue.getAttribute());
|
||||
}
|
||||
|
||||
HeightfieldColorAttribute::HeightfieldColorAttribute(const QString& name) :
|
||||
InlineAttribute<HeightfieldColorDataPointer>(name) {
|
||||
}
|
||||
|
@ -1337,6 +1341,10 @@ bool HeightfieldColorAttribute::merge(void*& parent, void* children[], bool post
|
|||
return false;
|
||||
}
|
||||
|
||||
AttributeValue HeightfieldColorAttribute::inherit(const AttributeValue& parentValue) const {
|
||||
return AttributeValue(parentValue.getAttribute());
|
||||
}
|
||||
|
||||
HeightfieldMaterialAttribute::HeightfieldMaterialAttribute(const QString& name) :
|
||||
InlineAttribute<HeightfieldMaterialDataPointer>(name) {
|
||||
}
|
||||
|
@ -1404,6 +1412,71 @@ bool HeightfieldMaterialAttribute::merge(void*& parent, void* children[], bool p
|
|||
return maxSize == 0;
|
||||
}
|
||||
|
||||
AttributeValue HeightfieldMaterialAttribute::inherit(const AttributeValue& parentValue) const {
|
||||
return AttributeValue(parentValue.getAttribute());
|
||||
}
|
||||
|
||||
static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
||||
QHash<uchar, int> counts;
|
||||
for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) {
|
||||
if (*src != 0) {
|
||||
counts[*src]++;
|
||||
}
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
|
||||
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
|
||||
return 0;
|
||||
}
|
||||
// first look for a matching existing material, noting the first reusable slot
|
||||
int firstEmptyIndex = -1;
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
const SharedObjectPointer& existingMaterial = materials.at(i);
|
||||
if (existingMaterial) {
|
||||
if (existingMaterial->equals(material.data())) {
|
||||
return i + 1;
|
||||
}
|
||||
} else if (firstEmptyIndex == -1) {
|
||||
firstEmptyIndex = i;
|
||||
}
|
||||
}
|
||||
// if nothing found, use the first empty slot or append
|
||||
if (firstEmptyIndex != -1) {
|
||||
materials[firstEmptyIndex] = material;
|
||||
return firstEmptyIndex + 1;
|
||||
}
|
||||
if (materials.size() < EIGHT_BIT_MAXIMUM) {
|
||||
materials.append(material);
|
||||
return materials.size();
|
||||
}
|
||||
// last resort: find the least-used material and remove it
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
uchar materialIndex = 0;
|
||||
int lowestCount = INT_MAX;
|
||||
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
|
||||
if (it.value() < lowestCount) {
|
||||
materialIndex = it.key();
|
||||
lowestCount = it.value();
|
||||
}
|
||||
}
|
||||
contents.replace((char)materialIndex, (char)0);
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents) {
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
if (counts.value(i + 1) == 0) {
|
||||
materials[i] = SharedObjectPointer();
|
||||
}
|
||||
}
|
||||
while (!(materials.isEmpty() || materials.last())) {
|
||||
materials.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6;
|
||||
|
||||
static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ,
|
||||
|
|
|
@ -536,6 +536,8 @@ public:
|
|||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
|
||||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldColorData> HeightfieldColorDataPointer;
|
||||
|
@ -580,6 +582,8 @@ public:
|
|||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
|
||||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||
};
|
||||
|
||||
typedef QExplicitlySharedDataPointer<HeightfieldMaterialData> HeightfieldMaterialDataPointer;
|
||||
|
@ -646,8 +650,17 @@ public:
|
|||
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
|
||||
|
||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||
|
||||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||
};
|
||||
|
||||
/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index,
|
||||
/// creating a new entry in the list if necessary.
|
||||
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents);
|
||||
|
||||
/// Utility method for editing: removes any unused materials from the supplied list.
|
||||
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents);
|
||||
|
||||
typedef QExplicitlySharedDataPointer<VoxelColorData> VoxelColorDataPointer;
|
||||
|
||||
/// Contains a block of voxel color data.
|
||||
|
|
|
@ -1509,7 +1509,7 @@ bool MetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
|||
DefaultMetavoxelGuide::DefaultMetavoxelGuide() {
|
||||
}
|
||||
|
||||
static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float lodBase, int encodedOrder) {
|
||||
static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int encodedOrder) {
|
||||
MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation();
|
||||
nextVisitation.info.size = visitation.info.size * 0.5f;
|
||||
for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) {
|
||||
|
@ -1519,14 +1519,14 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float
|
|||
for (int j = 0; j < visitation.inputNodes.size(); j++) {
|
||||
MetavoxelNode* node = visitation.inputNodes.at(j);
|
||||
const AttributeValue& parentValue = visitation.info.inputValues.at(j);
|
||||
MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
|
||||
MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase *
|
||||
parentValue.getAttribute()->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
||||
nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
|
||||
child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue);
|
||||
}
|
||||
for (int j = 0; j < visitation.outputNodes.size(); j++) {
|
||||
MetavoxelNode* node = visitation.outputNodes.at(j);
|
||||
MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
|
||||
MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase *
|
||||
visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
||||
nextVisitation.outputNodes[j] = child;
|
||||
}
|
||||
|
@ -1604,9 +1604,10 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, float
|
|||
|
||||
bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
||||
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
|
||||
float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
||||
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
||||
visitation.visitor->getLOD().threshold;
|
||||
visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier());
|
||||
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
|
||||
visitation.visitor->getMinimumLODThresholdMultiplier());
|
||||
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
|
||||
int encodedOrder = visitation.visitor->visit(visitation.info);
|
||||
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
|
||||
|
@ -1628,14 +1629,15 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
|
|||
if (encodedOrder == MetavoxelVisitor::STOP_RECURSION) {
|
||||
return true;
|
||||
}
|
||||
return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, lodBase, encodedOrder));
|
||||
return (encodedOrder == MetavoxelVisitor::STOP_RECURSION || defaultGuideToChildren(visitation, encodedOrder));
|
||||
}
|
||||
|
||||
bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
||||
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
|
||||
float lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
||||
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
|
||||
visitation.visitor->getLOD().threshold;
|
||||
visitation.info.isLODLeaf = (visitation.info.size < lodBase * visitation.visitor->getMinimumLODThresholdMultiplier());
|
||||
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
|
||||
visitation.visitor->getMinimumLODThresholdMultiplier());
|
||||
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
|
||||
int encodedOrder = visitation.visitor->visit(visitation.info);
|
||||
if (encodedOrder == MetavoxelVisitor::SHORT_CIRCUIT) {
|
||||
|
@ -1658,7 +1660,7 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
|||
return true;
|
||||
}
|
||||
if (encodedOrder & MetavoxelVisitor::ALL_NODES_REST) {
|
||||
return defaultGuideToChildren(visitation, lodBase, encodedOrder);
|
||||
return defaultGuideToChildren(visitation, encodedOrder);
|
||||
}
|
||||
bool onlyVisitDifferent = !(encodedOrder & MetavoxelVisitor::ALL_NODES);
|
||||
MetavoxelVisitation& nextVisitation = visitation.visitor->acquireVisitation();
|
||||
|
@ -1672,7 +1674,8 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
|||
for (int j = 0; j < visitation.inputNodes.size(); j++) {
|
||||
MetavoxelNode* node = visitation.inputNodes.at(j);
|
||||
const AttributeValue& parentValue = visitation.info.inputValues.at(j);
|
||||
bool expand = (visitation.info.size >= lodBase * parentValue.getAttribute()->getLODThresholdMultiplier());
|
||||
bool expand = (visitation.info.size >= visitation.info.lodBase *
|
||||
parentValue.getAttribute()->getLODThresholdMultiplier());
|
||||
MetavoxelNode* child = (node && expand) ? node->getChild(index) : NULL;
|
||||
nextVisitation.info.inputValues[j] = ((nextVisitation.inputNodes[j] = child)) ?
|
||||
child->getAttributeValue(parentValue.getAttribute()) : parentValue.getAttribute()->inherit(parentValue);
|
||||
|
@ -1686,7 +1689,7 @@ bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
|
|||
}
|
||||
for (int j = 0; j < visitation.outputNodes.size(); j++) {
|
||||
MetavoxelNode* node = visitation.outputNodes.at(j);
|
||||
MetavoxelNode* child = (node && (visitation.info.size >= lodBase *
|
||||
MetavoxelNode* child = (node && (visitation.info.size >= visitation.info.lodBase *
|
||||
visitation.visitor->getOutputs().at(j)->getLODThresholdMultiplier())) ? node->getChild(index) : NULL;
|
||||
nextVisitation.outputNodes[j] = child;
|
||||
}
|
||||
|
@ -1911,6 +1914,12 @@ MetavoxelVisitation::MetavoxelVisitation(MetavoxelVisitation* previous,
|
|||
MetavoxelVisitation::MetavoxelVisitation() {
|
||||
}
|
||||
|
||||
bool MetavoxelVisitation::isInputLeaf(int index) const {
|
||||
MetavoxelNode* node = inputNodes.at(index);
|
||||
return !node || node->isLeaf() || info.size < info.lodBase *
|
||||
info.inputValues.at(index).getAttribute()->getLODThresholdMultiplier();
|
||||
}
|
||||
|
||||
bool MetavoxelVisitation::allInputNodesLeaves() const {
|
||||
foreach (MetavoxelNode* node, inputNodes) {
|
||||
if (node && !node->isLeaf()) {
|
||||
|
|
|
@ -278,6 +278,7 @@ public:
|
|||
float size; ///< the size of the voxel in all dimensions
|
||||
QVector<AttributeValue> inputValues;
|
||||
QVector<OwnedAttributeValue> outputValues;
|
||||
float lodBase;
|
||||
bool isLODLeaf;
|
||||
bool isLeaf;
|
||||
|
||||
|
@ -537,6 +538,7 @@ public:
|
|||
MetavoxelVisitation(MetavoxelVisitation* previous, MetavoxelVisitor* visitor, int inputNodesSize, int outputNodesSize);
|
||||
MetavoxelVisitation();
|
||||
|
||||
bool isInputLeaf(int index) const;
|
||||
bool allInputNodesLeaves() const;
|
||||
AttributeValue getInheritedOutputValue(int index) const;
|
||||
};
|
||||
|
|
|
@ -445,71 +445,11 @@ PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const g
|
|||
_material(material),
|
||||
_color(color) {
|
||||
|
||||
glm::vec3 extents(_radius, _radius, _radius);
|
||||
const float LARGE_EXTENT = 100000.0f;
|
||||
glm::vec3 extents(_radius, LARGE_EXTENT, _radius);
|
||||
_bounds = Box(_position - extents, _position + extents);
|
||||
}
|
||||
|
||||
static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
||||
QHash<uchar, int> counts;
|
||||
for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) {
|
||||
if (*src != 0) {
|
||||
counts[*src]++;
|
||||
}
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
|
||||
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
|
||||
return 0;
|
||||
}
|
||||
// first look for a matching existing material, noting the first reusable slot
|
||||
int firstEmptyIndex = -1;
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
const SharedObjectPointer& existingMaterial = materials.at(i);
|
||||
if (existingMaterial) {
|
||||
if (existingMaterial->equals(material.data())) {
|
||||
return i + 1;
|
||||
}
|
||||
} else if (firstEmptyIndex == -1) {
|
||||
firstEmptyIndex = i;
|
||||
}
|
||||
}
|
||||
// if nothing found, use the first empty slot or append
|
||||
if (firstEmptyIndex != -1) {
|
||||
materials[firstEmptyIndex] = material;
|
||||
return firstEmptyIndex + 1;
|
||||
}
|
||||
if (materials.size() < EIGHT_BIT_MAXIMUM) {
|
||||
materials.append(material);
|
||||
return materials.size();
|
||||
}
|
||||
// last resort: find the least-used material and remove it
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
uchar materialIndex = 0;
|
||||
int lowestCount = INT_MAX;
|
||||
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
|
||||
if (it.value() < lowestCount) {
|
||||
materialIndex = it.key();
|
||||
lowestCount = it.value();
|
||||
}
|
||||
}
|
||||
contents.replace((char)materialIndex, (char)0);
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, QByteArray& contents) {
|
||||
QHash<uchar, int> counts = countIndices(contents);
|
||||
for (int i = 0; i < materials.size(); i++) {
|
||||
if (counts.value(i + 1) == 0) {
|
||||
materials[i] = SharedObjectPointer();
|
||||
}
|
||||
}
|
||||
while (!(materials.isEmpty() || materials.last())) {
|
||||
materials.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
||||
if (!info.getBounds().intersects(_bounds)) {
|
||||
return STOP_RECURSION;
|
||||
|
@ -581,7 +521,6 @@ int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
|
|||
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
|
||||
float squaredRadius = scaledRadius * scaledRadius;
|
||||
bool changed = false;
|
||||
QHash<uchar, int> counts;
|
||||
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
|
||||
uchar* dest = lineDest;
|
||||
for (float x = startX; x <= endX; x += 1.0f, dest++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -583,6 +583,13 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const
|
|||
return result;
|
||||
}
|
||||
|
||||
PickRay ViewFrustum::computePickRay(float x, float y) {
|
||||
glm::vec3 pickRayOrigin;
|
||||
glm::vec3 pickRayDirection;
|
||||
computePickRay(x, y, pickRayOrigin, pickRayDirection);
|
||||
return PickRay(pickRayOrigin, pickRayDirection);
|
||||
}
|
||||
|
||||
void ViewFrustum::computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const {
|
||||
origin = _nearTopLeft + x*(_nearTopRight - _nearTopLeft) + y*(_nearBottomLeft - _nearTopLeft);
|
||||
direction = glm::normalize(origin - (_position + _orientation * _eyeOffsetPosition));
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "AABox.h"
|
||||
#include "AACube.h"
|
||||
#include "Plane.h"
|
||||
|
@ -105,6 +107,7 @@ public:
|
|||
bool isVerySimilar(const ViewFrustum& compareTo, bool debug = false) const;
|
||||
bool isVerySimilar(const ViewFrustum* compareTo, bool debug = false) const { return isVerySimilar(*compareTo, debug); }
|
||||
|
||||
PickRay computePickRay(float x, float y);
|
||||
void computePickRay(float x, float y, glm::vec3& origin, glm::vec3& direction) const;
|
||||
|
||||
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearValue, float& farValue,
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
#include "HFActionEvent.h"
|
||||
|
||||
HFActionEvent::HFActionEvent(QEvent::Type type, const QPointF& localPosition) :
|
||||
HFActionEvent::HFActionEvent(QEvent::Type type, const PickRay& actionRay) :
|
||||
HFMetaEvent(type),
|
||||
localPosition(localPosition)
|
||||
actionRay(actionRay)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ QEvent::Type HFActionEvent::endType() {
|
|||
|
||||
QScriptValue HFActionEvent::toScriptValue(QScriptEngine* engine, const HFActionEvent& event) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("x", event.localPosition.x());
|
||||
obj.setProperty("y", event.localPosition.y());
|
||||
obj.setProperty("actionRay", pickRayToScriptValue(engine, event.actionRay));
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,14 +12,17 @@
|
|||
#ifndef hifi_HFActionEvent_h
|
||||
#define hifi_HFActionEvent_h
|
||||
|
||||
#include "HFMetaEvent.h"
|
||||
|
||||
#include <qscriptengine.h>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include "HFMetaEvent.h"
|
||||
|
||||
class HFActionEvent : public HFMetaEvent {
|
||||
public:
|
||||
HFActionEvent() {};
|
||||
HFActionEvent(QEvent::Type type, const QPointF& localPosition);
|
||||
HFActionEvent(QEvent::Type type, const PickRay& actionRay);
|
||||
|
||||
static QEvent::Type startType();
|
||||
static QEvent::Type endType();
|
||||
|
@ -27,7 +30,7 @@ public:
|
|||
static QScriptValue toScriptValue(QScriptEngine* engine, const HFActionEvent& event);
|
||||
static void fromScriptValue(const QScriptValue& object, HFActionEvent& event);
|
||||
|
||||
QPointF localPosition;
|
||||
PickRay actionRay;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(HFActionEvent)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -53,7 +53,8 @@ void qURLFromScriptValue(const QScriptValue& object, QUrl& url);
|
|||
|
||||
class PickRay {
|
||||
public:
|
||||
PickRay() : origin(0.0f), direction(0.0f) { }
|
||||
PickRay() : origin(0.0f), direction(0.0f) { }
|
||||
PickRay(const glm::vec3& origin, const glm::vec3 direction) : origin(origin), direction(direction) {}
|
||||
glm::vec3 origin;
|
||||
glm::vec3 direction;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue