mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-25 13:23:19 +02:00
Merge 'master' into 'out-of-body-experience'
This commit is contained in:
commit
d54c07ed14
101 changed files with 3910 additions and 277 deletions
|
@ -1278,6 +1278,13 @@ void DomainServer::handleMetaverseHeartbeatError(QNetworkReply& requestReply) {
|
|||
}
|
||||
|
||||
void DomainServer::sendICEServerAddressToMetaverseAPI() {
|
||||
if (_sendICEServerAddressToMetaverseAPIInProgress) {
|
||||
// don't have more than one of these in-flight at a time. set a flag to indicate that once the current one
|
||||
// is done, we need to do update metaverse again.
|
||||
_sendICEServerAddressToMetaverseAPIRedo = true;
|
||||
return;
|
||||
}
|
||||
_sendICEServerAddressToMetaverseAPIInProgress = true;
|
||||
const QString ICE_SERVER_ADDRESS = "ice_server_address";
|
||||
|
||||
QJsonObject domainObject;
|
||||
|
@ -1302,6 +1309,8 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
|
|||
JSONCallbackParameters callbackParameters;
|
||||
callbackParameters.errorCallbackReceiver = this;
|
||||
callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate";
|
||||
callbackParameters.jsonCallbackReceiver = this;
|
||||
callbackParameters.jsonCallbackMethod = "handleSuccessfulICEServerAddressUpdate";
|
||||
|
||||
static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex
|
||||
("Updating ice-server address in High Fidelity Metaverse API to [^ \n]+");
|
||||
|
@ -1317,13 +1326,32 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() {
|
|||
domainUpdateJSON.toUtf8());
|
||||
}
|
||||
|
||||
void DomainServer::handleSuccessfulICEServerAddressUpdate(QNetworkReply& requestReply) {
|
||||
_sendICEServerAddressToMetaverseAPIInProgress = false;
|
||||
if (_sendICEServerAddressToMetaverseAPIRedo) {
|
||||
qDebug() << "ice-server address updated with metaverse, but has since changed. redoing update...";
|
||||
_sendICEServerAddressToMetaverseAPIRedo = false;
|
||||
sendICEServerAddressToMetaverseAPI();
|
||||
} else {
|
||||
qDebug() << "ice-server address updated with metaverse.";
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::handleFailedICEServerAddressUpdate(QNetworkReply& requestReply) {
|
||||
const int ICE_SERVER_UPDATE_RETRY_MS = 2 * 1000;
|
||||
_sendICEServerAddressToMetaverseAPIInProgress = false;
|
||||
if (_sendICEServerAddressToMetaverseAPIRedo) {
|
||||
// if we have new data, retry right away, even though the previous attempt didn't go well.
|
||||
_sendICEServerAddressToMetaverseAPIRedo = false;
|
||||
sendICEServerAddressToMetaverseAPI();
|
||||
} else {
|
||||
const int ICE_SERVER_UPDATE_RETRY_MS = 2 * 1000;
|
||||
|
||||
qWarning() << "Failed to update ice-server address with High Fidelity Metaverse - error was" << requestReply.errorString();
|
||||
qWarning() << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds";
|
||||
qWarning() << "Failed to update ice-server address with High Fidelity Metaverse - error was"
|
||||
<< requestReply.errorString();
|
||||
qWarning() << "\tRe-attempting in" << ICE_SERVER_UPDATE_RETRY_MS / 1000 << "seconds";
|
||||
|
||||
QTimer::singleShot(ICE_SERVER_UPDATE_RETRY_MS, this, SLOT(sendICEServerAddressToMetaverseAPI()));
|
||||
QTimer::singleShot(ICE_SERVER_UPDATE_RETRY_MS, this, SLOT(sendICEServerAddressToMetaverseAPI()));
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::sendHeartbeatToIceServer() {
|
||||
|
|
|
@ -96,6 +96,7 @@ private slots:
|
|||
void handleICEHostInfo(const QHostInfo& hostInfo);
|
||||
|
||||
void sendICEServerAddressToMetaverseAPI();
|
||||
void handleSuccessfulICEServerAddressUpdate(QNetworkReply& requestReply);
|
||||
void handleFailedICEServerAddressUpdate(QNetworkReply& requestReply);
|
||||
|
||||
signals:
|
||||
|
@ -211,6 +212,9 @@ private:
|
|||
int _iceServerPort;
|
||||
bool _overrideDomainID { false }; // should we override the domain-id from settings?
|
||||
QUuid _overridingDomainID { QUuid() }; // what should we override it with?
|
||||
|
||||
bool _sendICEServerAddressToMetaverseAPIInProgress { false };
|
||||
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,28 +1,49 @@
|
|||
{
|
||||
"name": "XBox to Standard",
|
||||
"channels": [
|
||||
{ "from": "GamePad.LY", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Standard.LY" },
|
||||
{ "from": "GamePad.LX", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Standard.LX" },
|
||||
{ "from": "GamePad.LY", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.TranslateZ" },
|
||||
{ "from": "GamePad.LX", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Actions.TranslateX" },
|
||||
{ "from": "GamePad.LT", "to": "Standard.LT" },
|
||||
{ "from": "GamePad.LB", "to": "Standard.LB" },
|
||||
{ "from": "GamePad.LS", "to": "Standard.LS" },
|
||||
|
||||
{ "from": "GamePad.RY", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Standard.RY" },
|
||||
{ "from": "GamePad.RX", "filters": { "type": "deadZone", "min": 0.05 }, "to": "Standard.RX" },
|
||||
|
||||
{ "from": "GamePad.RX",
|
||||
"when": [ "Application.InHMD", "Application.SnapTurn" ],
|
||||
"to": "Actions.StepYaw",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "deadZone", "min": 0.15 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.25 },
|
||||
{ "type": "scale", "scale": 22.5 }
|
||||
]
|
||||
},
|
||||
|
||||
{ "from": "GamePad.RX", "to": "Actions.Yaw" },
|
||||
|
||||
{ "from": "GamePad.RY",
|
||||
"to": "Actions.VERTICAL_UP",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "deadZone", "min": 0.95 },
|
||||
"invert"
|
||||
]
|
||||
},
|
||||
|
||||
{ "from": "GamePad.RT", "to": "Standard.RT" },
|
||||
{ "from": "GamePad.RB", "to": "Standard.RB" },
|
||||
{ "from": "GamePad.RS", "to": "Standard.RS" },
|
||||
|
||||
{ "from": "GamePad.Back", "to": "Standard.Back" },
|
||||
{ "from": "GamePad.Start", "to": "Standard.Start" },
|
||||
{ "from": "GamePad.Start", "to": "Actions.CycleCamera" },
|
||||
{ "from": "GamePad.Back", "to": "Actions.ContextMenu" },
|
||||
|
||||
{ "from": [ "GamePad.DU", "GamePad.DL", "GamePad.DR", "GamePad.DD" ], "to": "Standard.LeftPrimaryThumb", "peek": true },
|
||||
{ "from": "GamePad.DU", "to": "Standard.DU" },
|
||||
{ "from": "GamePad.DD", "to": "Standard.DD" },
|
||||
{ "from": "GamePad.DL", "to": "Standard.DL" },
|
||||
{ "from": "GamePad.DR", "to": "Standard.DR" },
|
||||
|
||||
{ "from": [ "GamePad.A", "GamePad.B", "GamePad.X", "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true },
|
||||
{ "from": [ "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true },
|
||||
{ "from": "GamePad.A", "to": "Standard.A" },
|
||||
{ "from": "GamePad.B", "to": "Standard.B" },
|
||||
{ "from": "GamePad.X", "to": "Standard.X" },
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
Binary file not shown.
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 67 KiB |
Binary file not shown.
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
@ -221,7 +221,7 @@ Window {
|
|||
}
|
||||
}
|
||||
onActiveFocusChanged: {
|
||||
cursorVisible = isCursorVisible;
|
||||
cursorVisible = isCursorVisible && focus;
|
||||
}
|
||||
MouseArea {
|
||||
// If user clicks in address bar show cursor to indicate ability to enter address.
|
||||
|
@ -229,6 +229,7 @@ Window {
|
|||
onClicked: {
|
||||
isCursorVisible = true;
|
||||
parent.cursorVisible = true;
|
||||
parent.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ ScrollingWindow {
|
|||
prompt.selected.connect(function (jsonResult) {
|
||||
if (jsonResult) {
|
||||
var result = JSON.parse(jsonResult);
|
||||
var url = result.textInput;
|
||||
var url = result.textInput.trim();
|
||||
var shapeType;
|
||||
switch (result.comboBox) {
|
||||
case SHAPE_TYPE_SIMPLE_HULL:
|
||||
|
|
|
@ -27,6 +27,11 @@ Item {
|
|||
loginDialog.login(usernameField.text, passwordField.text)
|
||||
}
|
||||
|
||||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
|
||||
onKeyboardRaisedChanged: d.resize();
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int minWidth: 480
|
||||
|
@ -35,13 +40,13 @@ Item {
|
|||
readonly property int maxHeight: 720
|
||||
|
||||
function resize() {
|
||||
var targetWidth = Math.max(titleWidth, form.contentWidth)
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
|
||||
4 * hifi.dimensions.contentSpacing.y + form.height +
|
||||
4 * hifi.dimensions.contentSpacing.y + buttons.height
|
||||
var targetWidth = Math.max(titleWidth, form.contentWidth);
|
||||
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height
|
||||
+ 4 * hifi.dimensions.contentSpacing.y + form.height + hifi.dimensions.contentSpacing.y + buttons.height;
|
||||
|
||||
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
||||
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||
+ (linkAccountBody.keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,13 +135,39 @@ Item {
|
|||
|
||||
}
|
||||
|
||||
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
|
||||
Keyboard {
|
||||
y: parent.keyboardRaised ? parent.height : 0
|
||||
height: parent.keyboardRaised ? 200 : 0
|
||||
visible: parent.keyboardRaised && !parent.punctuationMode
|
||||
enabled: parent.keyboardRaised && !parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: buttons.top
|
||||
bottomMargin: parent.keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardPunctuation {
|
||||
y: parent.keyboardRaised ? parent.height : 0
|
||||
height: parent.keyboardRaised ? 200 : 0
|
||||
visible: parent.keyboardRaised && parent.punctuationMode
|
||||
enabled: parent.keyboardRaised && parent.punctuationMode
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: buttons.top
|
||||
bottomMargin: parent.keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: buttons
|
||||
anchors {
|
||||
top: form.bottom
|
||||
right: parent.right
|
||||
margins: 0
|
||||
topMargin: 3 * hifi.dimensions.contentSpacing.y
|
||||
bottom: parent.bottom
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
spacing: hifi.dimensions.contentSpacing.x
|
||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||
|
|
|
@ -32,6 +32,8 @@ FocusScope {
|
|||
|
||||
readonly property ComboBox control: comboBox
|
||||
|
||||
signal accepted();
|
||||
|
||||
implicitHeight: comboBox.height;
|
||||
focus: true
|
||||
|
||||
|
@ -134,6 +136,7 @@ FocusScope {
|
|||
function hideList() {
|
||||
popup.visible = false;
|
||||
scrollView.hoverEnabled = false;
|
||||
root.accepted();
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
|
|
|
@ -211,6 +211,7 @@ ModalWindow {
|
|||
left: parent.left;
|
||||
bottom: parent.bottom;
|
||||
leftMargin: 6; // Magic number to align with warning icon
|
||||
bottomMargin: 6;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +225,10 @@ ModalWindow {
|
|||
bottom: parent.bottom;
|
||||
}
|
||||
model: root.comboBox ? root.comboBox.items : [];
|
||||
onCurrentTextChanged: updateCheckbox();
|
||||
onAccepted: {
|
||||
updateCheckbox();
|
||||
focus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,6 +340,7 @@ ModalWindow {
|
|||
|
||||
Component.onCompleted: {
|
||||
updateIcon();
|
||||
updateCheckbox();
|
||||
d.resize();
|
||||
textField.forceActiveFocus();
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
#include <PhysicsEngine.h>
|
||||
#include <PhysicsHelpers.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <plugins/PluginUtils.h>
|
||||
#include <plugins/CodecPlugin.h>
|
||||
#include <RecordingScriptingInterface.h>
|
||||
#include <RenderableWebEntityItem.h>
|
||||
|
@ -1255,8 +1256,87 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
return entityServerNode && !isPhysicsEnabled();
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Get sandbox content set version, if available
|
||||
auto acDirPath = PathUtils::getRootDataDirectory() + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/";
|
||||
auto contentVersionPath = acDirPath + "content-version.txt";
|
||||
qDebug() << "Checking " << contentVersionPath << " for content version";
|
||||
auto contentVersion = 0;
|
||||
QFile contentVersionFile(contentVersionPath);
|
||||
if (contentVersionFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QString line = contentVersionFile.readAll();
|
||||
// toInt() returns 0 if the conversion fails, so we don't need to specifically check for failure
|
||||
contentVersion = line.toInt();
|
||||
}
|
||||
qDebug() << "Server content version: " << contentVersion;
|
||||
|
||||
bool hasTutorialContent = contentVersion >= 1;
|
||||
|
||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||
bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable("OpenVR (Vive)") && PluginUtils::isHandControllerAvailable();
|
||||
Setting::Handle<bool> tutorialComplete { "tutorialComplete", false };
|
||||
|
||||
bool shouldGoToTutorial = hasHMDAndHandControllers && hasTutorialContent && !tutorialComplete.get();
|
||||
|
||||
qDebug() << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName();
|
||||
qDebug() << "Has tutorial content: " << hasTutorialContent;
|
||||
qDebug() << "Tutorial complete: " << tutorialComplete.get();
|
||||
qDebug() << "Should go to tutorial: " << shouldGoToTutorial;
|
||||
|
||||
// when --url in command line, teleport to location
|
||||
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
|
||||
int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY);
|
||||
QString addressLookupString;
|
||||
if (urlIndex != -1) {
|
||||
addressLookupString = arguments().value(urlIndex + 1);
|
||||
}
|
||||
|
||||
const QString TUTORIAL_PATH = "/tutorial_begin";
|
||||
|
||||
if (shouldGoToTutorial) {
|
||||
DependencyManager::get<AddressManager>()->ifLocalSandboxRunningElse([=]() {
|
||||
qDebug() << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH);
|
||||
}, [=]() {
|
||||
qDebug() << "Home sandbox does not appear to be running, going to Entry.";
|
||||
if (firstRun.get()) {
|
||||
showHelp();
|
||||
}
|
||||
if (addressLookupString.isEmpty()) {
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
} else {
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
bool isFirstRun = firstRun.get();
|
||||
|
||||
if (isFirstRun) {
|
||||
showHelp();
|
||||
}
|
||||
|
||||
// If this is a first run we short-circuit the address passed in
|
||||
if (isFirstRun) {
|
||||
if (hasHMDAndHandControllers) {
|
||||
DependencyManager::get<AddressManager>()->ifLocalSandboxRunningElse([=]() {
|
||||
qDebug() << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox();
|
||||
}, [=]() {
|
||||
qDebug() << "Home sandbox does not appear to be running, going to Entry.";
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
});
|
||||
} else {
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
}
|
||||
}
|
||||
|
||||
// After all of the constructor is completed, then set firstRun to false.
|
||||
Setting::Handle<bool> firstRun{ Settings::firstRun, true };
|
||||
firstRun.set(false);
|
||||
}
|
||||
|
||||
|
@ -3279,15 +3359,6 @@ void Application::init() {
|
|||
|
||||
_timerStart.start();
|
||||
_lastTimeUpdated.start();
|
||||
|
||||
// when --url in command line, teleport to location
|
||||
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
|
||||
int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY);
|
||||
QString addressLookupString;
|
||||
if (urlIndex != -1) {
|
||||
addressLookupString = arguments().value(urlIndex + 1);
|
||||
}
|
||||
|
||||
// when +connect_lobby in command line, join steam lobby
|
||||
const QString STEAM_LOBBY_COMMAND_LINE_KEY = "+connect_lobby";
|
||||
int lobbyIndex = arguments().indexOf(STEAM_LOBBY_COMMAND_LINE_KEY);
|
||||
|
@ -3296,21 +3367,6 @@ void Application::init() {
|
|||
SteamClient::joinLobby(lobbyId);
|
||||
}
|
||||
|
||||
Setting::Handle<bool> firstRun { Settings::firstRun, true };
|
||||
if (addressLookupString.isEmpty() && firstRun.get()) {
|
||||
qCDebug(interfaceapp) << "First run and no URL passed... attempting to go to Home or Entry...";
|
||||
DependencyManager::get<AddressManager>()->ifLocalSandboxRunningElse([](){
|
||||
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
|
||||
DependencyManager::get<AddressManager>()->goToLocalSandbox();
|
||||
},
|
||||
[](){
|
||||
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
|
||||
DependencyManager::get<AddressManager>()->goToEntry();
|
||||
});
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
|
||||
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
|
||||
}
|
||||
|
||||
qCDebug(interfaceapp) << "Loaded settings";
|
||||
|
||||
|
@ -4562,11 +4618,15 @@ void Application::resetSensors(bool andReload) {
|
|||
|
||||
void Application::updateWindowTitle() const {
|
||||
|
||||
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
||||
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) ";
|
||||
QString username = DependencyManager::get<AccountManager>()->getAccountInfo().getUsername();
|
||||
QString buildVersion = " (build " + applicationVersion() + ")";
|
||||
|
||||
QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)";
|
||||
|
||||
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
||||
QString username = accountManager->getAccountInfo().getUsername();
|
||||
QString currentPlaceName = DependencyManager::get<AddressManager>()->getHost();
|
||||
|
||||
if (currentPlaceName.isEmpty()) {
|
||||
|
@ -4574,7 +4634,7 @@ void Application::updateWindowTitle() const {
|
|||
}
|
||||
|
||||
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||
+ currentPlaceName + connectionStatus + buildVersion;
|
||||
+ currentPlaceName + connectionStatus + loginStatus + buildVersion;
|
||||
|
||||
#ifndef WIN32
|
||||
// crashes with vs2013/win32
|
||||
|
|
|
@ -13,10 +13,12 @@
|
|||
|
||||
#include <QtScript/QScriptContext>
|
||||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <plugins/PluginUtils.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
HMDScriptingInterface::HMDScriptingInterface() {
|
||||
|
@ -47,6 +49,14 @@ glm::vec2 HMDScriptingInterface::overlayToSpherical(const glm::vec2 & position)
|
|||
return qApp->getApplicationCompositor().overlayToSpherical(position);
|
||||
}
|
||||
|
||||
bool HMDScriptingInterface::isHMDAvailable() {
|
||||
return PluginUtils::isHMDAvailable();
|
||||
}
|
||||
|
||||
bool HMDScriptingInterface::isHandControllerAvailable() {
|
||||
return PluginUtils::isHandControllerAvailable();
|
||||
}
|
||||
|
||||
QScriptValue HMDScriptingInterface::getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine) {
|
||||
glm::vec3 hudIntersection;
|
||||
auto instance = DependencyManager::get<HMDScriptingInterface>();
|
||||
|
|
|
@ -38,6 +38,9 @@ public:
|
|||
Q_INVOKABLE QString preferredAudioInput() const;
|
||||
Q_INVOKABLE QString preferredAudioOutput() const;
|
||||
|
||||
Q_INVOKABLE bool isHMDAvailable();
|
||||
Q_INVOKABLE bool isHandControllerAvailable();
|
||||
|
||||
Q_INVOKABLE bool setHandLasers(int hands, bool enabled, const glm::vec4& color, const glm::vec3& direction) const;
|
||||
|
||||
Q_INVOKABLE void disableHandLasers(int hands) const;
|
||||
|
|
|
@ -124,6 +124,12 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
properties.remove("start"); // so that Base3DOverlay doesn't respond to it
|
||||
|
||||
auto localStart = properties["localStart"];
|
||||
if (localStart.isValid()) {
|
||||
_start = vec3FromVariant(localStart);
|
||||
}
|
||||
properties.remove("localStart"); // so that Base3DOverlay doesn't respond to it
|
||||
|
||||
auto end = properties["end"];
|
||||
// if "end" property was not there, check to see if they included aliases: endPoint
|
||||
if (!end.isValid()) {
|
||||
|
@ -133,6 +139,12 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
setEnd(vec3FromVariant(end));
|
||||
}
|
||||
|
||||
auto localEnd = properties["localEnd"];
|
||||
if (localEnd.isValid()) {
|
||||
_end = vec3FromVariant(localEnd);
|
||||
}
|
||||
properties.remove("localEnd"); // so that Base3DOverlay doesn't respond to it
|
||||
|
||||
auto glow = properties["glow"];
|
||||
if (glow.isValid()) {
|
||||
setGlow(glow.toFloat());
|
||||
|
|
|
@ -867,6 +867,10 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
|||
|
||||
void AudioClient::handleAudioInput() {
|
||||
|
||||
if (!_inputDevice) {
|
||||
return;
|
||||
}
|
||||
|
||||
// input samples required to produce exactly NETWORK_FRAME_SAMPLES of output
|
||||
const int inputSamplesRequired = (_inputToNetworkResampler ?
|
||||
_inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) :
|
||||
|
|
|
@ -95,6 +95,11 @@ namespace controller {
|
|||
return getValue(Input(device, source, ChannelType::BUTTON).getID());
|
||||
}
|
||||
|
||||
float ScriptingInterface::getAxisValue(int source) const {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
return userInputMapper->getValue(Input((uint32_t)source));
|
||||
}
|
||||
|
||||
float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const {
|
||||
return getValue(Input(device, source, ChannelType::AXIS).getID());
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ namespace controller {
|
|||
Q_INVOKABLE float getValue(const int& source) const;
|
||||
Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const;
|
||||
Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const;
|
||||
Q_INVOKABLE float getAxisValue(int source) const;
|
||||
Q_INVOKABLE Pose getPoseValue(const int& source) const;
|
||||
Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const;
|
||||
|
||||
|
|
|
@ -27,19 +27,27 @@ AnyEndpoint::AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPU
|
|||
}
|
||||
}
|
||||
|
||||
// The value of an any-point is considered to be the maxiumum absolute value,
|
||||
// this handles any's of multiple axis values as well as single values as well
|
||||
float AnyEndpoint::peek() const {
|
||||
float result = 0;
|
||||
float result = 0.0f;
|
||||
for (auto& child : _children) {
|
||||
result = std::max(result, child->peek());
|
||||
auto childValue = child->peek();
|
||||
if (std::abs(childValue) > std::abs(result)) {
|
||||
result = childValue;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Fetching the value must trigger any necessary side effects of value() on ALL the children.
|
||||
float AnyEndpoint::value() {
|
||||
float result = 0;
|
||||
float result = 0.0f;
|
||||
for (auto& child : _children) {
|
||||
result = std::max(result, child->value());
|
||||
auto childValue = child->value();
|
||||
if (std::abs(childValue) > std::abs(result)) {
|
||||
result = childValue;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -124,7 +124,6 @@ void HmdDisplayPlugin::uncustomizeContext() {
|
|||
batch.setFramebuffer(_compositeFramebuffer);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
|
||||
});
|
||||
internalPresent();
|
||||
_overlayRenderer = OverlayRenderer();
|
||||
Parent::uncustomizeContext();
|
||||
}
|
||||
|
@ -375,7 +374,8 @@ void HmdDisplayPlugin::updateFrameData() {
|
|||
}
|
||||
|
||||
// this offset needs to match GRAB_POINT_SPHERE_OFFSET in scripts/system/libraries/controllers.js
|
||||
static const vec3 GRAB_POINT_SPHERE_OFFSET = vec3(0.1f, 0.04f, -0.32f);
|
||||
//static const vec3 GRAB_POINT_SPHERE_OFFSET = vec3(0.1f, 0.04f, -0.32f);
|
||||
static const vec3 GRAB_POINT_SPHERE_OFFSET = vec3(0.0f, 0.0f, -0.175f);
|
||||
vec3 grabPointOffset = GRAB_POINT_SPHERE_OFFSET;
|
||||
if (i == 0) {
|
||||
grabPointOffset.x *= -1.0f; // this changes between left and right hands
|
||||
|
|
|
@ -478,7 +478,8 @@ bool EntityTreeRenderer::applySkyboxAndHasAmbient() {
|
|||
}
|
||||
}
|
||||
|
||||
if (_pendingSkyboxTexture && !_skyboxTexture) {
|
||||
if (_pendingSkyboxTexture &&
|
||||
(!_skyboxTexture || (_skyboxTexture->getURL() != _skyboxTextureURL))) {
|
||||
_skyboxTexture = textureCache->getTexture(_skyboxTextureURL, NetworkTexture::CUBE_TEXTURE);
|
||||
}
|
||||
if (_skyboxTexture && _skyboxTexture->isLoaded()) {
|
||||
|
|
|
@ -83,6 +83,9 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
|||
++_currentWebCount;
|
||||
// Save the original GL context, because creating a QML surface will create a new context
|
||||
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
||||
if (!currentContext) {
|
||||
return false;
|
||||
}
|
||||
QSurface * currentSurface = currentContext->surface();
|
||||
|
||||
auto deleter = [](OffscreenQmlSurface* webSurface) {
|
||||
|
|
|
@ -666,7 +666,6 @@ void GLBackend::recycle() const {
|
|||
for (auto pair : externalTexturesTrash) {
|
||||
auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
pair.second(pair.first, fence);
|
||||
decrementTextureGPUCount();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,6 +192,11 @@ void GLBackend::resetResourceStage() {
|
|||
|
||||
void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) {
|
||||
GLuint slot = batch._params[paramOffset + 1]._uint;
|
||||
if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) {
|
||||
// "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" + slot + " which doesn't exist. MaxNumResourceTextures = " + getMaxNumResourceTextures());
|
||||
return;
|
||||
}
|
||||
|
||||
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||
|
||||
if (!resourceTexture) {
|
||||
|
|
|
@ -20,9 +20,20 @@ std::shared_ptr<GLTextureTransferHelper> GLTexture::_textureTransferHelper;
|
|||
|
||||
// FIXME placeholder for texture memory over-use
|
||||
#define DEFAULT_MAX_MEMORY_MB 256
|
||||
#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f
|
||||
#define OVER_MEMORY_PRESSURE 2.0f
|
||||
|
||||
// FIXME other apps show things like Oculus home consuming large amounts of GPU memory
|
||||
// which causes us to blur textures needlessly (since other app GPU memory usage will likely
|
||||
// be swapped out and not cause any actual impact
|
||||
//#define CHECK_MIN_FREE_GPU_MEMORY
|
||||
#ifdef CHECK_MIN_FREE_GPU_MEMORY
|
||||
#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f
|
||||
#endif
|
||||
|
||||
// Allow 65% of all available GPU memory to be consumed by textures
|
||||
// FIXME overly conservative?
|
||||
#define MAX_CONSUMED_TEXTURE_MEMORY_PERCENTAGE 0.65f
|
||||
|
||||
const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = {
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
||||
|
@ -107,6 +118,7 @@ float GLTexture::getMemoryPressure() {
|
|||
// If we can't query the dedicated memory just use a fallback fixed value of 256 MB
|
||||
totalGpuMemory = MB_TO_BYTES(DEFAULT_MAX_MEMORY_MB);
|
||||
} else {
|
||||
#ifdef CHECK_MIN_FREE_GPU_MEMORY
|
||||
// Check the global free GPU memory
|
||||
auto freeGpuMemory = getFreeDedicatedMemory();
|
||||
if (freeGpuMemory) {
|
||||
|
@ -115,21 +127,26 @@ float GLTexture::getMemoryPressure() {
|
|||
if (freeGpuMemory != lastFreeGpuMemory) {
|
||||
lastFreeGpuMemory = freeGpuMemory;
|
||||
if (freePercentage < MIN_FREE_GPU_MEMORY_PERCENTAGE) {
|
||||
qDebug() << "Exceeded max GPU memory";
|
||||
qCDebug(gpugllogging) << "Exceeded min free GPU memory " << freePercentage;
|
||||
return OVER_MEMORY_PRESSURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Allow 50% of all available GPU memory to be consumed by textures
|
||||
// FIXME overly conservative?
|
||||
availableTextureMemory = (totalGpuMemory >> 1);
|
||||
availableTextureMemory = static_cast<gpu::Size>(totalGpuMemory * MAX_CONSUMED_TEXTURE_MEMORY_PERCENTAGE);
|
||||
}
|
||||
|
||||
// Return the consumed texture memory divided by the available texture memory.
|
||||
auto consumedGpuMemory = Context::getTextureGPUMemoryUsage();
|
||||
return (float)consumedGpuMemory / (float)availableTextureMemory;
|
||||
float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory;
|
||||
static Context::Size lastConsumedGpuMemory = 0;
|
||||
if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) {
|
||||
lastConsumedGpuMemory = consumedGpuMemory;
|
||||
qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory;
|
||||
}
|
||||
return memoryPressure;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ Buffer::Size Buffer::getBufferGPUMemoryUsage() {
|
|||
}
|
||||
|
||||
Buffer::Buffer(Size pageSize) :
|
||||
_pages(pageSize) {
|
||||
_renderPages(pageSize), _pages(pageSize) {
|
||||
_bufferCPUCount++;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ vec2 float32x3_to_oct(in vec3 v) {
|
|||
|
||||
vec3 oct_to_float32x3(in vec2 e) {
|
||||
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
|
||||
if (v.z < 0) {
|
||||
if (v.z < 0.0) {
|
||||
v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
|
||||
}
|
||||
return normalize(v);
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
bool isSupported() const override { return true; }
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
bool isHandController() const override { return false; }
|
||||
|
||||
void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
|
||||
void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ public:
|
|||
virtual bool isSupported() const override;
|
||||
virtual const QString& getName() const override { return NAME; }
|
||||
|
||||
bool isHandController() const override { return false; }
|
||||
|
||||
virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); }
|
||||
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override;
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ const gpu::TexturePointer& TextureCache::getGrayTexture() {
|
|||
if (!_grayTexture) {
|
||||
_grayTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||
_grayTexture->setSource("TextureCache::_grayTexture");
|
||||
_grayTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_GRAY);
|
||||
_grayTexture->assignStoredMip(0, _grayTexture->getTexelFormat(), sizeof(OPAQUE_GRAY), OPAQUE_GRAY);
|
||||
}
|
||||
return _grayTexture;
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() {
|
|||
if (!_blackTexture) {
|
||||
_blackTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, 1, 1));
|
||||
_blackTexture->setSource("TextureCache::_blackTexture");
|
||||
_blackTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK);
|
||||
_blackTexture->assignStoredMip(0, _blackTexture->getTexelFormat(), sizeof(OPAQUE_BLACK), OPAQUE_BLACK);
|
||||
}
|
||||
return _blackTexture;
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ float evalLightAttenuation(Light l, float d) {
|
|||
|
||||
// "Fade" the edges of light sources to make things look a bit more attractive.
|
||||
// Note: this tends to look a bit odd at lower exponents.
|
||||
attenuation *= min(1, max(0, -(d - cutoff)));
|
||||
attenuation *= min(1.0, max(0.0, -(d - cutoff)));
|
||||
|
||||
return attenuation;
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ SphericalHarmonics getLightAmbientSphere(Light l) {
|
|||
}
|
||||
|
||||
bool getLightHasAmbientMap(Light l) {
|
||||
return l._control.x > 0;
|
||||
return l._control.x > 0.0;
|
||||
}
|
||||
|
||||
float getLightAmbientMapNumMips(Light l) {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include "AccountManager.h"
|
||||
|
||||
const QString HIFI_URL_SCHEME = "hifi";
|
||||
const QString DEFAULT_HIFI_ADDRESS = "hifi://entry";
|
||||
const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome";
|
||||
const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
|
||||
const QString SANDBOX_STATUS_URL = "http://localhost:60332/status";
|
||||
const QString INDEX_PATH = "/";
|
||||
|
@ -92,7 +92,7 @@ public slots:
|
|||
|
||||
void goBack();
|
||||
void goForward();
|
||||
void goToLocalSandbox(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(SANDBOX_HIFI_ADDRESS, trigger); }
|
||||
void goToLocalSandbox(QString path = "", LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); }
|
||||
void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(DEFAULT_HIFI_ADDRESS, trigger); }
|
||||
|
||||
void goToUser(const QString& username);
|
||||
|
|
|
@ -141,7 +141,11 @@ bool haveAssetServer() {
|
|||
}
|
||||
|
||||
GetMappingRequest* AssetClient::createGetMappingRequest(const AssetPath& path) {
|
||||
return new GetMappingRequest(path);
|
||||
auto request = new GetMappingRequest(path);
|
||||
|
||||
request->moveToThread(thread());
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
GetAllMappingsRequest* AssetClient::createGetAllMappingsRequest() {
|
||||
|
@ -305,7 +309,7 @@ void AssetClient::handleAssetGetInfoReply(QSharedPointer<ReceivedMessage> messag
|
|||
void AssetClient::handleAssetGetReply(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
|
||||
auto assetHash = message->read(SHA256_HASH_LENGTH);
|
||||
auto assetHash = message->readHead(SHA256_HASH_LENGTH);
|
||||
qCDebug(asset_client) << "Got reply for asset: " << assetHash.toHex();
|
||||
|
||||
MessageID messageID;
|
||||
|
@ -349,8 +353,8 @@ void AssetClient::handleAssetGetReply(QSharedPointer<ReceivedMessage> message, S
|
|||
} else {
|
||||
auto weakNode = senderNode.toWeakRef();
|
||||
|
||||
connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length]() {
|
||||
handleProgressCallback(weakNode, messageID, length);
|
||||
connect(message.data(), &ReceivedMessage::progress, this, [this, weakNode, messageID, length](qint64 size) {
|
||||
handleProgressCallback(weakNode, messageID, size, length);
|
||||
});
|
||||
connect(message.data(), &ReceivedMessage::completed, this, [this, weakNode, messageID]() {
|
||||
handleCompleteCallback(weakNode, messageID);
|
||||
|
@ -358,7 +362,8 @@ void AssetClient::handleAssetGetReply(QSharedPointer<ReceivedMessage> message, S
|
|||
}
|
||||
}
|
||||
|
||||
void AssetClient::handleProgressCallback(const QWeakPointer<Node>& node, MessageID messageID, DataOffset length) {
|
||||
void AssetClient::handleProgressCallback(const QWeakPointer<Node>& node, MessageID messageID,
|
||||
qint64 size, DataOffset length) {
|
||||
auto senderNode = node.toStrongRef();
|
||||
|
||||
if (!senderNode) {
|
||||
|
@ -381,13 +386,7 @@ void AssetClient::handleProgressCallback(const QWeakPointer<Node>& node, Message
|
|||
}
|
||||
|
||||
auto& callbacks = requestIt->second;
|
||||
auto& message = callbacks.message;
|
||||
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks.progressCallback(message->getSize(), length);
|
||||
callbacks.progressCallback(size, length);
|
||||
}
|
||||
|
||||
void AssetClient::handleCompleteCallback(const QWeakPointer<Node>& node, MessageID messageID) {
|
||||
|
|
|
@ -93,7 +93,7 @@ private:
|
|||
bool cancelGetAssetRequest(MessageID id);
|
||||
bool cancelUploadAssetRequest(MessageID id);
|
||||
|
||||
void handleProgressCallback(const QWeakPointer<Node>& node, MessageID messageID, DataOffset length);
|
||||
void handleProgressCallback(const QWeakPointer<Node>& node, MessageID messageID, qint64 size, DataOffset length);
|
||||
void handleCompleteCallback(const QWeakPointer<Node>& node, MessageID messageID);
|
||||
|
||||
struct GetAssetRequestData {
|
||||
|
|
|
@ -11,10 +11,20 @@
|
|||
|
||||
#include "AssetResourceRequest.h"
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
#include "AssetClient.h"
|
||||
#include "AssetUtils.h"
|
||||
#include "MappingRequest.h"
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include "NetworkLogging.h"
|
||||
|
||||
static const int DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS = 5;
|
||||
|
||||
AssetResourceRequest::AssetResourceRequest(const QUrl& url) :
|
||||
ResourceRequest(url)
|
||||
{
|
||||
_lastProgressDebug = p_high_resolution_clock::now() - std::chrono::seconds(DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS);
|
||||
}
|
||||
|
||||
AssetResourceRequest::~AssetResourceRequest() {
|
||||
if (_assetMappingRequest) {
|
||||
|
@ -24,10 +34,6 @@ AssetResourceRequest::~AssetResourceRequest() {
|
|||
if (_assetRequest) {
|
||||
_assetRequest->deleteLater();
|
||||
}
|
||||
|
||||
if (_sendTimer) {
|
||||
cleanupTimer();
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetResourceRequest::urlIsAssetHash() const {
|
||||
|
@ -37,24 +43,6 @@ bool AssetResourceRequest::urlIsAssetHash() const {
|
|||
return hashRegex.exactMatch(_url.toString());
|
||||
}
|
||||
|
||||
void AssetResourceRequest::setupTimer() {
|
||||
Q_ASSERT(!_sendTimer);
|
||||
static const int TIMEOUT_MS = 2000;
|
||||
|
||||
_sendTimer = new QTimer(this);
|
||||
connect(_sendTimer, &QTimer::timeout, this, &AssetResourceRequest::onTimeout);
|
||||
|
||||
_sendTimer->setSingleShot(true);
|
||||
_sendTimer->start(TIMEOUT_MS);
|
||||
}
|
||||
|
||||
void AssetResourceRequest::cleanupTimer() {
|
||||
Q_ASSERT(_sendTimer);
|
||||
disconnect(_sendTimer, 0, this, 0);
|
||||
_sendTimer->deleteLater();
|
||||
_sendTimer = nullptr;
|
||||
}
|
||||
|
||||
void AssetResourceRequest::doSend() {
|
||||
// We'll either have a hash or an ATP path to a file (that maps to a hash)
|
||||
if (urlIsAssetHash()) {
|
||||
|
@ -81,8 +69,6 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) {
|
|||
Q_ASSERT(_state == InProgress);
|
||||
Q_ASSERT(request == _assetMappingRequest);
|
||||
|
||||
cleanupTimer();
|
||||
|
||||
switch (request->getError()) {
|
||||
case MappingRequest::NoError:
|
||||
// we have no error, we should have a resulting hash - use that to send of a request for that asset
|
||||
|
@ -118,7 +104,6 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) {
|
|||
_assetMappingRequest = nullptr;
|
||||
});
|
||||
|
||||
setupTimer();
|
||||
_assetMappingRequest->start();
|
||||
}
|
||||
|
||||
|
@ -133,8 +118,6 @@ void AssetResourceRequest::requestHash(const AssetHash& hash) {
|
|||
Q_ASSERT(_state == InProgress);
|
||||
Q_ASSERT(req == _assetRequest);
|
||||
Q_ASSERT(req->getState() == AssetRequest::Finished);
|
||||
|
||||
cleanupTimer();
|
||||
|
||||
switch (req->getError()) {
|
||||
case AssetRequest::Error::NoError:
|
||||
|
@ -162,35 +145,29 @@ void AssetResourceRequest::requestHash(const AssetHash& hash) {
|
|||
_assetRequest = nullptr;
|
||||
});
|
||||
|
||||
setupTimer();
|
||||
_assetRequest->start();
|
||||
}
|
||||
|
||||
void AssetResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||
Q_ASSERT(_state == InProgress);
|
||||
|
||||
// We've received data, so reset the timer
|
||||
_sendTimer->start();
|
||||
|
||||
emit progress(bytesReceived, bytesTotal);
|
||||
|
||||
auto now = p_high_resolution_clock::now();
|
||||
|
||||
// if we haven't received the full asset check if it is time to output progress to log
|
||||
// we do so every X seconds to assist with ATP download tracking
|
||||
|
||||
if (bytesReceived != bytesTotal
|
||||
&& now - _lastProgressDebug > std::chrono::seconds(DOWNLOAD_PROGRESS_LOG_INTERVAL_SECONDS)) {
|
||||
|
||||
int percentage = roundf((float) bytesReceived / (float) bytesTotal * 100.0f);
|
||||
|
||||
qCDebug(networking).nospace() << "Progress for " << _url.path() << " - "
|
||||
<< bytesReceived << " of " << bytesTotal << " bytes - " << percentage << "%";
|
||||
|
||||
_lastProgressDebug = now;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AssetResourceRequest::onTimeout() {
|
||||
if (_state == InProgress) {
|
||||
qWarning() << "Asset request timed out: " << _url;
|
||||
if (_assetRequest) {
|
||||
disconnect(_assetRequest, 0, this, 0);
|
||||
_assetRequest->deleteLater();
|
||||
_assetRequest = nullptr;
|
||||
}
|
||||
if (_assetMappingRequest) {
|
||||
disconnect(_assetMappingRequest, 0, this, 0);
|
||||
_assetMappingRequest->deleteLater();
|
||||
_assetMappingRequest = nullptr;
|
||||
}
|
||||
_result = Timeout;
|
||||
_state = Finished;
|
||||
emit finished();
|
||||
}
|
||||
cleanupTimer();
|
||||
}
|
||||
|
|
|
@ -14,13 +14,15 @@
|
|||
|
||||
#include <QUrl>
|
||||
|
||||
#include <PortableHighResolutionClock.h>
|
||||
|
||||
#include "AssetRequest.h"
|
||||
#include "ResourceRequest.h"
|
||||
|
||||
class AssetResourceRequest : public ResourceRequest {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AssetResourceRequest(const QUrl& url) : ResourceRequest(url) { }
|
||||
AssetResourceRequest(const QUrl& url);
|
||||
virtual ~AssetResourceRequest() override;
|
||||
|
||||
protected:
|
||||
|
@ -28,21 +30,17 @@ protected:
|
|||
|
||||
private slots:
|
||||
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
void onTimeout();
|
||||
|
||||
private:
|
||||
void setupTimer();
|
||||
void cleanupTimer();
|
||||
|
||||
bool urlIsAssetHash() const;
|
||||
|
||||
void requestMappingForPath(const AssetPath& path);
|
||||
void requestHash(const AssetHash& hash);
|
||||
|
||||
QTimer* _sendTimer { nullptr };
|
||||
|
||||
GetMappingRequest* _assetMappingRequest { nullptr };
|
||||
AssetRequest* _assetRequest { nullptr };
|
||||
|
||||
p_high_resolution_clock::time_point _lastProgressDebug;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -54,14 +54,14 @@ void ReceivedMessage::appendPacket(NLPacket& packet) {
|
|||
"We should not be appending to a complete message");
|
||||
|
||||
// Limit progress signal to every X packets
|
||||
const int EMIT_PROGRESS_EVERY_X_PACKETS = 100;
|
||||
const int EMIT_PROGRESS_EVERY_X_PACKETS = 50;
|
||||
|
||||
++_numPackets;
|
||||
|
||||
_data.append(packet.getPayload(), packet.getPayloadSize());
|
||||
|
||||
if (_numPackets % EMIT_PROGRESS_EVERY_X_PACKETS == 0) {
|
||||
emit progress();
|
||||
emit progress(getSize());
|
||||
}
|
||||
|
||||
if (packet.getPacketPosition() == NLPacket::PacketPosition::LAST) {
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
template<typename T> qint64 readHeadPrimitive(T* data);
|
||||
|
||||
signals:
|
||||
void progress();
|
||||
void progress(qint64 size);
|
||||
void completed();
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -24,6 +24,16 @@ void UserActivityLoggerScriptingInterface::toggledAway(bool isAway) {
|
|||
logAction("toggled_away", { { "is_away", isAway } });
|
||||
}
|
||||
|
||||
void UserActivityLoggerScriptingInterface::tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, float tutorialElapsedTime) {
|
||||
logAction("tutorial_progress", {
|
||||
{ "step", stepName },
|
||||
{ "step_number", stepNumber },
|
||||
{ "seconds_to_complete", secondsToComplete },
|
||||
{ "tutorial_elapsed_seconds", tutorialElapsedTime }
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void UserActivityLoggerScriptingInterface::logAction(QString action, QJsonObject details) {
|
||||
QMetaObject::invokeMethod(&UserActivityLogger::getInstance(), "logAction",
|
||||
Q_ARG(QString, action),
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
Q_INVOKABLE void enabledEdit();
|
||||
Q_INVOKABLE void openedMarketplace();
|
||||
Q_INVOKABLE void toggledAway(bool isAway);
|
||||
Q_INVOKABLE void tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, float tutorialElapsedTime);
|
||||
|
||||
private:
|
||||
void logAction(QString action, QJsonObject details = {});
|
||||
|
|
|
@ -19,7 +19,8 @@ namespace controller {
|
|||
class InputPlugin : public Plugin {
|
||||
public:
|
||||
virtual void pluginFocusOutEvent() = 0;
|
||||
|
||||
virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) = 0;
|
||||
|
||||
virtual bool isHandController() const = 0;
|
||||
};
|
||||
|
||||
|
|
34
libraries/plugins/src/plugins/PluginUtils.cpp
Normal file
34
libraries/plugins/src/plugins/PluginUtils.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
// PluginUtils.cpp
|
||||
// input-plugins/src/input-plugins
|
||||
//
|
||||
// Created by Ryan Huffman on 9/22/16.
|
||||
// Copyright 2016 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 "PluginUtils.h"
|
||||
|
||||
#include "DisplayPlugin.h"
|
||||
#include "InputPlugin.h"
|
||||
#include "PluginManager.h"
|
||||
|
||||
bool PluginUtils::isHMDAvailable(const QString& pluginName) {
|
||||
for (auto& displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) {
|
||||
// Temporarily only enable this for Vive
|
||||
if (displayPlugin->isHmd() && (pluginName.isEmpty() || displayPlugin->getName() == pluginName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PluginUtils::isHandControllerAvailable() {
|
||||
for (auto& inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
|
||||
if (inputPlugin->isHandController()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
19
libraries/plugins/src/plugins/PluginUtils.h
Normal file
19
libraries/plugins/src/plugins/PluginUtils.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
// PluginUtils.h
|
||||
// input-plugins/src/input-plugins
|
||||
//
|
||||
// Created by Ryan Huffman on 9/22/16.
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
class PluginUtils {
|
||||
public:
|
||||
static bool isHMDAvailable(const QString& pluginName = "");
|
||||
static bool isHandControllerAvailable();
|
||||
};
|
|
@ -51,7 +51,7 @@ DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) {
|
|||
vec4 specularVal;
|
||||
|
||||
DeferredFragment frag;
|
||||
frag.depthVal = -1;
|
||||
frag.depthVal = -1.0;
|
||||
normalVal = texture(normalMap, texcoord);
|
||||
diffuseVal = texture(albedoMap, texcoord);
|
||||
specularVal = texture(specularMap, texcoord);
|
||||
|
@ -138,8 +138,8 @@ void unpackMidLowNormalCurvature(vec2 texcoord, out vec4 midNormalCurvature, out
|
|||
lowNormalCurvature = fetchDiffusedCurvature(texcoord);
|
||||
midNormalCurvature.xyz = normalize((midNormalCurvature.xyz - 0.5f) * 2.0f);
|
||||
lowNormalCurvature.xyz = normalize((lowNormalCurvature.xyz - 0.5f) * 2.0f);
|
||||
midNormalCurvature.w = (midNormalCurvature.w * 2 - 1);
|
||||
lowNormalCurvature.w = (lowNormalCurvature.w * 2 - 1);
|
||||
midNormalCurvature.w = (midNormalCurvature.w * 2.0 - 1.0);
|
||||
lowNormalCurvature.w = (lowNormalCurvature.w * 2.0 - 1.0);
|
||||
}
|
||||
<@endfunc@>
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ float evalOpaqueFinalAlpha(float alpha, float mapAlpha) {
|
|||
}
|
||||
|
||||
const float DEFAULT_ROUGHNESS = 0.9;
|
||||
const float DEFAULT_SHININESS = 10;
|
||||
const float DEFAULT_METALLIC = 0;
|
||||
const float DEFAULT_SHININESS = 10.0;
|
||||
const float DEFAULT_METALLIC = 0.0;
|
||||
const vec3 DEFAULT_SPECULAR = vec3(0.1);
|
||||
const vec3 DEFAULT_EMISSIVE = vec3(0.0);
|
||||
const float DEFAULT_OCCLUSION = 1.0;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
// prepareGlobalLight
|
||||
// Transform directions to worldspace
|
||||
vec3 fragNormal = vec3((normal));
|
||||
vec3 fragEyeVector = vec3(invViewMat * vec4(-position, 0.0));
|
||||
vec3 fragEyeVector = vec3(invViewMat * vec4(-1.0*position, 0.0));
|
||||
vec3 fragEyeDir = normalize(fragEyeVector);
|
||||
|
||||
// Get light
|
||||
|
@ -143,13 +143,13 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur
|
|||
float facingLight = step(PERPENDICULAR_THRESHOLD, diffuseDot);
|
||||
|
||||
// Reevaluate the shadow attenuation for light facing fragments
|
||||
float lightAttenuation = (1 - facingLight) + facingLight * shadowAttenuation;
|
||||
float lightAttenuation = (1.0 - facingLight) + facingLight * shadowAttenuation;
|
||||
|
||||
// Diffuse light is the lightmap dimmed by shadow
|
||||
vec3 diffuseLight = lightAttenuation * lightmap;
|
||||
|
||||
// Ambient light is the lightmap when in shadow
|
||||
vec3 ambientLight = (1 - lightAttenuation) * lightmap * getLightAmbientIntensity(light);
|
||||
vec3 ambientLight = (1.0 - lightAttenuation) * lightmap * getLightAmbientIntensity(light);
|
||||
|
||||
return isLightmapEnabled() * obscurance * albedo * (diffuseLight + ambientLight);
|
||||
}
|
||||
|
|
|
@ -478,6 +478,8 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
|
|||
// Setup the global directional pass pipeline
|
||||
{
|
||||
if (deferredLightingEffect->_shadowMapEnabled) {
|
||||
// If the keylight has an ambient Map then use the Skybox version of the pass
|
||||
// otherwise use the ambient sphere version
|
||||
if (keyLight->getAmbientMap()) {
|
||||
program = deferredLightingEffect->_directionalSkyboxLightShadow;
|
||||
locations = deferredLightingEffect->_directionalSkyboxLightShadowLocations;
|
||||
|
@ -486,11 +488,11 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
|
|||
locations = deferredLightingEffect->_directionalAmbientSphereLightShadowLocations;
|
||||
}
|
||||
} else {
|
||||
// If the keylight has an ambient Map then use the Skybox version of the pass
|
||||
// otherwise use the ambient sphere version
|
||||
if (keyLight->getAmbientMap()) {
|
||||
program = deferredLightingEffect->_directionalAmbientSphereLight;
|
||||
locations = deferredLightingEffect->_directionalAmbientSphereLightLocations;
|
||||
//program = deferredLightingEffect->_directionalSkyboxLight;
|
||||
//locations = deferredLightingEffect->_directionalSkyboxLightLocations;
|
||||
program = deferredLightingEffect->_directionalSkyboxLight;
|
||||
locations = deferredLightingEffect->_directionalSkyboxLightLocations;
|
||||
} else {
|
||||
program = deferredLightingEffect->_directionalAmbientSphereLight;
|
||||
locations = deferredLightingEffect->_directionalAmbientSphereLightLocations;
|
||||
|
|
|
@ -23,7 +23,7 @@ vec4 evalSkyboxLight(vec3 direction, float lod) {
|
|||
<@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@>
|
||||
|
||||
vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) {
|
||||
return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5);
|
||||
return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0);
|
||||
}
|
||||
|
||||
<@if supportAmbientMap@>
|
||||
|
@ -32,7 +32,7 @@ vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float
|
|||
|
||||
vec3 evalAmbientSpecularIrradiance(Light light, vec3 fragEyeDir, vec3 fragNormal, float roughness, vec3 fresnel) {
|
||||
vec3 direction = -reflect(fragEyeDir, fragNormal);
|
||||
vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1 - roughness);
|
||||
vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1.0 - roughness);
|
||||
vec3 specularLight;
|
||||
<@if supportIfAmbientMapElseAmbientSphere@>
|
||||
if (getLightHasAmbientMap(light))
|
||||
|
@ -76,7 +76,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, Light light, vec3
|
|||
|
||||
|
||||
// Diffuse from ambient
|
||||
diffuse = (1 - metallic) * evalSphericalLight(getLightAmbientSphere(light), normal).xyz;
|
||||
diffuse = (1.0 - metallic) * evalSphericalLight(getLightAmbientSphere(light), normal).xyz;
|
||||
|
||||
// Specular highlight from ambient
|
||||
specular = evalAmbientSpecularIrradiance(light, eyeDir, normal, roughness, fresnel) * obscurance * getLightAmbientIntensity(light);
|
||||
|
|
|
@ -40,7 +40,7 @@ void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light,
|
|||
if (isShowLightContour() > 0.0) {
|
||||
// Show edge
|
||||
float edge = abs(2.0 * ((getLightRadius(light) - fragLightDistance) / (0.1)) - 1.0);
|
||||
if (edge < 1) {
|
||||
if (edge < 1.0) {
|
||||
float edgeCoord = exp2(-8.0*edge*edge);
|
||||
diffuse = vec3(edgeCoord * edgeCoord * getLightShowContour(light) * getLightColor(light));
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light,
|
|||
float edgeDistS = dot(fragLightDistance * vec2(cosSpotAngle, sqrt(1.0 - cosSpotAngle * cosSpotAngle)), -getLightSpotOutsideNormal2(light));
|
||||
float edgeDist = min(edgeDistR, edgeDistS);
|
||||
float edge = abs(2.0 * (edgeDist / (0.1)) - 1.0);
|
||||
if (edge < 1) {
|
||||
if (edge < 1.0) {
|
||||
float edgeCoord = exp2(-8.0*edge*edge);
|
||||
diffuse = vec3(edgeCoord * edgeCoord * getLightColor(light));
|
||||
}
|
||||
|
|
|
@ -118,8 +118,8 @@ vec3 fresnelSchlickColor(vec3 fresnelColor, vec3 lightDir, vec3 halfDir) {
|
|||
|
||||
float specularDistribution(float roughness, vec3 normal, vec3 halfDir) {
|
||||
float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0);
|
||||
float gloss2 = pow(0.001 + roughness, 4);
|
||||
float denom = (ndoth * ndoth*(gloss2 - 1) + 1);
|
||||
float gloss2 = pow(0.001 + roughness, 4.0);
|
||||
float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0);
|
||||
float power = gloss2 / (3.14159 * denom * denom);
|
||||
return power;
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float m
|
|||
float power = specularDistribution(roughness, fragNormal, halfDir);
|
||||
vec3 specular = power * fresnelColor * diffuse;
|
||||
|
||||
return vec4(specular, (1.0 - metallic) * diffuse * (1 - fresnelColor.x));
|
||||
return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x));
|
||||
}
|
||||
<@endfunc@>
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ vec3 integrate(float cosTheta, float skinRadius) {
|
|||
uniform sampler2D scatteringLUT;
|
||||
|
||||
vec3 fetchBRDF(float LdotN, float curvature) {
|
||||
return texture(scatteringLUT, vec2( clamp(LdotN * 0.5 + 0.5, 0.0, 1.0), clamp(2 * curvature, 0.0, 1.0))).xyz;
|
||||
return texture(scatteringLUT, vec2( clamp(LdotN * 0.5 + 0.5, 0.0, 1.0), clamp(2.0 * curvature, 0.0, 1.0))).xyz;
|
||||
}
|
||||
|
||||
vec3 fetchBRDFSpectrum(vec3 LdotNSpectrum, float curvature) {
|
||||
|
@ -183,7 +183,7 @@ float tuneCurvatureUnsigned(float curvature) {
|
|||
}
|
||||
|
||||
float unpackCurvature(float packedCurvature) {
|
||||
return (packedCurvature * 2 - 1);
|
||||
return (packedCurvature * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
vec3 evalScatteringBentNdotL(vec3 normal, vec3 midNormal, vec3 lowNormal, vec3 lightDir) {
|
||||
|
@ -210,7 +210,7 @@ vec3 evalSkinBRDF(vec3 lightDir, vec3 normal, vec3 midNormal, vec3 lowNormal, fl
|
|||
return lowNormal * 0.5 + vec3(0.5);
|
||||
}
|
||||
if (showCurvature()) {
|
||||
return (curvature > 0 ? vec3(curvature, 0.0, 0.0) : vec3(0.0, 0.0, -curvature));
|
||||
return (curvature > 0.0 ? vec3(curvature, 0.0, 0.0) : vec3(0.0, 0.0, -curvature));
|
||||
}
|
||||
|
||||
vec3 bentNdotL = evalScatteringBentNdotL(normal, midNormal, lowNormal, lightDir);
|
||||
|
|
|
@ -26,7 +26,7 @@ void main(void) {
|
|||
);
|
||||
vec4 pos = UNIT_QUAD[gl_VertexID];
|
||||
|
||||
_texCoord0 = (pos.xy + 1) * 0.5;
|
||||
_texCoord0 = (pos.xy + 1.0) * 0.5;
|
||||
|
||||
_texCoord0 *= texcoordFrameTransform.zw;
|
||||
_texCoord0 += texcoordFrameTransform.xy;
|
||||
|
|
|
@ -47,7 +47,7 @@ void main(void) {
|
|||
);
|
||||
vec4 pos = UNIT_QUAD[gl_VertexID];
|
||||
|
||||
_texCoord0 = vec4((pos.xy + 1) * 0.5, 0.0, 1.0);
|
||||
_texCoord0 = vec4((pos.xy + 1.0) * 0.5, 0.0, 1.0);
|
||||
|
||||
if (cam_isStereo()) {
|
||||
_texCoord0.x = 0.5 * (_texCoord0.x + cam_getStereoSide());
|
||||
|
|
|
@ -60,7 +60,7 @@ void main(void) {
|
|||
);
|
||||
vec4 pos = UNIT_QUAD[gl_VertexID];
|
||||
|
||||
_texCoord0 = vec4((pos.xy + 1) * 0.5, 0.0, 1.0);
|
||||
_texCoord0 = vec4((pos.xy + 1.0) * 0.5, 0.0, 1.0);
|
||||
if (cam_isStereo()) {
|
||||
_texCoord0.x = 0.5 * (_texCoord0.x + cam_getStereoSide());
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// directional_light.frag
|
||||
// directional_ambient_light.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Andrzej Kapolka on 9/3/14.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// directional_light.frag
|
||||
// directional_skybox_light.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Sam Gateau on 5/8/2015.
|
||||
|
|
|
@ -1533,6 +1533,7 @@ void ScriptEngine::doWithEnvironment(const EntityItemID& entityID, const QUrl& s
|
|||
#else
|
||||
operation();
|
||||
#endif
|
||||
hadUncaughtExceptions(*this, _fileNameString);
|
||||
|
||||
currentEntityIdentifier = oldIdentifier;
|
||||
currentSandboxURL = oldSandboxURL;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QDir>
|
||||
#include <QUrl>
|
||||
#include "PathUtils.h"
|
||||
#include <QtCore/QStandardPaths>
|
||||
|
||||
|
||||
const QString& PathUtils::resourcesPath() {
|
||||
|
@ -29,6 +30,19 @@ const QString& PathUtils::resourcesPath() {
|
|||
return staticResourcePath;
|
||||
}
|
||||
|
||||
QString PathUtils::getRootDataDirectory() {
|
||||
auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
dataPath += "/AppData/Roaming/";
|
||||
#elif defined(Q_OS_OSX)
|
||||
dataPath += "/Library/Application Support/";
|
||||
#else
|
||||
dataPath += "/.local/share/";
|
||||
#endif
|
||||
|
||||
return dataPath;
|
||||
}
|
||||
|
||||
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions) {
|
||||
QString fileNameLowered = fileName.toLower();
|
||||
|
|
|
@ -22,6 +22,7 @@ class PathUtils : public QObject, public Dependency {
|
|||
Q_PROPERTY(QString resources READ resourcesPath)
|
||||
public:
|
||||
static const QString& resourcesPath();
|
||||
static QString getRootDataDirectory();
|
||||
};
|
||||
|
||||
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions);
|
||||
|
|
|
@ -15,16 +15,10 @@
|
|||
#include <QtWidgets/qapplication.h>
|
||||
#include <QDebug>
|
||||
|
||||
QString ServerPathUtils::getDataDirectory() {
|
||||
auto dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||
#include "PathUtils.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
dataPath += "/AppData/Roaming/";
|
||||
#elif defined(Q_OS_OSX)
|
||||
dataPath += "/Library/Application Support/";
|
||||
#else
|
||||
dataPath += "/.local/share/";
|
||||
#endif
|
||||
QString ServerPathUtils::getDataDirectory() {
|
||||
auto dataPath = PathUtils::getRootDataDirectory();
|
||||
|
||||
dataPath += qApp->organizationName() + "/" + qApp->applicationName();
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ class NeuronPlugin : public InputPlugin {
|
|||
public:
|
||||
friend void FrameDataReceivedCallback(void* context, void* sender, _BvhDataHeaderEx* header, float* data);
|
||||
|
||||
bool isHandController() const override { return false; }
|
||||
|
||||
// Plugin functions
|
||||
virtual bool isSupported() const override;
|
||||
virtual const QString& getName() const override { return NAME; }
|
||||
|
|
|
@ -26,6 +26,8 @@ public:
|
|||
bool isSupported() const override;
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
bool isHandController() const override { return false; }
|
||||
|
||||
void init() override;
|
||||
void deinit() override;
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@ public:
|
|||
virtual const QString& getName() const override { return NAME; }
|
||||
virtual const QString& getID() const override { return HYDRA_ID_STRING; }
|
||||
|
||||
// Sixense always seems to initialize even if the hydras are not present. Is there
|
||||
// a way we can properly detect whether the hydras are present?
|
||||
bool isHandController() const override { return false; }
|
||||
|
||||
virtual bool activate() override;
|
||||
virtual void deactivate() override;
|
||||
|
||||
|
|
|
@ -61,6 +61,10 @@ void OculusBaseDisplayPlugin::customizeContext() {
|
|||
Parent::customizeContext();
|
||||
}
|
||||
|
||||
void OculusBaseDisplayPlugin::uncustomizeContext() {
|
||||
Parent::uncustomizeContext();
|
||||
}
|
||||
|
||||
bool OculusBaseDisplayPlugin::internalActivate() {
|
||||
_session = acquireOculusSession();
|
||||
if (!_session) {
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
|
||||
protected:
|
||||
void customizeContext() override;
|
||||
void uncustomizeContext() override;
|
||||
bool internalActivate() override;
|
||||
void internalDeactivate() override;
|
||||
void updatePresentPose() override;
|
||||
|
|
|
@ -26,6 +26,8 @@ public:
|
|||
bool isSupported() const override;
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
bool isHandController() const override { return _touch != nullptr; }
|
||||
|
||||
bool activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
|
|
|
@ -39,6 +39,10 @@ const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; // this probab
|
|||
PoseData _nextRenderPoseData;
|
||||
PoseData _nextSimPoseData;
|
||||
|
||||
#define MIN_CORES_FOR_NORMAL_RENDER 5
|
||||
bool forceInterleavedReprojection = (QThread::idealThreadCount() < MIN_CORES_FOR_NORMAL_RENDER);
|
||||
|
||||
|
||||
static std::array<vr::Hmd_Eye, 2> VR_EYES { { vr::Eye_Left, vr::Eye_Right } };
|
||||
bool _openVrDisplayActive { false };
|
||||
// Flip y-axis since GL UV coords are backwards.
|
||||
|
@ -399,7 +403,10 @@ bool OpenVrDisplayPlugin::internalActivate() {
|
|||
});
|
||||
|
||||
// enable async time warp
|
||||
//vr::VRCompositor()->ForceInterleavedReprojectionOn(true);
|
||||
if (forceInterleavedReprojection) {
|
||||
vr::VRCompositor()->ForceInterleavedReprojectionOn(true);
|
||||
}
|
||||
|
||||
|
||||
// set up default sensor space such that the UI overlay will align with the front of the room.
|
||||
auto chaperone = vr::VRChaperone();
|
||||
|
@ -628,7 +635,11 @@ void OpenVrDisplayPlugin::postPreview() {
|
|||
_nextSimPoseData = nextSim;
|
||||
});
|
||||
_nextRenderPoseData = nextRender;
|
||||
|
||||
// FIXME - this looks wrong!
|
||||
_hmdActivityLevel = vr::k_EDeviceActivityLevel_UserInteraction; // _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd);
|
||||
#else
|
||||
_hmdActivityLevel = _system->GetTrackedDeviceActivityLevel(vr::k_unTrackedDeviceIndex_Hmd);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -103,6 +103,15 @@ void releaseOpenVrSystem() {
|
|||
#if DEV_BUILD
|
||||
qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system";
|
||||
#endif
|
||||
|
||||
// HACK: workaround openvr crash, call submit with an invalid texture, right before VR_Shutdown.
|
||||
const GLuint INVALID_GL_TEXTURE_HANDLE = -1;
|
||||
vr::Texture_t vrTexture{ (void*)INVALID_GL_TEXTURE_HANDLE, vr::API_OpenGL, vr::ColorSpace_Auto };
|
||||
static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_LEFT{ 0, 0, 0.5f, 1 };
|
||||
static vr::VRTextureBounds_t OPENVR_TEXTURE_BOUNDS_RIGHT{ 0.5f, 0, 1, 1 };
|
||||
vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT);
|
||||
vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT);
|
||||
|
||||
vr::VR_Shutdown();
|
||||
_openVrQuitRequested = false;
|
||||
activeHmd = nullptr;
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
bool isSupported() const override;
|
||||
const QString& getName() const override { return NAME; }
|
||||
|
||||
bool isHandController() const override { return true; }
|
||||
|
||||
bool activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
var MESSAGE_CHANNEL = "io.highfidelity.summon-crowd";
|
||||
|
||||
print('crowd-agent version 1');
|
||||
print('crowd-agent version 2');
|
||||
|
||||
/* Observations:
|
||||
- File urls for AC scripts silently fail. Use a local server (e.g., python SimpleHTTPServer) for development.
|
||||
|
@ -24,9 +24,26 @@ print('crowd-agent version 1');
|
|||
- JSON.stringify(Avatar) silently fails (even when Agent.isAvatar)
|
||||
*/
|
||||
|
||||
function messageSend(message) {
|
||||
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
||||
}
|
||||
|
||||
function getSound(data, callback) { // callback(sound) when downloaded (which may be immediate).
|
||||
var sound = SoundCache.getSound(data.url);
|
||||
if (sound.downloaded) {
|
||||
return callback(sound);
|
||||
}
|
||||
sound.ready.connect(function () { callback(sound); });
|
||||
}
|
||||
function onFinishedPlaying() {
|
||||
messageSend({key: 'finishedSound'});
|
||||
}
|
||||
|
||||
var MILLISECONDS_IN_SECOND = 1000;
|
||||
function startAgent(parameters) { // Can also be used to update.
|
||||
print('crowd-agent starting params', JSON.stringify(parameters), JSON.stringify(Agent));
|
||||
Agent.isAvatar = true;
|
||||
Agent.isListeningToAudioStream = true; // Send silence when not chattering.
|
||||
if (parameters.position) {
|
||||
Avatar.position = parameters.position;
|
||||
}
|
||||
|
@ -36,6 +53,12 @@ function startAgent(parameters) { // Can also be used to update.
|
|||
if (parameters.skeletonModelURL) {
|
||||
Avatar.skeletonModelURL = parameters.skeletonModelURL;
|
||||
}
|
||||
if (parameters.soundData) {
|
||||
getSound(parameters.soundData, function (sound) {
|
||||
Script.setTimeout(onFinishedPlaying, sound.duration * MILLISECONDS_IN_SECOND);
|
||||
Agent.playAvatarSound(sound);
|
||||
});
|
||||
}
|
||||
if (parameters.animationData) {
|
||||
data = parameters.animationData;
|
||||
Avatar.startAnimation(data.url, data.fps || 30, 1.0, (data.loopFlag === undefined) ? true : data.loopFlag, false, data.startFrame || 0, data.endFrame);
|
||||
|
@ -47,9 +70,6 @@ function stopAgent(parameters) {
|
|||
print('crowd-agent stopped', JSON.stringify(parameters), JSON.stringify(Agent));
|
||||
}
|
||||
|
||||
function messageSend(message) {
|
||||
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
||||
}
|
||||
function messageHandler(channel, messageString, senderID) {
|
||||
if (channel !== MESSAGE_CHANNEL) {
|
||||
return;
|
||||
|
|
|
@ -18,15 +18,25 @@ var label = "summon";
|
|||
function debug() {
|
||||
print.apply(null, [].concat.apply([label, version], [].map.call(arguments, JSON.stringify)));
|
||||
}
|
||||
var MINIMUM_AVATARS = 25;
|
||||
var MINIMUM_AVATARS = 25; // We will summon agents to produce this many total. (Of course, there might not be enough agents.)
|
||||
var DENSITY = 0.3; // square meters per person. Some say 10 sq ft is arm's length (0.9m^2), 4.5 is crowd (0.4m^2), 2.5 is mosh pit (0.2m^2).
|
||||
var SOUND_DATA = {url: "http://howard-stearns.github.io/models/sounds/piano1.wav"};
|
||||
var AVATARS_CHATTERING_AT_ONCE = 4; // How many of the agents should we request to play SOUND at once.
|
||||
var NEXT_SOUND_SPREAD = 500; // millisecond range of how long to wait after one sound finishes, before playing the next
|
||||
|
||||
var spread = Math.sqrt(MINIMUM_AVATARS * DENSITY); // meters
|
||||
var turnSpread = 90; // How many degrees should turn from front range over.
|
||||
|
||||
function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2.
|
||||
|
||||
function contains(array, item) { return array.indexOf(item) >= 0; }
|
||||
function without(array, itemsToRemove) { return array.filter(function (item) { return !contains(itemsToRemove, item); }); }
|
||||
function nextAfter(array, id) { // Wrapping next element in array after id.
|
||||
var index = array.indexOf(id) + 1;
|
||||
return array[(index >= array.length) ? 0 : index];
|
||||
}
|
||||
|
||||
var summonedAgents = [];
|
||||
var chattering = [];
|
||||
var MESSAGE_CHANNEL = "io.highfidelity.summon-crowd";
|
||||
function messageSend(message) {
|
||||
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
||||
|
@ -48,15 +58,20 @@ function messageHandler(channel, messageString, senderID) {
|
|||
switch (message.key) {
|
||||
case "hello":
|
||||
// There can be avatars we've summoned that do not yet appear in the AvatarList.
|
||||
avatarIdentifiers = AvatarList.getAvatarIdentifiers().filter(function (id) { return summonedAgents.indexOf(id) === -1; });
|
||||
avatarIdentifiers = without(AvatarList.getAvatarIdentifiers(), summonedAgents);
|
||||
debug('present', avatarIdentifiers, summonedAgents);
|
||||
if ((summonedAgents.length + avatarIdentifiers.length) < MINIMUM_AVATARS ) {
|
||||
var chatter = chattering.length < AVATARS_CHATTERING_AT_ONCE;
|
||||
if (chatter) {
|
||||
chattering.push(senderID);
|
||||
}
|
||||
summonedAgents.push(senderID);
|
||||
messageSend({
|
||||
key: 'SUMMON',
|
||||
rcpt: senderID,
|
||||
position: Vec3.sum(MyAvatar.position, {x: coord(), y: 0, z: coord()}),
|
||||
orientation: Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(MyAvatar.orientation).y + (turnSpread * (Math.random() - 0.5)), 0)/*,
|
||||
orientation: Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(MyAvatar.orientation).y + (turnSpread * (Math.random() - 0.5)), 0),
|
||||
soundData: chatter && SOUND_DATA/*
|
||||
// No need to specify skeletonModelURL
|
||||
//skeletonModelURL: "file:///c:/Program Files/High Fidelity Release/resources/meshes/being_of_light/being_of_light.fbx",
|
||||
//skeletonModelURL: "file:///c:/Program Files/High Fidelity Release/resources/meshes/defaultAvatar_full.fst"/,
|
||||
|
@ -71,6 +86,16 @@ function messageHandler(channel, messageString, senderID) {
|
|||
});
|
||||
}
|
||||
break;
|
||||
case "finishedSound": // Give someone else a chance.
|
||||
chattering = without(chattering, [senderID]);
|
||||
Script.setTimeout(function () {
|
||||
messageSend({
|
||||
key: 'SUMMON',
|
||||
rcpt: nextAfter(without(summonedAgents, chattering), senderID),
|
||||
soundData: SOUND_DATA
|
||||
});
|
||||
}, Math.random() * NEXT_SOUND_SPREAD);
|
||||
break;
|
||||
case "HELO":
|
||||
Window.alert("Someone else is summoning avatars.");
|
||||
break;
|
||||
|
@ -93,11 +118,12 @@ Script.scriptEnding.connect(function () {
|
|||
|
||||
messageSend({key: 'HELO'}); // Ask agents to report in now.
|
||||
Script.setTimeout(function () {
|
||||
var total = AvatarList.getAvatarIdentifiers().length;
|
||||
if (0 === summonedAgents.length) {
|
||||
Window.alert("No agents reported.\n\Please run " + MINIMUM_AVATARS + " instances of\n\
|
||||
http://cdn.highfidelity.com/davidkelly/production/scripts/tests/performance/crowd-agent.js\n\
|
||||
on your domain server.");
|
||||
} else if (summonedAgents.length < MINIMUM_AVATARS) {
|
||||
Window.alert("Only " + summonedAgents.length + " of the expected " + MINIMUM_AVATARS + " agents reported in.");
|
||||
} else if (total < MINIMUM_AVATARS) {
|
||||
Window.alert("Only " + summonedAgents.length + " of the expected " + (MINIMUM_AVATARS - total) + " agents reported in.");
|
||||
}
|
||||
}, 5000);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var BASIC_TIMER_INTERVAL = 50; // 50ms = 20hz
|
||||
var OVERLAY_WIDTH = 1920;
|
||||
var OVERLAY_HEIGHT = 1080;
|
||||
var OVERLAY_DATA = {
|
||||
|
@ -49,6 +50,21 @@ var AWAY_INTRO = {
|
|||
endFrame: 83.0
|
||||
};
|
||||
|
||||
// MAIN CONTROL
|
||||
var isEnabled = true;
|
||||
var wasMuted; // unknonwn?
|
||||
var isAway = false; // we start in the un-away state
|
||||
var wasOverlaysVisible = Menu.isOptionChecked("Overlays");
|
||||
var eventMappingName = "io.highfidelity.away"; // goActive on hand controller button events, too.
|
||||
var eventMapping = Controller.newMapping(eventMappingName);
|
||||
var avatarPosition = MyAvatar.position;
|
||||
var wasHmdMounted = HMD.mounted;
|
||||
|
||||
|
||||
// some intervals we may create/delete
|
||||
var avatarMovedInterval;
|
||||
|
||||
|
||||
// prefetch the kneel animation and hold a ref so it's always resident in memory when we need it.
|
||||
var _animation = AnimationCache.prefetch(AWAY_INTRO.url);
|
||||
|
||||
|
@ -125,41 +141,28 @@ function maybeMoveOverlay() {
|
|||
var halfWayBetweenOldAndLookAt = Vec3.multiply(lookAtChange, EASE_BY_RATIO);
|
||||
var newOverlayPosition = Vec3.sum(lastOverlayPosition, halfWayBetweenOldAndLookAt);
|
||||
lastOverlayPosition = newOverlayPosition;
|
||||
|
||||
var actualOverlayPositon = moveCloserToCamera(lastOverlayPosition);
|
||||
Overlays.editOverlay(overlayHMD, { visible: true, position: actualOverlayPositon });
|
||||
|
||||
// make sure desktop version is hidden
|
||||
Overlays.editOverlay(overlay, { visible: false });
|
||||
|
||||
// also remember avatar position
|
||||
avatarPosition = MyAvatar.position;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ifAvatarMovedGoActive() {
|
||||
if (Vec3.distance(MyAvatar.position, avatarPosition) > AVATAR_MOVE_FOR_ACTIVE_DISTANCE) {
|
||||
var newAvatarPosition = MyAvatar.position;
|
||||
if (Vec3.distance(newAvatarPosition, avatarPosition) > AVATAR_MOVE_FOR_ACTIVE_DISTANCE) {
|
||||
goActive();
|
||||
}
|
||||
avatarPosition = newAvatarPosition;
|
||||
}
|
||||
|
||||
// MAIN CONTROL
|
||||
var isEnabled = true;
|
||||
var wasMuted, isAway;
|
||||
var wasOverlaysVisible = Menu.isOptionChecked("Overlays");
|
||||
var eventMappingName = "io.highfidelity.away"; // goActive on hand controller button events, too.
|
||||
var eventMapping = Controller.newMapping(eventMappingName);
|
||||
var avatarPosition = MyAvatar.position;
|
||||
|
||||
// backward compatible version of getting HMD.mounted, so it works in old clients
|
||||
function safeGetHMDMounted() {
|
||||
if (HMD.mounted === undefined) {
|
||||
return true;
|
||||
}
|
||||
return HMD.mounted;
|
||||
}
|
||||
|
||||
var wasHmdMounted = safeGetHMDMounted();
|
||||
|
||||
function goAway() {
|
||||
function goAway(fromStartup) {
|
||||
if (!isEnabled || isAway) {
|
||||
return;
|
||||
}
|
||||
|
@ -167,7 +170,6 @@ function goAway() {
|
|||
UserActivityLogger.toggledAway(true);
|
||||
|
||||
isAway = true;
|
||||
print('going "away"');
|
||||
wasMuted = AudioDevice.getMuted();
|
||||
if (!wasMuted) {
|
||||
AudioDevice.toggleMute();
|
||||
|
@ -189,10 +191,21 @@ function goAway() {
|
|||
// For HMD, the hmd preview will show the system mouse because of allowMouseCapture,
|
||||
// but we want to turn off our Reticle so that we don't get two in preview and a stuck one in headset.
|
||||
Reticle.visible = !HMD.active;
|
||||
wasHmdMounted = safeGetHMDMounted(); // always remember the correct state
|
||||
wasHmdMounted = HMD.mounted; // always remember the correct state
|
||||
|
||||
avatarPosition = MyAvatar.position;
|
||||
Script.update.connect(ifAvatarMovedGoActive);
|
||||
|
||||
// If we're entering away mode from some other state than startup, then we create our move timer immediately.
|
||||
// However if we're just stating up, we need to delay this process so that we don't think the initial teleport
|
||||
// is actually a move.
|
||||
if (fromStartup === undefined || fromStartup === false) {
|
||||
avatarMovedInterval = Script.setInterval(ifAvatarMovedGoActive, BASIC_TIMER_INTERVAL);
|
||||
} else {
|
||||
var WAIT_FOR_MOVE_ON_STARTUP = 3000; // 3 seconds
|
||||
Script.setTimeout(function() {
|
||||
avatarMovedInterval = Script.setInterval(ifAvatarMovedGoActive, BASIC_TIMER_INTERVAL);
|
||||
}, WAIT_FOR_MOVE_ON_STARTUP);
|
||||
}
|
||||
}
|
||||
|
||||
function goActive() {
|
||||
|
@ -203,7 +216,6 @@ function goActive() {
|
|||
UserActivityLogger.toggledAway(false);
|
||||
|
||||
isAway = false;
|
||||
print('going "active"');
|
||||
if (!wasMuted) {
|
||||
AudioDevice.toggleMute();
|
||||
}
|
||||
|
@ -230,9 +242,9 @@ function goActive() {
|
|||
if (HMD.active) {
|
||||
Reticle.position = HMD.getHUDLookAtPosition2D();
|
||||
}
|
||||
wasHmdMounted = safeGetHMDMounted(); // always remember the correct state
|
||||
wasHmdMounted = HMD.mounted; // always remember the correct state
|
||||
|
||||
Script.update.disconnect(ifAvatarMovedGoActive);
|
||||
Script.clearInterval(avatarMovedInterval);
|
||||
}
|
||||
|
||||
function maybeGoActive(event) {
|
||||
|
@ -250,10 +262,12 @@ var wasHmdActive = HMD.active;
|
|||
var wasMouseCaptured = Reticle.mouseCaptured;
|
||||
|
||||
function maybeGoAway() {
|
||||
// If our active state change (went to or from HMD mode), and we are now in the HMD, go into away
|
||||
if (HMD.active !== wasHmdActive) {
|
||||
wasHmdActive = !wasHmdActive;
|
||||
if (wasHmdActive) {
|
||||
goAway();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,19 +278,30 @@ function maybeGoAway() {
|
|||
wasMouseCaptured = !wasMouseCaptured;
|
||||
if (!wasMouseCaptured) {
|
||||
goAway();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If you've removed your HMD from your head, and we can detect it, we will also go away...
|
||||
var hmdMounted = safeGetHMDMounted();
|
||||
if (HMD.active && !hmdMounted && wasHmdMounted) {
|
||||
wasHmdMounted = hmdMounted;
|
||||
goAway();
|
||||
if (HMD.mounted != wasHmdMounted) {
|
||||
wasHmdMounted = HMD.mounted;
|
||||
print("HMD mounted changed...");
|
||||
|
||||
// We're putting the HMD on... switch to those devices
|
||||
if (HMD.mounted) {
|
||||
print("NOW mounted...");
|
||||
} else {
|
||||
print("HMD NOW un-mounted...");
|
||||
|
||||
if (HMD.active) {
|
||||
goAway();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setEnabled(value) {
|
||||
print("setting away enabled: ", value);
|
||||
if (!value) {
|
||||
goActive();
|
||||
}
|
||||
|
@ -293,9 +318,12 @@ var handleMessage = function(channel, message, sender) {
|
|||
Messages.subscribe(CHANNEL_AWAY_ENABLE);
|
||||
Messages.messageReceived.connect(handleMessage);
|
||||
|
||||
Script.update.connect(maybeMoveOverlay);
|
||||
var maybeIntervalTimer = Script.setInterval(function(){
|
||||
maybeMoveOverlay();
|
||||
maybeGoAway();
|
||||
}, BASIC_TIMER_INTERVAL);
|
||||
|
||||
|
||||
Script.update.connect(maybeGoAway);
|
||||
Controller.mousePressEvent.connect(goActive);
|
||||
Controller.keyPressEvent.connect(maybeGoActive);
|
||||
// Note peek() so as to not interfere with other mappings.
|
||||
|
@ -316,11 +344,17 @@ eventMapping.from(Controller.Standard.Start).peek().to(goActive);
|
|||
Controller.enableMapping(eventMappingName);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
Script.update.disconnect(maybeGoAway);
|
||||
Script.clearInterval(maybeIntervalTimer);
|
||||
goActive();
|
||||
Controller.disableMapping(eventMappingName);
|
||||
Controller.mousePressEvent.disconnect(goActive);
|
||||
Controller.keyPressEvent.disconnect(maybeGoActive);
|
||||
});
|
||||
|
||||
if (HMD.active && !HMD.mounted) {
|
||||
print("Starting script, while HMD is active and not mounted...");
|
||||
goAway(true);
|
||||
}
|
||||
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
|
|
@ -26,7 +26,7 @@ var WANT_DEBUG = false;
|
|||
var WANT_DEBUG_STATE = false;
|
||||
var WANT_DEBUG_SEARCH_NAME = null;
|
||||
|
||||
var FORCE_IGNORE_IK = true;
|
||||
var FORCE_IGNORE_IK = false;
|
||||
var SHOW_GRAB_POINT_SPHERE = true;
|
||||
|
||||
//
|
||||
|
@ -112,7 +112,7 @@ var CHECK_TOO_FAR_UNEQUIP_TIME = 0.3; // seconds, duration between checks
|
|||
|
||||
|
||||
var GRAB_POINT_SPHERE_RADIUS = NEAR_GRAB_RADIUS;
|
||||
var GRAB_POINT_SPHERE_COLOR = { red: 20, green: 90, blue: 238 };
|
||||
var GRAB_POINT_SPHERE_COLOR = { red: 240, green: 240, blue: 240 };
|
||||
var GRAB_POINT_SPHERE_ALPHA = 0.85;
|
||||
|
||||
|
||||
|
@ -184,6 +184,10 @@ var STATE_FAR_TRIGGER = 5;
|
|||
var STATE_HOLD = 6;
|
||||
var STATE_ENTITY_TOUCHING = 7;
|
||||
|
||||
var holdEnabled = true;
|
||||
var nearGrabEnabled = true;
|
||||
var farGrabEnabled = true;
|
||||
|
||||
// "collidesWith" is specified by comma-separated list of group names
|
||||
// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar
|
||||
var COLLIDES_WITH_WHILE_GRABBED = "dynamic,otherAvatar";
|
||||
|
@ -1071,12 +1075,6 @@ function MyController(hand) {
|
|||
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
var worldHandPosition = controllerLocation.position;
|
||||
|
||||
if (controllerLocation.valid) {
|
||||
this.grabPointSphereOn();
|
||||
} else {
|
||||
this.grabPointSphereOff();
|
||||
}
|
||||
|
||||
var candidateEntities = Entities.findEntities(worldHandPosition, MAX_EQUIP_HOTSPOT_RADIUS);
|
||||
entityPropertiesCache.addEntities(candidateEntities);
|
||||
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
|
||||
|
@ -1099,9 +1097,11 @@ function MyController(hand) {
|
|||
if (!this.grabPointIntersectsEntity) {
|
||||
Controller.triggerHapticPulse(1, 20, this.hand);
|
||||
this.grabPointIntersectsEntity = true;
|
||||
this.grabPointSphereOn();
|
||||
}
|
||||
} else {
|
||||
this.grabPointIntersectsEntity = false;
|
||||
this.grabPointSphereOff();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1382,10 +1382,11 @@ function MyController(hand) {
|
|||
this.chooseBestEquipHotspot = function(candidateEntities) {
|
||||
var DISTANCE = 0;
|
||||
var equippableHotspots = this.chooseNearEquipHotspots(candidateEntities, DISTANCE);
|
||||
var _this = this;
|
||||
if (equippableHotspots.length > 0) {
|
||||
// sort by distance
|
||||
equippableHotspots.sort(function(a, b) {
|
||||
var handControllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
var handControllerLocation = getControllerWorldLocation(_this.handToController(), true);
|
||||
var aDistance = Vec3.distance(a.worldPosition, handControllerLocation.position);
|
||||
var bDistance = Vec3.distance(b.worldPosition, handControllerLocation.position);
|
||||
return aDistance - bDistance;
|
||||
|
@ -1423,12 +1424,6 @@ function MyController(hand) {
|
|||
var controllerLocation = getControllerWorldLocation(this.handToController(), true);
|
||||
var handPosition = controllerLocation.position;
|
||||
|
||||
if (controllerLocation.valid) {
|
||||
this.grabPointSphereOn();
|
||||
} else {
|
||||
this.grabPointSphereOff();
|
||||
}
|
||||
|
||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||
|
||||
if (rayPickInfo.entityID) {
|
||||
|
@ -1440,7 +1435,7 @@ function MyController(hand) {
|
|||
|
||||
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateHotSpotEntities);
|
||||
if (potentialEquipHotspot) {
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
if (this.triggerSmoothedGrab() && holdEnabled) {
|
||||
this.grabbedHotspot = potentialEquipHotspot;
|
||||
this.grabbedEntity = potentialEquipHotspot.entityID;
|
||||
this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'");
|
||||
|
@ -1483,7 +1478,7 @@ function MyController(hand) {
|
|||
// potentialNearTriggerEntity = entity;
|
||||
}
|
||||
} else {
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
if (this.triggerSmoothedGrab() && nearGrabEnabled) {
|
||||
var props = entityPropertiesCache.getProps(entity);
|
||||
var grabProps = entityPropertiesCache.getGrabProps(entity);
|
||||
var refCount = grabProps.refCount ? grabProps.refCount : 0;
|
||||
|
@ -1571,7 +1566,7 @@ function MyController(hand) {
|
|||
// potentialFarTriggerEntity = entity;
|
||||
}
|
||||
} else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) {
|
||||
if (this.triggerSmoothedGrab() && !isEditing()) {
|
||||
if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled) {
|
||||
this.grabbedEntity = entity;
|
||||
this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'");
|
||||
return;
|
||||
|
@ -1589,7 +1584,9 @@ function MyController(hand) {
|
|||
equipHotspotBuddy.highlightHotspot(potentialEquipHotspot);
|
||||
}
|
||||
|
||||
this.searchIndicatorOn(rayPickInfo.searchRay);
|
||||
if (farGrabEnabled) {
|
||||
this.searchIndicatorOn(rayPickInfo.searchRay);
|
||||
}
|
||||
Reticle.setVisible(false);
|
||||
};
|
||||
|
||||
|
@ -1900,7 +1897,7 @@ function MyController(hand) {
|
|||
if (FORCE_IGNORE_IK) {
|
||||
this.ignoreIK = true;
|
||||
} else {
|
||||
this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false;
|
||||
this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true;
|
||||
}
|
||||
|
||||
var handRotation;
|
||||
|
@ -2219,7 +2216,9 @@ function MyController(hand) {
|
|||
if (intersection.intersects) {
|
||||
this.intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
||||
}
|
||||
this.searchIndicatorOn(pickRay);
|
||||
if (farGrabEnabled) {
|
||||
this.searchIndicatorOn(pickRay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2327,7 +2326,9 @@ function MyController(hand) {
|
|||
}
|
||||
|
||||
this.intersectionDistance = intersectInfo.distance;
|
||||
this.searchIndicatorOn(intersectInfo.searchRay);
|
||||
if (farGrabEnabled) {
|
||||
this.searchIndicatorOn(intersectInfo.searchRay);
|
||||
}
|
||||
Reticle.setVisible(false);
|
||||
} else {
|
||||
this.setState(STATE_OFF, "grabbed entity was destroyed");
|
||||
|
@ -2681,6 +2682,7 @@ function update(deltaTime) {
|
|||
entityPropertiesCache.update();
|
||||
}
|
||||
|
||||
Messages.subscribe('Hifi-Grab-Disable');
|
||||
Messages.subscribe('Hifi-Hand-Disabler');
|
||||
Messages.subscribe('Hifi-Hand-Grab');
|
||||
Messages.subscribe('Hifi-Hand-RayPick-Blacklist');
|
||||
|
|
|
@ -308,6 +308,17 @@ function hudReticleDistance() { // 3d distance from camera to the reticle positi
|
|||
var reticlePositionOnHUD = HMD.worldPointFromOverlay(Reticle.position);
|
||||
return Vec3.distance(reticlePositionOnHUD, HMD.position);
|
||||
}
|
||||
|
||||
function maybeAdjustReticleDepth() {
|
||||
if (HMD.active) { // set depth
|
||||
if (isPointingAtOverlay()) {
|
||||
Reticle.depth = hudReticleDistance();
|
||||
}
|
||||
}
|
||||
}
|
||||
var ADJUST_RETICLE_DEPTH_INTERVAL = 50; // 20hz
|
||||
Script.setInterval(maybeAdjustReticleDepth,ADJUST_RETICLE_DEPTH_INTERVAL);
|
||||
|
||||
function onMouseMove() {
|
||||
// Display cursor at correct depth (as in depthReticle.js), and updateMouseActivity.
|
||||
if (ignoreMouseActivity()) {
|
||||
|
@ -363,7 +374,7 @@ function makeToggleAction(hand) { // return a function(0|1) that makes the speci
|
|||
};
|
||||
}
|
||||
|
||||
var clickMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
||||
var clickMapping = Controller.newMapping('handControllerPointer-click');
|
||||
Script.scriptEnding.connect(clickMapping.disable);
|
||||
|
||||
// Gather the trigger data for smoothing.
|
||||
|
|
|
@ -18,6 +18,7 @@ var mappingName, basicMapping, isChecked;
|
|||
var TURN_RATE = 1000;
|
||||
var MENU_ITEM_NAME = "Advanced Movement For Hand Controllers";
|
||||
var SETTINGS_KEY = 'advancedMovementForHandControllersIsChecked';
|
||||
var isDisabled = false;
|
||||
var previousSetting = Settings.getValue(SETTINGS_KEY);
|
||||
if (previousSetting === '' || previousSetting === false || previousSetting === 'false') {
|
||||
previousSetting = false;
|
||||
|
@ -54,6 +55,9 @@ function registerBasicMapping() {
|
|||
mappingName = 'Hifi-AdvancedMovement-Dev-' + Math.random();
|
||||
basicMapping = Controller.newMapping(mappingName);
|
||||
basicMapping.from(Controller.Standard.LY).to(function(value) {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
var stick = Controller.getValue(Controller.Standard.LS);
|
||||
if (value === 1 && Controller.Hardware.OculusTouch !== undefined) {
|
||||
rotate180();
|
||||
|
@ -70,6 +74,9 @@ function registerBasicMapping() {
|
|||
});
|
||||
basicMapping.from(Controller.Standard.LX).to(Controller.Standard.RX);
|
||||
basicMapping.from(Controller.Standard.RY).to(function(value) {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
var stick = Controller.getValue(Controller.Standard.RS);
|
||||
if (value === 1 && Controller.Hardware.OculusTouch !== undefined) {
|
||||
rotate180();
|
||||
|
@ -144,4 +151,19 @@ HMD.displayModeChanged.connect(function(isHMDMode) {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
var HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL = 'Hifi-Advanced-Movement-Disabler';
|
||||
function handleMessage(channel, message, sender) {
|
||||
if (channel == HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL) {
|
||||
if (message == 'disable') {
|
||||
isDisabled = true;
|
||||
} else if (message == 'enable') {
|
||||
isDisabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Messages.subscribe(HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL);
|
||||
Messages.messageReceived.connect(handleMessage);
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
|
|
@ -830,7 +830,7 @@ function loaded() {
|
|||
|
||||
elGrabbable.checked = properties.dynamic;
|
||||
elWantsTrigger.checked = false;
|
||||
elIgnoreIK.checked = false;
|
||||
elIgnoreIK.checked = true;
|
||||
var parsedUserData = {}
|
||||
try {
|
||||
parsedUserData = JSON.parse(properties.userData);
|
||||
|
@ -1143,7 +1143,7 @@ function loaded() {
|
|||
userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false);
|
||||
});
|
||||
elIgnoreIK.addEventListener('change', function() {
|
||||
userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, false);
|
||||
userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, true);
|
||||
});
|
||||
|
||||
elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL'));
|
||||
|
@ -1596,4 +1596,4 @@ function loaded() {
|
|||
document.addEventListener("contextmenu", function(event) {
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
|
||||
// var GRAB_POINT_SPHERE_OFFSET = { x: 0, y: 0.2, z: 0 };
|
||||
// var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.175, z: 0.04 };
|
||||
// var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.32, z: 0.04 };
|
||||
|
||||
// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp
|
||||
var GRAB_POINT_SPHERE_OFFSET = { x: 0.1, y: 0.32, z: 0.04 };
|
||||
var GRAB_POINT_SPHERE_OFFSET = { x: 0.0, y: 0.175, z: 0.0 };
|
||||
|
||||
getGrabPointSphereOffset = function(handController) {
|
||||
if (handController === Controller.Standard.RightHand) {
|
||||
|
|
|
@ -39,6 +39,7 @@ function shouldShowWebTablet() {
|
|||
|
||||
function showMarketplace(marketplaceID) {
|
||||
if (shouldShowWebTablet()) {
|
||||
updateButtonState(true);
|
||||
marketplaceWebTablet = new WebTablet("https://metaverse.highfidelity.com/marketplace");
|
||||
} else {
|
||||
var url = MARKETPLACE_URL;
|
||||
|
@ -58,6 +59,7 @@ function hideMarketplace() {
|
|||
marketplaceWindow.setVisible(false);
|
||||
marketplaceWindow.setURL("about:blank");
|
||||
} else if (marketplaceWebTablet) {
|
||||
updateButtonState(false);
|
||||
marketplaceWebTablet.destroy();
|
||||
marketplaceWebTablet = null;
|
||||
}
|
||||
|
@ -83,10 +85,13 @@ var browseExamplesButton = toolBar.addButton({
|
|||
alpha: 0.9
|
||||
});
|
||||
|
||||
function updateButtonState(visible) {
|
||||
browseExamplesButton.writeProperty('buttonState', visible ? 0 : 1);
|
||||
browseExamplesButton.writeProperty('defaultState', visible ? 0 : 1);
|
||||
browseExamplesButton.writeProperty('hoverState', visible ? 2 : 3);
|
||||
}
|
||||
function onMarketplaceWindowVisibilityChanged() {
|
||||
browseExamplesButton.writeProperty('buttonState', marketplaceWindow.visible ? 0 : 1);
|
||||
browseExamplesButton.writeProperty('defaultState', marketplaceWindow.visible ? 0 : 1);
|
||||
browseExamplesButton.writeProperty('hoverState', marketplaceWindow.visible ? 2 : 3);
|
||||
updateButtonState(marketplaceWindow.visible);
|
||||
marketplaceVisible = marketplaceWindow.visible;
|
||||
}
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ var triggerMapping = Controller.newMapping(Script.resolvePath('') + '-click');
|
|||
function controllerComputePickRay(hand) {
|
||||
var controllerPose = getControllerWorldLocation(hand, true);
|
||||
if (controllerPose.valid) {
|
||||
return { origin: controllerPose.position, direction: controllerPose.orientation };
|
||||
return { origin: controllerPose.position, direction: Quat.getUp(controllerPose.orientation) };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -469,7 +469,7 @@ var usersWindow = (function () {
|
|||
|
||||
Overlays.editOverlay(minimizeButton, {
|
||||
x: windowLeft + WINDOW_WIDTH - WINDOW_MARGIN / 2 - MIN_MAX_BUTTON_WIDTH,
|
||||
y: windowTop + WINDOW_MARGIN / 2
|
||||
y: windowTop + WINDOW_MARGIN
|
||||
});
|
||||
|
||||
scrollbarBackgroundPosition.x = windowLeft + WINDOW_WIDTH - 0.5 * WINDOW_MARGIN - SCROLLBAR_BACKGROUND_WIDTH;
|
||||
|
@ -559,34 +559,34 @@ var usersWindow = (function () {
|
|||
});
|
||||
|
||||
Overlays.editOverlay(windowHeading, {
|
||||
text: linesOfUsers.length > 0 ? "Users online" : "No users online"
|
||||
text: isLoggedIn ? (linesOfUsers.length > 0 ? "Users online" : "No users online") : "Users online - log in to view"
|
||||
});
|
||||
}
|
||||
|
||||
function updateOverlayVisibility() {
|
||||
Overlays.editOverlay(windowBorder, {
|
||||
visible: isLoggedIn && isVisible && isBorderVisible
|
||||
visible: isVisible && isBorderVisible
|
||||
});
|
||||
Overlays.editOverlay(windowPane, {
|
||||
visible: isLoggedIn && isVisible
|
||||
visible: isVisible
|
||||
});
|
||||
Overlays.editOverlay(windowHeading, {
|
||||
visible: isLoggedIn && isVisible
|
||||
visible: isVisible
|
||||
});
|
||||
Overlays.editOverlay(minimizeButton, {
|
||||
visible: isLoggedIn && isVisible
|
||||
});
|
||||
Overlays.editOverlay(scrollbarBackground, {
|
||||
visible: isLoggedIn && isVisible && isUsingScrollbars && !isMinimized
|
||||
visible: isVisible && isUsingScrollbars && !isMinimized
|
||||
});
|
||||
Overlays.editOverlay(scrollbarBar, {
|
||||
visible: isLoggedIn && isVisible && isUsingScrollbars && !isMinimized
|
||||
visible: isVisible && isUsingScrollbars && !isMinimized
|
||||
});
|
||||
Overlays.editOverlay(friendsButton, {
|
||||
visible: isLoggedIn && isVisible && !isMinimized
|
||||
visible: isVisible && !isMinimized
|
||||
});
|
||||
displayControl.setVisible(isLoggedIn && isVisible && !isMinimized);
|
||||
visibilityControl.setVisible(isLoggedIn && isVisible && !isMinimized);
|
||||
displayControl.setVisible(isVisible && !isMinimized);
|
||||
visibilityControl.setVisible(isVisible && !isMinimized);
|
||||
}
|
||||
|
||||
function checkLoggedIn() {
|
||||
|
@ -594,6 +594,13 @@ var usersWindow = (function () {
|
|||
|
||||
isLoggedIn = Account.isLoggedIn();
|
||||
if (isLoggedIn !== wasLoggedIn) {
|
||||
if (wasLoggedIn) {
|
||||
setMinimized(true);
|
||||
calculateWindowHeight();
|
||||
updateOverlayPositions();
|
||||
updateUsersDisplay();
|
||||
}
|
||||
|
||||
updateOverlayVisibility();
|
||||
}
|
||||
}
|
||||
|
@ -1021,7 +1028,7 @@ var usersWindow = (function () {
|
|||
color: WINDOW_HEADING_COLOR,
|
||||
alpha: WINDOW_HEADING_ALPHA,
|
||||
backgroundAlpha: 0.0,
|
||||
text: "No users online",
|
||||
text: "Users online",
|
||||
font: WINDOW_FONT,
|
||||
visible: false
|
||||
});
|
||||
|
@ -1126,6 +1133,7 @@ var usersWindow = (function () {
|
|||
if (VISIBILITY_VALUES.indexOf(myVisibility) === -1) {
|
||||
myVisibility = VISIBILITY_FRIENDS;
|
||||
}
|
||||
GlobalServices.findableBy = myVisibility;
|
||||
|
||||
visibilityControl = new PopUpMenu({
|
||||
prompt: VISIBILITY_PROMPT,
|
||||
|
|
|
@ -42,7 +42,7 @@ const appIcon = path.join(__dirname, '../resources/console.png');
|
|||
const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days
|
||||
const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/;
|
||||
|
||||
const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home.tgz";
|
||||
const HOME_CONTENT_URL = "http://cachefly.highfidelity.com/home-tutorial-9.tar.gz";
|
||||
|
||||
function getBuildInfo() {
|
||||
var buildInfoPath = null;
|
||||
|
|
195
tutorial/controllerDisplay.js
Normal file
195
tutorial/controllerDisplay.js
Normal file
|
@ -0,0 +1,195 @@
|
|||
var VISIBLE_BY_DEFAULT = false;
|
||||
var PARENT_ID = "{00000000-0000-0000-0000-000000000001}";
|
||||
|
||||
var DEBUG = false;
|
||||
function debug() {
|
||||
if (DEBUG) {
|
||||
print.apply(self, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
createControllerDisplay = function(config) {
|
||||
var controllerDisplay = {
|
||||
overlays: [],
|
||||
partOverlays: {
|
||||
},
|
||||
parts: {
|
||||
},
|
||||
mappingName: "mapping-display",
|
||||
|
||||
setVisible: function(visible) {
|
||||
print("CONTROLLER_DISPLAY::Setting visible", this.overlays.length);
|
||||
for (var i = 0; i < this.overlays.length; ++i) {
|
||||
print("i", i, this.overlays[i]);
|
||||
Overlays.editOverlay(this.overlays[i], {
|
||||
visible: visible
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setPartVisible: function(partName, visible) {
|
||||
return;
|
||||
if (partName in this.partOverlays) {
|
||||
debug("Setting part visible", partName, visible);
|
||||
for (var i = 0; i < this.partOverlays[partName].length; ++i) {
|
||||
Overlays.editOverlay(this.partOverlays[partName][i], {
|
||||
//visible: visible
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setLayerForPart: function(partName, layerName) {
|
||||
if (partName in this.parts) {
|
||||
debug("Setting layer...", partName, layerName);
|
||||
var part = this.parts[partName];
|
||||
if (part.textureLayers && layerName in part.textureLayers) {
|
||||
var layer = part.textureLayers[layerName];
|
||||
var textures = {};
|
||||
if (layer.defaultTextureURL) {
|
||||
textures[part.textureName] = layer.defaultTextureURL;
|
||||
}
|
||||
for (var i = 0; i < this.partOverlays[partName].length; ++i) {
|
||||
Overlays.editOverlay(this.partOverlays[partName][i], {
|
||||
textures: textures
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
var mapping = Controller.newMapping(controllerDisplay.mappingName);
|
||||
for (var i = 0; i < config.controllers.length; ++i) {
|
||||
var controller = config.controllers[i];
|
||||
var position = controller.position;
|
||||
|
||||
if (controller.naturalPosition) {
|
||||
position = Vec3.sum(Vec3.multiplyQbyV(
|
||||
controller.rotation, controller.naturalPosition), position);
|
||||
}
|
||||
|
||||
var overlayID = Overlays.addOverlay("model", {
|
||||
url: controller.modelURL,
|
||||
dimensions: controller.dimensions,
|
||||
localRotation: controller.rotation,
|
||||
localPosition: position,
|
||||
parentID: PARENT_ID,
|
||||
parentJointIndex: controller.jointIndex,
|
||||
ignoreRayIntersection: true,
|
||||
});
|
||||
|
||||
controllerDisplay.overlays.push(overlayID);
|
||||
overlayID = null;
|
||||
|
||||
function clamp(value, min, max) {
|
||||
if (value < min) {
|
||||
return min;
|
||||
} else if (value > max) {
|
||||
return max
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
if (controller.parts) {
|
||||
for (var partName in controller.parts) {
|
||||
var part = controller.parts[partName];
|
||||
var partPosition = Vec3.sum(controller.position, Vec3.multiplyQbyV(controller.rotation, part.naturalPosition));
|
||||
var innerRotation = controller.rotation
|
||||
|
||||
controllerDisplay.parts[partName] = controller.parts[partName];
|
||||
|
||||
var properties = {
|
||||
url: part.modelURL,
|
||||
localPosition: partPosition,
|
||||
localRotation: innerRotation,
|
||||
parentID: PARENT_ID,
|
||||
parentJointIndex: controller.jointIndex,
|
||||
ignoreRayIntersection: true,
|
||||
};
|
||||
|
||||
if (part.defaultTextureLayer) {
|
||||
var textures = {};
|
||||
textures[part.textureName] = part.textureLayers[part.defaultTextureLayer].defaultTextureURL;
|
||||
properties['textures'] = textures;
|
||||
}
|
||||
|
||||
var overlayID = Overlays.addOverlay("model", properties);
|
||||
|
||||
if (part.type == "rotational") {
|
||||
var range = part.maxValue - part.minValue;
|
||||
mapping.from([part.input]).peek().to(function(controller, overlayID, part) {
|
||||
return function(value) {
|
||||
value = clamp(value, part.minValue, part.maxValue);
|
||||
|
||||
var pct = (value - part.minValue) / part.maxValue;
|
||||
var angle = pct * part.maxAngle;
|
||||
var rotation = Quat.angleAxis(angle, part.axis);
|
||||
|
||||
var offset = { x: 0, y: 0, z: 0 };
|
||||
if (part.origin) {
|
||||
var offset = Vec3.multiplyQbyV(rotation, part.origin);
|
||||
offset = Vec3.subtract(offset, part.origin);
|
||||
}
|
||||
|
||||
var partPosition = Vec3.sum(controller.position,
|
||||
Vec3.multiplyQbyV(controller.rotation, Vec3.sum(offset, part.naturalPosition)));
|
||||
|
||||
Overlays.editOverlay(overlayID, {
|
||||
localPosition: partPosition,
|
||||
localRotation: Quat.multiply(controller.rotation, rotation)
|
||||
});
|
||||
}
|
||||
}(controller, overlayID, part));
|
||||
} else if (part.type == "touchpad") {
|
||||
function resolveHardware(path) {
|
||||
var parts = path.split(".");
|
||||
function resolveInner(base, path, i) {
|
||||
if (i >= path.length) {
|
||||
return base;
|
||||
}
|
||||
return resolveInner(base[path[i]], path, ++i);
|
||||
}
|
||||
return resolveInner(Controller.Hardware, parts, 0);
|
||||
}
|
||||
|
||||
var visibleInput = resolveHardware(part.visibleInput);
|
||||
var xinput = resolveHardware(part.xInput);
|
||||
var yinput = resolveHardware(part.yInput);
|
||||
|
||||
// TODO: Touchpad inputs are currently only working for half
|
||||
// of the touchpad. When that is fixed, it would be useful
|
||||
// to update these to display the current finger position.
|
||||
mapping.from([visibleInput]).peek().to(function(value) {
|
||||
});
|
||||
mapping.from([xinput]).peek().to(function(value) {
|
||||
});
|
||||
mapping.from([yinput]).peek().invert().to(function(value) {
|
||||
});
|
||||
} else if (part.type == "static") {
|
||||
} else {
|
||||
print("TYPE NOT SUPPORTED: ", part.type);
|
||||
}
|
||||
|
||||
controllerDisplay.overlays.push(overlayID);
|
||||
if (!(partName in controllerDisplay.partOverlays)) {
|
||||
controllerDisplay.partOverlays[partName] = [];
|
||||
}
|
||||
controllerDisplay.partOverlays[partName].push(overlayID);
|
||||
}
|
||||
}
|
||||
}
|
||||
Controller.enableMapping(controllerDisplay.mappingName);
|
||||
return controllerDisplay;
|
||||
}
|
||||
|
||||
ControllerDisplay = function() {
|
||||
};
|
||||
|
||||
deleteControllerDisplay = function(controllerDisplay) {
|
||||
print("Deleting controller display");
|
||||
for (var i = 0; i < controllerDisplay.overlays.length; ++i) {
|
||||
Overlays.deleteOverlay(controllerDisplay.overlays[i]);
|
||||
}
|
||||
Controller.disableMapping(controllerDisplay.mappingName);
|
||||
}
|
||||
|
137
tutorial/entityData.js
Normal file
137
tutorial/entityData.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
birdFirework1 = {
|
||||
"clientOnly": 0,
|
||||
"collisionsWillMove": 1,
|
||||
"created": "2016-09-13T23:05:08Z",
|
||||
"dimensions": {
|
||||
"x": 0.10120716691017151,
|
||||
"y": 0.12002291530370712,
|
||||
"z": 0.18833979964256287
|
||||
},
|
||||
"collisionsWillMove": 1,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.2,
|
||||
z: 0
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -10,
|
||||
"z": 0
|
||||
},
|
||||
"id": "{1c4061bc-b2e7-4435-bc47-3fcc39ae6624}",
|
||||
"modelURL": "atp:/tutorial_models/birdStatue15.fbx",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"position": {
|
||||
"x": 0.11612319946289062,
|
||||
"y": 0,
|
||||
"z": 0.21749019622802734
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.24519434571266174,
|
||||
"x": -0.0064739733934402466,
|
||||
"y": -0.12259717285633087,
|
||||
"z": 0.094893023371696472
|
||||
},
|
||||
"rotation": {
|
||||
"w": -0.083054840564727783,
|
||||
"x": 0.93615627288818359,
|
||||
"y": 0.34154272079467773,
|
||||
"z": -0.0073701143264770508
|
||||
},
|
||||
"shapeType": "simple-hull",
|
||||
"type": "Model",
|
||||
"userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}"
|
||||
} ;
|
||||
|
||||
birdFirework2 = {
|
||||
"clientOnly": 0,
|
||||
"collisionsWillMove": 1,
|
||||
"created": "2016-09-12T22:56:48Z",
|
||||
"dimensions": {
|
||||
"x": 0.098819166421890259,
|
||||
"y": 0.11143554747104645,
|
||||
"z": 0.18833979964256287
|
||||
},
|
||||
"collisionsWillMove": 1,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -10,
|
||||
"z": 0
|
||||
},
|
||||
"id": "{ba067084-8d0f-4eeb-a8a1-c6814527c1bb}",
|
||||
"modelURL": "atp:/tutorial_models/statuebird4.fbx",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"position": {
|
||||
"x": 0,
|
||||
"y": 0.014694660902023315,
|
||||
"z": 0
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.24011452496051788,
|
||||
"x": -0.12005726248025894,
|
||||
"y": -0.10536260157823563,
|
||||
"z": -0.12005726248025894
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.55410087108612061,
|
||||
"x": 0.36000609397888184,
|
||||
"y": -0.33641564846038818,
|
||||
"z": -0.67092394828796387
|
||||
},
|
||||
"shapeType": "simple-compound",
|
||||
"type": "Model",
|
||||
"userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}"
|
||||
};
|
||||
|
||||
|
||||
Step1BlockData = {
|
||||
"clientOnly": 0,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2016-08-22T22:54:07Z",
|
||||
"dimensions": {
|
||||
"x": 0.20000000298023224,
|
||||
"y": 0.20000000298023224,
|
||||
"z": 0.20000000298023224
|
||||
},
|
||||
name: "tutorial/block",
|
||||
"collisionsWillMove": 1,
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.2,
|
||||
z: 0
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -10,
|
||||
"z": 0
|
||||
},
|
||||
"id": "{5c7223f8-3bc5-4cb4-913c-0e93f5994ca2}",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"queryAACube": {
|
||||
"scale": 0.34641015529632568,
|
||||
"x": -0.17320507764816284,
|
||||
"y": -0.17320507764816284,
|
||||
"z": -0.17320507764816284
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -0.0001373291015625,
|
||||
"y": -7.62939453125e-05,
|
||||
"z": -0.0003204345703125
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"userData": JSON.stringify({ hifiHomeKey: { reset: true } }),
|
||||
};
|
160
tutorial/firePit/fire.js
Normal file
160
tutorial/firePit/fire.js
Normal file
|
@ -0,0 +1,160 @@
|
|||
// Originally written by James Pollack, modified by Ryan Huffman for the tutorial
|
||||
//
|
||||
// this script turns an entity into an exploder -- anything that collides with it will be vaporized!
|
||||
|
||||
(function() {
|
||||
|
||||
var _this = this;
|
||||
|
||||
function Fire() {
|
||||
_this = this;
|
||||
}
|
||||
|
||||
var RED = {
|
||||
red: 255,
|
||||
green: 0,
|
||||
blue: 0
|
||||
};
|
||||
|
||||
var ORANGE = {
|
||||
red: 255,
|
||||
green: 165,
|
||||
blue: 0
|
||||
};
|
||||
|
||||
var YELLOW = {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 0
|
||||
};
|
||||
|
||||
var GREEN = {
|
||||
red: 0,
|
||||
green: 255,
|
||||
blue: 0
|
||||
};
|
||||
|
||||
var BLUE = {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 255
|
||||
};
|
||||
|
||||
var INDIGO = {
|
||||
red: 128,
|
||||
green: 0,
|
||||
blue: 128
|
||||
};
|
||||
|
||||
var VIOLET = {
|
||||
red: 75,
|
||||
green: 0,
|
||||
blue: 130
|
||||
};
|
||||
|
||||
var colors = [RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET];
|
||||
|
||||
Fire.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.EXPLOSION_SOUND = SoundCache.getSound("atp:/firepit/fire_burst.wav");
|
||||
},
|
||||
collisionWithEntity: function(myID, otherID, collisionInfo) {
|
||||
var otherProps = Entities.getEntityProperties(otherID);
|
||||
var data = null;
|
||||
try {
|
||||
data = JSON.parse(otherProps.userData)
|
||||
} catch (err) {
|
||||
print('ERROR GETTING USERDATA!');
|
||||
}
|
||||
if (data === null || "") {
|
||||
return;
|
||||
} else {
|
||||
if (data.hasOwnProperty('hifiHomeKey')) {
|
||||
if (data.hifiHomeKey.reset === true) {
|
||||
_this.playSoundAtCurrentPosition();
|
||||
_this.explodeWithColor();
|
||||
Entities.deleteEntity(otherID)
|
||||
Messages.sendLocalMessage('Entity-Exploded', JSON.stringify({
|
||||
entityID: otherID,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
explodeWithColor: function() {
|
||||
var myProps = Entities.getEntityProperties(this.entityID);
|
||||
var color = colors[Math.floor(Math.random() * colors.length)];
|
||||
var explosionParticleProperties = {
|
||||
"color": color,
|
||||
"isEmitting": 1,
|
||||
"maxParticles": 1000,
|
||||
"lifespan": 0.25,
|
||||
"emitRate": 1,
|
||||
"emitSpeed": 0.1,
|
||||
"speedSpread": 1,
|
||||
"emitOrientation": Quat.getUp(myProps.rotation),
|
||||
"emitDimensions": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"polarStart": 0,
|
||||
"polarFinish": 0,
|
||||
"azimuthStart": 0,
|
||||
"azimuthFinish": 0,
|
||||
"emitAcceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"accelerationSpread": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"particleRadius": 0.829,
|
||||
"radiusSpread": 0,
|
||||
"radiusStart": 0.361,
|
||||
"radiusFinish": 0.294,
|
||||
"colorSpread": {
|
||||
"red": 0,
|
||||
"green": 0,
|
||||
"blue": 0
|
||||
},
|
||||
"colorStart": {
|
||||
"red": 255,
|
||||
"green": 255,
|
||||
"blue": 255
|
||||
},
|
||||
"colorFinish": {
|
||||
"red": 255,
|
||||
"green": 255,
|
||||
"blue": 255
|
||||
},
|
||||
"alpha": 1,
|
||||
"alphaSpread": 0,
|
||||
"alphaStart": -0.2,
|
||||
"alphaFinish": 0.5,
|
||||
"emitterShouldTrail": 0,
|
||||
"textures": "atp:/firepit/explode.png",
|
||||
"type": "ParticleEffect",
|
||||
lifetime: 1,
|
||||
position: myProps.position
|
||||
};
|
||||
|
||||
var explosion = Entities.addEntity(explosionParticleProperties);
|
||||
},
|
||||
playSoundAtCurrentPosition: function() {
|
||||
|
||||
var audioProperties = {
|
||||
volume: 0.5,
|
||||
position: Entities.getEntityProperties(this.entityID).position
|
||||
};
|
||||
|
||||
Audio.playSound(this.EXPLOSION_SOUND, audioProperties);
|
||||
},
|
||||
}
|
||||
|
||||
return new Fire();
|
||||
});
|
52
tutorial/firePit/flicker.js
Normal file
52
tutorial/firePit/flicker.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Originally written for the Home content set. Pulled into the tutorial by Ryan Huffman
|
||||
(function() {
|
||||
|
||||
var MINIMUM_LIGHT_INTENSITY = 50.0;
|
||||
var MAXIMUM_LIGHT_INTENSITY = 200.0;
|
||||
var LIGHT_FALLOFF_RADIUS = 0.1;
|
||||
var LIGHT_INTENSITY_RANDOMNESS = 0.1;
|
||||
|
||||
function randFloat(low, high) {
|
||||
return low + Math.random() * (high - low);
|
||||
}
|
||||
|
||||
var _this;
|
||||
|
||||
function FlickeringFlame() {
|
||||
_this = this;
|
||||
}
|
||||
|
||||
var totalTime = 0;
|
||||
var spacer = 2;
|
||||
FlickeringFlame.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
Script.update.connect(this.update);
|
||||
},
|
||||
update: function(deltaTime) {
|
||||
|
||||
totalTime += deltaTime;
|
||||
if (totalTime > spacer) {
|
||||
var howManyAvatars = AvatarList.getAvatarIdentifiers().length;
|
||||
var intensity = (MINIMUM_LIGHT_INTENSITY + (MAXIMUM_LIGHT_INTENSITY + (Math.sin(totalTime) * MAXIMUM_LIGHT_INTENSITY)));
|
||||
intensity += randFloat(-LIGHT_INTENSITY_RANDOMNESS, LIGHT_INTENSITY_RANDOMNESS);
|
||||
|
||||
Entities.editEntity(_this.entityID, {
|
||||
intensity: intensity
|
||||
});
|
||||
|
||||
spacer = Math.random(0, 100) * (2 / howManyAvatars);
|
||||
totalTime = 0;
|
||||
} else {
|
||||
//just keep counting
|
||||
}
|
||||
},
|
||||
unload: function() {
|
||||
Script.update.disconnect(this.update)
|
||||
}
|
||||
}
|
||||
|
||||
return new FlickeringFlame
|
||||
|
||||
|
||||
});
|
99
tutorial/fuse.js
Normal file
99
tutorial/fuse.js
Normal file
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// fuse.js
|
||||
//
|
||||
// Created by Ryan Huffman on 9/1/16.
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
(function() {
|
||||
Script.include('utils.js');
|
||||
|
||||
var DEBUG = false;
|
||||
function debug() {
|
||||
if (DEBUG) {
|
||||
print.apply(self, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
var fuseSound = SoundCache.getSound("atp:/tutorial_sounds/fuse.wav");
|
||||
function getChildProperties(entityID, propertyNames) {
|
||||
var childEntityIDs = Entities.getChildrenIDs(entityID);
|
||||
var results = {}
|
||||
for (var i = 0; i < childEntityIDs.length; ++i) {
|
||||
var childEntityID = childEntityIDs[i];
|
||||
var properties = Entities.getEntityProperties(childEntityID, propertyNames);
|
||||
results[childEntityID] = properties;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
var Fuse = function() {
|
||||
};
|
||||
Fuse.prototype = {
|
||||
light: function() {
|
||||
debug("LIT", this.entityID);
|
||||
var anim = Entities.getEntityProperties(this.entityID, ['animation']).animation;
|
||||
|
||||
if (anim.currentFrame < 140) {
|
||||
return;
|
||||
}
|
||||
Entities.editEntity(this.entityID, {
|
||||
animation: {
|
||||
currentFrame: 1,
|
||||
lastFrame: 150,
|
||||
running: 1,
|
||||
url: "atp:/tutorial_models/fuse.fbx",
|
||||
loop: 0
|
||||
},
|
||||
});
|
||||
var injector = Audio.playSound(fuseSound, {
|
||||
position: Entities.getEntityProperties(this.entityID, 'position').position,
|
||||
volume: 0.7,
|
||||
loop: true
|
||||
});
|
||||
|
||||
var childrenProps = getChildProperties(this.entityID, ['type']);
|
||||
for (var childEntityID in childrenProps) {
|
||||
var props = childrenProps[childEntityID];
|
||||
if (props.type == "ParticleEffect") {
|
||||
Entities.editEntity(childEntityID, {
|
||||
emitRate: 140,
|
||||
});
|
||||
} else if (props.type == "Light") {
|
||||
Entities.editEntity(childEntityID, {
|
||||
visible: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var self = this;
|
||||
Script.setTimeout(function() {
|
||||
debug("BLOW UP");
|
||||
var spinnerID = Utils.findEntity({ name: "tutorial/equip/spinner" }, 20);
|
||||
Entities.callEntityMethod(spinnerID, "onLit");
|
||||
injector.stop();
|
||||
|
||||
var childrenProps = getChildProperties(self.entityID, ['type']);
|
||||
for (var childEntityID in childrenProps) {
|
||||
var props = childrenProps[childEntityID];
|
||||
if (props.type == "ParticleEffect") {
|
||||
Entities.editEntity(childEntityID, {
|
||||
emitRate: 0,
|
||||
});
|
||||
} else if (props.type == "Light") {
|
||||
Entities.editEntity(childEntityID, {
|
||||
visible: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}, 4900);
|
||||
},
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
},
|
||||
};
|
||||
return new Fuse();
|
||||
});
|
17
tutorial/fuseCollider.js
Normal file
17
tutorial/fuseCollider.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
(function() {
|
||||
Script.include('utils.js');
|
||||
|
||||
var Fuse = function() {
|
||||
};
|
||||
Fuse.prototype = {
|
||||
onLit: function() {
|
||||
print("LIT", this.entityID);
|
||||
var fuseID = Utils.findEntity({ name: "tutorial/equip/fuse" }, 20);
|
||||
Entities.callEntityMethod(fuseID, "light");
|
||||
},
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
},
|
||||
};
|
||||
return new Fuse();
|
||||
});
|
189
tutorial/lighter/butaneLighter.js
Normal file
189
tutorial/lighter/butaneLighter.js
Normal file
|
@ -0,0 +1,189 @@
|
|||
//
|
||||
// Created by Thijs Wenker on September 14, 2016.
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
(function() {
|
||||
var _this;
|
||||
|
||||
function getResourceURL(file) {
|
||||
return 'atp:/' + file;
|
||||
};
|
||||
|
||||
const LIGHTER_ON_SOUND_URL = getResourceURL('tutorial_sounds/lighter_on.wav');
|
||||
const BUTANE_SOUND_URL = getResourceURL('tutorial_sounds/butane.wav');
|
||||
|
||||
// TODO: fix this in the client, changing the sound volume while a sound is playing doesn't seem to work right now
|
||||
const DYNAMIC_SOUND_VOLUME = false;
|
||||
const BUTANE_MIN_SOUND_VOLUME = 0.05;
|
||||
|
||||
const FLAME_LENGTH = 0.05;
|
||||
|
||||
const BUTANE_SOUND_SETTINGS = {
|
||||
volume: 0.4,
|
||||
loop: true,
|
||||
playbackGap: 1000,
|
||||
playbackGapRange: 1000
|
||||
};
|
||||
|
||||
const LIGHTER_ON_SOUND_SETTINGS = {
|
||||
volume: 0.5,
|
||||
loop: false
|
||||
};
|
||||
|
||||
function RemoteLogSender(channel, identifier) {
|
||||
this.channel = channel;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
RemoteLogSender.prototype = {
|
||||
channel: null,
|
||||
identifier: null,
|
||||
debug: function(message) {
|
||||
Messages.sendLocalMessage(this.channel, JSON.stringify({
|
||||
message: '[DEBUG ' + this.identifier + '] ' + message
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
var remoteLogSender = null;
|
||||
|
||||
function debugPrint(message) {
|
||||
if (remoteLogSender !== null) {
|
||||
remoteLogSender.debug(message);
|
||||
}
|
||||
}
|
||||
|
||||
ButaneLighter = function() {
|
||||
_this = this;
|
||||
_this.triggerValue = 0.0; // be sure to set this value in the constructor, otherwise it will be a shared value
|
||||
_this.isFiring = false;
|
||||
}
|
||||
|
||||
ButaneLighter.prototype = {
|
||||
entityID: null,
|
||||
lighterOnSound: null,
|
||||
butaneSound: null,
|
||||
lighterOnSoundInjector: null,
|
||||
butaneSoundInjector: null,
|
||||
butaneSoundInjectorOptions: null,
|
||||
lighterParticleEntity: null,
|
||||
buttonBeingPressed: null,
|
||||
triggerValue: null,
|
||||
isFiring: null,
|
||||
getLighterParticleEntity: function() {
|
||||
var result = null;
|
||||
Entities.getChildrenIDs(_this.entityID).forEach(function(entity) {
|
||||
var name = Entities.getEntityProperties(entity, ['name']).name;
|
||||
if (name === 'lighter_particle') {
|
||||
result = entity;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
preload: function(entityID) {
|
||||
_this.entityID = entityID;
|
||||
_this.lighterOnSound = SoundCache.getSound(LIGHTER_ON_SOUND_URL);
|
||||
_this.butaneSound = SoundCache.getSound(BUTANE_SOUND_URL);
|
||||
var properties = Entities.getEntityProperties(_this.entityID, ['userData']);
|
||||
try {
|
||||
var userData = JSON.parse(properties.userData);
|
||||
if (userData['debug'] !== undefined && userData['debug']['sessionUUID'] === MyAvatar.sessionUUID &&
|
||||
userData['debug']['channel'] !== undefined)
|
||||
{
|
||||
remoteLogSender = new RemoteLogSender(userData['debug']['channel'], _this.entityID);
|
||||
remoteLogSender.debug('Debugger initialized');
|
||||
}
|
||||
} catch (e) {
|
||||
//failed to detect if we have a debugger
|
||||
}
|
||||
debugPrint(_this.getLighterParticleEntity());
|
||||
},
|
||||
startEquip: function(entityID, args) {
|
||||
_this.lighterParticleEntity = _this.getLighterParticleEntity();
|
||||
},
|
||||
continueEquip: function(entityID, args) {
|
||||
_this.triggerValue = Controller.getValue(args[0] === 'left' ? Controller.Standard.LT : Controller.Standard.RT);
|
||||
if (_this.triggerValue > 0.2) {
|
||||
if (!_this.isFiring) {
|
||||
_this.startFiring();
|
||||
}
|
||||
_this.tryToIgnite();
|
||||
_this.updateButaneSound()
|
||||
return;
|
||||
}
|
||||
_this.stopFiring();
|
||||
},
|
||||
startFiring: function() {
|
||||
if (_this.isFiring) {
|
||||
return;
|
||||
}
|
||||
_this.isFiring = true;
|
||||
if (_this.lighterOnSound.downloaded) {
|
||||
// We don't want to override the default volume setting, so lets clone the default SETTINGS object
|
||||
var lighterOnOptions = JSON.parse(JSON.stringify(LIGHTER_ON_SOUND_SETTINGS));
|
||||
lighterOnOptions['position'] = Entities.getEntityProperties(_this.entityID, ['position']).position;
|
||||
_this.lighterOnSoundInjector = Audio.playSound(_this.lighterOnSound, lighterOnOptions);
|
||||
}
|
||||
if (_this.butaneSound.downloaded) {
|
||||
_this.butaneSoundInjectorOptions = JSON.parse(JSON.stringify(BUTANE_SOUND_SETTINGS));
|
||||
_this.butaneSoundInjectorOptions['position'] = Entities.getEntityProperties(_this.lighterParticleEntity, ['position']).position;
|
||||
if (DYNAMIC_SOUND_VOLUME) {
|
||||
_this.butaneSoundInjectorOptions['volume'] = BUTANE_MIN_SOUND_VOLUME;
|
||||
}
|
||||
_this.butaneSoundInjector = Audio.playSound(_this.butaneSound, _this.butaneSoundInjectorOptions);
|
||||
}
|
||||
Entities.editEntity(_this.lighterParticleEntity, {isEmitting: _this.isFiring});
|
||||
|
||||
},
|
||||
stopFiring: function() {
|
||||
if (!_this.isFiring) {
|
||||
return;
|
||||
}
|
||||
_this.isFiring = false;
|
||||
Entities.editEntity(_this.lighterParticleEntity, {isEmitting: _this.isFiring});
|
||||
_this.stopButaneSound();
|
||||
},
|
||||
tryToIgnite: function() {
|
||||
var flameProperties = Entities.getEntityProperties(_this.lighterParticleEntity, ['position', 'rotation']);
|
||||
var pickRay = {
|
||||
origin: flameProperties.position,
|
||||
direction: Quat.inverse(Quat.getFront(flameProperties.rotation))
|
||||
}
|
||||
var intersection = Entities.findRayIntersection(pickRay, true, [], [_this.entityID, _this.lighterParticleEntity]);
|
||||
if (intersection.intersects && intersection.distance <= FLAME_LENGTH && intersection.properties.script !== '') {
|
||||
Entities.callEntityMethod(intersection.properties.id, 'onLit', [_this.triggerValue]);
|
||||
debugPrint('Light it up! found: ' + intersection.properties.id);
|
||||
}
|
||||
},
|
||||
releaseEquip: function(entityID, args) {
|
||||
_this.stopFiring();
|
||||
// reset trigger value;
|
||||
_this.triggerValue = 0.0;
|
||||
},
|
||||
updateButaneSound: function() {
|
||||
if (_this.butaneSoundInjector !== null && _this.butaneSoundInjector.isPlaying()) {
|
||||
_this.butaneSoundInjectorOptions = _this.butaneSoundInjector.options;
|
||||
_this.butaneSoundInjectorOptions['position'] = Entities.getEntityProperties(_this.entityID, ['position']).position;
|
||||
if (DYNAMIC_SOUND_VOLUME) {
|
||||
_this.butaneSoundInjectorOptions['volume'] = ((BUTANE_SOUND_SETTINGS.volume - BUTANE_MIN_SOUND_VOLUME) *
|
||||
_this.triggerValue) + BUTANE_MIN_SOUND_VOLUME;
|
||||
}
|
||||
_this.butaneSoundInjector.options = _this.butaneSoundInjectorOptions;
|
||||
}
|
||||
},
|
||||
stopButaneSound: function() {
|
||||
if (_this.butaneSoundInjector !== null && _this.butaneSoundInjector.isPlaying()) {
|
||||
_this.butaneSoundInjector.stop();
|
||||
}
|
||||
_this.butaneSoundInjector = null;
|
||||
},
|
||||
unload: function() {
|
||||
_this.stopButaneSound();
|
||||
},
|
||||
};
|
||||
return new ButaneLighter();
|
||||
})
|
214
tutorial/lighter/createButaneLighter.js
Normal file
214
tutorial/lighter/createButaneLighter.js
Normal file
|
@ -0,0 +1,214 @@
|
|||
//
|
||||
// Created by Thijs Wenker on September 14, 2016.
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
const TEST_MODE = false;
|
||||
const SCRIPT_URL = 'atp:/tutorial/lighter/butaneLighter.js';
|
||||
|
||||
//Creates an entity and returns a mixed object of the creation properties and the assigned entityID
|
||||
var createEntity = function(entityProperties, parent) {
|
||||
if (parent.rotation !== undefined) {
|
||||
if (entityProperties.rotation !== undefined) {
|
||||
entityProperties.rotation = Quat.multiply(parent.rotation, entityProperties.rotation);
|
||||
} else {
|
||||
entityProperties.rotation = parent.rotation;
|
||||
}
|
||||
}
|
||||
if (parent.position !== undefined) {
|
||||
var localPosition = (parent.rotation !== undefined) ? Vec3.multiplyQbyV(parent.rotation, entityProperties.position) : entityProperties.position;
|
||||
entityProperties.position = Vec3.sum(localPosition, parent.position)
|
||||
}
|
||||
if (parent.id !== undefined) {
|
||||
entityProperties.parentID = parent.id;
|
||||
}
|
||||
entityProperties.id = Entities.addEntity(entityProperties);
|
||||
return entityProperties;
|
||||
};
|
||||
|
||||
createButaneLighter = function(transform) {
|
||||
var entityProperties = {
|
||||
collisionsWillMove: true,
|
||||
dimensions: {
|
||||
x: 0.025599999353289604,
|
||||
y: 0.057399999350309372,
|
||||
z: 0.37419998645782471
|
||||
},
|
||||
dynamic: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
},
|
||||
modelURL: 'atp:/tutorial_models/lighterIceCreamSandwich.fbx',
|
||||
name: 'BrutaneLighter',
|
||||
shapeType: 'simple-compound',
|
||||
type: 'Model',
|
||||
userData: JSON.stringify({
|
||||
tag: "equip-temporary",
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
},
|
||||
wearable: {
|
||||
joints: {
|
||||
RightHand: [{
|
||||
x: 0.029085848480463028,
|
||||
y: 0.09807153046131134,
|
||||
z: 0.03062543272972107
|
||||
}, {
|
||||
x: 0.5929139256477356,
|
||||
y: 0.3207578659057617,
|
||||
z: 0.7151655554771423,
|
||||
w: -0.18468326330184937
|
||||
}],
|
||||
LeftHand: [{
|
||||
x: -0.029085848480463028,
|
||||
y: 0.09807153046131134,
|
||||
z: 0.03062543272972107
|
||||
}, {
|
||||
x: -0.5929139256477356,
|
||||
y: 0.3207578659057617,
|
||||
z: 0.7151655554771423,
|
||||
w: -0.18468326330184937
|
||||
}]
|
||||
}
|
||||
}
|
||||
}),
|
||||
script: SCRIPT_URL
|
||||
};
|
||||
return createEntity(entityProperties, transform);
|
||||
}
|
||||
|
||||
function createFireParticle(butaneLighter) {
|
||||
var entityProperties = {
|
||||
userData: JSON.stringify({ tag: "equip-temporary" }),
|
||||
accelerationSpread: {
|
||||
x: 0.1,
|
||||
y: 0,
|
||||
z: 0.1
|
||||
},
|
||||
alpha: 0.039999999105930328,
|
||||
alphaFinish: 0.039999999105930328,
|
||||
alphaStart: 0.039999999105930328,
|
||||
azimuthFinish: 0.039999999105930328,
|
||||
azimuthStart: 0,
|
||||
dimensions: {
|
||||
x: 0.49194091558456421,
|
||||
y: 0.49194091558456421,
|
||||
z: 0.49194091558456421
|
||||
},
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
emitOrientation: {
|
||||
w: 1,
|
||||
x: -1.52587890625e-05,
|
||||
y: -1.52587890625e-05,
|
||||
z: -1.52587890625e-05
|
||||
},
|
||||
emitRate: 770,
|
||||
emitSpeed: 0.014000000432133675,
|
||||
isEmitting: false,
|
||||
lifespan: 0.37000000476837158,
|
||||
maxParticles: 820,
|
||||
name: 'lighter_particle',
|
||||
particleRadius: 0.0027000000700354576,
|
||||
position: {
|
||||
x: -0.00044769048690795898,
|
||||
y: 0.016354814171791077,
|
||||
z: 0.19217036664485931
|
||||
},
|
||||
radiusFinish: 0.0027000000700354576,
|
||||
radiusSpread: 3,
|
||||
radiusStart: 0.0027000000700354576,
|
||||
rotation: {
|
||||
w: 1,
|
||||
x: -0.0001678466796875,
|
||||
y: -1.52587890625e-05,
|
||||
z: -1.52587890625e-05
|
||||
},
|
||||
speedSpread: 0.56999999284744263,
|
||||
textures: 'atp:/textures/fire3.png',
|
||||
type: 'ParticleEffect',
|
||||
|
||||
|
||||
"color": {
|
||||
"red": 255,
|
||||
"green": 255,
|
||||
"blue": 255
|
||||
},
|
||||
"isEmitting": 0,
|
||||
"maxParticles": 820,
|
||||
"lifespan": 0.28,
|
||||
"emitRate": 1100,
|
||||
"emitSpeed": 0.007,
|
||||
"speedSpread": 0.5699999928474426,
|
||||
"emitOrientation": {
|
||||
"x": -0.0000152587890625,
|
||||
"y": -0.0000152587890625,
|
||||
"z": -0.0000152587890625,
|
||||
"w": 1
|
||||
},
|
||||
"emitDimensions": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"polarStart": 0,
|
||||
"polarFinish": 0,
|
||||
"azimuthStart": 0,
|
||||
"azimuthFinish": 0.03999999910593033,
|
||||
"emitAcceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"accelerationSpread": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"particleRadius": 0.0037,
|
||||
"radiusSpread": 3,
|
||||
"radiusStart": 0.008,
|
||||
"radiusFinish": 0.0004,
|
||||
"colorSpread": {
|
||||
"red": 0,
|
||||
"green": 0,
|
||||
"blue": 0
|
||||
},
|
||||
"colorStart": {
|
||||
"red": 255,
|
||||
"green": 255,
|
||||
"blue": 255
|
||||
},
|
||||
"colorFinish": {
|
||||
"red": 255,
|
||||
"green": 255,
|
||||
"blue": 255
|
||||
},
|
||||
"alpha": 0.03999999910593033,
|
||||
"alphaSpread": 0,
|
||||
"alphaStart": 0.141,
|
||||
"alphaFinish": 0.02,
|
||||
"emitterShouldTrail": 0,
|
||||
"textures": "atp:/textures/fire3.png"
|
||||
};
|
||||
return createEntity(entityProperties, butaneLighter);
|
||||
}
|
||||
|
||||
doCreateButaneLighter = function(transform) {
|
||||
var butaneLighter = createButaneLighter(transform);
|
||||
createFireParticle(butaneLighter);
|
||||
return butaneLighter;
|
||||
}
|
187
tutorial/ownershipToken.js
Normal file
187
tutorial/ownershipToken.js
Normal file
|
@ -0,0 +1,187 @@
|
|||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function(oThis) {
|
||||
if (typeof this !== 'function') {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function() {},
|
||||
fBound = function() {
|
||||
return fToBind.apply(this instanceof fNOP
|
||||
? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
if (this.prototype) {
|
||||
// Function.prototype doesn't have a prototype property
|
||||
fNOP.prototype = this.prototype;
|
||||
}
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
function getOption(options, key, defaultValue) {
|
||||
if (options.hasOwnProperty(key)) {
|
||||
return options[key];
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
var TOKEN_NAME_PREFIX = "ownership_token-";
|
||||
|
||||
function getOwnershipTokenID(parentEntityID) {
|
||||
var childEntityIDs = Entities.getChildrenIDs(parentEntityID);
|
||||
var ownerID = null;
|
||||
var ownerName = '';
|
||||
for (var i = 0; i < childEntityIDs.length; ++i) {
|
||||
var childID = childEntityIDs[i];
|
||||
var properties = Entities.getEntityProperties(childID, ['name', 'userData', 'lifetime', 'age']);
|
||||
var childName = properties.name;
|
||||
if (childName.indexOf(TOKEN_NAME_PREFIX) == 0) {
|
||||
if (ownerID === null || childName < ownerName) {
|
||||
ownerID = childID;
|
||||
ownerName = childName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ownerID;
|
||||
}
|
||||
|
||||
function createOwnershipToken(name, parentEntityID) {
|
||||
return Entities.addEntity({
|
||||
type: "Box",
|
||||
name: TOKEN_NAME_PREFIX + name,
|
||||
visible: false,
|
||||
parentID: parentEntityID,
|
||||
locationPosition: { x: 0, y: 0, z: 0 },
|
||||
dimensions: { x: 100, y: 100, z: 100 },
|
||||
collisionless: true,
|
||||
lifetime: 5
|
||||
});
|
||||
}
|
||||
|
||||
var DEBUG = true;
|
||||
function debug() {
|
||||
if (DEBUG) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
print.apply(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
var TOKEN_STATE_DESTROYED = -1;
|
||||
var TOKEN_STATE_UNOWNED = 0;
|
||||
var TOKEN_STATE_REQUESTING_OWNERSHIP = 1;
|
||||
var TOKEN_STATE_OWNED = 2;
|
||||
|
||||
OwnershipToken = function(name, parentEntityID, options) {
|
||||
this.name = MyAvatar.sessionUUID + "-" + Math.floor(Math.random() * 10000000);
|
||||
this.name = Math.floor(Math.random() * 10000000);
|
||||
this.parentEntityID = parentEntityID;
|
||||
|
||||
// How often to check whether the token is available if we don't currently own it
|
||||
this.checkEverySeconds = getOption(options, 'checkEverySeconds', 1000);
|
||||
this.updateTokenLifetimeEvery = getOption(options, 'updateTokenLifetimeEvery', 2000);
|
||||
|
||||
this.onGainedOwnership = getOption(options, 'onGainedOwnership', function() { });
|
||||
this.onLostOwnership = getOption(options, 'onLostOwnership', function() { });
|
||||
|
||||
this.ownershipTokenID = null;
|
||||
this.setState(TOKEN_STATE_UNOWNED);
|
||||
};
|
||||
|
||||
OwnershipToken.prototype = {
|
||||
destroy: function() {
|
||||
debug(this.name, "Destroying token");
|
||||
this.setState(TOKEN_STATE_DESTROYED);
|
||||
},
|
||||
|
||||
setState: function(newState) {
|
||||
if (this.state == newState) {
|
||||
debug(this.name, "Warning: Trying to set state to the current state");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.updateLifetimeID) {
|
||||
debug(this.name, "Clearing update lifetime interval");
|
||||
Script.clearInterval(this.updateLifetimeID);
|
||||
this.updateLifetimeID = null;
|
||||
}
|
||||
|
||||
if (this.checkOwnershipAvailableID) {
|
||||
Script.clearInterval(this.checkOwnershipAvailableID);
|
||||
this.checkOwnershipAvailableID = null;
|
||||
}
|
||||
|
||||
if (this.state == TOKEN_STATE_OWNED) {
|
||||
this.onLostOwnership(this);
|
||||
}
|
||||
|
||||
if (newState == TOKEN_STATE_UNOWNED) {
|
||||
this.checkOwnershipAvailableID = Script.setInterval(
|
||||
this.tryRequestingOwnership.bind(this), this.checkEverySeconds);
|
||||
|
||||
} else if (newState == TOKEN_STATE_REQUESTING_OWNERSHIP) {
|
||||
|
||||
} else if (newState == TOKEN_STATE_OWNED) {
|
||||
this.onGainedOwnership(this);
|
||||
this.updateLifetimeID = Script.setInterval(
|
||||
this.updateTokenLifetime.bind(this), this.updateTokenLifetimeEvery);
|
||||
} else if (newState == TOKEN_STATE_DESTROYED) {
|
||||
Entities.deleteEntity(this.ownershipTokenID);
|
||||
}
|
||||
|
||||
debug(this.name, "Info: Switching to state:", newState);
|
||||
this.state = newState;
|
||||
},
|
||||
updateTokenLifetime: function() {
|
||||
if (this.state != TOKEN_STATE_OWNED) {
|
||||
debug(this.name, "Error: Trying to update token while it is unowned");
|
||||
return;
|
||||
}
|
||||
|
||||
debug(this.name, "Updating entity lifetime");
|
||||
var age = Entities.getEntityProperties(this.ownershipTokenID, 'age').age;
|
||||
Entities.editEntity(this.ownershipTokenID, {
|
||||
lifetime: age + 5
|
||||
});
|
||||
},
|
||||
tryRequestingOwnership: function() {
|
||||
if (this.state == TOKEN_STATE_REQUESTING_OWNERSHIP || this.state == TOKEN_STATE_OWNED) {
|
||||
debug(this.name, "We already have or are requesting ownership");
|
||||
return;
|
||||
}
|
||||
|
||||
var ownerID = getOwnershipTokenID(this.parentEntityID);
|
||||
if (ownerID !== null) {
|
||||
// Already owned, return
|
||||
debug(this.name, "Token already owned by another client, return");
|
||||
return;
|
||||
}
|
||||
|
||||
this.ownershipTokenID = createOwnershipToken(this.name, this.parentEntityID);
|
||||
this.setState(TOKEN_STATE_REQUESTING_OWNERSHIP);
|
||||
|
||||
function checkOwnershipRequest() {
|
||||
var ownerID = getOwnershipTokenID(this.parentEntityID);
|
||||
if (ownerID == this.ownershipTokenID) {
|
||||
debug(this.name, "Info: Obtained ownership");
|
||||
this.setState(TOKEN_STATE_OWNED);
|
||||
} else {
|
||||
if (ownerID === null) {
|
||||
debug(this.name, "Warning: Checked ownership request and no tokens existed");
|
||||
}
|
||||
debug(this.name, "Info: Lost ownership request")
|
||||
this.ownershipTokenID = null;
|
||||
this.setState(TOKEN_STATE_UNOWNED);
|
||||
}
|
||||
}
|
||||
|
||||
Script.setTimeout(checkOwnershipRequest.bind(this), 2000);
|
||||
},
|
||||
};
|
81
tutorial/spinner.js
Normal file
81
tutorial/spinner.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// spinner.js
|
||||
//
|
||||
// Created by Ryan Huffman on 9/1/16.
|
||||
// Copyright 2016 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
|
||||
//
|
||||
|
||||
(function() {
|
||||
var DEBUG = false;
|
||||
function debug() {
|
||||
if (DEBUG) {
|
||||
print.apply(self, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
var spinnerSound = SoundCache.getSound("atp:/tutorial_sounds/Pinwheel.L.wav");
|
||||
var Spinner = function() {
|
||||
};
|
||||
function getChildProperties(entityID, propertyNames) {
|
||||
var childEntityIDs = Entities.getChildrenIDs(entityID);
|
||||
var results = {}
|
||||
for (var i = 0; i < childEntityIDs.length; ++i) {
|
||||
var childEntityID = childEntityIDs[i];
|
||||
var properties = Entities.getEntityProperties(childEntityID, propertyNames);
|
||||
results[childEntityID] = properties;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
Spinner.prototype = {
|
||||
onLit: function() {
|
||||
debug("LIT SPINNER", this.entityID);
|
||||
Entities.editEntity(this.entityID, {
|
||||
"angularDamping": 0.1,
|
||||
"angularVelocity": {
|
||||
"x": 20.471975326538086,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
});
|
||||
var injector = Audio.playSound(spinnerSound, {
|
||||
position: Entities.getEntityProperties(this.entityID, 'position').position,
|
||||
volume: 1.0,
|
||||
loop: false
|
||||
});
|
||||
|
||||
var childrenProps = getChildProperties(this.entityID, ['type']);
|
||||
for (var childEntityID in childrenProps) {
|
||||
var props = childrenProps[childEntityID];
|
||||
if (props.type == "ParticleEffect") {
|
||||
Entities.editEntity(childEntityID, {
|
||||
emitRate: 35,
|
||||
});
|
||||
}
|
||||
}
|
||||
Messages.sendLocalMessage("Tutorial-Spinner", "wasLit");
|
||||
|
||||
var self = this;
|
||||
Script.setTimeout(function() {
|
||||
debug("BLOW UP");
|
||||
injector.stop();
|
||||
|
||||
var childrenProps = getChildProperties(self.entityID, ['type']);
|
||||
for (var childEntityID in childrenProps) {
|
||||
var props = childrenProps[childEntityID];
|
||||
if (props.type == "ParticleEffect") {
|
||||
Entities.editEntity(childEntityID, {
|
||||
emitRate: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 4900);
|
||||
},
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
},
|
||||
};
|
||||
return new Spinner();
|
||||
});
|
BIN
tutorial/success.wav
Normal file
BIN
tutorial/success.wav
Normal file
Binary file not shown.
BIN
tutorial/success48.wav
Normal file
BIN
tutorial/success48.wav
Normal file
Binary file not shown.
1077
tutorial/tutorial.js
Normal file
1077
tutorial/tutorial.js
Normal file
File diff suppressed because it is too large
Load diff
151
tutorial/tutorialEntityIDs.js
Normal file
151
tutorial/tutorialEntityIDs.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
TUTORIAL_TAG_TO_ENTITY_IDS_MAP = {
|
||||
"teleport": {
|
||||
"{ff064b9e-7fa4-4693-a386-a67b9f92a948}": {
|
||||
"tag": "teleport"
|
||||
},
|
||||
"{4478f7b5-d3ac-4213-9a7b-ad8cd69575b8}": {
|
||||
"tag": "teleport"
|
||||
}
|
||||
},
|
||||
"finish": {
|
||||
"{340e05b5-88df-4b2b-b43c-756dd714d6d8}": {
|
||||
"tag": "finish"
|
||||
}
|
||||
},
|
||||
"door": {
|
||||
"{9c5b0fee-e695-4516-94cd-153371e3857b}": {
|
||||
"tag": "door"
|
||||
}
|
||||
},
|
||||
"farGrab": {
|
||||
"{70fcd96c-cd59-4f23-9ca5-a167f2f85680}": {
|
||||
"visible": false,
|
||||
"tag": "farGrab"
|
||||
},
|
||||
"{ff7b9793-0d94-4f18-bc09-4ab589126e60}": {
|
||||
"tag": "farGrab"
|
||||
},
|
||||
"{fdd77d2c-af36-41c1-ba57-74b7ae79d996}": {
|
||||
"tag": "farGrab"
|
||||
},
|
||||
"{e11700f6-bc9a-411f-9ddc-bf265d4e3ccf}": {
|
||||
"tag": "farGrab"
|
||||
},
|
||||
"{95850c56-cd1c-42b9-ab6b-a163a6f2878f}": {
|
||||
"tag": "farGrab"
|
||||
}
|
||||
},
|
||||
"nearGrab": {
|
||||
"{55c861ef-60ca-4722-a6c5-9c6967966ec5}": {
|
||||
"tag": "nearGrab"
|
||||
},
|
||||
"{644d655b-ae66-43b1-9bab-a44b9a8ad632}": {
|
||||
"tag": "nearGrab"
|
||||
},
|
||||
"{88221a22-b710-4d35-852b-5257b0aa77dc}": {
|
||||
"tag": "nearGrab"
|
||||
},
|
||||
"{8bf0baa1-88d0-448a-a782-100d4413bd82}": {
|
||||
"tag": "nearGrab"
|
||||
},
|
||||
"{5cf22b9c-fb22-4854-8821-554422980b24}": {
|
||||
"visible": false,
|
||||
"tag": "nearGrab"
|
||||
}
|
||||
},
|
||||
"equip-part1": {
|
||||
"{d73822ca-0a34-4cf4-a530-3258ac459a14}": {
|
||||
"tag": "equip-part1"
|
||||
},
|
||||
"{97ced5e7-fc81-40f9-a9e8-f85b4b30f24c}": {
|
||||
"tag": "equip-part1"
|
||||
},
|
||||
"{8572d991-5777-45df-97bf-7243d7b12f81}": {
|
||||
"tag": "equip-part1"
|
||||
},
|
||||
"{da5ea72e-54b6-41ac-b711-742b062b6968}": {
|
||||
"tag": "equip-part1"
|
||||
},
|
||||
"{c8944a13-9acb-4d77-b1ee-851845e98357}": {
|
||||
"tag": "equip-part1"
|
||||
},
|
||||
"{e9481c78-1a21-43f7-b54c-58f2efdf3c8f}": {
|
||||
"tag": "equip-part1"
|
||||
},
|
||||
"{ca3c28f3-15fc-4349-a85e-eaca0fad6434}": {
|
||||
"tag": "equip-part1"
|
||||
},
|
||||
"{09ddcb94-52a7-4f50-a5a2-db9db28fc519}": {
|
||||
"tag": "equip-part1"
|
||||
},
|
||||
"{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}": {
|
||||
"tag": "equip-part1"
|
||||
}
|
||||
},
|
||||
"equip-part2": {
|
||||
"{8b92eec5-aeed-4368-bce0-432cc9ad4c51}": {
|
||||
"tag": "equip-part2"
|
||||
},
|
||||
"{6307cd16-dd1d-4988-a339-578178436b45}": {
|
||||
"tag": "equip-part2"
|
||||
}
|
||||
},
|
||||
"turnAround": {
|
||||
"{ce74b3ca-d1c7-4980-bd98-2d488095a39e}": {
|
||||
"tag": "turnAround"
|
||||
}
|
||||
},
|
||||
"bothGrab": {
|
||||
"{14792a6e-dc6f-4e7a-843f-4b109b06b5a4}": {
|
||||
"visible": false,
|
||||
"tag": "bothGrab",
|
||||
"collidable": true
|
||||
},
|
||||
"{215dcd14-88fc-4604-9033-cbd2a660178a}": {
|
||||
"tag": "bothGrab"
|
||||
},
|
||||
"{fbc2e40d-0633-45ac-b1c9-97fc8465f93b}": {
|
||||
"tag": "bothGrab"
|
||||
},
|
||||
"{6752dad6-109d-4dc5-aef7-dc8509468cf4}": {
|
||||
"tag": "bothGrab"
|
||||
},
|
||||
"{178e2c71-dff5-4231-8d28-df47fddf4709}": {
|
||||
"soundKey": {
|
||||
"playbackGapRange": 0,
|
||||
"url": "atp:/sounds/crackling_fire.L.wav",
|
||||
"volume": 0.5,
|
||||
"playbackGap": 5,
|
||||
"playing": false,
|
||||
"loop": true
|
||||
},
|
||||
"tag": "bothGrab"
|
||||
},
|
||||
"{52445ac5-8730-4457-827e-6c076d2c609c}": {
|
||||
"tag": "bothGrab"
|
||||
}
|
||||
},
|
||||
"raiseHands": {
|
||||
"{7139e45d-25cf-470b-b133-c0fda0099d2b}": {
|
||||
"tag": "raiseHands"
|
||||
}
|
||||
},
|
||||
"equip": {
|
||||
"{e7897c9c-f4fa-4989-a383-28af56c2e544}": {
|
||||
"visible": false,
|
||||
"tag": "equip"
|
||||
},
|
||||
"{9df518da-9e65-4b76-8a79-eeefdb0b7310}": {
|
||||
"visible": false,
|
||||
"tag": "equip"
|
||||
},
|
||||
"{1a77c20e-5d9b-4b54-bf20-1416141a7ca8}": {
|
||||
"tag": "equip"
|
||||
}
|
||||
},
|
||||
"orient": {
|
||||
"{95d233ab-ed0a-46e1-b047-1c542688ef3f}": {
|
||||
"tag": "orient"
|
||||
}
|
||||
}
|
||||
}
|
45
tutorial/tutorialStartZone.js
Normal file
45
tutorial/tutorialStartZone.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
(function() {
|
||||
var TutorialStartZone = function() {
|
||||
print("TutorialStartZone | Creating");
|
||||
};
|
||||
|
||||
TutorialStartZone.prototype = {
|
||||
preload: function(entityID) {
|
||||
print("TutorialStartZone | Preload");
|
||||
this.entityID = entityID;
|
||||
this.sendStartIntervalID = null;
|
||||
},
|
||||
enterEntity: function() {
|
||||
var self = this;
|
||||
// send message to outer zone
|
||||
print("TutorialStartZone | Entered the tutorial start area");
|
||||
if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable()) {
|
||||
function sendStart() {
|
||||
print("TutorialStartZone | Checking parent ID");
|
||||
var parentID = Entities.getEntityProperties(self.entityID, 'parentID').parentID;
|
||||
print("TutorialStartZone | Parent ID is: ", parentID);
|
||||
if (parentID) {
|
||||
print("TutorialStartZone | Sending start");
|
||||
Entities.callEntityMethod(parentID, 'start');
|
||||
} else {
|
||||
print("TutorialStartZone | ERROR: No parent id found on tutorial start zone");
|
||||
}
|
||||
}
|
||||
this.sendStartIntervalID = Script.setInterval(sendStart, 1500);
|
||||
sendStart();
|
||||
} else {
|
||||
print("TutorialStartZone | User tried to go to tutorial with HMD and hand controllers, sending back to /");
|
||||
Window.alert("To proceed with this tutorial, please connect your VR headset and hand controllers.");
|
||||
location = "/";
|
||||
}
|
||||
},
|
||||
leaveEntity: function() {
|
||||
print("TutorialStartZone | Exited the tutorial start area");
|
||||
if (this.sendStartIntervalID) {
|
||||
Script.clearInterval(this.sendStartIntervalID);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new TutorialStartZone();
|
||||
});
|
109
tutorial/tutorialZone.js
Normal file
109
tutorial/tutorialZone.js
Normal file
|
@ -0,0 +1,109 @@
|
|||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function(oThis) {
|
||||
if (typeof this !== 'function') {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function() {},
|
||||
fBound = function() {
|
||||
return fToBind.apply(this instanceof fNOP
|
||||
? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
if (this.prototype) {
|
||||
// Function.prototype doesn't have a prototype property
|
||||
fNOP.prototype = this.prototype;
|
||||
}
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
(function() {
|
||||
var ownershipTokenPath = Script.resolvePath("ownershipToken.js");
|
||||
var tutorialPath = Script.resolvePath("tutorial.js");
|
||||
Script.include(ownershipTokenPath);
|
||||
Script.include(tutorialPath);
|
||||
|
||||
var TutorialZone = function() {
|
||||
print("TutorialZone | Creating");
|
||||
this.token = null;
|
||||
};
|
||||
|
||||
TutorialZone.prototype = {
|
||||
keyReleaseHandler: function(event) {
|
||||
print(event.text);
|
||||
if (event.isShifted && event.isAlt) {
|
||||
if (event.text == ",") {
|
||||
if (!this.tutorialManager.startNextStep()) {
|
||||
this.tutorialManager.startTutorial();
|
||||
}
|
||||
} else if (event.text == "F11") {
|
||||
this.tutorialManager.restartStep();
|
||||
} else if (event.text == "F10") {
|
||||
MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally;
|
||||
} else if (event.text == "r") {
|
||||
this.tutorialManager.stopTutorial();
|
||||
this.tutorialManager.startTutorial();
|
||||
}
|
||||
}
|
||||
},
|
||||
preload: function(entityID) {
|
||||
print("TutorialZone | Preload");
|
||||
this.entityID = entityID;
|
||||
},
|
||||
start: function() {
|
||||
print("TutorialZone | Got start");
|
||||
var self = this;
|
||||
if (!this.token) {
|
||||
print("TutorialZone | Creating token");
|
||||
this.token = new OwnershipToken(Math.random() * 100000, this.entityID, {
|
||||
onGainedOwnership: function(token) {
|
||||
print("TutorialZone | GOT OWNERSHIP");
|
||||
if (!self.tutorialManager) {
|
||||
self.tutorialManager = new TutorialManager();
|
||||
}
|
||||
self.tutorialManager.startTutorial();
|
||||
print("TutorialZone | making bound release handler");
|
||||
self.keyReleaseHandlerBound = self.keyReleaseHandler.bind(self);
|
||||
print("TutorialZone | binding");
|
||||
Controller.keyReleaseEvent.connect(self.keyReleaseHandlerBound);
|
||||
print("TutorialZone | done");
|
||||
},
|
||||
onLostOwnership: function(token) {
|
||||
print("TutorialZone | LOST OWNERSHIP");
|
||||
if (self.tutorialManager) {
|
||||
print("TutorialZone | stopping tutorial..");
|
||||
self.tutorialManager.stopTutorial();
|
||||
print("TutorialZone | done");
|
||||
Controller.keyReleaseEvent.disconnect(self.keyReleaseHandlerBound);
|
||||
} else {
|
||||
print("TutorialZone | no tutorial manager...");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
enterEntity: function() {
|
||||
print("TutorialZone | ENTERED THE TUTORIAL AREA");
|
||||
},
|
||||
leaveEntity: function() {
|
||||
print("TutorialZone | EXITED THE TUTORIAL AREA");
|
||||
if (this.token) {
|
||||
print("TutorialZone | Destroying token");
|
||||
this.token.destroy();
|
||||
this.token = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new TutorialZone();
|
||||
});
|
281
tutorial/viveControllerConfiguration.js
Normal file
281
tutorial/viveControllerConfiguration.js
Normal file
|
@ -0,0 +1,281 @@
|
|||
var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND");
|
||||
var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND");
|
||||
|
||||
var leftBaseRotation = Quat.multiply(
|
||||
Quat.fromPitchYawRollDegrees(0, 0, 45),
|
||||
Quat.multiply(
|
||||
Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
Quat.fromPitchYawRollDegrees(0, 0, 90)
|
||||
)
|
||||
);
|
||||
|
||||
var rightBaseRotation = Quat.multiply(
|
||||
Quat.fromPitchYawRollDegrees(0, 0, -45),
|
||||
Quat.multiply(
|
||||
Quat.fromPitchYawRollDegrees(90, 0, 0),
|
||||
Quat.fromPitchYawRollDegrees(0, 0, -90)
|
||||
)
|
||||
);
|
||||
var CONTROLLER_LENGTH_OFFSET = 0.0762;
|
||||
var leftBasePosition = {
|
||||
x: CONTROLLER_LENGTH_OFFSET / 2,
|
||||
y: CONTROLLER_LENGTH_OFFSET * 2,
|
||||
z: CONTROLLER_LENGTH_OFFSET / 2
|
||||
};
|
||||
var rightBasePosition = {
|
||||
x: -CONTROLLER_LENGTH_OFFSET / 2,
|
||||
y: CONTROLLER_LENGTH_OFFSET * 2,
|
||||
z: CONTROLLER_LENGTH_OFFSET / 2
|
||||
};
|
||||
|
||||
var viveNaturalDimensions = {
|
||||
x: 0.1174320001155138,
|
||||
y: 0.08361100335605443,
|
||||
z: 0.21942697931081057
|
||||
};
|
||||
|
||||
var viveNaturalPosition = {
|
||||
x: 0,
|
||||
y: -0.034076502197422087,
|
||||
z: 0.06380049744620919
|
||||
};
|
||||
|
||||
var viveModelURL = "atp:/controller/vive_body.fbx";
|
||||
var viveTipsModelURL = "atp:/controller/vive_tips.fbx"
|
||||
|
||||
VIVE_CONTROLLER_CONFIGURATION_LEFT = {
|
||||
name: "Vive",
|
||||
controllers: [
|
||||
{
|
||||
modelURL: viveModelURL,
|
||||
jointIndex: MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"),
|
||||
naturalPosition: viveNaturalPosition,
|
||||
rotation: leftBaseRotation,
|
||||
position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, 45), leftBasePosition),
|
||||
|
||||
dimensions: viveNaturalDimensions,
|
||||
|
||||
parts: {
|
||||
tips: {
|
||||
type: "static",
|
||||
modelURL: viveTipsModelURL,
|
||||
naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323},
|
||||
|
||||
textureName: "Tex.Blank",
|
||||
|
||||
defaultTextureLayer: "blank",
|
||||
textureLayers: {
|
||||
blank: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Blank.png",
|
||||
},
|
||||
trigger: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Trigger.png",
|
||||
},
|
||||
arrows: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Rotate.png",
|
||||
},
|
||||
grip: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Grip.png",
|
||||
},
|
||||
teleport: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Teleport.png",
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// The touchpad type draws a dot indicating the current touch/thumb position
|
||||
// and swaps in textures based on the thumb position.
|
||||
touchpad: {
|
||||
type: "touchpad",
|
||||
modelURL: "atp:/controller/vive_trackpad.fbx",
|
||||
visibleInput: "Vive.RSTouch",
|
||||
xInput: "Vive.LX",
|
||||
yInput: "Vive.LY",
|
||||
naturalPosition: {"x":0,"y":0.000979491975158453,"z":0.04872849956154823},
|
||||
minValue: 0.0,
|
||||
maxValue: 1.0,
|
||||
minPosition: { x: -0.035, y: 0.004, z: -0.005 },
|
||||
maxPosition: { x: -0.035, y: 0.004, z: -0.005 },
|
||||
disable_textureName: "Tex.touchpad-blank",
|
||||
|
||||
disable_defaultTextureLayer: "blank",
|
||||
disable_textureLayers: {
|
||||
blank: {
|
||||
defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg",
|
||||
},
|
||||
teleport: {
|
||||
defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg",
|
||||
},
|
||||
arrows: {
|
||||
defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows.jpg",
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
trigger: {
|
||||
type: "rotational",
|
||||
modelURL: "atp:/controller/vive_trigger.fbx",
|
||||
input: Controller.Standard.LT,
|
||||
naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763},
|
||||
origin: { x: 0, y: -0.015, z: -0.00 },
|
||||
minValue: 0.0,
|
||||
maxValue: 1.0,
|
||||
axis: { x: -1, y: 0, z: 0 },
|
||||
maxAngle: 20,
|
||||
},
|
||||
|
||||
l_grip: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_l_grip.fbx",
|
||||
naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226},
|
||||
},
|
||||
|
||||
r_grip: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_r_grip.fbx",
|
||||
naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226},
|
||||
},
|
||||
|
||||
sys_button: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_sys_button.fbx",
|
||||
naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311},
|
||||
},
|
||||
|
||||
button: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_button.fbx",
|
||||
naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}
|
||||
},
|
||||
button2: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_button.fbx",
|
||||
naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
|
||||
VIVE_CONTROLLER_CONFIGURATION_RIGHT = {
|
||||
name: "Vive Right",
|
||||
controllers: [
|
||||
{
|
||||
modelURL: viveModelURL,
|
||||
jointIndex: MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"),
|
||||
|
||||
rotation: rightBaseRotation,
|
||||
position: Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, 0, -45), rightBasePosition),
|
||||
|
||||
dimensions: viveNaturalDimensions,
|
||||
|
||||
naturalPosition: {
|
||||
x: 0,
|
||||
y: -0.034076502197422087,
|
||||
z: 0.06380049744620919
|
||||
},
|
||||
|
||||
parts: {
|
||||
tips: {
|
||||
type: "static",
|
||||
modelURL: viveTipsModelURL,
|
||||
naturalPosition: {"x":-0.004377640783786774,"y":-0.034371938556432724,"z":0.06769277155399323},
|
||||
|
||||
textureName: "Tex.Blank",
|
||||
|
||||
defaultTextureLayer: "blank",
|
||||
textureLayers: {
|
||||
blank: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Blank.png",
|
||||
},
|
||||
trigger: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Trigger.png",
|
||||
},
|
||||
arrows: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Rotate.png",
|
||||
},
|
||||
grip: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Grip.png",
|
||||
},
|
||||
teleport: {
|
||||
defaultTextureURL: viveTipsModelURL + "/Controller-Tips.fbm/Teleport.png",
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// The touchpad type draws a dot indicating the current touch/thumb position
|
||||
// and swaps in textures based on the thumb position.
|
||||
touchpad: {
|
||||
type: "touchpad",
|
||||
modelURL: "atp:/controller/vive_trackpad.fbx",
|
||||
visibleInput: "Vive.RSTouch",
|
||||
xInput: "Vive.RX",
|
||||
yInput: "Vive.RY",
|
||||
naturalPosition: { x: 0, y: 0.000979491975158453, z: 0.04872849956154823 },
|
||||
minValue: 0.0,
|
||||
maxValue: 1.0,
|
||||
minPosition: { x: -0.035, y: 0.004, z: -0.005 },
|
||||
maxPosition: { x: -0.035, y: 0.004, z: -0.005 },
|
||||
disable_textureName: "Tex.touchpad-blank",
|
||||
|
||||
disable_defaultTextureLayer: "blank",
|
||||
disable_textureLayers: {
|
||||
blank: {
|
||||
defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-blank.jpg",
|
||||
},
|
||||
teleport: {
|
||||
defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-teleport-active-LG.jpg",
|
||||
},
|
||||
arrows: {
|
||||
defaultTextureURL: "atp:/controller/vive_trackpad.fbx/Touchpad.fbm/touchpad-look-arrows-active.jpg",
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
trigger: {
|
||||
type: "rotational",
|
||||
modelURL: "atp:/controller/vive_trigger.fbx",
|
||||
input: Controller.Standard.RT,
|
||||
naturalPosition: {"x":0.000004500150680541992,"y":-0.027690507471561432,"z":0.04830199480056763},
|
||||
origin: { x: 0, y: -0.015, z: -0.00 },
|
||||
minValue: 0.0,
|
||||
maxValue: 1.0,
|
||||
axis: { x: -1, y: 0, z: 0 },
|
||||
maxAngle: 25,
|
||||
},
|
||||
|
||||
l_grip: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_l_grip.fbx",
|
||||
naturalPosition: {"x":-0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226},
|
||||
},
|
||||
|
||||
r_grip: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_r_grip.fbx",
|
||||
naturalPosition: {"x":0.01720449887216091,"y":-0.014324013143777847,"z":0.08714400231838226},
|
||||
},
|
||||
|
||||
sys_button: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_sys_button.fbx",
|
||||
naturalPosition: {"x":0,"y":0.0020399854984134436,"z":0.08825899660587311},
|
||||
},
|
||||
|
||||
button: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_button.fbx",
|
||||
naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}
|
||||
},
|
||||
button2: {
|
||||
type: "static",
|
||||
modelURL: "atp:/controller/vive_button.fbx",
|
||||
naturalPosition: {"x":0,"y":0.005480996798723936,"z":0.019918499514460564}
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
|
194
tutorial/viveHandsv2.js
Normal file
194
tutorial/viveHandsv2.js
Normal file
|
@ -0,0 +1,194 @@
|
|||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function(oThis) {
|
||||
if (typeof this !== 'function') {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function() {},
|
||||
fBound = function() {
|
||||
return fToBind.apply(this instanceof fNOP
|
||||
? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
if (this.prototype) {
|
||||
// Function.prototype doesn't have a prototype property
|
||||
fNOP.prototype = this.prototype;
|
||||
}
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
Script.setTimeout(function() { print('timeout') }, 100);
|
||||
|
||||
Script.include("controllerDisplay.js");
|
||||
Script.include("viveControllerConfiguration.js");
|
||||
|
||||
function debug() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift("CONTROLLER DEBUG:");
|
||||
print.apply(this, args);
|
||||
}
|
||||
|
||||
var zeroPosition = { x: 0, y: 0, z: 0 };
|
||||
var zeroRotation = { x: 0, y: 0, z: 0, w: 1 };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Management of controller display //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
ControllerDisplayManager = function() {
|
||||
var self = this;
|
||||
var controllerLeft = null;
|
||||
var controllerRight = null;
|
||||
var controllerCheckerIntervalID = null;
|
||||
|
||||
this.setLeftVisible = function(visible) {
|
||||
print("settings controller display to visible");
|
||||
if (controllerLeft) {
|
||||
print("doign it...", visible);
|
||||
controllerLeft.setVisible(visible);
|
||||
}
|
||||
};
|
||||
|
||||
this.setRightVisible = function(visible) {
|
||||
print("settings controller display to visible");
|
||||
if (controllerRight) {
|
||||
print("doign it...", visible);
|
||||
controllerRight.setVisible(visible);
|
||||
}
|
||||
};
|
||||
|
||||
function updateControllers() {
|
||||
if (HMD.active) {
|
||||
if ("Vive" in Controller.Hardware) {
|
||||
if (!controllerLeft) {
|
||||
debug("Found vive left!");
|
||||
controllerLeft = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION_LEFT);
|
||||
}
|
||||
if (!controllerRight) {
|
||||
debug("Found vive right!");
|
||||
controllerRight = createControllerDisplay(VIVE_CONTROLLER_CONFIGURATION_RIGHT);
|
||||
}
|
||||
// We've found the controllers, we no longer need to look for active controllers
|
||||
if (controllerCheckerIntervalID) {
|
||||
Script.clearInterval(controllerCheckerIntervalID);
|
||||
controllerCheckerIntervalID = null;
|
||||
}
|
||||
} else {
|
||||
debug("HMD active, but no controllers found");
|
||||
self.deleteControllerDisplays();
|
||||
if (controllerCheckerIntervalID == null) {
|
||||
controllerCheckerIntervalID = Script.setInterval(updateControllers, 1000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug("HMD inactive");
|
||||
// We aren't in HMD mode, we no longer need to look for active controllers
|
||||
if (controllerCheckerIntervalID) {
|
||||
debug("Clearing controller checker interval");
|
||||
Script.clearInterval(controllerCheckerIntervalID);
|
||||
controllerCheckerIntervalID = null;
|
||||
}
|
||||
self.deleteControllerDisplays();
|
||||
}
|
||||
}
|
||||
|
||||
Messages.subscribe('Controller-Display');
|
||||
var handleMessages = function(channel, message, sender) {
|
||||
if (!controllerLeft && !controllerRight) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender === MyAvatar.sessionUUID) {
|
||||
if (channel === 'Controller-Display') {
|
||||
var data = JSON.parse(message);
|
||||
var name = data.name;
|
||||
var visible = data.visible;
|
||||
//c.setDisplayAnnotation(name, visible);
|
||||
if (controllerLeft) {
|
||||
if (name in controllerLeft.annotations) {
|
||||
debug("hiding");
|
||||
for (var i = 0; i < controllerLeft.annotations[name].length; ++i) {
|
||||
debug("hiding", i);
|
||||
Overlays.editOverlay(controllerLeft.annotations[name][i], { visible: visible });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (controllerRight) {
|
||||
if (name in controllerRight.annotations) {
|
||||
debug("hiding");
|
||||
for (var i = 0; i < controllerRight.annotations[name].length; ++i) {
|
||||
debug("hiding", i);
|
||||
Overlays.editOverlay(controllerRight.annotations[name][i], { visible: visible });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (channel === 'Controller-Display-Parts') {
|
||||
debug('here part');
|
||||
var data = JSON.parse(message);
|
||||
for (var name in data) {
|
||||
var visible = data[name];
|
||||
if (controllerLeft) {
|
||||
controllerLeft.setPartVisible(name, visible);
|
||||
}
|
||||
if (controllerRight) {
|
||||
controllerRight.setPartVisible(name, visible);
|
||||
}
|
||||
}
|
||||
} else if (channel === 'Controller-Set-Part-Layer') {
|
||||
var data = JSON.parse(message);
|
||||
for (var name in data) {
|
||||
var layer = data[name];
|
||||
if (controllerLeft) {
|
||||
controllerLeft.setLayerForPart(name, layer);
|
||||
}
|
||||
if (controllerRight) {
|
||||
controllerRight.setLayerForPart(name, layer);
|
||||
}
|
||||
}
|
||||
} else if (channel == 'Hifi-Object-Manipulation') {// && sender == MyAvatar.sessionUUID) {
|
||||
//print("got manip");
|
||||
var data = JSON.parse(message);
|
||||
//print("post data", data);
|
||||
var visible = data.action != 'equip';
|
||||
//print("Calling...");
|
||||
if (data.joint == "LeftHand") {
|
||||
self.setLeftVisible(visible);
|
||||
} else if (data.joint == "RightHand") {
|
||||
self.setRightVisible(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Messages.messageReceived.connect(handleMessages);
|
||||
|
||||
this.deleteControllerDisplays = function() {
|
||||
if (controllerLeft) {
|
||||
deleteControllerDisplay(controllerLeft);
|
||||
controllerLeft = null;
|
||||
}
|
||||
if (controllerRight) {
|
||||
deleteControllerDisplay(controllerRight);
|
||||
controllerRight = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.destroy = function() {
|
||||
print("Destroying controller display");
|
||||
Messages.messageReceived.disconnect(handleMessages);
|
||||
self.deleteControllerDisplays();
|
||||
};
|
||||
|
||||
HMD.displayModeChanged.connect(updateControllers);
|
||||
|
||||
updateControllers();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue