mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 13:27:01 +02:00
Merge branch 'master' into oculus_old_renderer
This commit is contained in:
commit
5e5bdec88d
126 changed files with 5442 additions and 4203 deletions
assignment-client/src/avatars
cmake/externals/tbb
domain-server
examples
defaultScripts.jsedit.js
entityScripts
example/entities
hmdDefaults.jslibraries
lobby.jslook.jsnotifications.jsusers.jsutilities
interface
external/sixense
resources/html
src
Application.cppApplication.hDatagramProcessor.cppDiscoverabilityManager.cppLODManager.cppLODManager.hMenu.cppMenu.hModelPackager.cppModelPackager.hModelPropertiesDialog.cppModelPropertiesDialog.h
avatar
devices
scripting
ControllerScriptingInterface.cppWebWindowClass.cppWebWindowClass.hWindowScriptingInterface.cppWindowScriptingInterface.h
ui
ui
libraries
audio/src
avatars/src
entities-renderer/src
entities/src
EntityItem.cppEntityItem.hEntityItemID.cppEntityItemProperties.cppEntityItemProperties.hEntityItemPropertiesDefaults.hEntityTree.cppEntityTree.hModelEntityItem.cppModelEntityItem.h
fbx/src
gpu/src/gpu
Batch.hConfig.slhGLBackendPipeline.cppGLBackendShader.cppGLBackendShared.hGLBackendTransform.cppGPUConfig.hShader.hTransform.slh
model/src/model
networking/src
AccountManager.cppAccountManager.hAddressManager.cppAddressManager.hLimitedNodeList.cppPacketHeaders.cppPacketHeaders.hResourceCache.cppResourceCache.hUserActivityLogger.cpp
octree/src
physics/src
|
@ -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;
|
||||
|
|
27
cmake/externals/tbb/CMakeLists.txt
vendored
27
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -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 ()
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
348
examples/edit.js
348
examples/edit.js
|
@ -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();
|
||||
|
|
228
examples/entityScripts/lightController.js
Normal file
228
examples/entityScripts/lightController.js
Normal 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();
|
||||
}
|
||||
};
|
||||
})
|
|
@ -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;
|
||||
|
|
157
examples/example/entities/makeHouses.js
Normal file
157
examples/example/entities/makeHouses.js
Normal 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);
|
||||
})();
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
});
|
2
interface/external/sixense/readme.txt
vendored
2
interface/external/sixense/readme.txt
vendored
|
@ -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.
|
||||
|
|
2058
interface/resources/html/edit-commands.html
Normal file
2058
interface/resources/html/edit-commands.html
Normal file
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 |
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <QMessageBox>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
#include <FSTReader.h>
|
||||
|
||||
#include "ModelSelector.h"
|
||||
#include "ModelPropertiesDialog.h"
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
#include <QFileInfo>
|
||||
#include <QVariantHash>
|
||||
|
||||
#include <FBXReader.h>
|
||||
|
||||
#include "ui/ModelsBrowser.h"
|
||||
|
||||
class ModelPackager : public QObject {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <FSTReader.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "ModelPropertiesDialog.h"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -114,7 +114,7 @@ private:
|
|||
quint64 _lastMouseMove;
|
||||
bool _magnifier;
|
||||
|
||||
float _alpha;
|
||||
float _alpha = 1.0f;
|
||||
float _oculusUIRadius;
|
||||
float _trailingAudioLoudness;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
<string><style type="text/css">
|
||||
a { text-decoration: none; color: #267077;}
|
||||
</style>
|
||||
Invalid username or password. <a href="https://metaverse.highfidelity.io/password/new">Recover?</a></string>
|
||||
Invalid username or password. <a href="https://metaverse.highfidelity.com/password/new">Recover?</a></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><style type="text/css">
|
||||
a { text-decoration: none; color: #267077;}
|
||||
</style>
|
||||
<a href="https://metaverse.highfidelity.io/password/new">Recover password?</a></string>
|
||||
<a href="https://metaverse.highfidelity.com/password/new">Recover password?</a></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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*)
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
//
|
||||
|
|
|
@ -25,7 +25,7 @@ EntityItemID::EntityItemID() :
|
|||
creatorTokenID(UNKNOWN_ENTITY_TOKEN),
|
||||
isKnownID(false)
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
EntityItemID::EntityItemID(const EntityItemID& other) :
|
||||
id(other.id),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
99
libraries/fbx/src/FSTReader.cpp
Normal file
99
libraries/fbx/src/FSTReader.cpp
Normal 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();
|
||||
}
|
36
libraries/fbx/src/FSTReader.h
Normal file
36
libraries/fbx/src/FSTReader.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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@>
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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, ¤tProgram);
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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@>
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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@>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)();
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue