mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 19:17:14 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix/audio-bg-join
This commit is contained in:
commit
defc3c1c4f
67 changed files with 1214 additions and 402 deletions
21
interface/resources/images/Announce-Blast.svg
Normal file
21
interface/resources/images/Announce-Blast.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 576 576" style="enable-background:new 0 0 576 576;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M359.2,249.6c16.7,43.9,33.3,87.9,50,131.8c1.3,3.4,2.5,6.7-0.2,9.9c-2.8,3.3-6.3,3-10.1,2.2
|
||||
c-42.4-8.8-84.8-17.5-127.2-26.4c-5.6-1.2-10.7-1-16,1.4c-5,2.2-10.3,3.9-15.4,5.9c-8.2,3.2-9.5,7.6-4.1,14.4
|
||||
c22.2,28.7,44.4,57.3,66.6,86c1.2,1.6,2.5,3.1,3.5,4.8c2.3,4.1,1.3,7.8-2.8,10.2c-1.6,0.9-3.3,1.6-5,2.2
|
||||
c-15.6,5.9-31.2,11.8-46.7,17.7c-8.7,3.3-10.2,2.9-15.7-4.2c-24-31-48-62.1-72-93.2c-4.8-6.3-6.5-6.7-14-3.9c-3,1.1-5.9,2.3-8.9,3.3
|
||||
c-10.9,3.5-20.5-1.1-24.6-11.7c-13-34.1-25.9-68.2-38.8-102.4c-4.5-11.7-0.2-21.5,11.6-26.2c9.8-3.9,19.6-7.5,29.4-11.2
|
||||
c28-10.6,56-21.4,84.2-31.8c5.4-2,9.4-5.1,12.8-9.7c25.7-34.6,51.6-69.1,77.3-103.7c2.3-3.1,4.7-5.8,8.9-5.3c4.4,0.5,5.7,4,7,7.5
|
||||
C325.7,161.4,342.4,205.5,359.2,249.6z"/>
|
||||
<path class="st0" d="M348.2,141.2c-2.2,0-4.5-0.6-6.5-1.8c-5.9-3.6-7.8-11.3-4.2-17.2l39.3-64.3c3.6-5.9,11.3-7.8,17.2-4.2
|
||||
c5.9,3.6,7.8,11.3,4.2,17.2l-39.3,64.3C356.5,139.1,352.4,141.2,348.2,141.2z"/>
|
||||
<path class="st0" d="M504.6,363c-0.9,0-1.8-0.1-2.7-0.3L424,345.6c-6.7-1.5-11-8.1-9.5-14.9c1.5-6.7,8.1-11,14.9-9.5l77.8,17.1
|
||||
c6.7,1.5,11,8.1,9.5,14.9C515.5,359,510.3,363,504.6,363z"/>
|
||||
<path class="st0" d="M393.2,237.7c-4.7,0-9.2-2.7-11.3-7.2c-2.9-6.3-0.2-13.7,6.1-16.6l78.4-36.4c6.3-2.9,13.7-0.2,16.6,6.1
|
||||
c2.9,6.3,0.2,13.7-6.1,16.6l-78.4,36.4C396.7,237.3,394.9,237.7,393.2,237.7z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -17,26 +17,26 @@ Rectangle {
|
|||
property alias pixelSize: label.font.pixelSize;
|
||||
property bool selected: false
|
||||
property bool hovered: false
|
||||
property bool enabled: false
|
||||
property int spacing: 2
|
||||
property var action: function () {}
|
||||
property string enabledColor: hifi.colors.blueHighlight
|
||||
property string disabledColor: hifi.colors.blueHighlight
|
||||
property string highlightColor: hifi.colors.blueHighlight;
|
||||
width: label.width + 64
|
||||
height: 32
|
||||
color: hifi.colors.white
|
||||
enabled: false
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
RalewaySemiBold {
|
||||
id: label;
|
||||
color: enabledColor
|
||||
color: enabled ? enabledColor : disabledColor
|
||||
font.pixelSize: 15;
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter;
|
||||
verticalCenter: parent.verticalCenter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: indicator
|
||||
|
|
|
@ -8,6 +8,7 @@ import "../styles" as HifiStyles
|
|||
import "../styles-uit"
|
||||
import "../"
|
||||
import "."
|
||||
|
||||
Item {
|
||||
id: web
|
||||
HifiConstants { id: hifi }
|
||||
|
@ -22,17 +23,14 @@ Item {
|
|||
property bool keyboardRaised: false
|
||||
property bool punctuationMode: false
|
||||
property bool isDesktop: false
|
||||
property string initialPage: ""
|
||||
property bool startingUp: true
|
||||
property alias webView: webview
|
||||
property alias profile: webview.profile
|
||||
property bool remove: false
|
||||
property var urlList: []
|
||||
property var forwardList: []
|
||||
|
||||
|
||||
property int currentPage: -1 // used as a model for repeater
|
||||
property alias pagesModel: pagesModel
|
||||
// Manage own browse history because WebEngineView history is wiped when a new URL is loaded via
|
||||
// onNewViewRequested, e.g., as happens when a social media share button is clicked.
|
||||
property var history: []
|
||||
property int historyIndex: -1
|
||||
|
||||
Rectangle {
|
||||
id: buttons
|
||||
|
@ -51,21 +49,22 @@ Item {
|
|||
|
||||
TabletWebButton {
|
||||
id: back
|
||||
enabledColor: hifi.colors.baseGray
|
||||
enabled: false
|
||||
enabledColor: hifi.colors.darkGray
|
||||
disabledColor: hifi.colors.lightGrayText
|
||||
enabled: historyIndex > 0
|
||||
text: "BACK"
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: goBack()
|
||||
hoverEnabled: true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
TabletWebButton {
|
||||
id: close
|
||||
enabledColor: hifi.colors.darkGray
|
||||
disabledColor: hifi.colors.lightGrayText
|
||||
enabled: true
|
||||
text: "CLOSE"
|
||||
|
||||
MouseArea {
|
||||
|
@ -75,7 +74,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
RalewaySemiBold {
|
||||
id: displayUrl
|
||||
color: hifi.colors.baseGray
|
||||
|
@ -90,7 +88,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
preventStealing: true
|
||||
|
@ -98,29 +95,10 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: pagesModel
|
||||
onCountChanged: {
|
||||
currentPage = count - 1;
|
||||
if (currentPage > 0) {
|
||||
back.enabledColor = hifi.colors.darkGray;
|
||||
} else {
|
||||
back.enabledColor = hifi.colors.baseGray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
if (webview.canGoBack) {
|
||||
forwardList.push(webview.url);
|
||||
webview.goBack();
|
||||
} else if (web.urlList.length > 0) {
|
||||
var url = web.urlList.pop();
|
||||
loadUrl(url);
|
||||
} else if (web.forwardList.length > 0) {
|
||||
var url = web.forwardList.pop();
|
||||
loadUrl(url);
|
||||
web.forwardList = [];
|
||||
if (historyIndex > 0) {
|
||||
historyIndex--;
|
||||
loadUrl(history[historyIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,19 +115,12 @@ Item {
|
|||
}
|
||||
|
||||
function goForward() {
|
||||
if (currentPage < pagesModel.count - 1) {
|
||||
currentPage++;
|
||||
if (historyIndex < history.length - 1) {
|
||||
historyIndex++;
|
||||
loadUrl(history[historyIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
function gotoPage(url) {
|
||||
urlAppend(url)
|
||||
}
|
||||
|
||||
function isUrlLoaded(url) {
|
||||
return (pagesModel.get(currentPage).webUrl === url);
|
||||
}
|
||||
|
||||
function reloadPage() {
|
||||
view.reloadAndBypassCache()
|
||||
view.setActiveFocusOnPress(true);
|
||||
|
@ -161,36 +132,8 @@ Item {
|
|||
web.url = webview.url;
|
||||
}
|
||||
|
||||
function onInitialPage(url) {
|
||||
return (url === webview.url);
|
||||
}
|
||||
|
||||
|
||||
function urlAppend(url) {
|
||||
var lurl = decodeURIComponent(url)
|
||||
if (lurl[lurl.length - 1] !== "/") {
|
||||
lurl = lurl + "/"
|
||||
}
|
||||
web.urlList.push(url);
|
||||
setBackButtonStatus();
|
||||
}
|
||||
|
||||
function setBackButtonStatus() {
|
||||
if (web.urlList.length > 0 || webview.canGoBack) {
|
||||
back.enabledColor = hifi.colors.darkGray;
|
||||
back.enabled = true;
|
||||
} else {
|
||||
back.enabledColor = hifi.colors.baseGray;
|
||||
back.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
onUrlChanged: {
|
||||
loadUrl(url);
|
||||
if (startingUp) {
|
||||
web.initialPage = webview.url;
|
||||
startingUp = false;
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
|
@ -258,6 +201,17 @@ Item {
|
|||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
onUrlChanged: {
|
||||
// Record history, skipping null and duplicate items.
|
||||
var urlString = url + "";
|
||||
urlString = urlString.replace(/\//g, "%2F"); // Consistent representation of "/"s to avoid false differences.
|
||||
if (urlString.length > 0 && (historyIndex === -1 || urlString !== history[historyIndex])) {
|
||||
historyIndex++;
|
||||
history = history.slice(0, historyIndex);
|
||||
history.push(urlString);
|
||||
}
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
keyboardRaised = false;
|
||||
punctuationMode = false;
|
||||
|
@ -277,17 +231,11 @@ Item {
|
|||
}
|
||||
|
||||
if (WebEngineView.LoadSucceededStatus == loadRequest.status) {
|
||||
if (startingUp) {
|
||||
web.initialPage = webview.url;
|
||||
startingUp = false;
|
||||
}
|
||||
webview.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
onNewViewRequested: {
|
||||
var currentUrl = webview.url;
|
||||
urlAppend(currentUrl);
|
||||
request.openIn(webview);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,11 +167,9 @@ Item {
|
|||
Rectangle {
|
||||
id: lozenge;
|
||||
visible: isAnnouncement;
|
||||
color: hifi.colors.redHighlight;
|
||||
color: lozengeHot.containsMouse ? hifi.colors.redAccent : hifi.colors.redHighlight;
|
||||
anchors.fill: infoRow;
|
||||
radius: lozenge.height / 2.0;
|
||||
border.width: lozengeHot.containsMouse ? 4 : 0;
|
||||
border.color: "white";
|
||||
}
|
||||
Row {
|
||||
id: infoRow;
|
||||
|
|
|
@ -23,7 +23,8 @@ Item {
|
|||
property var callbackFunction;
|
||||
property int dialogWidth;
|
||||
property int dialogHeight;
|
||||
property int comboOptionTextSize: 18;
|
||||
property int comboOptionTextSize: 16;
|
||||
property int comboBodyTextSize: 16;
|
||||
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
|
||||
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
|
||||
visible: false;
|
||||
|
@ -63,7 +64,7 @@ Item {
|
|||
anchors.left: parent.left;
|
||||
anchors.leftMargin: 20;
|
||||
size: 24;
|
||||
color: 'black';
|
||||
color: hifi.colors.darkGray;
|
||||
horizontalAlignment: Text.AlignLeft;
|
||||
verticalAlignment: Text.AlignTop;
|
||||
}
|
||||
|
@ -141,6 +142,7 @@ Item {
|
|||
height: 30;
|
||||
size: comboOptionTextSize;
|
||||
wrapMode: Text.WordWrap;
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
|
@ -148,11 +150,12 @@ Item {
|
|||
text: bodyText;
|
||||
anchors.top: optionTitle.bottom;
|
||||
anchors.left: comboOptionSelected.right;
|
||||
anchors.leftMargin: 25;
|
||||
anchors.leftMargin: 10;
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: 10;
|
||||
size: comboOptionTextSize;
|
||||
size: comboBodyTextSize;
|
||||
wrapMode: Text.WordWrap;
|
||||
color: hifi.colors.darkGray;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
|
|
|
@ -1013,10 +1013,10 @@ Rectangle {
|
|||
onClicked: {
|
||||
popupComboDialog("Set your availability:",
|
||||
availabilityComboBox.availabilityStrings,
|
||||
["Your username will be visible in everyone's 'Nearby' list.\nAnyone will be able to jump to your location from within the 'Nearby' list.",
|
||||
"Your location will be visible in the 'Connections' list only for those with whom you are connected or friends.\nThey will be able to jump to your location if the domain allows.",
|
||||
"Your location will be visible in the 'Connections' list only for those with whom you are friends.\nThey will be able to jump to your location if the domain allows.",
|
||||
"Your location will not be visible in the 'Connections' list of any other users. Only domain admins will be able to see your username in the 'Nearby' list."],
|
||||
["Your username will be visible in everyone's 'Nearby' list. Anyone will be able to jump to your location from within the 'Nearby' list.",
|
||||
"Your location will be visible in the 'Connections' list only for those with whom you are connected or friends. They'll be able to jump to your location if the domain allows.",
|
||||
"Your location will be visible in the 'Connections' list only for those with whom you are friends. They'll be able to jump to your location if the domain allows. You will only receive 'Happening Now' notifications in 'Go To' from friends.",
|
||||
"You will appear offline in the 'Connections' list, and you will not receive 'Happening Now' notifications in 'Go To'."],
|
||||
["all", "connections", "friends", "none"]);
|
||||
}
|
||||
onEntered: availabilityComboBox.color = hifi.colors.lightGrayText;
|
||||
|
|
|
@ -5459,7 +5459,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance());
|
||||
}
|
||||
|
||||
scriptEngine->registerGlobalObject("Overlays", &_overlays);
|
||||
scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this));
|
||||
|
||||
// hook our avatar and avatar hash map object into this script engine
|
||||
|
@ -5558,6 +5557,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
|
||||
auto entityScriptServerLog = DependencyManager::get<EntityScriptServerLogClient>();
|
||||
scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data());
|
||||
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
|
||||
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue);
|
||||
|
||||
|
|
|
@ -140,9 +140,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
auto orientation = myAvatar->getLocalOrientation();
|
||||
_rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState);
|
||||
|
||||
// evaluate AnimGraph animation and update jointStates.
|
||||
Model::updateRig(deltaTime, parentTransform);
|
||||
|
||||
Rig::EyeParameters eyeParams;
|
||||
eyeParams.eyeLookAt = lookAt;
|
||||
eyeParams.eyeSaccade = head->getSaccade();
|
||||
|
@ -153,6 +150,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
|
||||
_rig->updateFromEyeParameters(eyeParams);
|
||||
|
||||
// evaluate AnimGraph animation and update jointStates.
|
||||
Parent::updateRig(deltaTime, parentTransform);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class AvatarInputs : public QQuickItem {
|
|||
|
||||
public:
|
||||
static AvatarInputs* getInstance();
|
||||
float loudnessToAudioLevel(float loudness);
|
||||
Q_INVOKABLE float loudnessToAudioLevel(float loudness);
|
||||
AvatarInputs(QQuickItem* parent = nullptr);
|
||||
void update();
|
||||
bool showAudioTools() const { return _showAudioTools; }
|
||||
|
|
|
@ -28,11 +28,15 @@ const int MAX_HISTORY_SIZE = 64;
|
|||
const QString COMMAND_STYLE = "color: #266a9b;";
|
||||
|
||||
const QString RESULT_SUCCESS_STYLE = "color: #677373;";
|
||||
const QString RESULT_INFO_STYLE = "color: #223bd1;";
|
||||
const QString RESULT_WARNING_STYLE = "color: #d13b22;";
|
||||
const QString RESULT_ERROR_STYLE = "color: #d13b22;";
|
||||
|
||||
const QString GUTTER_PREVIOUS_COMMAND = "<span style=\"color: #57b8bb;\"><</span>";
|
||||
const QString GUTTER_ERROR = "<span style=\"color: #d13b22;\">X</span>";
|
||||
|
||||
const QString JSConsole::_consoleFileName { "about:console" };
|
||||
|
||||
JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
|
||||
QWidget(parent),
|
||||
_ui(new Ui::Console),
|
||||
|
@ -77,6 +81,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
|
|||
}
|
||||
if (_scriptEngine != NULL) {
|
||||
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
|
||||
disconnect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo);
|
||||
disconnect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning);
|
||||
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError);
|
||||
if (_ownScriptEngine) {
|
||||
_scriptEngine->deleteLater();
|
||||
|
@ -84,10 +90,12 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
|
|||
}
|
||||
|
||||
// if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine
|
||||
_ownScriptEngine = scriptEngine == NULL;
|
||||
_scriptEngine = _ownScriptEngine ? DependencyManager::get<ScriptEngines>()->loadScript(QString(), false) : scriptEngine;
|
||||
_ownScriptEngine = (scriptEngine == NULL);
|
||||
_scriptEngine = _ownScriptEngine ? DependencyManager::get<ScriptEngines>()->loadScript(_consoleFileName, false) : scriptEngine;
|
||||
|
||||
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
|
||||
connect(_scriptEngine, &ScriptEngine::infoMessage, this, &JSConsole::handleInfo);
|
||||
connect(_scriptEngine, &ScriptEngine::warningMessage, this, &JSConsole::handleWarning);
|
||||
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError);
|
||||
}
|
||||
|
||||
|
@ -107,11 +115,10 @@ void JSConsole::executeCommand(const QString& command) {
|
|||
|
||||
QScriptValue JSConsole::executeCommandInWatcher(const QString& command) {
|
||||
QScriptValue result;
|
||||
static const QString filename = "JSConcole";
|
||||
QMetaObject::invokeMethod(_scriptEngine, "evaluate", Qt::ConnectionType::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QScriptValue, result),
|
||||
Q_ARG(const QString&, command),
|
||||
Q_ARG(const QString&, filename));
|
||||
Q_ARG(const QString&, _consoleFileName));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -134,16 +141,26 @@ void JSConsole::commandFinished() {
|
|||
resetCurrentCommandHistory();
|
||||
}
|
||||
|
||||
void JSConsole::handleError(const QString& scriptName, const QString& message) {
|
||||
void JSConsole::handleError(const QString& message, const QString& scriptName) {
|
||||
Q_UNUSED(scriptName);
|
||||
appendMessage(GUTTER_ERROR, "<span style='" + RESULT_ERROR_STYLE + "'>" + message.toHtmlEscaped() + "</span>");
|
||||
}
|
||||
|
||||
void JSConsole::handlePrint(const QString& scriptName, const QString& message) {
|
||||
void JSConsole::handlePrint(const QString& message, const QString& scriptName) {
|
||||
Q_UNUSED(scriptName);
|
||||
appendMessage("", message);
|
||||
}
|
||||
|
||||
void JSConsole::handleInfo(const QString& message, const QString& scriptName) {
|
||||
Q_UNUSED(scriptName);
|
||||
appendMessage("", "<span style='" + RESULT_INFO_STYLE + "'>" + message.toHtmlEscaped() + "</span>");
|
||||
}
|
||||
|
||||
void JSConsole::handleWarning(const QString& message, const QString& scriptName) {
|
||||
Q_UNUSED(scriptName);
|
||||
appendMessage("", "<span style='" + RESULT_WARNING_STYLE + "'>" + message.toHtmlEscaped() + "</span>");
|
||||
}
|
||||
|
||||
void JSConsole::mouseReleaseEvent(QMouseEvent* event) {
|
||||
_ui->promptTextEdit->setFocus();
|
||||
}
|
||||
|
|
|
@ -47,8 +47,10 @@ protected:
|
|||
protected slots:
|
||||
void scrollToBottom();
|
||||
void resizeTextInput();
|
||||
void handlePrint(const QString& scriptName, const QString& message);
|
||||
void handleError(const QString& scriptName, const QString& message);
|
||||
void handlePrint(const QString& message, const QString& scriptName);
|
||||
void handleInfo(const QString& message, const QString& scriptName);
|
||||
void handleWarning(const QString& message, const QString& scriptName);
|
||||
void handleError(const QString& message, const QString& scriptName);
|
||||
void commandFinished();
|
||||
|
||||
private:
|
||||
|
@ -66,6 +68,7 @@ private:
|
|||
bool _ownScriptEngine;
|
||||
QString _rootCommand;
|
||||
ScriptEngine* _scriptEngine;
|
||||
static const QString _consoleFileName;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -81,6 +81,10 @@ QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& propert
|
|||
void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
||||
QVariantMap properties = originalProperties;
|
||||
|
||||
if (properties["name"].isValid()) {
|
||||
setName(properties["name"].toString());
|
||||
}
|
||||
|
||||
// carry over some legacy keys
|
||||
if (!properties["position"].isValid() && !properties["localPosition"].isValid()) {
|
||||
if (properties["p1"].isValid()) {
|
||||
|
@ -207,6 +211,9 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
}
|
||||
|
||||
QVariant Base3DOverlay::getProperty(const QString& property) {
|
||||
if (property == "name") {
|
||||
return _name;
|
||||
}
|
||||
if (property == "position" || property == "start" || property == "p1" || property == "point") {
|
||||
return vec3toVariant(getPosition());
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ public:
|
|||
virtual OverlayID getOverlayID() const override { return OverlayID(getID().toString()); }
|
||||
void setOverlayID(OverlayID overlayID) override { setID(overlayID); }
|
||||
|
||||
virtual QString getName() const override { return QString("Overlay:") + _name; }
|
||||
void setName(QString name) { _name = name; }
|
||||
|
||||
// getters
|
||||
virtual bool is3D() const override { return true; }
|
||||
|
||||
|
@ -74,6 +77,8 @@ protected:
|
|||
bool _drawInFront;
|
||||
bool _isAA;
|
||||
bool _isGrabbable { false };
|
||||
|
||||
QString _name;
|
||||
};
|
||||
|
||||
#endif // hifi_Base3DOverlay_h
|
||||
|
|
|
@ -288,3 +288,10 @@ void ModelOverlay::locationChanged(bool tellPhysics) {
|
|||
_model->setTranslation(getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
QString ModelOverlay::getName() const {
|
||||
if (_name != "") {
|
||||
return QString("Overlay:") + getType() + ":" + _name;
|
||||
}
|
||||
return QString("Overlay:") + getType() + ":" + _url.toString();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ public:
|
|||
static QString const TYPE;
|
||||
virtual QString getType() const override { return TYPE; }
|
||||
|
||||
virtual QString getName() const override;
|
||||
|
||||
ModelOverlay();
|
||||
ModelOverlay(const ModelOverlay* modelOverlay);
|
||||
|
||||
|
|
|
@ -1127,28 +1127,27 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
|
|||
}
|
||||
|
||||
void AudioClient::prepareLocalAudioInjectors() {
|
||||
if (_outputPeriod == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int bufferCapacity = _localInjectorsStream.getSampleCapacity();
|
||||
if (_localToOutputResampler) {
|
||||
// avoid overwriting the buffer,
|
||||
// instead of failing on writes because the buffer is used as a lock-free pipe
|
||||
bufferCapacity -=
|
||||
_localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) *
|
||||
AudioConstants::STEREO;
|
||||
bufferCapacity += 1;
|
||||
}
|
||||
|
||||
int samplesNeeded = std::numeric_limits<int>::max();
|
||||
while (samplesNeeded > 0) {
|
||||
// lock for every write to avoid locking out the device callback
|
||||
// this lock is intentional - the buffer is only lock-free in its use in the device callback
|
||||
RecursiveLock lock(_localAudioMutex);
|
||||
// unlock between every write to allow device switching
|
||||
Lock lock(_localAudioMutex);
|
||||
|
||||
// in case of a device switch, consider bufferCapacity volatile across iterations
|
||||
if (_outputPeriod == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int bufferCapacity = _localInjectorsStream.getSampleCapacity();
|
||||
int maxOutputSamples = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * AudioConstants::STEREO;
|
||||
if (_localToOutputResampler) {
|
||||
maxOutputSamples =
|
||||
_localToOutputResampler->getMaxOutput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL) *
|
||||
AudioConstants::STEREO;
|
||||
}
|
||||
|
||||
samplesNeeded = bufferCapacity - _localSamplesAvailable.load(std::memory_order_relaxed);
|
||||
if (samplesNeeded <= 0) {
|
||||
if (samplesNeeded < maxOutputSamples) {
|
||||
// avoid overwriting the buffer to prevent losing frames
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1198,16 +1197,18 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
|
|||
memset(mixBuffer, 0, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO * sizeof(float));
|
||||
|
||||
for (AudioInjector* injector : _activeLocalAudioInjectors) {
|
||||
if (injector->getLocalBuffer()) {
|
||||
// the lock guarantees that injectorBuffer, if found, is invariant
|
||||
AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer();
|
||||
if (injectorBuffer) {
|
||||
|
||||
static const int HRTF_DATASET_INDEX = 1;
|
||||
|
||||
int numChannels = injector->isAmbisonic() ? AudioConstants::AMBISONIC : (injector->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qint64 bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
|
||||
size_t bytesToRead = numChannels * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
|
||||
|
||||
// get one frame from the injector
|
||||
memset(_localScratchBuffer, 0, bytesToRead);
|
||||
if (0 < injector->getLocalBuffer()->readData((char*)_localScratchBuffer, bytesToRead)) {
|
||||
if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) {
|
||||
|
||||
if (injector->isAmbisonic()) {
|
||||
|
||||
|
@ -1347,15 +1348,17 @@ void AudioClient::setIsStereoInput(bool isStereoInput) {
|
|||
}
|
||||
|
||||
bool AudioClient::outputLocalInjector(AudioInjector* injector) {
|
||||
Lock lock(_injectorsMutex);
|
||||
if (injector->getLocalBuffer() && _audioInput ) {
|
||||
// just add it to the vector of active local injectors, if
|
||||
// not already there.
|
||||
// Since this is invoked with invokeMethod, there _should_ be
|
||||
// no reason to lock access to the vector of injectors.
|
||||
AudioInjectorLocalBuffer* injectorBuffer = injector->getLocalBuffer();
|
||||
if (injectorBuffer) {
|
||||
// local injectors are on the AudioInjectorsThread, so we must guard access
|
||||
Lock lock(_injectorsMutex);
|
||||
if (!_activeLocalAudioInjectors.contains(injector)) {
|
||||
qCDebug(audioclient) << "adding new injector";
|
||||
_activeLocalAudioInjectors.append(injector);
|
||||
|
||||
// move local buffer to the LocalAudioThread to avoid dataraces with AudioInjector (like stop())
|
||||
injectorBuffer->setParent(nullptr);
|
||||
injectorBuffer->moveToThread(&_localAudioThread);
|
||||
} else {
|
||||
qCDebug(audioclient) << "injector exists in active list already";
|
||||
}
|
||||
|
@ -1363,7 +1366,7 @@ bool AudioClient::outputLocalInjector(AudioInjector* injector) {
|
|||
return true;
|
||||
|
||||
} else {
|
||||
// no local buffer or audio
|
||||
// no local buffer
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1482,7 +1485,7 @@ void AudioClient::outputNotify() {
|
|||
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) {
|
||||
bool supportedFormat = false;
|
||||
|
||||
RecursiveLock lock(_localAudioMutex);
|
||||
Lock lock(_localAudioMutex);
|
||||
_localSamplesAvailable.exchange(0, std::memory_order_release);
|
||||
|
||||
// cleanup any previously initialized device
|
||||
|
@ -1711,8 +1714,12 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
|
||||
int injectorSamplesPopped = 0;
|
||||
{
|
||||
RecursiveLock lock(_audio->_localAudioMutex);
|
||||
bool append = networkSamplesPopped > 0;
|
||||
// this does not require a lock as of the only two functions adding to _localSamplesAvailable (samples count):
|
||||
// - prepareLocalAudioInjectors will only increase samples count
|
||||
// - switchOutputToAudioDevice will zero samples count
|
||||
// stop the device, so that readData will exhaust the existing buffer or see a zeroed samples count
|
||||
// and start the device, which can only see a zeroed samples count
|
||||
samplesRequested = std::min(samplesRequested, _audio->_localSamplesAvailable.load(std::memory_order_acquire));
|
||||
if ((injectorSamplesPopped = _localInjectorsStream.appendSamples(mixBuffer, samplesRequested, append)) > 0) {
|
||||
_audio->_localSamplesAvailable.fetch_sub(injectorSamplesPopped, std::memory_order_release);
|
||||
|
|
|
@ -83,8 +83,6 @@ public:
|
|||
using AudioPositionGetter = std::function<glm::vec3()>;
|
||||
using AudioOrientationGetter = std::function<glm::quat()>;
|
||||
|
||||
using RecursiveMutex = std::recursive_mutex;
|
||||
using RecursiveLock = std::unique_lock<RecursiveMutex>;
|
||||
using Mutex = std::mutex;
|
||||
using Lock = std::unique_lock<Mutex>;
|
||||
|
||||
|
@ -332,7 +330,7 @@ private:
|
|||
float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
|
||||
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
|
||||
float* _localOutputMixBuffer { NULL };
|
||||
RecursiveMutex _localAudioMutex;
|
||||
Mutex _localAudioMutex;
|
||||
|
||||
AudioLimiter _audioLimiter;
|
||||
|
||||
|
|
|
@ -33,7 +33,11 @@ public:
|
|||
PacketType packetType, QString codecName = QString(""));
|
||||
|
||||
public slots:
|
||||
// threadsafe
|
||||
// moves injector->getLocalBuffer() to another thread (so removes its parent)
|
||||
// take care to delete it when ~AudioInjector, as parenting Qt semantics will not work
|
||||
virtual bool outputLocalInjector(AudioInjector* injector) = 0;
|
||||
|
||||
virtual bool shouldLoopbackInjectors() { return false; }
|
||||
|
||||
virtual void setIsStereoInput(bool stereo) = 0;
|
||||
|
|
|
@ -51,6 +51,10 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt
|
|||
{
|
||||
}
|
||||
|
||||
AudioInjector::~AudioInjector() {
|
||||
deleteLocalBuffer();
|
||||
}
|
||||
|
||||
bool AudioInjector::stateHas(AudioInjectorState state) const {
|
||||
return (_state & state) == state;
|
||||
}
|
||||
|
@ -87,11 +91,7 @@ void AudioInjector::finish() {
|
|||
|
||||
emit finished();
|
||||
|
||||
if (_localBuffer) {
|
||||
_localBuffer->stop();
|
||||
_localBuffer->deleteLater();
|
||||
_localBuffer = NULL;
|
||||
}
|
||||
deleteLocalBuffer();
|
||||
|
||||
if (stateHas(AudioInjectorState::PendingDelete)) {
|
||||
// we've been asked to delete after finishing, trigger a deleteLater here
|
||||
|
@ -163,7 +163,7 @@ bool AudioInjector::injectLocally() {
|
|||
if (_localAudioInterface) {
|
||||
if (_audioData.size() > 0) {
|
||||
|
||||
_localBuffer = new AudioInjectorLocalBuffer(_audioData, this);
|
||||
_localBuffer = new AudioInjectorLocalBuffer(_audioData);
|
||||
|
||||
_localBuffer->open(QIODevice::ReadOnly);
|
||||
_localBuffer->setShouldLoop(_options.loop);
|
||||
|
@ -172,7 +172,8 @@ bool AudioInjector::injectLocally() {
|
|||
_localBuffer->setCurrentOffset(_currentSendOffset);
|
||||
|
||||
// call this function on the AudioClient's thread
|
||||
success = QMetaObject::invokeMethod(_localAudioInterface, "outputLocalInjector", Q_ARG(AudioInjector*, this));
|
||||
// this will move the local buffer's thread to the LocalInjectorThread
|
||||
success = _localAudioInterface->outputLocalInjector(this);
|
||||
|
||||
if (!success) {
|
||||
qCDebug(audio) << "AudioInjector::injectLocally could not output locally via _localAudioInterface";
|
||||
|
@ -185,6 +186,14 @@ bool AudioInjector::injectLocally() {
|
|||
return success;
|
||||
}
|
||||
|
||||
void AudioInjector::deleteLocalBuffer() {
|
||||
if (_localBuffer) {
|
||||
_localBuffer->stop();
|
||||
_localBuffer->deleteLater();
|
||||
_localBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const uchar MAX_INJECTOR_VOLUME = packFloatGainToByte(1.0f);
|
||||
static const int64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = -1;
|
||||
static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0;
|
||||
|
|
|
@ -52,6 +52,7 @@ class AudioInjector : public QObject {
|
|||
public:
|
||||
AudioInjector(const Sound& sound, const AudioInjectorOptions& injectorOptions);
|
||||
AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions);
|
||||
~AudioInjector();
|
||||
|
||||
bool isFinished() const { return (stateHas(AudioInjectorState::Finished)); }
|
||||
|
||||
|
@ -99,6 +100,7 @@ private:
|
|||
int64_t injectNextFrame();
|
||||
bool inject(bool(AudioInjectorManager::*injection)(AudioInjector*));
|
||||
bool injectLocally();
|
||||
void deleteLocalBuffer();
|
||||
|
||||
static AbstractAudioInterface* _localAudioInterface;
|
||||
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
|
||||
#include "AudioInjectorLocalBuffer.h"
|
||||
|
||||
AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent) :
|
||||
QIODevice(parent),
|
||||
AudioInjectorLocalBuffer::AudioInjectorLocalBuffer(const QByteArray& rawAudioArray) :
|
||||
_rawAudioArray(rawAudioArray),
|
||||
_shouldLoop(false),
|
||||
_isStopped(false),
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
class AudioInjectorLocalBuffer : public QIODevice {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioInjectorLocalBuffer(const QByteArray& rawAudioArray, QObject* parent);
|
||||
AudioInjectorLocalBuffer(const QByteArray& rawAudioArray);
|
||||
|
||||
void stop();
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
|||
}
|
||||
|
||||
// evaluate AnimGraph animation and update jointStates.
|
||||
Model::updateRig(deltaTime, parentTransform);
|
||||
Parent::updateRig(deltaTime, parentTransform);
|
||||
}
|
||||
|
||||
void SkeletonModel::updateAttitude() {
|
||||
|
@ -136,7 +136,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
if (fullUpdate) {
|
||||
setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
Parent::simulate(deltaTime, fullUpdate);
|
||||
|
||||
// let rig compute the model offset
|
||||
glm::vec3 registrationPoint;
|
||||
|
@ -144,7 +144,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
setOffset(registrationPoint);
|
||||
}
|
||||
} else {
|
||||
Model::simulate(deltaTime, fullUpdate);
|
||||
Parent::simulate(deltaTime, fullUpdate);
|
||||
}
|
||||
|
||||
if (!isActive() || !_owningAvatar->isMyAvatar()) {
|
||||
|
|
|
@ -23,6 +23,7 @@ using SkeletonModelWeakPointer = std::weak_ptr<SkeletonModel>;
|
|||
|
||||
/// A skeleton loaded from a model.
|
||||
class SkeletonModel : public CauterizedModel {
|
||||
using Parent = CauterizedModel;
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
|
|
@ -357,6 +357,8 @@ class AvatarData : public QObject, public SpatiallyNestable {
|
|||
|
||||
public:
|
||||
|
||||
virtual QString getName() const override { return QString("Avatar:") + _displayName; }
|
||||
|
||||
static const QString FRAME_NAME;
|
||||
|
||||
static void fromFrame(const QByteArray& frameData, AvatarData& avatar, bool useFrameSkeleton = true);
|
||||
|
|
|
@ -281,7 +281,7 @@ public:
|
|||
float getAngularDamping() const;
|
||||
void setAngularDamping(float value);
|
||||
|
||||
QString getName() const;
|
||||
virtual QString getName() const override;
|
||||
void setName(const QString& value);
|
||||
QString getDebugName();
|
||||
|
||||
|
|
|
@ -407,9 +407,11 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
// return QUuid();
|
||||
// }
|
||||
|
||||
bool entityFound { false };
|
||||
_entityTree->withReadLock([&] {
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
entityFound = true;
|
||||
// make sure the properties has a type, so that the encode can know which properties to include
|
||||
properties.setType(entity->getType());
|
||||
bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges();
|
||||
|
@ -464,6 +466,27 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
});
|
||||
}
|
||||
});
|
||||
if (!entityFound) {
|
||||
// we've made an edit to an entity we don't know about, or to a non-entity. If it's a known non-entity,
|
||||
// print a warning and don't send an edit packet to the entity-server.
|
||||
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
|
||||
if (parentFinder) {
|
||||
bool success;
|
||||
auto nestableWP = parentFinder->find(id, success, static_cast<SpatialParentTree*>(_entityTree.get()));
|
||||
if (success) {
|
||||
auto nestable = nestableWP.lock();
|
||||
if (nestable) {
|
||||
NestableType nestableType = nestable->getNestableType();
|
||||
if (nestableType == NestableType::Overlay || nestableType == NestableType::Avatar) {
|
||||
qCWarning(entities) << "attempted edit on non-entity: " << id << nestable->getName();
|
||||
return QUuid(); // null UUID to indicate failure
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// we queue edit packets even if we don't know about the entity. This is to allow AC agents
|
||||
// to edit entities they know only by ID.
|
||||
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
|
||||
return id;
|
||||
}
|
||||
|
@ -1515,6 +1538,24 @@ bool EntityScriptingInterface::isChildOfParent(QUuid childID, QUuid parentID) {
|
|||
return isChild;
|
||||
}
|
||||
|
||||
QString EntityScriptingInterface::getNestableType(QUuid id) {
|
||||
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
|
||||
if (!parentFinder) {
|
||||
return "unknown";
|
||||
}
|
||||
bool success;
|
||||
SpatiallyNestableWeakPointer objectWP = parentFinder->find(id, success);
|
||||
if (!success) {
|
||||
return "unknown";
|
||||
}
|
||||
SpatiallyNestablePointer object = objectWP.lock();
|
||||
if (!object) {
|
||||
return "unknown";
|
||||
}
|
||||
NestableType nestableType = object->getNestableType();
|
||||
return SpatiallyNestable::nestableTypeToString(nestableType);
|
||||
}
|
||||
|
||||
QVector<QUuid> EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex) {
|
||||
QVector<QUuid> result;
|
||||
if (!_entityTree) {
|
||||
|
|
|
@ -304,6 +304,8 @@ public slots:
|
|||
Q_INVOKABLE QVector<QUuid> getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex);
|
||||
Q_INVOKABLE bool isChildOfParent(QUuid childID, QUuid parentID);
|
||||
|
||||
Q_INVOKABLE QString getNestableType(QUuid id);
|
||||
|
||||
Q_INVOKABLE QUuid getKeyboardFocusEntity() const;
|
||||
Q_INVOKABLE void setKeyboardFocusEntity(QUuid id);
|
||||
|
||||
|
|
|
@ -990,6 +990,17 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
entityItemID, properties);
|
||||
endDecode = usecTimestampNow();
|
||||
|
||||
EntityItemPointer existingEntity;
|
||||
if (!isAdd) {
|
||||
// search for the entity by EntityItemID
|
||||
startLookup = usecTimestampNow();
|
||||
existingEntity = findEntityByEntityItemID(entityItemID);
|
||||
endLookup = usecTimestampNow();
|
||||
if (!existingEntity) {
|
||||
// this is not an add-entity operation, and we don't know about the identified entity.
|
||||
validEditPacket = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) {
|
||||
bool passedWhiteList = false;
|
||||
|
@ -1036,12 +1047,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
|||
// If we got a valid edit packet, then it could be a new entity or it could be an update to
|
||||
// an existing entity... handle appropriately
|
||||
if (validEditPacket) {
|
||||
|
||||
// search for the entity by EntityItemID
|
||||
startLookup = usecTimestampNow();
|
||||
EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID);
|
||||
endLookup = usecTimestampNow();
|
||||
|
||||
startFilter = usecTimestampNow();
|
||||
bool wasChanged = false;
|
||||
// Having (un)lock rights bypasses the filter, unless it's a physics result.
|
||||
|
|
|
@ -149,6 +149,10 @@ void GLBackend::resetUniformStage() {
|
|||
|
||||
void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) {
|
||||
GLuint slot = batch._params[paramOffset + 3]._uint;
|
||||
if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) {
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers();
|
||||
return;
|
||||
}
|
||||
BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
|
||||
GLintptr rangeStart = batch._params[paramOffset + 1]._uint;
|
||||
GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint;
|
||||
|
@ -203,7 +207,7 @@ void GLBackend::resetResourceStage() {
|
|||
void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) {
|
||||
GLuint slot = batch._params[paramOffset + 1]._uint;
|
||||
if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) {
|
||||
// "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" + slot + " which doesn't exist. MaxNumResourceBuffers = " + getMaxNumResourceBuffers());
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -233,7 +237,7 @@ void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) {
|
|||
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());
|
||||
qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ namespace ktx {
|
|||
}
|
||||
|
||||
std::unique_ptr<KTX> KTX::create(const StoragePointer& src) {
|
||||
if (!src) {
|
||||
if (!src || !(*src)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -421,7 +421,7 @@ void NetworkTexture::startRequestForNextMipLevel() {
|
|||
|
||||
_ktxResourceState = PENDING_MIP_REQUEST;
|
||||
|
||||
init();
|
||||
init(false);
|
||||
float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip;
|
||||
setLoadPriority(this, priority);
|
||||
_url.setFragment(QString::number(_lowestKnownPopulatedMip - 1));
|
||||
|
@ -472,6 +472,10 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
|
|||
void NetworkTexture::ktxHeaderRequestFinished() {
|
||||
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA);
|
||||
|
||||
if (!_ktxHeaderRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
_ktxHeaderRequestFinished = true;
|
||||
maybeHandleFinishedInitialLoad();
|
||||
}
|
||||
|
@ -479,6 +483,10 @@ void NetworkTexture::ktxHeaderRequestFinished() {
|
|||
void NetworkTexture::ktxMipRequestFinished() {
|
||||
Q_ASSERT(_ktxResourceState == LOADING_INITIAL_DATA || _ktxResourceState == REQUESTING_MIP);
|
||||
|
||||
if (!_ktxMipRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_ktxResourceState == LOADING_INITIAL_DATA) {
|
||||
_ktxHighMipRequestFinished = true;
|
||||
maybeHandleFinishedInitialLoad();
|
||||
|
@ -682,6 +690,27 @@ void NetworkTexture::loadContent(const QByteArray& content) {
|
|||
QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _maxNumPixels));
|
||||
}
|
||||
|
||||
void NetworkTexture::refresh() {
|
||||
if ((_ktxHeaderRequest || _ktxMipRequest) && !_loaded && !_failedToLoad) {
|
||||
return;
|
||||
}
|
||||
if (_ktxHeaderRequest || _ktxMipRequest) {
|
||||
if (_ktxHeaderRequest) {
|
||||
_ktxHeaderRequest->disconnect(this);
|
||||
_ktxHeaderRequest->deleteLater();
|
||||
_ktxHeaderRequest = nullptr;
|
||||
}
|
||||
if (_ktxMipRequest) {
|
||||
_ktxMipRequest->disconnect(this);
|
||||
_ktxMipRequest->deleteLater();
|
||||
_ktxMipRequest = nullptr;
|
||||
}
|
||||
TextureCache::requestCompleted(_self);
|
||||
}
|
||||
|
||||
Resource::refresh();
|
||||
}
|
||||
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& resource, const QUrl& url, const QByteArray& data, int maxNumPixels) :
|
||||
_resource(resource),
|
||||
_url(url),
|
||||
|
|
|
@ -58,6 +58,8 @@ public:
|
|||
|
||||
gpu::TexturePointer getFallbackTexture() const;
|
||||
|
||||
void refresh() override;
|
||||
|
||||
signals:
|
||||
void networkTextureCreated(const QWeakPointer<NetworkTexture>& self);
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
#include <DependencyManager.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <comdef.h>
|
||||
#include <Wbemidl.h>
|
||||
#include <Windows.h>
|
||||
#include <winreg.h>
|
||||
#endif //Q_OS_WIN
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
|
@ -30,6 +30,9 @@
|
|||
#endif //Q_OS_MAC
|
||||
|
||||
static const QString FALLBACK_FINGERPRINT_KEY = "fallbackFingerprint";
|
||||
|
||||
QUuid FingerprintUtils::_machineFingerprint { QUuid() };
|
||||
|
||||
QString FingerprintUtils::getMachineFingerprintString() {
|
||||
QString uuidString;
|
||||
#ifdef Q_OS_LINUX
|
||||
|
@ -47,122 +50,32 @@ QString FingerprintUtils::getMachineFingerprintString() {
|
|||
#endif //Q_OS_MAC
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
HRESULT hres;
|
||||
IWbemLocator *pLoc = NULL;
|
||||
|
||||
// initialize com. Interface already does, but other
|
||||
// users of this lib don't necessarily do so.
|
||||
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
if (FAILED(hres)) {
|
||||
qCDebug(networking) << "Failed to initialize COM library!";
|
||||
return uuidString;
|
||||
}
|
||||
HKEY cryptoKey;
|
||||
|
||||
// initialize WbemLocator
|
||||
hres = CoCreateInstance(
|
||||
CLSID_WbemLocator,
|
||||
0,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IWbemLocator, (LPVOID *) &pLoc);
|
||||
// try and open the key that contains the machine GUID
|
||||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ, &cryptoKey) == ERROR_SUCCESS) {
|
||||
DWORD type;
|
||||
DWORD guidSize;
|
||||
|
||||
if (FAILED(hres)) {
|
||||
qCDebug(networking) << "Failed to initialize WbemLocator";
|
||||
return uuidString;
|
||||
}
|
||||
|
||||
// Connect to WMI through the IWbemLocator::ConnectServer method
|
||||
IWbemServices *pSvc = NULL;
|
||||
const char* MACHINE_GUID_KEY = "MachineGuid";
|
||||
|
||||
// Connect to the root\cimv2 namespace with
|
||||
// the current user and obtain pointer pSvc
|
||||
// to make IWbemServices calls.
|
||||
hres = pLoc->ConnectServer(
|
||||
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
|
||||
NULL, // User name. NULL = current user
|
||||
NULL, // User password. NULL = current
|
||||
0, // Locale. NULL indicates current
|
||||
NULL, // Security flags.
|
||||
0, // Authority (for example, Kerberos)
|
||||
0, // Context object
|
||||
&pSvc // pointer to IWbemServices proxy
|
||||
);
|
||||
// try and retrieve the size of the GUID value
|
||||
if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, &type, NULL, &guidSize) == ERROR_SUCCESS) {
|
||||
// make sure that the value is a string
|
||||
if (type == REG_SZ) {
|
||||
// retrieve the machine GUID and return that as our UUID string
|
||||
std::string machineGUID(guidSize / sizeof(char), '\0');
|
||||
|
||||
if (FAILED(hres)) {
|
||||
pLoc->Release();
|
||||
qCDebug(networking) << "Failed to connect to WMI";
|
||||
return uuidString;
|
||||
}
|
||||
|
||||
// Set security levels on the proxy
|
||||
hres = CoSetProxyBlanket(
|
||||
pSvc, // Indicates the proxy to set
|
||||
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
|
||||
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
|
||||
NULL, // Server principal name
|
||||
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
|
||||
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
|
||||
NULL, // client identity
|
||||
EOAC_NONE // proxy capabilities
|
||||
);
|
||||
|
||||
if (FAILED(hres)) {
|
||||
pSvc->Release();
|
||||
pLoc->Release();
|
||||
qCDebug(networking) << "Failed to set security on proxy blanket";
|
||||
return uuidString;
|
||||
}
|
||||
|
||||
// Use the IWbemServices pointer to grab the Win32_BIOS stuff
|
||||
IEnumWbemClassObject* pEnumerator = NULL;
|
||||
hres = pSvc->ExecQuery(
|
||||
bstr_t("WQL"),
|
||||
bstr_t("SELECT * FROM Win32_ComputerSystemProduct"),
|
||||
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
|
||||
NULL,
|
||||
&pEnumerator);
|
||||
|
||||
if (FAILED(hres)) {
|
||||
pSvc->Release();
|
||||
pLoc->Release();
|
||||
qCDebug(networking) << "query to get Win32_ComputerSystemProduct info";
|
||||
return uuidString;
|
||||
}
|
||||
|
||||
// Get the SerialNumber from the Win32_BIOS data
|
||||
IWbemClassObject *pclsObj;
|
||||
ULONG uReturn = 0;
|
||||
|
||||
SHORT sRetStatus = -100;
|
||||
|
||||
while (pEnumerator) {
|
||||
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
|
||||
|
||||
if(0 == uReturn){
|
||||
break;
|
||||
}
|
||||
|
||||
VARIANT vtProp;
|
||||
|
||||
// Get the value of the Name property
|
||||
hr = pclsObj->Get(L"UUID", 0, &vtProp, 0, 0);
|
||||
if (!FAILED(hres)) {
|
||||
switch (vtProp.vt) {
|
||||
case VT_BSTR:
|
||||
uuidString = QString::fromWCharArray(vtProp.bstrVal);
|
||||
break;
|
||||
if (RegQueryValueEx(cryptoKey, MACHINE_GUID_KEY, NULL, NULL,
|
||||
reinterpret_cast<LPBYTE>(&machineGUID[0]), &guidSize) == ERROR_SUCCESS) {
|
||||
uuidString = QString::fromStdString(machineGUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
VariantClear(&vtProp);
|
||||
|
||||
pclsObj->Release();
|
||||
RegCloseKey(cryptoKey);
|
||||
}
|
||||
pEnumerator->Release();
|
||||
|
||||
// Cleanup
|
||||
pSvc->Release();
|
||||
pLoc->Release();
|
||||
|
||||
qCDebug(networking) << "Windows BIOS UUID: " << uuidString;
|
||||
#endif //Q_OS_WIN
|
||||
|
||||
return uuidString;
|
||||
|
@ -171,29 +84,36 @@ QString FingerprintUtils::getMachineFingerprintString() {
|
|||
|
||||
QUuid FingerprintUtils::getMachineFingerprint() {
|
||||
|
||||
QString uuidString = getMachineFingerprintString();
|
||||
if (_machineFingerprint.isNull()) {
|
||||
QString uuidString = getMachineFingerprintString();
|
||||
|
||||
// now, turn into uuid. A malformed string will
|
||||
// return QUuid() ("{00000...}"), which handles
|
||||
// any errors in getting the string
|
||||
QUuid uuid(uuidString);
|
||||
|
||||
// now, turn into uuid. A malformed string will
|
||||
// return QUuid() ("{00000...}"), which handles
|
||||
// any errors in getting the string
|
||||
QUuid uuid(uuidString);
|
||||
if (uuid == QUuid()) {
|
||||
// if you cannot read a fallback key cuz we aren't saving them, just generate one for
|
||||
// this session and move on
|
||||
if (DependencyManager::get<Setting::Manager>().isNull()) {
|
||||
return QUuid::createUuid();
|
||||
}
|
||||
// read fallback key (if any)
|
||||
Settings settings;
|
||||
uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString());
|
||||
qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString();
|
||||
if (uuid == QUuid()) {
|
||||
// no fallback yet, set one
|
||||
uuid = QUuid::createUuid();
|
||||
settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString());
|
||||
qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString();
|
||||
// if you cannot read a fallback key cuz we aren't saving them, just generate one for
|
||||
// this session and move on
|
||||
if (DependencyManager::get<Setting::Manager>().isNull()) {
|
||||
return QUuid::createUuid();
|
||||
}
|
||||
// read fallback key (if any)
|
||||
Settings settings;
|
||||
uuid = QUuid(settings.value(FALLBACK_FINGERPRINT_KEY).toString());
|
||||
qCDebug(networking) << "read fallback maching fingerprint: " << uuid.toString();
|
||||
|
||||
if (uuid == QUuid()) {
|
||||
// no fallback yet, set one
|
||||
uuid = QUuid::createUuid();
|
||||
settings.setValue(FALLBACK_FINGERPRINT_KEY, uuid.toString());
|
||||
qCDebug(networking) << "no fallback machine fingerprint, setting it to: " << uuid.toString();
|
||||
}
|
||||
}
|
||||
|
||||
_machineFingerprint = uuid;
|
||||
}
|
||||
return uuid;
|
||||
|
||||
return _machineFingerprint;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
|
||||
private:
|
||||
static QString getMachineFingerprintString();
|
||||
static QUuid _machineFingerprint;
|
||||
};
|
||||
|
||||
#endif // hifi_FingerprintUtils_h
|
||||
|
|
|
@ -533,13 +533,13 @@ void Resource::ensureLoading() {
|
|||
}
|
||||
|
||||
void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) {
|
||||
if (!(_failedToLoad || _loaded)) {
|
||||
if (!(_failedToLoad)) {
|
||||
_loadPriorities.insert(owner, priority);
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) {
|
||||
if (_failedToLoad || _loaded) {
|
||||
if (_failedToLoad) {
|
||||
return;
|
||||
}
|
||||
for (QHash<QPointer<QObject>, float>::const_iterator it = priorities.constBegin();
|
||||
|
@ -549,7 +549,7 @@ void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& prioriti
|
|||
}
|
||||
|
||||
void Resource::clearLoadPriority(const QPointer<QObject>& owner) {
|
||||
if (!(_failedToLoad || _loaded)) {
|
||||
if (!(_failedToLoad)) {
|
||||
_loadPriorities.remove(owner);
|
||||
}
|
||||
}
|
||||
|
@ -612,10 +612,12 @@ void Resource::allReferencesCleared() {
|
|||
}
|
||||
}
|
||||
|
||||
void Resource::init() {
|
||||
void Resource::init(bool resetLoaded) {
|
||||
_startedLoading = false;
|
||||
_failedToLoad = false;
|
||||
_loaded = false;
|
||||
if (resetLoaded) {
|
||||
_loaded = false;
|
||||
}
|
||||
_attempts = 0;
|
||||
_activeUrl = _url;
|
||||
|
||||
|
|
|
@ -385,7 +385,7 @@ public:
|
|||
float getProgress() const { return (_bytesTotal <= 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; }
|
||||
|
||||
/// Refreshes the resource.
|
||||
void refresh();
|
||||
virtual void refresh();
|
||||
|
||||
void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
|
||||
|
||||
|
@ -425,7 +425,7 @@ protected slots:
|
|||
void attemptRequest();
|
||||
|
||||
protected:
|
||||
virtual void init();
|
||||
virtual void init(bool resetLoaded = true);
|
||||
|
||||
/// Called by ResourceCache to begin loading this Resource.
|
||||
/// This method can be overriden to provide custom request functionality. If this is done,
|
||||
|
@ -454,9 +454,14 @@ protected:
|
|||
QUrl _url;
|
||||
QUrl _activeUrl;
|
||||
ByteRange _requestByteRange;
|
||||
|
||||
// _loaded == true means we are in a loaded and usable state. It is possible that there may still be
|
||||
// active requests/loading while in this state. Example: Progressive KTX downloads, where higher resolution
|
||||
// mips are being download.
|
||||
bool _startedLoading = false;
|
||||
bool _failedToLoad = false;
|
||||
bool _loaded = false;
|
||||
|
||||
QHash<QPointer<QObject>, float> _loadPriorities;
|
||||
QWeakPointer<Resource> _self;
|
||||
QPointer<ResourceCache> _cache;
|
||||
|
|
|
@ -28,10 +28,10 @@ public:
|
|||
const std::unordered_set<int>& getCauterizeBoneSet() const { return _cauterizeBoneSet; }
|
||||
void setCauterizeBoneSet(const std::unordered_set<int>& boneSet) { _cauterizeBoneSet = boneSet; }
|
||||
|
||||
void deleteGeometry() override;
|
||||
bool updateGeometry() override;
|
||||
void deleteGeometry() override;
|
||||
bool updateGeometry() override;
|
||||
|
||||
void createVisibleRenderItemSet() override;
|
||||
void createVisibleRenderItemSet() override;
|
||||
void createCollisionRenderItemSet() override;
|
||||
|
||||
virtual void updateClusterMatrices() override;
|
||||
|
@ -41,7 +41,7 @@ public:
|
|||
|
||||
protected:
|
||||
std::unordered_set<int> _cauterizeBoneSet;
|
||||
QVector<Model::MeshState> _cauterizeMeshStates;
|
||||
QVector<Model::MeshState> _cauterizeMeshStates;
|
||||
bool _isCauterized { false };
|
||||
bool _enableCauterization { false };
|
||||
};
|
||||
|
|
|
@ -573,7 +573,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
|
|||
|
||||
bool somethingAdded = false;
|
||||
if (_collisionGeometry) {
|
||||
if (_collisionRenderItems.empty()) {
|
||||
if (_collisionRenderItemsMap.empty()) {
|
||||
foreach (auto renderItem, _collisionRenderItems) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
|
@ -583,7 +583,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
|
|||
transaction.resetItem(item, renderPayload);
|
||||
_collisionRenderItemsMap.insert(item, renderPayload);
|
||||
}
|
||||
somethingAdded = !_collisionRenderItems.empty();
|
||||
somethingAdded = !_collisionRenderItemsMap.empty();
|
||||
}
|
||||
} else {
|
||||
if (_modelMeshRenderItemsMap.empty()) {
|
||||
|
@ -632,7 +632,7 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti
|
|||
transaction.removeItem(item);
|
||||
}
|
||||
_collisionRenderItems.clear();
|
||||
_collisionRenderItems.clear();
|
||||
_collisionRenderItemsMap.clear();
|
||||
_addedToScene = false;
|
||||
|
||||
_renderInfoVertexCount = 0;
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <GLMHelpers.h>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Mat4.h"
|
||||
|
||||
glm::mat4 Mat4::multiply(const glm::mat4& m1, const glm::mat4& m2) const {
|
||||
|
@ -66,10 +68,12 @@ glm::vec3 Mat4::getUp(const glm::mat4& m) const {
|
|||
return glm::vec3(m[0][1], m[1][1], m[2][1]);
|
||||
}
|
||||
|
||||
void Mat4::print(const QString& label, const glm::mat4& m) const {
|
||||
qCDebug(scriptengine) << qPrintable(label) <<
|
||||
"row0 =" << m[0][0] << "," << m[1][0] << "," << m[2][0] << "," << m[3][0] <<
|
||||
"row1 =" << m[0][1] << "," << m[1][1] << "," << m[2][1] << "," << m[3][1] <<
|
||||
"row2 =" << m[0][2] << "," << m[1][2] << "," << m[2][2] << "," << m[3][2] <<
|
||||
"row3 =" << m[0][3] << "," << m[1][3] << "," << m[2][3] << "," << m[3][3];
|
||||
void Mat4::print(const QString& label, const glm::mat4& m, bool transpose) const {
|
||||
glm::dmat4 out = transpose ? glm::transpose(m) : m;
|
||||
QString message = QString("%1 %2").arg(qPrintable(label));
|
||||
message = message.arg(glm::to_string(out).c_str());
|
||||
qCDebug(scriptengine) << message;
|
||||
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
|
||||
scriptEngine->print(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
/// Scriptable Mat4 object. Used exclusively in the JavaScript API
|
||||
class Mat4 : public QObject {
|
||||
class Mat4 : public QObject, protected QScriptable {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
|
@ -43,7 +44,7 @@ public slots:
|
|||
glm::vec3 getRight(const glm::mat4& m) const;
|
||||
glm::vec3 getUp(const glm::mat4& m) const;
|
||||
|
||||
void print(const QString& label, const glm::mat4& m) const;
|
||||
void print(const QString& label, const glm::mat4& m, bool transpose = false) const;
|
||||
};
|
||||
|
||||
#endif // hifi_Mat4_h
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
|
||||
#include <OctreeConstants.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Quat.h"
|
||||
|
||||
quat Quat::normalize(const glm::quat& q) {
|
||||
|
@ -114,8 +116,17 @@ float Quat::dot(const glm::quat& q1, const glm::quat& q2) {
|
|||
return glm::dot(q1, q2);
|
||||
}
|
||||
|
||||
void Quat::print(const QString& label, const glm::quat& q) {
|
||||
qCDebug(scriptengine) << qPrintable(label) << q.x << "," << q.y << "," << q.z << "," << q.w;
|
||||
void Quat::print(const QString& label, const glm::quat& q, bool asDegrees) {
|
||||
QString message = QString("%1 %2").arg(qPrintable(label));
|
||||
if (asDegrees) {
|
||||
message = message.arg(glm::to_string(glm::dvec3(safeEulerAngles(q))).c_str());
|
||||
} else {
|
||||
message = message.arg(glm::to_string(glm::dquat(q)).c_str());
|
||||
}
|
||||
qCDebug(scriptengine) << message;
|
||||
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
|
||||
scriptEngine->print(message);
|
||||
}
|
||||
}
|
||||
|
||||
bool Quat::equal(const glm::quat& q1, const glm::quat& q2) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
/**jsdoc
|
||||
* A Quaternion
|
||||
|
@ -30,7 +31,7 @@
|
|||
*/
|
||||
|
||||
/// Scriptable interface a Quaternion helper class object. Used exclusively in the JavaScript API
|
||||
class Quat : public QObject {
|
||||
class Quat : public QObject, protected QScriptable {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
|
@ -58,7 +59,7 @@ public slots:
|
|||
glm::quat slerp(const glm::quat& q1, const glm::quat& q2, float alpha);
|
||||
glm::quat squad(const glm::quat& q1, const glm::quat& q2, const glm::quat& s1, const glm::quat& s2, float h);
|
||||
float dot(const glm::quat& q1, const glm::quat& q2);
|
||||
void print(const QString& label, const glm::quat& q);
|
||||
void print(const QString& label, const glm::quat& q, bool asDegrees = false);
|
||||
bool equal(const glm::quat& q1, const glm::quat& q2);
|
||||
glm::quat cancelOutRollAndPitch(const glm::quat& q);
|
||||
glm::quat cancelOutRoll(const glm::quat& q);
|
||||
|
|
|
@ -105,11 +105,11 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
|
|||
}
|
||||
message += context->argument(i).toString();
|
||||
}
|
||||
qCDebug(scriptengineScript).noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline
|
||||
qCDebug(scriptengineScript).noquote() << message; // noquote() so that \n is treated as newline
|
||||
|
||||
// FIXME - this approach neeeds revisiting. print() comes here, which ends up calling Script.print?
|
||||
engine->globalObject().property("Script").property("print")
|
||||
.call(engine->nullValue(), QScriptValueList({ message }));
|
||||
if (ScriptEngine *scriptEngine = qobject_cast<ScriptEngine*>(engine)) {
|
||||
scriptEngine->print(message);
|
||||
}
|
||||
|
||||
return QScriptValue();
|
||||
}
|
||||
|
@ -472,6 +472,11 @@ void ScriptEngine::scriptInfoMessage(const QString& message) {
|
|||
emit infoMessage(message, getFilename());
|
||||
}
|
||||
|
||||
void ScriptEngine::scriptPrintedMessage(const QString& message) {
|
||||
qCDebug(scriptengine) << message;
|
||||
emit printedMessage(message, getFilename());
|
||||
}
|
||||
|
||||
// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
|
||||
// callAnimationStateHandler requires that the type be registered.
|
||||
// These two are meaningful, if we ever do want to use them...
|
||||
|
|
|
@ -221,6 +221,7 @@ public:
|
|||
void scriptErrorMessage(const QString& message);
|
||||
void scriptWarningMessage(const QString& message);
|
||||
void scriptInfoMessage(const QString& message);
|
||||
void scriptPrintedMessage(const QString& message);
|
||||
|
||||
int getNumRunningEntityScripts() const;
|
||||
bool getEntityScriptDetails(const EntityItemID& entityID, EntityScriptDetails &details) const;
|
||||
|
|
|
@ -453,7 +453,8 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
|
|||
(scriptFilename.scheme() != "http" &&
|
||||
scriptFilename.scheme() != "https" &&
|
||||
scriptFilename.scheme() != "atp" &&
|
||||
scriptFilename.scheme() != "file")) {
|
||||
scriptFilename.scheme() != "file" &&
|
||||
scriptFilename.scheme() != "about")) {
|
||||
// deal with a "url" like c:/something
|
||||
scriptUrl = normalizeScriptURL(QUrl::fromLocalFile(scriptFilename.toString()));
|
||||
} else {
|
||||
|
@ -472,7 +473,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
|
|||
}, Qt::QueuedConnection);
|
||||
|
||||
|
||||
if (scriptFilename.isEmpty()) {
|
||||
if (scriptFilename.isEmpty() || !scriptUrl.isValid()) {
|
||||
launchScriptEngine(scriptEngine);
|
||||
} else {
|
||||
// connect to the appropriate signals of this script engine
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "ScriptUUID.h"
|
||||
|
||||
QUuid ScriptUUID::fromString(const QString& s) {
|
||||
|
@ -36,6 +37,11 @@ bool ScriptUUID::isNull(const QUuid& id) {
|
|||
return id.isNull();
|
||||
}
|
||||
|
||||
void ScriptUUID::print(const QString& lable, const QUuid& id) {
|
||||
qCDebug(scriptengine) << qPrintable(lable) << id.toString();
|
||||
void ScriptUUID::print(const QString& label, const QUuid& id) {
|
||||
QString message = QString("%1 %2").arg(qPrintable(label));
|
||||
message = message.arg(id.toString());
|
||||
qCDebug(scriptengine) << message;
|
||||
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
|
||||
scriptEngine->print(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,10 @@
|
|||
#define hifi_ScriptUUID_h
|
||||
|
||||
#include <QUuid>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
/// Scriptable interface for a UUID helper class object. Used exclusively in the JavaScript API
|
||||
class ScriptUUID : public QObject {
|
||||
class ScriptUUID : public QObject, protected QScriptable {
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
|
@ -26,7 +27,7 @@ public slots:
|
|||
QUuid generate();
|
||||
bool isEqual(const QUuid& idA, const QUuid& idB);
|
||||
bool isNull(const QUuid& id);
|
||||
void print(const QString& lable, const QUuid& id);
|
||||
void print(const QString& label, const QUuid& id);
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptUUID_h
|
||||
|
|
|
@ -14,20 +14,26 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "NumericalConstants.h"
|
||||
#include "Vec3.h"
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
float Vec3::orientedAngle(const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& v3) {
|
||||
float radians = glm::orientedAngle(glm::normalize(v1), glm::normalize(v2), glm::normalize(v3));
|
||||
return glm::degrees(radians);
|
||||
}
|
||||
|
||||
|
||||
void Vec3::print(const QString& lable, const glm::vec3& v) {
|
||||
qCDebug(scriptengine) << qPrintable(lable) << v.x << "," << v.y << "," << v.z;
|
||||
void Vec3::print(const QString& label, const glm::vec3& v) {
|
||||
QString message = QString("%1 %2").arg(qPrintable(label));
|
||||
message = message.arg(glm::to_string(glm::dvec3(v)).c_str());
|
||||
qCDebug(scriptengine) << message;
|
||||
if (ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(engine())) {
|
||||
scriptEngine->print(message);
|
||||
}
|
||||
}
|
||||
|
||||
bool Vec3::withinEpsilon(const glm::vec3& v1, const glm::vec3& v2, float epsilon) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
|
@ -48,7 +49,7 @@
|
|||
*/
|
||||
|
||||
/// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API
|
||||
class Vec3 : public QObject {
|
||||
class Vec3 : public QObject, protected QScriptable {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT)
|
||||
Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT)
|
||||
|
|
|
@ -1138,3 +1138,17 @@ SpatiallyNestablePointer SpatiallyNestable::findByID(QUuid id, bool& success) {
|
|||
}
|
||||
return parentWP.lock();
|
||||
}
|
||||
|
||||
|
||||
QString SpatiallyNestable::nestableTypeToString(NestableType nestableType) {
|
||||
switch(nestableType) {
|
||||
case NestableType::Entity:
|
||||
return "entity";
|
||||
case NestableType::Avatar:
|
||||
return "avatar";
|
||||
case NestableType::Overlay:
|
||||
return "overlay";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ public:
|
|||
virtual const QUuid getID() const;
|
||||
virtual void setID(const QUuid& id);
|
||||
|
||||
virtual QString getName() const { return "SpatiallyNestable"; }
|
||||
|
||||
virtual const QUuid getParentID() const;
|
||||
virtual void setParentID(const QUuid& parentID);
|
||||
|
||||
|
@ -62,6 +64,8 @@ public:
|
|||
static glm::vec3 localToWorldAngularVelocity(const glm::vec3& angularVelocity,
|
||||
const QUuid& parentID, int parentJointIndex, bool& success);
|
||||
|
||||
static QString nestableTypeToString(NestableType nestableType);
|
||||
|
||||
// world frame
|
||||
virtual const Transform getTransform(bool& success, int depth = 0) const;
|
||||
virtual const Transform getTransform() const;
|
||||
|
|
|
@ -20,9 +20,10 @@
|
|||
print('<span style="color:red">Tests completed with ' +
|
||||
errorCount + ' ' + ERROR + '.<span>');
|
||||
}
|
||||
if (pending.length)
|
||||
if (pending.length) {
|
||||
print ('<span style="color:darkorange">disabled: <br /> '+
|
||||
pending.join('<br /> ')+'</span>');
|
||||
}
|
||||
print('Tests completed in ' + (endTime - startTime) + 'ms.');
|
||||
};
|
||||
this.suiteStarted = function(obj) {
|
||||
|
|
39
scripts/developer/tests/printTest.js
Normal file
39
scripts/developer/tests/printTest.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* eslint-env jasmine */
|
||||
|
||||
// this test generates sample print, Script.print, etc. output
|
||||
|
||||
main();
|
||||
|
||||
function main() {
|
||||
// to match with historical behavior, Script.print(message) output only triggers
|
||||
// the printedMessage signal (and therefore doesn't show up in the application log)
|
||||
Script.print('[Script.print] hello world');
|
||||
|
||||
// the rest of these should show up in both the application log and signaled print handlers
|
||||
print('[print]', 'hello', 'world');
|
||||
|
||||
// note: these trigger the equivalent of an emit
|
||||
Script.printedMessage('[Script.printedMessage] hello world', '{filename}');
|
||||
Script.infoMessage('[Script.infoMessage] hello world', '{filename}');
|
||||
Script.warningMessage('[Script.warningMessage] hello world', '{filename}');
|
||||
Script.errorMessage('[Script.errorMessage] hello world', '{filename}');
|
||||
|
||||
{
|
||||
Vec3.print('[Vec3.print]', Vec3.HALF);
|
||||
|
||||
var q = Quat.fromPitchYawRollDegrees(45, 45, 45);
|
||||
Quat.print('[Quat.print]', q);
|
||||
Quat.print('[Quat.print (euler)]', q, true);
|
||||
|
||||
function vec4(x,y,z,w) {
|
||||
return { x: x, y: y, z: z, w: w };
|
||||
}
|
||||
var m = Mat4.createFromColumns(
|
||||
vec4(1,2,3,4), vec4(5,6,7,8), vec4(9,10,11,12), vec4(13,14,15,16)
|
||||
);
|
||||
Mat4.print('[Mat4.print (col major)]', m);
|
||||
Mat4.print('[Mat4.print (row major)]', m, true);
|
||||
|
||||
Uuid.print('[Uuid.print]', Uuid.fromString(Uuid.toString(0)));
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
/* eslint-env jasmine */
|
||||
|
||||
// Art3mis
|
||||
// eslint-disable-next-line max-len
|
||||
var DEFAULT_AVATAR_URL = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/e76946cc-c272-4adf-9bb6-02cde0a4b57d/8fd984ea6fe1495147a3303f87fa6e23.fst?1460131758";
|
||||
|
||||
var ORIGIN = {x: 0, y: 0, z: 0};
|
||||
|
@ -8,6 +10,15 @@ var ROT_IDENT = {x: 0, y: 0, z: 0, w: 1};
|
|||
|
||||
describe("MyAvatar", function () {
|
||||
|
||||
// backup/restore current skeletonModelURL
|
||||
beforeAll(function() {
|
||||
this.oldURL = MyAvatar.skeletonModelURL;
|
||||
});
|
||||
|
||||
afterAll(function() {
|
||||
MyAvatar.skeletonModelURL = this.oldURL;
|
||||
});
|
||||
|
||||
// reload the avatar from scratch before each test.
|
||||
beforeEach(function (done) {
|
||||
MyAvatar.skeletonModelURL = DEFAULT_AVATAR_URL;
|
||||
|
@ -20,12 +31,12 @@ describe("MyAvatar", function () {
|
|||
MyAvatar.position = ORIGIN;
|
||||
MyAvatar.orientation = ROT_IDENT;
|
||||
// give the avatar 1/2 a second to settle on the ground in the idle pose.
|
||||
Script.setTimeout(function () {
|
||||
Script.setTimeout(function () {
|
||||
done();
|
||||
}, 500);
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
}, 10000 /* timeout -- allow time to download avatar*/);
|
||||
|
||||
// makes the assumption that there is solid ground somewhat underneath the avatar.
|
||||
it("position and orientation getters", function () {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-env jasmine */
|
||||
|
||||
Script.include('../../../system/libraries/utils.js');
|
||||
|
||||
describe('Bind', function() {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-env jasmine */
|
||||
|
||||
describe('Entity', function() {
|
||||
var center = Vec3.sum(
|
||||
MyAvatar.position,
|
||||
|
@ -19,6 +21,14 @@ describe('Entity', function() {
|
|||
},
|
||||
};
|
||||
|
||||
it('serversExist', function() {
|
||||
expect(Entities.serversExist()).toBe(true);
|
||||
});
|
||||
|
||||
it('canRezTmp', function() {
|
||||
expect(Entities.canRezTmp()).toBe(true);
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
boxEntity = Entities.addEntity(boxProps);
|
||||
});
|
||||
|
@ -62,4 +72,4 @@ describe('Entity', function() {
|
|||
props = Entities.getEntityProperties(boxEntity);
|
||||
expect(props.lastEdited).toBeGreaterThan(prevLastEdited);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,13 +1,30 @@
|
|||
/* eslint-env jasmine */
|
||||
|
||||
// Include testing library
|
||||
Script.include('../../libraries/jasmine/jasmine.js');
|
||||
Script.include('../../libraries/jasmine/hifi-boot.js')
|
||||
Script.include('../../libraries/jasmine/hifi-boot.js');
|
||||
|
||||
// Include unit tests
|
||||
// FIXME: Figure out why jasmine done() is not working.
|
||||
// Script.include('avatarUnitTests.js');
|
||||
Script.include('avatarUnitTests.js');
|
||||
Script.include('bindUnitTest.js');
|
||||
Script.include('entityUnitTests.js');
|
||||
|
||||
describe("jasmine internal tests", function() {
|
||||
it('should support async .done()', function(done) {
|
||||
var start = new Date;
|
||||
Script.setTimeout(function() {
|
||||
expect((new Date - start)/1000).toBeCloseTo(0.5, 1);
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
// jasmine pending test
|
||||
xit('disabled test', function() {
|
||||
expect(false).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// invoke Script.stop (after any async tests complete)
|
||||
jasmine.getEnv().addReporter({ jasmineDone: Script.stop });
|
||||
|
||||
// Run the tests
|
||||
jasmine.getEnv().execute();
|
||||
Script.stop();
|
||||
|
|
|
@ -3881,6 +3881,7 @@ function MyController(hand) {
|
|||
// we appear to be holding something and this script isn't in a state that would be holding something.
|
||||
// unhook it. if we previously took note of this entity's parent, put it back where it was. This
|
||||
// works around some problems that happen when more than one hand or avatar is passing something around.
|
||||
var childType = Entities.getNestableType(childID);
|
||||
if (_this.previousParentID[childID]) {
|
||||
var previousParentID = _this.previousParentID[childID];
|
||||
var previousParentJointIndex = _this.previousParentJointIndex[childID];
|
||||
|
@ -3898,7 +3899,7 @@ function MyController(hand) {
|
|||
}
|
||||
_this.previouslyUnhooked[childID] = now;
|
||||
|
||||
if (Overlays.getProperty(childID, "grabbable")) {
|
||||
if (childType == "overlay" && Overlays.getProperty(childID, "grabbable")) {
|
||||
// only auto-unhook overlays that were flagged as grabbable. this avoids unhooking overlays
|
||||
// used in tutorial.
|
||||
Overlays.editOverlay(childID, {
|
||||
|
@ -3906,12 +3907,20 @@ function MyController(hand) {
|
|||
parentJointIndex: previousParentJointIndex
|
||||
});
|
||||
}
|
||||
Entities.editEntity(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex });
|
||||
if (childType == "entity") {
|
||||
Entities.editEntity(childID, {
|
||||
parentID: previousParentID,
|
||||
parentJointIndex: previousParentJointIndex
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
Entities.editEntity(childID, { parentID: NULL_UUID });
|
||||
if (Overlays.getProperty(childID, "grabbable")) {
|
||||
Overlays.editOverlay(childID, { parentID: NULL_UUID });
|
||||
if (childType == "entity") {
|
||||
Entities.editEntity(childID, { parentID: NULL_UUID });
|
||||
} else if (childType == "overlay") {
|
||||
if (Overlays.getProperty(childID, "grabbable")) {
|
||||
Overlays.editOverlay(childID, { parentID: NULL_UUID });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,7 +275,8 @@ WebTablet.prototype.getLocation = function() {
|
|||
};
|
||||
|
||||
WebTablet.prototype.setHomeButtonTexture = function() {
|
||||
Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})});
|
||||
// TODO - is this still needed?
|
||||
// Entities.editEntity(this.tabletEntityID, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})});
|
||||
};
|
||||
|
||||
WebTablet.prototype.setURL = function (url) {
|
||||
|
@ -338,7 +339,8 @@ WebTablet.prototype.geometryChanged = function (geometry) {
|
|||
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties);
|
||||
Entities.editEntity(this.tabletEntityID, tabletProperties);
|
||||
// TODO -- is this still needed?
|
||||
// Entities.editEntity(this.tabletEntityID, tabletProperties);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -439,7 +441,8 @@ WebTablet.prototype.onHmdChanged = function () {
|
|||
var tabletProperties = {};
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
this.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties);
|
||||
Entities.editEntity(this.tabletEntityID, tabletProperties);
|
||||
// TODO -- is this still needed?
|
||||
// Entities.editEntity(this.tabletEntityID, tabletProperties);
|
||||
|
||||
// Full scene FXAA should be disabled on the overlay when the tablet in desktop mode.
|
||||
// This should make the text more readable.
|
||||
|
@ -530,7 +533,8 @@ WebTablet.prototype.cameraModeChanged = function (newMode) {
|
|||
var tabletProperties = {};
|
||||
// compute position, rotation & parentJointIndex of the tablet
|
||||
self.calculateTabletAttachmentProperties(NO_HANDS, false, tabletProperties);
|
||||
Entities.editEntity(self.tabletEntityID, tabletProperties);
|
||||
// TODO -- is this still needed?
|
||||
// Entities.editEntity(self.tabletEntityID, tabletProperties);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//
|
||||
|
||||
/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays,
|
||||
MyAvatar, Menu, Vec3 */
|
||||
MyAvatar, Menu, AvatarInputs, Vec3 */
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
var tabletRezzed = false;
|
||||
|
@ -25,9 +25,18 @@
|
|||
var debugTablet = false;
|
||||
var tabletScalePercentage = 100.0;
|
||||
UIWebTablet = null;
|
||||
var MSECS_PER_SEC = 1000.0;
|
||||
var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone";
|
||||
var gTablet = null;
|
||||
|
||||
Script.include("../libraries/WebTablet.js");
|
||||
|
||||
function checkTablet() {
|
||||
if (gTablet === null) {
|
||||
gTablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
}
|
||||
}
|
||||
|
||||
function tabletIsValid() {
|
||||
if (!UIWebTablet) {
|
||||
return false;
|
||||
|
@ -49,7 +58,8 @@
|
|||
}
|
||||
|
||||
function getTabletScalePercentageFromSettings() {
|
||||
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode;
|
||||
checkTablet()
|
||||
var toolbarMode = gTablet.toolbarMode;
|
||||
var tabletScalePercentage = DEFAULT_TABLET_SCALE;
|
||||
if (!toolbarMode) {
|
||||
if (HMD.active) {
|
||||
|
@ -77,6 +87,7 @@
|
|||
if (debugTablet) {
|
||||
print("TABLET rezzing");
|
||||
}
|
||||
checkTablet()
|
||||
|
||||
tabletScalePercentage = getTabletScalePercentageFromSettings();
|
||||
UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml",
|
||||
|
@ -92,7 +103,8 @@
|
|||
}
|
||||
|
||||
function showTabletUI() {
|
||||
Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = true;
|
||||
checkTablet()
|
||||
gTablet.tabletShown = true;
|
||||
|
||||
if (!tabletRezzed || !tabletIsValid()) {
|
||||
closeTabletUI();
|
||||
|
@ -114,7 +126,8 @@
|
|||
}
|
||||
|
||||
function hideTabletUI() {
|
||||
Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = false;
|
||||
checkTablet()
|
||||
gTablet.tabletShown = false;
|
||||
if (!UIWebTablet) {
|
||||
return;
|
||||
}
|
||||
|
@ -130,7 +143,8 @@
|
|||
}
|
||||
|
||||
function closeTabletUI() {
|
||||
Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown = false;
|
||||
checkTablet()
|
||||
gTablet.tabletShown = false;
|
||||
if (UIWebTablet) {
|
||||
if (UIWebTablet.onClose) {
|
||||
UIWebTablet.onClose();
|
||||
|
@ -149,17 +163,19 @@
|
|||
print("TABLET closeTabletUI, UIWebTablet is null");
|
||||
}
|
||||
tabletRezzed = false;
|
||||
gTablet = null
|
||||
}
|
||||
|
||||
|
||||
function updateShowTablet() {
|
||||
var MSECS_PER_SEC = 1000.0;
|
||||
var now = Date.now();
|
||||
|
||||
checkTablet()
|
||||
|
||||
// close the WebTablet if it we go into toolbar mode.
|
||||
var tabletShown = Tablet.getTablet("com.highfidelity.interface.tablet.system").tabletShown;
|
||||
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode;
|
||||
var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape;
|
||||
var tabletShown = gTablet.tabletShown;
|
||||
var toolbarMode = gTablet.toolbarMode;
|
||||
var landscape = gTablet.landscape;
|
||||
|
||||
if (tabletShown && toolbarMode) {
|
||||
closeTabletUI();
|
||||
|
@ -167,18 +183,20 @@
|
|||
return;
|
||||
}
|
||||
|
||||
//TODO: move to tablet qml?
|
||||
if (tabletShown) {
|
||||
var MUTE_MICROPHONE_MENU_ITEM = "Mute Microphone";
|
||||
var currentMicEnabled = !Menu.isOptionChecked(MUTE_MICROPHONE_MENU_ITEM);
|
||||
var currentMicLevel = getMicLevel();
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
tablet.updateMicEnabled(currentMicEnabled);
|
||||
tablet.updateAudioBar(currentMicLevel);
|
||||
gTablet.updateMicEnabled(currentMicEnabled);
|
||||
gTablet.updateAudioBar(currentMicLevel);
|
||||
}
|
||||
|
||||
updateTabletWidthFromSettings();
|
||||
if (UIWebTablet) {
|
||||
UIWebTablet.setLandscape(landscape);
|
||||
if (validCheckTime - now > MSECS_PER_SEC/4) {
|
||||
//each 250ms should be just fine
|
||||
updateTabletWidthFromSettings();
|
||||
if (UIWebTablet) {
|
||||
UIWebTablet.setLandscape(landscape);
|
||||
}
|
||||
}
|
||||
|
||||
if (validCheckTime - now > MSECS_PER_SEC) {
|
||||
|
@ -217,21 +235,20 @@
|
|||
|
||||
// also cause the stylus model to be loaded
|
||||
var tmpStylusID = Overlays.addOverlay("model", {
|
||||
name: "stylus",
|
||||
url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx",
|
||||
loadPriority: 10.0,
|
||||
position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})),
|
||||
dimensions: { x: 0.01, y: 0.01, z: 0.2 },
|
||||
solid: true,
|
||||
visible: true,
|
||||
ignoreRayIntersection: true,
|
||||
drawInFront: false,
|
||||
lifetime: 3
|
||||
});
|
||||
name: "stylus",
|
||||
url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx",
|
||||
loadPriority: 10.0,
|
||||
position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})),
|
||||
dimensions: { x: 0.01, y: 0.01, z: 0.2 },
|
||||
solid: true,
|
||||
visible: true,
|
||||
ignoreRayIntersection: true,
|
||||
drawInFront: false,
|
||||
lifetime: 3
|
||||
});
|
||||
Script.setTimeout(function() {
|
||||
Overlays.deleteOverlay(tmpStylusID);
|
||||
}, 300);
|
||||
|
||||
} else if (!tabletShown) {
|
||||
hideTabletUI();
|
||||
}
|
||||
|
@ -246,7 +263,8 @@
|
|||
}
|
||||
if (channel === "home") {
|
||||
if (UIWebTablet) {
|
||||
Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape = false;
|
||||
checkTablet()
|
||||
gTablet.landscape = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,30 +275,10 @@
|
|||
|
||||
Script.setInterval(updateShowTablet, 100);
|
||||
|
||||
// Initialise variables used to calculate audio level
|
||||
var accumulatedLevel = 0.0;
|
||||
// Note: Might have to tweak the following two based on the rate we're getting the data
|
||||
var AVERAGING_RATIO = 0.05;
|
||||
|
||||
// Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js
|
||||
function getMicLevel() {
|
||||
var LOUDNESS_FLOOR = 11.0;
|
||||
var LOUDNESS_SCALE = 2.8 / 5.0;
|
||||
var LOG2 = Math.log(2.0);
|
||||
var micLevel = 0.0;
|
||||
accumulatedLevel = AVERAGING_RATIO * accumulatedLevel + (1 - AVERAGING_RATIO) * (MyAvatar.audioLoudness);
|
||||
// Convert to log base 2
|
||||
var logLevel = Math.log(accumulatedLevel + 1) / LOG2;
|
||||
|
||||
if (logLevel <= LOUDNESS_FLOOR) {
|
||||
micLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE;
|
||||
} else {
|
||||
micLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE;
|
||||
}
|
||||
if (micLevel > 1.0) {
|
||||
micLevel = 1.0;
|
||||
}
|
||||
return micLevel;
|
||||
//reuse already existing C++ code
|
||||
return AvatarInputs.loudnessToAudioLevel(MyAvatar.audioLoudness)
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
|
|
|
@ -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://cdn.highfidelity.com/content-sets/home-tutorial-28.tar.gz";
|
||||
const HOME_CONTENT_URL = "http://cdn.highfidelity.com/content-sets/home-tutorial-RC39.tar.gz";
|
||||
|
||||
function getBuildInfo() {
|
||||
var buildInfoPath = null;
|
||||
|
|
3
tutorial/Changelog.md
Normal file
3
tutorial/Changelog.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
* home-tutorial-34
|
||||
* Update tutorial to only start if `HMD.active`
|
||||
* Update builder's grid to use "Good - Sub-meshes" for collision shape type
|
179
unpublishedScripts/interaction/Interaction.js
Normal file
179
unpublishedScripts/interaction/Interaction.js
Normal file
|
@ -0,0 +1,179 @@
|
|||
//
|
||||
// Interaction.js
|
||||
// scripts/interaction
|
||||
//
|
||||
// Created by Trevor Berninger on 3/20/17.
|
||||
// Copyright 2017 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(){
|
||||
print("loading interaction script");
|
||||
|
||||
var Avatar = false;
|
||||
var NPC = false;
|
||||
var previousNPC = false;
|
||||
var hasCenteredOnNPC = false;
|
||||
var distance = 10;
|
||||
var r = 8;
|
||||
var player = false;
|
||||
|
||||
var baselineX = 0;
|
||||
var baselineY = 0;
|
||||
var nodRange = 20;
|
||||
var shakeRange = 20;
|
||||
|
||||
var ticker = false;
|
||||
var heartbeatTimer = false;
|
||||
|
||||
function callOnNPC(message) {
|
||||
if(NPC)
|
||||
Messages.sendMessage("interactionComs", NPC + ":" + message);
|
||||
else
|
||||
Messages.sendMessage("interactionComs", previousNPC + ":" + message);
|
||||
}
|
||||
|
||||
LimitlessSpeechRecognition.onFinishedSpeaking.connect(function(speech) {
|
||||
print("Got: " + speech);
|
||||
callOnNPC("voiceData:" + speech);
|
||||
});
|
||||
|
||||
LimitlessSpeechRecognition.onReceivedTranscription.connect(function(speech) {
|
||||
callOnNPC("speaking");
|
||||
});
|
||||
|
||||
function setBaselineRotations(rot) {
|
||||
baselineX = rot.x;
|
||||
baselineY = rot.y;
|
||||
}
|
||||
|
||||
function findLookedAtNPC() {
|
||||
var intersection = AvatarList.findRayIntersection({origin: MyAvatar.position, direction: Quat.getFront(Camera.getOrientation())}, true);
|
||||
if (intersection.intersects && intersection.distance <= distance){
|
||||
var npcAvatar = AvatarList.getAvatar(intersection.avatarID);
|
||||
if (npcAvatar.displayName.search("NPC") != -1) {
|
||||
setBaselineRotations(Quat.safeEulerAngles(Camera.getOrientation()));
|
||||
return intersection.avatarID;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isStillFocusedNPC() {
|
||||
var avatar = AvatarList.getAvatar(NPC);
|
||||
if (avatar) {
|
||||
var avatarPosition = avatar.position;
|
||||
return Vec3.distance(MyAvatar.position, avatarPosition) <= distance && Math.abs(Quat.dot(Camera.getOrientation(), Quat.lookAtSimple(MyAvatar.position, avatarPosition))) > 0.6;
|
||||
}
|
||||
return false; // NPC reference died. Maybe it crashed or we teleported to a new world?
|
||||
}
|
||||
|
||||
function onWeLostFocus() {
|
||||
print("lost NPC: " + NPC);
|
||||
callOnNPC("onLostFocused");
|
||||
var baselineX = 0;
|
||||
var baselineY = 0;
|
||||
}
|
||||
|
||||
function onWeGainedFocus() {
|
||||
print("found NPC: " + NPC);
|
||||
callOnNPC("onFocused");
|
||||
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
|
||||
baselineX = rotation.x;
|
||||
baselineY = rotation.y;
|
||||
LimitlessSpeechRecognition.setListeningToVoice(true);
|
||||
}
|
||||
|
||||
function checkFocus() {
|
||||
var newNPC = findLookedAtNPC();
|
||||
|
||||
if (NPC && newNPC != NPC && !isStillFocusedNPC()) {
|
||||
onWeLostFocus();
|
||||
previousNPC = NPC;
|
||||
NPC = false;
|
||||
}
|
||||
if (!NPC && newNPC != false) {
|
||||
NPC = newNPC;
|
||||
onWeGainedFocus();
|
||||
}
|
||||
}
|
||||
|
||||
function checkGesture() {
|
||||
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
|
||||
|
||||
var deltaX = Math.abs(rotation.x - baselineX);
|
||||
if (deltaX > 180) {
|
||||
deltaX -= 180;
|
||||
}
|
||||
var deltaY = Math.abs(rotation.y - baselineY);
|
||||
if (deltaY > 180) {
|
||||
deltaY -= 180;
|
||||
}
|
||||
|
||||
if (deltaX >= nodRange && deltaY <= shakeRange) {
|
||||
callOnNPC("onNodReceived");
|
||||
} else if (deltaY >= shakeRange && deltaX <= nodRange) {
|
||||
callOnNPC("onShakeReceived");
|
||||
}
|
||||
}
|
||||
|
||||
function tick() {
|
||||
checkFocus();
|
||||
if (NPC) {
|
||||
checkGesture();
|
||||
}
|
||||
}
|
||||
|
||||
function heartbeat() {
|
||||
callOnNPC("beat");
|
||||
}
|
||||
|
||||
Messages.subscribe("interactionComs");
|
||||
|
||||
Messages.messageReceived.connect(function (channel, message, sender) {
|
||||
if(channel === "interactionComs" && player) {
|
||||
var codeIndex = message.search('clientexec');
|
||||
if(codeIndex != -1) {
|
||||
var code = message.substr(codeIndex+11);
|
||||
Script.evaluate(code, '');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.enterEntity = function(id) {
|
||||
player = true;
|
||||
print("Something entered me: " + id);
|
||||
LimitlessSpeechRecognition.setAuthKey("testKey");
|
||||
if (!ticker) {
|
||||
ticker = Script.setInterval(tick, 333);
|
||||
}
|
||||
if(!heartbeatTimer) {
|
||||
heartbeatTimer = Script.setInterval(heartbeat, 1000);
|
||||
}
|
||||
};
|
||||
this.leaveEntity = function(id) {
|
||||
LimitlessSpeechRecognition.setListeningToVoice(false);
|
||||
player = false;
|
||||
print("Something left me: " + id);
|
||||
if (previousNPC)
|
||||
Messages.sendMessage("interactionComs", previousNPC + ":leftArea");
|
||||
if (ticker) {
|
||||
ticker.stop();
|
||||
ticker = false;
|
||||
}
|
||||
if (heartbeatTimer) {
|
||||
heartbeatTimer.stop();
|
||||
heartbeatTimer = false;
|
||||
}
|
||||
};
|
||||
this.unload = function() {
|
||||
print("Okay. I'm Unloading!");
|
||||
if (ticker) {
|
||||
ticker.stop();
|
||||
ticker = false;
|
||||
}
|
||||
};
|
||||
print("finished loading interaction script");
|
||||
});
|
179
unpublishedScripts/interaction/NPCHelpers.js
Normal file
179
unpublishedScripts/interaction/NPCHelpers.js
Normal file
|
@ -0,0 +1,179 @@
|
|||
//
|
||||
// NPCHelpers.js
|
||||
// scripts/interaction
|
||||
//
|
||||
// Created by Trevor Berninger on 3/20/17.
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
var audioInjector = false;
|
||||
var blocked = false;
|
||||
var playingResponseAnim = false;
|
||||
var storyURL = "";
|
||||
var _qid = "start";
|
||||
|
||||
print("TESTTEST");
|
||||
|
||||
function strContains(str, sub) {
|
||||
return str.search(sub) != -1;
|
||||
}
|
||||
|
||||
function callbackOnCondition(conditionFunc, ms, callback, count) {
|
||||
var thisCount = 0;
|
||||
if (typeof count !== 'undefined') {
|
||||
thisCount = count;
|
||||
}
|
||||
if (conditionFunc()) {
|
||||
callback();
|
||||
} else if (thisCount < 10) {
|
||||
Script.setTimeout(function() {
|
||||
callbackOnCondition(conditionFunc, ms, callback, thisCount + 1);
|
||||
}, ms);
|
||||
} else {
|
||||
print("callbackOnCondition timeout");
|
||||
}
|
||||
}
|
||||
|
||||
function playAnim(animURL, looping, onFinished) {
|
||||
print("got anim: " + animURL);
|
||||
print("looping: " + looping);
|
||||
// Start caching the animation if not already cached.
|
||||
AnimationCache.getAnimation(animURL);
|
||||
|
||||
// Tell the avatar to animate so that we can tell if the animation is ready without crashing
|
||||
Avatar.startAnimation(animURL, 30, 1, false, false, 0, 1);
|
||||
|
||||
// Continually check if the animation is ready
|
||||
callbackOnCondition(function(){
|
||||
var details = Avatar.getAnimationDetails();
|
||||
// if we are running the request animation and are past the first frame, the anim is loaded properly
|
||||
print("running: " + details.running);
|
||||
print("url and animURL: " + details.url.trim().replace(/ /g, "%20") + " | " + animURL.trim().replace(/ /g, "%20"));
|
||||
print("currentFrame: " + details.currentFrame);
|
||||
return details.running && details.url.trim().replace(/ /g, "%20") == animURL.trim().replace(/ /g, "%20") && details.currentFrame > 0;
|
||||
}, 250, function(){
|
||||
var timeOfAnim = ((AnimationCache.getAnimation(animURL).frames.length / 30) * 1000) + 100; // frames to miliseconds plus a small buffer
|
||||
print("animation loaded. length: " + timeOfAnim);
|
||||
// Start the animation again but this time with frame information
|
||||
Avatar.startAnimation(animURL, 30, 1, looping, true, 0, AnimationCache.getAnimation(animURL).frames.length);
|
||||
if (typeof onFinished !== 'undefined') {
|
||||
print("onFinished defined. setting the timeout with timeOfAnim");
|
||||
timers.push(Script.setTimeout(onFinished, timeOfAnim));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function playSound(soundURL, onFinished) {
|
||||
callbackOnCondition(function() {
|
||||
return SoundCache.getSound(soundURL).downloaded;
|
||||
}, 250, function() {
|
||||
if (audioInjector) {
|
||||
audioInjector.stop();
|
||||
}
|
||||
audioInjector = Audio.playSound(SoundCache.getSound(soundURL), {position: Avatar.position, volume: 1.0});
|
||||
if (typeof onFinished !== 'undefined') {
|
||||
audioInjector.finished.connect(onFinished);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function npcRespond(soundURL, animURL, onFinished) {
|
||||
if (typeof soundURL !== 'undefined' && soundURL != '') {
|
||||
print("npcRespond got soundURL!");
|
||||
playSound(soundURL, function(){
|
||||
print("sound finished");
|
||||
var animDetails = Avatar.getAnimationDetails();
|
||||
print("animDetails.lastFrame: " + animDetails.lastFrame);
|
||||
print("animDetails.currentFrame: " + animDetails.currentFrame);
|
||||
if (animDetails.lastFrame < animDetails.currentFrame + 1 || !playingResponseAnim) {
|
||||
onFinished();
|
||||
}
|
||||
audioInjector = false;
|
||||
});
|
||||
}
|
||||
if (typeof animURL !== 'undefined' && animURL != '') {
|
||||
print("npcRespond got animURL!");
|
||||
playingResponseAnim = true;
|
||||
playAnim(animURL, false, function() {
|
||||
print("anim finished");
|
||||
playingResponseAnim = false;
|
||||
print("injector: " + audioInjector);
|
||||
if (!audioInjector || !audioInjector.isPlaying()) {
|
||||
print("resetting Timer");
|
||||
print("about to call onFinished");
|
||||
onFinished();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function npcRespondBlocking(soundURL, animURL, onFinished) {
|
||||
print("blocking response requested");
|
||||
if (!blocked) {
|
||||
print("not already blocked");
|
||||
blocked = true;
|
||||
npcRespond(soundURL, animURL, function(){
|
||||
if (onFinished){
|
||||
onFinished();
|
||||
}blocked = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function npcContinueStory(soundURL, animURL, nextID, onFinished) {
|
||||
if (!nextID) {
|
||||
nextID = _qid;
|
||||
}
|
||||
npcRespondBlocking(soundURL, animURL, function(){
|
||||
if (onFinished){
|
||||
onFinished();
|
||||
}setQid(nextID);
|
||||
});
|
||||
}
|
||||
|
||||
function setQid(newQid) {
|
||||
print("setting quid");
|
||||
print("_qid: " + _qid);
|
||||
_qid = newQid;
|
||||
print("_qid: " + _qid);
|
||||
doActionFromServer("init");
|
||||
}
|
||||
|
||||
function runOnClient(code) {
|
||||
Messages.sendMessage("interactionComs", "clientexec:" + code);
|
||||
}
|
||||
|
||||
function doActionFromServer(action, data, useServerCache) {
|
||||
if (action == "start") {
|
||||
ignoreCount = 0;
|
||||
_qid = "start";
|
||||
}
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "http://gserv_devel.studiolimitless.com/story", true);
|
||||
xhr.onreadystatechange = function(){
|
||||
if (xhr.readyState == 4){
|
||||
if (xhr.status == 200) {
|
||||
print("200!");
|
||||
print("evaluating: " + xhr.responseText);
|
||||
Script.evaluate(xhr.responseText, "");
|
||||
} else if (xhr.status == 444) {
|
||||
print("Limitless Serv 444: API error: " + xhr.responseText);
|
||||
} else {
|
||||
print("HTTP Code: " + xhr.status + ": " + xhr.responseText);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||
var postData = "url=" + storyURL + "&action=" + action + "&qid=" + _qid;
|
||||
if (typeof data !== 'undefined' && data != '') {
|
||||
postData += "&data=" + data;
|
||||
}
|
||||
if (typeof useServerCache !== 'undefined' && !useServerCache) {
|
||||
postData += "&nocache=true";
|
||||
}
|
||||
print("Sending: " + postData);
|
||||
xhr.send(postData);
|
||||
}
|
102
unpublishedScripts/interaction/NPC_AC.js
Normal file
102
unpublishedScripts/interaction/NPC_AC.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// NPC_AC.js
|
||||
// scripts/interaction
|
||||
//
|
||||
// Created by Trevor Berninger on 3/20/17.
|
||||
// Copyright 2017 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
|
||||
//
|
||||
|
||||
var currentlyUsedIndices = [];
|
||||
var timers = [];
|
||||
var currentlyEngaged = false;
|
||||
var questionNumber = 0;
|
||||
var heartbeatTimeout = false;
|
||||
function getRandomRiddle() {
|
||||
var randIndex = null;
|
||||
do {
|
||||
randIndex = Math.floor(Math.random() * 15) + 1;
|
||||
} while (randIndex in currentlyUsedIndices);
|
||||
|
||||
currentlyUsedIndices.push(randIndex);
|
||||
return randIndex.toString();
|
||||
}
|
||||
|
||||
Script.include("https://raw.githubusercontent.com/Delamare2112/hifi/Interaction/unpublishedScripts/interaction/NPCHelpers.js", function(){
|
||||
print("NPCHelpers included.");main();
|
||||
});
|
||||
|
||||
var idleAnim = "https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/idle.fbx";
|
||||
var FST = "https://s3.amazonaws.com/hifi-public/tony/fixed-sphinx/sphinx.fst";
|
||||
|
||||
Agent.isAvatar = true;
|
||||
Avatar.skeletonModelURL = FST;
|
||||
Avatar.displayName = "NPC";
|
||||
Avatar.position = {x: 0.3, y: -23.4, z: 8.0};
|
||||
Avatar.orientation = {x: 0, y: 1, z: 0, w: 0};
|
||||
// Avatar.position = {x: 1340.3555, y: 4.078, z: -420.1562};
|
||||
// Avatar.orientation = {x: 0, y: -0.707, z: 0, w: 0.707};
|
||||
Avatar.scale = 2;
|
||||
|
||||
Messages.subscribe("interactionComs");
|
||||
|
||||
function endInteraction() {
|
||||
print("ending interaction");
|
||||
blocked = false;
|
||||
currentlyEngaged = false;
|
||||
if(audioInjector)
|
||||
audioInjector.stop();
|
||||
for (var t in timers) {
|
||||
Script.clearTimeout(timers[t]);
|
||||
}
|
||||
if(_qid != "Restarting") {
|
||||
npcRespondBlocking(
|
||||
'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/EarlyExit_0' + (Math.floor(Math.random() * 2) + 1).toString() + '.wav',
|
||||
'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/reversedSphinx.fbx',
|
||||
function(){
|
||||
Avatar.startAnimation('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', 0);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
storyURL = "https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Sphinx.json";
|
||||
Messages.messageReceived.connect(function (channel, message, sender) {
|
||||
if(!strContains(message, 'beat'))
|
||||
print(sender + " -> NPC @" + Agent.sessionUUID + ": " + message);
|
||||
if (channel === "interactionComs" && strContains(message, Agent.sessionUUID)) {
|
||||
if (strContains(message, 'beat')) {
|
||||
if(heartbeatTimeout) {
|
||||
Script.clearTimeout(heartbeatTimeout);
|
||||
heartbeatTimeout = false;
|
||||
}
|
||||
heartbeatTimeout = Script.setTimeout(endInteraction, 1500);
|
||||
}
|
||||
else if (strContains(message, "onFocused") && !currentlyEngaged) {
|
||||
blocked = false;
|
||||
currentlyEngaged = true;
|
||||
currentlyUsedIndices = [];
|
||||
doActionFromServer("start");
|
||||
} else if (strContains(message, "leftArea")) {
|
||||
|
||||
} else if (strContains(message, "speaking")) {
|
||||
|
||||
} else {
|
||||
var voiceDataIndex = message.search("voiceData");
|
||||
if (voiceDataIndex != -1) {
|
||||
var words = message.substr(voiceDataIndex+10);
|
||||
if (!isNaN(_qid) && (strContains(words, "repeat") || (strContains(words, "say") && strContains(words, "again")))) {
|
||||
doActionFromServer("init");
|
||||
} else {
|
||||
doActionFromServer("words", words);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Script.update.connect(updateGem);
|
||||
Avatar.startAnimation("https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx", 0);
|
||||
}
|
159
unpublishedScripts/interaction/Sphinx.json
Normal file
159
unpublishedScripts/interaction/Sphinx.json
Normal file
|
@ -0,0 +1,159 @@
|
|||
{
|
||||
"Name": "10 Questions",
|
||||
"Defaults":
|
||||
{
|
||||
"Actions":
|
||||
{
|
||||
"positive": "var x=function(){if(questionNumber>=2){setQid('Finished');return;}var suffix=['A', 'B'][questionNumber++] + '_0' + (Math.floor(Math.random() * 2) + 2).toString() + '.wav';npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/RightAnswer'+suffix, 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/RightAnswerB_02.fbx', getRandomRiddle());};x();",
|
||||
"unknown": "var suffix=(Math.floor(Math.random() * 3) + 1).toString();npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/WrongAnswer_0' + suffix + '.wav','https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/WrongAnswer_0' + suffix + '.fbx', getRandomRiddle());",
|
||||
"hint": "var suffix=(Math.floor(Math.random() * 2) + 1).toString();npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Hint_0' + suffix + '.wav','https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hint_0' + suffix + '.fbx')"
|
||||
},
|
||||
"Responses":
|
||||
{
|
||||
"positive": ["yes","yup","yeah","yahoo","sure","affirmative","okay","aye","right","exactly","course","naturally","unquestionably","positively","yep","definitely","certainly","fine","absolutely","positive","love","fantastic"],
|
||||
"thinking": ["oh", "think about", "i know", "what was", "well", "not sure", "one before", "hold", "one moment", "one second", "1 second", "1 sec", "one sec"],
|
||||
"hint": ["hint", "heads"]
|
||||
}
|
||||
},
|
||||
"Story":
|
||||
[
|
||||
{
|
||||
"QID": "start",
|
||||
"init": "questionNumber=0;npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/HiFi_Sphinx_Anim_Combined_Entrance_Audio.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', getRandomRiddle());"
|
||||
},
|
||||
{
|
||||
"QID": "1",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Blackboard.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Blackboard.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["blackboard", "chalkboard", "chalk board", "slate"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "2",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Breath.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Breath.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["breath", "death"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "3",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Clock.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Clock.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["clock", "cock"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "4",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Coffin.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Coffin.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["coffin", "casket", "possum"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "5",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Coin.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Coin.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["coin", "boing", "coinage", "coin piece", "change", "join"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "6",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Corn.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Corn.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["corn", "born", "maize", "maze", "means", "torn", "horn", "worn", "porn"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "7",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Darkness.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Darkness.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["darkness", "dark", "blackness"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "8",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Gloves.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Gloves.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["gloves", "love"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "9",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Gold.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Gold.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["gold", "old", "bold", "cold", "told"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "10",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_River.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_River.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["river", "bigger", "stream", "creek", "brook"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "11",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Secret.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Secret.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["secret"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "12",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Shadow.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Shadow.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["shadow"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "13",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Silence.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Silence.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["silence", "lance", "quiet"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "14",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Stairs.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Stairs.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["stairs", "steps", "stair", "stairwell", "there's", "stairway"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "15",
|
||||
"init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Umbrella.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Umbrella.fbx');",
|
||||
"responses":
|
||||
{
|
||||
"positive": ["umbrella"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"QID": "Finished",
|
||||
"init": "Script.clearTimeout(heartbeatTimeout);heartbeatTimeout = false;npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/ConclusionRight_02.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/ConclusionRight_02.fbx', function(){runOnClient('MyAvatar.goToLocation({x: 5, y: -29, z: -63}, true, true);');setQid('Restarting');});",
|
||||
"positive": "",
|
||||
"negative": "",
|
||||
"unknown": ""
|
||||
},
|
||||
{
|
||||
"QID": "Restarting",
|
||||
"init": "npcRespondBlocking('', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/reversedSphinx.fbx', function(){Avatar.startAnimation('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', 0);_qid='';});",
|
||||
"positive": "",
|
||||
"negative": "",
|
||||
"unknown": ""
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue