mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' into vr-edit-a
This commit is contained in:
commit
450badf023
25 changed files with 261 additions and 130 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -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;\"><</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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
#include "AudioSRC.h"
|
||||
#include "AudioHelpers.h"
|
||||
|
||||
int audioInjectorPtrMetaTypeId = qRegisterMetaType<AudioInjector*>();
|
||||
|
||||
AbstractAudioInterface* AudioInjector::_localAudioInterface{ nullptr };
|
||||
|
||||
AudioInjectorState operator& (AudioInjectorState lhs, AudioInjectorState rhs) {
|
||||
|
|
|
@ -125,6 +125,4 @@ private:
|
|||
friend class AudioInjectorManager;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AudioInjectorPointer)
|
||||
|
||||
#endif // hifi_AudioInjector_h
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue