Merge branch 'master' of github.com:highfidelity/hifi into vhacd-knobs

This commit is contained in:
Seth Alves 2015-03-30 13:44:39 -07:00
commit 071b6a21de
84 changed files with 1760 additions and 1277 deletions

View file

@ -149,7 +149,8 @@ void AvatarMixer::broadcastAvatarData() {
// about a given otherNode to this node // about a given otherNode to this node
// FIXME does this mean we should sort the othernodes by distance before iterating // FIXME does this mean we should sort the othernodes by distance before iterating
// over them? // over them?
float outputBandwidth = node->getOutboundBandwidth(); // float outputBandwidth =
node->getOutboundBandwidth();
// this is an AGENT we have received head data from // this is an AGENT we have received head data from
// send back a packet with other active node data to this node // send back a packet with other active node data to this node
@ -169,7 +170,7 @@ void AvatarMixer::broadcastAvatarData() {
return true; return true;
}, },
[&](const SharedNodePointer& otherNode) { [&](const SharedNodePointer& otherNode) {
AvatarMixerClientData* otherNodeData = otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData()); AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
MutexTryLocker lock(otherNodeData->getMutex()); MutexTryLocker lock(otherNodeData->getMutex());
if (!lock.isLocked()) { if (!lock.isLocked()) {
return; return;

View file

@ -8,8 +8,8 @@ if (ANDROID)
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150209oss_src.tgz URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_src.tgz
URL_MD5 f09c9abe8ec74e6558c1f89cebbe2893 URL_MD5 bf090eaa86cf89ea014b7b462786a440
BUILD_COMMAND ${NDK_BUILD_COMMAND} --directory=jni target=android tbb tbbmalloc arch=arm BUILD_COMMAND ${NDK_BUILD_COMMAND} --directory=jni target=android tbb tbbmalloc arch=arm
BUILD_IN_SOURCE 1 BUILD_IN_SOURCE 1
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
@ -20,19 +20,20 @@ if (ANDROID)
) )
else () else ()
if (APPLE) if (APPLE)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150209oss_osx.tgz) set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_osx.tgz)
set(DOWNLOAD_MD5 3e683c19792582b61382e0d760ea5db2) set(DOWNLOAD_MD5 25a36ebff070ff801760ec658079f6aa)
elseif (WIN32) elseif (WIN32)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150209oss_win.zip) set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_win.zip)
set(DOWNLOAD_MD5 e19c184f2bb0e944fc5f397f1e34ca84) set(DOWNLOAD_MD5 d250d40bb93b255f75bcbb19e976a440)
else () else ()
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150209oss_lin.tgz) set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_lin.tgz)
set(DOWNLOAD_MD5 d9c2a6f7807df364be44a8c3c05e8457) set(DOWNLOAD_MD5 7830ba2bc62438325fba2ec0c95367a5)
endif () endif ()
ExternalProject_Add( ExternalProject_Add(
${EXTERNAL_NAME} ${EXTERNAL_NAME}
URL ${DOWNLOAD_URL} URL ${DOWNLOAD_URL}
URL_MD5 ${DOWNLOAD_MD5}
BUILD_COMMAND "" BUILD_COMMAND ""
CONFIGURE_COMMAND "" CONFIGURE_COMMAND ""
INSTALL_COMMAND "" INSTALL_COMMAND ""
@ -94,9 +95,15 @@ elseif (UNIX)
endif () endif ()
if (DEFINED _TBB_LIB_DIR) if (DEFINED _TBB_LIB_DIR)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbb_debug.${_LIB_EXT} CACHE FILEPATH "TBB debug library location") if (NOT APPLE)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbb_debug.${_LIB_EXT} CACHE FILEPATH "TBB debug library location")
set(${EXTERNAL_NAME_UPPER}_MALLOC_LIBRARY_DEBUG ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbbmalloc_debug.${_LIB_EXT} CACHE FILEPATH "TBB malloc debug library location")
else ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "TBB debug library location")
set(${EXTERNAL_NAME_UPPER}_MALLOC_LIBRARY_DEBUG "" CACHE FILEPATH "TBB malloc debug library location")
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbb.${_LIB_EXT} CACHE FILEPATH "TBB release library location") set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbb.${_LIB_EXT} CACHE FILEPATH "TBB release library location")
set(${EXTERNAL_NAME_UPPER}_MALLOC_LIBRARY_DEBUG ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbbmalloc_debug.${_LIB_EXT} CACHE FILEPATH "TBB malloc debug library location")
set(${EXTERNAL_NAME_UPPER}_MALLOC_LIBRARY_RELEASE ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbbmalloc.${_LIB_EXT} CACHE FILEPATH "TBB malloc release library location") set(${EXTERNAL_NAME_UPPER}_MALLOC_LIBRARY_RELEASE ${_TBB_LIB_DIR}/${_LIB_PREFIX}tbbmalloc.${_LIB_EXT} CACHE FILEPATH "TBB malloc release library location")
endif () endif ()

View file

@ -6,7 +6,7 @@
{ {
"name": "access_token", "name": "access_token",
"label": "Access Token", "label": "Access Token",
"help": "This is an access token generated on the <a href='https://metaverse.highfidelity.io/user/security' target='_blank'>My Security</a> page of your High Fidelity account.<br/>Generate a token with the 'domains' scope and paste it here.<br/>This is required to associate this domain-server with a domain in your account." "help": "This is an access token generated on the <a href='https://metaverse.highfidelity.com/user/security' target='_blank'>My Security</a> page of your High Fidelity account.<br/>Generate a token with the 'domains' scope and paste it here.<br/>This is required to associate this domain-server with a domain in your account."
}, },
{ {
"name": "id", "name": "id",
@ -30,7 +30,7 @@
}, },
{ {
"value": "disabled", "value": "disabled",
"label": "None: use the network information I have entered for this domain at metaverse.highfidelity.io" "label": "None: use the network information I have entered for this domain at metaverse.highfidelity.com"
} }
] ]
}, },

View file

@ -652,7 +652,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
clickedButton.attr('disabled', 'disabled') clickedButton.attr('disabled', 'disabled')
// get a list of user domains from data-web // get a list of user domains from data-web
data_web_domains_url = "https://metaverse.highfidelity.io/api/v1/domains?access_token=" data_web_domains_url = "https://metaverse.highfidelity.com/api/v1/domains?access_token="
$.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){ $.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){
modal_buttons = { modal_buttons = {
@ -682,7 +682,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
modal_buttons["success"] = { modal_buttons["success"] = {
label: 'Create new domain', label: 'Create new domain',
callback: function() { callback: function() {
window.open("https://metaverse.highfidelity.io/user/domains", '_blank'); window.open("https://metaverse.highfidelity.com/user/domains", '_blank');
} }
} }
modal_body = "<p>You do not have any domains in your High Fidelity account." + modal_body = "<p>You do not have any domains in your High Fidelity account." +
@ -708,4 +708,4 @@ function chooseFromHighFidelityDomains(clickedButton) {
title: "Access token required" title: "Access token required"
}) })
} }
} }

View file

@ -814,8 +814,9 @@ void DomainServer::requestUserPublicKey(const QString& username) {
qDebug() << "Requesting public key for user" << username; qDebug() << "Requesting public key for user" << username;
AccountManager::getInstance().unauthenticatedRequest(USER_PUBLIC_KEY_PATH.arg(username), AccountManager::getInstance().sendRequest(USER_PUBLIC_KEY_PATH.arg(username),
QNetworkAccessManager::GetOperation, callbackParams); AccountManagerAuth::None,
QNetworkAccessManager::GetOperation, callbackParams);
} }
QUrl DomainServer::oauthRedirectURL() { QUrl DomainServer::oauthRedirectURL() {
@ -1116,8 +1117,10 @@ void DomainServer::sendPendingTransactionsToServer() {
transactionCallbackParams.jsonCallbackMethod = "transactionJSONCallback"; transactionCallbackParams.jsonCallbackMethod = "transactionJSONCallback";
while (i != _pendingAssignmentCredits.end()) { while (i != _pendingAssignmentCredits.end()) {
accountManager.authenticatedRequest("api/v1/transactions", QNetworkAccessManager::PostOperation, accountManager.sendRequest("api/v1/transactions",
transactionCallbackParams, i.value()->postJson().toJson()); AccountManagerAuth::Required,
QNetworkAccessManager::PostOperation,
transactionCallbackParams, i.value()->postJson().toJson());
// set this transaction to finalized so we don't add additional credits to it // set this transaction to finalized so we don't add additional credits to it
i.value()->setIsFinalized(true); i.value()->setIsFinalized(true);
@ -1240,10 +1243,11 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson())); QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson()));
AccountManager::getInstance().authenticatedRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)), AccountManager::getInstance().sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(domainID)),
QNetworkAccessManager::PutOperation, AccountManagerAuth::Required,
JSONCallbackParameters(), QNetworkAccessManager::PutOperation,
domainUpdateJSON.toUtf8()); JSONCallbackParameters(),
domainUpdateJSON.toUtf8());
} }
// todo: have data-web respond with ice-server hostname to use // todo: have data-web respond with ice-server hostname to use

View file

@ -76,7 +76,6 @@ var DEFAULT_DIMENSIONS = {
var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS); var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS);
var MENU_INSPECT_TOOL_ENABLED = "Inspect Tool";
var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select"; var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select";
var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus"; var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus";
var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode"; var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode";
@ -89,13 +88,7 @@ var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode";
var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain." var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain."
var modelURLs = [ var modelURLs = [
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx", "Insert the URL to your FBX"
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush1.fbx",
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush6.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; var mode = 0;
@ -130,7 +123,7 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", {
visible: false, visible: false,
}); });
var MARKETPLACE_URL = "https://metaverse.highfidelity.io/marketplace"; var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace";
var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false); var marketplaceWindow = new WebWindow('Marketplace', MARKETPLACE_URL, 900, 700, false);
marketplaceWindow.setVisible(false); marketplaceWindow.setVisible(false);
@ -338,7 +331,11 @@ var toolBar = (function () {
return true; return true;
} }
if (browseModelsButton === toolBar.clicked(clickedOverlay)) { if (browseModelsButton === toolBar.clicked(clickedOverlay)) {
if (marketplaceWindow.url != MARKETPLACE_URL) {
marketplaceWindow.setURL(MARKETPLACE_URL);
}
marketplaceWindow.setVisible(true); marketplaceWindow.setVisible(true);
marketplaceWindow.raise();
return true; return true;
} }
@ -384,9 +381,7 @@ var toolBar = (function () {
position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_LIGHT_DIMENSIONS), DEFAULT_LIGHT_DIMENSIONS), position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_LIGHT_DIMENSIONS), DEFAULT_LIGHT_DIMENSIONS),
dimensions: DEFAULT_LIGHT_DIMENSIONS, dimensions: DEFAULT_LIGHT_DIMENSIONS,
isSpotlight: false, isSpotlight: false,
diffuseColor: { red: 255, green: 255, blue: 255 }, color: { red: 150, green: 150, blue: 150 },
ambientColor: { red: 255, green: 255, blue: 255 },
specularColor: { red: 0, green: 0, blue: 0 },
constantAttenuation: 1, constantAttenuation: 1,
linearAttenuation: 0, linearAttenuation: 0,
@ -540,7 +535,7 @@ function mousePressEvent(event) {
mouseHasMovedSincePress = false; mouseHasMovedSincePress = false;
mouseCapturedByTool = false; mouseCapturedByTool = false;
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) { if (propertyMenu.mousePressEvent(event) || toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) {
mouseCapturedByTool = true; mouseCapturedByTool = true;
return; return;
} }
@ -549,18 +544,6 @@ function mousePressEvent(event) {
// Event handled; do nothing. // Event handled; do nothing.
return; return;
} }
} 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);
}
} }
} }
@ -572,6 +555,8 @@ var IDLE_MOUSE_TIMEOUT = 200;
var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0; var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0;
function mouseMoveEvent(event) { function mouseMoveEvent(event) {
mouseHasMovedSincePress = true;
if (placingEntityID) { if (placingEntityID) {
if (!placingEntityID.isKnownID) { if (!placingEntityID.isKnownID) {
placingEntityID = Entities.identifyEntity(placingEntityID); placingEntityID = Entities.identifyEntity(placingEntityID);
@ -592,10 +577,8 @@ function mouseMoveEvent(event) {
Script.clearTimeout(idleMouseTimerId); Script.clearTimeout(idleMouseTimerId);
} }
mouseHasMovedSincePress = true;
// allow the selectionDisplay and cameraManager to handle the event first, if it doesn't handle it, then do our own thing // 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)) { if (selectionDisplay.mouseMoveEvent(event) || propertyMenu.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) {
return; return;
} }
@ -640,7 +623,7 @@ function highlightEntityUnderCursor(position, accurateRay) {
function mouseReleaseEvent(event) { function mouseReleaseEvent(event) {
if (toolBar.mouseReleaseEvent(event)) { if (propertyMenu.mouseReleaseEvent(event) || toolBar.mouseReleaseEvent(event)) {
return true; return true;
} }
if (placingEntityID) { if (placingEntityID) {
@ -664,74 +647,88 @@ function mouseReleaseEvent(event) {
} }
function mouseClickEvent(event) { function mouseClickEvent(event) {
if (!event.isLeftButton || !isActive) { if (isActive && event.isLeftButton) {
return; var result = findClickedEntity(event);
} if (result === null) {
var result = findClickedEntity(event);
if (result === null) {
if (!event.isShifted) {
selectionManager.clearSelections();
}
return;
}
toolBar.setActive(true);
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;
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 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 sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
if (0 < x && sizeOK) {
entitySelected = true;
selectedEntityID = foundEntity;
orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
if (!event.isShifted) { if (!event.isShifted) {
selectionManager.setSelections([foundEntity]); selectionManager.clearSelections();
}
return;
}
toolBar.setActive(true);
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;
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 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 sizeOK = (allowLargeModels || angularSize < MAX_ANGULAR_SIZE)
&& (allowSmallModels || angularSize > MIN_ANGULAR_SIZE);
if (0 < x && sizeOK) {
entitySelected = true;
selectedEntityID = foundEntity;
orientation = MyAvatar.orientation;
intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation));
if (!event.isShifted) {
selectionManager.setSelections([foundEntity]);
} else {
selectionManager.addEntity(foundEntity, true);
}
print("Model selected: " + foundEntity.id);
selectionDisplay.select(selectedEntityID, event);
if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) {
cameraManager.focus(selectionManager.worldPosition,
selectionManager.worldDimensions,
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
}
}
}
} else if (event.isRightButton) {
var result = findClickedEntity(event);
if (result) {
var properties = Entities.getEntityProperties(result.entityID);
if (properties.marketplaceID) {
propertyMenu.marketplaceID = properties.marketplaceID;
propertyMenu.updateMenuItemText(showMenuItem, "Show in Marketplace");
} else { } else {
selectionManager.addEntity(foundEntity, true); propertyMenu.marketplaceID = null;
} propertyMenu.updateMenuItemText(showMenuItem, "No marketplace info");
print("Model selected: " + foundEntity.id);
selectionDisplay.select(selectedEntityID, event);
if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) {
cameraManager.focus(selectionManager.worldPosition,
selectionManager.worldDimensions,
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
} }
propertyMenu.setPosition(event.x, event.y);
propertyMenu.show();
} else {
propertyMenu.hide();
} }
} }
} }
@ -776,7 +773,7 @@ function setupModelMenus() {
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities", shortcutKey: "CTRL+META+I", afterItem: "Export Entities" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities", shortcutKey: "CTRL+META+I", afterItem: "Export Entities" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities from URL", shortcutKey: "CTRL+META+U", afterItem: "Import Entities" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities from URL", shortcutKey: "CTRL+META+U", afterItem: "Import Entities" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT, afterItem: MENU_INSPECT_TOOL_ENABLED, Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT,
isCheckable: true, isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" }); isCheckable: true, isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_AUTO_FOCUS_ON_SELECT, Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_AUTO_FOCUS_ON_SELECT,
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
@ -807,7 +804,6 @@ function cleanupModelMenus() {
Menu.removeMenuItem("File", "Import Entities"); Menu.removeMenuItem("File", "Import Entities");
Menu.removeMenuItem("File", "Import Entities from URL"); Menu.removeMenuItem("File", "Import Entities from URL");
Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED);
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT); Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS); Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS);
Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE); Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
@ -829,11 +825,21 @@ Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(importingSVOTextOverlay); Overlays.deleteOverlay(importingSVOTextOverlay);
}); });
var lastOrientation = null;
var lastPosition = null;
// Do some stuff regularly, like check for placement of various overlays // Do some stuff regularly, like check for placement of various overlays
Script.update.connect(function (deltaTime) { Script.update.connect(function (deltaTime) {
toolBar.move(); toolBar.move();
progressDialog.move(); progressDialog.move();
selectionDisplay.checkMove(); selectionDisplay.checkMove();
var dOrientation = Math.abs(Quat.dot(Camera.orientation, lastOrientation) - 1);
var dPosition = Vec3.distance(Camera.position, lastPosition);
if (dOrientation > 0.001 || dPosition > 0.001) {
propertyMenu.hide();
lastOrientation = Camera.orientation;
lastPosition = Camera.position;
}
}); });
function insideBox(center, dimensions, point) { function insideBox(center, dimensions, point) {
@ -910,8 +916,8 @@ function handeMenuEvent(menuItem) {
if (!selectionManager.hasSelection()) { if (!selectionManager.hasSelection()) {
Window.alert("No entities have been selected."); Window.alert("No entities have been selected.");
} else { } else {
var filename = "models__" + Window.location.hostname + "__.svo"; var filename = "entities__" + Window.location.hostname + ".svo.json";
filename = Window.save("Select where to save", filename, "*.svo") filename = Window.save("Select where to save", filename, "*.json")
if (filename) { if (filename) {
var success = Clipboard.exportEntities(filename, selectionManager.selections); var success = Clipboard.exportEntities(filename, selectionManager.selections);
if (!success) { if (!success) {
@ -923,7 +929,7 @@ function handeMenuEvent(menuItem) {
var importURL; var importURL;
if (menuItem == "Import Entities") { if (menuItem == "Import Entities") {
importURL = Window.browse("Select models to import", "", "*.svo"); importURL = Window.browse("Select models to import", "", "*.json");
} else { } else {
importURL = Window.prompt("URL of SVO to import", ""); importURL = Window.prompt("URL of SVO to import", "");
} }
@ -1143,6 +1149,12 @@ PropertiesTool = function(opts) {
} }
pushCommandForSelections(); pushCommandForSelections();
selectionManager._update(); selectionManager._update();
} else if (data.type == "showMarketplace") {
if (marketplaceWindow.url != data.url) {
marketplaceWindow.setURL(data.url);
}
marketplaceWindow.setVisible(true);
marketplaceWindow.raise();
} else if (data.type == "action") { } else if (data.type == "action") {
if (data.action == "moveSelectionToGrid") { if (data.action == "moveSelectionToGrid") {
if (selectionManager.hasSelection()) { if (selectionManager.hasSelection()) {
@ -1216,4 +1228,142 @@ PropertiesTool = function(opts) {
return that; return that;
}; };
PopupMenu = function() {
var self = this;
var MENU_ITEM_HEIGHT = 21;
var MENU_ITEM_SPACING = 1;
var TEXT_MARGIN = 7;
var overlays = [];
var overlayInfo = {};
var upColor = { red: 0, green: 0, blue: 0 };
var downColor = { red: 192, green: 192, blue: 192 };
var overColor = { red: 128, green: 128, blue: 128 };
self.onSelectMenuItem = function() { };
self.addMenuItem = function(name) {
var id = Overlays.addOverlay("text", {
text: name,
backgroundAlpha: 1.0,
backgroundColor: upColor,
topMargin: TEXT_MARGIN,
leftMargin: TEXT_MARGIN,
width: 210,
height: MENU_ITEM_HEIGHT,
font: { size: 12 },
visible: false,
});
overlays.push(id);
overlayInfo[id] = { name: name };
return id;
};
self.updateMenuItemText = function(id, newText) {
Overlays.editOverlay(id, { text: newText });
};
self.setPosition = function(x, y) {
for (var key in overlayInfo) {
Overlays.editOverlay(key, {
x: x,
y: y,
});
y += MENU_ITEM_HEIGHT + MENU_ITEM_SPACING;
}
};
self.onSelected = function() { };
var pressingOverlay = null;
var hoveringOverlay = null;
self.mousePressEvent = function(event) {
if (event.isLeftButton) {
var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (overlay in overlayInfo) {
pressingOverlay = overlay;
Overlays.editOverlay(pressingOverlay, { backgroundColor: downColor });
} else {
self.hide();
}
return false;
}
};
self.mouseMoveEvent = function(event) {
if (visible) {
var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (!pressingOverlay) {
if (hoveringOverlay != null && overlay != hoveringOverlay) {
Overlays.editOverlay(hoveringOverlay, { backgroundColor: upColor});
hoveringOverlay = null;
}
if (overlay != hoveringOverlay && overlay in overlayInfo) {
Overlays.editOverlay(overlay, { backgroundColor: overColor });
hoveringOverlay = overlay;
}
}
}
return false;
};
self.mouseReleaseEvent = function(event) {
var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (pressingOverlay != null) {
if (overlay == pressingOverlay) {
self.onSelectMenuItem(overlayInfo[overlay].name);
}
Overlays.editOverlay(pressingOverlay, { backgroundColor: upColor });
pressingOverlay = null;
self.hide();
}
};
var visible = false;
self.setVisible = function(newVisible) {
if (newVisible != visible) {
visible = newVisible;
for (var key in overlayInfo) {
Overlays.editOverlay(key, { visible: newVisible });
}
}
}
self.show = function() {
self.setVisible(true);
}
self.hide = function() {
self.setVisible(false);
}
function cleanup() {
for (var i = 0; i < overlays.length; i++) {
Overlays.deleteOverlay(overlays[i]);
}
}
Controller.mousePressEvent.connect(self.mousePressEvent);
Controller.mouseMoveEvent.connect(self.mouseMoveEvent);
Controller.mouseReleaseEvent.connect(self.mouseReleaseEvent);
Script.scriptEnding.connect(cleanup);
return this;
};
var propertyMenu = PopupMenu();
propertyMenu.onSelectMenuItem = function(name) {
if (propertyMenu.marketplaceID) {
var url = "https://metaverse.highfidelity.io/marketplace/items/" + propertyMenu.marketplaceID;
if (marketplaceWindow.url != url) {
marketplaceWindow.setURL(url);
}
marketplaceWindow.setVisible(true);
marketplaceWindow.raise();
}
};
var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace");
propertiesTool = PropertiesTool(); propertiesTool = PropertiesTool();

View file

@ -1,8 +1,32 @@
(function() { (function() {
this.entityID = null; this.entityID = null;
this.properties = null;
this.lightID = null; this.lightID = null;
this.sound = null; this.sound = null;
this.soundURLs = ["https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_1.wav",
"https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav",
"https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_3.wav"]
var DEFAULT_USER_DATA = {
creatingLight: false,
lightID: null,
lightDefaultProperties: {
type: "Light",
position: { x: 0, y: 0, z: 0 },
dimensions: { x: 5, y: 5, z: 5 },
isSpotlight: false,
color: { red: 255, green: 48, blue: 0 },
diffuseColor: { red: 255, green: 255, blue: 255 },
ambientColor: { red: 255, green: 255, blue: 255 },
specularColor: { red: 0, green: 0, blue: 0 },
constantAttenuation: 1,
linearAttenuation: 0,
quadraticAttenuation: 0,
intensity: 10,
exponent: 0,
cutoff: 180, // in degrees
},
soundIndex: Math.floor(Math.random() * this.soundURLs.length)
};
function copyObject(object) { function copyObject(object) {
return JSON.parse(JSON.stringify(object)); return JSON.parse(JSON.stringify(object));
@ -33,7 +57,8 @@
// Download sound if needed // Download sound if needed
this.maybeDownloadSound = function() { this.maybeDownloadSound = function() {
if (this.sound === null) { if (this.sound === null) {
this.sound = SoundCache.getSound("http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav"); var soundIndex = getUserData(this.entityID).soundIndex;
this.sound = SoundCache.getSound(this.soundURLs[soundIndex]);
} }
} }
// Play switch sound // Play switch sound
@ -47,17 +72,21 @@
print("Warning: Couldn't play sound."); print("Warning: Couldn't play sound.");
} }
} }
// Toggles the associated light entity // Checks whether the userData is well-formed and updates it if not
this.toggleLight = function() { this.checkUserData = function() {
if (this.lightID) { var userData = getUserData(this.entityID);
var lightProperties = Entities.getEntityProperties(this.lightID); if (!userData) {
Entities.editEntity(this.lightID, { visible: !lightProperties.visible }); userData = DEFAULT_USER_DATA;
} else { } else if (!userData.lightDefaultProperties) {
print("Warning: No light to turn on/off"); userData.lightDefaultProperties = DEFAULT_USER_DATA.lightDefaultProperties;
} else if (!userData.soundIndex) {
userData.soundIndex = DEFAULT_USER_DATA.soundIndex;
} }
updateUserData(this.entityID, userData);
} }
// Create a Light entity
this.createLight = function(userData) { this.createLight = function(userData) {
var lightProperties = copyObject(userData.lightDefaultProperties); var lightProperties = copyObject(userData.lightDefaultProperties);
if (lightProperties) { if (lightProperties) {
@ -74,56 +103,48 @@
} }
} }
// Tries to find a valid light, creates one otherwise
this.updateLightID = function() { this.updateLightID = function() {
var userData = getUserData(this.entityID);
if (!userData) {
userData = {
lightID: null,
lightDefaultProperties: {
type: "Light",
position: { x: 0, y: 0, z: 0 },
dimensions: { x: 5, y: 5, z: 5 },
isSpotlight: false,
color: { red: 255, green: 48, blue: 0 },
diffuseColor: { red: 255, green: 255, blue: 255 },
ambientColor: { red: 255, green: 255, blue: 255 },
specularColor: { red: 0, green: 0, blue: 0 },
constantAttenuation: 1,
linearAttenuation: 0,
quadraticAttenuation: 0,
intensity: 10,
exponent: 0,
cutoff: 180, // in degrees
}
};
updateUserData(this.entityID, userData);
}
// Find valid light // Find valid light
if (doesEntityExistNow(this.lightID)) { if (doesEntityExistNow(this.lightID)) {
if (!didEntityExist(this.lightID)) {
// Light now has an ID, so update it in userData
this.lightID = getTrueID(this.lightID);
userData.lightID = this.lightID;
updateUserData(this.entityID, userData);
}
return; return;
} }
var userData = getUserData(this.entityID);
if (doesEntityExistNow(userData.lightID)) { if (doesEntityExistNow(userData.lightID)) {
this.lightID = getTrueID(userData.lightID); this.lightID = userData.lightID;
return; return;
} }
// No valid light, create one if (!userData.creatingLight) {
this.lightID = this.createLight(userData); // No valid light, create one
print("Created new light entity"); userData.creatingLight = true;
updateUserData(this.entityID, userData);
// Update user data with new ID this.lightID = this.createLight(userData);
this.maybeUpdateLightIDInUserData();
print("Created new light entity");
}
}
this.maybeUpdateLightIDInUserData = function() {
if (getTrueID(this.lightID).isKnownID) {
this.lightID = getTrueID(this.lightID);
this.updateLightIDInUserData();
} else {
var that = this;
Script.setTimeout(function() { that.maybeUpdateLightIDInUserData() }, 500);
}
}
// Update user data with new lightID
this.updateLightIDInUserData = function() {
var userData = getUserData(this.entityID);
userData.lightID = this.lightID; userData.lightID = this.lightID;
userData.creatingLight = false;
updateUserData(this.entityID, userData); updateUserData(this.entityID, userData);
} }
// Moves light entity if the lamp entity moved
this.maybeMoveLight = function() { this.maybeMoveLight = function() {
var entityProperties = Entities.getEntityProperties(this.entityID); var entityProperties = Entities.getEntityProperties(this.entityID);
var lightProperties = Entities.getEntityProperties(this.lightID); var lightProperties = Entities.getEntityProperties(this.lightID);
@ -139,8 +160,9 @@
} }
} }
// Stores light entity relative position in the lamp metadata
this.updateRelativeLightPosition = function() { this.updateRelativeLightPosition = function() {
if (!doesEntityExistNow(this.entityID) || !doesEntityExistNow(this.lightID)) { if (!doesEntityExistNow(this.lightID)) {
print("Warning: ID invalid, couldn't save relative position."); print("Warning: ID invalid, couldn't save relative position.");
return; return;
} }
@ -168,21 +190,37 @@
updateUserData(this.entityID, userData); updateUserData(this.entityID, userData);
print("Relative properties of light entity saved."); print("Relative properties of light entity saved.");
} }
// This function should be called before any callback is executed
this.preOperation = function(entityID) {
this.entityID = entityID;
this.checkUserData();
this.maybeDownloadSound();
}
// Toggles the associated light entity
this.toggleLight = function() {
if (this.lightID) {
var lightProperties = Entities.getEntityProperties(this.lightID);
Entities.editEntity(this.lightID, { visible: !lightProperties.visible });
this.playSound();
} else {
print("Warning: No light to turn on/off");
}
}
this.preload = function(entityID) { this.preload = function(entityID) {
this.entityID = entityID; this.preOperation(entityID);
this.maybeDownloadSound();
}; };
this.clickReleaseOnEntity = function(entityID, mouseEvent) { this.clickReleaseOnEntity = function(entityID, mouseEvent) {
this.entityID = entityID; this.preOperation(entityID);
this.maybeDownloadSound();
if (mouseEvent.isLeftButton) { if (mouseEvent.isLeftButton) {
this.updateLightID(); this.updateLightID();
this.maybeMoveLight(); this.maybeMoveLight();
this.toggleLight(); this.toggleLight();
this.playSound();
} else if (mouseEvent.isRightButton) { } else if (mouseEvent.isRightButton) {
this.updateRelativeLightPosition(); this.updateRelativeLightPosition();
} }

View file

@ -9,15 +9,20 @@
this.preload = function(entityID) { this.preload = function(entityID) {
teleport = SoundCache.getSound("http://s3.amazonaws.com/hifi-public/birarda/teleport.raw"); teleport = SoundCache.getSound("http://s3.amazonaws.com/hifi-public/birarda/teleport.raw");
var properties = Entities.getEntityProperties(entityID); var properties = Entities.getEntityProperties(entityID);
portalDestination = properties.userData;
animationURL = properties.modelURL; animationURL = properties.modelURL;
print("The portal destination is " + portalDestination); print("The portal destination is " + portalDestination);
} }
this.enterEntity = function(entityID) { this.enterEntity = function(entityID) {
var properties = Entities.getEntityProperties(entityID); // in case the userData/portalURL has changed
portalDestination = properties.userData;
print("enterEntity() .... The portal destination is " + portalDestination);
if (portalDestination.length > 0) { if (portalDestination.length > 0) {
print("Teleporting to hifi://" + portalDestination); print("Teleporting to hifi://" + portalDestination);
Window.location = "hifi://" + portalDestination; Window.location = "hifi://" + portalDestination;

View file

@ -140,7 +140,6 @@
var elLifetime = document.getElementById("property-lifetime"); var elLifetime = document.getElementById("property-lifetime");
var elScriptURL = document.getElementById("property-script-url"); var elScriptURL = document.getElementById("property-script-url");
var elUserData = document.getElementById("property-user-data"); var elUserData = document.getElementById("property-user-data");
var elAttribution = document.getElementById("property-attribution");
var elBoxSections = document.querySelectorAll(".box-section"); var elBoxSections = document.querySelectorAll(".box-section");
var elBoxColorRed = document.getElementById("property-box-red"); var elBoxColorRed = document.getElementById("property-box-red");
@ -264,7 +263,6 @@
elLifetime.value = properties.lifetime; elLifetime.value = properties.lifetime;
elScriptURL.value = properties.script; elScriptURL.value = properties.script;
elUserData.value = properties.userData; elUserData.value = properties.userData;
elAttribution.value = properties.attribution;
if (properties.type != "Box") { if (properties.type != "Box") {
for (var i = 0; i < elBoxSections.length; i++) { for (var i = 0; i < elBoxSections.length; i++) {
@ -395,7 +393,6 @@
elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime'));
elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script')); elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData'));
elAttribution.addEventListener('change', createEmitTextPropertyUpdateFunction('attribution'));
var boxColorChangeFunction = createEmitColorPropertyUpdateFunction( var boxColorChangeFunction = createEmitColorPropertyUpdateFunction(
'color', elBoxColorRed, elBoxColorGreen, elBoxColorBlue); 'color', elBoxColorRed, elBoxColorGreen, elBoxColorBlue);
@ -630,13 +627,6 @@
</div> </div>
</div> </div>
<div class="property">
<div class="label">Attribution</div>
<div class="value">
<textarea id="property-attribution"></textarea>
</div>
</div>
<div class="box-section property"> <div class="box-section property">
<div class="label">Color</div> <div class="label">Color</div>

View file

@ -1263,11 +1263,13 @@ SelectionDisplay = (function () {
duplicatedEntityIDs = []; duplicatedEntityIDs = [];
for (var otherEntityID in SelectionManager.savedProperties) { for (var otherEntityID in SelectionManager.savedProperties) {
var properties = SelectionManager.savedProperties[otherEntityID]; var properties = SelectionManager.savedProperties[otherEntityID];
var entityID = Entities.addEntity(properties); if (!properties.locked) {
duplicatedEntityIDs.push({ var entityID = Entities.addEntity(properties);
entityID: entityID, duplicatedEntityIDs.push({
properties: properties, entityID: entityID,
}); properties: properties,
});
}
} }
} else { } else {
duplicatedEntityIDs = null; duplicatedEntityIDs = null;
@ -1361,11 +1363,13 @@ SelectionDisplay = (function () {
duplicatedEntityIDs = []; duplicatedEntityIDs = [];
for (var otherEntityID in SelectionManager.savedProperties) { for (var otherEntityID in SelectionManager.savedProperties) {
var properties = SelectionManager.savedProperties[otherEntityID]; var properties = SelectionManager.savedProperties[otherEntityID];
var entityID = Entities.addEntity(properties); if (!properties.locked) {
duplicatedEntityIDs.push({ var entityID = Entities.addEntity(properties);
entityID: entityID, duplicatedEntityIDs.push({
properties: properties, entityID: entityID,
}); properties: properties,
});
}
} }
} else { } else {
duplicatedEntityIDs = null; duplicatedEntityIDs = null;

View file

@ -21,8 +21,8 @@ modelUploader = (function () {
//svoBuffer, //svoBuffer,
mapping, mapping,
geometry, geometry,
API_URL = "https://metaverse.highfidelity.io/api/v1/models", API_URL = "https://metaverse.highfidelity.com/api/v1/models",
MODEL_URL = "http://public.highfidelity.io/models/content", MODEL_URL = "http://public.highfidelity.com/models/content",
NAME_FIELD = "name", NAME_FIELD = "name",
SCALE_FIELD = "scale", SCALE_FIELD = "scale",
FILENAME_FIELD = "filename", FILENAME_FIELD = "filename",
@ -690,4 +690,4 @@ modelUploader = (function () {
}; };
return that; return that;
}()); }());

View file

@ -158,7 +158,7 @@ var places = {};
function changeLobbyTextures() { function changeLobbyTextures() {
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.open("GET", "https://metaverse.highfidelity.io/api/v1/places?limit=21", false); req.open("GET", "https://metaverse.highfidelity.com/api/v1/places?limit=21", false);
req.send(); req.send();
places = JSON.parse(req.responseText).data.places; places = JSON.parse(req.responseText).data.places;

View file

@ -80,7 +80,6 @@ function touchBeginEvent(event) {
yawFromTouch = 0; yawFromTouch = 0;
pitchFromTouch = 0; pitchFromTouch = 0;
startedTouching = true; startedTouching = true;
print("TOUCH BEGIN");
} }
function touchEndEvent(event) { function touchEndEvent(event) {
@ -88,7 +87,6 @@ function touchEndEvent(event) {
print("touchEndEvent event.x,y=" + event.x + ", " + event.y); print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
} }
startedTouching = false; startedTouching = false;
print("TOUCH END");
} }
function touchUpdateEvent(event) { function touchUpdateEvent(event) {

View file

@ -92,11 +92,13 @@ var NotificationType = {
SNAPSHOT: 2, SNAPSHOT: 2,
WINDOW_RESIZE: 3, WINDOW_RESIZE: 3,
LOD_WARNING: 4, LOD_WARNING: 4,
CONNECTION_REFUSED: 5,
properties: [ properties: [
{ text: "Mute Toggle" }, { text: "Mute Toggle" },
{ text: "Snapshot" }, { text: "Snapshot" },
{ text: "Window Resize" }, { text: "Window Resize" },
{ text: "Level of Detail" } { text: "Level of Detail" },
{ text: "Connection Refused" }
], ],
getTypeFromMenuItem: function(menuItemName) { getTypeFromMenuItem: function(menuItemName) {
if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) { if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) {
@ -501,6 +503,10 @@ function onMuteStateChanged() {
createNotification(muteString, NotificationType.MUTE_TOGGLE); createNotification(muteString, NotificationType.MUTE_TOGGLE);
} }
function onDomainConnectionRefused(reason) {
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED );
}
// handles mouse clicks on buttons // handles mouse clicks on buttons
function mousePressEvent(event) { function mousePressEvent(event) {
var pickRay, var pickRay,
@ -608,5 +614,6 @@ Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update); Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding); Script.scriptEnding.connect(scriptEnding);
Menu.menuItemEvent.connect(menuItemEvent); Menu.menuItemEvent.connect(menuItemEvent);
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
setup(); setup();

View file

@ -11,7 +11,7 @@
var usersWindow = (function () { var usersWindow = (function () {
var WINDOW_WIDTH_2D = 150, var WINDOW_WIDTH_2D = 160,
WINDOW_MARGIN_2D = 12, WINDOW_MARGIN_2D = 12,
WINDOW_FONT_2D = { size: 12 }, WINDOW_FONT_2D = { size: 12 },
WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 }, WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 },
@ -22,6 +22,17 @@ var usersWindow = (function () {
WINDOW_BACKGROUND_ALPHA_2D = 0.7, WINDOW_BACKGROUND_ALPHA_2D = 0.7,
windowPane2D, windowPane2D,
windowHeading2D, windowHeading2D,
SCROLLBAR_BACKGROUND_WIDTH_2D = 12,
SCROLLBAR_BACKGROUND_COLOR_2D = { red: 80, green: 80, blue: 80 },
SCROLLBAR_BACKGROUND_ALPHA_2D = 0.8,
scrollbarBackground2D,
SCROLLBAR_BAR_MIN_HEIGHT = 5,
SCROLLBAR_BAR_COLOR_2D = { red: 180, green: 180, blue: 180 },
SCROLLBAR_BAR_ALPHA_2D = 0.8,
SCROLLBAR_BAR_SELECTED_ALPHA_2D = 0.9,
scrollbarBar2D,
scrollbarBackgroundHeight,
scrollbarBarHeight,
VISIBILITY_SPACER_2D = 12, // Space between list of users and visibility controls VISIBILITY_SPACER_2D = 12, // Space between list of users and visibility controls
visibilityHeading2D, visibilityHeading2D,
VISIBILITY_RADIO_SPACE = 16, VISIBILITY_RADIO_SPACE = 16,
@ -33,8 +44,10 @@ var usersWindow = (function () {
usersOnline, // Raw users data usersOnline, // Raw users data
linesOfUsers = [], // Array of indexes pointing into usersOnline linesOfUsers = [], // Array of indexes pointing into usersOnline
numUsersToDisplay = 0,
firstUserToDisplay = 0,
API_URL = "https://metaverse.highfidelity.io/api/v1/users?status=online", API_URL = "https://metaverse.highfidelity.com/api/v1/users?status=online",
HTTP_GET_TIMEOUT = 60000, // ms = 1 minute HTTP_GET_TIMEOUT = 60000, // ms = 1 minute
usersRequest, usersRequest,
processUsers, processUsers,
@ -54,6 +67,15 @@ var usersWindow = (function () {
isVisible = true, isVisible = true,
viewportHeight, viewportHeight,
isMirrorDisplay = false,
isFullscreenMirror = false,
isUsingScrollbars = false,
isMovingScrollbar = false,
scrollbarBackgroundPosition = {},
scrollbarBarPosition = {},
scrollbarBarClickedAt, // 0.0 .. 1.0
scrollbarValue = 0.0, // 0.0 .. 1.0
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/", HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/",
RADIO_BUTTON_SVG = HIFI_PUBLIC_BUCKET + "images/radio-button.svg", RADIO_BUTTON_SVG = HIFI_PUBLIC_BUCKET + "images/radio-button.svg",
@ -62,10 +84,32 @@ var usersWindow = (function () {
radioButtonDiameter; radioButtonDiameter;
function calculateWindowHeight() { function calculateWindowHeight() {
var AUDIO_METER_HEIGHT = 52,
MIRROR_HEIGHT = 220,
nonUsersHeight,
maxWindowHeight;
// Reserve 5 lines for window heading plus visibility heading and controls // Reserve 5 lines for window heading plus visibility heading and controls
// Subtract windowLineSpacing for both end of user list and end of controls // Subtract windowLineSpacing for both end of user list and end of controls
windowHeight = (linesOfUsers.length > 0 ? linesOfUsers.length + 5 : 5) * windowLineHeight nonUsersHeight = 5 * windowLineHeight - 2 * windowLineSpacing + VISIBILITY_SPACER_2D + 2 * WINDOW_MARGIN_2D;
- 2 * windowLineSpacing + VISIBILITY_SPACER_2D + 2 * WINDOW_MARGIN_2D;
// Limit window to height of viewport minus VU meter and mirror if displayed
windowHeight = linesOfUsers.length * windowLineHeight + nonUsersHeight;
maxWindowHeight = viewportHeight - AUDIO_METER_HEIGHT;
if (isMirrorDisplay && !isFullscreenMirror) {
maxWindowHeight -= MIRROR_HEIGHT;
}
windowHeight = Math.max(Math.min(windowHeight, maxWindowHeight), nonUsersHeight);
// Corresponding number of users to actually display
numUsersToDisplay = Math.max(Math.round((windowHeight - nonUsersHeight) / windowLineHeight), 0);
isUsingScrollbars = 0 < numUsersToDisplay && numUsersToDisplay < linesOfUsers.length;
if (isUsingScrollbars) {
firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay));
} else {
firstUserToDisplay = 0;
scrollbarValue = 0.0;
}
} }
function updateOverlayPositions() { function updateOverlayPositions() {
@ -78,6 +122,16 @@ var usersWindow = (function () {
Overlays.editOverlay(windowHeading2D, { Overlays.editOverlay(windowHeading2D, {
y: viewportHeight - windowHeight + WINDOW_MARGIN_2D y: viewportHeight - windowHeight + WINDOW_MARGIN_2D
}); });
scrollbarBackgroundPosition.y = viewportHeight - windowHeight + WINDOW_MARGIN_2D + windowTextHeight;
Overlays.editOverlay(scrollbarBackground2D, {
y: scrollbarBackgroundPosition.y
});
scrollbarBarPosition.y = scrollbarBackgroundPosition.y + 1
+ scrollbarValue * (scrollbarBackgroundHeight - scrollbarBarHeight - 2);
Overlays.editOverlay(scrollbarBar2D, {
y: scrollbarBarPosition.y
});
Overlays.editOverlay(visibilityHeading2D, { Overlays.editOverlay(visibilityHeading2D, {
y: viewportHeight - 4 * windowLineHeight + windowLineSpacing - WINDOW_MARGIN_2D y: viewportHeight - 4 * windowLineHeight + windowLineSpacing - WINDOW_MARGIN_2D
}); });
@ -104,57 +158,59 @@ var usersWindow = (function () {
function updateUsersDisplay() { function updateUsersDisplay() {
var displayText = "", var displayText = "",
myUsername,
user, user,
userText, userText,
textWidth, textWidth,
maxTextWidth, maxTextWidth,
ellipsisWidth,
reducedTextWidth,
i; i;
myUsername = GlobalServices.username; maxTextWidth = WINDOW_WIDTH_2D - (isUsingScrollbars ? SCROLLBAR_BACKGROUND_WIDTH_2D : 0) - 2 * WINDOW_MARGIN_2D;
linesOfUsers = []; ellipsisWidth = Overlays.textSize(windowPane2D, "...").width;
for (i = 0; i < usersOnline.length; i += 1) { reducedTextWidth = maxTextWidth - ellipsisWidth;
user = usersOnline[i];
if (user.username !== myUsername && user.online) {
userText = user.username;
if (user.location.root) {
userText += " @ " + user.location.root.name;
}
textWidth = Overlays.textSize(windowPane2D, userText).width;
maxTextWidth = WINDOW_WIDTH_2D - 2 * WINDOW_MARGIN_2D; for (i = 0; i < numUsersToDisplay; i += 1) {
if (textWidth > maxTextWidth) { user = usersOnline[linesOfUsers[firstUserToDisplay + i]];
// Trim and append "..." to fit window width userText = user.text;
maxTextWidth = maxTextWidth - Overlays.textSize(windowPane2D, "...").width; textWidth = user.textWidth;
while (textWidth > maxTextWidth) {
userText = userText.slice(0, -1); if (textWidth > maxTextWidth) {
textWidth = Overlays.textSize(windowPane2D, userText).width; // Trim and append "..." to fit window width
} maxTextWidth = maxTextWidth - Overlays.textSize(windowPane2D, "...").width;
userText += "..."; while (textWidth > reducedTextWidth) {
userText = userText.slice(0, -1);
textWidth = Overlays.textSize(windowPane2D, userText).width; textWidth = Overlays.textSize(windowPane2D, userText).width;
} }
userText += "...";
usersOnline[i].textWidth = textWidth;
linesOfUsers.push(i);
displayText += "\n" + userText;
} }
displayText += "\n" + userText;
} }
displayText = displayText.slice(1); // Remove leading "\n". displayText = displayText.slice(1); // Remove leading "\n".
calculateWindowHeight();
Overlays.editOverlay(windowPane2D, { Overlays.editOverlay(windowPane2D, {
y: viewportHeight - windowHeight,
height: windowHeight, height: windowHeight,
text: displayText text: displayText
}); });
Overlays.editOverlay(windowHeading2D, { Overlays.editOverlay(windowHeading2D, {
y: viewportHeight - windowHeight + WINDOW_MARGIN_2D,
text: linesOfUsers.length > 0 ? "Users online" : "No users online" text: linesOfUsers.length > 0 ? "Users online" : "No users online"
}); });
scrollbarBackgroundHeight = numUsersToDisplay * windowLineHeight - windowLineSpacing / 2;
Overlays.editOverlay(scrollbarBackground2D, {
height: scrollbarBackgroundHeight,
visible: isUsingScrollbars
});
scrollbarBarHeight = Math.max(numUsersToDisplay / linesOfUsers.length * scrollbarBackgroundHeight,
SCROLLBAR_BAR_MIN_HEIGHT);
Overlays.editOverlay(scrollbarBar2D, {
height: scrollbarBarHeight,
visible: isUsingScrollbars
});
updateOverlayPositions(); updateOverlayPositions();
} }
@ -168,7 +224,11 @@ var usersWindow = (function () {
} }
processUsers = function () { processUsers = function () {
var response; var response,
myUsername,
user,
userText,
i;
if (usersRequest.readyState === usersRequest.DONE) { if (usersRequest.readyState === usersRequest.DONE) {
if (usersRequest.status === 200) { if (usersRequest.status === 200) {
@ -185,7 +245,26 @@ var usersWindow = (function () {
} }
usersOnline = response.data.users; usersOnline = response.data.users;
myUsername = GlobalServices.username;
linesOfUsers = [];
for (i = 0; i < usersOnline.length; i += 1) {
user = usersOnline[i];
if (user.username !== myUsername && user.online) {
userText = user.username;
if (user.location.root) {
userText += " @ " + user.location.root.name;
}
usersOnline[i].text = userText;
usersOnline[i].textWidth = Overlays.textSize(windowPane2D, userText).width;
linesOfUsers.push(i);
}
}
calculateWindowHeight();
updateUsersDisplay(); updateUsersDisplay();
} else { } else {
print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText); print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText);
usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay. usersTimer = Script.setTimeout(pollUsers, HTTP_GET_TIMEOUT); // Try again after a longer delay.
@ -226,6 +305,8 @@ var usersWindow = (function () {
Overlays.editOverlay(windowPane2D, { visible: isVisible }); Overlays.editOverlay(windowPane2D, { visible: isVisible });
Overlays.editOverlay(windowHeading2D, { visible: isVisible }); Overlays.editOverlay(windowHeading2D, { visible: isVisible });
Overlays.editOverlay(scrollbarBackground2D, { visible: isVisible && isUsingScrollbars });
Overlays.editOverlay(scrollbarBar2D, { visible: isVisible && isUsingScrollbars });
Overlays.editOverlay(visibilityHeading2D, { visible: isVisible }); Overlays.editOverlay(visibilityHeading2D, { visible: isVisible });
for (i = 0; i < visibilityControls2D.length; i += 1) { for (i = 0; i < visibilityControls2D.length; i += 1) {
Overlays.editOverlay(visibilityControls2D[i].radioOverlay, { visible: isVisible }); Overlays.editOverlay(visibilityControls2D[i].radioOverlay, { visible: isVisible });
@ -247,8 +328,10 @@ var usersWindow = (function () {
minY, minY,
maxY, maxY,
lineClicked, lineClicked,
userClicked,
i, i,
visibilityChanged; visibilityChanged,
delta;
if (!isVisible) { if (!isVisible) {
return; return;
@ -261,7 +344,7 @@ var usersWindow = (function () {
overlayX = event.x - WINDOW_MARGIN_2D; overlayX = event.x - WINDOW_MARGIN_2D;
overlayY = event.y - viewportHeight + windowHeight - WINDOW_MARGIN_2D - windowLineHeight; overlayY = event.y - viewportHeight + windowHeight - WINDOW_MARGIN_2D - windowLineHeight;
numLinesBefore = Math.floor(overlayY / windowLineHeight); numLinesBefore = Math.round(overlayY / windowLineHeight);
minY = numLinesBefore * windowLineHeight; minY = numLinesBefore * windowLineHeight;
maxY = minY + windowTextHeight; maxY = minY + windowTextHeight;
@ -270,10 +353,12 @@ var usersWindow = (function () {
lineClicked = numLinesBefore; lineClicked = numLinesBefore;
} }
if (0 <= lineClicked && lineClicked < linesOfUsers.length userClicked = firstUserToDisplay + lineClicked;
&& 0 <= overlayX && overlayX <= usersOnline[linesOfUsers[lineClicked]].textWidth) {
//print("Go to " + usersOnline[linesOfUsers[lineClicked]].username); if (0 <= userClicked && userClicked < linesOfUsers.length
location.goToUser(usersOnline[linesOfUsers[lineClicked]].username); && 0 <= overlayX && overlayX <= usersOnline[linesOfUsers[userClicked]].textWidth) {
//print("Go to " + usersOnline[linesOfUsers[userClicked]].username);
location.goToUser(usersOnline[linesOfUsers[userClicked]].username);
} }
} }
@ -292,13 +377,74 @@ var usersWindow = (function () {
} }
updateVisibilityControls(); updateVisibilityControls();
} }
if (clickedOverlay === scrollbarBar2D) {
scrollbarBarClickedAt = (event.y - scrollbarBarPosition.y) / scrollbarBarHeight;
Overlays.editOverlay(scrollbarBar2D, {
backgroundAlpha: SCROLLBAR_BAR_SELECTED_ALPHA_2D
});
isMovingScrollbar = true;
}
if (clickedOverlay === scrollbarBackground2D) {
delta = scrollbarBarHeight / (scrollbarBackgroundHeight - scrollbarBarHeight);
if (event.y < scrollbarBarPosition.y) {
scrollbarValue = Math.max(scrollbarValue - delta, 0.0);
} else {
scrollbarValue = Math.min(scrollbarValue + delta, 1.0);
}
firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay));
updateOverlayPositions();
updateUsersDisplay();
}
}
function onMouseMoveEvent(event) {
if (isMovingScrollbar) {
if (scrollbarBackgroundPosition.x - WINDOW_MARGIN_2D <= event.x
&& event.x <= scrollbarBackgroundPosition.x + SCROLLBAR_BACKGROUND_WIDTH_2D + WINDOW_MARGIN_2D
&& scrollbarBackgroundPosition.y - WINDOW_MARGIN_2D <= event.y
&& event.y <= scrollbarBackgroundPosition.y + scrollbarBackgroundHeight + WINDOW_MARGIN_2D) {
scrollbarValue = (event.y - scrollbarBarClickedAt * scrollbarBarHeight - scrollbarBackgroundPosition.y)
/ (scrollbarBackgroundHeight - scrollbarBarHeight - 2);
scrollbarValue = Math.min(Math.max(scrollbarValue, 0.0), 1.0);
firstUserToDisplay = Math.floor(scrollbarValue * (linesOfUsers.length - numUsersToDisplay));
updateOverlayPositions();
updateUsersDisplay();
} else {
Overlays.editOverlay(scrollbarBar2D, {
backgroundAlpha: SCROLLBAR_BAR_ALPHA_2D
});
isMovingScrollbar = false;
}
}
}
function onMouseReleaseEvent() {
Overlays.editOverlay(scrollbarBar2D, {
backgroundAlpha: SCROLLBAR_BAR_ALPHA_2D
});
isMovingScrollbar = false;
} }
function onScriptUpdate() { function onScriptUpdate() {
var oldViewportHeight = viewportHeight; var oldViewportHeight = viewportHeight,
oldIsMirrorDisplay = isMirrorDisplay,
oldIsFullscreenMirror = isFullscreenMirror,
MIRROR_MENU_ITEM = "Mirror",
FULLSCREEN_MIRROR_MENU_ITEM = "Fullscreen Mirror";
viewportHeight = Controller.getViewportDimensions().y; viewportHeight = Controller.getViewportDimensions().y;
if (viewportHeight !== oldViewportHeight) { isMirrorDisplay = Menu.isOptionChecked(MIRROR_MENU_ITEM);
isFullscreenMirror = Menu.isOptionChecked(FULLSCREEN_MIRROR_MENU_ITEM);
if (viewportHeight !== oldViewportHeight
|| isMirrorDisplay !== oldIsMirrorDisplay
|| isFullscreenMirror !== oldIsFullscreenMirror) {
calculateWindowHeight();
updateUsersDisplay();
updateOverlayPositions(); updateOverlayPositions();
} }
} }
@ -314,10 +460,10 @@ var usersWindow = (function () {
radioButtonDiameter = RADIO_BUTTON_DISPLAY_SCALE * windowTextHeight; radioButtonDiameter = RADIO_BUTTON_DISPLAY_SCALE * windowTextHeight;
Overlays.deleteOverlay(textSizeOverlay); Overlays.deleteOverlay(textSizeOverlay);
calculateWindowHeight();
viewportHeight = Controller.getViewportDimensions().y; viewportHeight = Controller.getViewportDimensions().y;
calculateWindowHeight();
windowPane2D = Overlays.addOverlay("text", { windowPane2D = Overlays.addOverlay("text", {
x: 0, x: 0,
y: viewportHeight, // Start up off-screen y: viewportHeight, // Start up off-screen
@ -349,6 +495,36 @@ var usersWindow = (function () {
visible: isVisible visible: isVisible
}); });
scrollbarBackgroundPosition = {
x: WINDOW_WIDTH_2D - 0.5 * WINDOW_MARGIN_2D - SCROLLBAR_BACKGROUND_WIDTH_2D,
y: viewportHeight
};
scrollbarBackground2D = Overlays.addOverlay("text", {
x: scrollbarBackgroundPosition.x,
y: scrollbarBackgroundPosition.y,
width: SCROLLBAR_BACKGROUND_WIDTH_2D,
height: windowTextHeight,
backgroundColor: SCROLLBAR_BACKGROUND_COLOR_2D,
backgroundAlpha: SCROLLBAR_BACKGROUND_ALPHA_2D,
text: "",
visible: isVisible && isUsingScrollbars
});
scrollbarBarPosition = {
x: WINDOW_WIDTH_2D - 0.5 * WINDOW_MARGIN_2D - SCROLLBAR_BACKGROUND_WIDTH_2D + 1,
y: viewportHeight
};
scrollbarBar2D = Overlays.addOverlay("text", {
x: scrollbarBarPosition.x,
y: scrollbarBarPosition.y,
width: SCROLLBAR_BACKGROUND_WIDTH_2D - 2,
height: windowTextHeight,
backgroundColor: SCROLLBAR_BAR_COLOR_2D,
backgroundAlpha: SCROLLBAR_BAR_ALPHA_2D,
text: "",
visible: isVisible && isUsingScrollbars
});
visibilityHeading2D = Overlays.addOverlay("text", { visibilityHeading2D = Overlays.addOverlay("text", {
x: WINDOW_MARGIN_2D, x: WINDOW_MARGIN_2D,
y: viewportHeight, y: viewportHeight,
@ -390,7 +566,7 @@ var usersWindow = (function () {
textOverlay: Overlays.addOverlay("text", { textOverlay: Overlays.addOverlay("text", {
x: WINDOW_MARGIN_2D, x: WINDOW_MARGIN_2D,
y: viewportHeight, y: viewportHeight,
width: WINDOW_WIDTH_2D - 2 * WINDOW_MARGIN_2D, width: WINDOW_WIDTH_2D - SCROLLBAR_BACKGROUND_WIDTH_2D - 2 * WINDOW_MARGIN_2D,
height: windowTextHeight, height: windowTextHeight,
topMargin: 0, topMargin: 0,
leftMargin: VISIBILITY_RADIO_SPACE, leftMargin: VISIBILITY_RADIO_SPACE,
@ -429,6 +605,8 @@ var usersWindow = (function () {
updateVisibilityControls(); updateVisibilityControls();
Controller.mousePressEvent.connect(onMousePressEvent); Controller.mousePressEvent.connect(onMousePressEvent);
Controller.mouseMoveEvent.connect(onMouseMoveEvent);
Controller.mouseReleaseEvent.connect(onMouseReleaseEvent);
Menu.addMenuItem({ Menu.addMenuItem({
menuName: MENU_NAME, menuName: MENU_NAME,
@ -456,6 +634,8 @@ var usersWindow = (function () {
Script.clearTimeout(usersTimer); Script.clearTimeout(usersTimer);
Overlays.deleteOverlay(windowPane2D); Overlays.deleteOverlay(windowPane2D);
Overlays.deleteOverlay(windowHeading2D); Overlays.deleteOverlay(windowHeading2D);
Overlays.deleteOverlay(scrollbarBackground2D);
Overlays.deleteOverlay(scrollbarBar2D);
Overlays.deleteOverlay(visibilityHeading2D); Overlays.deleteOverlay(visibilityHeading2D);
for (i = 0; i <= visibilityControls2D.length; i += 1) { for (i = 0; i <= visibilityControls2D.length; i += 1) {
Overlays.deleteOverlay(visibilityControls2D[i].textOverlay); Overlays.deleteOverlay(visibilityControls2D[i].textOverlay);

View file

@ -138,7 +138,7 @@
<h3>Import models</h3> <h3>Import models</h3>
<img class="grid-img" src="img/models.png" alt"Import models"></img> <img class="grid-img" src="img/models.png" alt"Import models"></img>
<p> <p>
Use the editEntitles.js script to<br> Use the <strong>edit.js</strong> script to<br>
add FBX models in-world. You<br> add FBX models in-world. You<br>
can use grids and fine tune<br> can use grids and fine tune<br>
placement-related parameters<br> placement-related parameters<br>

View file

@ -297,7 +297,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_lastSendDownstreamAudioStats(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()),
_isVSyncOn(true), _isVSyncOn(true),
_aboutToQuit(false), _aboutToQuit(false),
_notifiedPacketVersionMismatchThisDomain(false) _notifiedPacketVersionMismatchThisDomain(false),
_domainConnectionRefusals(QList<QString>())
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
installNativeEventFilter(&MyNativeEventFilter::getInstance()); installNativeEventFilter(&MyNativeEventFilter::getInstance());
@ -344,6 +345,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// put the NodeList and datagram processing on the node thread // put the NodeList and datagram processing on the node thread
nodeList->moveToThread(nodeThread); nodeList->moveToThread(nodeThread);
// geometry background downloads need to happen on the Datagram Processor Thread. The idle loop will
// emit checkBackgroundDownloads to cause the GeometryCache to check it's queue for requested background
// downloads.
QSharedPointer<GeometryCache> geometryCacheP = DependencyManager::get<GeometryCache>();
ResourceCache *geometryCache = geometryCacheP.data();
connect(this, &Application::checkBackgroundDownloads, geometryCache, &ResourceCache::checkAsynchronousGets);
// connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal
connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams);
@ -789,7 +797,7 @@ void Application::paintGL() {
DependencyManager::get<GlowEffect>()->render(); DependencyManager::get<GlowEffect>()->render();
if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { {
PerformanceTimer perfTimer("renderOverlay"); PerformanceTimer perfTimer("renderOverlay");
_applicationOverlay.renderOverlay(true); _applicationOverlay.renderOverlay(true);
_applicationOverlay.displayOverlayTexture(); _applicationOverlay.displayOverlayTexture();
@ -987,12 +995,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
resetSensors(); resetSensors();
break; break;
case Qt::Key_G:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::ObeyEnvironmentalGravity);
}
break;
case Qt::Key_A: case Qt::Key_A:
if (isShifted) { if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::Atmosphere); Menu::getInstance()->triggerOption(MenuOption::Atmosphere);
@ -1127,13 +1129,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror);
} }
break; break;
case Qt::Key_Slash:
Menu::getInstance()->triggerOption(MenuOption::UserInterface);
break;
case Qt::Key_P: case Qt::Key_P:
Menu::getInstance()->triggerOption(MenuOption::FirstPerson); Menu::getInstance()->triggerOption(MenuOption::FirstPerson);
break; break;
case Qt::Key_Percent: case Qt::Key_Slash:
Menu::getInstance()->triggerOption(MenuOption::Stats); Menu::getInstance()->triggerOption(MenuOption::Stats);
break; break;
case Qt::Key_Plus: case Qt::Key_Plus:
@ -1168,10 +1167,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
break; break;
} }
case Qt::Key_Comma: {
_myAvatar->togglePhysicsEnabled();
}
default: default:
event->ignore(); event->ignore();
break; break;
@ -1569,6 +1564,9 @@ void Application::idle() {
idleTimer->start(2); idleTimer->start(2);
} }
} }
// check for any requested background downloads.
emit checkBackgroundDownloads();
} }
void Application::setFullscreen(bool fullscreen) { void Application::setFullscreen(bool fullscreen) {
@ -1755,7 +1753,7 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
exportTree.addEntity(entityItem->getEntityItemID(), properties); exportTree.addEntity(entityItem->getEntityItemID(), properties);
} }
exportTree.writeToSVOFile(filename.toLocal8Bit().constData()); exportTree.writeToJSONFile(filename.toLocal8Bit().constData());
// restore the main window's active state // restore the main window's active state
_window->activateWindow(); _window->activateWindow();
@ -1909,8 +1907,6 @@ void Application::init() {
_physicsEngine.init(&_entityEditSender); _physicsEngine.init(&_entityEditSender);
_physicsEngine.setAvatarData(_myAvatar);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
connect(&_physicsEngine, &EntitySimulation::entityCollisionWithEntity, connect(&_physicsEngine, &EntitySimulation::entityCollisionWithEntity,
@ -3020,8 +3016,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
_nodeBoundsDisplay.draw(); _nodeBoundsDisplay.draw();
// Render the world box // Render the world box
if (theCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats) && if (theCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
PerformanceTimer perfTimer("worldBox"); PerformanceTimer perfTimer("worldBox");
renderWorldBox(); renderWorldBox();
} }
@ -3141,7 +3136,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
int viewport[4]; int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport); glGetIntegerv(GL_VIEWPORT, viewport);
bool eyeRelativeCamera = false; // bool eyeRelativeCamera = false;
if (billboard) { if (billboard) {
_mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees
_mirrorCamera.setPosition(_myAvatar->getPosition() + _mirrorCamera.setPosition(_myAvatar->getPosition() +
@ -3294,6 +3289,14 @@ void Application::clearDomainOctreeDetails() {
void Application::domainChanged(const QString& domainHostname) { void Application::domainChanged(const QString& domainHostname) {
updateWindowTitle(); updateWindowTitle();
clearDomainOctreeDetails(); clearDomainOctreeDetails();
_domainConnectionRefusals.clear();
}
void Application::domainConnectionDenied(const QString& reason) {
if (!_domainConnectionRefusals.contains(reason)) {
_domainConnectionRefusals.append(reason);
emit domainConnectionRefused(reason);
}
} }
void Application::connectedToDomain(const QString& hostname) { void Application::connectedToDomain(const QString& hostname) {
@ -3600,6 +3603,7 @@ void Application::initializeAcceptedFiles() {
if (_acceptedExtensions.size() == 0) { if (_acceptedExtensions.size() == 0) {
_acceptedExtensions[SNAPSHOT_EXTENSION] = &Application::acceptSnapshot; _acceptedExtensions[SNAPSHOT_EXTENSION] = &Application::acceptSnapshot;
_acceptedExtensions[SVO_EXTENSION] = &Application::importSVOFromURL; _acceptedExtensions[SVO_EXTENSION] = &Application::importSVOFromURL;
_acceptedExtensions[SVO_JSON_EXTENSION] = &Application::importSVOFromURL;
_acceptedExtensions[JS_EXTENSION] = &Application::askToLoadScript; _acceptedExtensions[JS_EXTENSION] = &Application::askToLoadScript;
_acceptedExtensions[FST_EXTENSION] = &Application::askToSetAvatarUrl; _acceptedExtensions[FST_EXTENSION] = &Application::askToSetAvatarUrl;
} }
@ -4208,7 +4212,7 @@ void Application::checkSkeleton() {
_myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL); _myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL);
_myAvatar->sendIdentityPacket(); _myAvatar->sendIdentityPacket();
} else { } else {
_myAvatar->updateLocalAABox(); _myAvatar->updateCharacterController();
_physicsEngine.setAvatarData(_myAvatar); _physicsEngine.setCharacterController(_myAvatar->getCharacterController());
} }
} }

View file

@ -95,6 +95,7 @@ static const float NODE_KILLED_BLUE = 0.0f;
static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString SNAPSHOT_EXTENSION = ".jpg";
static const QString SVO_EXTENSION = ".svo"; static const QString SVO_EXTENSION = ".svo";
static const QString SVO_JSON_EXTENSION = ".svo.json";
static const QString JS_EXTENSION = ".js"; static const QString JS_EXTENSION = ".js";
static const QString FST_EXTENSION = ".fst"; static const QString FST_EXTENSION = ".fst";
@ -332,6 +333,9 @@ signals:
void svoImportRequested(const QString& url); void svoImportRequested(const QString& url);
void checkBackgroundDownloads();
void domainConnectionRefused(const QString& reason);
public slots: public slots:
void domainChanged(const QString& domainHostname); void domainChanged(const QString& domainHostname);
void updateWindowTitle(); void updateWindowTitle();
@ -382,6 +386,8 @@ public slots:
void setActiveFaceTracker(); void setActiveFaceTracker();
void domainConnectionDenied(const QString& reason);
private slots: private slots:
void clearDomainOctreeDetails(); void clearDomainOctreeDetails();
void checkFPS(); void checkFPS();
@ -606,6 +612,8 @@ private:
int _menuBarHeight; int _menuBarHeight;
QHash<QString, AcceptURLMethod> _acceptedExtensions; QHash<QString, AcceptURLMethod> _acceptedExtensions;
QList<QString> _domainConnectionRefusals;
}; };
#endif // hifi_Application_h #endif // hifi_Application_h

View file

@ -127,6 +127,7 @@ void DatagramProcessor::processDatagrams() {
// and check and signal for an access token so that we can make sure they are logged in // and check and signal for an access token so that we can make sure they are logged in
qDebug() << "The domain-server denied a connection request: " << reason; qDebug() << "The domain-server denied a connection request: " << reason;
qDebug() << "You may need to re-log to generate a keypair so you can provide a username signature."; qDebug() << "You may need to re-log to generate a keypair so you can provide a username signature.";
application->domainConnectionDenied(reason);
AccountManager::getInstance().checkAndSignalForAccessToken(); AccountManager::getInstance().checkAndSignalForAccessToken();
break; break;
} }

View file

@ -30,8 +30,9 @@ DiscoverabilityManager::DiscoverabilityManager() :
const QString API_USER_LOCATION_PATH = "/api/v1/user/location"; const QString API_USER_LOCATION_PATH = "/api/v1/user/location";
void DiscoverabilityManager::updateLocation() { void DiscoverabilityManager::updateLocation() {
AccountManager& accountManager = AccountManager::getInstance();
if (_mode.get() != Discoverability::None) { if (_mode.get() != Discoverability::None) {
AccountManager& accountManager = AccountManager::getInstance();
auto addressManager = DependencyManager::get<AddressManager>(); auto addressManager = DependencyManager::get<AddressManager>();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler(); DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
@ -61,17 +62,25 @@ void DiscoverabilityManager::updateLocation() {
uuidStringWithoutCurlyBraces(domainHandler.getUUID())); uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
} }
const QString FRIENDS_ONLY_KEY_IN_LOCATION = "friends_only";
locationObject.insert(FRIENDS_ONLY_KEY_IN_LOCATION, (_mode.get() == Discoverability::Friends));
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject); rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
accountManager.authenticatedRequest(API_USER_LOCATION_PATH, QNetworkAccessManager::PutOperation, accountManager.sendRequest(API_USER_LOCATION_PATH, AccountManagerAuth::Required,
JSONCallbackParameters(), QJsonDocument(rootObject).toJson()); QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), QJsonDocument(rootObject).toJson());
} }
} else {
// we still send a heartbeat to the metaverse server for stats collection
const QString API_USER_HEARTBEAT_PATH = "/api/v1/user/heartbeat";
accountManager.sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Required, QNetworkAccessManager::PutOperation);
} }
} }
void DiscoverabilityManager::removeLocation() { void DiscoverabilityManager::removeLocation() {
AccountManager& accountManager = AccountManager::getInstance(); AccountManager& accountManager = AccountManager::getInstance();
accountManager.authenticatedRequest(API_USER_LOCATION_PATH, QNetworkAccessManager::DeleteOperation); accountManager.sendRequest(API_USER_LOCATION_PATH, AccountManagerAuth::Required, QNetworkAccessManager::DeleteOperation);
} }
void DiscoverabilityManager::setDiscoverabilityMode(Discoverability::Mode discoverabilityMode) { void DiscoverabilityManager::setDiscoverabilityMode(Discoverability::Mode discoverabilityMode) {

View file

@ -40,26 +40,55 @@ float LODManager::getLODIncreaseFPS() {
void LODManager::autoAdjustLOD(float currentFPS) { void LODManager::autoAdjustLOD(float currentFPS) {
// NOTE: our first ~100 samples at app startup are completely all over the place, and we don't // NOTE: our first ~100 samples at app startup are completely all over the place, and we don't
// really want to count them in our average, so we will ignore the real frame rates and stuff // really want to count them in our average, so we will ignore the real frame rates and stuff
// our moving average with simulated good data // our moving average with simulated good data
const int IGNORE_THESE_SAMPLES = 100; const int IGNORE_THESE_SAMPLES = 100;
const float ASSUMED_FPS = 60.0f; if (_fpsAverageUpWindow.getSampleCount() < IGNORE_THESE_SAMPLES) {
if (_fpsAverage.getSampleCount() < IGNORE_THESE_SAMPLES) {
currentFPS = ASSUMED_FPS; currentFPS = ASSUMED_FPS;
_lastStable = _lastUpShift = _lastDownShift = usecTimestampNow();
} }
_fpsAverage.updateAverage(currentFPS);
_fastFPSAverage.updateAverage(currentFPS); _fpsAverageStartWindow.updateAverage(currentFPS);
_fpsAverageDownWindow.updateAverage(currentFPS);
_fpsAverageUpWindow.updateAverage(currentFPS);
quint64 now = usecTimestampNow(); quint64 now = usecTimestampNow();
bool changed = false; bool changed = false;
bool octreeChanged = false; quint64 elapsedSinceDownShift = now - _lastDownShift;
quint64 elapsed = now - _lastAdjust; quint64 elapsedSinceUpShift = now - _lastUpShift;
quint64 lastStableOrUpshift = glm::max(_lastUpShift, _lastStable);
quint64 elapsedSinceStableOrUpShift = now - lastStableOrUpshift;
if (_automaticLODAdjust) { if (_automaticLODAdjust) {
// LOD Downward adjustment
if (elapsed > ADJUST_LOD_DOWN_DELAY && _fpsAverage.getAverage() < getLODDecreaseFPS()) { // LOD Downward adjustment
// If we've been downshifting, we watch a shorter downshift window so that we will quickly move toward our
// target frame rate. But if we haven't just done a downshift (either because our last shift was an upshift,
// or because we've just started out) then we look at a much longer window to consider whether or not to start
// downshifting.
bool doDownShift = false;
if (_isDownshifting) {
// only consider things if our DOWN_SHIFT time has elapsed...
if (elapsedSinceDownShift > DOWN_SHIFT_ELPASED) {
doDownShift = _fpsAverageDownWindow.getAverage() < getLODDecreaseFPS();
if (!doDownShift) {
qDebug() << "---- WE APPEAR TO BE DONE DOWN SHIFTING -----";
_isDownshifting = false;
_lastStable = now;
}
}
} else {
doDownShift = (elapsedSinceStableOrUpShift > START_SHIFT_ELPASED
&& _fpsAverageStartWindow.getAverage() < getLODDecreaseFPS());
}
if (doDownShift) {
// Octree items... stepwise adjustment // Octree items... stepwise adjustment
if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) { if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) {
@ -67,40 +96,66 @@ void LODManager::autoAdjustLOD(float currentFPS) {
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
} }
octreeChanged = changed = true; changed = true;
} }
if (changed) { if (changed) {
_lastAdjust = now; if (_isDownshifting) {
qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() // subsequent downshift
<< "_octreeSizeScale=" << _octreeSizeScale; qDebug() << "adjusting LOD DOWN..."
<< "average fps for last "<< DOWN_SHIFT_WINDOW_IN_SECS <<"seconds was "
<< _fpsAverageDownWindow.getAverage()
<< "minimum is:" << getLODDecreaseFPS()
<< "elapsedSinceDownShift:" << elapsedSinceDownShift
<< " NEW _octreeSizeScale=" << _octreeSizeScale;
} else {
// first downshift
qDebug() << "adjusting LOD DOWN after initial delay..."
<< "average fps for last "<< START_DELAY_WINDOW_IN_SECS <<"seconds was "
<< _fpsAverageStartWindow.getAverage()
<< "minimum is:" << getLODDecreaseFPS()
<< "elapsedSinceUpShift:" << elapsedSinceUpShift
<< " NEW _octreeSizeScale=" << _octreeSizeScale;
}
_lastDownShift = now;
_isDownshifting = true;
emit LODDecreased(); emit LODDecreased();
} }
} } else {
// LOD Upward adjustment // LOD Upward adjustment
if (elapsed > ADJUST_LOD_UP_DELAY && _fpsAverage.getAverage() > getLODIncreaseFPS()) { if (elapsedSinceUpShift > UP_SHIFT_ELPASED) {
if (_fpsAverageUpWindow.getAverage() > getLODIncreaseFPS()) {
// Octee items... stepwise adjustment // Octee items... stepwise adjustment
if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) { if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) { if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE; _octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
} else { } else {
_octreeSizeScale *= ADJUST_LOD_UP_BY; _octreeSizeScale *= ADJUST_LOD_UP_BY;
}
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
}
changed = true;
}
} }
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
}
octreeChanged = changed = true;
}
if (changed) { if (changed) {
_lastAdjust = now; qDebug() << "adjusting LOD UP... average fps for last "<< UP_SHIFT_WINDOW_IN_SECS <<"seconds was "
qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage() << _fpsAverageUpWindow.getAverage()
<< "_octreeSizeScale=" << _octreeSizeScale; << "upshift point is:" << getLODIncreaseFPS()
<< "elapsedSinceUpShift:" << elapsedSinceUpShift
<< " NEW _octreeSizeScale=" << _octreeSizeScale;
emit LODIncreased(); _lastUpShift = now;
_isDownshifting = false;
emit LODIncreased();
}
} }
} }
@ -116,9 +171,11 @@ void LODManager::autoAdjustLOD(float currentFPS) {
} }
void LODManager::resetLODAdjust() { void LODManager::resetLODAdjust() {
_fpsAverage.reset(); _fpsAverageStartWindow.reset();
_fastFPSAverage.reset(); _fpsAverageDownWindow.reset();
_lastAdjust = usecTimestampNow(); _fpsAverageUpWindow.reset();
_lastUpShift = _lastDownShift = usecTimestampNow();
_isDownshifting = false;
} }
QString LODManager::getLODFeedbackText() { QString LODManager::getLODFeedbackText() {

View file

@ -19,10 +19,22 @@
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0; const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0;
const float DEFAULT_HMD_LOD_DOWN_FPS = 60.0; const float DEFAULT_HMD_LOD_DOWN_FPS = 60.0;
const float INCREASE_LOD_GAP = 5.0f; const float MAX_LIKELY_DESKTOP_FPS = 59.0; // this is essentially, V-synch - 1 fps
const float MAX_LIKELY_HMD_FPS = 74.0; // this is essentially, V-synch - 1 fps
const float INCREASE_LOD_GAP = 15.0f;
const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 0.5; // Consider adjusting LOD down after half a second const float START_DELAY_WINDOW_IN_SECS = 3.0f; // wait at least this long after steady state/last upshift to consider downshifts
const quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2; const float DOWN_SHIFT_WINDOW_IN_SECS = 0.5f;
const float UP_SHIFT_WINDOW_IN_SECS = 2.5f;
const int ASSUMED_FPS = 60;
const quint64 START_SHIFT_ELPASED = USECS_PER_SECOND * START_DELAY_WINDOW_IN_SECS;
const quint64 DOWN_SHIFT_ELPASED = USECS_PER_SECOND * DOWN_SHIFT_WINDOW_IN_SECS; // Consider adjusting LOD down after half a second
const quint64 UP_SHIFT_ELPASED = USECS_PER_SECOND * UP_SHIFT_WINDOW_IN_SECS;
const int START_DELAY_SAMPLES_OF_FRAMES = ASSUMED_FPS * START_DELAY_WINDOW_IN_SECS;
const int DOWN_SHIFT_SAMPLES_OF_FRAMES = ASSUMED_FPS * DOWN_SHIFT_WINDOW_IN_SECS;
const int UP_SHIFT_SAMPLES_OF_FRAMES = ASSUMED_FPS * UP_SHIFT_WINDOW_IN_SECS;
const float ADJUST_LOD_DOWN_BY = 0.9f; const float ADJUST_LOD_DOWN_BY = 0.9f;
const float ADJUST_LOD_UP_BY = 1.1f; const float ADJUST_LOD_UP_BY = 1.1f;
@ -37,9 +49,6 @@ const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
// do. But both are still culled using the same angular size logic. // do. But both are still culled using the same angular size logic.
const float AVATAR_TO_ENTITY_RATIO = 2.0f; const float AVATAR_TO_ENTITY_RATIO = 2.0f;
const int ONE_SECOND_OF_FRAMES = 60;
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
class LODManager : public QObject, public Dependency { class LODManager : public QObject, public Dependency {
Q_OBJECT Q_OBJECT
@ -51,11 +60,11 @@ public:
Q_INVOKABLE void setDesktopLODDecreaseFPS(float value) { _desktopLODDecreaseFPS = value; } Q_INVOKABLE void setDesktopLODDecreaseFPS(float value) { _desktopLODDecreaseFPS = value; }
Q_INVOKABLE float getDesktopLODDecreaseFPS() const { return _desktopLODDecreaseFPS; } Q_INVOKABLE float getDesktopLODDecreaseFPS() const { return _desktopLODDecreaseFPS; }
Q_INVOKABLE float getDesktopLODIncreaseFPS() const { return _desktopLODDecreaseFPS + INCREASE_LOD_GAP; } Q_INVOKABLE float getDesktopLODIncreaseFPS() const { return glm::min(_desktopLODDecreaseFPS + INCREASE_LOD_GAP, MAX_LIKELY_DESKTOP_FPS); }
Q_INVOKABLE void setHMDLODDecreaseFPS(float value) { _hmdLODDecreaseFPS = value; } Q_INVOKABLE void setHMDLODDecreaseFPS(float value) { _hmdLODDecreaseFPS = value; }
Q_INVOKABLE float getHMDLODDecreaseFPS() const { return _hmdLODDecreaseFPS; } Q_INVOKABLE float getHMDLODDecreaseFPS() const { return _hmdLODDecreaseFPS; }
Q_INVOKABLE float getHMDLODIncreaseFPS() const { return _hmdLODDecreaseFPS + INCREASE_LOD_GAP; } Q_INVOKABLE float getHMDLODIncreaseFPS() const { return glm::min(_hmdLODDecreaseFPS + INCREASE_LOD_GAP, MAX_LIKELY_HMD_FPS); }
Q_INVOKABLE float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } Q_INVOKABLE float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; }
@ -67,10 +76,6 @@ public:
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust); Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
Q_INVOKABLE void resetLODAdjust();
Q_INVOKABLE float getFPSAverage() const { return _fpsAverage.getAverage(); }
Q_INVOKABLE float getFastFPSAverage() const { return _fastFPSAverage.getAverage(); }
Q_INVOKABLE float getLODDecreaseFPS(); Q_INVOKABLE float getLODDecreaseFPS();
Q_INVOKABLE float getLODIncreaseFPS(); Q_INVOKABLE float getLODIncreaseFPS();
@ -79,6 +84,7 @@ public:
void loadSettings(); void loadSettings();
void saveSettings(); void saveSettings();
void resetLODAdjust();
signals: signals:
void LODIncreased(); void LODIncreased();
@ -96,9 +102,14 @@ private:
float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE; float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE;
int _boundaryLevelAdjust = 0; int _boundaryLevelAdjust = 0;
quint64 _lastAdjust = 0; quint64 _lastDownShift = 0;
SimpleMovingAverage _fpsAverage = FIVE_SECONDS_OF_FRAMES; quint64 _lastUpShift = 0;
SimpleMovingAverage _fastFPSAverage = ONE_SECOND_OF_FRAMES; quint64 _lastStable = 0;
bool _isDownshifting = false; // start out as if we're not downshifting
SimpleMovingAverage _fpsAverageStartWindow = START_DELAY_SAMPLES_OF_FRAMES;
SimpleMovingAverage _fpsAverageDownWindow = DOWN_SHIFT_SAMPLES_OF_FRAMES;
SimpleMovingAverage _fpsAverageUpWindow = UP_SHIFT_SAMPLES_OF_FRAMES;
bool _shouldRenderTableNeedsRebuilding = true; bool _shouldRenderTableNeedsRebuilding = true;
QMap<float, float> _shouldRenderTable; QMap<float, float> _shouldRenderTable;

View file

@ -201,9 +201,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false, addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true,
avatar, SLOT(updateMotionBehavior()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true,
avatar, SLOT(updateMotionBehavior())); avatar, SLOT(updateMotionBehavior()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false,
avatar, SLOT(updateMotionBehavior())); avatar, SLOT(updateMotionBehavior()));
@ -230,7 +228,6 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
qApp, SLOT(cameraMenuChanged())); qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::UserInterface, Qt::Key_Slash, true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::META | Qt::Key_H, addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::META | Qt::Key_H,
false, false,
@ -259,14 +256,13 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
addDisabledActionAndSeparator(viewMenu, "Stats"); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Percent);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, qApp, SLOT(toggleLogDialog())); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, qApp, SLOT(toggleLogDialog()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
dialogsManager.data(), SLOT(bandwidthDetails())); dialogsManager.data(), SLOT(bandwidthDetails()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0,
dialogsManager.data(), SLOT(octreeStatsDetails())); dialogsManager.data(), SLOT(octreeStatsDetails()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp()));
QMenu* developerMenu = addMenu("Developer"); QMenu* developerMenu = addMenu("Developer");
@ -538,10 +534,12 @@ Menu::Menu() {
statsRenderer.data(), statsRenderer.data(),
SLOT(toggleShowInjectedStreams())); SLOT(toggleShowInjectedStreams()));
#ifndef Q_OS_MAC
QMenu* helpMenu = addMenu("Help"); QMenu* helpMenu = addMenu("Help");
QAction* helpAction = helpMenu->addAction(MenuOption::AboutApp); addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp()));
connect(helpAction, SIGNAL(triggered()), qApp, SLOT(aboutApp()));
#ifndef Q_OS_MAC
QAction* aboutAction = helpMenu->addAction(MenuOption::AboutApp);
connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutApp()));
#endif #endif
} }

View file

@ -153,6 +153,7 @@ namespace MenuOption {
const QString EchoServerAudio = "Echo Server Audio"; const QString EchoServerAudio = "Echo Server Audio";
const QString EditEntitiesHelp = "Edit Entities Help..."; const QString EditEntitiesHelp = "Edit Entities Help...";
const QString Enable3DTVMode = "Enable 3DTV Mode"; const QString Enable3DTVMode = "Enable 3DTV Mode";
const QString EnableCharacterController = "Enable avatar collisions";
const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)"; const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)";
const QString EnableVRMode = "Enable VR Mode"; const QString EnableVRMode = "Enable VR Mode";
const QString Entities = "Entities"; const QString Entities = "Entities";
@ -184,12 +185,9 @@ namespace MenuOption {
const QString Mirror = "Mirror"; const QString Mirror = "Mirror";
const QString MuteAudio = "Mute Microphone"; const QString MuteAudio = "Mute Microphone";
const QString MuteEnvironment = "Mute Environment"; const QString MuteEnvironment = "Mute Environment";
const QString NewVoxelCullingMode = "New Voxel Culling Mode";
const QString NoFaceTracking = "None"; const QString NoFaceTracking = "None";
const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity"; const QString OctreeStats = "Entity Statistics";
const QString OctreeStats = "Voxel and Entity Statistics";
const QString OffAxisProjection = "Off-Axis Projection"; const QString OffAxisProjection = "Off-Axis Projection";
const QString OldVoxelCullingMode = "Old Voxel Culling Mode";
const QString OnlyDisplayTopTen = "Only Display Top Ten"; const QString OnlyDisplayTopTen = "Only Display Top Ten";
const QString Pair = "Pair"; const QString Pair = "Pair";
const QString PipelineWarnings = "Log Render Pipeline Warnings"; const QString PipelineWarnings = "Log Render Pipeline Warnings";
@ -233,13 +231,11 @@ namespace MenuOption {
const QString ScriptEditor = "Script Editor..."; const QString ScriptEditor = "Script Editor...";
const QString ScriptedMotorControl = "Enable Scripted Motor Control"; const QString ScriptedMotorControl = "Enable Scripted Motor Control";
const QString ShowBordersEntityNodes = "Show Entity Nodes"; const QString ShowBordersEntityNodes = "Show Entity Nodes";
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
const QString ShowIKConstraints = "Show IK Constraints"; const QString ShowIKConstraints = "Show IK Constraints";
const QString SimpleShadows = "Simple"; const QString SimpleShadows = "Simple";
const QString SixenseEnabled = "Enable Hydra Support"; const QString SixenseEnabled = "Enable Hydra Support";
const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString SixenseMouseInput = "Enable Sixense Mouse Input";
const QString SixenseLasers = "Enable Sixense UI Lasers"; const QString SixenseLasers = "Enable Sixense UI Lasers";
const QString StandOnNearbyFloors = "Stand on nearby floors";
const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations"; const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations";
const QString Stars = "Stars"; const QString Stars = "Stars";
const QString Stats = "Stats"; const QString Stats = "Stats";
@ -251,7 +247,6 @@ namespace MenuOption {
const QString TransmitterDrive = "Transmitter Drive"; const QString TransmitterDrive = "Transmitter Drive";
const QString TurnWithHead = "Turn using Head"; const QString TurnWithHead = "Turn using Head";
const QString PackageModel = "Package Model"; const QString PackageModel = "Package Model";
const QString UserInterface = "User Interface";
const QString Visage = "Visage"; const QString Visage = "Visage";
const QString Wireframe = "Wireframe"; const QString Wireframe = "Wireframe";
} }

View file

@ -14,6 +14,8 @@
#include <QMessageBox> #include <QMessageBox>
#include <QTemporaryDir> #include <QTemporaryDir>
#include <FSTReader.h>
#include "ModelSelector.h" #include "ModelSelector.h"
#include "ModelPropertiesDialog.h" #include "ModelPropertiesDialog.h"

View file

@ -15,8 +15,6 @@
#include <QFileInfo> #include <QFileInfo>
#include <QVariantHash> #include <QVariantHash>
#include <FBXReader.h>
#include "ui/ModelsBrowser.h" #include "ui/ModelsBrowser.h"
class ModelPackager : public QObject { class ModelPackager : public QObject {

View file

@ -19,6 +19,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <FSTReader.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include "ModelPropertiesDialog.h" #include "ModelPropertiesDialog.h"

View file

@ -23,19 +23,6 @@ class QComboBox;
class QCheckBox; class QCheckBox;
class QVBoxLayout; class QVBoxLayout;
static const QString NAME_FIELD = "name";
static const QString FILENAME_FIELD = "filename";
static const QString TEXDIR_FIELD = "texdir";
static const QString LOD_FIELD = "lod";
static const QString JOINT_INDEX_FIELD = "jointIndex";
static const QString SCALE_FIELD = "scale";
static const QString TRANSLATION_X_FIELD = "tx";
static const QString TRANSLATION_Y_FIELD = "ty";
static const QString TRANSLATION_Z_FIELD = "tz";
static const QString JOINT_FIELD = "joint";
static const QString FREE_JOINT_FIELD = "freeJoint";
static const QString BLENDSHAPE_FIELD = "bs";
/// A dialog that allows customization of various model properties. /// A dialog that allows customization of various model properties.
class ModelPropertiesDialog : public QDialog { class ModelPropertiesDialog : public QDialog {
Q_OBJECT Q_OBJECT

View file

@ -70,7 +70,6 @@ MyAvatar::MyAvatar() :
Avatar(), Avatar(),
_turningKeyPressTime(0.0f), _turningKeyPressTime(0.0f),
_gravity(0.0f, 0.0f, 0.0f), _gravity(0.0f, 0.0f, 0.0f),
_shouldJump(false),
_wasPushing(false), _wasPushing(false),
_isPushing(false), _isPushing(false),
_isBraking(false), _isBraking(false),
@ -82,6 +81,7 @@ MyAvatar::MyAvatar() :
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
_motionBehaviors(AVATAR_MOTION_DEFAULTS), _motionBehaviors(AVATAR_MOTION_DEFAULTS),
_characterController(this),
_lookAtTargetAvatar(), _lookAtTargetAvatar(),
_shouldRender(true), _shouldRender(true),
_billboardValid(false), _billboardValid(false),
@ -100,6 +100,7 @@ MyAvatar::MyAvatar() :
// connect to AddressManager signal for location jumps // connect to AddressManager signal for location jumps
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired, connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
this, &MyAvatar::goToLocation); this, &MyAvatar::goToLocation);
_characterController.setEnabled(true);
} }
MyAvatar::~MyAvatar() { MyAvatar::~MyAvatar() {
@ -146,10 +147,6 @@ void MyAvatar::update(float deltaTime) {
head->setAudioLoudness(audio->getLastInputLoudness()); head->setAudioLoudness(audio->getLastInputLoudness());
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) {
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
}
simulate(deltaTime); simulate(deltaTime);
if (_feetTouchFloor) { if (_feetTouchFloor) {
_skeletonModel.updateStandingFoot(); _skeletonModel.updateStandingFoot();
@ -173,11 +170,7 @@ void MyAvatar::simulate(float deltaTime) {
{ {
PerformanceTimer perfTimer("transform"); PerformanceTimer perfTimer("transform");
updateOrientation(deltaTime); updateOrientation(deltaTime);
if (isPhysicsEnabled()) { updatePosition(deltaTime);
updatePositionWithPhysics(deltaTime);
} else {
updatePosition(deltaTime);
}
} }
{ {
@ -483,26 +476,6 @@ void MyAvatar::loadLastRecording() {
_player->loadRecording(_recorder->getRecording()); _player->loadRecording(_recorder->getRecording());
} }
void MyAvatar::setLocalGravity(glm::vec3 gravity) {
_motionBehaviors |= AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
// Environmental and Local gravities are incompatible. Since Local is being set here
// the environmental setting must be removed.
_motionBehaviors &= ~AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
setGravity(gravity);
}
void MyAvatar::setGravity(const glm::vec3& gravity) {
_gravity = gravity;
// use the gravity to determine the new world up direction, if possible
float gravityLength = glm::length(gravity);
if (gravityLength > EPSILON) {
_worldUpDirection = _gravity / -gravityLength;
}
// NOTE: the else case here it to leave _worldUpDirection unchanged
// so it continues to point opposite to the previous gravity setting.
}
AnimationHandlePointer MyAvatar::addAnimationHandle() { AnimationHandlePointer MyAvatar::addAnimationHandle() {
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
_animationHandles.append(handle); _animationHandles.append(handle);
@ -954,15 +927,15 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
return Avatar::getPosition(); return Avatar::getPosition();
} }
void MyAvatar::updateLocalAABox() { void MyAvatar::updateCharacterController() {
// compute localAABox
const CapsuleShape& capsule = _skeletonModel.getBoundingShape(); const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
float radius = capsule.getRadius(); float radius = capsule.getRadius();
float height = 2.0f * (capsule.getHalfHeight() + radius); float height = 2.0f * (capsule.getHalfHeight() + radius);
glm::vec3 offset = _skeletonModel.getBoundingShapeOffset();
glm::vec3 corner(-radius, -0.5f * height, -radius); glm::vec3 corner(-radius, -0.5f * height, -radius);
corner += offset; corner += _skeletonModel.getBoundingShapeOffset();
glm::vec3 scale(2.0f * radius, height, 2.0f * radius); glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
_localAABox.setBox(corner, scale); _characterController.setLocalBoundingBox(corner, scale);
} }
QString MyAvatar::getScriptedMotorFrame() const { QString MyAvatar::getScriptedMotorFrame() const {
@ -1257,128 +1230,38 @@ glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVe
return localVelocity + motorEfficiency * deltaVelocity; return localVelocity + motorEfficiency * deltaVelocity;
} }
const float NEARBY_FLOOR_THRESHOLD = 5.0f;
void MyAvatar::updatePosition(float deltaTime) { void MyAvatar::updatePosition(float deltaTime) {
// check for floor by casting a ray straight down from avatar's position
float heightAboveFloor = FLT_MAX;
bool hasFloor = false;
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
const float maxFloorDistance = boundingShape.getBoundingRadius() * NEARBY_FLOOR_THRESHOLD;
RayIntersectionInfo intersection;
// NOTE: avatar is center of PhysicsSimulation, so rayStart is the origin for the purposes of the raycast
intersection._rayStart = glm::vec3(0.0f);
intersection._rayDirection = - _worldUpDirection;
intersection._rayLength = 4.0f * boundingShape.getBoundingRadius();
// velocity is initialized to the measured _velocity but will be modified by friction, external thrust, etc
glm::vec3 velocity = _velocity;
bool pushingUp = (_driveKeys[UP] - _driveKeys[DOWN] > 0.0f) || _scriptedMotorVelocity.y > 0.0f;
if (_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) {
const float MAX_SPEED_UNDER_GRAVITY = 2.0f * _scale * MAX_WALKING_SPEED;
if (pushingUp || glm::length2(velocity) > MAX_SPEED_UNDER_GRAVITY * MAX_SPEED_UNDER_GRAVITY) {
// we're pushing up or moving quickly, so disable gravity
setLocalGravity(glm::vec3(0.0f));
hasFloor = false;
} else {
if (heightAboveFloor > maxFloorDistance) {
// disable local gravity when floor is too far away
setLocalGravity(glm::vec3(0.0f));
hasFloor = false;
} else {
// enable gravity
setLocalGravity(-_worldUpDirection);
}
}
}
bool zeroDownwardVelocity = false;
bool gravityEnabled = (glm::length2(_gravity) > EPSILON);
if (gravityEnabled) {
const float SLOP = 0.002f;
if (heightAboveFloor < SLOP) {
if (heightAboveFloor < 0.0) {
// Gravity is in effect so we assume that the avatar is colliding against the world and we need
// to lift avatar out of floor, but we don't want to do it too fast (keep it smooth).
float distanceToLift = glm::min(-heightAboveFloor, MAX_WALKING_SPEED * deltaTime);
// We don't use applyPositionDelta() for this lift distance because we don't want the avatar
// to come flying out of the floor. Instead we update position directly, and set a boolean
// that will remind us later to zero any downward component of the velocity.
_position += distanceToLift * _worldUpDirection;
}
zeroDownwardVelocity = true;
}
if (!zeroDownwardVelocity) {
velocity += (deltaTime * GRAVITY_EARTH) * _gravity;
}
}
// rotate velocity into camera frame
glm::quat rotation = getHead()->getCameraOrientation();
glm::vec3 localVelocity = glm::inverse(rotation) * velocity;
// apply motors in camera frame
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor);
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
// rotate back into world-frame
velocity = rotation * newLocalVelocity;
// apply thrust
velocity += _thrust * deltaTime;
_thrust = glm::vec3(0.0f);
// remove downward velocity so we don't push into floor
if (zeroDownwardVelocity) {
float verticalSpeed = glm::dot(velocity, _worldUpDirection);
if (verticalSpeed < 0.0f || !pushingUp) {
velocity -= verticalSpeed * _worldUpDirection;
}
}
// cap avatar speed
float speed = glm::length(velocity);
if (speed > MAX_AVATAR_SPEED) {
velocity *= MAX_AVATAR_SPEED / speed;
speed = MAX_AVATAR_SPEED;
}
// update position
if (speed > MIN_AVATAR_SPEED) {
applyPositionDelta(deltaTime * velocity);
}
// update _moving flag based on speed
const float MOVING_SPEED_THRESHOLD = 0.01f;
_moving = speed > MOVING_SPEED_THRESHOLD;
measureMotionDerivatives(deltaTime);
}
void MyAvatar::updatePositionWithPhysics(float deltaTime) {
// rotate velocity into camera frame // rotate velocity into camera frame
glm::quat rotation = getHead()->getCameraOrientation(); glm::quat rotation = getHead()->getCameraOrientation();
glm::vec3 localVelocity = glm::inverse(rotation) * _velocity; glm::vec3 localVelocity = glm::inverse(rotation) * _velocity;
bool hasFloor = false; bool isOnGround = _characterController.onGround();
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor); glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isOnGround);
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity); newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
// cap avatar speed
float speed = glm::length(newLocalVelocity);
if (speed > MAX_WALKING_SPEED) {
newLocalVelocity *= MAX_WALKING_SPEED / speed;
}
// rotate back into world-frame // rotate back into world-frame
_velocity = rotation * newLocalVelocity; _velocity = rotation * newLocalVelocity;
_velocity += _thrust * deltaTime; _velocity += _thrust * deltaTime;
_thrust = glm::vec3(0.0f); _thrust = glm::vec3(0.0f);
// cap avatar speed
float speed = glm::length(_velocity);
if (speed > MAX_AVATAR_SPEED) {
_velocity *= MAX_AVATAR_SPEED / speed;
speed = MAX_AVATAR_SPEED;
}
if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) {
// update position ourselves
applyPositionDelta(deltaTime * _velocity);
measureMotionDerivatives(deltaTime);
} // else physics will move avatar later
// update _moving flag based on speed
const float MOVING_SPEED_THRESHOLD = 0.01f;
_moving = speed > MOVING_SPEED_THRESHOLD;
} }
void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) { void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) {
@ -1495,23 +1378,6 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
void MyAvatar::updateMotionBehavior() { void MyAvatar::updateMotionBehavior() {
Menu* menu = Menu::getInstance(); Menu* menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) {
_motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
// Environmental and Local gravities are incompatible. Environmental setting trumps local.
_motionBehaviors &= ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY;
} else {
_motionBehaviors &= ~AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY;
}
if (! (_motionBehaviors & (AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_LOCAL_GRAVITY))) {
setGravity(glm::vec3(0.0f));
}
if (menu->isOptionChecked(MenuOption::StandOnNearbyFloors)) {
_motionBehaviors |= AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
// standing on floors requires collision with voxels
// TODO: determine what to do with this now that voxels are gone
} else {
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
}
if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) { if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) {
_motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED; _motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED;
} else { } else {
@ -1522,6 +1388,7 @@ void MyAvatar::updateMotionBehavior() {
} else { } else {
_motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; _motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
} }
_characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));
_feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations); _feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations);
} }

View file

@ -13,6 +13,7 @@
#define hifi_MyAvatar_h #define hifi_MyAvatar_h
#include <SettingHandle.h> #include <SettingHandle.h>
#include <CharacterController.h>
#include "Avatar.h" #include "Avatar.h"
@ -24,7 +25,7 @@ class MyAvatar : public Avatar {
Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity)
Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale)
Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame) Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame)
Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setLocalGravity) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
public: public:
MyAvatar(); MyAvatar();
@ -43,13 +44,11 @@ public:
// setters // setters
void setLeanScale(float scale) { _leanScale = scale; } void setLeanScale(float scale) { _leanScale = scale; }
void setLocalGravity(glm::vec3 gravity);
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; }
void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); }
// getters // getters
float getLeanScale() const { return _leanScale; } float getLeanScale() const { return _leanScale; }
glm::vec3 getGravity() const { return _gravity; }
Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; Q_INVOKABLE glm::vec3 getDefaultEyePosition() const;
bool getShouldRenderLocally() const { return _shouldRender; } bool getShouldRenderLocally() const { return _shouldRender; }
float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); }
@ -88,7 +87,7 @@ public:
void clearDriveKeys(); void clearDriveKeys();
void setDriveKeys(int key, float val) { _driveKeys[key] = val; }; void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; }; bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
void jump() { _shouldJump = true; }; void jump() { _characterController.jump(); }
bool isMyAvatar() { return true; } bool isMyAvatar() { return true; }
@ -122,6 +121,8 @@ public:
virtual glm::vec3 getSkeletonPosition() const; virtual glm::vec3 getSkeletonPosition() const;
void updateLocalAABox(); void updateLocalAABox();
CharacterController* getCharacterController() { return &_characterController; }
void updateCharacterController();
void clearJointAnimationPriorities(); void clearJointAnimationPriorities();
@ -186,7 +187,6 @@ private:
float _turningKeyPressTime; float _turningKeyPressTime;
glm::vec3 _gravity; glm::vec3 _gravity;
bool _shouldJump;
float _driveKeys[MAX_DRIVE_KEYS]; float _driveKeys[MAX_DRIVE_KEYS];
bool _wasPushing; bool _wasPushing;
bool _isPushing; bool _isPushing;
@ -202,6 +202,8 @@ private:
int _scriptedMotorFrame; int _scriptedMotorFrame;
quint32 _motionBehaviors; quint32 _motionBehaviors;
CharacterController _characterController;
QWeakPointer<AvatarData> _lookAtTargetAvatar; QWeakPointer<AvatarData> _lookAtTargetAvatar;
glm::vec3 _targetAvatarPosition; glm::vec3 _targetAvatarPosition;
bool _shouldRender; bool _shouldRender;
@ -224,10 +226,8 @@ private:
glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool walkingOnFloor); glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool walkingOnFloor);
glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity); glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity);
void updatePosition(float deltaTime); void updatePosition(float deltaTime);
void updatePositionWithPhysics(float deltaTime);
void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency);
void maybeUpdateBillboard(); void maybeUpdateBillboard();
void setGravity(const glm::vec3& gravity);
}; };
#endif // hifi_MyAvatar_h #endif // hifi_MyAvatar_h

View file

@ -223,7 +223,7 @@ void SixenseManager::update(float deltaTime) {
palm->setJoystick(data->joystick_x, data->joystick_y); palm->setJoystick(data->joystick_x, data->joystick_y);
// Emulate the mouse so we can use scripts // Emulate the mouse so we can use scripts
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) { if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput) && !_controllersAtBase) {
emulateMouse(palm, numActiveControllers - 1); emulateMouse(palm, numActiveControllers - 1);
} }

View file

@ -104,7 +104,6 @@ void TV3DManager::display(Camera& whichCamera) {
// We only need to render the overlays to a texture once, then we just render the texture as a quad // We only need to render the overlays to a texture once, then we just render the texture as a quad
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
applicationOverlay.renderOverlay(true); applicationOverlay.renderOverlay(true);
const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::UserInterface);
DependencyManager::get<GlowEffect>()->prepare(); DependencyManager::get<GlowEffect>()->prepare();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -135,9 +134,7 @@ void TV3DManager::display(Camera& whichCamera) {
eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0)); eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0));
Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO); Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO);
if (displayOverlays) { applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
}
_activeEye = NULL; _activeEye = NULL;
} }
glPopMatrix(); glPopMatrix();
@ -166,9 +163,7 @@ void TV3DManager::display(Camera& whichCamera) {
eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0)); eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0));
Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO); Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO);
if (displayOverlays) { applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
}
_activeEye = NULL; _activeEye = NULL;
} }
glPopMatrix(); glPopMatrix();

View file

@ -353,7 +353,7 @@ void InputController::update() {
// TODO for now the InputController is only supporting a JointTracker from a MotionTracker // TODO for now the InputController is only supporting a JointTracker from a MotionTracker
MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId)); MotionTracker* motionTracker = dynamic_cast< MotionTracker*> (DeviceTracker::getDevice(_deviceTrackerId));
if (motionTracker) { if (motionTracker) {
if (_subTrackerId < motionTracker->numJointTrackers()) { if ((int)_subTrackerId < motionTracker->numJointTrackers()) {
const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId); const MotionTracker::JointTracker* joint = motionTracker->getJointTracker(_subTrackerId);
if (joint->isActive()) { if (joint->isActive()) {

View file

@ -55,6 +55,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid
_windowWidget = dockWidget; _windowWidget = dockWidget;
} else { } else {
_windowWidget = new QWidget(Application::getInstance()->getWindow(), Qt::Window); _windowWidget = new QWidget(Application::getInstance()->getWindow(), Qt::Window);
_windowWidget->setWindowTitle(title);
_windowWidget->setMinimumSize(width, height); _windowWidget->setMinimumSize(width, height);
auto layout = new QVBoxLayout(_windowWidget); auto layout = new QVBoxLayout(_windowWidget);
@ -96,6 +97,18 @@ void WebWindowClass::setVisible(bool visible) {
QMetaObject::invokeMethod(_windowWidget, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible)); QMetaObject::invokeMethod(_windowWidget, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible));
} }
void WebWindowClass::setURL(const QString& url) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setURL", Qt::BlockingQueuedConnection, Q_ARG(QString, url));
return;
}
_webView->setUrl(url);
}
void WebWindowClass::raise() {
QMetaObject::invokeMethod(_windowWidget, "raise", Qt::BlockingQueuedConnection);
}
QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
WebWindowClass* retVal; WebWindowClass* retVal;
QString file = context->argument(0).toString(); QString file = context->argument(0).toString();

View file

@ -34,6 +34,7 @@ signals:
class WebWindowClass : public QObject { class WebWindowClass : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QObject* eventBridge READ getEventBridge) Q_PROPERTY(QObject* eventBridge READ getEventBridge)
Q_PROPERTY(QString url READ getURL)
public: public:
WebWindowClass(const QString& title, const QString& url, int width, int height, bool isToolWindow = false); WebWindowClass(const QString& title, const QString& url, int width, int height, bool isToolWindow = false);
~WebWindowClass(); ~WebWindowClass();
@ -42,6 +43,9 @@ public:
public slots: public slots:
void setVisible(bool visible); void setVisible(bool visible);
QString getURL() const { return _webView->url().url(); }
void setURL(const QString& url);
void raise();
ScriptEventBridge* getEventBridge() const { return _eventBridge; } ScriptEventBridge* getEventBridge() const { return _eventBridge; }
void addEventBridgeToWindowObject(); void addEventBridgeToWindowObject();

View file

@ -33,6 +33,7 @@ WindowScriptingInterface::WindowScriptingInterface() :
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler(); const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged); connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged);
connect(Application::getInstance(), &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested); connect(Application::getInstance(), &Application::svoImportRequested, this, &WindowScriptingInterface::svoImportRequested);
connect(Application::getInstance(), &Application::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
} }
WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height, bool isToolWindow) { WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height, bool isToolWindow) {

View file

@ -62,6 +62,7 @@ signals:
void inlineButtonClicked(const QString& name); void inlineButtonClicked(const QString& name);
void nonBlockingFormClosed(); void nonBlockingFormClosed();
void svoImportRequested(const QString& url); void svoImportRequested(const QString& url);
void domainConnectionRefused(const QString& reason);
private slots: private slots:
QScriptValue showAlert(const QString& message); QScriptValue showAlert(const QString& message);

View file

@ -139,6 +139,7 @@ ApplicationOverlay::ApplicationOverlay() :
_magnifier(true), _magnifier(true),
_alpha(1.0f), _alpha(1.0f),
_oculusUIRadius(1.0f), _oculusUIRadius(1.0f),
_trailingAudioLoudness(0.0f),
_crosshairTexture(0), _crosshairTexture(0),
_previousBorderWidth(-1), _previousBorderWidth(-1),
_previousBorderHeight(-1), _previousBorderHeight(-1),
@ -176,18 +177,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
_textureAspectRatio = (float)glCanvas->getDeviceWidth() / (float)glCanvas->getDeviceHeight(); _textureAspectRatio = (float)glCanvas->getDeviceWidth() / (float)glCanvas->getDeviceHeight();
//Handle fading and deactivation/activation of UI //Handle fading and deactivation/activation of UI
if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
_alpha += FADE_SPEED;
if (_alpha > 1.0f) {
_alpha = 1.0f;
}
} else {
_alpha -= FADE_SPEED;
if (_alpha <= 0.0f) {
_alpha = 0.0f;
}
}
// Render 2D overlay // Render 2D overlay
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);

