Merge branch 'master' into vr-edit-a

This commit is contained in:
David Rowe 2017-07-25 12:21:49 +12:00
commit 450badf023
25 changed files with 261 additions and 130 deletions

View file

@ -16,7 +16,7 @@ Contributing
git checkout -b new_branch_name
```
4. Code
* Follow the [coding standard](https://wiki.highfidelity.com/wiki/Coding_Standards)
* Follow the [coding standard](https://docs.highfidelity.com/build-guide/coding-standards)
5. Commit
* Use [well formed commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
6. Update your branch

View file

@ -91,9 +91,22 @@ void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) {
}
}
void AssignmentClientMonitor::childProcessFinished(qint64 pid) {
void AssignmentClientMonitor::childProcessFinished(qint64 pid, int exitCode, QProcess::ExitStatus exitStatus) {
auto message = "Child process " + QString::number(pid) + " has %1 with exit code " + QString::number(exitCode) + ".";
if (_childProcesses.remove(pid)) {
qDebug() << "Child process" << pid << "has finished. Removed from internal map.";
message.append(" Removed from internal map.");
} else {
message.append(" Could not find process in internal map.");
}
switch (exitStatus) {
case QProcess::NormalExit:
qDebug() << qPrintable(message.arg("returned"));
break;
case QProcess::CrashExit:
qCritical() << qPrintable(message.arg("crashed"));
break;
}
}
@ -221,7 +234,9 @@ void AssignmentClientMonitor::spawnChildClient() {
auto pid = assignmentClient->processId();
// make sure we hear that this process has finished when it does
connect(assignmentClient, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, [this, pid]() { childProcessFinished(pid); });
this, [this, pid](int exitCode, QProcess::ExitStatus exitStatus) {
childProcessFinished(pid, exitCode, exitStatus);
});
qDebug() << "Spawned a child client with PID" << assignmentClient->processId();

View file

@ -44,7 +44,7 @@ public:
void stopChildProcesses();
private slots:
void checkSpares();
void childProcessFinished(qint64 pid);
void childProcessFinished(qint64 pid, int exitCode, QProcess::ExitStatus exitStatus);
void handleChildStatusPacket(QSharedPointer<ReceivedMessage> message);
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override;

View file

@ -81,7 +81,11 @@
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
"when": "Keyboard.RightMouseButton",
"to": "Actions.Yaw"
"to": "Actions.Yaw",
"filters":
[
{ "type": "scale", "scale": 0.6 }
]
},
{ "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" },
@ -102,8 +106,19 @@
{ "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" },
{ "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_UP" },
{ "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_UP",
"filters":
[
{ "type": "scale", "scale": 0.6 }
]
},
{ "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseButton", "to": "Actions.PITCH_DOWN",
"filters":
[
{ "type": "scale", "scale": 0.6 }
]
},
{ "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" },
{ "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" },

View file

@ -506,7 +506,7 @@ Rectangle {
}
HifiControls.Tree {
id: treeView
height: 430
height: 290
anchors.leftMargin: hifi.dimensions.contentMargin.x + 2 // Extra for border
anchors.rightMargin: hifi.dimensions.contentMargin.x + 2 // Extra for border
anchors.left: parent.left

View file

@ -21,7 +21,7 @@ TabView {
enabled: true
property string originalUrl: ""
Rectangle {
Rectangle {
color: "#404040"
Text {
@ -180,7 +180,7 @@ TabView {
WebView {
id: entityListToolWebView
url: "../../../../../scripts/system/html/entityList.html"
url: Paths.defaultScripts + "/system/html/entityList.html"
anchors.fill: parent
enabled: true
}
@ -194,7 +194,7 @@ TabView {
WebView {
id: entityPropertiesWebView
url: "../../../../../scripts/system/html/entityProperties.html"
url: Paths.defaultScripts + "/system/html/entityProperties.html"
anchors.fill: parent
enabled: true
}
@ -208,7 +208,7 @@ TabView {
WebView {
id: gridControlsWebView
url: "../../../../../scripts/system/html/gridControls.html"
url: Paths.defaultScripts + "/system/html/gridControls.html"
anchors.fill: parent
enabled: true
}
@ -222,7 +222,7 @@ TabView {
WebView {
id: particleExplorerWebView
url: "../../../../../scripts/system/particle_explorer/particleExplorer.html"
url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html"
anchors.fill: parent
enabled: true
}
@ -293,16 +293,16 @@ TabView {
break;
case 'list':
editTabView.currentIndex = 1;
break;
break;
case 'properties':
editTabView.currentIndex = 2;
break;
break;
case 'grid':
editTabView.currentIndex = 3;
break;
break;
case 'particle':
editTabView.currentIndex = 4;
break;
break;
default:
console.warn('Attempt to switch to invalid tab:', id);
}
@ -310,4 +310,4 @@ TabView {
console.warn('Attempt to switch tabs with invalid input:', JSON.stringify(id));
}
}
}
}

View file

@ -69,8 +69,8 @@ const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // action motor gets add
const float MIN_AVATAR_SPEED = 0.05f;
const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this
const float YAW_SPEED_DEFAULT = 120.0f; // degrees/sec
const float PITCH_SPEED_DEFAULT = 90.0f; // degrees/sec
const float YAW_SPEED_DEFAULT = 100.0f; // degrees/sec
const float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec
// TODO: normalize avatar speed for standard avatar size, then scale all motion logic
// to properly follow avatar size.

View file

@ -81,7 +81,7 @@ private:
float _inputVolume { 1.0f };
float _inputLevel { 0.0f };
bool _isMuted { false };
bool _enableNoiseReduction;
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
bool _contextIsHMD { false };
AudioDevices* getDevices() { return &_devices; }

View file

@ -36,6 +36,21 @@ Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
}
}
static QString getTargetDevice(bool hmd, QAudio::Mode mode) {
QString deviceName;
auto& setting = getSetting(hmd, mode);
if (setting.isSet()) {
deviceName = setting.get();
} else if (hmd) {
if (mode == QAudio::AudioInput) {
deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice();
} else { // if (_mode == QAudio::AudioOutput)
deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice();
}
}
return deviceName;
}
QHash<int, QByteArray> AudioDeviceList::_roles {
{ Qt::DisplayRole, "display" },
{ Qt::CheckStateRole, "selected" },
@ -59,10 +74,15 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
}
}
void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
void AudioDeviceList::resetDevice(bool contextIsHMD) {
auto client = DependencyManager::get<AudioClient>().data();
auto deviceName = getSetting(contextIsHMD, _mode).get();
QString deviceName = getTargetDevice(contextIsHMD, _mode);
// FIXME can't use blocking connections here, so we can't determine whether the switch succeeded or not
// We need to have the AudioClient emit signals on switch success / failure
QMetaObject::invokeMethod(client, "switchAudioDevice",
Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName));
#if 0
bool switchResult = false;
QMetaObject::invokeMethod(client, "switchAudioDevice", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, switchResult),
@ -85,6 +105,7 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode));
}
}
#endif
}
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
@ -137,11 +158,8 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
}
void AudioDevices::onContextChanged(const QString& context) {
auto input = getSetting(_contextIsHMD, QAudio::AudioInput).get();
auto output = getSetting(_contextIsHMD, QAudio::AudioOutput).get();
_inputs.resetDevice(_contextIsHMD, input);
_outputs.resetDevice(_contextIsHMD, output);
_inputs.resetDevice(_contextIsHMD);
_outputs.resetDevice(_contextIsHMD);
}
void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) {
@ -182,8 +200,16 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d
void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) {
if (mode == QAudio::AudioInput) {
if (_requestedInputDevice == device) {
onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice);
_requestedInputDevice = QAudioDeviceInfo();
}
_inputs.onDeviceChanged(device);
} else { // if (mode == QAudio::AudioOutput)
if (_requestedOutputDevice == device) {
onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice);
_requestedOutputDevice = QAudioDeviceInfo();
}
_outputs.onDeviceChanged(device);
}
}
@ -201,28 +227,16 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceI
void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device) {
auto client = DependencyManager::get<AudioClient>();
bool success = false;
_requestedInputDevice = device;
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, success),
Q_ARG(QAudio::Mode, QAudio::AudioInput),
Q_ARG(const QAudioDeviceInfo&, device));
if (success) {
onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice);
}
}
void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device) {
auto client = DependencyManager::get<AudioClient>();
bool success = false;
_requestedOutputDevice = device;
QMetaObject::invokeMethod(client.data(), "switchAudioDevice",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, success),
Q_ARG(QAudio::Mode, QAudio::AudioOutput),
Q_ARG(const QAudioDeviceInfo&, device));
if (success) {
onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice);
}
}

View file

@ -39,7 +39,7 @@ public:
QVariant data(const QModelIndex& index, int role) const override;
// reset device to the last selected device in this context, or the default
void resetDevice(bool contextIsHMD, const QString& device);
void resetDevice(bool contextIsHMD);
signals:
void deviceChanged(const QAudioDeviceInfo& device);
@ -87,8 +87,10 @@ private:
AudioDeviceList _inputs { QAudio::AudioInput };
AudioDeviceList _outputs { QAudio::AudioOutput };
QAudioDeviceInfo _requestedOutputDevice;
QAudioDeviceInfo _requestedInputDevice;
bool& _contextIsHMD;
const bool& _contextIsHMD;
};
};

View file

@ -24,13 +24,14 @@
#include "ScriptHighlighting.h"
const int NO_CURRENT_HISTORY_COMMAND = -1;
const int MAX_HISTORY_SIZE = 64;
const int MAX_HISTORY_SIZE = 256;
const QString HISTORY_FILENAME = "JSConsole.history.json";
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_WARNING_STYLE = "color: #999922;";
const QString RESULT_ERROR_STYLE = "color: #d13b22;";
const QString GUTTER_PREVIOUS_COMMAND = "<span style=\"color: #57b8bb;\">&lt;</span>";
@ -38,14 +39,35 @@ const QString GUTTER_ERROR = "<span style=\"color: #d13b22;\">X</span>";
const QString JSConsole::_consoleFileName { "about:console" };
const QString JSON_KEY = "entries";
QList<QString> _readLines(const QString& filename) {
QFile file(filename);
file.open(QFile::ReadOnly);
auto json = QTextStream(&file).readAll().toUtf8();
auto root = QJsonDocument::fromJson(json).object();
// TODO: check root["version"]
return root[JSON_KEY].toVariant().toStringList();
}
void _writeLines(const QString& filename, const QList<QString>& lines) {
QFile file(filename);
file.open(QFile::WriteOnly);
auto root = QJsonObject();
root["version"] = 1.0;
root["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate);
root[JSON_KEY] = QJsonArray::fromStringList(lines);
auto json = QJsonDocument(root).toJson();
QTextStream(&file) << json;
}
JSConsole::JSConsole(QWidget* parent, ScriptEngine* scriptEngine) :
QWidget(parent),
_ui(new Ui::Console),
_currentCommandInHistory(NO_CURRENT_HISTORY_COMMAND),
_commandHistory(),
_savedHistoryFilename(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + HISTORY_FILENAME),
_commandHistory(_readLines(_savedHistoryFilename)),
_ownScriptEngine(scriptEngine == NULL),
_scriptEngine(NULL) {
_ui->setupUi(this);
_ui->promptTextEdit->setLineWrapMode(QTextEdit::NoWrap);
_ui->promptTextEdit->setWordWrapMode(QTextOption::NoWrap);
@ -101,9 +123,12 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
}
void JSConsole::executeCommand(const QString& command) {
_commandHistory.prepend(command);
if (_commandHistory.length() > MAX_HISTORY_SIZE) {
_commandHistory.removeLast();
if (_commandHistory.isEmpty() || _commandHistory.constFirst() != command) {
_commandHistory.prepend(command);
if (_commandHistory.length() > MAX_HISTORY_SIZE) {
_commandHistory.removeLast();
}
_writeLines(_savedHistoryFilename, _commandHistory);
}
_ui->promptTextEdit->setDisabled(true);
@ -182,7 +207,7 @@ bool JSConsole::eventFilter(QObject* sender, QEvent* event) {
// a new QTextBlock isn't created.
keyEvent->setModifiers(keyEvent->modifiers() & ~Qt::ShiftModifier);
} else {
QString command = _ui->promptTextEdit->toPlainText().trimmed();
QString command = _ui->promptTextEdit->toPlainText().replace("\r\n","\n").trimmed();
if (!command.isEmpty()) {
QTextCursor cursor = _ui->promptTextEdit->textCursor();

View file

@ -63,6 +63,7 @@ private:
QFutureWatcher<QScriptValue> _executeWatcher;
Ui::Console* _ui;
int _currentCommandInHistory;
QString _savedHistoryFilename;
QList<QString> _commandHistory;
// Keeps track if the script engine is created inside the JSConsole
bool _ownScriptEngine;

View file

@ -27,8 +27,6 @@
#include "AudioSRC.h"
#include "AudioHelpers.h"
int audioInjectorPtrMetaTypeId = qRegisterMetaType<AudioInjector*>();
AbstractAudioInterface* AudioInjector::_localAudioInterface{ nullptr };
AudioInjectorState operator& (AudioInjectorState lhs, AudioInjectorState rhs) {

View file

@ -125,6 +125,4 @@ private:
friend class AudioInjectorManager;
};
Q_DECLARE_METATYPE(AudioInjectorPointer)
#endif // hifi_AudioInjector_h

View file

@ -123,6 +123,34 @@ glm::vec3 OBJTokenizer::getVec3() {
}
return v;
}
bool OBJTokenizer::getVertex(glm::vec3& vertex, glm::vec3& vertexColor) {
// Used for vertices which may also have a vertex color (RGB [0,1]) to follow.
// NOTE: Returns true if there is a vertex color.
auto x = getFloat(); // N.B.: getFloat() has side-effect
auto y = getFloat(); // And order of arguments is different on Windows/Linux.
auto z = getFloat();
vertex = glm::vec3(x, y, z);
auto r = 1.0f, g = 1.0f, b = 1.0f;
bool hasVertexColor = false;
if (isNextTokenFloat()) {
// If there's another float it's one of two things: a W component or an R component. The standard OBJ spec
// doesn't output a W component, so we're making the assumption that if a float follows (unless it's
// only a single value) that it's a vertex color.
r = getFloat();
if (isNextTokenFloat()) {
// Safe to assume the following values are the green/blue components.
g = getFloat();
b = getFloat();
hasVertexColor = true;
}
vertexColor = glm::vec3(r, g, b);
}
return hasVertexColor;
}
glm::vec2 OBJTokenizer::getVec2() {
float uCoord = getFloat();
float vCoord = 1.0f - getFloat();
@ -140,7 +168,9 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) {
}
// OBJFace
bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector<glm::vec3>& vertices) {
// NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just
// pairing it with the vertices vector for consistency.
bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& vertexColors) {
bool ok;
int index = vertexIndex.toInt(&ok);
if (!ok) {
@ -382,7 +412,14 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
#endif
}
} else if (token == "v") {
vertices.append(tokenizer.getVec3());
glm::vec3 vertex, vertexColor;
bool hasVertexColor = tokenizer.getVertex(vertex, vertexColor);
vertices.append(vertex);
if(hasVertexColor) {
vertexColors.append(vertexColor);
}
} else if (token == "vn") {
normals.append(tokenizer.getVec3());
} else if (token == "vt") {
@ -410,7 +447,8 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
assert(parts.count() >= 1);
assert(parts.count() <= 3);
const QByteArray noData {};
face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData, vertices);
face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData,
vertices, vertexColors);
face.groupName = currentGroup;
face.materialName = currentMaterialName;
}
@ -540,6 +578,15 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]);
glm::vec3 v2 = checked_at(vertices, face.vertexIndices[2]);
glm::vec3 vc0, vc1, vc2;
bool hasVertexColors = (vertexColors.size() > 0);
if (hasVertexColors) {
// If there are any vertex colors, it's safe to assume all meshes had them exported.
vc0 = checked_at(vertexColors, face.vertexIndices[0]);
vc1 = checked_at(vertexColors, face.vertexIndices[1]);
vc2 = checked_at(vertexColors, face.vertexIndices[2]);
}
// Scale the vertices if the OBJ file scale is specified as non-one.
if (scaleGuess != 1.0f) {
v0 *= scaleGuess;
@ -555,6 +602,13 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
meshPart.triangleIndices.append(mesh.vertices.count());
mesh.vertices << v2;
if (hasVertexColors) {
// Add vertex colors.
mesh.colors << vc0;
mesh.colors << vc1;
mesh.colors << vc2;
}
glm::vec3 n0, n1, n2;
if (face.normalIndices.count()) {
n0 = checked_at(normals, face.normalIndices[0]);
@ -690,6 +744,7 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) {
qCDebug(modelformat) << " meshes.count() =" << fbxgeo.meshes.count();
foreach (FBXMesh mesh, fbxgeo.meshes) {
qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count();
qCDebug(modelformat) << " colors.count() =" << mesh.colors.count();
qCDebug(modelformat) << " normals.count() =" << mesh.normals.count();
/*if (mesh.normals.count() == mesh.vertices.count()) {
for (int i = 0; i < mesh.normals.count(); i++) {

View file

@ -20,6 +20,7 @@ public:
void ungetChar(char ch) { _device->ungetChar(ch); }
const QString getComment() const { return _comment; }
glm::vec3 getVec3();
bool getVertex(glm::vec3& vertex, glm::vec3& vertexColor);
glm::vec2 getVec2();
float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); }
@ -38,7 +39,8 @@ public:
QString groupName; // We don't make use of hierarchical structure, but it can be preserved for debugging and future use.
QString materialName;
// Add one more set of vertex data. Answers true if successful
bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector<glm::vec3>& vertices);
bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& vertexColors);
// Return a set of one or more OBJFaces from this one, in which each is just a triangle.
// Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles.
QVector<OBJFace> triangulate();
@ -65,7 +67,8 @@ class OBJReader: public QObject { // QObject so we can make network requests.
Q_OBJECT
public:
typedef QVector<OBJFace> FaceGroup;
QVector<glm::vec3> vertices; // all that we ever encounter while reading
QVector<glm::vec3> vertices;
QVector<glm::vec3> vertexColors;
QVector<glm::vec2> textureUVs;
QVector<glm::vec3> normals;
QVector<FaceGroup> faceGroups;

View file

@ -13,6 +13,8 @@
#define hifi_PathUtils_h
#include <QtCore/QObject>
#include <QtCore/QUrl>
#include "DependencyManager.h"
/**jsdoc
@ -24,6 +26,7 @@ class PathUtils : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_PROPERTY(QString resources READ resourcesPath)
Q_PROPERTY(QUrl defaultScripts READ defaultScriptsLocation)
public:
static const QString& resourcesPath();

View file

@ -47,6 +47,7 @@ extern "C" FILE * __cdecl __iob_func(void) {
#include <QtCore/QDebug>
#include <QDateTime>
#include <QElapsedTimer>
#include <QTimer>
#include <QProcess>
#include <QSysInfo>
#include <QThread>
@ -1077,14 +1078,20 @@ void setMaxCores(uint8_t maxCores) {
#endif
}
#ifdef Q_OS_WIN
VOID CALLBACK parentDiedCallback(PVOID lpParameter, BOOLEAN timerOrWaitFired) {
if (!timerOrWaitFired && qApp) {
void quitWithParentProcess() {
if (qApp) {
qDebug() << "Parent process died, quitting";
qApp->quit();
}
}
#ifdef Q_OS_WIN
VOID CALLBACK parentDiedCallback(PVOID lpParameter, BOOLEAN timerOrWaitFired) {
if (!timerOrWaitFired) {
quitWithParentProcess();
}
}
void watchParentProcess(int parentPID) {
DWORD processID = parentPID;
HANDLE procHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
@ -1092,8 +1099,17 @@ void watchParentProcess(int parentPID) {
HANDLE newHandle;
RegisterWaitForSingleObject(&newHandle, procHandle, parentDiedCallback, NULL, INFINITE, WT_EXECUTEONLYONCE);
}
#else
#elif defined(Q_OS_MAC) || defined(Q_OS_LINUX)
void watchParentProcess(int parentPID) {
qWarning() << "Parent PID option not implemented on this plateform";
auto timer = new QTimer(qApp);
timer->setInterval(MSECS_PER_SECOND);
QObject::connect(timer, &QTimer::timeout, qApp, [parentPID]() {
auto ppid = getppid();
if (parentPID != ppid) {
// If the PPID changed, then that means our parent process died.
quitWithParentProcess();
}
});
timer->start();
}
#endif // Q_OS_WIN
#endif

View file

@ -258,7 +258,8 @@ namespace cache {
};
}
void FileCache::eject(const FilePointer& file) {
// Take file pointer by value to insure it doesn't get destructed during the "erase()" calls
void FileCache::eject(FilePointer file) {
file->_locked = false;
const auto& length = file->getLength();
const auto& key = file->getKey();

View file

@ -119,7 +119,7 @@ private:
void clean();
void clear();
// Remove a file from the cache
void eject(const FilePointer& file);
void eject(FilePointer file);
size_t getOverbudgetAmount() const;

View file

@ -229,7 +229,6 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
} else {
removeButtonsFromToolbar();
addButtonsToHomeScreen();
emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL));
// destroy desktop window
if (_desktopWindow) {
@ -237,6 +236,8 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
_desktopWindow = nullptr;
}
}
loadHomeScreen(true);
emit screenChanged(QVariant("Home"), QVariant(TABLET_SOURCE_URL));
}
static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) {

View file

@ -1034,9 +1034,18 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp) {
function getControllerJointIndex(hand) {
if (HMD.isHandControllerAvailable()) {
return MyAvatar.getJointIndex(hand === RIGHT_HAND ?
"_CONTROLLER_RIGHTHAND" :
"_CONTROLLER_LEFTHAND");
var controllerJointIndex = -1;
if (Camera.mode === "first person") {
controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
"_CONTROLLER_RIGHTHAND" :
"_CONTROLLER_LEFTHAND");
} else if (Camera.mode === "third person") {
controllerJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ?
"_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" :
"_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
}
return controllerJointIndex;
}
return MyAvatar.getJointIndex("Head");

View file

@ -1074,7 +1074,7 @@ function loaded() {
elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex'));
elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex', 0));
var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);

View file

@ -1560,7 +1560,6 @@ SelectionDisplay = (function() {
visible: rotationOverlaysVisible
});
// TODO: we have not implemented the rotating handle/controls yet... so for now, these handles are hidden
Overlays.editOverlay(yawHandle, {
visible: rotateHandlesVisible,
position: yawCorner,
@ -3615,24 +3614,21 @@ SelectionDisplay = (function() {
onMove: function(event) {
var pickRay = generalComputePickRay(event.x, event.y);
Overlays.editOverlay(selectionBox, {
ignoreRayIntersection: true,
visible: false
});
Overlays.editOverlay(baseOfEntityProjectionOverlay, {
ignoreRayIntersection: true,
visible: false
});
Overlays.editOverlay(rotateOverlayTarget, {
ignoreRayIntersection: false
});
var result = Overlays.findRayIntersection(pickRay);
var result = Overlays.findRayIntersection(pickRay, true, [rotateOverlayTarget]);
if (result.intersects) {
var center = yawCenter;
var zero = yawZero;
// TODO: these vectors are backwards to their names, doesn't matter for this use case (inverted vectors still give same angle)
var centerToZero = Vec3.subtract(center, zero);
var centerToIntersect = Vec3.subtract(center, result.intersection);
// TODO: orientedAngle wants normalized centerToZero and centerToIntersect
var angleFromZero = Vec3.orientedAngle(centerToZero, centerToIntersect, rotationNormal);
var distanceFromCenter = Vec3.distance(center, result.intersection);
var snapToInner = distanceFromCenter < innerRadius;
@ -3785,17 +3781,12 @@ SelectionDisplay = (function() {
onMove: function(event) {
var pickRay = generalComputePickRay(event.x, event.y);
Overlays.editOverlay(selectionBox, {
ignoreRayIntersection: true,
visible: false
});
Overlays.editOverlay(baseOfEntityProjectionOverlay, {
ignoreRayIntersection: true,
visible: false
});
Overlays.editOverlay(rotateOverlayTarget, {
ignoreRayIntersection: false
});
var result = Overlays.findRayIntersection(pickRay);
var result = Overlays.findRayIntersection(pickRay, true, [rotateOverlayTarget]);
if (result.intersects) {
var properties = Entities.getEntityProperties(selectionManager.selections[0]);
@ -3947,17 +3938,12 @@ SelectionDisplay = (function() {
onMove: function(event) {
var pickRay = generalComputePickRay(event.x, event.y);
Overlays.editOverlay(selectionBox, {
ignoreRayIntersection: true,
visible: false
});
Overlays.editOverlay(baseOfEntityProjectionOverlay, {
ignoreRayIntersection: true,
visible: false
});
Overlays.editOverlay(rotateOverlayTarget, {
ignoreRayIntersection: false
});
var result = Overlays.findRayIntersection(pickRay);
var result = Overlays.findRayIntersection(pickRay, true, [rotateOverlayTarget]);
if (result.intersects) {
var properties = Entities.getEntityProperties(selectionManager.selections[0]);
@ -4074,21 +4060,8 @@ SelectionDisplay = (function() {
return false;
}
// before we do a ray test for grabbers, disable the ray intersection for our selection box
Overlays.editOverlay(selectionBox, {
ignoreRayIntersection: true
});
Overlays.editOverlay(yawHandle, {
ignoreRayIntersection: true
});
Overlays.editOverlay(pitchHandle, {
ignoreRayIntersection: true
});
Overlays.editOverlay(rollHandle, {
ignoreRayIntersection: true
});
result = Overlays.findRayIntersection(pickRay);
// ignore ray intersection for our selection box and yaw/pitch/roll
result = Overlays.findRayIntersection(pickRay, true, null, [ yawHandle, pitchHandle, rollHandle, selectionBox ] );
if (result.intersects) {
if (wantDebug) {
print("something intersects... ");
@ -4191,17 +4164,8 @@ SelectionDisplay = (function() {
}
// After testing our stretch handles, then check out rotate handles
Overlays.editOverlay(yawHandle, {
ignoreRayIntersection: false
});
Overlays.editOverlay(pitchHandle, {
ignoreRayIntersection: false
});
Overlays.editOverlay(rollHandle, {
ignoreRayIntersection: false
});
var result = Overlays.findRayIntersection(pickRay);
// Only intersect versus yaw/pitch/roll.
var result = Overlays.findRayIntersection(pickRay, true, [ yawHandle, pitchHandle, rollHandle ] );
var overlayOrientation;
var overlayCenter;
@ -4306,6 +4270,7 @@ SelectionDisplay = (function() {
});
// TODO: these three duplicate prior three, remove them.
Overlays.editOverlay(yawHandle, {
visible: false
});
@ -4402,10 +4367,8 @@ SelectionDisplay = (function() {
}
if (!somethingClicked) {
Overlays.editOverlay(selectionBox, {
ignoreRayIntersection: false
});
var result = Overlays.findRayIntersection(pickRay);
// Only intersect versus selectionBox.
var result = Overlays.findRayIntersection(pickRay, true, [selectionBox]);
if (result.intersects) {
switch (result.overlayID) {
case selectionBox:

View file

@ -12,9 +12,9 @@
Script.include("/~/system/libraries/utils.js");
if (!String.prototype.startsWith) {
String.prototype.startsWith = function(searchString, position){
position = position || 0;
return this.substr(position, searchString.length) === searchString;
};
position = position || 0;
return this.substr(position, searchString.length) === searchString;
};
}
var SETTING_KEY = "com.highfidelity.avatar.isSitting";
@ -122,10 +122,20 @@
this.rolesToOverride = function() {
return MyAvatar.getAnimationRoles().filter(function(role) {
return role === "fly" || role.startsWith("inAir");
return !(role.startsWith("right") || role.startsWith("left"));
});
}
// Handler for user changing the avatar model while sitting. There's currently an issue with changing avatar models while override role animations are applied,
// so to avoid that problem, re-apply the role overrides once the model has finished changing.
this.modelURLChangeFinished = function () {
print("Sitter's model has FINISHED changing. Reapply anim role overrides.");
var roles = this.rolesToOverride();
for (i in roles) {
MyAvatar.overrideRoleAnimation(roles[i], ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME);
}
}
this.sitDown = function() {
if (this.checkSeatForAvatar()) {
print("Someone is already sitting in that chair.");
@ -164,12 +174,14 @@
return { headType: 0 };
}, ["headType"]);
Script.update.connect(this, this.update);
MyAvatar.onLoadComplete.connect(this, this.modelURLChangeFinished);
}
this.standUp = function() {
print("Standing up (" + this.entityID + ")");
MyAvatar.removeAnimationStateHandler(this.animStateHandlerID);
Script.update.disconnect(this, this.update);
MyAvatar.onLoadComplete.disconnect(this, this.modelURLChangeFinished);
if (MyAvatar.sessionUUID === this.getSeatUser()) {
this.setSeatUser(null);
@ -331,7 +343,7 @@
}
this.cleanupOverlay();
}
this.clickDownOnEntity = function (id, event) {
if (isInEditMode()) {
return;