Merge branch 'master' into oculus_old_renderer

This commit is contained in:
Bradley Austin Davis 2015-03-30 16:36:00 -07:00
commit 5e5bdec88d
126 changed files with 5442 additions and 4203 deletions
assignment-client/src/avatars
cmake/externals/tbb
domain-server
examples
interface
libraries

View file

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

View file

@ -8,8 +8,8 @@ if (ANDROID)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150209oss_src.tgz
URL_MD5 f09c9abe8ec74e6558c1f89cebbe2893
URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_src.tgz
URL_MD5 bf090eaa86cf89ea014b7b462786a440
BUILD_COMMAND ${NDK_BUILD_COMMAND} --directory=jni target=android tbb tbbmalloc arch=arm
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ""
@ -20,19 +20,20 @@ if (ANDROID)
)
else ()
if (APPLE)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150209oss_osx.tgz)
set(DOWNLOAD_MD5 3e683c19792582b61382e0d760ea5db2)
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_osx.tgz)
set(DOWNLOAD_MD5 25a36ebff070ff801760ec658079f6aa)
elseif (WIN32)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150209oss_win.zip)
set(DOWNLOAD_MD5 e19c184f2bb0e944fc5f397f1e34ca84)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_win.zip)
set(DOWNLOAD_MD5 d250d40bb93b255f75bcbb19e976a440)
else ()
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150209oss_lin.tgz)
set(DOWNLOAD_MD5 d9c2a6f7807df364be44a8c3c05e8457)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_lin.tgz)
set(DOWNLOAD_MD5 7830ba2bc62438325fba2ec0c95367a5)
endif ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL ${DOWNLOAD_URL}
URL_MD5 ${DOWNLOAD_MD5}
BUILD_COMMAND ""
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
@ -94,9 +95,15 @@ elseif (UNIX)
endif ()
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}_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")
endif ()

View file

@ -6,7 +6,7 @@
{
"name": "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",
@ -30,7 +30,7 @@
},
{
"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')
// 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){
modal_buttons = {
@ -682,7 +682,7 @@ function chooseFromHighFidelityDomains(clickedButton) {
modal_buttons["success"] = {
label: 'Create new domain',
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." +
@ -708,4 +708,4 @@ function chooseFromHighFidelityDomains(clickedButton) {
title: "Access token required"
})
}
}
}

View file

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

View file

@ -18,4 +18,3 @@ Script.load("lobby.js");
Script.load("notifications.js");
Script.load("look.js");
Script.load("users.js");
Script.load("utilities/LODWarning.js");

View file

@ -76,7 +76,6 @@ var 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_EASE_ON_FOCUS = "Ease Orientation on Focus";
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 modelURLs = [
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx",
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush1.fbx",
HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush6.fbx",
HIFI_PUBLIC_BUCKET + "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"
"Insert the URL to your FBX"
];
var mode = 0;
@ -130,7 +123,7 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", {
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);
marketplaceWindow.setVisible(false);
@ -338,7 +331,11 @@ var toolBar = (function () {
return true;
}
if (browseModelsButton === toolBar.clicked(clickedOverlay)) {
if (marketplaceWindow.url != MARKETPLACE_URL) {
marketplaceWindow.setURL(MARKETPLACE_URL);
}
marketplaceWindow.setVisible(true);
marketplaceWindow.raise();
return true;
}
@ -384,9 +381,7 @@ var toolBar = (function () {
position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_LIGHT_DIMENSIONS), DEFAULT_LIGHT_DIMENSIONS),
dimensions: DEFAULT_LIGHT_DIMENSIONS,
isSpotlight: false,
diffuseColor: { red: 255, green: 255, blue: 255 },
ambientColor: { red: 255, green: 255, blue: 255 },
specularColor: { red: 0, green: 0, blue: 0 },
color: { red: 150, green: 150, blue: 150 },
constantAttenuation: 1,
linearAttenuation: 0,
@ -540,7 +535,7 @@ function mousePressEvent(event) {
mouseHasMovedSincePress = false;
mouseCapturedByTool = false;
if (toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) {
if (propertyMenu.mousePressEvent(event) || toolBar.mousePressEvent(event) || progressDialog.mousePressEvent(event)) {
mouseCapturedByTool = true;
return;
}
@ -549,18 +544,6 @@ function mousePressEvent(event) {
// Event handled; do nothing.
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;
function mouseMoveEvent(event) {
mouseHasMovedSincePress = true;
if (placingEntityID) {
if (!placingEntityID.isKnownID) {
placingEntityID = Entities.identifyEntity(placingEntityID);
@ -592,10 +577,8 @@ function mouseMoveEvent(event) {
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
if (selectionDisplay.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) {
if (selectionDisplay.mouseMoveEvent(event) || propertyMenu.mouseMoveEvent(event) || cameraManager.mouseMoveEvent(event)) {
return;
}
@ -640,7 +623,7 @@ function highlightEntityUnderCursor(position, accurateRay) {
function mouseReleaseEvent(event) {
if (toolBar.mouseReleaseEvent(event)) {
if (propertyMenu.mouseReleaseEvent(event) || toolBar.mouseReleaseEvent(event)) {
return true;
}
if (placingEntityID) {
@ -664,74 +647,88 @@ function mouseReleaseEvent(event) {
}
function mouseClickEvent(event) {
if (!event.isLeftButton || !isActive) {
return;
}
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 (isActive && event.isLeftButton) {
var result = findClickedEntity(event);
if (result === null) {
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 {
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));
propertyMenu.marketplaceID = null;
propertyMenu.updateMenuItemText(showMenuItem, "No marketplace info");
}
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 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" });
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" });
@ -807,7 +804,6 @@ function cleanupModelMenus() {
Menu.removeMenuItem("File", "Import Entities");
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_EASE_ON_FOCUS);
Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
@ -829,11 +825,21 @@ Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(importingSVOTextOverlay);
});
var lastOrientation = null;
var lastPosition = null;
// Do some stuff regularly, like check for placement of various overlays
Script.update.connect(function (deltaTime) {
toolBar.move();
progressDialog.move();
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) {
@ -910,8 +916,8 @@ function handeMenuEvent(menuItem) {
if (!selectionManager.hasSelection()) {
Window.alert("No entities have been selected.");
} else {
var filename = "models__" + Window.location.hostname + "__.svo";
filename = Window.save("Select where to save", filename, "*.svo")
var filename = "entities__" + Window.location.hostname + ".svo.json";
filename = Window.save("Select where to save", filename, "*.json")
if (filename) {
var success = Clipboard.exportEntities(filename, selectionManager.selections);
if (!success) {
@ -923,7 +929,7 @@ function handeMenuEvent(menuItem) {
var importURL;
if (menuItem == "Import Entities") {
importURL = Window.browse("Select models to import", "", "*.svo");
importURL = Window.browse("Select models to import", "", "*.json");
} else {
importURL = Window.prompt("URL of SVO to import", "");
}
@ -1143,6 +1149,12 @@ PropertiesTool = function(opts) {
}
pushCommandForSelections();
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") {
if (data.action == "moveSelectionToGrid") {
if (selectionManager.hasSelection()) {
@ -1216,4 +1228,142 @@ PropertiesTool = function(opts) {
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();

View file

@ -0,0 +1,228 @@
(function() {
this.entityID = null;
this.lightID = 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) {
return JSON.parse(JSON.stringify(object));
}
function didEntityExist(entityID) {
return entityID && entityID.isKnownID;
}
function doesEntityExistNow(entityID) {
return entityID && getTrueID(entityID).isKnownID;
}
function getTrueID(entityID) {
var properties = Entities.getEntityProperties(entityID);
return { id: properties.id, creatorTokenID: properties.creatorTokenID, isKnownID: properties.isKnownID };
}
function getUserData(entityID) {
var properties = Entities.getEntityProperties(entityID);
if (properties.userData) {
return JSON.parse(properties.userData);
} else {
print("Warning: light controller has no user data.");
return null;
}
}
function updateUserData(entityID, userData) {
Entities.editEntity(entityID, { userData: JSON.stringify(userData) });
}
// Download sound if needed
this.maybeDownloadSound = function() {
if (this.sound === null) {
var soundIndex = getUserData(this.entityID).soundIndex;
this.sound = SoundCache.getSound(this.soundURLs[soundIndex]);
}
}
// Play switch sound
this.playSound = function() {
if (this.sound && this.sound.downloaded) {
Audio.playSound(this.sound, {
position: Entities.getEntityProperties(this.entityID).position,
volume: 0.2
});
} else {
print("Warning: Couldn't play sound.");
}
}
// Checks whether the userData is well-formed and updates it if not
this.checkUserData = function() {
var userData = getUserData(this.entityID);
if (!userData) {
userData = DEFAULT_USER_DATA;
} else if (!userData.lightDefaultProperties) {
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) {
var lightProperties = copyObject(userData.lightDefaultProperties);
if (lightProperties) {
var entityProperties = Entities.getEntityProperties(this.entityID);
lightProperties.visible = false;
lightProperties.position = Vec3.sum(entityProperties.position,
Vec3.multiplyQbyV(entityProperties.rotation,
lightProperties.position));
return Entities.addEntity(lightProperties);
} else {
print("Warning: light controller has no default light.");
return null;
}
}
// Tries to find a valid light, creates one otherwise
this.updateLightID = function() {
// Find valid light
if (doesEntityExistNow(this.lightID)) {
return;
}
var userData = getUserData(this.entityID);
if (doesEntityExistNow(userData.lightID)) {
this.lightID = userData.lightID;
return;
}
if (!userData.creatingLight) {
// No valid light, create one
userData.creatingLight = true;
updateUserData(this.entityID, userData);
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.creatingLight = false;
updateUserData(this.entityID, userData);
}
// Moves light entity if the lamp entity moved
this.maybeMoveLight = function() {
var entityProperties = Entities.getEntityProperties(this.entityID);
var lightProperties = Entities.getEntityProperties(this.lightID);
var lightDefaultProperties = getUserData(this.entityID).lightDefaultProperties;
var position = Vec3.sum(entityProperties.position,
Vec3.multiplyQbyV(entityProperties.rotation,
lightDefaultProperties.position));
if (!Vec3.equal(position, lightProperties.position)) {
print("Lamp entity moved, moving light entity as well");
Entities.editEntity(this.lightID, { position: position });
}
}
// Stores light entity relative position in the lamp metadata
this.updateRelativeLightPosition = function() {
if (!doesEntityExistNow(this.lightID)) {
print("Warning: ID invalid, couldn't save relative position.");
return;
}
var userData = getUserData(this.entityID);
var entityProperties = Entities.getEntityProperties(this.entityID);
var lightProperties = Entities.getEntityProperties(this.lightID);
var newProperties = {};
// Copy only meaningful properties (trying to save space in userData here)
for (var key in userData.lightDefaultProperties) {
if (userData.lightDefaultProperties.hasOwnProperty(key)) {
newProperties[key] = lightProperties[key];
}
}
// Compute new relative position
newProperties.position = Quat.multiply(Quat.inverse(entityProperties.rotation),
Vec3.subtract(lightProperties.position,
entityProperties.position));
// inverse "visible" because right after we loaded the properties, the light entity is toggled.
newProperties.visible = !lightProperties.visible;
userData.lightDefaultProperties = copyObject(newProperties);
updateUserData(this.entityID, userData);
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.preOperation(entityID);
};
this.clickReleaseOnEntity = function(entityID, mouseEvent) {
this.preOperation(entityID);
if (mouseEvent.isLeftButton) {
this.updateLightID();
this.maybeMoveLight();
this.toggleLight();
} else if (mouseEvent.isRightButton) {
this.updateRelativeLightPosition();
}
};
})

View file

@ -9,15 +9,20 @@
this.preload = function(entityID) {
teleport = SoundCache.getSound("http://s3.amazonaws.com/hifi-public/birarda/teleport.raw");
var properties = Entities.getEntityProperties(entityID);
portalDestination = properties.userData;
animationURL = properties.modelURL;
print("The portal destination is " + portalDestination);
}
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) {
print("Teleporting to hifi://" + portalDestination);
Window.location = "hifi://" + portalDestination;

View file

@ -0,0 +1,157 @@
//
// makeHouses.js
//
//
// Created by Stojce Slavkovski on March 14, 2015
// Copyright 2015 High Fidelity, Inc.
//
// This sample script that creates house entities based on parameters.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function () {
/** options **/
var numHouses = 100;
var xRange = 300;
var yRange = 300;
var sizeOfTheHouse = {
x: 10,
y: 15,
z: 10
};
var randomizeModels = false;
/**/
var modelUrlPrefix = "http://public.highfidelity.io/load_testing/3-Buildings-2-SanFranciscoHouse-";
var modelurlExt = ".fbx";
var modelVariations = 100;
var houses = [];
function addHouseAt(position, rotation) {
// get house model
var modelNumber = randomizeModels ?
1 + Math.floor(Math.random() * (modelVariations - 1)) :
(houses.length + 1) % modelVariations;
if (modelNumber == 0) {
modelNumber = modelVariations;
}
var modelUrl = modelUrlPrefix + (modelNumber + "") + modelurlExt;
print("Model ID:" + modelNumber);
print("Model URL:" + modelUrl);
var properties = {
type: "Model",
position: position,
rotation: rotation,
dimensions: sizeOfTheHouse,
modelURL: modelUrl
};
return Entities.addEntity(properties);
}
// calculate initial position
var posX = MyAvatar.position.x - (xRange / 2);
var measures = calculateParcels(numHouses, xRange, yRange);
var dd = 0;
// avatar facing rotation
var rotEven = Quat.fromPitchYawRollDegrees(0, 270.0 + MyAvatar.bodyYaw, 0.0);
// avatar opposite rotation
var rotOdd = Quat.fromPitchYawRollDegrees(0, 90.0 + MyAvatar.bodyYaw, 0.0);
var housePos = Vec3.sum(MyAvatar.position, Quat.getFront(Camera.getOrientation()));
for (var j = 0; j < measures.rows; j++) {
var posX1 = 0 - (xRange / 2);
dd += measures.parcelLength;
for (var i = 0; i < measures.cols; i++) {
// skip reminder of houses
if (houses.length > numHouses) {
break;
}
var posShift = {
x: posX1,
y: 0,
z: dd
};
print("House nr.:" + (houses.length + 1));
houses.push(
addHouseAt(Vec3.sum(housePos, posShift), (j % 2 == 0) ? rotEven : rotOdd)
);
posX1 += measures.parcelWidth;
}
}
// calculate rows and columns in area, and dimension of single parcel
function calculateParcels(items, areaWidth, areaLength) {
var idealSize = Math.min(Math.sqrt(areaWidth * areaLength / items), areaWidth, areaLength);
var baseWidth = Math.min(Math.floor(areaWidth / idealSize), items);
var baseLength = Math.min(Math.floor(areaLength / idealSize), items);
var sirRows = baseWidth;
var sirCols = Math.ceil(items / sirRows);
var sirW = areaWidth / sirRows;
var sirL = areaLength / sirCols;
var visCols = baseLength;
var visRows = Math.ceil(items / visCols);
var visW = areaWidth / visRows;
var visL = areaLength / visCols;
var rows = 0;
var cols = 0;
var parcelWidth = 0;
var parcelLength = 0;
if (Math.min(sirW, sirL) > Math.min(visW, visL)) {
rows = sirRows;
cols = sirCols;
parcelWidth = sirW;
parcelLength = sirL;
} else {
rows = visRows;
cols = visCols;
parcelWidth = visW;
parcelLength = visL;
}
print("rows:" + rows);
print("cols:" + cols);
print("parcelWidth:" + parcelWidth);
print("parcelLength:" + parcelLength);
return {
rows: rows,
cols: cols,
parcelWidth: parcelWidth,
parcelLength: parcelLength
};
}
function cleanup() {
while (houses.length > 0) {
if (!houses[0].isKnownID) {
houses[0] = Entities.identifyEntity(houses[0]);
}
Entities.deleteEntity(houses.shift());
}
}
Script.scriptEnding.connect(cleanup);
})();

View file

@ -13,5 +13,4 @@ Script.load("progress.js");
Script.load("lobby.js");
Script.load("notifications.js");
Script.load("controllers/oculus/goTo.js");
Script.load("utilities/LODWarning.js");
//Script.load("scripts.js"); // Not created yet

View file

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

View file

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

View file

@ -158,7 +158,7 @@ var places = {};
function changeLobbyTextures() {
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();
places = JSON.parse(req.responseText).data.places;

View file

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

View file

@ -43,7 +43,6 @@
// after that we will send it to createNotification(text).
// If the message is 42 chars or less you should bypass wordWrap() and call createNotification() directly.
// To add a keypress driven notification:
//
// 1. Add a key to the keyPressEvent(key).
@ -85,16 +84,21 @@ var PLAY_NOTIFICATION_SOUNDS_MENU_ITEM = "Play Notification Sounds";
var NOTIFICATION_MENU_ITEM_POST = " Notifications";
var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds";
var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_";
var lodTextID = false;
var NotificationType = {
UNKNOWN: 0,
MUTE_TOGGLE: 1,
SNAPSHOT: 2,
WINDOW_RESIZE: 3,
LOD_WARNING: 4,
CONNECTION_REFUSED: 5,
properties: [
{ text: "Mute Toggle" },
{ text: "Snapshot" },
{ text: "Window Resize" }
{ text: "Window Resize" },
{ text: "Level of Detail" },
{ text: "Connection Refused" }
],
getTypeFromMenuItem: function(menuItemName) {
if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) {
@ -143,6 +147,10 @@ function createArrays(notice, button, createTime, height, myAlpha) {
// This handles the final dismissal of a notification after fading
function dismiss(firstNoteOut, firstButOut, firstOut) {
if (firstNoteOut == lodTextID) {
lodTextID = false;
}
Overlays.deleteOverlay(firstNoteOut);
Overlays.deleteOverlay(firstButOut);
notifications.splice(firstOut, 1);
@ -261,7 +269,8 @@ function notify(notice, button, height) {
height: noticeHeight
});
} else {
notifications.push((Overlays.addOverlay("text", notice)));
var notificationText = Overlays.addOverlay("text", notice);
notifications.push((notificationText));
buttons.push((Overlays.addOverlay("image", button)));
}
@ -272,6 +281,7 @@ function notify(notice, button, height) {
last = notifications.length - 1;
createArrays(notifications[last], buttons[last], times[last], heights[last], myAlpha[last]);
fadeIn(notifications[last], buttons[last]);
return notificationText;
}
// This function creates and sizes the overlays
@ -331,11 +341,15 @@ function createNotification(text, notificationType) {
randomSounds.playRandom();
}
notify(noticeProperties, buttonProperties, height);
return notify(noticeProperties, buttonProperties, height);
}
function deleteNotification(index) {
Overlays.deleteOverlay(notifications[index]);
var notificationTextID = notifications[index];
if (notificationTextID == lodTextID) {
lodTextID = false;
}
Overlays.deleteOverlay(notificationTextID);
Overlays.deleteOverlay(buttons[index]);
notifications.splice(index, 1);
buttons.splice(index, 1);
@ -489,6 +503,10 @@ function onMuteStateChanged() {
createNotification(muteString, NotificationType.MUTE_TOGGLE);
}
function onDomainConnectionRefused(reason) {
createNotification("Connection refused: " + reason, NotificationType.CONNECTION_REFUSED );
}
// handles mouse clicks on buttons
function mousePressEvent(event) {
var pickRay,
@ -575,6 +593,20 @@ function menuItemEvent(menuItem) {
}
}
LODManager.LODDecreased.connect(function() {
var warningText = "\n"
+ "Due to the complexity of the content, the \n"
+ "level of detail has been decreased."
+ "You can now see: \n"
+ LODManager.getLODFeedbackText();
if (lodTextID == false) {
lodTextID = createNotification(warningText, NotificationType.LOD_WARNING);
} else {
Overlays.editOverlay(lodTextID, { text: warningText });
}
});
AudioDevice.muteToggled.connect(onMuteStateChanged);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.mousePressEvent.connect(mousePressEvent);
@ -582,5 +614,6 @@ Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);
Menu.menuItemEvent.connect(menuItemEvent);
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
setup();

View file

@ -11,7 +11,7 @@
var usersWindow = (function () {
var WINDOW_WIDTH_2D = 150,
var WINDOW_WIDTH_2D = 160,
WINDOW_MARGIN_2D = 12,
WINDOW_FONT_2D = { size: 12 },
WINDOW_FOREGROUND_COLOR_2D = { red: 240, green: 240, blue: 240 },
@ -22,6 +22,17 @@ var usersWindow = (function () {
WINDOW_BACKGROUND_ALPHA_2D = 0.7,
windowPane2D,
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
visibilityHeading2D,
VISIBILITY_RADIO_SPACE = 16,
@ -33,8 +44,10 @@ var usersWindow = (function () {
usersOnline, // Raw users data
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
usersRequest,
processUsers,
@ -54,6 +67,15 @@ var usersWindow = (function () {
isVisible = true,
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/",
RADIO_BUTTON_SVG = HIFI_PUBLIC_BUCKET + "images/radio-button.svg",
@ -62,10 +84,32 @@ var usersWindow = (function () {
radioButtonDiameter;
function calculateWindowHeight() {
var AUDIO_METER_HEIGHT = 52,
MIRROR_HEIGHT = 220,
nonUsersHeight,
maxWindowHeight;
// Reserve 5 lines for window heading plus visibility heading and controls
// Subtract windowLineSpacing for both end of user list and end of controls
windowHeight = (linesOfUsers.length > 0 ? linesOfUsers.length + 5 : 5) * windowLineHeight
- 2 * windowLineSpacing + VISIBILITY_SPACER_2D + 2 * WINDOW_MARGIN_2D;
nonUsersHeight = 5 * windowLineHeight - 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() {
@ -78,6 +122,16 @@ var usersWindow = (function () {
Overlays.editOverlay(windowHeading2D, {
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, {
y: viewportHeight - 4 * windowLineHeight + windowLineSpacing - WINDOW_MARGIN_2D
});
@ -104,57 +158,59 @@ var usersWindow = (function () {
function updateUsersDisplay() {
var displayText = "",
myUsername,
user,
userText,
textWidth,
maxTextWidth,
ellipsisWidth,
reducedTextWidth,
i;
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;
}
textWidth = Overlays.textSize(windowPane2D, userText).width;
maxTextWidth = WINDOW_WIDTH_2D - (isUsingScrollbars ? SCROLLBAR_BACKGROUND_WIDTH_2D : 0) - 2 * WINDOW_MARGIN_2D;
ellipsisWidth = Overlays.textSize(windowPane2D, "...").width;
reducedTextWidth = maxTextWidth - ellipsisWidth;
maxTextWidth = WINDOW_WIDTH_2D - 2 * WINDOW_MARGIN_2D;
if (textWidth > maxTextWidth) {
// Trim and append "..." to fit window width
maxTextWidth = maxTextWidth - Overlays.textSize(windowPane2D, "...").width;
while (textWidth > maxTextWidth) {
userText = userText.slice(0, -1);
textWidth = Overlays.textSize(windowPane2D, userText).width;
}
userText += "...";
for (i = 0; i < numUsersToDisplay; i += 1) {
user = usersOnline[linesOfUsers[firstUserToDisplay + i]];
userText = user.text;
textWidth = user.textWidth;
if (textWidth > maxTextWidth) {
// Trim and append "..." to fit window width
maxTextWidth = maxTextWidth - Overlays.textSize(windowPane2D, "...").width;
while (textWidth > reducedTextWidth) {
userText = userText.slice(0, -1);
textWidth = Overlays.textSize(windowPane2D, userText).width;
}
usersOnline[i].textWidth = textWidth;
linesOfUsers.push(i);
displayText += "\n" + userText;
userText += "...";
}
displayText += "\n" + userText;
}
displayText = displayText.slice(1); // Remove leading "\n".
calculateWindowHeight();
Overlays.editOverlay(windowPane2D, {
y: viewportHeight - windowHeight,
height: windowHeight,
text: displayText
});
Overlays.editOverlay(windowHeading2D, {
y: viewportHeight - windowHeight + WINDOW_MARGIN_2D,
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();
}
@ -168,7 +224,11 @@ var usersWindow = (function () {
}
processUsers = function () {
var response;
var response,
myUsername,
user,
userText,
i;
if (usersRequest.readyState === usersRequest.DONE) {
if (usersRequest.status === 200) {
@ -185,7 +245,26 @@ var usersWindow = (function () {
}
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();
} else {
print("Error: Request for users status returned " + usersRequest.status + " " + usersRequest.statusText);
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(windowHeading2D, { visible: isVisible });
Overlays.editOverlay(scrollbarBackground2D, { visible: isVisible && isUsingScrollbars });
Overlays.editOverlay(scrollbarBar2D, { visible: isVisible && isUsingScrollbars });
Overlays.editOverlay(visibilityHeading2D, { visible: isVisible });
for (i = 0; i < visibilityControls2D.length; i += 1) {
Overlays.editOverlay(visibilityControls2D[i].radioOverlay, { visible: isVisible });
@ -247,8 +328,10 @@ var usersWindow = (function () {
minY,
maxY,
lineClicked,
userClicked,
i,
visibilityChanged;
visibilityChanged,
delta;
if (!isVisible) {
return;
@ -261,7 +344,7 @@ var usersWindow = (function () {
overlayX = event.x - WINDOW_MARGIN_2D;
overlayY = event.y - viewportHeight + windowHeight - WINDOW_MARGIN_2D - windowLineHeight;
numLinesBefore = Math.floor(overlayY / windowLineHeight);
numLinesBefore = Math.round(overlayY / windowLineHeight);
minY = numLinesBefore * windowLineHeight;
maxY = minY + windowTextHeight;
@ -270,10 +353,12 @@ var usersWindow = (function () {
lineClicked = numLinesBefore;
}
if (0 <= lineClicked && lineClicked < linesOfUsers.length
&& 0 <= overlayX && overlayX <= usersOnline[linesOfUsers[lineClicked]].textWidth) {
//print("Go to " + usersOnline[linesOfUsers[lineClicked]].username);
location.goToUser(usersOnline[linesOfUsers[lineClicked]].username);
userClicked = firstUserToDisplay + lineClicked;
if (0 <= userClicked && userClicked < linesOfUsers.length
&& 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();
}
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() {
var oldViewportHeight = viewportHeight;
var oldViewportHeight = viewportHeight,
oldIsMirrorDisplay = isMirrorDisplay,
oldIsFullscreenMirror = isFullscreenMirror,
MIRROR_MENU_ITEM = "Mirror",
FULLSCREEN_MIRROR_MENU_ITEM = "Fullscreen Mirror";
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();
}
}
@ -314,10 +460,10 @@ var usersWindow = (function () {
radioButtonDiameter = RADIO_BUTTON_DISPLAY_SCALE * windowTextHeight;
Overlays.deleteOverlay(textSizeOverlay);
calculateWindowHeight();
viewportHeight = Controller.getViewportDimensions().y;
calculateWindowHeight();
windowPane2D = Overlays.addOverlay("text", {
x: 0,
y: viewportHeight, // Start up off-screen
@ -349,6 +495,36 @@ var usersWindow = (function () {
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", {
x: WINDOW_MARGIN_2D,
y: viewportHeight,
@ -390,7 +566,7 @@ var usersWindow = (function () {
textOverlay: Overlays.addOverlay("text", {
x: WINDOW_MARGIN_2D,
y: viewportHeight,
width: WINDOW_WIDTH_2D - 2 * WINDOW_MARGIN_2D,
width: WINDOW_WIDTH_2D - SCROLLBAR_BACKGROUND_WIDTH_2D - 2 * WINDOW_MARGIN_2D,
height: windowTextHeight,
topMargin: 0,
leftMargin: VISIBILITY_RADIO_SPACE,
@ -429,6 +605,8 @@ var usersWindow = (function () {
updateVisibilityControls();
Controller.mousePressEvent.connect(onMousePressEvent);
Controller.mouseMoveEvent.connect(onMouseMoveEvent);
Controller.mouseReleaseEvent.connect(onMouseReleaseEvent);
Menu.addMenuItem({
menuName: MENU_NAME,
@ -456,6 +634,8 @@ var usersWindow = (function () {
Script.clearTimeout(usersTimer);
Overlays.deleteOverlay(windowPane2D);
Overlays.deleteOverlay(windowHeading2D);
Overlays.deleteOverlay(scrollbarBackground2D);
Overlays.deleteOverlay(scrollbarBar2D);
Overlays.deleteOverlay(visibilityHeading2D);
for (i = 0; i <= visibilityControls2D.length; i += 1) {
Overlays.deleteOverlay(visibilityControls2D[i].textOverlay);

View file

@ -1,115 +0,0 @@
// LODWarning.js
// examples
//
// Created by Brad Hefta-Gaub on 3/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// This script will display a warning when the LOD is adjusted to do scene complexity.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var DISPLAY_WARNING_FOR = 3; // in seconds
var DISTANCE_FROM_CAMERA = 2;
var SHOW_LOD_UP_MESSAGE = false; // By default we only display the LOD message when reducing LOD
var warningIsVisible = false; // initially the warning is hidden
var warningShownAt = 0;
var billboardPosition = Vec3.sum(Camera.getPosition(),
Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation())));
var warningOverlay = Overlays.addOverlay("text3d", {
position: billboardPosition,
dimensions: { x: 2, y: 1.25 },
width: 2,
height: 1.25,
backgroundColor: { red: 0, green: 0, blue: 0 },
color: { red: 255, green: 255, blue: 255},
topMargin: 0.1,
leftMargin: 0.1,
lineHeight: 0.07,
text: "",
alpha: 0.5,
backgroundAlpha: 0.7,
isFacingAvatar: true,
visible: warningIsVisible,
});
// Handle moving the billboard to remain in front of the camera
var billboardNeedsMoving = false;
Script.update.connect(function() {
if (warningIsVisible) {
var bestBillboardPosition = Vec3.sum(Camera.getPosition(),
Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation())));
var MAX_DISTANCE = 0.5;
var CLOSE_ENOUGH = 0.01;
if (!billboardNeedsMoving && Vec3.distance(bestBillboardPosition, billboardPosition) > MAX_DISTANCE) {
billboardNeedsMoving = true;
}
if (billboardNeedsMoving && Vec3.distance(bestBillboardPosition, billboardPosition) <= CLOSE_ENOUGH) {
billboardNeedsMoving = false;
}
if (billboardNeedsMoving) {
// slurp the billboard to the best location
moveVector = Vec3.multiply(0.05, Vec3.subtract(bestBillboardPosition, billboardPosition));
billboardPosition = Vec3.sum(billboardPosition, moveVector);
Overlays.editOverlay(warningOverlay, { position: billboardPosition });
}
var now = new Date();
var sinceWarningShown = now - warningShownAt;
if (sinceWarningShown > 1000 * DISPLAY_WARNING_FOR) {
warningIsVisible = false;
Overlays.editOverlay(warningOverlay, { visible: warningIsVisible });
}
}
});
LODManager.LODIncreased.connect(function() {
if (SHOW_LOD_UP_MESSAGE) {
// if the warning wasn't visible, then move it before showing it.
if (!warningIsVisible) {
billboardPosition = Vec3.sum(Camera.getPosition(),
Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation())));
Overlays.editOverlay(warningOverlay, { position: billboardPosition });
}
warningShownAt = new Date();
warningIsVisible = true;
warningText = "Level of detail has been increased. \n"
+ "You can now see: \n"
+ LODManager.getLODFeedbackText();
Overlays.editOverlay(warningOverlay, { visible: warningIsVisible, text: warningText });
}
});
LODManager.LODDecreased.connect(function() {
// if the warning wasn't visible, then move it before showing it.
if (!warningIsVisible) {
billboardPosition = Vec3.sum(Camera.getPosition(),
Vec3.multiply(DISTANCE_FROM_CAMERA, Quat.getFront(Camera.getOrientation())));
Overlays.editOverlay(warningOverlay, { position: billboardPosition });
}
warningShownAt = new Date();
warningIsVisible = true;
warningText = "\n"
+ "Due to the complexity of the content, the \n"
+ "level of detail has been decreased. \n"
+ "You can now see: \n"
+ LODManager.getLODFeedbackText();
Overlays.editOverlay(warningOverlay, { visible: warningIsVisible, text: warningText });
});
Script.scriptEnding.connect(function() {
Overlays.deleteOverlay(warningOverlay);
});

View file

@ -2,7 +2,7 @@
Instructions for adding the Sixense driver to Interface
Andrzej Kapolka, November 18, 2013
1. Copy the Sixense sdk folders (lib, include) into the interface/external/Sixense folder. This readme.txt should be there as well.
1. Copy the Sixense sdk folders (bin, include, lib, and samples) into the interface/external/Sixense folder. This readme.txt should be there as well.
You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects).
If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sixense' that contains the folders mentioned above.

File diff suppressed because it is too large Load diff

After

(image error) Size: 197 KiB

File diff suppressed because it is too large Load diff

Before

(image error) Size: 193 KiB

View file

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

View file

@ -297,7 +297,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_lastSendDownstreamAudioStats(usecTimestampNow()),
_isVSyncOn(true),
_aboutToQuit(false),
_notifiedPacketVersionMismatchThisDomain(false)
_notifiedPacketVersionMismatchThisDomain(false),
_domainConnectionRefusals(QList<QString>())
{
#ifdef Q_OS_WIN
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
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(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams);
@ -382,6 +390,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
connect(locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
// if we get a domain change, immediately attempt update location in metaverse server
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain,
discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);
connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);
@ -781,7 +793,7 @@ void Application::paintGL() {
DependencyManager::get<GlowEffect>()->render();
if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
{
PerformanceTimer perfTimer("renderOverlay");
_applicationOverlay.renderOverlay(true);
_applicationOverlay.displayOverlayTexture();
@ -979,12 +991,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
resetSensors();
break;
case Qt::Key_G:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::ObeyEnvironmentalGravity);
}
break;
case Qt::Key_A:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::Atmosphere);
@ -1119,13 +1125,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror);
}
break;
case Qt::Key_Slash:
Menu::getInstance()->triggerOption(MenuOption::UserInterface);
break;
case Qt::Key_P:
Menu::getInstance()->triggerOption(MenuOption::FirstPerson);
break;
case Qt::Key_Percent:
case Qt::Key_Slash:
Menu::getInstance()->triggerOption(MenuOption::Stats);
break;
case Qt::Key_Plus:
@ -1160,10 +1163,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
}
case Qt::Key_Comma: {
_myAvatar->togglePhysicsEnabled();
}
default:
event->ignore();
break;
@ -1561,6 +1560,9 @@ void Application::idle() {
idleTimer->start(2);
}
}
// check for any requested background downloads.
emit checkBackgroundDownloads();
}
void Application::setFullscreen(bool fullscreen) {
@ -1747,7 +1749,7 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
exportTree.addEntity(entityItem->getEntityItemID(), properties);
}
exportTree.writeToSVOFile(filename.toLocal8Bit().constData());
exportTree.writeToJSONFile(filename.toLocal8Bit().constData());
// restore the main window's active state
_window->activateWindow();
@ -1782,6 +1784,7 @@ bool Application::exportEntities(const QString& filename, float x, float y, floa
void Application::loadSettings() {
DependencyManager::get<AudioClient>()->loadSettings();
DependencyManager::get<LODManager>()->loadSettings();
Menu::getInstance()->loadSettings();
_myAvatar->loadData();
@ -1789,6 +1792,7 @@ void Application::loadSettings() {
void Application::saveSettings() {
DependencyManager::get<AudioClient>()->saveSettings();
DependencyManager::get<LODManager>()->saveSettings();
Menu::getInstance()->saveSettings();
_myAvatar->saveData();
@ -1899,8 +1903,6 @@ void Application::init() {
_physicsEngine.init(&_entityEditSender);
_physicsEngine.setAvatarData(_myAvatar);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
connect(&_physicsEngine, &EntitySimulation::entityCollisionWithEntity,
@ -1965,7 +1967,7 @@ bool Application::isLookingAtMyAvatar(Avatar* avatar) {
void Application::updateLOD() {
PerformanceTimer perfTimer("LOD");
// adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) {
if (!isThrottleRendering()) {
DependencyManager::get<LODManager>()->autoAdjustLOD(_fps);
} else {
DependencyManager::get<LODManager>()->resetLODAdjust();
@ -3010,8 +3012,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
_nodeBoundsDisplay.draw();
// Render the world box
if (theCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats) &&
Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) {
if (theCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) {
PerformanceTimer perfTimer("worldBox");
renderWorldBox();
}
@ -3131,7 +3132,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
bool eyeRelativeCamera = false;
// bool eyeRelativeCamera = false;
if (billboard) {
_mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees
_mirrorCamera.setPosition(_myAvatar->getPosition() +
@ -3284,6 +3285,14 @@ void Application::clearDomainOctreeDetails() {
void Application::domainChanged(const QString& domainHostname) {
updateWindowTitle();
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) {
@ -3291,11 +3300,6 @@ void Application::connectedToDomain(const QString& hostname) {
const QUuid& domainID = DependencyManager::get<NodeList>()->getDomainHandler().getUUID();
if (accountManager.isLoggedIn() && !domainID.isNull()) {
// update our data-server with the domain-server we're logged in with
QString domainPutJsonString = "{\"location\":{\"domain_id\":\"" + uuidStringWithoutCurlyBraces(domainID) + "\"}}";
accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), domainPutJsonString.toUtf8());
_notifiedPacketVersionMismatchThisDomain = false;
}
}
@ -3595,6 +3599,7 @@ void Application::initializeAcceptedFiles() {
if (_acceptedExtensions.size() == 0) {
_acceptedExtensions[SNAPSHOT_EXTENSION] = &Application::acceptSnapshot;
_acceptedExtensions[SVO_EXTENSION] = &Application::importSVOFromURL;
_acceptedExtensions[SVO_JSON_EXTENSION] = &Application::importSVOFromURL;
_acceptedExtensions[JS_EXTENSION] = &Application::askToLoadScript;
_acceptedExtensions[FST_EXTENSION] = &Application::askToSetAvatarUrl;
}
@ -4203,7 +4208,7 @@ void Application::checkSkeleton() {
_myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL);
_myAvatar->sendIdentityPacket();
} else {
_myAvatar->updateLocalAABox();
_physicsEngine.setAvatarData(_myAvatar);
_myAvatar->updateCharacterController();
_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 SVO_EXTENSION = ".svo";
static const QString SVO_JSON_EXTENSION = ".svo.json";
static const QString JS_EXTENSION = ".js";
static const QString FST_EXTENSION = ".fst";
@ -113,7 +114,7 @@ static const float MIRROR_FIELD_OF_VIEW = 30.0f;
static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND;
static const QString INFO_HELP_PATH = "html/interface-welcome.html";
static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-entities-commands.html";
static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";
#ifdef Q_OS_WIN
static const UINT UWM_IDENTIFY_INSTANCES =
@ -332,6 +333,9 @@ signals:
void svoImportRequested(const QString& url);
void checkBackgroundDownloads();
void domainConnectionRefused(const QString& reason);
public slots:
void domainChanged(const QString& domainHostname);
void updateWindowTitle();
@ -382,6 +386,8 @@ public slots:
void setActiveFaceTracker();
void domainConnectionDenied(const QString& reason);
private slots:
void clearDomainOctreeDetails();
void checkFPS();
@ -606,6 +612,8 @@ private:
int _menuBarHeight;
QHash<QString, AcceptURLMethod> _acceptedExtensions;
QList<QString> _domainConnectionRefusals;
};
#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
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.";
application->domainConnectionDenied(reason);
AccountManager::getInstance().checkAndSignalForAccessToken();
break;
}

View file

@ -30,8 +30,9 @@ DiscoverabilityManager::DiscoverabilityManager() :
const QString API_USER_LOCATION_PATH = "/api/v1/user/location";
void DiscoverabilityManager::updateLocation() {
AccountManager& accountManager = AccountManager::getInstance();
if (_mode.get() != Discoverability::None) {
AccountManager& accountManager = AccountManager::getInstance();
auto addressManager = DependencyManager::get<AddressManager>();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
@ -61,17 +62,25 @@ void DiscoverabilityManager::updateLocation() {
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);
accountManager.authenticatedRequest(API_USER_LOCATION_PATH, QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), QJsonDocument(rootObject).toJson());
accountManager.sendRequest(API_USER_LOCATION_PATH, AccountManagerAuth::Required,
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() {
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) {

View file

@ -17,96 +17,165 @@
#include "LODManager.h"
Setting::Handle<bool> automaticAvatarLOD("automaticAvatarLOD", true);
Setting::Handle<float> avatarLODDecreaseFPS("avatarLODDecreaseFPS", DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS);
Setting::Handle<float> avatarLODIncreaseFPS("avatarLODIncreaseFPS", ADJUST_LOD_UP_FPS);
Setting::Handle<float> avatarLODDistanceMultiplier("avatarLODDistanceMultiplier",
DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER);
Setting::Handle<int> boundaryLevelAdjust("boundaryLevelAdjust", 0);
Setting::Handle<float> octreeSizeScale("octreeSizeScale", DEFAULT_OCTREE_SIZE_SCALE);
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
LODManager::LODManager() {
calculateAvatarLODDistanceMultiplier();
}
float LODManager::getLODDecreaseFPS() {
if (Application::getInstance()->isHMDMode()) {
return getHMDLODDecreaseFPS();
}
return getDesktopLODDecreaseFPS();
}
float LODManager::getLODIncreaseFPS() {
if (Application::getInstance()->isHMDMode()) {
return getHMDLODIncreaseFPS();
}
return getDesktopLODIncreaseFPS();
}
void LODManager::autoAdjustLOD(float currentFPS) {
// 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
// our moving average with simulated good data
const int IGNORE_THESE_SAMPLES = 100;
const float ASSUMED_FPS = 60.0f;
if (_fpsAverage.getSampleCount() < IGNORE_THESE_SAMPLES) {
if (_fpsAverageUpWindow.getSampleCount() < IGNORE_THESE_SAMPLES) {
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();
const quint64 ADJUST_AVATAR_LOD_DOWN_DELAY = 1000 * 1000;
if (_automaticAvatarLOD) {
if (_fastFPSAverage.getAverage() < _avatarLODDecreaseFPS) {
if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) {
// attempt to lower the detail in proportion to the fps difference
float targetFps = (_avatarLODDecreaseFPS + _avatarLODIncreaseFPS) * 0.5f;
float averageFps = _fastFPSAverage.getAverage();
const float MAXIMUM_MULTIPLIER_SCALE = 2.0f;
_avatarLODDistanceMultiplier = qMin(MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, _avatarLODDistanceMultiplier *
(averageFps < EPSILON ? MAXIMUM_MULTIPLIER_SCALE :
qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps)));
_lastAvatarDetailDrop = now;
}
} else if (_fastFPSAverage.getAverage() > _avatarLODIncreaseFPS) {
// let the detail level creep slowly upwards
const float DISTANCE_DECREASE_RATE = 0.05f;
_avatarLODDistanceMultiplier = qMax(MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER,
_avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE);
}
}
bool changed = false;
quint64 elapsed = now - _lastAdjust;
if (elapsed > ADJUST_LOD_DOWN_DELAY && _fpsAverage.getAverage() < ADJUST_LOD_DOWN_FPS
&& _octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale *= ADJUST_LOD_DOWN_BY;
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
}
changed = true;
_lastAdjust = now;
qDebug() << "adjusting LOD down... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage()
<< "_octreeSizeScale=" << _octreeSizeScale;
emit LODDecreased();
}
if (elapsed > ADJUST_LOD_UP_DELAY && _fpsAverage.getAverage() > ADJUST_LOD_UP_FPS
&& _octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
_octreeSizeScale *= ADJUST_LOD_UP_BY;
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
}
changed = true;
_lastAdjust = now;
qDebug() << "adjusting LOD up... average fps for last approximately 5 seconds=" << _fpsAverage.getAverage()
<< "_octreeSizeScale=" << _octreeSizeScale;
emit LODIncreased();
}
bool changed = false;
quint64 elapsedSinceDownShift = now - _lastDownShift;
quint64 elapsedSinceUpShift = now - _lastUpShift;
quint64 lastStableOrUpshift = glm::max(_lastUpShift, _lastStable);
quint64 elapsedSinceStableOrUpShift = now - lastStableOrUpshift;
if (changed) {
_shouldRenderTableNeedsRebuilding = true;
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
if (lodToolsDialog) {
lodToolsDialog->reloadSliders();
if (_automaticLODAdjust) {
// 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
if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale *= ADJUST_LOD_DOWN_BY;
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
}
changed = true;
}
if (changed) {
if (_isDownshifting) {
// subsequent downshift
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();
}
} else {
// LOD Upward adjustment
if (elapsedSinceUpShift > UP_SHIFT_ELPASED) {
if (_fpsAverageUpWindow.getAverage() > getLODIncreaseFPS()) {
// Octee items... stepwise adjustment
if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
} else {
_octreeSizeScale *= ADJUST_LOD_UP_BY;
}
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
}
changed = true;
}
}
if (changed) {
qDebug() << "adjusting LOD UP... average fps for last "<< UP_SHIFT_WINDOW_IN_SECS <<"seconds was "
<< _fpsAverageUpWindow.getAverage()
<< "upshift point is:" << getLODIncreaseFPS()
<< "elapsedSinceUpShift:" << elapsedSinceUpShift
<< " NEW _octreeSizeScale=" << _octreeSizeScale;
_lastUpShift = now;
_isDownshifting = false;
emit LODIncreased();
}
}
}
if (changed) {
calculateAvatarLODDistanceMultiplier();
_shouldRenderTableNeedsRebuilding = true;
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
if (lodToolsDialog) {
lodToolsDialog->reloadSliders();
}
}
}
}
void LODManager::resetLODAdjust() {
_fpsAverage.reset();
_fastFPSAverage.reset();
_lastAvatarDetailDrop = _lastAdjust = usecTimestampNow();
_fpsAverageStartWindow.reset();
_fpsAverageDownWindow.reset();
_fpsAverageUpWindow.reset();
_lastUpShift = _lastDownShift = usecTimestampNow();
_isDownshifting = false;
}
QString LODManager::getLODFeedbackText() {
@ -116,29 +185,33 @@ QString LODManager::getLODFeedbackText() {
switch (boundaryLevelAdjust) {
case 0: {
granularityFeedback = QString("at standard granularity.");
granularityFeedback = QString(".");
} break;
case 1: {
granularityFeedback = QString("at half of standard granularity.");
granularityFeedback = QString(" at half of standard granularity.");
} break;
case 2: {
granularityFeedback = QString("at a third of standard granularity.");
granularityFeedback = QString(" at a third of standard granularity.");
} break;
default: {
granularityFeedback = QString("at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1);
granularityFeedback = QString(" at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1);
} break;
}
// distance feedback
float octreeSizeScale = getOctreeSizeScale();
float relativeToDefault = octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE;
int relativeToTwentyTwenty = 20 / relativeToDefault;
QString result;
if (relativeToDefault > 1.01) {
result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback);
result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',2).arg(granularityFeedback);
} else if (relativeToDefault > 0.99) {
result = QString("the default distance %1").arg(granularityFeedback);
result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback);
} else if (relativeToDefault > 0.01) {
result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
} else {
result = QString("%1 of default %2").arg(relativeToDefault,8,'f',3).arg(granularityFeedback);
result = QString("%2 of default distance for average vision%3").arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
}
return result;
}
@ -184,9 +257,14 @@ bool LODManager::shouldRenderMesh(float largestDimension, float distanceToCamera
void LODManager::setOctreeSizeScale(float sizeScale) {
_octreeSizeScale = sizeScale;
calculateAvatarLODDistanceMultiplier();
_shouldRenderTableNeedsRebuilding = true;
}
void LODManager::calculateAvatarLODDistanceMultiplier() {
_avatarLODDistanceMultiplier = AVATAR_TO_ENTITY_RATIO / (_octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE);
}
void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
_boundaryLevelAdjust = boundaryLevelAdjust;
_shouldRenderTableNeedsRebuilding = true;
@ -194,21 +272,13 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
void LODManager::loadSettings() {
setAutomaticAvatarLOD(automaticAvatarLOD.get());
setAvatarLODDecreaseFPS(avatarLODDecreaseFPS.get());
setAvatarLODIncreaseFPS(avatarLODIncreaseFPS.get());
setAvatarLODDistanceMultiplier(avatarLODDistanceMultiplier.get());
setBoundaryLevelAdjust(boundaryLevelAdjust.get());
setOctreeSizeScale(octreeSizeScale.get());
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get());
setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get());
}
void LODManager::saveSettings() {
automaticAvatarLOD.set(getAutomaticAvatarLOD());
avatarLODDecreaseFPS.set(getAvatarLODDecreaseFPS());
avatarLODIncreaseFPS.set(getAvatarLODIncreaseFPS());
avatarLODDistanceMultiplier.set(getAvatarLODDistanceMultiplier());
boundaryLevelAdjust.set(getBoundaryLevelAdjust());
octreeSizeScale.set(getOctreeSizeScale());
desktopLODDecreaseFPS.set(getDesktopLODDecreaseFPS());
hmdLODDecreaseFPS.set(getHMDLODDecreaseFPS());
}

View file

@ -17,12 +17,24 @@
#include <SharedUtil.h>
#include <SimpleMovingAverage.h>
const float ADJUST_LOD_DOWN_FPS = 40.0;
const float ADJUST_LOD_UP_FPS = 55.0;
const float DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS = 30.0f;
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0;
const float DEFAULT_HMD_LOD_DOWN_FPS = 60.0;
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 quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2;
const float START_DELAY_WINDOW_IN_SECS = 3.0f; // wait at least this long after steady state/last upshift to consider downshifts
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_UP_BY = 1.1f;
@ -33,12 +45,9 @@ const float ADJUST_LOD_UP_BY = 1.1f;
const float ADJUST_LOD_MIN_SIZE_SCALE = 1.0f;
const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
const float MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 0.1f;
const float MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER = 15.0f;
const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f;
const int ONE_SECOND_OF_FRAMES = 60;
const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES;
// The ratio of "visibility" of avatars to other content. A value larger than 1 will mean Avatars "cull" later than entities
// do. But both are still culled using the same angular size logic.
const float AVATAR_TO_ENTITY_RATIO = 2.0f;
class LODManager : public QObject, public Dependency {
@ -46,14 +55,18 @@ class LODManager : public QObject, public Dependency {
SINGLETON_DEPENDENCY
public:
void setAutomaticAvatarLOD(bool automaticAvatarLOD) { _automaticAvatarLOD = automaticAvatarLOD; }
bool getAutomaticAvatarLOD() const { return _automaticAvatarLOD; }
void setAvatarLODDecreaseFPS(float avatarLODDecreaseFPS) { _avatarLODDecreaseFPS = avatarLODDecreaseFPS; }
float getAvatarLODDecreaseFPS() const { return _avatarLODDecreaseFPS; }
void setAvatarLODIncreaseFPS(float avatarLODIncreaseFPS) { _avatarLODIncreaseFPS = avatarLODIncreaseFPS; }
float getAvatarLODIncreaseFPS() const { return _avatarLODIncreaseFPS; }
void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; }
float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; }
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
Q_INVOKABLE void setDesktopLODDecreaseFPS(float value) { _desktopLODDecreaseFPS = value; }
Q_INVOKABLE float getDesktopLODDecreaseFPS() const { return _desktopLODDecreaseFPS; }
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 float getHMDLODDecreaseFPS() const { return _hmdLODDecreaseFPS; }
Q_INVOKABLE float getHMDLODIncreaseFPS() const { return glm::min(_hmdLODDecreaseFPS + INCREASE_LOD_GAP, MAX_LIKELY_HMD_FPS); }
Q_INVOKABLE float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; }
// User Tweakable LOD Items
Q_INVOKABLE QString getLODFeedbackText();
@ -63,35 +76,40 @@ public:
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
void autoAdjustLOD(float currentFPS);
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 getLODIncreaseFPS();
bool shouldRenderMesh(float largestDimension, float distanceToCamera);
void autoAdjustLOD(float currentFPS);
void loadSettings();
void saveSettings();
void resetLODAdjust();
signals:
void LODIncreased();
void LODDecreased();
private:
LODManager() {}
bool _automaticAvatarLOD = true;
float _avatarLODDecreaseFPS = DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS;
float _avatarLODIncreaseFPS = ADJUST_LOD_UP_FPS;
float _avatarLODDistanceMultiplier = DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER;
LODManager();
void calculateAvatarLODDistanceMultiplier();
bool _automaticLODAdjust = true;
float _desktopLODDecreaseFPS = DEFAULT_DESKTOP_LOD_DOWN_FPS;
float _hmdLODDecreaseFPS = DEFAULT_HMD_LOD_DOWN_FPS;
float _avatarLODDistanceMultiplier;
float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE;
int _boundaryLevelAdjust = 0;
quint64 _lastAdjust = 0;
quint64 _lastAvatarDetailDrop = 0;
SimpleMovingAverage _fpsAverage = FIVE_SECONDS_OF_FRAMES;
SimpleMovingAverage _fastFPSAverage = ONE_SECOND_OF_FRAMES;
quint64 _lastDownShift = 0;
quint64 _lastUpShift = 0;
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;
QMap<float, float> _shouldRenderTable;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -23,19 +23,6 @@ class QComboBox;
class QCheckBox;
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.
class ModelPropertiesDialog : public QDialog {
Q_OBJECT

View file

@ -87,7 +87,8 @@ void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentSta
void FaceModel::updateJointState(int index) {
JointState& state = _jointStates[index];
const FBXJoint& joint = state.getFBXJoint();
if (joint.parentIndex != -1) {
// guard against out-of-bounds access to _jointStates
if (joint.parentIndex != -1 && joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
const JointState& parentState = _jointStates.at(joint.parentIndex);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (index == geometry.neckJointIndex) {

View file

@ -70,7 +70,6 @@ MyAvatar::MyAvatar() :
Avatar(),
_turningKeyPressTime(0.0f),
_gravity(0.0f, 0.0f, 0.0f),
_shouldJump(false),
_wasPushing(false),
_isPushing(false),
_isBraking(false),
@ -82,6 +81,7 @@ MyAvatar::MyAvatar() :
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
_characterController(this),
_lookAtTargetAvatar(),
_shouldRender(true),
_billboardValid(false),
@ -100,6 +100,7 @@ MyAvatar::MyAvatar() :
// connect to AddressManager signal for location jumps
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
this, &MyAvatar::goToLocation);
_characterController.setEnabled(true);
}
MyAvatar::~MyAvatar() {
@ -146,10 +147,6 @@ void MyAvatar::update(float deltaTime) {
head->setAudioLoudness(audio->getLastInputLoudness());
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) {
setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition()));
}
simulate(deltaTime);
if (_feetTouchFloor) {
_skeletonModel.updateStandingFoot();
@ -173,11 +170,7 @@ void MyAvatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("transform");
updateOrientation(deltaTime);
if (isPhysicsEnabled()) {
updatePositionWithPhysics(deltaTime);
} else {
updatePosition(deltaTime);
}
updatePosition(deltaTime);
}
{
@ -483,26 +476,6 @@ void MyAvatar::loadLastRecording() {
_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 handle = _skeletonModel.createAnimationHandle();
_animationHandles.append(handle);
@ -954,15 +927,15 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
return Avatar::getPosition();
}
void MyAvatar::updateLocalAABox() {
void MyAvatar::updateCharacterController() {
// compute localAABox
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
float radius = capsule.getRadius();
float height = 2.0f * (capsule.getHalfHeight() + radius);
glm::vec3 offset = _skeletonModel.getBoundingShapeOffset();
glm::vec3 corner(-radius, -0.5f * height, -radius);
corner += offset;
corner += _skeletonModel.getBoundingShapeOffset();
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
_localAABox.setBox(corner, scale);
_characterController.setLocalBoundingBox(corner, scale);
}
QString MyAvatar::getScriptedMotorFrame() const {
@ -1257,128 +1230,38 @@ glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVe
return localVelocity + motorEfficiency * deltaVelocity;
}
const float NEARBY_FLOOR_THRESHOLD = 5.0f;
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
glm::quat rotation = getHead()->getCameraOrientation();
glm::vec3 localVelocity = glm::inverse(rotation) * _velocity;
bool hasFloor = false;
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor);
bool isOnGround = _characterController.onGround();
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isOnGround);
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
_velocity = rotation * newLocalVelocity;
_velocity += _thrust * deltaTime;
_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) {
@ -1495,23 +1378,6 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
void MyAvatar::updateMotionBehavior() {
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)) {
_motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED;
} else {
@ -1522,6 +1388,7 @@ void MyAvatar::updateMotionBehavior() {
} else {
_motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
}
_characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));
_feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations);
}

View file

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

View file

@ -223,7 +223,7 @@ void SixenseManager::update(float deltaTime) {
palm->setJoystick(data->joystick_x, data->joystick_y);
// 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);
}

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
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
applicationOverlay.renderOverlay(true);
const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::UserInterface);
DependencyManager::get<GlowEffect>()->prepare();
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));
Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO);
if (displayOverlays) {
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
}
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
_activeEye = NULL;
}
glPopMatrix();
@ -166,9 +163,7 @@ void TV3DManager::display(Camera& whichCamera) {
eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0));
Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO);
if (displayOverlays) {
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
}
applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov);
_activeEye = NULL;
}
glPopMatrix();

View file

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

View file

@ -55,6 +55,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid
_windowWidget = dockWidget;
} else {
_windowWidget = new QWidget(Application::getInstance()->getWindow(), Qt::Window);
_windowWidget->setWindowTitle(title);
_windowWidget->setMinimumSize(width, height);
auto layout = new QVBoxLayout(_windowWidget);
@ -96,6 +97,18 @@ void WebWindowClass::setVisible(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) {
WebWindowClass* retVal;
QString file = context->argument(0).toString();

View file

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

View file

@ -33,6 +33,7 @@ WindowScriptingInterface::WindowScriptingInterface() :
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged);
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) {

View file

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

View file

@ -139,6 +139,7 @@ ApplicationOverlay::ApplicationOverlay() :
_magnifier(true),
_alpha(1.0f),
_oculusUIRadius(1.0f),
_trailingAudioLoudness(0.0f),
_crosshairTexture(0),
_previousBorderWidth(-1),
_previousBorderHeight(-1),
@ -176,18 +177,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
_textureAspectRatio = (float)glCanvas->getDeviceWidth() / (float)glCanvas->getDeviceHeight();
//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
glMatrixMode(GL_PROJECTION);
glDisable(GL_DEPTH_TEST);

View file

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

View file

@ -35,9 +35,24 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) :
// Create layouter
QFormLayout* form = new QFormLayout(this);
// Create a label with feedback...
_feedback = new QLabel(this);
QPalette palette = _feedback->palette();
const unsigned redish = 0xfff00000;
palette.setColor(QPalette::WindowText, QColor::fromRgb(redish));
_feedback->setPalette(palette);
_feedback->setText(lodManager->getLODFeedbackText());
const int FEEDBACK_WIDTH = 350;
_feedback->setFixedWidth(FEEDBACK_WIDTH);
form->addRow("You can see... ", _feedback);
form->addRow("Manually Adjust Level of Detail:", _manualLODAdjust = new QCheckBox(this));
_manualLODAdjust->setChecked(!lodManager->getAutomaticLODAdjust());
connect(_manualLODAdjust, SIGNAL(toggled(bool)), SLOT(updateAutomaticLODAdjust()));
_lodSize = new QSlider(Qt::Horizontal, this);
const int MAX_LOD_SIZE = MAX_LOD_SIZE_MULTIPLIER;
const int MIN_LOD_SIZE = 0;
const int MIN_LOD_SIZE = ADJUST_LOD_MIN_SIZE_SCALE;
const int STEP_LOD_SIZE = 1;
const int PAGE_STEP_LOD_SIZE = 100;
const int SLIDER_WIDTH = 300;
@ -50,55 +65,8 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) :
_lodSize->setPageStep(PAGE_STEP_LOD_SIZE);
int sliderValue = lodManager->getOctreeSizeScale() / TREE_SCALE;
_lodSize->setValue(sliderValue);
form->addRow("LOD Size Scale:", _lodSize);
form->addRow("Level of Detail:", _lodSize);
connect(_lodSize,SIGNAL(valueChanged(int)),this,SLOT(sizeScaleValueChanged(int)));
_boundaryLevelAdjust = new QSlider(Qt::Horizontal, this);
const int MAX_ADJUST = 10;
const int MIN_ADJUST = 0;
const int STEP_ADJUST = 1;
_boundaryLevelAdjust->setMaximum(MAX_ADJUST);
_boundaryLevelAdjust->setMinimum(MIN_ADJUST);
_boundaryLevelAdjust->setSingleStep(STEP_ADJUST);
_boundaryLevelAdjust->setTickInterval(STEP_ADJUST);
_boundaryLevelAdjust->setTickPosition(QSlider::TicksBelow);
_boundaryLevelAdjust->setFixedWidth(SLIDER_WIDTH);
sliderValue = lodManager->getBoundaryLevelAdjust();
_boundaryLevelAdjust->setValue(sliderValue);
form->addRow("Boundary Level Adjust:", _boundaryLevelAdjust);
connect(_boundaryLevelAdjust,SIGNAL(valueChanged(int)),this,SLOT(boundaryLevelValueChanged(int)));
// Create a label with feedback...
_feedback = new QLabel(this);
QPalette palette = _feedback->palette();
const unsigned redish = 0xfff00000;
palette.setColor(QPalette::WindowText, QColor::fromRgb(redish));
_feedback->setPalette(palette);
_feedback->setText(lodManager->getLODFeedbackText());
const int FEEDBACK_WIDTH = 350;
_feedback->setFixedWidth(FEEDBACK_WIDTH);
form->addRow("You can see... ", _feedback);
form->addRow("Automatic Avatar LOD Adjustment:", _automaticAvatarLOD = new QCheckBox(this));
_automaticAvatarLOD->setChecked(lodManager->getAutomaticAvatarLOD());
connect(_automaticAvatarLOD, SIGNAL(toggled(bool)), SLOT(updateAvatarLODControls()));
form->addRow("Decrease Avatar LOD Below FPS:", _avatarLODDecreaseFPS = new QDoubleSpinBox(this));
_avatarLODDecreaseFPS->setValue(lodManager->getAvatarLODDecreaseFPS());
_avatarLODDecreaseFPS->setDecimals(0);
connect(_avatarLODDecreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateAvatarLODValues()));
form->addRow("Increase Avatar LOD Above FPS:", _avatarLODIncreaseFPS = new QDoubleSpinBox(this));
_avatarLODIncreaseFPS->setValue(lodManager->getAvatarLODIncreaseFPS());
_avatarLODIncreaseFPS->setDecimals(0);
connect(_avatarLODIncreaseFPS, SIGNAL(valueChanged(double)), SLOT(updateAvatarLODValues()));
form->addRow("Avatar LOD:", _avatarLOD = new QDoubleSpinBox(this));
_avatarLOD->setDecimals(3);
_avatarLOD->setRange(1.0 / MAXIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER, 1.0 / MINIMUM_AVATAR_LOD_DISTANCE_MULTIPLIER);
_avatarLOD->setSingleStep(0.001);
_avatarLOD->setValue(1.0 / lodManager->getAvatarLODDistanceMultiplier());
connect(_avatarLOD, SIGNAL(valueChanged(double)), SLOT(updateAvatarLODValues()));
// Add a button to reset
QPushButton* resetButton = new QPushButton("Reset", this);
@ -107,49 +75,19 @@ LodToolsDialog::LodToolsDialog(QWidget* parent) :
this->QDialog::setLayout(form);
updateAvatarLODControls();
updateAutomaticLODAdjust();
}
void LodToolsDialog::reloadSliders() {
auto lodManager = DependencyManager::get<LODManager>();
_lodSize->setValue(lodManager->getOctreeSizeScale() / TREE_SCALE);
_boundaryLevelAdjust->setValue(lodManager->getBoundaryLevelAdjust());
_feedback->setText(lodManager->getLODFeedbackText());
}
void LodToolsDialog::updateAvatarLODControls() {
QFormLayout* form = static_cast<QFormLayout*>(layout());
void LodToolsDialog::updateAutomaticLODAdjust() {
auto lodManager = DependencyManager::get<LODManager>();
lodManager->setAutomaticAvatarLOD(_automaticAvatarLOD->isChecked());
_avatarLODDecreaseFPS->setVisible(_automaticAvatarLOD->isChecked());
form->labelForField(_avatarLODDecreaseFPS)->setVisible(_automaticAvatarLOD->isChecked());
_avatarLODIncreaseFPS->setVisible(_automaticAvatarLOD->isChecked());
form->labelForField(_avatarLODIncreaseFPS)->setVisible(_automaticAvatarLOD->isChecked());
_avatarLOD->setVisible(!_automaticAvatarLOD->isChecked());
form->labelForField(_avatarLOD)->setVisible(!_automaticAvatarLOD->isChecked());
if (!_automaticAvatarLOD->isChecked()) {
_avatarLOD->setValue(1.0 / lodManager->getAvatarLODDistanceMultiplier());
}
if (isVisible()) {
adjustSize();
}
}
void LodToolsDialog::updateAvatarLODValues() {
auto lodManager = DependencyManager::get<LODManager>();
if (_automaticAvatarLOD->isChecked()) {
lodManager->setAvatarLODDecreaseFPS(_avatarLODDecreaseFPS->value());
lodManager->setAvatarLODIncreaseFPS(_avatarLODIncreaseFPS->value());
} else {
lodManager->setAvatarLODDistanceMultiplier(1.0 / _avatarLOD->value());
}
lodManager->setAutomaticLODAdjust(!_manualLODAdjust->isChecked());
_lodSize->setEnabled(_manualLODAdjust->isChecked());
}
void LodToolsDialog::sizeScaleValueChanged(int value) {
@ -160,20 +98,13 @@ void LodToolsDialog::sizeScaleValueChanged(int value) {
_feedback->setText(lodManager->getLODFeedbackText());
}
void LodToolsDialog::boundaryLevelValueChanged(int value) {
auto lodManager = DependencyManager::get<LODManager>();
lodManager->setBoundaryLevelAdjust(value);
_feedback->setText(lodManager->getLODFeedbackText());
}
void LodToolsDialog::resetClicked(bool checked) {
int sliderValue = DEFAULT_OCTREE_SIZE_SCALE / TREE_SCALE;
//sizeScaleValueChanged(sliderValue);
_lodSize->setValue(sliderValue);
_boundaryLevelAdjust->setValue(0);
_automaticAvatarLOD->setChecked(true);
_avatarLODDecreaseFPS->setValue(DEFAULT_ADJUST_AVATAR_LOD_DOWN_FPS);
_avatarLODIncreaseFPS->setValue(ADJUST_LOD_UP_FPS);
_manualLODAdjust->setChecked(false);
updateAutomaticLODAdjust(); // tell our LOD manager about the reset
}
void LodToolsDialog::reject() {
@ -184,6 +115,15 @@ void LodToolsDialog::reject() {
void LodToolsDialog::closeEvent(QCloseEvent* event) {
this->QDialog::closeEvent(event);
emit closed();
auto lodManager = DependencyManager::get<LODManager>();
// always revert back to automatic LOD adjustment when closed
lodManager->setAutomaticLODAdjust(true);
// if the user adjusted the LOD above "normal" then always revert back to default
if (lodManager->getOctreeSizeScale() > DEFAULT_OCTREE_SIZE_SCALE) {
lodManager->setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE);
}
}

View file

@ -31,11 +31,9 @@ signals:
public slots:
void reject();
void sizeScaleValueChanged(int value);
void boundaryLevelValueChanged(int value);
void resetClicked(bool checked);
void reloadSliders();
void updateAvatarLODControls();
void updateAvatarLODValues();
void updateAutomaticLODAdjust();
protected:
@ -44,11 +42,13 @@ protected:
private:
QSlider* _lodSize;
QSlider* _boundaryLevelAdjust;
QCheckBox* _automaticAvatarLOD;
QDoubleSpinBox* _avatarLODDecreaseFPS;
QDoubleSpinBox* _avatarLODIncreaseFPS;
QDoubleSpinBox* _avatarLOD;
QCheckBox* _manualLODAdjust;
QDoubleSpinBox* _desktopLODDecreaseFPS;
QDoubleSpinBox* _hmdLODDecreaseFPS;
QLabel* _feedback;
};

View file

@ -23,7 +23,7 @@
#include "LoginDialog.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) :
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),

View file

@ -19,6 +19,7 @@
#include "Application.h"
#include "MainWindow.h"
#include "LODManager.h"
#include "Menu.h"
#include "ModelsBrowser.h"
#include "PreferencesDialog.h"
@ -174,6 +175,10 @@ void PreferencesDialog::loadPreferences() {
ui.sixenseReticleMoveSpeedSpin->setValue(sixense.getReticleMoveSpeed());
ui.invertSixenseButtonsCheckBox->setChecked(sixense.getInvertButtons());
// LOD items
auto lodManager = DependencyManager::get<LODManager>();
ui.desktopMinimumFPSSpin->setValue(lodManager->getDesktopLODDecreaseFPS());
ui.hmdMinimumFPSSpin->setValue(lodManager->getHMDLODDecreaseFPS());
}
void PreferencesDialog::savePreferences() {
@ -275,4 +280,9 @@ void PreferencesDialog::savePreferences() {
audio->setOutputStarveDetectionPeriod(ui.outputStarveDetectionPeriodSpinner->value());
Application::getInstance()->resizeGL(glCanvas->width(), glCanvas->height());
// LOD items
auto lodManager = DependencyManager::get<LODManager>();
lodManager->setDesktopLODDecreaseFPS(ui.desktopMinimumFPSSpin->value());
lodManager->setHMDLODDecreaseFPS(ui.hmdMinimumFPSSpin->value());
}

View file

@ -136,7 +136,7 @@
<string>&lt;style type=&quot;text/css&quot;&gt;
a { text-decoration: none; color: #267077;}
&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 name="alignment">
<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;
a { text-decoration: none; color: #267077;}
&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 name="openExternalLinks">
<bool>true</bool>

View file

@ -701,6 +701,219 @@
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_10">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="levelOfDetailTitleLabel">
<property name="font">
<font>
<family>Arial</family>
<pointsize>18</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:#29967e</string>
</property>
<property name="text">
<string>Level of Detail Tuning</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_111x">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_9x">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Minimum Desktop FPS</string>
</property>
<property name="indent">
<number>0</number>
</property>
<!--
<property name="buddy">
<cstring>fieldOfViewSpin</cstring>
</property>
-->
</widget>
</item>
<item>
<spacer name="horizontalSpacer_111x">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="desktopMinimumFPSSpin">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>95</width>
<height>36</height>
</size>
</property>
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>120</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_111y">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item>
<widget class="QLabel" name="label_9y">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Minimum HMD FPS</string>
</property>
<property name="indent">
<number>0</number>
</property>
<!--
<property name="buddy">
<cstring>fieldOfViewSpin</cstring>
</property>
-->
</widget>
</item>
<item>
<spacer name="horizontalSpacer_111y">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="hmdMinimumFPSSpin">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>95</width>
<height>36</height>
</size>
</property>
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>120</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_8">
<property name="orientation">
@ -717,6 +930,7 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="avatarTitleLabel">
<property name="font">
@ -738,6 +952,7 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_111">
<property name="spacing">
@ -820,6 +1035,9 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">

View file

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

View file

@ -88,7 +88,7 @@ public:
}
void loadProfile(int profileIndex) {
if (profileIndex >= 0 && profileIndex < _profileCount) {
if (profileIndex >= 0 && profileIndex < (int)_profileCount) {
for (uint32_t i = 0; i < _filterCount; ++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_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 =
AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED |
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED |
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
// these bits will be expanded as features are exposed
const quint32 AVATAR_MOTION_SCRIPTABLE_BITS =
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED |
AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY |
AVATAR_MOTION_OBEY_LOCAL_GRAVITY |
AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED;
// 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 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; }
Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
@ -375,8 +357,8 @@ protected:
HeadData* _headData;
HandData* _handData;
QUrl _faceModelURL = DEFAULT_HEAD_MODEL_URL;
QUrl _skeletonModelURL = DEFAULT_BODY_MODEL_URL;
QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit
QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit
QVector<AttachmentData> _attachmentData;
QString _displayName;
@ -409,9 +391,6 @@ private:
// privatize the copy constructor and assignment operator so they cannot be called
AvatarData(const AvatarData&);
AvatarData& operator= (const AvatarData&);
QReadWriteLock _lock;
bool _enablePhysics = false;
};
Q_DECLARE_METATYPE(AvatarData*)

View file

@ -277,7 +277,6 @@ void EntityTreeRenderer::update() {
void EntityTreeRenderer::checkEnterLeaveEntities() {
if (_tree && !_shuttingDown) {
_tree->lockForWrite(); // so that our scripts can do edits if they want
glm::vec3 avatarPosition = _viewState->getAvatarPosition();
if (avatarPosition != _lastAvatarPosition) {
@ -286,6 +285,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
QVector<EntityItemID> entitiesContainingAvatar;
// 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);
// create a list of entities that actually contain the avatar's position
@ -294,6 +294,11 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
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
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
@ -322,14 +327,12 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
_currentEntitiesInside = entitiesContainingAvatar;
_lastAvatarPosition = avatarPosition;
}
_tree->unlock();
}
}
void EntityTreeRenderer::leaveAllEntities() {
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
foreach(const EntityItemID& entityID, _currentEntitiesInside) {
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
// first chance, we'll check for enter/leave entity events.
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
_tree->unlock();
}
}
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {

View file

@ -266,6 +266,26 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking);
}
void RenderableModelEntityItem::setCollisionModelURL(const QString& url) {
ModelEntityItem::setCollisionModelURL(url);
if (_model) {
_model->setCollisionModelURL(QUrl(url));
}
}
bool RenderableModelEntityItem::hasCollisionModel() const {
if (_model) {
return ! _model->getCollisionURL().isEmpty();
} else {
return !_collisionModelURL.isEmpty();
}
}
const QString& RenderableModelEntityItem::getCollisionModelURL() const {
assert (!_model || _collisionModelURL == _model->getCollisionURL().toString());
return _collisionModelURL;
}
bool RenderableModelEntityItem::isReadyToComputeShape() {
if (!_model) {
@ -294,13 +314,67 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
const QSharedPointer<NetworkGeometry> collisionNetworkGeometry = _model->getCollisionGeometry();
const FBXGeometry& fbxGeometry = collisionNetworkGeometry->getFBXGeometry();
AABox aaBox;
_points.clear();
unsigned int i = 0;
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
_points << mesh.vertices;
foreach (const FBXMeshPart &meshPart, mesh.parts) {
QVector<glm::vec3> pointsInPart;
unsigned int triangleCount = meshPart.triangleIndices.size() / 3;
assert((unsigned int)meshPart.triangleIndices.size() == triangleCount*3);
for (unsigned int j = 0; j < triangleCount; j++) {
unsigned int p0Index = meshPart.triangleIndices[j*3];
unsigned int p1Index = meshPart.triangleIndices[j*3+1];
unsigned int p2Index = meshPart.triangleIndices[j*3+2];
assert(p0Index < (unsigned int)mesh.vertices.size());
assert(p1Index < (unsigned int)mesh.vertices.size());
assert(p2Index < (unsigned int)mesh.vertices.size());
glm::vec3 p0 = mesh.vertices[p0Index];
glm::vec3 p1 = mesh.vertices[p1Index];
glm::vec3 p2 = mesh.vertices[p2Index];
aaBox += p0;
aaBox += p1;
aaBox += p2;
if (!pointsInPart.contains(p0)) {
pointsInPart << p0;
}
if (!pointsInPart.contains(p1)) {
pointsInPart << p1;
}
if (!pointsInPart.contains(p2)) {
pointsInPart << p2;
}
}
QVector<glm::vec3> newMeshPoints;
_points << newMeshPoints;
_points[i++] << pointsInPart;
}
}
info.setParams(getShapeType(), 0.5f * getDimensions(), _collisionModelURL);
info.setConvexHull(_points);
// make sure we aren't about to divide by zero
glm::vec3 aaBoxDim = aaBox.getDimensions();
aaBoxDim = glm::clamp(aaBoxDim, glm::vec3(FLT_EPSILON), aaBoxDim);
glm::vec3 scale = _dimensions / aaBoxDim;
// 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 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;
}
}
info.setParams(getShapeType(), _dimensions, _collisionModelURL);
info.setConvexHulls(_points);
}
}
@ -308,7 +382,9 @@ ShapeType RenderableModelEntityItem::getShapeType() const {
// XXX make hull an option in edit.js ?
if (!_model || _model->getCollisionURL().isEmpty()) {
return _shapeType;
} else {
} else if (_points.size() == 1) {
return SHAPE_TYPE_CONVEX_HULL;
} else {
return SHAPE_TYPE_COMPOUND;
}
}

View file

@ -52,6 +52,10 @@ public:
bool needsToCallUpdate() const;
virtual void setCollisionModelURL(const QString& url);
virtual bool hasCollisionModel() const;
virtual const QString& getCollisionModelURL() const;
bool isReadyToComputeShape();
void computeShapeInfo(ShapeInfo& info);
ShapeType getShapeType() const;
@ -66,7 +70,7 @@ private:
QString _currentTextures;
QStringList _originalTextures;
bool _originalTexturesRead;
QVector<glm::vec3> _points;
QVector<QVector<glm::vec3>> _points;
};
#endif // hifi_RenderableModelEntityItem_h

View file

@ -57,6 +57,7 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
_collisionsWillMove = ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE;
_locked = ENTITY_ITEM_DEFAULT_LOCKED;
_userData = ENTITY_ITEM_DEFAULT_USER_DATA;
_marketplaceID = ENTITY_ITEM_DEFAULT_MARKETPLACE_ID;
}
EntityItem::EntityItem(const EntityItemID& entityItemID) {
@ -116,6 +117,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_COLLISIONS_WILL_MOVE;
requestedProperties += PROP_LOCKED;
requestedProperties += PROP_USER_DATA;
requestedProperties += PROP_MARKETPLACE_ID;
return requestedProperties;
}
@ -238,6 +240,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, appendValue, getCollisionsWillMove());
APPEND_ENTITY_PROPERTY(PROP_LOCKED, appendValue, getLocked());
APPEND_ENTITY_PROPERTY(PROP_USER_DATA, appendValue, getUserData());
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, getMarketplaceID());
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
requestedProperties,
@ -550,10 +553,25 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY_SETTER(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions);
READ_ENTITY_PROPERTY_SETTER(PROP_COLLISIONS_WILL_MOVE, bool, updateCollisionsWillMove);
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_MARKETPLACE_ID) {
READ_ENTITY_PROPERTY_STRING(PROP_MARKETPLACE_ID, setMarketplaceID);
}
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
////////////////////////////////////
// WARNING: Do not add stream content here after the subclass. Always add it before the subclass
//
// NOTE: we had a bad version of the stream that we added stream data after the subclass. We can attempt to recover
// 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
if (args.bitstreamVersion == VERSION_ENTITIES_HAS_MARKETPLACE_ID_DAMAGED) {
READ_ENTITY_PROPERTY_STRING(PROP_MARKETPLACE_ID, setMarketplaceID);
}
if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY))) {
// NOTE: This code is attempting to "repair" the old data we just got from the server to make it more
// closely match where the entities should be if they'd stepped forward in time to "now". The server
@ -820,6 +838,7 @@ EntityItemProperties EntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionsWillMove, getCollisionsWillMove);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(locked, getLocked);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(userData, getUserData);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(marketplaceID, getMarketplaceID);
properties._defaultSettings = false;
@ -848,6 +867,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
if (somethingChanged) {
somethingChangedNotification(); // notify derived classes that something has changed

View file

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

View file

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

View file

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

View file

@ -93,16 +93,26 @@ enum EntityPropertyList {
PROP_LOCAL_GRAVITY,
PROP_PARTICLE_RADIUS,
// NOTE: add new properties ABOVE this line and then modify PROP_LAST_ITEM below
PROP_LAST_ITEM = PROP_PARTICLE_RADIUS,
PROP_COLLISION_MODEL_URL,
PROP_MARKETPLACE_ID,
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties ABOVE this line and then modify PROP_LAST_ITEM below
PROP_LAST_ITEM = PROP_MARKETPLACE_ID,
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// WARNING! Do not add props here unless you intentionally mean to reuse PROP_ indexes
//
// These properties of TextEntity piggy back off of properties of ModelEntities, the type doesn't matter
// since the derived class knows how to interpret it's own properties and knows the types it expects
PROP_TEXT_COLOR = PROP_COLOR,
PROP_TEXT = PROP_MODEL_URL,
PROP_LINE_HEIGHT = PROP_ANIMATION_URL,
PROP_BACKGROUND_COLOR = PROP_ANIMATION_FPS,
PROP_COLLISION_MODEL_URL,
PROP_COLLISION_MODEL_URL_OLD_VERSION = PROP_ANIMATION_FPS + 1
// WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above
};
typedef PropertyFlags<EntityPropertyList> EntityPropertyFlags;
@ -195,6 +205,7 @@ public:
DEFINE_PROPERTY(PROP_EMIT_STRENGTH, EmitStrength, emitStrength, float);
DEFINE_PROPERTY(PROP_LOCAL_GRAVITY, LocalGravity, localGravity, float);
DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float);
DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString);
public:
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
@ -322,6 +333,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EmitStrength, emitStrength, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, LocalGravity, localGravity, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, "");
debug << " last edited:" << properties.getLastEdited() << "\n";
debug << " edited ago:" << properties.getEditedAgo() << "\n";

View file

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

View file

@ -148,6 +148,7 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
}
}
} else {
QString entityScriptBefore = entity->getScript();
uint32_t preFlags = entity->getDirtyFlags();
UpdateEntityOperator theOperator(this, containingElement, entity, properties);
recurseTreeWithOperator(&theOperator);
@ -166,6 +167,11 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
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).

View file

@ -95,7 +95,6 @@ public:
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = false);
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = false);
void removeEntityFromSimulation(EntityItem* entity);
/// \param position point of query in world-frame (meters)
/// \param targetRadius radius of query (meters)

View file

@ -93,16 +93,18 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
int bytesRead = 0;
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
READ_ENTITY_PROPERTY_STRING(PROP_MODEL_URL, setModelURL);
if (args.bitstreamVersion < VERSION_ENTITIES_HAS_COLLISION_MODEL) {
setCollisionModelURL("");
} else if (args.bitstreamVersion == VERSION_ENTITIES_HAS_COLLISION_MODEL) {
READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL_OLD_VERSION, setCollisionModelURL);
} else {
READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL, setCollisionModelURL);
}
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_URL, setAnimationURL);
// Because we're using AnimationLoop which will reset the frame index if you change it's running state
// we want to read these values in the order they appear in the buffer, but call our setters in an
// order that allows AnimationLoop to preserve the correct frame rate.
@ -279,6 +281,13 @@ void ModelEntityItem::updateShapeType(ShapeType type) {
}
}
void ModelEntityItem::setCollisionModelURL(const QString& url) {
if (_collisionModelURL != url) {
_collisionModelURL = url;
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
}
}
void ModelEntityItem::setAnimationURL(const QString& url) {
_dirtyFlags |= EntityItem::DIRTY_UPDATEABLE;
_animationURL = url;

View file

@ -57,13 +57,13 @@ public:
const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
bool hasModel() const { return !_modelURL.isEmpty(); }
bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); }
virtual bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); }
static const QString DEFAULT_MODEL_URL;
const QString& getModelURL() const { return _modelURL; }
static const QString DEFAULT_COLLISION_MODEL_URL;
const QString& getCollisionModelURL() const { return _collisionModelURL; }
virtual const QString& getCollisionModelURL() const { return _collisionModelURL; }
bool hasAnimation() const { return !_animationURL.isEmpty(); }
static const QString DEFAULT_ANIMATION_URL;
@ -78,7 +78,7 @@ public:
// model related properties
void setModelURL(const QString& url) { _modelURL = url; }
void setCollisionModelURL(const QString& url) { _collisionModelURL = url; }
virtual void setCollisionModelURL(const QString& url);
void setAnimationURL(const QString& url);
static const float DEFAULT_ANIMATION_FRAME_INDEX;
void setAnimationFrameIndex(float value);

View file

@ -458,41 +458,6 @@ FBXNode parseFBX(QIODevice* device) {
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> values;
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;
}
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) {
QBuffer buffer(const_cast<QByteArray*>(&model));
buffer.open(QIODevice::ReadOnly);

View file

@ -261,12 +261,6 @@ public:
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.
/// \exception QString if an error occurs in parsing
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) {
meshPart.quadIndices << indices;
} 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 {
// something we don't (yet) care about

View file

@ -56,8 +56,11 @@ enum Primitive {
};
enum ReservedSlot {
TRANSFORM_OBJECT_SLOT = 6,
/* TRANSFORM_OBJECT_SLOT = 6,
TRANSFORM_CAMERA_SLOT = 7,
*/
TRANSFORM_OBJECT_SLOT = 1,
TRANSFORM_CAMERA_SLOT = 2,
};
class Batch {

View file

@ -12,10 +12,16 @@
<@def GPU_CONFIG_SLH@>
<@if GLPROFILE == PC_GL @>
<@def GPU_FEATURE_PROFILE GPU_CORE@>
<@def GPU_TRANSFORM_PROFILE GPU_CORE@>
<@def VERSION_HEADER #version 330 compatibility@>
<@elif GLPROFILE == MAC_GL @>
<@def GPU_FEATURE_PROFILE GPU_LEGACY@>
<@def GPU_TRANSFORM_PROFILE GPU_LEGACY@>
<@def VERSION_HEADER #version 120@>
<@else@>
<@def GPU_FEATURE_PROFILE GPU_LEGACY@>
<@def GPU_TRANSFORM_PROFILE GPU_LEGACY@>
<@def VERSION_HEADER #version 120@>
<@endif@>

View file

@ -55,19 +55,17 @@ void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) {
BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
GLintptr rangeStart = batch._params[paramOffset + 1]._uint;
GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint;
#if defined(Q_OS_MAC)
#if (GPU_FEATURE_PROFILE == GPU_CORE)
GLuint bo = getBufferID(*uniformBuffer);
glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize);
#else
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
// NOT working so we ll stick to the uniform float array until we move to core profile
// GLuint bo = getBufferID(*uniformBuffer);
//glUniformBufferEXT(_shader._program, slot, bo);
#elif defined(Q_OS_WIN)
GLuint bo = getBufferID(*uniformBuffer);
glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize);
#else
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
#endif
CHECK_GL_ERROR();
}

View file

@ -41,6 +41,12 @@ void makeBindings(GLBackend::GLShader* shader) {
glBindAttribLocation(glprogram, gpu::Stream::POSITION, "position");
}
//Check for gpu specific attribute slotBindings
loc = glGetAttribLocation(glprogram, "gl_Vertex");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::POSITION, "position");
}
loc = glGetAttribLocation(glprogram, "normal");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "normal");
@ -88,7 +94,7 @@ void makeBindings(GLBackend::GLShader* shader) {
// now assign the ubo binding, then DON't relink!
//Check for gpu specific uniform slotBindings
#if defined(Q_OS_WIN)
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
loc = glGetUniformBlockIndex(glprogram, "transformObjectBuffer");
if (loc >= 0) {
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
@ -503,6 +509,12 @@ ElementResource getFormatFromGLUniform(GLenum gltype) {
int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) {
GLint uniformsCount = 0;
#if (GPU_FEATURE_PROFILE == GPU_LEGACY)
GLint currentProgram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, &currentProgram);
glUseProgram(glprogram);
#endif
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
for (int i = 0; i < uniformsCount; i++) {
@ -520,18 +532,36 @@ int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, S
// The uniform as a standard var type
if (location != INVALID_UNIFORM_LOCATION) {
// Let's make sure the name doesn't contains an array element
std::string sname(name);
auto foundBracket = sname.find_first_of('[');
if (foundBracket != std::string::npos) {
// std::string arrayname = sname.substr(0, foundBracket);
if (sname[foundBracket + 1] == '0') {
sname = sname.substr(0, foundBracket);
} else {
// skip this uniform since it's not the first element of an array
continue;
}
}
if (elementResource._resource == Resource::BUFFER) {
uniforms.insert(Shader::Slot(name, location, elementResource._element, elementResource._resource));
uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource));
} else {
// For texture/Sampler, the location is the actual binding value
GLint binding = -1;
glGetUniformiv(glprogram, location, &binding);
auto requestedBinding = slotBindings.find(std::string(name));
auto requestedBinding = slotBindings.find(std::string(sname));
if (requestedBinding != slotBindings.end()) {
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
#if (GPU_FEATURE_PROFILE == GPU_LEGACY)
glUniform1i(location, binding);
#else
glProgramUniform1i(glprogram, location, binding);
#endif
}
}
@ -541,6 +571,10 @@ int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, S
}
}
#if (GPU_FEATURE_PROFILE == GPU_LEGACY)
glUseProgram(currentProgram);
#endif
return uniformsCount;
}
@ -551,7 +585,9 @@ bool isUnusedSlot(GLint binding) {
int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) {
GLint buffersCount = 0;
#if defined(Q_OS_WIN)
#if (GPU_FEATURE_PROFILE == GPU_CORE)
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount);
// fast exit

View file

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

View file

@ -32,7 +32,7 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) {
}
void GLBackend::initTransform() {
#if defined(Q_OS_WIN)
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
glGenBuffers(1, &_transform._transformObjectBuffer);
glGenBuffers(1, &_transform._transformCameraBuffer);
@ -49,7 +49,7 @@ void GLBackend::initTransform() {
}
void GLBackend::killTransform() {
#if defined(Q_OS_WIN)
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
glDeleteBuffers(1, &_transform._transformObjectBuffer);
glDeleteBuffers(1, &_transform._transformCameraBuffer);
#else
@ -77,34 +77,30 @@ void GLBackend::updateTransform() {
_transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated;
}
#if (GPU_TRANSFORM_PROFILE == GPU_CORE)
if (_transform._invalidView || _transform._invalidProj) {
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR();
#endif
}
if (_transform._invalidModel) {
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformObjectBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR();
#endif
}
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._transformObjectBuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _transform._transformCameraBuffer);
CHECK_GL_ERROR();
#endif
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
#if (GPU_TRANSFORM_PROFILE == GPU_LEGACY)
// Do it again for fixed pipeline until we can get rid of it
if (_transform._invalidProj) {
if (_transform._lastMode != GL_PROJECTION) {

View file

@ -14,21 +14,32 @@
#define GL_GLEXT_PROTOTYPES 1
#define GPU_CORE 1
#define GPU_LEGACY 0
#if defined(__APPLE__)
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#define GPU_FEATURE_PROFILE GPU_LEGACY
#define GPU_TRANSFORM_PROFILE GPU_LEGACY
#elif defined(WIN32)
#include <windowshacks.h>
#include <GL/glew.h>
#include <GL/wglew.h>
#define GPU_FEATURE_PROFILE GPU_CORE
#define GPU_TRANSFORM_PROFILE GPU_CORE
#elif defined(ANDROID)
#else
#include <GL/gl.h>
#include <GL/glext.h>
#define GPU_FEATURE_PROFILE GPU_LEGACY
#define GPU_TRANSFORM_PROFILE GPU_LEGACY
#endif

View file

@ -41,31 +41,58 @@ public:
Language _lang = GLSL;
};
static const int32 INVALID_LOCATION = -1;
class Slot {
public:
std::string _name;
uint32 _location;
int32 _location{INVALID_LOCATION};
Element _element;
uint16 _resourceType;
uint16 _resourceType{Resource::BUFFER};
Slot(const std::string& name, uint16 location, const Element& element, uint16 resourceType = Resource::BUFFER) :
Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType) {}
Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType) {}
Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER) :
_name(name), _location(location), _element(element), _resourceType(resourceType) {}
Slot(const std::string& name) : _name(name) {}
Slot& operator= (const Slot& s) {
_name = s._name;
_location = s._location;
_element = s._element;
_resourceType = s._resourceType;
return (*this); }
};
class Binding {
public:
std::string _name;
uint32 _location;
Binding(const std::string&& name, uint32 loc = 0) : _name(name), _location(loc) {}
int32 _location;
Binding(const std::string& name, int32 loc = INVALID_LOCATION) : _name(name), _location(loc) {}
};
template <typename T> class Less {
public:
bool operator() (const T& x, const T& y) const { return x._name < y._name; }
};
typedef std::set<Slot, Less<Slot>> SlotSet;
class SlotSet : public std::set<Slot, Less<Slot>> {
public:
Slot findSlot(const std::string& name) const {
auto key = Slot(name);
auto found = static_cast<const std::set<Slot, Less<Slot>>*>(this)->find(key);
if (found != end()) {
return (*found);
}
return key;
}
int32 findLocation(const std::string& name) const {
return findSlot(name)._location;
}
protected:
};
typedef std::set<Binding, Less<Binding>> BindingSet;

View file

@ -10,6 +10,7 @@
<@if not GPU_TRANSFORM_STATE_SLH@>
<@def GPU_TRANSFORM_STATE_SLH@>
<@func declareStandardTransform()@>
struct TransformObject {
mat4 _model;
mat4 _modelInverse;
@ -23,76 +24,100 @@ struct TransformCamera {
vec4 _viewport;
};
vec4 transformModelToClipPos(TransformCamera camera, TransformObject object, vec4 pos) {
<@if GLPROFILE == MAC_GL@>
return gl_ModelViewProjectionMatrix * pos;
<@elif GLPROFILE == PC_GL@>
vec4 epos = (object._model * pos) + vec4(-pos.w * camera._viewInverse[3].xyz, 0.0);
return camera._projectionViewUntranslated * epos;
// Equivalent to the following but hoppefully a bit more accurate
// return camera._projection * camera._view * object._model * pos;
<@else@>
return gl_ModelViewProjectionMatrix * pos;
<@endif@>
}
vec3 transformModelToEyeDir(TransformCamera camera, TransformObject object, vec3 dir) {
<@if GLPROFILE == MAC_GL@>
return gl_NormalMatrix * dir;
<@elif GLPROFILE == PC_GL@>
vec3 mr0 = vec3(object._modelInverse[0].x, object._modelInverse[1].x, object._modelInverse[2].x);
vec3 mr1 = vec3(object._modelInverse[0].y, object._modelInverse[1].y, object._modelInverse[2].y);
vec3 mr2 = vec3(object._modelInverse[0].z, object._modelInverse[1].z, object._modelInverse[2].z);
vec3 mvc0 = vec3(dot(camera._viewInverse[0].xyz, mr0), dot(camera._viewInverse[0].xyz, mr1), dot(camera._viewInverse[0].xyz, mr2));
vec3 mvc1 = vec3(dot(camera._viewInverse[1].xyz, mr0), dot(camera._viewInverse[1].xyz, mr1), dot(camera._viewInverse[1].xyz, mr2));
vec3 mvc2 = vec3(dot(camera._viewInverse[2].xyz, mr0), dot(camera._viewInverse[2].xyz, mr1), dot(camera._viewInverse[2].xyz, mr2));
vec3 result = vec3(dot(mvc0, dir), dot(mvc1, dir), dot(mvc2, dir));
return result;
<@else@>
return gl_NormalMatrix * dir;
<@endif@>
}
<@if GLPROFILE == PC_GL@>
<@if GPU_TRANSFORM_PROFILE == GPU_CORE@>
uniform transformObjectBuffer {
TransformObject object;
TransformObject _object;
};
TransformObject getTransformObject() {
return object;
return _object;
}
uniform transformCameraBuffer {
TransformCamera camera;
TransformCamera _camera;
};
TransformCamera getTransformCamera() {
return camera;
}
<@elif GLPROFILE == MAC_GL@>
TransformObject getTransformObject() {
TransformObject object;
return object;
return _camera;
}
TransformCamera getTransformCamera() {
TransformCamera camera;
return camera;
}
<@else@>
//uniform vec4 transformObjectBuffer[8];
TransformObject getTransformObject() {
TransformObject object;
/* object._model[0] = transformObjectBuffer[0];
object._model[1] = transformObjectBuffer[1];
object._model[2] = transformObjectBuffer[2];
object._model[3] = transformObjectBuffer[3];
object._modelInverse[0] = transformObjectBuffer[4];
object._modelInverse[1] = transformObjectBuffer[5];
object._modelInverse[2] = transformObjectBuffer[6];
object._modelInverse[3] = transformObjectBuffer[7];
*/
return object;
}
//uniform vec4 transformCameraBuffer[17];
TransformCamera getTransformCamera() {
TransformCamera camera;
/* camera._view[0] = transformCameraBuffer[0];
camera._view[1] = transformCameraBuffer[1];
camera._view[2] = transformCameraBuffer[2];
camera._view[3] = transformCameraBuffer[3];
camera._viewInverse[0] = transformCameraBuffer[4];
camera._viewInverse[1] = transformCameraBuffer[5];
camera._viewInverse[2] = transformCameraBuffer[6];
camera._viewInverse[3] = transformCameraBuffer[7];
camera._projectionViewUntranslated[0] = transformCameraBuffer[8];
camera._projectionViewUntranslated[1] = transformCameraBuffer[9];
camera._projectionViewUntranslated[2] = transformCameraBuffer[10];
camera._projectionViewUntranslated[3] = transformCameraBuffer[11];
camera._projection[0] = transformCameraBuffer[12];
camera._projection[1] = transformCameraBuffer[13];
camera._projection[2] = transformCameraBuffer[14];
camera._projection[3] = transformCameraBuffer[15];
camera._viewport = transformCameraBuffer[16];
*/
return camera;
}
<@endif@>
<@endfunc@>
<@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@>
<@if GPU_TRANSFORM_PROFILE == GPU_CORE@>
<!// Equivalent to the following but hoppefully a tad more accurate
//return camera._projection * camera._view * object._model * pos; !>
{ // transformModelToClipPos
vec4 _eyepos = (<$objectTransform$>._model * <$modelPos$>) + vec4(-<$modelPos$>.w * <$cameraTransform$>._viewInverse[3].xyz, 0.0);
<$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * _eyepos;
}
<@else@>
<$clipPos$> = gl_ModelViewProjectionMatrix * <$modelPos$>;
<@endif@>
<@endfunc@>
<@func transformModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@>
<@if GPU_TRANSFORM_PROFILE == GPU_CORE@>
{ // transformModelToEyeDir
vec3 mr0 = vec3(<$objectTransform$>._modelInverse[0].x, <$objectTransform$>._modelInverse[1].x, <$objectTransform$>._modelInverse[2].x);
vec3 mr1 = vec3(<$objectTransform$>._modelInverse[0].y, <$objectTransform$>._modelInverse[1].y, <$objectTransform$>._modelInverse[2].y);
vec3 mr2 = vec3(<$objectTransform$>._modelInverse[0].z, <$objectTransform$>._modelInverse[1].z, <$objectTransform$>._modelInverse[2].z);
vec3 mvc0 = vec3(dot(<$cameraTransform$>._viewInverse[0].xyz, mr0), dot(<$cameraTransform$>._viewInverse[0].xyz, mr1), dot(<$cameraTransform$>._viewInverse[0].xyz, mr2));
vec3 mvc1 = vec3(dot(<$cameraTransform$>._viewInverse[1].xyz, mr0), dot(<$cameraTransform$>._viewInverse[1].xyz, mr1), dot(<$cameraTransform$>._viewInverse[1].xyz, mr2));
vec3 mvc2 = vec3(dot(<$cameraTransform$>._viewInverse[2].xyz, mr0), dot(<$cameraTransform$>._viewInverse[2].xyz, mr1), dot(<$cameraTransform$>._viewInverse[2].xyz, mr2));
<$eyeDir$> = vec3(dot(mvc0, <$modelDir$>), dot(mvc1, <$modelDir$>), dot(mvc2, <$modelDir$>));
}
<@else@>
<$eyeDir$> = gl_NormalMatrix * <$modelDir$>;
<@endif@>
<@endfunc@>
<@endif@>

View file

@ -252,27 +252,18 @@ public:
// Schema to access the attribute values of the light
class Schema {
public:
Vec4 _position;
Vec3 _direction;
float _spare0;
Color _color;
float _intensity;
Vec4 _attenuation;
Vec4 _spot;
Vec4 _shadow;
Vec4 _position{0.0f, 0.0f, 0.0f, 1.0f};
Vec3 _direction{0.0f, 0.0f, -1.0f};
float _spare0{0.0f};
Color _color{1.0f};
float _intensity{1.0f};
Vec4 _attenuation{1.0f};
Vec4 _spot{0.0f, 0.0f, 0.0f, 3.0f};
Vec4 _shadow{0.0f};
Vec4 _control;
Vec4 _control{0.0f};
Schema() :
_position(0.0f, 0.0f, 0.0f, 1.0f),
_direction(0.0f, 0.0f, -1.0f),
_spare0(0.f),
_color(1.0f),
_intensity(1.0f),
_attenuation(1.0f, 1.0f, 1.0f, 1.0f),
_spot(0.0f, 0.0f, 0.0f, 3.0f),
_control(0.0f)
{}
Schema() {}
};
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }

View file

@ -19,7 +19,6 @@ struct Light {
vec4 _spot;
vec4 _shadow;
vec4 _control;
};
@ -65,29 +64,15 @@ float getLightShowContour(Light l) {
return l._control.w;
}
<@if GLPROFILE == PC_GL@>
<@if GPU_FEATURE_PROFILE == GPU_CORE @>
uniform lightBuffer {
Light light;
};
Light getLight() {
return light;
}
<@elif GLPROFILE == MAC_GL@>
uniform vec4 lightBuffer[9];
Light getLight() {
Light light;
light._position = lightBuffer[0];
light._direction = lightBuffer[1];
light._color = lightBuffer[2];
light._attenuation = lightBuffer[3];
light._spot = lightBuffer[4];
light._shadow = lightBuffer[5];
light._control = lightBuffer[6];
return light;
}
<@else@>
uniform vec4 lightBuffer[9];
uniform vec4 lightBuffer[7];
Light getLight() {
Light light;
light._position = lightBuffer[0];

View file

@ -79,20 +79,15 @@ public:
class Schema {
public:
Color _diffuse;
float _opacity;
Color _specular;
float _shininess;
Color _emissive;
float _spare0;
Color _diffuse{0.5f};
float _opacity{1.f};
Color _specular{0.03f};
float _shininess{0.1f};
Color _emissive{0.0f};
float _spare0{0.0f};
glm::vec4 _spareVec4{0.0f}; // for alignment beauty, Material size == Mat4x4
Schema() :
_diffuse(0.5f),
_opacity(1.0f),
_specular(0.03f),
_shininess(0.1f),
_emissive(0.0f)
{}
Schema() {}
};
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }

View file

@ -14,7 +14,8 @@
struct Material {
vec4 _diffuse;
vec4 _specular;
vec4 _emissive;
vec4 _spare;
};
float getMaterialOpacity(Material m) { return m._diffuse.a; }
@ -24,36 +25,21 @@ float getMaterialShininess(Material m) { return m._specular.a; }
<@if GLPROFILE == PC_GL@>
<@if GPU_FEATURE_PROFILE == GPU_CORE@>
uniform materialBuffer {
Material _mat;
};
Material getMaterial() {
return _mat;
}
<@elif GLPROFILE == MAC_GL@>
uniform vec4 materialBuffer[2];
Material getMaterial() {
Material mat;
mat._diffuse = materialBuffer[0];
mat._specular = materialBuffer[1];
return mat;
}
<!/* tryed and failed...
bindable uniform struct {
Material mat;
} materialBuffer;
Material getMaterial() {
return materialBuffer.mat;
}
*/!>
<@else@>
uniform vec4 materialBuffer[2];
uniform vec4 materialBuffer[4];
Material getMaterial() {
Material mat;
mat._diffuse = materialBuffer[0];
mat._specular = materialBuffer[1];
mat._emissive = materialBuffer[2];
mat._spare = materialBuffer[3];
return mat;
}
<@endif@>

View file

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

View file

@ -37,6 +37,16 @@ public:
QString updateSlot;
};
namespace AccountManagerAuth {
enum Type {
None,
Required,
Optional
};
}
Q_DECLARE_METATYPE(AccountManagerAuth::Type);
const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
class AccountManager : public QObject {
@ -44,19 +54,13 @@ class AccountManager : public QObject {
public:
static AccountManager& getInstance(bool forceReset = false);
void authenticatedRequest(const QString& path,
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
const QByteArray& dataByteArray = QByteArray(),
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()) ;
Q_INVOKABLE void sendRequest(const QString& path,
AccountManagerAuth::Type authType,
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; }
void setAuthURL(const QUrl& authURL);
@ -107,14 +111,6 @@ private:
void passSuccessToCallback(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;
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;

View file

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

View file

@ -23,7 +23,7 @@
#include "AccountManager.h"
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 glm::quat (*OrientationGetter)();

View file

@ -36,7 +36,7 @@ const char SOLO_NODE_TYPES[2] = {
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) :
linkedDataCreateCallback(NULL),

View file

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

View file

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

View file

@ -18,6 +18,7 @@
#include <QTimer>
#include <SharedUtil.h>
#include <assert.h>
#include "NetworkAccessManager.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,
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()) {
// This will re-call this method in the main thread. If block is true and the main thread
// is waiting on a lock, we'll deadlock here.
if (block) {
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);
}
assert(delayLoad);
getResourceAsynchronously(url);
return QSharedPointer<Resource>();
}
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {
return getResource(fallback, QUrl(), delayLoad);
}
QSharedPointer<Resource> resource = _resources.value(url);
if (resource.isNull()) {
resource = createResource(url, fallback.isValid() ?
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);

View file

@ -21,6 +21,8 @@
#include <QSharedPointer>
#include <QUrl>
#include <QWeakPointer>
#include <QReadWriteLock>
#include <QQueue>
#include <DependencyManager.h>
@ -79,6 +81,9 @@ public:
void refresh(const QUrl& url);
public slots:
void checkAsynchronousGets();
protected:
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
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 extra extra data to pass to the creator, if appropriate
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.
virtual QSharedPointer<Resource> createResource(const QUrl& url,
@ -109,6 +114,11 @@ private:
int _lastLRUKey = 0;
static int _requestLimit;
void getResourceAsynchronously(const QUrl& url);
QReadWriteLock _resourcesToBeGottenLock;
QQueue<QUrl> _resourcesToBeGotten;
};
/// Base class for resources.

View file

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

View file

@ -23,7 +23,7 @@ const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of t
const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * 400.0f;
// This is used in the LOD Tools to translate between the size scale slider and the values used to set the OctreeSizeScale
const float MAX_LOD_SIZE_MULTIPLIER = 2000.0f;
const float MAX_LOD_SIZE_MULTIPLIER = 800.0f;
const int NUMBER_OF_CHILDREN = 8;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
/*
Bullet Continuous Collision Detection and Physics Library
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.
In no event will the authors be held liable for any damages arising from the use of this software.
@ -19,6 +20,8 @@ subject to the following restrictions:
#ifndef hifi_CharacterController_h
#define hifi_CharacterController_h
#include <AvatarData.h>
#include <btBulletDynamicsCommon.h>
#include <BulletDynamics/Character/btCharacterControllerInterface.h>
#include <BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h>
@ -35,75 +38,79 @@ 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.
///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user.
ATTRIBUTE_ALIGNED16(class) CharacterController : public btCharacterControllerInterface
{
protected:
btScalar m_halfHeight;
AvatarData* _avatarData = NULL;
btPairCachingGhostObject* _ghostObject;
btPairCachingGhostObject* m_ghostObject;
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 _radius;
btScalar _halfHeight;
btScalar m_verticalVelocity;
btScalar m_verticalOffset;
btScalar m_fallSpeed;
btScalar m_jumpSpeed;
btScalar m_maxJumpHeight;
btScalar m_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 m_gravity;
btScalar _verticalVelocity;
btScalar _verticalOffset; // fall distance from velocity this frame
btScalar _maxFallSpeed;
btScalar _jumpSpeed;
btScalar _maxJumpHeight;
btScalar _maxSlopeRadians; // Slope angle that is set (used for returning the exact value)
btScalar _maxSlopeCosine; // Cosine equivalent of _maxSlopeRadians (calculated once when set, for optimization)
btScalar _gravity;
btScalar m_turnAngle;
btScalar _stepUpHeight; // height of stepUp prior to stepForward
btScalar _stepDownHeight; // height of stepDown
btScalar m_stepHeight;
btScalar m_addedMargin;//@todo: remove this and fix the code
btScalar _addedMargin;//@todo: remove this and fix the code
///this is the desired walk direction, set by the user
btVector3 m_walkDirection;
btVector3 m_normalizedDirection;
btVector3 _walkDirection;
btVector3 _normalizedDirection;
//some internal variables
btVector3 m_currentPosition;
btScalar m_currentStepOffset;
btVector3 m_targetPosition;
btVector3 _currentPosition;
btQuaternion _currentRotation;
btVector3 _targetPosition;
glm::vec3 _lastPosition;
btScalar _lastStepUp;
///keep track of the contact manifolds
btManifoldArray m_manifoldArray;
btManifoldArray _manifoldArray;
bool m_touchingContact;
btVector3 m_touchingNormal;
bool _touchingContact;
btVector3 _floorNormal; // points from object to character
bool m_wasOnGround;
bool m_wasJumping;
bool m_useGhostObjectSweepTest;
bool m_useWalkDirection;
btScalar m_velocityTimeInterval;
int m_upAxis;
bool _enabled;
bool _isOnGround;
bool _isJumping;
bool _isHovering;
quint64 _jumpToHoverStart;
btScalar _velocityTimeInterval;
btScalar _stepDt;
uint32_t _pendingFlags;
static btVector3* getUpAxisDirections();
bool m_interpolateUp;
bool full_drop;
bool bounce_fix;
glm::vec3 _shapeLocalOffset;
glm::vec3 _boxScale; // used to compute capsule shape
btDynamicsWorld* _dynamicsWorld = NULL;
btVector3 computeReflectionDirection(const btVector3& direction, const btVector3& normal);
btVector3 parallelComponent(const btVector3& direction, const btVector3& normal);
btVector3 perpindicularComponent(const btVector3& direction, const btVector3& normal);
bool recoverFromPenetration(btCollisionWorld* collisionWorld);
void scanDown(btCollisionWorld* collisionWorld);
void stepUp(btCollisionWorld* collisionWorld);
void updateTargetPositionBasedOnCollision(const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0));
void stepForwardAndStrafe(btCollisionWorld* collisionWorld, const btVector3& walkMove);
void stepForward(btCollisionWorld* collisionWorld, const btVector3& walkMove);
void stepDown(btCollisionWorld* collisionWorld, btScalar dt);
void createShapeAndGhost();
public:
BT_DECLARE_ALIGNED_ALLOCATOR();
CharacterController(
btPairCachingGhostObject* ghostObject,
btConvexShape* convexShape,
btScalar stepHeight,
int upAxis = 1);
CharacterController(AvatarData* avatarData);
~CharacterController();
@ -116,14 +123,6 @@ public:
///btActionInterface interface
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 is neither a direction nor a velocity, but the amount to
/// increment the position each simulation iteration, regardless
@ -139,18 +138,19 @@ public:
virtual void setVelocityForTimeInterval(const btVector3& velocity,
btScalar timeInterval);
void reset(btCollisionWorld* collisionWorld );
void warp(const btVector3& origin);
virtual void reset(btCollisionWorld* collisionWorld );
virtual void warp(const btVector3& origin);
void preStep(btCollisionWorld* collisionWorld);
void playerStep(btCollisionWorld* collisionWorld, btScalar dt);
virtual void preStep(btCollisionWorld* collisionWorld);
virtual void playerStep(btCollisionWorld* collisionWorld, btScalar dt);
void setFallSpeed(btScalar fallSpeed);
virtual bool canJump() const;
virtual void jump();
virtual bool onGround() const;
void setMaxFallSpeed(btScalar speed);
void setJumpSpeed(btScalar jumpSpeed);
void setMaxJumpHeight(btScalar maxJumpHeight);
bool canJump() const;
void jump();
void setGravity(btScalar gravity);
btScalar getGravity() const;
@ -161,12 +161,21 @@ public:
btScalar getMaxSlope() const;
btPairCachingGhostObject* getGhostObject();
void setUseGhostSweepTest(bool useGhostObjectSweepTest) {
m_useGhostObjectSweepTest = useGhostObjectSweepTest;
}
bool onGround() const;
void setUpInterpolate(bool value);
bool needsRemoval() const;
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 postSimulation();
};
#endif // hifi_CharacterController_h

View file

@ -169,7 +169,9 @@ void EntityMotionState::updateObjectVelocities() {
}
void EntityMotionState::computeShapeInfo(ShapeInfo& shapeInfo) {
_entity->computeShapeInfo(shapeInfo);
if (_entity->isReadyToComputeShape()) {
_entity->computeShapeInfo(shapeInfo);
}
}
float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const {

Some files were not shown because too many files have changed in this diff Show more