View file

@ -114,7 +114,7 @@ private:
quint64 _lastMouseMove; quint64 _lastMouseMove;
bool _magnifier; bool _magnifier;
float _alpha; float _alpha = 1.0f;
float _oculusUIRadius; float _oculusUIRadius;
float _trailingAudioLoudness; float _trailingAudioLoudness;

View file

@ -23,7 +23,7 @@
#include "LoginDialog.h" #include "LoginDialog.h"
#include "UIUtil.h" #include "UIUtil.h"
const QString FORGOT_PASSWORD_URL = "https://metaverse.highfidelity.io/users/password/new"; const QString FORGOT_PASSWORD_URL = "https://metaverse.highfidelity.com/users/password/new";
LoginDialog::LoginDialog(QWidget* parent) : LoginDialog::LoginDialog(QWidget* parent) :
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP), FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),

View file

@ -136,7 +136,7 @@
<string>&lt;style type=&quot;text/css&quot;&gt; <string>&lt;style type=&quot;text/css&quot;&gt;
a { text-decoration: none; color: #267077;} a { text-decoration: none; color: #267077;}
&lt;/style&gt; &lt;/style&gt;
Invalid username or password. &lt;a href=&quot;https://metaverse.highfidelity.io/password/new&quot;&gt;Recover?&lt;/a&gt;</string> Invalid username or password. &lt;a href=&quot;https://metaverse.highfidelity.com/password/new&quot;&gt;Recover?&lt;/a&gt;</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
@ -458,7 +458,7 @@ border-radius: 4px; padding-top: 1px;</string>
<string>&lt;style type=&quot;text/css&quot;&gt; <string>&lt;style type=&quot;text/css&quot;&gt;
a { text-decoration: none; color: #267077;} a { text-decoration: none; color: #267077;}
&lt;/style&gt; &lt;/style&gt;
&lt;a href=&quot;https://metaverse.highfidelity.io/password/new&quot;&gt;Recover password?&lt;/a&gt;</string> &lt;a href=&quot;https://metaverse.highfidelity.com/password/new&quot;&gt;Recover password?&lt;/a&gt;</string>
</property> </property>
<property name="openExternalLinks"> <property name="openExternalLinks">
<bool>true</bool> <bool>true</bool>

View file

@ -88,9 +88,9 @@ template< typename T >
void AudioFrameBuffer< T >::deallocateFrames() { void AudioFrameBuffer< T >::deallocateFrames() {
if (_frameBuffer) { if (_frameBuffer) {
for (uint32_t i = 0; i < _channelCountMax; ++i) { for (uint32_t i = 0; i < _channelCountMax; ++i) {
delete _frameBuffer[i]; delete[] _frameBuffer[i];
} }
delete _frameBuffer; delete[] _frameBuffer;
} }
_frameBuffer = NULL; _frameBuffer = NULL;
} }

View file

@ -88,7 +88,7 @@ public:
} }
void loadProfile(int profileIndex) { void loadProfile(int profileIndex) {
if (profileIndex >= 0 && profileIndex < _profileCount) { if (profileIndex >= 0 && profileIndex < (int)_profileCount) {
for (uint32_t i = 0; i < _filterCount; ++i) { for (uint32_t i = 0; i < _filterCount; ++i) {
FilterParameter p = _profiles[profileIndex][i]; FilterParameter p = _profiles[profileIndex][i];

View file

@ -61,21 +61,13 @@ typedef unsigned long long quint64;
const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0; const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0;
const quint32 AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED = 1U << 1; const quint32 AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED = 1U << 1;
const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 2;
const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 3;
const quint32 AVATAR_MOTION_STAND_ON_NEARBY_FLOORS = 1U << 4;
const quint32 AVATAR_MOTION_DEFAULTS = const quint32 AVATAR_MOTION_DEFAULTS =
AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED | AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED |
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED | AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
// these bits will be expanded as features are exposed // these bits will be expanded as features are exposed
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED | AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY |
AVATAR_MOTION_OBEY_LOCAL_GRAVITY |
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
// Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of // Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of
@ -300,16 +292,6 @@ public:
const AABox& getLocalAABox() const { return _localAABox; } const AABox& getLocalAABox() const { return _localAABox; }
const Referential* getReferential() const { return _referential; } const Referential* getReferential() const { return _referential; }
void togglePhysicsEnabled() { _enablePhysics = !_enablePhysics; }
bool isPhysicsEnabled() { return _enablePhysics; }
void setPhysicsEnabled(bool enablePhysics) { _enablePhysics = enablePhysics; }
void lockForRead() { _lock.lockForRead(); }
bool tryLockForRead() { return _lock.tryLockForRead(); }
void lockForWrite() { _lock.lockForWrite(); }
bool tryLockForWrite() { return _lock.tryLockForWrite(); }
void unlock() { _lock.unlock(); }
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; } void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; } Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
@ -375,8 +357,8 @@ protected:
HeadData* _headData; HeadData* _headData;
HandData* _handData; HandData* _handData;
QUrl _faceModelURL = DEFAULT_HEAD_MODEL_URL; QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit
QUrl _skeletonModelURL = DEFAULT_BODY_MODEL_URL; QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit
QVector<AttachmentData> _attachmentData; QVector<AttachmentData> _attachmentData;
QString _displayName; QString _displayName;
@ -409,9 +391,6 @@ private:
// privatize the copy constructor and assignment operator so they cannot be called // privatize the copy constructor and assignment operator so they cannot be called
AvatarData(const AvatarData&); AvatarData(const AvatarData&);
AvatarData& operator= (const AvatarData&); AvatarData& operator= (const AvatarData&);
QReadWriteLock _lock;
bool _enablePhysics = false;
}; };
Q_DECLARE_METATYPE(AvatarData*) Q_DECLARE_METATYPE(AvatarData*)

View file

@ -277,7 +277,6 @@ void EntityTreeRenderer::update() {
void EntityTreeRenderer::checkEnterLeaveEntities() { void EntityTreeRenderer::checkEnterLeaveEntities() {
if (_tree && !_shuttingDown) { if (_tree && !_shuttingDown) {
_tree->lockForWrite(); // so that our scripts can do edits if they want
glm::vec3 avatarPosition = _viewState->getAvatarPosition(); glm::vec3 avatarPosition = _viewState->getAvatarPosition();
if (avatarPosition != _lastAvatarPosition) { if (avatarPosition != _lastAvatarPosition) {
@ -286,6 +285,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
QVector<EntityItemID> entitiesContainingAvatar; QVector<EntityItemID> entitiesContainingAvatar;
// find the entities near us // find the entities near us
_tree->lockForRead(); // don't let someone else change our tree while we search
static_cast<EntityTree*>(_tree)->findEntities(avatarPosition, radius, foundEntities); static_cast<EntityTree*>(_tree)->findEntities(avatarPosition, radius, foundEntities);
// create a list of entities that actually contain the avatar's position // create a list of entities that actually contain the avatar's position
@ -294,6 +294,11 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
entitiesContainingAvatar << entity->getEntityItemID(); entitiesContainingAvatar << entity->getEntityItemID();
} }
} }
_tree->unlock();
// Note: at this point we don't need to worry about the tree being locked, because we only deal with
// EntityItemIDs from here. The loadEntityScript() method is robust against attempting to load scripts
// for entity IDs that no longer exist.
// for all of our previous containing entities, if they are no longer containing then send them a leave event // for all of our previous containing entities, if they are no longer containing then send them a leave event
foreach(const EntityItemID& entityID, _currentEntitiesInside) { foreach(const EntityItemID& entityID, _currentEntitiesInside) {
@ -322,14 +327,12 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
_currentEntitiesInside = entitiesContainingAvatar; _currentEntitiesInside = entitiesContainingAvatar;
_lastAvatarPosition = avatarPosition; _lastAvatarPosition = avatarPosition;
} }
_tree->unlock();
} }
} }
void EntityTreeRenderer::leaveAllEntities() { void EntityTreeRenderer::leaveAllEntities() {
if (_tree && !_shuttingDown) { if (_tree && !_shuttingDown) {
_tree->lockForWrite(); // so that our scripts can do edits if they want
// for all of our previous containing entities, if they are no longer containing then send them a leave event // for all of our previous containing entities, if they are no longer containing then send them a leave event
foreach(const EntityItemID& entityID, _currentEntitiesInside) { foreach(const EntityItemID& entityID, _currentEntitiesInside) {
emit leaveEntity(entityID); emit leaveEntity(entityID);
@ -344,7 +347,6 @@ void EntityTreeRenderer::leaveAllEntities() {
// make sure our "last avatar position" is something other than our current position, so that on our // 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. // first chance, we'll check for enter/leave entity events.
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE); _lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
_tree->unlock();
} }
} }
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) { void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {

View file

@ -366,6 +366,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
// multiply each point by scale before handing the point-set off to the physics engine // multiply each point by scale before handing the point-set off to the physics engine
for (int i = 0; i < _points.size(); i++) { for (int i = 0; i < _points.size(); i++) {
for (int j = 0; j < _points[i].size(); j++) { for (int j = 0; j < _points[i].size(); j++) {
// compensate for registraion
_points[i][j] += _model->getOffset();
// scale so the collision points match the model points
_points[i][j] *= scale; _points[i][j] *= scale;
} }
} }

View file

@ -57,7 +57,7 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_collisionsWillMove = ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE; _collisionsWillMove = ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE;
_locked = ENTITY_ITEM_DEFAULT_LOCKED; _locked = ENTITY_ITEM_DEFAULT_LOCKED;
_userData = ENTITY_ITEM_DEFAULT_USER_DATA; _userData = ENTITY_ITEM_DEFAULT_USER_DATA;
_attribution = ENTITY_ITEM_DEFAULT_ATTRIBUTION; _marketplaceID = ENTITY_ITEM_DEFAULT_MARKETPLACE_ID;
} }
EntityItem::EntityItem(const EntityItemID& entityItemID) { EntityItem::EntityItem(const EntityItemID& entityItemID) {
@ -117,7 +117,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_COLLISIONS_WILL_MOVE; requestedProperties += PROP_COLLISIONS_WILL_MOVE;
requestedProperties += PROP_LOCKED; requestedProperties += PROP_LOCKED;
requestedProperties += PROP_USER_DATA; requestedProperties += PROP_USER_DATA;
requestedProperties += PROP_ATTRIBUTION; requestedProperties += PROP_MARKETPLACE_ID;
return requestedProperties; return requestedProperties;
} }
@ -240,7 +240,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove()); APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, appendValue, getLocked()); APPEND_ENTITY_PROPERTY(PROP_LOCKED, appendValue, getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, appendValue, getUserData()); APPEND_ENTITY_PROPERTY(PROP_USER_DATA, appendValue, getUserData());
APPEND_ENTITY_PROPERTY(PROP_ATTRIBUTION, appendValue, getAttribution()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, getMarketplaceID());
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
requestedProperties, requestedProperties,
@ -555,8 +555,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, _locked); READ_ENTITY_PROPERTY(PROP_LOCKED, bool, _locked);
READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA, setUserData); READ_ENTITY_PROPERTY_STRING(PROP_USER_DATA, setUserData);
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_ATTRIBUTION) { if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
READ_ENTITY_PROPERTY_STRING(PROP_ATTRIBUTION, setAttribution); READ_ENTITY_PROPERTY_STRING(PROP_MARKETPLACE_ID, setMarketplaceID);
} }
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
@ -568,8 +568,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
// by doing this parsing here... but it's not likely going to fully recover the content. // by doing this parsing here... but it's not likely going to fully recover the content.
// //
// TODO: Remove this conde once we've sufficiently migrated content past this damaged version // TODO: Remove this conde once we've sufficiently migrated content past this damaged version
if (args.bitstreamVersion == VERSION_ENTITIES_HAS_ATTRIBUTION_DAMAGED) { if (args.bitstreamVersion == VERSION_ENTITIES_HAS_MARKETPLACE_ID_DAMAGED) {
READ_ENTITY_PROPERTY_STRING(PROP_ATTRIBUTION, setAttribution); READ_ENTITY_PROPERTY_STRING(PROP_MARKETPLACE_ID, setMarketplaceID);
} }
if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) { if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) {
@ -838,7 +838,7 @@ EntityItemProperties EntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove); COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked); COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData); COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(attribution, getAttribution); COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID);
properties._defaultSettings = false; properties._defaultSettings = false;
@ -867,7 +867,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked); SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData); SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(attribution, setAttribution); SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
if (somethingChanged) { if (somethingChanged) {
somethingChangedNotification(); // notify derived classes that something has changed somethingChangedNotification(); // notify derived classes that something has changed

View file

@ -251,8 +251,8 @@ public:
const QString& getUserData() const { return _userData; } const QString& getUserData() const { return _userData; }
void setUserData(const QString& value) { _userData = value; } void setUserData(const QString& value) { _userData = value; }
const QString& getAttribution() const { return _attribution; } const QString& getMarketplaceID() const { return _marketplaceID; }
void setAttribution(const QString& value) { _attribution = value; } void setMarketplaceID(const QString& value) { _marketplaceID = value; }
// TODO: get rid of users of getRadius()... // TODO: get rid of users of getRadius()...
float getRadius() const; float getRadius() const;
@ -342,7 +342,7 @@ protected:
bool _collisionsWillMove; bool _collisionsWillMove;
bool _locked; bool _locked;
QString _userData; QString _userData;
QString _attribution; QString _marketplaceID;
// NOTE: Damping is applied like this: v *= pow(1 - damping, dt) // NOTE: Damping is applied like this: v *= pow(1 - damping, dt)
// //

View file

@ -25,7 +25,7 @@ EntityItemID::EntityItemID() :
creatorTokenID(UNKNOWN_ENTITY_TOKEN), creatorTokenID(UNKNOWN_ENTITY_TOKEN),
isKnownID(false) isKnownID(false)
{ {
}; }
EntityItemID::EntityItemID(const EntityItemID& other) : EntityItemID::EntityItemID(const EntityItemID& other) :
id(other.id), id(other.id),

View file

@ -70,7 +70,7 @@ EntityItemProperties::EntityItemProperties() :
CONSTRUCT_PROPERTY(emitStrength, ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH), CONSTRUCT_PROPERTY(emitStrength, ParticleEffectEntityItem::DEFAULT_EMIT_STRENGTH),
CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY), CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY),
CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS), CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS),
CONSTRUCT_PROPERTY(attribution, ENTITY_ITEM_DEFAULT_ATTRIBUTION), CONSTRUCT_PROPERTY(marketplaceID, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID),
_id(UNKNOWN_ENTITY_ID), _id(UNKNOWN_ENTITY_ID),
_idSet(false), _idSet(false),
@ -260,7 +260,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_EMIT_STRENGTH, emitStrength); CHECK_PROPERTY_CHANGE(PROP_EMIT_STRENGTH, emitStrength);
CHECK_PROPERTY_CHANGE(PROP_LOCAL_GRAVITY, localGravity); CHECK_PROPERTY_CHANGE(PROP_LOCAL_GRAVITY, localGravity);
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius); CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius);
CHECK_PROPERTY_CHANGE(PROP_ATTRIBUTION, attribution); CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID);
return changedProperties; return changedProperties;
} }
@ -323,7 +323,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(emitStrength); COPY_PROPERTY_TO_QSCRIPTVALUE(emitStrength);
COPY_PROPERTY_TO_QSCRIPTVALUE(localGravity); COPY_PROPERTY_TO_QSCRIPTVALUE(localGravity);
COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius); COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius);
COPY_PROPERTY_TO_QSCRIPTVALUE(attribution); COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID);
// Sitting properties support // Sitting properties support
QScriptValue sittingPoints = engine->newObject(); QScriptValue sittingPoints = engine->newObject();
@ -405,7 +405,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(emitStrength, setEmitStrength); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(emitStrength, setEmitStrength);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localGravity, setLocalGravity); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(localGravity, setLocalGravity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(particleRadius, setParticleRadius); COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(particleRadius, setParticleRadius);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(attribution, setAttribution); COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(marketplaceID, setMarketplaceID);
_lastEdited = usecTimestampNow(); _lastEdited = usecTimestampNow();
} }
@ -591,7 +591,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, properties.getParticleRadius()); APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, properties.getParticleRadius());
} }
APPEND_ENTITY_PROPERTY(PROP_ATTRIBUTION, appendValue, properties.getAttribution()); APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, properties.getMarketplaceID());
} }
if (propertyCount > 0) { if (propertyCount > 0) {
int endOfEntityItemData = packetData->getUncompressedByteOffset(); int endOfEntityItemData = packetData->getUncompressedByteOffset();
@ -822,7 +822,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius);
} }
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ATTRIBUTION, setAttribution); READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MARKETPLACE_ID, setMarketplaceID);
return valid; return valid;
} }
@ -905,7 +905,7 @@ void EntityItemProperties::markAllChanged() {
_localGravityChanged = true; _localGravityChanged = true;
_particleRadiusChanged = true; _particleRadiusChanged = true;
_attributionChanged = true; _marketplaceIDChanged = true;
} }
/// The maximum bounding cube for the entity, independent of it's rotation. /// The maximum bounding cube for the entity, independent of it's rotation.

View file

@ -94,11 +94,11 @@ enum EntityPropertyList {
PROP_PARTICLE_RADIUS, PROP_PARTICLE_RADIUS,
PROP_COLLISION_MODEL_URL, PROP_COLLISION_MODEL_URL,
PROP_ATTRIBUTION, PROP_MARKETPLACE_ID,
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties ABOVE this line and then modify PROP_LAST_ITEM below // ATTENTION: add new properties ABOVE this line and then modify PROP_LAST_ITEM below
PROP_LAST_ITEM = PROP_ATTRIBUTION, PROP_LAST_ITEM = PROP_MARKETPLACE_ID,
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
@ -205,7 +205,7 @@ public:
DEFINE_PROPERTY(PROP_EMIT_STRENGTH, EmitStrength, emitStrength, float); DEFINE_PROPERTY(PROP_EMIT_STRENGTH, EmitStrength, emitStrength, float);
DEFINE_PROPERTY(PROP_LOCAL_GRAVITY, LocalGravity, localGravity, float); DEFINE_PROPERTY(PROP_LOCAL_GRAVITY, LocalGravity, localGravity, float);
DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float); DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float);
DEFINE_PROPERTY_REF(PROP_ATTRIBUTION, Attribution, attribution, QString); DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString);
public: public:
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); } float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
@ -333,7 +333,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitStrength, emitStrength, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitStrength, emitStrength, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalGravity, localGravity, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalGravity, localGravity, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Attribution, attribution, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, "");
debug << " last edited:" << properties.getLastEdited() << "\n"; debug << " last edited:" << properties.getLastEdited() << "\n";
debug << " edited ago:" << properties.getEditedAgo() << "\n"; debug << " edited ago:" << properties.getEditedAgo() << "\n";

View file

@ -22,7 +22,7 @@ const glm::vec3 ENTITY_ITEM_ZERO_VEC3(0.0f);
const bool ENTITY_ITEM_DEFAULT_LOCKED = false; const bool ENTITY_ITEM_DEFAULT_LOCKED = false;
const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString(""); const QString ENTITY_ITEM_DEFAULT_USER_DATA = QString("");
const QString ENTITY_ITEM_DEFAULT_ATTRIBUTION = QString(""); const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString("");
const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f; const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
const float ENTITY_ITEM_DEFAULT_GLOW_LEVEL = 0.0f; const float ENTITY_ITEM_DEFAULT_GLOW_LEVEL = 0.0f;

View file

@ -148,6 +148,7 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
} }
} }
} else { } else {
QString entityScriptBefore = entity->getScript();
uint32_t preFlags = entity->getDirtyFlags(); uint32_t preFlags = entity->getDirtyFlags();
UpdateEntityOperator theOperator(this, containingElement, entity, properties); UpdateEntityOperator theOperator(this, containingElement, entity, properties);
recurseTreeWithOperator(&theOperator); recurseTreeWithOperator(&theOperator);
@ -166,6 +167,11 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
entity->clearDirtyFlags(); entity->clearDirtyFlags();
} }
} }
QString entityScriptAfter = entity->getScript();
if (entityScriptBefore != entityScriptAfter) {
emitEntityScriptChanging(entity->getEntityItemID()); // the entity script has changed
}
} }
// TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG). // TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG).

View file

@ -458,41 +458,6 @@ FBXNode parseFBX(QIODevice* device) {
return top; return top;
} }
QVariantHash parseMapping(QIODevice* device) {
QVariantHash properties;
QByteArray line;
while (!(line = device->readLine()).isEmpty()) {
if ((line = line.trimmed()).startsWith('#')) {
continue; // comment
}
QList<QByteArray> sections = line.split('=');
if (sections.size() < 2) {
continue;
}
QByteArray name = sections.at(0).trimmed();
if (sections.size() == 2) {
properties.insertMulti(name, sections.at(1).trimmed());
} else if (sections.size() == 3) {
QVariantHash heading = properties.value(name).toHash();
heading.insertMulti(sections.at(1).trimmed(), sections.at(2).trimmed());
properties.insert(name, heading);
} else if (sections.size() >= 4) {
QVariantHash heading = properties.value(name).toHash();
QVariantList contents;
for (int i = 2; i < sections.size(); i++) {
contents.append(sections.at(i).trimmed());
}
heading.insertMulti(sections.at(1).trimmed(), contents);
properties.insert(name, heading);
}
}
return properties;
}
QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) { QVector<glm::vec3> createVec3Vector(const QVector<double>& doubleVector) {
QVector<glm::vec3> values; QVector<glm::vec3> values;
for (const double* it = doubleVector.constData(), *end = it + (doubleVector.size() / 3 * 3); it != end; ) { for (const double* it = doubleVector.constData(), *end = it + (doubleVector.size() / 3 * 3); it != end; ) {
@ -2473,39 +2438,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
return geometry; return geometry;
} }
QVariantHash readMapping(const QByteArray& data) {
QBuffer buffer(const_cast<QByteArray*>(&data));
buffer.open(QIODevice::ReadOnly);
return parseMapping(&buffer);
}
QByteArray writeMapping(const QVariantHash& mapping) {
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
for (QVariantHash::const_iterator first = mapping.constBegin(); first != mapping.constEnd(); first++) {
QByteArray key = first.key().toUtf8() + " = ";
QVariantHash hashValue = first.value().toHash();
if (hashValue.isEmpty()) {
buffer.write(key + first.value().toByteArray() + "\n");
continue;
}
for (QVariantHash::const_iterator second = hashValue.constBegin(); second != hashValue.constEnd(); second++) {
QByteArray extendedKey = key + second.key().toUtf8();
QVariantList listValue = second.value().toList();
if (listValue.isEmpty()) {
buffer.write(extendedKey + " = " + second.value().toByteArray() + "\n");
continue;
}
buffer.write(extendedKey);
for (QVariantList::const_iterator third = listValue.constBegin(); third != listValue.constEnd(); third++) {
buffer.write(" = " + third->toByteArray());
}
buffer.write("\n");
}
}
return buffer.data();
}
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) { FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) {
QBuffer buffer(const_cast<QByteArray*>(&model)); QBuffer buffer(const_cast<QByteArray*>(&model));
buffer.open(QIODevice::ReadOnly); buffer.open(QIODevice::ReadOnly);

View file

@ -261,12 +261,6 @@ public:
Q_DECLARE_METATYPE(FBXGeometry) Q_DECLARE_METATYPE(FBXGeometry)
/// Reads an FST mapping from the supplied data.
QVariantHash readMapping(const QByteArray& data);
/// Writes an FST mapping to a byte array.
QByteArray writeMapping(const QVariantHash& mapping);
/// Reads FBX geometry from the supplied model and mapping data. /// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing /// \exception QString if an error occurs in parsing
FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f); FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f);

View file

@ -0,0 +1,99 @@
//
// FSTReader.cpp
//
//
// Created by Clement on 3/26/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QBuffer>
#include "FSTReader.h"
QVariantHash parseMapping(QIODevice* device) {
QVariantHash properties;
QByteArray line;
while (!(line = device->readLine()).isEmpty()) {
if ((line = line.trimmed()).startsWith('#')) {
continue; // comment
}
QList<QByteArray> sections = line.split('=');
if (sections.size() < 2) {
continue;
}
QByteArray name = sections.at(0).trimmed();
if (sections.size() == 2) {
properties.insertMulti(name, sections.at(1).trimmed());
} else if (sections.size() == 3) {
QVariantHash heading = properties.value(name).toHash();
heading.insertMulti(sections.at(1).trimmed(), sections.at(2).trimmed());
properties.insert(name, heading);
} else if (sections.size() >= 4) {
QVariantHash heading = properties.value(name).toHash();
QVariantList contents;
for (int i = 2; i < sections.size(); i++) {
contents.append(sections.at(i).trimmed());
}
heading.insertMulti(sections.at(1).trimmed(), contents);
properties.insert(name, heading);
}
}
return properties;
}
QVariantHash readMapping(const QByteArray& data) {
QBuffer buffer(const_cast<QByteArray*>(&data));
buffer.open(QIODevice::ReadOnly);
return parseMapping(&buffer);
}
void writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it) {
QByteArray key = it.key().toUtf8() + " = ";
QVariantHash hashValue = it.value().toHash();
if (hashValue.isEmpty()) {
buffer.write(key + it.value().toByteArray() + "\n");
return;
}
for (QVariantHash::const_iterator second = hashValue.constBegin(); second != hashValue.constEnd(); second++) {
QByteArray extendedKey = key + second.key().toUtf8();
QVariantList listValue = second.value().toList();
if (listValue.isEmpty()) {
buffer.write(extendedKey + " = " + second.value().toByteArray() + "\n");
continue;
}
buffer.write(extendedKey);
for (QVariantList::const_iterator third = listValue.constBegin(); third != listValue.constEnd(); third++) {
buffer.write(" = " + third->toByteArray());
}
buffer.write("\n");
}
}
QByteArray writeMapping(const QVariantHash& mapping) {
static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << SCALE_FIELD << FILENAME_FIELD
<< TEXDIR_FIELD << JOINT_FIELD << FREE_JOINT_FIELD
<< BLENDSHAPE_FIELD << JOINT_INDEX_FIELD;
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
for (auto key : PREFERED_ORDER) {
auto it = mapping.find(key);
if (it != mapping.constEnd()) {
writeVariant(buffer, it);
}
}
for (auto it = mapping.constBegin(); it != mapping.constEnd(); it++) {
if (!PREFERED_ORDER.contains(it.key())) {
writeVariant(buffer, it);
}
}
return buffer.data();
}

View file

@ -0,0 +1,36 @@
//
// FSTReader.h
//
//
// Created by Clement on 3/26/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_FSTReader_h
#define hifi_FSTReader_h
#include <QVariantHash>
static const QString NAME_FIELD = "name";
static const QString FILENAME_FIELD = "filename";
static const QString TEXDIR_FIELD = "texdir";
static const QString LOD_FIELD = "lod";
static const QString JOINT_INDEX_FIELD = "jointIndex";
static const QString SCALE_FIELD = "scale";
static const QString TRANSLATION_X_FIELD = "tx";
static const QString TRANSLATION_Y_FIELD = "ty";
static const QString TRANSLATION_Z_FIELD = "tz";
static const QString JOINT_FIELD = "joint";
static const QString FREE_JOINT_FIELD = "freeJoint";
static const QString BLENDSHAPE_FIELD = "bs";
/// Reads an FST mapping from the supplied data.
QVariantHash readMapping(const QByteArray& data);
/// Writes an FST mapping to a byte array.
QByteArray writeMapping(const QVariantHash& mapping);
#endif // hifi_FSTReader_h

View file

@ -249,7 +249,20 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
} else if (indices.count() == 4) { } else if (indices.count() == 4) {
meshPart.quadIndices << indices; meshPart.quadIndices << indices;
} else { } else {
qDebug() << "no support for more than 4 vertices on a face in OBJ files"; // some obj writers (maya) will write a face with lots of points.
for (int i = 1; i < indices.count() - 1; i++) {
// break the face into triangles
meshPart.triangleIndices.append(indices[0]);
meshPart.triangleIndices.append(indices[i]);
meshPart.triangleIndices.append(indices[i+1]);
}
if (indices.count() == normalIndices.count()) {
for (int i = 1; i < normalIndices.count() - 1; i++) {
faceNormalIndexes.append(normalIndices[0]);
faceNormalIndexes.append(normalIndices[i]);
faceNormalIndexes.append(normalIndices[i+1]);
}
}
} }
} else { } else {
// something we don't (yet) care about // something we don't (yet) care about

View file

@ -49,7 +49,10 @@ static const GLenum _elementTypeToGLType[NUM_TYPES]= {
GL_UNSIGNED_BYTE GL_UNSIGNED_BYTE
}; };
#if _DEBUG
#define CHECK_GL_ERROR() ::gpu::GLBackend::checkGLError() #define CHECK_GL_ERROR() ::gpu::GLBackend::checkGLError()
//#define CHECK_GL_ERROR() #else
#define CHECK_GL_ERROR()
#endif
#endif #endif

View file

@ -115,7 +115,7 @@ void AccountManager::updateBalance() {
callbackParameters.jsonCallbackReceiver = &_accountInfo; callbackParameters.jsonCallbackReceiver = &_accountInfo;
callbackParameters.jsonCallbackMethod = "setBalanceFromJSON"; callbackParameters.jsonCallbackMethod = "setBalanceFromJSON";
authenticatedRequest("/api/v1/wallets/mine", QNetworkAccessManager::GetOperation, callbackParameters); sendRequest("/api/v1/wallets/mine", AccountManagerAuth::Required, QNetworkAccessManager::GetOperation, callbackParameters);
} }
} }
@ -159,50 +159,30 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
} }
} }
void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation, void AccountManager::sendRequest(const QString& path,
const JSONCallbackParameters& callbackParams, AccountManagerAuth::Type authType,
const QByteArray& dataByteArray, QNetworkAccessManager::Operation operation,
QHttpMultiPart* dataMultiPart, const JSONCallbackParameters& callbackParams,
const QVariantMap& propertyMap) { const QByteArray& dataByteArray,
QHttpMultiPart* dataMultiPart,
const QVariantMap& propertyMap) {
QMetaObject::invokeMethod(this, "invokedRequest", if (thread() != QThread::currentThread()) {
Q_ARG(const QString&, path), QMetaObject::invokeMethod(this, "sendRequest",
Q_ARG(bool, true), Q_ARG(const QString&, path),
Q_ARG(QNetworkAccessManager::Operation, operation), Q_ARG(AccountManagerAuth::Type, AccountManagerAuth::Required),
Q_ARG(const JSONCallbackParameters&, callbackParams), Q_ARG(QNetworkAccessManager::Operation, operation),
Q_ARG(const QByteArray&, dataByteArray), Q_ARG(const JSONCallbackParameters&, callbackParams),
Q_ARG(QHttpMultiPart*, dataMultiPart), Q_ARG(const QByteArray&, dataByteArray),
Q_ARG(QVariantMap, propertyMap)); Q_ARG(QHttpMultiPart*, dataMultiPart),
} Q_ARG(QVariantMap, propertyMap));
}
void AccountManager::unauthenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray,
QHttpMultiPart* dataMultiPart,
const QVariantMap& propertyMap) {
QMetaObject::invokeMethod(this, "invokedRequest",
Q_ARG(const QString&, path),
Q_ARG(bool, false),
Q_ARG(QNetworkAccessManager::Operation, operation),
Q_ARG(const JSONCallbackParameters&, callbackParams),
Q_ARG(const QByteArray&, dataByteArray),
Q_ARG(QHttpMultiPart*, dataMultiPart),
Q_ARG(QVariantMap, propertyMap));
}
void AccountManager::invokedRequest(const QString& path,
bool requiresAuthentication,
QNetworkAccessManager::Operation operation,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart,
const QVariantMap& propertyMap) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest; QNetworkRequest networkRequest;
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QUrl requestURL = _authURL; QUrl requestURL = _authURL;
if (path.startsWith("/")) { if (path.startsWith("/")) {
@ -211,13 +191,17 @@ void AccountManager::invokedRequest(const QString& path,
requestURL.setPath("/" + path); requestURL.setPath("/" + path);
} }
if (requiresAuthentication) { if (authType != AccountManagerAuth::None ) {
if (hasValidAccessToken()) { if (hasValidAccessToken()) {
networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, networkRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
_accountInfo.getAccessToken().authorizationHeaderValue()); _accountInfo.getAccessToken().authorizationHeaderValue());
} else { } else {
qDebug() << "No valid access token present. Bailing on authenticated invoked request."; if (authType == AccountManagerAuth::Required) {
return; qDebug() << "No valid access token present. Bailing on invoked request to"
<< path << "that requires authentication";
return;
}
} }
} }
@ -298,7 +282,7 @@ void AccountManager::processReply() {
} else { } else {
passErrorToCallback(requestReply); passErrorToCallback(requestReply);
} }
delete requestReply; requestReply->deleteLater();
} }
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
@ -540,8 +524,8 @@ void AccountManager::processGeneratedKeypair(const QByteArray& publicKey, const
requestMultiPart->append(keyPart); requestMultiPart->append(keyPart);
authenticatedRequest(PUBLIC_KEY_UPDATE_PATH, QNetworkAccessManager::PutOperation, sendRequest(PUBLIC_KEY_UPDATE_PATH, AccountManagerAuth::Required, QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), QByteArray(), requestMultiPart); JSONCallbackParameters(), QByteArray(), requestMultiPart);
// get rid of the keypair generator now that we don't need it anymore // get rid of the keypair generator now that we don't need it anymore
sender()->deleteLater(); sender()->deleteLater();

View file

@ -37,6 +37,16 @@ public:
QString updateSlot; QString updateSlot;
}; };
namespace AccountManagerAuth {
enum Type {
None,
Required,
Optional
};
}
Q_DECLARE_METATYPE(AccountManagerAuth::Type);
const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization"; const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
class AccountManager : public QObject { class AccountManager : public QObject {
@ -44,19 +54,13 @@ class AccountManager : public QObject {
public: public:
static AccountManager& getInstance(bool forceReset = false); static AccountManager& getInstance(bool forceReset = false);
void authenticatedRequest(const QString& path, Q_INVOKABLE void sendRequest(const QString& path,
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation, AccountManagerAuth::Type authType,
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(), QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
const QByteArray& dataByteArray = QByteArray(), const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
QHttpMultiPart* dataMultiPart = NULL, const QByteArray& dataByteArray = QByteArray(),
const QVariantMap& propertyMap = QVariantMap()); QHttpMultiPart* dataMultiPart = NULL,
const QVariantMap& propertyMap = QVariantMap());
void unauthenticatedRequest(const QString& path,
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
const QByteArray& dataByteArray = QByteArray(),
QHttpMultiPart* dataMultiPart = NULL,
const QVariantMap& propertyMap = QVariantMap()) ;
const QUrl& getAuthURL() const { return _authURL; } const QUrl& getAuthURL() const { return _authURL; }
void setAuthURL(const QUrl& authURL); void setAuthURL(const QUrl& authURL);
@ -107,14 +111,6 @@ private:
void passSuccessToCallback(QNetworkReply* reply); void passSuccessToCallback(QNetworkReply* reply);
void passErrorToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply);
Q_INVOKABLE void invokedRequest(const QString& path,
bool requiresAuthentication,
QNetworkAccessManager::Operation operation,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray,
QHttpMultiPart* dataMultiPart,
const QVariantMap& propertyMap);
QUrl _authURL; QUrl _authURL;
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap; QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;

View file

@ -294,12 +294,11 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q
requestParams.insert(OVERRIDE_PATH_KEY, overridePath); requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
} }
AccountManager::getInstance().unauthenticatedRequest(GET_PLACE.arg(placeName), AccountManager::getInstance().sendRequest(GET_PLACE.arg(placeName),
QNetworkAccessManager::GetOperation, AccountManagerAuth::None,
apiCallbackParameters(), QNetworkAccessManager::GetOperation,
QByteArray(), apiCallbackParameters(),
NULL, QByteArray(), NULL, requestParams);
requestParams);
} }
bool AddressManager::handleNetworkAddress(const QString& lookupString) { bool AddressManager::handleNetworkAddress(const QString& lookupString) {
@ -439,9 +438,10 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
void AddressManager::goToUser(const QString& username) { void AddressManager::goToUser(const QString& username) {
QString formattedUsername = QUrl::toPercentEncoding(username); QString formattedUsername = QUrl::toPercentEncoding(username);
// this is a username - pull the captured name and lookup that user's location // this is a username - pull the captured name and lookup that user's location
AccountManager::getInstance().unauthenticatedRequest(GET_USER_LOCATION.arg(formattedUsername), AccountManager::getInstance().sendRequest(GET_USER_LOCATION.arg(formattedUsername),
QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional,
apiCallbackParameters()); QNetworkAccessManager::GetOperation,
apiCallbackParameters());
} }
void AddressManager::copyAddress() { void AddressManager::copyAddress() {

View file

@ -23,7 +23,7 @@
#include "AccountManager.h" #include "AccountManager.h"
const QString HIFI_URL_SCHEME = "hifi"; const QString HIFI_URL_SCHEME = "hifi";
const QString DEFAULT_HIFI_ADDRESS = "hifi://sandbox"; const QString DEFAULT_HIFI_ADDRESS = "hifi://entry";
typedef const glm::vec3& (*PositionGetter)(); typedef const glm::vec3& (*PositionGetter)();
typedef glm::quat (*OrientationGetter)(); typedef glm::quat (*OrientationGetter)();

View file

@ -36,7 +36,7 @@ const char SOLO_NODE_TYPES[2] = {
NodeType::AudioMixer NodeType::AudioMixer
}; };
const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://metaverse.highfidelity.io"); const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://metaverse.highfidelity.com");
LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) : LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short dtlsListenPort) :
linkedDataCreateCallback(NULL), linkedDataCreateCallback(NULL),

View file

@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) {
return 1; return 1;
case PacketTypeEntityAddOrEdit: case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData: case PacketTypeEntityData:
return VERSION_ENTITIES_HAS_ATTRIBUTION; return VERSION_ENTITIES_HAS_MARKETPLACE_ID;
case PacketTypeEntityErase: case PacketTypeEntityErase:
return 2; return 2;
case PacketTypeAudioStreamStats: case PacketTypeAudioStreamStats:

View file

@ -132,8 +132,8 @@ const PacketVersion VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES =
const PacketVersion VERSION_ENTITIES_HAS_PARTICLES = 10; const PacketVersion VERSION_ENTITIES_HAS_PARTICLES = 10;
const PacketVersion VERSION_ENTITIES_USE_METERS_AND_RADIANS = 11; const PacketVersion VERSION_ENTITIES_USE_METERS_AND_RADIANS = 11;
const PacketVersion VERSION_ENTITIES_HAS_COLLISION_MODEL = 12; const PacketVersion VERSION_ENTITIES_HAS_COLLISION_MODEL = 12;
const PacketVersion VERSION_ENTITIES_HAS_ATTRIBUTION_DAMAGED = 13; const PacketVersion VERSION_ENTITIES_HAS_MARKETPLACE_ID_DAMAGED = 13;
const PacketVersion VERSION_ENTITIES_HAS_ATTRIBUTION = 14; const PacketVersion VERSION_ENTITIES_HAS_MARKETPLACE_ID = 14;
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1; const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
#endif // hifi_PacketHeaders_h #endif // hifi_PacketHeaders_h

View file

@ -18,6 +18,7 @@
#include <QTimer> #include <QTimer>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <assert.h>
#include "NetworkAccessManager.h" #include "NetworkAccessManager.h"
#include "ResourceCache.h" #include "ResourceCache.h"
@ -48,32 +49,40 @@ void ResourceCache::refresh(const QUrl& url) {
} }
} }
void ResourceCache::getResourceAsynchronously(const QUrl& url) {
qDebug() << "ResourceCache::getResourceAsynchronously" << url.toString();
_resourcesToBeGottenLock.lockForWrite();
_resourcesToBeGotten.enqueue(QUrl(url));
_resourcesToBeGottenLock.unlock();
}
void ResourceCache::checkAsynchronousGets() {
assert(QThread::currentThread() == thread());
if (!_resourcesToBeGotten.isEmpty()) {
_resourcesToBeGottenLock.lockForWrite();
QUrl url = _resourcesToBeGotten.dequeue();
_resourcesToBeGottenLock.unlock();
getResource(url);
}
}
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback,
bool delayLoad, void* extra, bool block) { bool delayLoad, void* extra) {
QSharedPointer<Resource> resource = _resources.value(url);
if (!resource.isNull()) {
return resource;
}
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
// This will re-call this method in the main thread. If block is true and the main thread assert(delayLoad);
// is waiting on a lock, we'll deadlock here. getResourceAsynchronously(url);
if (block) { return QSharedPointer<Resource>();
QSharedPointer<Resource> result;
QMetaObject::invokeMethod(this, "getResource", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QSharedPointer<Resource>, result), Q_ARG(const QUrl&, url),
Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
return result;
} else {
// Queue the re-invocation of this method, but if the main thread is blocked, don't wait. The
// return value may be NULL -- it's expected that this will be called again later, in order
// to receive the actual Resource.
QMetaObject::invokeMethod(this, "getResource", Qt::QueuedConnection,
Q_ARG(const QUrl&, url),
Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra));
return _resources.value(url);
}
} }
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {
return getResource(fallback, QUrl(), delayLoad); return getResource(fallback, QUrl(), delayLoad);
} }
QSharedPointer<Resource> resource = _resources.value(url);
if (resource.isNull()) { if (resource.isNull()) {
resource = createResource(url, fallback.isValid() ? resource = createResource(url, fallback.isValid() ?
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra); getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);

View file

@ -21,6 +21,8 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QUrl> #include <QUrl>
#include <QWeakPointer> #include <QWeakPointer>
#include <QReadWriteLock>
#include <QQueue>
#include <DependencyManager.h> #include <DependencyManager.h>
@ -79,6 +81,9 @@ public:
void refresh(const QUrl& url); void refresh(const QUrl& url);
public slots:
void checkAsynchronousGets();
protected: protected:
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE; qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
qint64 _unusedResourcesSize = 0; qint64 _unusedResourcesSize = 0;
@ -89,7 +94,7 @@ protected:
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested /// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
/// \param extra extra data to pass to the creator, if appropriate /// \param extra extra data to pass to the creator, if appropriate
Q_INVOKABLE QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(), Q_INVOKABLE QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
bool delayLoad = false, void* extra = NULL, bool block = true); bool delayLoad = false, void* extra = NULL);
/// Creates a new resource. /// Creates a new resource.
virtual QSharedPointer<Resource> createResource(const QUrl& url, virtual QSharedPointer<Resource> createResource(const QUrl& url,
@ -109,6 +114,11 @@ private:
int _lastLRUKey = 0; int _lastLRUKey = 0;
static int _requestLimit; static int _requestLimit;
void getResourceAsynchronously(const QUrl& url);
QReadWriteLock _resourcesToBeGottenLock;
QQueue<QUrl> _resourcesToBeGotten;
}; };
/// Base class for resources. /// Base class for resources.

View file

@ -62,11 +62,10 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
params.errorCallbackMethod = "requestError"; params.errorCallbackMethod = "requestError";
} }
accountManager.authenticatedRequest(USER_ACTIVITY_URL, accountManager.sendRequest(USER_ACTIVITY_URL,
QNetworkAccessManager::PostOperation, AccountManagerAuth::Required,
params, QNetworkAccessManager::PostOperation,
NULL, params, NULL, multipart);
multipart);
} }
void UserActivityLogger::requestFinished(QNetworkReply& requestReply) { void UserActivityLogger::requestFinished(QNetworkReply& requestReply) {

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
/* /*
Bullet Continuous Collision Detection and Physics Library Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
2015.03.25 -- modified by Andrew Meadows andrew@highfidelity.io
This software is provided 'as-is', without any express or implied warranty. This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software. In no event will the authors be held liable for any damages arising from the use of this software.
@ -37,65 +38,69 @@ class btPairCachingGhostObject;
///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations. ///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations.
///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user. ///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user.
ATTRIBUTE_ALIGNED16(class) CharacterController : public btCharacterControllerInterface ATTRIBUTE_ALIGNED16(class) CharacterController : public btCharacterControllerInterface
{ {
protected: protected:
AvatarData* m_avatarData = NULL; AvatarData* _avatarData = NULL;
btPairCachingGhostObject* m_ghostObject; btPairCachingGhostObject* _ghostObject;
glm::vec3 m_shapeLocalOffset;
btConvexShape* m_convexShape;//is also in m_ghostObject, but it needs to be convex, so we store it here to avoid upcast btConvexShape* _convexShape;//is also in _ghostObject, but it needs to be convex, so we store it here to avoid upcast
btScalar m_radius; btScalar _radius;
btScalar m_halfHeight; btScalar _halfHeight;
btScalar m_verticalVelocity; btScalar _verticalVelocity;
btScalar m_verticalOffset; // fall distance from velocity this frame btScalar _verticalOffset; // fall distance from velocity this frame
btScalar m_maxFallSpeed; btScalar _maxFallSpeed;
btScalar m_jumpSpeed; btScalar _jumpSpeed;
btScalar m_maxJumpHeight; btScalar _maxJumpHeight;
btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value) btScalar _maxSlopeRadians; // Slope angle that is set (used for returning the exact value)
btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization) btScalar _maxSlopeCosine; // Cosine equivalent of _maxSlopeRadians (calculated once when set, for optimization)
btScalar m_gravity; btScalar _gravity;
btScalar m_turnAngle; btScalar _stepUpHeight; // height of stepUp prior to stepForward
btScalar _stepDownHeight; // height of stepDown
btScalar m_stepHeight; // height of stepUp prior to stepForward btScalar _addedMargin;//@todo: remove this and fix the code
btScalar m_addedMargin;//@todo: remove this and fix the code
///this is the desired walk direction, set by the user ///this is the desired walk direction, set by the user
btVector3 m_walkDirection; btVector3 _walkDirection;
btVector3 m_normalizedDirection; btVector3 _normalizedDirection;
//some internal variables //some internal variables
btVector3 m_currentPosition; btVector3 _currentPosition;
btVector3 m_targetPosition; btQuaternion _currentRotation;
btScalar m_lastStepUp; btVector3 _targetPosition;
glm::vec3 _lastPosition;
btScalar _lastStepUp;
///keep track of the contact manifolds ///keep track of the contact manifolds
btManifoldArray m_manifoldArray; btManifoldArray _manifoldArray;
bool m_touchingContact; bool _touchingContact;
btVector3 m_floorNormal; // points from object to character btVector3 _floorNormal; // points from object to character
bool m_enabled; bool _enabled;
bool m_wasOnGround; bool _isOnGround;
bool m_wasJumping; bool _isJumping;
bool m_useWalkDirection; bool _isHovering;
btScalar m_velocityTimeInterval; quint64 _jumpToHoverStart;
int m_upAxis; btScalar _velocityTimeInterval;
btScalar _stepDt;
uint32_t _pendingFlags;
static btVector3* getUpAxisDirections(); glm::vec3 _shapeLocalOffset;
bool m_interpolateUp; glm::vec3 _boxScale; // used to compute capsule shape
bool full_drop;
bool bounce_fix; btDynamicsWorld* _dynamicsWorld = NULL;
btVector3 computeReflectionDirection(const btVector3& direction, const btVector3& normal); btVector3 computeReflectionDirection(const btVector3& direction, const btVector3& normal);
btVector3 parallelComponent(const btVector3& direction, const btVector3& normal); btVector3 parallelComponent(const btVector3& direction, const btVector3& normal);
btVector3 perpindicularComponent(const btVector3& direction, const btVector3& normal); btVector3 perpindicularComponent(const btVector3& direction, const btVector3& normal);
bool recoverFromPenetration(btCollisionWorld* collisionWorld); bool recoverFromPenetration(btCollisionWorld* collisionWorld);
void scanDown(btCollisionWorld* collisionWorld);
void stepUp(btCollisionWorld* collisionWorld); void stepUp(btCollisionWorld* collisionWorld);
void updateTargetPositionBasedOnCollision(const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0)); void updateTargetPositionBasedOnCollision(const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0));
void stepForward(btCollisionWorld* collisionWorld, const btVector3& walkMove); void stepForward(btCollisionWorld* collisionWorld, const btVector3& walkMove);
@ -118,14 +123,6 @@ public:
///btActionInterface interface ///btActionInterface interface
void debugDraw(btIDebugDraw* debugDrawer); void debugDraw(btIDebugDraw* debugDrawer);
void setUpAxis(int axis) {
if (axis < 0)
axis = 0;
if (axis > 2)
axis = 2;
m_upAxis = axis;
}
/// This should probably be called setPositionIncrementPerSimulatorStep. /// This should probably be called setPositionIncrementPerSimulatorStep.
/// This is neither a direction nor a velocity, but the amount to /// This is neither a direction nor a velocity, but the amount to
/// increment the position each simulation iteration, regardless /// increment the position each simulation iteration, regardless
@ -141,18 +138,19 @@ public:
virtual void setVelocityForTimeInterval(const btVector3& velocity, virtual void setVelocityForTimeInterval(const btVector3& velocity,
btScalar timeInterval); btScalar timeInterval);
void reset(btCollisionWorld* collisionWorld ); virtual void reset(btCollisionWorld* collisionWorld );
void warp(const btVector3& origin); virtual void warp(const btVector3& origin);
void preStep(btCollisionWorld* collisionWorld); virtual void preStep(btCollisionWorld* collisionWorld);
void playerStep(btCollisionWorld* collisionWorld, btScalar dt); virtual void playerStep(btCollisionWorld* collisionWorld, btScalar dt);
virtual bool canJump() const;
virtual void jump();
virtual bool onGround() const;
void setMaxFallSpeed(btScalar speed); void setMaxFallSpeed(btScalar speed);
void setJumpSpeed(btScalar jumpSpeed); void setJumpSpeed(btScalar jumpSpeed);
void setMaxJumpHeight(btScalar maxJumpHeight); void setMaxJumpHeight(btScalar maxJumpHeight);
bool canJump() const;
void jump();
void setGravity(btScalar gravity); void setGravity(btScalar gravity);
btScalar getGravity() const; btScalar getGravity() const;
@ -164,11 +162,17 @@ public:
btPairCachingGhostObject* getGhostObject(); btPairCachingGhostObject* getGhostObject();
bool onGround() const;
void setUpInterpolate(bool value); void setUpInterpolate(bool value);
bool needsShapeUpdate(); bool needsRemoval() const;
void updateShape(); bool needsAddition() const;
void setEnabled(bool enabled);
bool isEnabled() const { return _enabled; }
void setDynamicsWorld(btDynamicsWorld* world);
void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale);
bool needsShapeUpdate() const;
void updateShapeIfNecessary();
void preSimulation(btScalar timeStep); void preSimulation(btScalar timeStep);
void postSimulation(); void postSimulation();

View file

@ -29,6 +29,12 @@ PhysicsEngine::PhysicsEngine(const glm::vec3& offset)
PhysicsEngine::~PhysicsEngine() { PhysicsEngine::~PhysicsEngine() {
// TODO: delete engine components... if we ever plan to create more than one instance // TODO: delete engine components... if we ever plan to create more than one instance
delete _collisionConfig;
delete _collisionDispatcher;
delete _broadphaseFilter;
delete _constraintSolver;
delete _dynamicsWorld;
// delete _ghostPairCallback;
} }
// begin EntitySimulation overrides // begin EntitySimulation overrides
@ -280,12 +286,12 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) {
void PhysicsEngine::stepSimulation() { void PhysicsEngine::stepSimulation() {
lock(); lock();
// NOTE: the grand order of operations is: // NOTE: the grand order of operations is:
// (1) relay incoming changes // (1) pull incoming changes
// (2) step simulation // (2) step simulation
// (3) synchronize outgoing motion states // (3) synchronize outgoing motion states
// (4) send outgoing packets // (4) send outgoing packets
// This is step (1). // This is step (1) pull incoming changes
relayIncomingChangesToSimulation(); relayIncomingChangesToSimulation();
const int MAX_NUM_SUBSTEPS = 4; const int MAX_NUM_SUBSTEPS = 4;
@ -294,16 +300,25 @@ void PhysicsEngine::stepSimulation() {
_clock.reset(); _clock.reset();
float timeStep = btMin(dt, MAX_TIMESTEP); float timeStep = btMin(dt, MAX_TIMESTEP);
// This is step (2). // TODO: move character->preSimulation() into relayIncomingChanges
if (_characterController) { if (_characterController) {
if (_characterController->needsRemoval()) {
_characterController->setDynamicsWorld(NULL);
}
_characterController->updateShapeIfNecessary();
if (_characterController->needsAddition()) {
_characterController->setDynamicsWorld(_dynamicsWorld);
}
_characterController->preSimulation(timeStep); _characterController->preSimulation(timeStep);
} }
// This is step (2) step simulation
int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP); int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
_numSubsteps += (uint32_t)numSubsteps; _numSubsteps += (uint32_t)numSubsteps;
stepNonPhysicalKinematics(usecTimestampNow()); stepNonPhysicalKinematics(usecTimestampNow());
unlock(); unlock();
// TODO: make all of this harvest stuff into one function: relayOutgoingChanges()
if (numSubsteps > 0) { if (numSubsteps > 0) {
// This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree.
// //
@ -598,34 +613,10 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
return true; return true;
} }
void PhysicsEngine::setAvatarData(AvatarData *avatarData) { void PhysicsEngine::setCharacterController(CharacterController* character) {
if (_characterController) { if (!_characterController) {
bool needsShapeUpdate = _characterController->needsShapeUpdate();
if (needsShapeUpdate) {
lock();
// remove old info
_dynamicsWorld->removeCollisionObject(_characterController->getGhostObject());
_dynamicsWorld->removeAction(_characterController);
// update shape
_characterController->updateShape();
// insert new info
_dynamicsWorld->addCollisionObject(_characterController->getGhostObject(),
btBroadphaseProxy::CharacterFilter,
btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
_dynamicsWorld->addAction(_characterController);
_characterController->reset(_dynamicsWorld);
unlock();
}
} else {
// initialize _characterController
assert(avatarData); // don't pass NULL argument
lock(); lock();
_characterController = new CharacterController(avatarData); _characterController = character;
_dynamicsWorld->addCollisionObject(_characterController->getGhostObject(),
btBroadphaseProxy::CharacterFilter,
btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
_dynamicsWorld->addAction(_characterController);
_characterController->reset(_dynamicsWorld);
unlock(); unlock();
} }
} }

View file

@ -17,9 +17,7 @@
#include <QSet> #include <QSet>
#include <btBulletDynamicsCommon.h> #include <btBulletDynamicsCommon.h>
#include <BulletCollision/CollisionDispatch/btGhostObject.h> #include <BulletCollision/CollisionDispatch/btGhostObject.h>
//#include <BulletCollision/CollisionShapes/btCapsuleShape.h>
#include <AvatarData.h>
#include <EntityItem.h> #include <EntityItem.h>
#include <EntitySimulation.h> #include <EntitySimulation.h>
@ -86,7 +84,7 @@ public:
/// process queue of changed from external sources /// process queue of changed from external sources
void relayIncomingChangesToSimulation(); void relayIncomingChangesToSimulation();
void setAvatarData(AvatarData *avatarData); void setCharacterController(CharacterController* character);
private: private:
/// \param motionState pointer to Object's MotionState /// \param motionState pointer to Object's MotionState

View file

@ -136,28 +136,32 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
} }
break; break;
case SHAPE_TYPE_CONVEX_HULL: { case SHAPE_TYPE_CONVEX_HULL: {
shape = new btConvexHullShape(); auto hull = new btConvexHullShape();
const QVector<QVector<glm::vec3>>& points = info.getPoints(); const QVector<QVector<glm::vec3>>& points = info.getPoints();
foreach (glm::vec3 point, points[0]) { foreach (glm::vec3 point, points[0]) {
btVector3 btPoint(point[0], point[1], point[2]); btVector3 btPoint(point[0], point[1], point[2]);
static_cast<btConvexHullShape*>(shape)->addPoint(btPoint); hull->addPoint(btPoint, false);
} }
hull->recalcLocalAabb();
shape = hull;
} }
break; break;
case SHAPE_TYPE_COMPOUND: { case SHAPE_TYPE_COMPOUND: {
shape = new btCompoundShape(); auto compound = new btCompoundShape();
const QVector<QVector<glm::vec3>>& points = info.getPoints(); const QVector<QVector<glm::vec3>>& points = info.getPoints();
foreach (QVector<glm::vec3> hullPoints, info.getPoints()) { btTransform trans;
trans.setIdentity();
foreach (QVector<glm::vec3> hullPoints, points) {
auto hull = new btConvexHullShape(); auto hull = new btConvexHullShape();
foreach (glm::vec3 point, hullPoints) { foreach (glm::vec3 point, hullPoints) {
btVector3 btPoint(point[0], point[1], point[2]); btVector3 btPoint(point[0], point[1], point[2]);
hull->addPoint(btPoint); hull->addPoint(btPoint, false);
} }
btTransform trans; hull->recalcLocalAabb();
trans.setIdentity(); compound->addChildShape (trans, hull);
static_cast<btCompoundShape*>(shape)->addChildShape (trans, hull);
} }
shape = compound;
} }
break; break;
} }

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QDebug>
#include <glm/gtx/norm.hpp> #include <glm/gtx/norm.hpp>
#include "ShapeInfoUtil.h" #include "ShapeInfoUtil.h"
@ -35,6 +37,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e4f; // 100 m cube const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e4f; // 100 m cube
if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED || diagonal > MAX_SHAPE_DIAGONAL_SQUARED) { if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED || diagonal > MAX_SHAPE_DIAGONAL_SQUARED) {
// qDebug() << "ShapeManager::getShape -- not making shape due to size" << diagonal;
return NULL; return NULL;
} }
DoubleHashKey key = info.getHash(); DoubleHashKey key = info.getHash();

View file

@ -134,7 +134,7 @@ void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radi
void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color, void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color,
float intensity, const glm::quat& orientation, float exponent, float cutoff) { float intensity, const glm::quat& orientation, float exponent, float cutoff) {
int lightID = _pointLights.size() + _spotLights.size() + _globalLights.size(); unsigned int lightID = _pointLights.size() + _spotLights.size() + _globalLights.size();
if (lightID >= _allocatedLights.size()) { if (lightID >= _allocatedLights.size()) {
_allocatedLights.push_back(model::LightPointer(new model::Light())); _allocatedLights.push_back(model::LightPointer(new model::Light()));
} }

View file

@ -21,6 +21,7 @@
#include <gpu/Batch.h> #include <gpu/Batch.h>
#include <gpu/GLBackend.h> #include <gpu/GLBackend.h>
#include <FSTReader.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "TextureCache.h" #include "TextureCache.h"
@ -1770,8 +1771,8 @@ void GeometryCache::renderLine(const glm::vec2& p1, const glm::vec2& p2,
} }
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad, bool block) { QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) {
return getResource(url, fallback, delayLoad, NULL, block).staticCast<NetworkGeometry>(); return getResource(url, fallback, delayLoad, NULL).staticCast<NetworkGeometry>();
} }
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url, QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,

View file

@ -203,8 +203,7 @@ public:
/// Loads geometry from the specified URL. /// Loads geometry from the specified URL.
/// \param fallback a fallback URL to load if the desired one is unavailable /// \param fallback a fallback URL to load if the desired one is unavailable
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
bool delayLoad = false, bool block = true);
protected: protected:

View file

@ -325,6 +325,8 @@ void Model::init() {
_skinTranslucentProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelTranslucentPixel)); _skinTranslucentProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelTranslucentPixel));
makeResult = gpu::Shader::makeProgram(*_skinTranslucentProgram, slotBindings); makeResult = gpu::Shader::makeProgram(*_skinTranslucentProgram, slotBindings);
initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations); initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations);
(void) makeResult; // quiet compiler
} }
} }
@ -1032,12 +1034,22 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
} }
} }
void Model::setCollisionModelURL(const QUrl& url, const QUrl& fallback, bool delayLoad) {
const QSharedPointer<NetworkGeometry> Model::getCollisionGeometry(bool delayLoad)
{
if (_collisionGeometry.isNull() && !_collisionUrl.isEmpty()) {
_collisionGeometry = DependencyManager::get<GeometryCache>()->getGeometry(_collisionUrl, QUrl(), delayLoad);
}
return _collisionGeometry;
}
void Model::setCollisionModelURL(const QUrl& url) {
if (_collisionUrl == url) { if (_collisionUrl == url) {
return; return;
} }
_collisionUrl = url; _collisionUrl = url;
_collisionGeometry = DependencyManager::get<GeometryCache>()->getGeometry(url, fallback, delayLoad); _collisionGeometry = DependencyManager::get<GeometryCache>()->getGeometry(url, QUrl(), true);
} }
bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {

View file

@ -109,7 +109,7 @@ public:
const QUrl& getURL() const { return _url; } const QUrl& getURL() const { return _url; }
// Set the model to use for collisions // Set the model to use for collisions
Q_INVOKABLE void setCollisionModelURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); Q_INVOKABLE void setCollisionModelURL(const QUrl& url);
const QUrl& getCollisionURL() const { return _collisionUrl; } const QUrl& getCollisionURL() const { return _collisionUrl; }
/// Sets the distance parameter used for LOD computations. /// Sets the distance parameter used for LOD computations.
@ -134,7 +134,7 @@ public:
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; } const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
/// Returns a reference to the shared collision geometry. /// Returns a reference to the shared collision geometry.
const QSharedPointer<NetworkGeometry> getCollisionGeometry() {return _collisionGeometry; } const QSharedPointer<NetworkGeometry> getCollisionGeometry(bool delayLoad = true);
/// Returns the number of joint states in the model. /// Returns the number of joint states in the model.
int getJointStateCount() const { return _jointStates.size(); } int getJointStateCount() const { return _jointStates.size(); }

View file

@ -360,10 +360,6 @@ void ScriptEngine::init() {
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT))); globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT)));
globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS))); globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS)));
globalObject().setProperty("AVATAR_MOTION_OBEY_LOCAL_GRAVITY", newVariant(QVariant(AVATAR_MOTION_OBEY_LOCAL_GRAVITY)));
globalObject().setProperty("AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY", newVariant(QVariant(AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY)));
} }
QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {

View file

@ -22,6 +22,8 @@
#include "XMLHttpRequestClass.h" #include "XMLHttpRequestClass.h"
#include "ScriptEngine.h" #include "ScriptEngine.h"
const QString METAVERSE_API_URL = "https://metaverse.highfidelity.com/api/";
Q_DECLARE_METATYPE(QByteArray*) Q_DECLARE_METATYPE(QByteArray*)
XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) :
@ -207,7 +209,7 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a
notImplemented(); notImplemented();
} }
} else { } else {
if (url.toLower().left(33) == "https://metaverse.highfidelity.io/api/") { if (url.toLower().left(METAVERSE_API_URL.length()) == METAVERSE_API_URL) {
AccountManager& accountManager = AccountManager::getInstance(); AccountManager& accountManager = AccountManager::getInstance();
if (accountManager.hasValidAccessToken()) { if (accountManager.hasValidAccessToken()) {

View file

@ -14,6 +14,7 @@
SimpleMovingAverage::SimpleMovingAverage(int numSamplesToAverage) : SimpleMovingAverage::SimpleMovingAverage(int numSamplesToAverage) :
_numSamples(0), _numSamples(0),
_lastEventTimestamp(0),
_average(0.0f), _average(0.0f),
_eventDeltaAverage(0.0f), _eventDeltaAverage(0.0f),
WEIGHTING(1.0f / numSamplesToAverage), WEIGHTING(1.0f / numSamplesToAverage),

View file

@ -2,13 +2,13 @@
rm -f TAGS rm -f TAGS
find . -name *.h -print | while read I find . -name *.h -print | grep -v build-ext |while read I
do do
etags --append "$I" etags --append "$I"
done done
find . -name *.cpp -print | grep -v 'moc_' | while read I find . -name *.cpp -print | grep -v 'moc_' | grep -v build-ext | while read I
do do
etags --append "$I" etags --append "$I"
done done