From 560696e41c20294c12029b2635345e0f37c597d1 Mon Sep 17 00:00:00 2001 From: vladest Date: Fri, 11 Aug 2017 20:39:17 +0200 Subject: [PATCH 001/202] Split logic and interface for HMD and desktop devices. Added CheckBox control based on QQC2 --- .../resources/qml/controls-uit/CheckBox2.qml | 111 +++++++++ interface/resources/qml/hifi/audio/Audio.qml | 234 +++++++++++++----- .../resources/qml/hifi/audio/CheckBox.qml | 4 +- interface/src/scripting/Audio.cpp | 8 +- interface/src/scripting/Audio.h | 4 +- interface/src/scripting/AudioDevices.cpp | 161 ++++++++---- interface/src/scripting/AudioDevices.h | 28 ++- 7 files changed, 436 insertions(+), 114 deletions(-) create mode 100644 interface/resources/qml/controls-uit/CheckBox2.qml diff --git a/interface/resources/qml/controls-uit/CheckBox2.qml b/interface/resources/qml/controls-uit/CheckBox2.qml new file mode 100644 index 0000000000..612e8d2662 --- /dev/null +++ b/interface/resources/qml/controls-uit/CheckBox2.qml @@ -0,0 +1,111 @@ +// +// CheckBox2.qml +// +// Created by Vlad Stelmahovsky on 10 Aug 2017 +// 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +import "../styles-uit" +import "../controls-uit" as HiFiControls + +CheckBox { + id: checkBox + + HifiConstants { id: hifi; } + + property int colorScheme: hifi.colorSchemes.light + property string color: hifi.colors.lightGrayText + readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light + property bool isRedCheck: false + property int boxSize: 14 + property int boxRadius: 3 + property bool wrap: true; + readonly property int checkSize: Math.max(boxSize - 8, 10) + readonly property int checkRadius: 2 + focusPolicy: Qt.ClickFocus + + indicator: Rectangle { + id: box + width: boxSize + height: boxSize + radius: boxRadius + x: checkBox.leftPadding + y: parent.height / 2 - height / 2 + border.width: 1 + border.color: pressed || hovered + ? hifi.colors.checkboxCheckedBorder + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + + gradient: Gradient { + GradientStop { + position: 0.2 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + } + } + + Rectangle { + visible: pressed || hovered + anchors.centerIn: parent + id: innerBox + width: checkSize - 4 + height: width + radius: checkRadius + color: hifi.colors.checkboxCheckedBorder + } + + Rectangle { + id: check + width: checkSize + height: checkSize + radius: checkRadius + anchors.centerIn: parent + color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked + border.width: 2 + border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder + visible: checked && !pressed || !checked && pressed + } + + Rectangle { + id: disabledOverlay + visible: !enabled + width: boxSize + height: boxSize + radius: boxRadius + border.width: 1 + border.color: hifi.colors.baseGrayHighlight + color: hifi.colors.baseGrayHighlight + opacity: 0.5 + } + } + + contentItem: Text { + id: root + FontLoader { id: ralewaySemiBold; source: pathToFonts + "fonts/Raleway-SemiBold.ttf"; } + font.pixelSize: hifi.fontSizes.inputLabel + font.family: ralewaySemiBold.name + text: checkBox.text + color: checkBox.color + x: 2 + wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap + elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight + enabled: checkBox.enabled + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + leftPadding: checkBox.indicator.width + checkBox.spacing + } +} + diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 93742e39a5..3d3a9ceeaa 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -12,7 +12,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import "../../styles-uit" @@ -36,13 +36,27 @@ Rectangle { return (root.parent !== null) && root.parent.objectName == "loader"; } + property bool isVR: Audio.context === "VR" + //placeholder for control sizes and paddings + //recalculates dynamically in case of UI size is changed + QtObject { + id: margins + property real paddings: parent.width / 20.25 + + property real sizeCheckBox: parent.width / 13.5 + property real sizeText: parent.width / 2.5 + property real sizeLevel: parent.width / 5.8 + property real sizeDesktop: parent.width / 5.8 + property real sizeVR: parent.width / 13.5 + } + Column { y: 16; // padding does not work spacing: 16; width: parent.width; RalewayRegular { - x: 16; // padding does not work + x: margins.paddings; // padding does not work size: 16; color: "white"; text: root.title; @@ -53,8 +67,9 @@ Rectangle { Separator { visible: root.showTitle() } ColumnLayout { - x: 16; // padding does not work + x: margins.paddings; // padding does not work spacing: 16; + width: parent.width; // mute is in its own row RowLayout { @@ -93,52 +108,106 @@ Rectangle { Separator {} RowLayout { + x: margins.paddings; + width: parent.width - margins.paddings*2 + height: 28 + spacing: 0 HiFiGlyphs { + Layout.minimumWidth: margins.sizeCheckBox + Layout.maximumWidth: margins.sizeCheckBox text: hifi.glyphs.mic; color: hifi.colors.primaryHighlight; anchors.verticalCenter: parent.verticalCenter; - size: 28; + size: 36; } RalewayRegular { + Layout.minimumWidth: margins.sizeText + margins.sizeLevel + Layout.maximumWidth: margins.sizeText + margins.sizeLevel anchors.verticalCenter: parent.verticalCenter; size: 16; color: hifi.colors.lightGrayText; text: qsTr("CHOOSE INPUT DEVICE"); } + + RalewayRegular { + Layout.minimumWidth: margins.sizeDesktop + Layout.maximumWidth: margins.sizeDesktop + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("DESKTOP"); + } + + RalewayRegular { + Layout.minimumWidth: margins.sizeVR + Layout.maximumWidth: margins.sizeVR + Layout.alignment: Qt.AlignRight + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("VR"); + } } ListView { - anchors { left: parent.left; right: parent.right; leftMargin: 70 } + id: inputView + width: parent.width - margins.paddings*2 + x: margins.paddings height: 125; spacing: 0; snapMode: ListView.SnapToItem; clip: true; model: Audio.devices.input; - delegate: Item { - width: parent.width; + delegate: RowLayout { + width: inputView.width; height: 36; - + spacing: 0 + + RalewaySemiBold { + Layout.minimumWidth: margins.sizeCheckBox + margins.sizeText + Layout.maximumWidth: margins.sizeCheckBox + margins.sizeText + Layout.alignment: Qt.AlignVCenter + clip: true + size: 16; + color: "white"; + text: devicename; + } + + //placeholder for invisible level + Item { + Layout.minimumWidth: margins.sizeLevel + Layout.maximumWidth: margins.sizeLevel + Layout.alignment: Qt.AlignVCenter + height: 8; + InputLevel { + visible: (isVR && selectedHMD) || (!isVR && selectedDesktop); + anchors.fill: parent + } + } AudioControls.CheckBox { - id: checkbox - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - text: display; - wrap: false; - checked: selected; - enabled: false; + Layout.minimumWidth: margins.sizeDesktop + Layout.maximumWidth: margins.sizeDesktop + leftPadding: margins.sizeDesktop - implicitWidth/2 + Layout.alignment: Qt.AlignCenter + checked: selectedDesktop; + onClicked: { + if (checked) { + Audio.setInputDevice(info, false); + } + } } - MouseArea { - anchors.fill: checkbox - onClicked: Audio.setInputDevice(info); - } - - InputLevel { - id: level; - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 30 - visible: selected; + AudioControls.CheckBox { + Layout.minimumWidth: margins.sizeVR + Layout.maximumWidth: margins.sizeVR + Layout.alignment: Qt.AlignCenter + leftPadding: margins.sizeVR - implicitWidth/2 + checked: selectedHMD; + onClicked: { + if (checked) { + Audio.setInputDevice(info, true); + } + } } } } @@ -146,51 +215,104 @@ Rectangle { Separator {} RowLayout { - Column { - RowLayout { - HiFiGlyphs { - text: hifi.glyphs.unmuted; - color: hifi.colors.primaryHighlight; - anchors.verticalCenter: parent.verticalCenter; - size: 36; - } - RalewayRegular { - anchors.verticalCenter: parent.verticalCenter; - size: 16; - color: hifi.colors.lightGrayText; - text: qsTr("CHOOSE OUTPUT DEVICE"); - } - } + x: margins.paddings; + width: parent.width - margins.paddings*2 + height: 28 + spacing: 0 + HiFiGlyphs { + Layout.minimumWidth: margins.sizeCheckBox + Layout.maximumWidth: margins.sizeCheckBox + text: hifi.glyphs.unmuted; + color: hifi.colors.primaryHighlight; + anchors.verticalCenter: parent.verticalCenter; + size: 28; + } + RalewayRegular { + Layout.minimumWidth: margins.sizeText + margins.sizeLevel + Layout.maximumWidth: margins.sizeText + margins.sizeLevel + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("CHOOSE OUTPUT DEVICE"); + } - PlaySampleSound { anchors { left: parent.left; leftMargin: 60 }} + RalewayRegular { + Layout.minimumWidth: margins.sizeDesktop + Layout.maximumWidth: margins.sizeDesktop + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("DESKTOP"); + } + + RalewayRegular { + Layout.minimumWidth: margins.sizeVR + Layout.maximumWidth: margins.sizeVR + Layout.alignment: Qt.AlignRight + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("VR"); } } ListView { - anchors { left: parent.left; right: parent.right; leftMargin: 70 } - height: Math.min(250, contentHeight); + id: outputView + width: parent.width - margins.paddings*2 + x: margins.paddings + height: Math.min(220, contentHeight); spacing: 0; snapMode: ListView.SnapToItem; clip: true; model: Audio.devices.output; - delegate: Item { - width: parent.width; + delegate: RowLayout { + width: inputView.width; height: 36; + spacing: 0 - AudioControls.CheckBox { - id: checkbox - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - text: display; - checked: selected; - enabled: false; + RalewaySemiBold { + Layout.minimumWidth: margins.sizeCheckBox + margins.sizeText + Layout.maximumWidth: margins.sizeCheckBox + margins.sizeText + Layout.alignment: Qt.AlignVCenter + clip: true + size: 16; + color: "white"; + text: devicename; } - MouseArea { - anchors.fill: checkbox - onClicked: Audio.setOutputDevice(info); + //placeholder for invisible level + Item { + Layout.minimumWidth: margins.sizeLevel + Layout.maximumWidth: margins.sizeLevel + Layout.alignment: Qt.AlignVCenter + height: 8; + } + AudioControls.CheckBox { + Layout.minimumWidth: margins.sizeDesktop + Layout.maximumWidth: margins.sizeDesktop + leftPadding: margins.sizeDesktop - implicitWidth/2 + Layout.alignment: Qt.AlignCenter + checked: selectedDesktop; + onClicked: { + if (checked) { + Audio.setOutputDevice(info, false); + } + } + } + AudioControls.CheckBox { + Layout.minimumWidth: margins.sizeVR + Layout.maximumWidth: margins.sizeVR + Layout.alignment: Qt.AlignCenter + leftPadding: margins.sizeVR - implicitWidth/2 + checked: selectedHMD; + onClicked: { + if (checked) { + Audio.setOutputDevice(info, true); + } + } } } } + PlaySampleSound { anchors { left: parent.left; leftMargin: margins.paddings }} } } diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml index 1f632ac479..77c001816f 100644 --- a/interface/resources/qml/hifi/audio/CheckBox.qml +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -9,10 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 import "../../controls-uit" as HifiControls -HifiControls.CheckBox { +HifiControls.CheckBox2 { color: "white" } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 9719c23885..f9c1a95fb5 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -135,10 +135,10 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) { DependencyManager::get()->setReverbOptions(options); } -void Audio::setInputDevice(const QAudioDeviceInfo& device) { - _devices.chooseInputDevice(device); +void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) { + _devices.chooseInputDevice(device, isHMD); } -void Audio::setOutputDevice(const QAudioDeviceInfo& device) { - _devices.chooseOutputDevice(device); +void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { + _devices.chooseOutputDevice(device, isHMD); } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index bd40de4303..abd2312cf0 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -50,8 +50,8 @@ public: void showMicMeter(bool show); void setInputVolume(float volume); - Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device); - Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device); + Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD); + Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD); Q_INVOKABLE void setReverb(bool enable); Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index f2e6dbf4d7..e31641389c 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -53,22 +53,28 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) { } QHash AudioDeviceList::_roles { - { Qt::DisplayRole, "display" }, - { Qt::CheckStateRole, "selected" }, - { Qt::UserRole, "info" } + { AudioDeviceList::DeviceNameRole, "devicename" }, + { AudioDeviceList::SelectedDesktopRole, "selectedDesktop" }, + { AudioDeviceList::SelectedHMDRole, "selectedHMD" }, + { AudioDeviceList::DeviceInfoRole, "info" } }; + Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; +AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) {} + QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() >= _devices.size()) { return QVariant(); } - if (role == Qt::DisplayRole) { + if (role == DeviceNameRole) { return _devices.at(index.row()).display; - } else if (role == Qt::CheckStateRole) { - return _devices.at(index.row()).selected; - } else if (role == Qt::UserRole) { + } else if (role == SelectedDesktopRole) { + return _devices.at(index.row()).selectedDesktop; + } else if (role == SelectedHMDRole) { + return _devices.at(index.row()).selectedHMD; + } else if (role == DeviceInfoRole) { return QVariant::fromValue(_devices.at(index.row()).info); } else { return QVariant(); @@ -109,36 +115,47 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) { #endif } -void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { - auto oldDevice = _selectedDevice; - _selectedDevice = device; +void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD) { + auto oldDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + selectedDevice = device; for (auto i = 0; i < _devices.size(); ++i) { AudioDevice& device = _devices[i]; - if (device.selected && device.info != _selectedDevice) { - device.selected = false; - } else if (device.info == _selectedDevice) { - device.selected = true; + bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; + if (isSelected && device.info != selectedDevice) { + isSelected = false; + } else if (device.info == selectedDevice) { + isSelected = true; } } - emit deviceChanged(_selectedDevice); + emit deviceChanged(selectedDevice); emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); } -void AudioDeviceList::onDevicesChanged(const QList& devices) { +void AudioDeviceList::onDevicesChanged(const QList& devices, bool isHMD) { + QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + + const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; beginResetModel(); _devices.clear(); foreach(const QAudioDeviceInfo& deviceInfo, devices) { AudioDevice device; + bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; device.info = deviceInfo; device.display = device.info.deviceName() .replace("High Definition", "HD") .remove("Device") .replace(" )", ")"); - device.selected = (device.info == _selectedDevice); + if (!selectedDevice.isNull()) { + isSelected = (device.info == selectedDevice); + } else { + //no selected device for context. fallback to saved + isSelected = (device.info.deviceName() == savedDeviceName); + } _devices.push_back(device); } @@ -151,11 +168,18 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { connect(client.data(), &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection); connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection); + _inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput), contextIsHMD); + _outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput), contextIsHMD); + // connections are made after client is initialized, so we must also fetch the devices - _inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput)); - _outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput)); - _inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput)); - _outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput)); + const QList& devicesInput = client->getAudioDevices(QAudio::AudioInput); + const QList& devicesOutput = client->getAudioDevices(QAudio::AudioOutput); + //setup HMD devices + _inputs.onDevicesChanged(devicesInput, true); + _outputs.onDevicesChanged(devicesOutput, true); + //setup Desktop devices + _inputs.onDevicesChanged(devicesInput, false); + _outputs.onDevicesChanged(devicesOutput, false); } void AudioDevices::onContextChanged(const QString& context) { @@ -163,10 +187,11 @@ void AudioDevices::onContextChanged(const QString& context) { _outputs.resetDevice(_contextIsHMD); } -void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { +void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, + const QAudioDeviceInfo& previousDevice, bool isHMD) { QString deviceName = device.isNull() ? QString() : device.deviceName(); - auto& setting = getSetting(_contextIsHMD, mode); + auto& setting = getSetting(isHMD, mode); // check for a previous device auto wasDefault = setting.get().isNull(); @@ -202,42 +227,94 @@ 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); + onDeviceSelected(QAudio::AudioInput, device, + _contextIsHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice, + _contextIsHMD); _requestedInputDevice = QAudioDeviceInfo(); } - _inputs.onDeviceChanged(device); + _inputs.onDeviceChanged(device, _contextIsHMD); } else { // if (mode == QAudio::AudioOutput) if (_requestedOutputDevice == device) { - onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice); + onDeviceSelected(QAudio::AudioOutput, device, + _contextIsHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice, + _contextIsHMD); _requestedOutputDevice = QAudioDeviceInfo(); } - _outputs.onDeviceChanged(device); + _outputs.onDeviceChanged(device, _contextIsHMD); } } void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices) { static std::once_flag once; + std::call_once(once, [&] { + //readout settings + auto client = DependencyManager::get(); + + _inputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioInput); + _inputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioInput); + + //fallback to default device + if (_inputs._desktopSavedDeviceName.isEmpty()) { + _inputs._desktopSavedDeviceName = client->getActiveAudioDevice(QAudio::AudioInput).deviceName(); + } + //fallback to desktop device + if (_inputs._hmdSavedDeviceName.isEmpty()) { + _inputs._hmdSavedDeviceName = _inputs._desktopSavedDeviceName; + } + + _outputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioOutput); + _outputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioOutput); + + if (_outputs._desktopSavedDeviceName.isEmpty()) { + _outputs._desktopSavedDeviceName = client->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); + } + if (_outputs._hmdSavedDeviceName.isEmpty()) { + _outputs._hmdSavedDeviceName = _outputs._desktopSavedDeviceName; + } + onContextChanged(QString()); + }); + + //set devices for both contexts if (mode == QAudio::AudioInput) { - _inputs.onDevicesChanged(devices); + _inputs.onDevicesChanged(devices, _contextIsHMD); + _inputs.onDevicesChanged(devices, !_contextIsHMD); } else { // if (mode == QAudio::AudioOutput) - _outputs.onDevicesChanged(devices); + _outputs.onDevicesChanged(devices, _contextIsHMD); + _outputs.onDevicesChanged(devices, !_contextIsHMD); } - std::call_once(once, [&] { onContextChanged(QString()); }); } -void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device) { - auto client = DependencyManager::get(); - _requestedInputDevice = device; - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Q_ARG(QAudio::Mode, QAudio::AudioInput), - Q_ARG(const QAudioDeviceInfo&, device)); +void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD) { + //check if current context equals device to change + if (_contextIsHMD == isHMD) { + auto client = DependencyManager::get(); + _requestedInputDevice = device; + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Q_ARG(QAudio::Mode, QAudio::AudioInput), + Q_ARG(const QAudioDeviceInfo&, device)); + } else { + //context is different. just save device in settings + onDeviceSelected(QAudio::AudioInput, device, + isHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice, + isHMD); + _inputs.onDeviceChanged(device, isHMD); + } } -void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device) { - auto client = DependencyManager::get(); - _requestedOutputDevice = device; - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Q_ARG(QAudio::Mode, QAudio::AudioOutput), - Q_ARG(const QAudioDeviceInfo&, device)); +void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { + //check if current context equals device to change + if (_contextIsHMD == isHMD) { + auto client = DependencyManager::get(); + _requestedOutputDevice = device; + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Q_ARG(QAudio::Mode, QAudio::AudioOutput), + Q_ARG(const QAudioDeviceInfo&, device)); + } else { + //context is different. just save device in settings + onDeviceSelected(QAudio::AudioOutput, device, + isHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice, + isHMD); + _outputs.onDeviceChanged(device, isHMD); + } } diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 3278a53374..5399123329 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -22,14 +22,22 @@ class AudioDevice { public: QAudioDeviceInfo info; QString display; - bool selected { false }; + bool selectedDesktop { false }; + bool selectedHMD { false }; }; class AudioDeviceList : public QAbstractListModel { Q_OBJECT + enum AudioDeviceRoles { + DeviceNameRole = Qt::UserRole + 1, + SelectedDesktopRole, + SelectedHMDRole, + DeviceInfoRole + }; + public: - AudioDeviceList(QAudio::Mode mode) : _mode(mode) {} + AudioDeviceList(QAudio::Mode mode); int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); } QHash roleNames() const override { return _roles; } @@ -45,8 +53,8 @@ signals: void deviceChanged(const QAudioDeviceInfo& device); private slots: - void onDeviceChanged(const QAudioDeviceInfo& device); - void onDevicesChanged(const QList& devices); + void onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD); + void onDevicesChanged(const QList& devices, bool isHMD); private: friend class AudioDevices; @@ -54,8 +62,11 @@ private: static QHash _roles; static Qt::ItemFlags _flags; const QAudio::Mode _mode; - QAudioDeviceInfo _selectedDevice; + QAudioDeviceInfo _selectedDesktopDevice; + QAudioDeviceInfo _selectedHMDDevice; QList _devices; + QString _hmdSavedDeviceName; + QString _desktopSavedDeviceName; }; class Audio; @@ -67,15 +78,16 @@ class AudioDevices : public QObject { public: AudioDevices(bool& contextIsHMD); - void chooseInputDevice(const QAudioDeviceInfo& device); - void chooseOutputDevice(const QAudioDeviceInfo& device); + void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD); + void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD); signals: void nop(); private slots: void onContextChanged(const QString& context); - void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice); + void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, + const QAudioDeviceInfo& previousDevice, bool isHMD); void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); void onDevicesChanged(QAudio::Mode mode, const QList& devices); From bef093b8e53a70fffeb9f7a6254b859dc85ec1b8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 11 Aug 2017 13:40:30 -0700 Subject: [PATCH 002/202] add handling for completed bake --- assignment-client/src/assets/AssetServer.cpp | 109 ++++++++++++++++++- assignment-client/src/assets/AssetServer.h | 8 +- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 3886ff8d92..911a650cf2 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -198,7 +199,6 @@ void AssetServer::completeSetup() { qCritical() << "Asset Server assignment will not continue because mapping file could not be loaded."; setFinished(true); } - } void AssetServer::cleanupUnmappedFiles() { @@ -785,3 +785,110 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { } } } + +static const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; + +void AssetServer::handleCompletedBake(AssetHash originalAssetHash, QDir temporaryOutputDir) { + // enumerate the baking result files in the temporary directory + QDirIterator dirIterator(temporaryOutputDir.absolutePath(), QDir::Files, QDirIterator::Subdirectories); + + bool errorCompletingBake { false }; + + while (dirIterator.hasNext()) { + QString filePath = dirIterator.next(); + + // figure out the hash for the contents of this file + QFile file(filePath); + + AssetHash bakedFileHash; + + if (file.open(QIODevice::ReadOnly)) { + QCryptographicHash hasher(QCryptographicHash::Sha256); + + if (hasher.addData(&file)) { + bakedFileHash = hasher.result().toHex(); + } else { + // stop handling this bake, couldn't hash the contents of the file + errorCompletingBake = true; + break; + } + + // first check that we don't already have this bake file in our list + auto bakeFileDestination = _filesDirectory.absoluteFilePath(bakedFileHash); + if (!QFile::exists(bakeFileDestination)) { + // copy each to our files folder (with the hash as their filename) + if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) { + // stop handling this bake, couldn't copy the bake file into our files directory + errorCompletingBake = true; + break; + } + } + + // setup the mapping for this bake file + auto relativeFilePath = temporaryOutputDir.relativeFilePath(filePath); + + static const QString BAKED_ASSET_SIMPLE_NAME = "asset.fbx"; + + if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) { + // for an FBX file, we replace the filename with the simple name + // (to handle the case where two mapped assets have the same hash but different names) + relativeFilePath = BAKED_ASSET_SIMPLE_NAME; + } + + QString bakeMapping = HIDDEN_BAKED_CONTENT_FOLDER + originalAssetHash + "/" + relativeFilePath; + + // add a mapping (under the hidden baked folder) for this file resulting from the bake + if (setMapping(bakeMapping , bakedFileHash)) { + qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash; + } else { + // stop handling this bake, couldn't add a mapping for this bake file + errorCompletingBake = true; + break; + } + } else { + // stop handling this bake, we couldn't open one of the files for reading + errorCompletingBake = true; + break; + } + } + + if (!errorCompletingBake) { + // create the meta file to store which version of the baking process we just completed + createMetaFile(originalAssetHash); + } else { + qWarning() << "Could not complete bake for" << originalAssetHash; + } +} + +bool AssetServer::createMetaFile(AssetHash originalAssetHash) { + // construct the JSON that will be in the meta file + QJsonObject metaFileObject; + + static const int BAKE_VERSION = 1; + static const QString VERSION_KEY = "version"; + + metaFileObject[VERSION_KEY] = BAKE_VERSION; + + QJsonDocument metaFileDoc; + metaFileDoc.setObject(metaFileObject); + + auto metaFileJSON = metaFileDoc.toJson(); + + // get a hash for the contents of the meta-file + AssetHash metaFileHash = QCryptographicHash::hash(metaFileJSON, QCryptographicHash::Sha256).toHex(); + + // create the meta file in our files folder, named by the hash of its contents + QFile metaFile(_filesDirectory.absoluteFilePath(metaFileHash)); + + if (metaFile.open(QIODevice::WriteOnly)) { + metaFile.write(metaFileJSON); + metaFile.close(); + + // add a mapping to the meta file so it doesn't get deleted because it is unmapped + auto metaFileMapping = HIDDEN_BAKED_CONTENT_FOLDER + originalAssetHash + "/" + "meta.json"; + + return setMapping(metaFileMapping, metaFileHash); + } else { + return false; + } +} diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 132fb51433..191ad25b1f 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -60,9 +60,15 @@ private: /// Rename mapping from `oldPath` to `newPath`. Returns true if successful bool renameMapping(AssetPath oldPath, AssetPath newPath); - // deletes any unmapped files from the local asset directory + /// Delete any unmapped files from the local asset directory void cleanupUnmappedFiles(); + /// Move baked content for asset to baked directory and update baked status + void handleCompletedBake(AssetHash originalAssetHash, QDir temporaryOutputDir); + + /// Create meta file to describe baked content for original asset + bool createMetaFile(AssetHash originalAssetHash); + Mappings _fileMappings; QDir _resourcesDirectory; From db3524a48fcc962ad3ce764d6b1ba2eb20a47a12 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 11 Aug 2017 14:26:27 -0700 Subject: [PATCH 003/202] add re-direct of get mapping for baked assets --- assignment-client/src/assets/AssetServer.cpp | 53 +++++++++++++++++-- .../networking/src/udt/PacketHeaders.cpp | 2 + libraries/networking/src/udt/PacketHeaders.h | 3 +- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 911a650cf2..c0729dcf53 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -264,14 +265,61 @@ void AssetServer::handleAssetMappingOperation(QSharedPointer me nodeList->sendPacketList(std::move(replyPacket), *senderNode); } +static const QStringList BAKEABLE_MODEL_EXTENSIONS = { ".fbx" }; +static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; +static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; + void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) { QString assetPath = message.readString(); auto it = _fileMappings.find(assetPath); if (it != _fileMappings.end()) { - auto assetHash = it->toString(); + + // check if we should re-direct to a baked asset + + // first, figure out from the mapping extension what type of file this is + auto assetPathExtension = assetPath.right(assetPath.lastIndexOf('.')).toLower(); + QString bakedRootFile; + + if (BAKEABLE_MODEL_EXTENSIONS.contains(assetPathExtension)) { + bakedRootFile = BAKED_MODEL_SIMPLE_NAME; + } else if (QImageReader::supportedImageFormats().contains(assetPathExtension.toLocal8Bit())) { + bakedRootFile = BAKED_TEXTURE_SIMPLE_NAME; + } + + auto originalAssetHash = it->toString(); + QString redirectedAssetHash; + QString bakedAssetPath; + quint8 wasRedirected = false; + + if (!bakedRootFile.isEmpty()) { + // we ran into an asset for which we could have a baked version, let's check if it's ready + bakedAssetPath = "/.baked/" + originalAssetHash + "/" + bakedRootFile; + auto bakedIt = _fileMappings.find(bakedAssetPath); + + if (bakedIt != _fileMappings.end()) { + // we found a baked version of the requested asset to serve, redirect to that + redirectedAssetHash = bakedIt->toString(); + wasRedirected = true; + } + } + replyPacket.writePrimitive(AssetServerError::NoError); - replyPacket.write(QByteArray::fromHex(assetHash.toUtf8())); + + if (wasRedirected) { + qDebug() << "Writing re-directed hash for" << originalAssetHash << "to" << redirectedAssetHash; + replyPacket.write(QByteArray::fromHex(redirectedAssetHash.toUtf8())); + + // add a flag saying that this mapping request was redirect + replyPacket.writePrimitive(wasRedirected); + + // include the re-directed path in case the caller needs to make relative path requests for the baked asset + replyPacket.write(bakedAssetPath.toUtf8()); + + } else { + replyPacket.write(QByteArray::fromHex(originalAssetHash.toUtf8())); + replyPacket.writePrimitive(wasRedirected); + } } else { replyPacket.writePrimitive(AssetServerError::AssetNotFound); } @@ -826,7 +874,6 @@ void AssetServer::handleCompletedBake(AssetHash originalAssetHash, QDir temporar // setup the mapping for this bake file auto relativeFilePath = temporaryOutputDir.relativeFilePath(filePath); - static const QString BAKED_ASSET_SIMPLE_NAME = "asset.fbx"; if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) { diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 241ccaf5d6..f453d096b6 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -42,6 +42,8 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: return 18; // ICE Server Heartbeat signing + case PacketType::AssetMappingOperationReply: + return static_cast(AssetServerPacketVersion::RedirectedMappings); case PacketType::AssetGetInfo: case PacketType::AssetGet: case PacketType::AssetUpload: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index e2304e62f7..ea831bd415 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -266,7 +266,8 @@ enum class EntityQueryPacketVersion: PacketVersion { enum class AssetServerPacketVersion: PacketVersion { VegasCongestionControl = 19, - RangeRequestSupport + RangeRequestSupport, + RedirectedMappings }; enum class AvatarMixerPacketVersion : PacketVersion { From b22745d57a90df73b54f8d967e04c0f3fd6d92f4 Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 12 Aug 2017 12:52:35 +0200 Subject: [PATCH 004/202] Fix binding loops --- .../resources/qml/controls-uit/CheckBox2.qml | 4 +-- interface/resources/qml/hifi/audio/Audio.qml | 30 +++++++------------ 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox2.qml b/interface/resources/qml/controls-uit/CheckBox2.qml index 612e8d2662..ffdb85c1a1 100644 --- a/interface/resources/qml/controls-uit/CheckBox2.qml +++ b/interface/resources/qml/controls-uit/CheckBox2.qml @@ -32,8 +32,8 @@ CheckBox { indicator: Rectangle { id: box - width: boxSize - height: boxSize + implicitWidth: boxSize + implicitHeight: boxSize radius: boxRadius x: checkBox.leftPadding y: parent.height / 2 - height / 2 diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 3d3a9ceeaa..aa2e0e32d5 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -41,13 +41,13 @@ Rectangle { //recalculates dynamically in case of UI size is changed QtObject { id: margins - property real paddings: parent.width / 20.25 + property real paddings: root.width / 20.25 - property real sizeCheckBox: parent.width / 13.5 - property real sizeText: parent.width / 2.5 - property real sizeLevel: parent.width / 5.8 - property real sizeDesktop: parent.width / 5.8 - property real sizeVR: parent.width / 13.5 + property real sizeCheckBox: root.width / 13.5 + property real sizeText: root.width / 2.5 + property real sizeLevel: root.width / 5.8 + property real sizeDesktop: root.width / 5.8 + property real sizeVR: root.width / 13.5 } Column { @@ -166,7 +166,6 @@ Rectangle { RalewaySemiBold { Layout.minimumWidth: margins.sizeCheckBox + margins.sizeText Layout.maximumWidth: margins.sizeCheckBox + margins.sizeText - Layout.alignment: Qt.AlignVCenter clip: true size: 16; color: "white"; @@ -177,7 +176,6 @@ Rectangle { Item { Layout.minimumWidth: margins.sizeLevel Layout.maximumWidth: margins.sizeLevel - Layout.alignment: Qt.AlignVCenter height: 8; InputLevel { visible: (isVR && selectedHMD) || (!isVR && selectedDesktop); @@ -187,8 +185,7 @@ Rectangle { AudioControls.CheckBox { Layout.minimumWidth: margins.sizeDesktop Layout.maximumWidth: margins.sizeDesktop - leftPadding: margins.sizeDesktop - implicitWidth/2 - Layout.alignment: Qt.AlignCenter + leftPadding: margins.sizeDesktop/2 - boxSize/2 checked: selectedDesktop; onClicked: { if (checked) { @@ -200,8 +197,7 @@ Rectangle { AudioControls.CheckBox { Layout.minimumWidth: margins.sizeVR Layout.maximumWidth: margins.sizeVR - Layout.alignment: Qt.AlignCenter - leftPadding: margins.sizeVR - implicitWidth/2 + leftPadding: margins.sizeVR/2 - boxSize/2 checked: selectedHMD; onClicked: { if (checked) { @@ -266,14 +262,13 @@ Rectangle { clip: true; model: Audio.devices.output; delegate: RowLayout { - width: inputView.width; + width: outputView.width; height: 36; spacing: 0 RalewaySemiBold { Layout.minimumWidth: margins.sizeCheckBox + margins.sizeText Layout.maximumWidth: margins.sizeCheckBox + margins.sizeText - Layout.alignment: Qt.AlignVCenter clip: true size: 16; color: "white"; @@ -284,14 +279,12 @@ Rectangle { Item { Layout.minimumWidth: margins.sizeLevel Layout.maximumWidth: margins.sizeLevel - Layout.alignment: Qt.AlignVCenter height: 8; } AudioControls.CheckBox { Layout.minimumWidth: margins.sizeDesktop Layout.maximumWidth: margins.sizeDesktop - leftPadding: margins.sizeDesktop - implicitWidth/2 - Layout.alignment: Qt.AlignCenter + leftPadding: margins.sizeDesktop/2 - boxSize/2 checked: selectedDesktop; onClicked: { if (checked) { @@ -302,8 +295,7 @@ Rectangle { AudioControls.CheckBox { Layout.minimumWidth: margins.sizeVR Layout.maximumWidth: margins.sizeVR - Layout.alignment: Qt.AlignCenter - leftPadding: margins.sizeVR - implicitWidth/2 + leftPadding: margins.sizeVR/2 - boxSize/2 checked: selectedHMD; onClicked: { if (checked) { From 03e952ec38f549c3a54a053c69ec8573a45dc170 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 14 Aug 2017 14:09:35 -0700 Subject: [PATCH 005/202] fix extension check for get mapping redirect --- assignment-client/src/assets/AssetServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index c0729dcf53..f82ef702f0 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -278,7 +278,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode // check if we should re-direct to a baked asset // first, figure out from the mapping extension what type of file this is - auto assetPathExtension = assetPath.right(assetPath.lastIndexOf('.')).toLower(); + auto assetPathExtension = assetPath.mid(assetPath.lastIndexOf('.')).toLower(); QString bakedRootFile; if (BAKEABLE_MODEL_EXTENSIONS.contains(assetPathExtension)) { @@ -286,7 +286,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode } else if (QImageReader::supportedImageFormats().contains(assetPathExtension.toLocal8Bit())) { bakedRootFile = BAKED_TEXTURE_SIMPLE_NAME; } - + auto originalAssetHash = it->toString(); QString redirectedAssetHash; QString bakedAssetPath; From 96672becc68dc2fac3db410017aff5d6aea68dad Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Aug 2017 15:19:20 -0700 Subject: [PATCH 006/202] read redirected path info in MappingRequest --- assignment-client/src/assets/AssetServer.cpp | 2 +- libraries/networking/src/MappingRequest.cpp | 11 +++++++++++ libraries/networking/src/MappingRequest.h | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index f82ef702f0..2082856f56 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -314,7 +314,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode replyPacket.writePrimitive(wasRedirected); // include the re-directed path in case the caller needs to make relative path requests for the baked asset - replyPacket.write(bakedAssetPath.toUtf8()); + replyPacket.writeString(bakedAssetPath); } else { replyPacket.write(QByteArray::fromHex(originalAssetHash.toUtf8())); diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index 810b5b376d..bd7c05bdf7 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -87,6 +87,17 @@ void GetMappingRequest::doStart() { if (!_error) { _hash = message->read(SHA256_HASH_LENGTH).toHex(); + + // check the boolean to see if this request got re-directed + quint8 wasRedirected; + message->readPrimitive(&wasRedirected); + _wasRedirected = wasRedirected; + + // if it did grab that re-directed path + if (_wasRedirected) { + _redirectedPath = message->readString(); + } + } emit finished(this); }); diff --git a/libraries/networking/src/MappingRequest.h b/libraries/networking/src/MappingRequest.h index 85b68e2427..e1cd8a39e8 100644 --- a/libraries/networking/src/MappingRequest.h +++ b/libraries/networking/src/MappingRequest.h @@ -53,6 +53,8 @@ public: GetMappingRequest(const AssetPath& path); AssetHash getHash() const { return _hash; } + AssetPath getRedirectedPath() const { return _redirectedPath; } + bool wasRedirected() const { return _wasRedirected; } signals: void finished(GetMappingRequest* thisRequest); @@ -62,6 +64,10 @@ private: AssetPath _path; AssetHash _hash; + + + AssetPath _redirectedPath; + bool _wasRedirected { false }; }; class SetMappingRequest : public MappingRequest { From 100ac659758ef05a914498bef023f8b1f6de1c59 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Aug 2017 15:27:44 -0700 Subject: [PATCH 007/202] store relative path URL in resource request on redirect --- libraries/networking/src/AssetRequest.h | 2 ++ libraries/networking/src/AssetResourceRequest.cpp | 5 +++++ libraries/networking/src/ResourceRequest.h | 1 + libraries/script-engine/src/AssetScriptingInterface.cpp | 1 - 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index df5cf80ecd..a7213a90d7 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -21,6 +21,8 @@ #include "ByteRange.h" +const QString ATP_SCHEME { "atp:" }; + class AssetRequest : public QObject { Q_OBJECT public: diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index a41283cc0d..ecaa6efc1e 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -91,6 +91,11 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_SUCCESS); + // if we got a redirected path we need to store that with the resource request as relative path URL + if (request->wasRedirected()) { + _relativePathURL = ATP_SCHEME + request->getRedirectedPath(); + } + break; default: { switch (request->getError()) { diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 3ee86025a2..bae0882e97 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -84,6 +84,7 @@ protected: virtual void doSend() = 0; QUrl _url; + QUrl _relativePathURL; State _state { NotStarted }; Result _result; QByteArray _data; diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 65259987c4..51b3d7ffbe 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -55,7 +55,6 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) { - const QString ATP_SCHEME { "atp:" }; if (!urlString.startsWith(ATP_SCHEME)) { return; From a59103aad5f9fab61c3226dc57d1c72532e53ad3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 15 Aug 2017 15:38:47 -0700 Subject: [PATCH 008/202] truncate filename for relative path URL in RR --- libraries/networking/src/AssetResourceRequest.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index ecaa6efc1e..e228582ca9 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -94,6 +94,9 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { // if we got a redirected path we need to store that with the resource request as relative path URL if (request->wasRedirected()) { _relativePathURL = ATP_SCHEME + request->getRedirectedPath(); + + // truncate the filename for the re-directed asset so we actually have a path + _relativePathURL = _relativePathURL.adjusted(QUrl::RemoveFilename); } break; From d6c426d064b5313965369dade6504f8968680d26 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 17 Aug 2017 17:17:02 +0200 Subject: [PATCH 009/202] Reworking according new design --- .../resources/qml/controls-uit/CheckBox2.qml | 1 - interface/resources/qml/hifi/audio/Audio.qml | 154 +++++------------- 2 files changed, 37 insertions(+), 118 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox2.qml b/interface/resources/qml/controls-uit/CheckBox2.qml index ffdb85c1a1..7bd69f0b19 100644 --- a/interface/resources/qml/controls-uit/CheckBox2.qml +++ b/interface/resources/qml/controls-uit/CheckBox2.qml @@ -103,7 +103,6 @@ CheckBox { wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight enabled: checkBox.enabled - horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter leftPadding: checkBox.indicator.width + checkBox.spacing } diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index aa2e0e32d5..fa34ffb6d1 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -50,21 +50,38 @@ Rectangle { property real sizeVR: root.width / 13.5 } + TabBar { + id: bar + spacing: 0 + width: parent.width + height: 36 + + AudioControls.AudioTabButton { + height: parent.height + text: qsTr("Desktop") + } + AudioControls.AudioTabButton { + height: parent.height + text: qsTr("VR") + } + } + Column { - y: 16; // padding does not work - spacing: 16; + spacing: 12; + anchors.topMargin: 8 + anchors.top: bar.bottom + anchors.bottom: parent.bottom + anchors.bottomMargin: 5 width: parent.width; RalewayRegular { - x: margins.paddings; // padding does not work + x: margins.paddings; size: 16; color: "white"; - text: root.title; - - visible: root.showTitle(); + text: qsTr("Mic Settings") } - Separator { visible: root.showTitle() } + Separator { } ColumnLayout { x: margins.paddings; // padding does not work @@ -128,80 +145,29 @@ Rectangle { color: hifi.colors.lightGrayText; text: qsTr("CHOOSE INPUT DEVICE"); } - - RalewayRegular { - Layout.minimumWidth: margins.sizeDesktop - Layout.maximumWidth: margins.sizeDesktop - anchors.verticalCenter: parent.verticalCenter; - size: 16; - color: hifi.colors.lightGrayText; - text: qsTr("DESKTOP"); - } - - RalewayRegular { - Layout.minimumWidth: margins.sizeVR - Layout.maximumWidth: margins.sizeVR - Layout.alignment: Qt.AlignRight - anchors.verticalCenter: parent.verticalCenter; - size: 16; - color: hifi.colors.lightGrayText; - text: qsTr("VR"); - } } ListView { id: inputView width: parent.width - margins.paddings*2 x: margins.paddings - height: 125; - spacing: 0; + height: 145; + spacing: 4; snapMode: ListView.SnapToItem; clip: true; model: Audio.devices.input; delegate: RowLayout { width: inputView.width; - height: 36; spacing: 0 - RalewaySemiBold { - Layout.minimumWidth: margins.sizeCheckBox + margins.sizeText - Layout.maximumWidth: margins.sizeCheckBox + margins.sizeText - clip: true - size: 16; - color: "white"; - text: devicename; - } - - //placeholder for invisible level - Item { - Layout.minimumWidth: margins.sizeLevel - Layout.maximumWidth: margins.sizeLevel - height: 8; - InputLevel { - visible: (isVR && selectedHMD) || (!isVR && selectedDesktop); - anchors.fill: parent - } - } AudioControls.CheckBox { - Layout.minimumWidth: margins.sizeDesktop - Layout.maximumWidth: margins.sizeDesktop - leftPadding: margins.sizeDesktop/2 - boxSize/2 - checked: selectedDesktop; + Layout.fillWidth: true + checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; + boxRadius: boxSize/2 + text: devicename onClicked: { if (checked) { - Audio.setInputDevice(info, false); - } - } - } - - AudioControls.CheckBox { - Layout.minimumWidth: margins.sizeVR - Layout.maximumWidth: margins.sizeVR - leftPadding: margins.sizeVR/2 - boxSize/2 - checked: selectedHMD; - onClicked: { - if (checked) { - Audio.setInputDevice(info, true); + Audio.setInputDevice(info, bar.currentIndex === 1); } } } @@ -231,25 +197,6 @@ Rectangle { color: hifi.colors.lightGrayText; text: qsTr("CHOOSE OUTPUT DEVICE"); } - - RalewayRegular { - Layout.minimumWidth: margins.sizeDesktop - Layout.maximumWidth: margins.sizeDesktop - anchors.verticalCenter: parent.verticalCenter; - size: 16; - color: hifi.colors.lightGrayText; - text: qsTr("DESKTOP"); - } - - RalewayRegular { - Layout.minimumWidth: margins.sizeVR - Layout.maximumWidth: margins.sizeVR - Layout.alignment: Qt.AlignRight - anchors.verticalCenter: parent.verticalCenter; - size: 16; - color: hifi.colors.lightGrayText; - text: qsTr("VR"); - } } ListView { @@ -257,49 +204,22 @@ Rectangle { width: parent.width - margins.paddings*2 x: margins.paddings height: Math.min(220, contentHeight); - spacing: 0; + spacing: 4; snapMode: ListView.SnapToItem; clip: true; model: Audio.devices.output; delegate: RowLayout { width: outputView.width; - height: 36; spacing: 0 - RalewaySemiBold { - Layout.minimumWidth: margins.sizeCheckBox + margins.sizeText - Layout.maximumWidth: margins.sizeCheckBox + margins.sizeText - clip: true - size: 16; - color: "white"; - text: devicename; - } - - //placeholder for invisible level - Item { - Layout.minimumWidth: margins.sizeLevel - Layout.maximumWidth: margins.sizeLevel - height: 8; - } AudioControls.CheckBox { - Layout.minimumWidth: margins.sizeDesktop - Layout.maximumWidth: margins.sizeDesktop - leftPadding: margins.sizeDesktop/2 - boxSize/2 - checked: selectedDesktop; + Layout.fillWidth: true + boxRadius: boxSize/2 + checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; + text: devicename onClicked: { if (checked) { - Audio.setOutputDevice(info, false); - } - } - } - AudioControls.CheckBox { - Layout.minimumWidth: margins.sizeVR - Layout.maximumWidth: margins.sizeVR - leftPadding: margins.sizeVR/2 - boxSize/2 - checked: selectedHMD; - onClicked: { - if (checked) { - Audio.setOutputDevice(info, true); + Audio.setOutputDevice(info, bar.currentIndex === 1); } } } From f77b913a143edeac05b8987cb8b2aa225cb9f0ca Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 17 Aug 2017 20:55:00 +0200 Subject: [PATCH 010/202] Added input meter --- interface/resources/qml/hifi/audio/Audio.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index fa34ffb6d1..dfecfbb50f 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -158,7 +158,7 @@ Rectangle { model: Audio.devices.input; delegate: RowLayout { width: inputView.width; - spacing: 0 + spacing: 5 AudioControls.CheckBox { Layout.fillWidth: true @@ -171,6 +171,10 @@ Rectangle { } } } + InputLevel { + visible: (bar.currentIndex === 1 && selectedHMD && isVR) || + (bar.currentIndex === 0 && selectedDesktop && !isVR); + } } } From 855f9d8a950481d6b583129b8ff50c09a2b35766 Mon Sep 17 00:00:00 2001 From: vladest Date: Thu, 17 Aug 2017 21:47:48 +0200 Subject: [PATCH 011/202] Missed qml file added --- .../qml/hifi/audio/AudioTabButton.qml | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 interface/resources/qml/hifi/audio/AudioTabButton.qml diff --git a/interface/resources/qml/hifi/audio/AudioTabButton.qml b/interface/resources/qml/hifi/audio/AudioTabButton.qml new file mode 100644 index 0000000000..bf50f1ba26 --- /dev/null +++ b/interface/resources/qml/hifi/audio/AudioTabButton.qml @@ -0,0 +1,34 @@ +// +// AudioTabButton.qml +// qml/hifi/audio +// +// Created by Vlad Stelmahovsky on 8/16/2017 +// 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 +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import "../../controls-uit" as HifiControls +import "../../styles-uit" + +TabButton { + id: control + + HifiConstants { id: hifi; } + + contentItem: Text { + text: control.text + font: control.font + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: control.checked ? hifi.colors.baseGray : "black" + } +} From 8e73eae341693162b7c8041f1d13b31f07bd8695 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 14 Aug 2017 15:25:18 -0700 Subject: [PATCH 012/202] Update nvtt to only use a single thread --- libraries/image/src/image/Image.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index f274dc54f8..eaa55d0315 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -447,7 +447,18 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { MyErrorHandler errorHandler; outputOptions.setErrorHandler(&errorHandler); + class SequentialTaskDispatcher : public nvtt::TaskDispatcher { + public: + virtual void dispatch(nvtt::Task* task, void* context, int count) { + for (int i = 0; i < count; i++) { + task(context, i); + } + } + }; + + SequentialTaskDispatcher dispatcher; nvtt::Compressor compressor; + compressor.setTaskDispatcher(&dispatcher); compressor.process(inputOptions, compressionOptions, outputOptions); #else texture->autoGenerateMips(-1); From b862336d17e44f7fe457490fc091e17ba38e151a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 14 Aug 2017 15:25:33 -0700 Subject: [PATCH 013/202] Add PathUtils::generateTemporaryDir() --- libraries/shared/src/PathUtils.cpp | 13 +++++++++++++ libraries/shared/src/PathUtils.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 0636411f51..20e30e15e8 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -54,6 +54,19 @@ QString PathUtils::getAppLocalDataFilePath(const QString& filename) { return QDir(getAppLocalDataPath()).absoluteFilePath(filename); } +QString PathUtils::generateTemporaryDir() { + QDir rootTempDir = QDir::tempPath(); + QString appName = qApp->applicationName(); + for (auto i = 0; i < 64; ++i) { + auto now = std::chrono::system_clock::now().time_since_epoch().count(); + QDir tempDir = rootTempDir.filePath(appName + "-" + QString::number(now)); + if (tempDir.mkpath(".")) { + return tempDir.absolutePath(); + } + } + return ""; +} + QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { QString fileNameLowered = fileName.toLower(); foreach (const QString possibleExtension, possibleExtensions) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 3cb3cd3b63..8c4bcf2394 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -14,6 +14,7 @@ #include #include +#include #include "DependencyManager.h" @@ -36,6 +37,8 @@ public: static QString getAppDataFilePath(const QString& filename); static QString getAppLocalDataFilePath(const QString& filename); + static QString generateTemporaryDir(); + static Qt::CaseSensitivity getFSCaseSensitivity(); static QString stripFilename(const QUrl& url); // note: this is FS-case-sensitive version of parentURL.isParentOf(childURL) From 8d4ab5f751edeed5e57d62adca875bcc7278e720 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 14 Aug 2017 15:27:02 -0700 Subject: [PATCH 014/202] Update FBXBaker to take output directory and update oven to use it --- tools/oven/src/BakerCLI.cpp | 2 +- tools/oven/src/DomainBaker.cpp | 20 +++- tools/oven/src/DomainBaker.h | 2 + tools/oven/src/FBXBaker.cpp | 150 ++++++++++++++----------- tools/oven/src/FBXBaker.h | 30 ++--- tools/oven/src/ui/ModelBakeWidget.cpp | 25 ++++- tools/oven/src/ui/ResultsWindow.cpp | 1 + tools/oven/src/ui/SkyboxBakeWidget.cpp | 3 + 8 files changed, 143 insertions(+), 90 deletions(-) diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index 14eb9de150..7b65f8bbf0 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -41,7 +41,7 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString outputPath) { // create our appropiate baker if (isFBX) { - _baker = std::unique_ptr { new FBXBaker(inputUrl, outputPath, []() -> QThread* { return qApp->getNextWorkerThread(); }) }; + _baker = std::unique_ptr { new FBXBaker(inputUrl, []() -> QThread* { return qApp->getNextWorkerThread(); }, outputPath) }; _baker->moveToThread(qApp->getFBXBakerThread()); } else if (isSupportedImage) { _baker = std::unique_ptr { new TextureBaker(inputUrl, image::TextureUsage::CUBE_TEXTURE, outputPath) }; diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 03bc350f42..5dd7c20b2e 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -192,10 +192,18 @@ void DomainBaker::enumerateEntities() { // setup an FBXBaker for this URL, as long as we don't already have one if (!_modelBakers.contains(modelURL)) { + auto filename = modelURL.fileName(); + auto baseName = filename.left(filename.lastIndexOf('.')); + auto subDirName = "/" + baseName; + int i = 0; + while (QDir(_contentOutputPath + subDirName).exists()) { + subDirName = "/" + baseName + "-" + i++; + } QSharedPointer baker { - new FBXBaker(modelURL, _contentOutputPath, []() -> QThread* { + new FBXBaker(modelURL, []() -> QThread* { return qApp->getNextWorkerThread(); - }), &FBXBaker::deleteLater + }, _contentOutputPath + subDirName + "/baked", _contentOutputPath + subDirName + "/original"), + &FBXBaker::deleteLater }; // make sure our handler is called when the baker is done @@ -309,7 +317,11 @@ void DomainBaker::handleFinishedModelBaker() { QUrl oldModelURL { entity[ENTITY_MODEL_URL_KEY].toString() }; // setup a new URL using the prefix we were passed - QUrl newModelURL = _destinationPath.resolved(baker->getBakedFBXRelativePath()); + auto relativeFBXFilePath = baker->getBakedFBXFilePath().remove(_contentOutputPath); + if (relativeFBXFilePath.startsWith("/")) { + relativeFBXFilePath = relativeFBXFilePath.right(relativeFBXFilePath.length() - 1); + } + QUrl newModelURL = _destinationPath.resolved(relativeFBXFilePath); // copy the fragment and query, and user info from the old model URL newModelURL.setQuery(oldModelURL.query()); @@ -335,7 +347,7 @@ void DomainBaker::handleFinishedModelBaker() { if (oldAnimationURL.matches(oldModelURL, QUrl::RemoveQuery | QUrl::RemoveFragment)) { // the animation URL matched the old model URL, so make the animation URL point to the baked FBX // with its original query and fragment - auto newAnimationURL = _destinationPath.resolved(baker->getBakedFBXRelativePath()); + auto newAnimationURL = _destinationPath.resolved(relativeFBXFilePath); newAnimationURL.setQuery(oldAnimationURL.query()); newAnimationURL.setFragment(oldAnimationURL.fragment()); newAnimationURL.setUserInfo(oldAnimationURL.userInfo()); diff --git a/tools/oven/src/DomainBaker.h b/tools/oven/src/DomainBaker.h index 34c5e11e63..6426af0710 100644 --- a/tools/oven/src/DomainBaker.h +++ b/tools/oven/src/DomainBaker.h @@ -55,6 +55,8 @@ private: QString _baseOutputPath; QString _uniqueOutputPath; QString _contentOutputPath; + QString _bakedOutputPath; + QString _originalOutputPath; QUrl _destinationPath; QJsonArray _entities; diff --git a/tools/oven/src/FBXBaker.cpp b/tools/oven/src/FBXBaker.cpp index 0259a6baf8..8ece76b6c4 100644 --- a/tools/oven/src/FBXBaker.cpp +++ b/tools/oven/src/FBXBaker.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include "ModelBakingLoggingCategory.h" #include "TextureBaker.h" @@ -33,12 +35,12 @@ std::once_flag onceFlag; FBXSDKManagerUniquePointer FBXBaker::_sdkManager { nullptr }; -FBXBaker::FBXBaker(const QUrl& fbxURL, const QString& baseOutputPath, - TextureBakerThreadGetter textureThreadGetter, bool copyOriginals) : +FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, + const QString& bakedOutputDir, const QString& originalOutputDir) : _fbxURL(fbxURL), - _baseOutputPath(baseOutputPath), - _textureThreadGetter(textureThreadGetter), - _copyOriginals(copyOriginals) + _bakedOutputDir(bakedOutputDir), + _originalOutputDir(originalOutputDir), + _textureThreadGetter(textureThreadGetter) { std::call_once(onceFlag, [](){ // create the static FBX SDK manager @@ -46,21 +48,21 @@ FBXBaker::FBXBaker(const QUrl& fbxURL, const QString& baseOutputPath, manager->Destroy(); }); }); - - // grab the name of the FBX from the URL, this is used for folder output names - auto fileName = fbxURL.fileName(); - _fbxName = fileName.left(fileName.lastIndexOf('.')); -} - -static const QString BAKED_OUTPUT_SUBFOLDER = "baked/"; -static const QString ORIGINAL_OUTPUT_SUBFOLDER = "original/"; - -QString FBXBaker::pathToCopyOfOriginal() const { - return _uniqueOutputPath + ORIGINAL_OUTPUT_SUBFOLDER + _fbxURL.fileName(); } void FBXBaker::bake() { - qCDebug(model_baking) << "Baking" << _fbxURL; + auto tempDir = PathUtils::generateTemporaryDir(); + + if (tempDir.isEmpty()) { + handleError("Failed to create a temporary directory."); + return; + } + + _tempDir = tempDir; + + _originalFBXFilePath = _tempDir.filePath(_fbxURL.fileName()); + qDebug() << "Made temporary dir " << _tempDir; + qDebug() << "Origin file path: " << _originalFBXFilePath; // setup the output folder for the results of this bake setupOutputFolder(); @@ -102,29 +104,25 @@ void FBXBaker::bakeSourceCopy() { } void FBXBaker::setupOutputFolder() { - // construct the output path using the name of the fbx and the base output path - _uniqueOutputPath = _baseOutputPath + "/" + _fbxName + "/"; - // make sure there isn't already an output directory using the same name int iteration = 0; - while (QDir(_uniqueOutputPath).exists()) { - _uniqueOutputPath = _baseOutputPath + "/" + _fbxName + "-" + QString::number(++iteration) + "/"; - } + if (QDir(_bakedOutputDir).exists()) { + qWarning() << "Output path" << _bakedOutputDir << "already exists. Continuing."; + //_bakedOutputDir = _baseOutputPath + "/" + _fbxName + "-" + QString::number(++iteration) + "/"; + } else { + qCDebug(model_baking) << "Creating FBX output folder" << _bakedOutputDir; - qCDebug(model_baking) << "Creating FBX output folder" << _uniqueOutputPath; - - // attempt to make the output folder - if (!QDir().mkdir(_uniqueOutputPath)) { - handleError("Failed to create FBX output folder " + _uniqueOutputPath); - return; - } - - // make the baked and original sub-folders used during export - QDir uniqueOutputDir = _uniqueOutputPath; - if (!uniqueOutputDir.mkdir(BAKED_OUTPUT_SUBFOLDER) || !uniqueOutputDir.mkdir(ORIGINAL_OUTPUT_SUBFOLDER)) { - handleError("Failed to create baked/original subfolders in " + _uniqueOutputPath); - return; + // attempt to make the output folder + if (!QDir().mkpath(_bakedOutputDir)) { + handleError("Failed to create FBX output folder " + _bakedOutputDir); + return; + } + // attempt to make the output folder + if (!QDir().mkpath(_originalOutputDir)) { + handleError("Failed to create FBX output folder " + _bakedOutputDir); + return; + } } } @@ -134,8 +132,21 @@ void FBXBaker::loadSourceFBX() { // load up the local file QFile localFBX { _fbxURL.toLocalFile() }; + qDebug() << "Local file url: " << _fbxURL << _fbxURL.toString() << _fbxURL.toLocalFile() << ", copying to: " << _originalFBXFilePath; + + if (!localFBX.exists()) { + //QMessageBox::warning(this, "Could not find " + _fbxURL.toString(), ""); + handleError("Could not find " + _fbxURL.toString()); + return; + } + // make a copy in the output folder - localFBX.copy(pathToCopyOfOriginal()); + if (!_originalOutputDir.isEmpty()) { + qDebug() << "Copying to: " << _originalOutputDir << "/" << _fbxURL.fileName(); + localFBX.copy(_originalOutputDir + "/" + _fbxURL.fileName()); + } + + localFBX.copy(_originalFBXFilePath); // emit our signal to start the import of the FBX source copy emit sourceCopyReadyToLoad(); @@ -167,19 +178,27 @@ void FBXBaker::handleFBXNetworkReply() { qCDebug(model_baking) << "Downloaded" << _fbxURL; // grab the contents of the reply and make a copy in the output folder - QFile copyOfOriginal(pathToCopyOfOriginal()); + QFile copyOfOriginal(_originalFBXFilePath); - qDebug(model_baking) << "Writing copy of original FBX to" << copyOfOriginal.fileName(); + qDebug(model_baking) << "Writing copy of original FBX to" << _originalFBXFilePath << copyOfOriginal.fileName(); - if (!copyOfOriginal.open(QIODevice::WriteOnly) || (copyOfOriginal.write(requestReply->readAll()) == -1)) { + if (!copyOfOriginal.open(QIODevice::WriteOnly)) { // add an error to the error list for this FBX stating that a duplicate of the original FBX could not be made - handleError("Could not create copy of " + _fbxURL.toString()); + handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to open " + _originalFBXFilePath + ")"); + return; + } + if (copyOfOriginal.write(requestReply->readAll()) == -1) { + handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to write)"); return; } // close that file now that we are done writing to it copyOfOriginal.close(); + if (!_originalOutputDir.isEmpty()) { + copyOfOriginal.copy(_originalOutputDir + "/" + _fbxURL.fileName()); + } + // emit our signal to start the import of the FBX source copy emit sourceCopyReadyToLoad(); } else { @@ -192,9 +211,9 @@ void FBXBaker::importScene() { // create an FBX SDK importer FbxImporter* importer = FbxImporter::Create(_sdkManager.get(), ""); + qDebug() << "file path: " << _originalFBXFilePath.toLocal8Bit().data() << QDir(_originalFBXFilePath).exists(); // import the copy of the original FBX file - QString originalCopyPath = pathToCopyOfOriginal(); - bool importStatus = importer->Initialize(originalCopyPath.toLocal8Bit().data()); + bool importStatus = importer->Initialize(_originalFBXFilePath.toLocal8Bit().data()); if (!importStatus) { // failed to initialize importer, print an error and return @@ -366,8 +385,9 @@ void FBXBaker::rewriteAndBakeSceneTextures() { // even if there was another texture with the same name at a different path auto bakedTextureFileName = createBakedTextureFileName(textureFileInfo); QString bakedTextureFilePath { - _uniqueOutputPath + BAKED_OUTPUT_SUBFOLDER + bakedTextureFileName + _bakedOutputDir + "/" + bakedTextureFileName }; + _outputFiles.push_back(bakedTextureFilePath); qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName() << "to" << bakedTextureFilePath; @@ -376,7 +396,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { auto urlToTexture = getTextureURL(textureFileInfo, fileTexture); // write the new filename into the FBX scene - fileTexture->SetFileName(bakedTextureFilePath.toLocal8Bit()); + fileTexture->SetFileName(bakedTextureFilePath.toUtf8().data()); // write the relative filename to be the baked texture file name since it will // be right beside the FBX @@ -384,7 +404,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { if (!_bakingTextures.contains(urlToTexture)) { // bake this texture asynchronously - bakeTexture(urlToTexture, textureType, _uniqueOutputPath + BAKED_OUTPUT_SUBFOLDER); + bakeTexture(urlToTexture, textureType, _bakedOutputDir); } } } @@ -422,7 +442,7 @@ void FBXBaker::handleBakedTexture() { if (bakedTexture) { if (!hasErrors()) { if (!bakedTexture->hasErrors()) { - if (_copyOriginals) { + if (!_originalOutputDir.isEmpty()) { // we've been asked to make copies of the originals, so we need to make copies of this if it is a linked texture // use the path to the texture being baked to determine if this was an embedded or a linked texture @@ -430,7 +450,7 @@ void FBXBaker::handleBakedTexture() { // it is embeddded if the texure being baked was inside the original output folder // since that is where the FBX SDK places the .fbm folder it generates when importing the FBX - auto originalOutputFolder = QUrl::fromLocalFile(_uniqueOutputPath + ORIGINAL_OUTPUT_SUBFOLDER); + auto originalOutputFolder = QUrl::fromLocalFile(_originalOutputDir); if (!originalOutputFolder.isParentOf(bakedTexture->getTextureURL())) { // for linked textures we want to save a copy of original texture beside the original FBX @@ -441,7 +461,7 @@ void FBXBaker::handleBakedTexture() { auto relativeTexturePath = texturePathRelativeToFBX(_fbxURL, bakedTexture->getTextureURL()); QFile originalTextureFile { - _uniqueOutputPath + ORIGINAL_OUTPUT_SUBFOLDER + relativeTexturePath + bakedTexture->getTextureURL().fileName() + _originalOutputDir + "/" + relativeTexturePath + bakedTexture->getTextureURL().fileName() }; if (relativeTexturePath.length() > 0) { @@ -491,38 +511,35 @@ void FBXBaker::exportScene() { // setup the exporter FbxExporter* exporter = FbxExporter::Create(_sdkManager.get(), ""); - auto rewrittenFBXPath = _uniqueOutputPath + BAKED_OUTPUT_SUBFOLDER + _fbxName + BAKED_FBX_EXTENSION; - // save the relative path to this FBX inside our passed output folder - _bakedFBXRelativePath = rewrittenFBXPath; - _bakedFBXRelativePath.remove(_baseOutputPath + "/"); - bool exportStatus = exporter->Initialize(rewrittenFBXPath.toLocal8Bit().data()); + auto fileName = _fbxURL.fileName(); + auto baseName = fileName.left(fileName.lastIndexOf('.')); + auto bakedFilename = baseName + BAKED_FBX_EXTENSION; + + _bakedFBXFilePath = _bakedOutputDir + "/" + bakedFilename; + + bool exportStatus = exporter->Initialize(_bakedFBXFilePath.toLocal8Bit().data()); if (!exportStatus) { // failed to initialize exporter, print an error and return - handleError("Failed to export FBX file at " + _fbxURL.toString() + " to " + rewrittenFBXPath + handleError("Failed to export FBX file at " + _fbxURL.toString() + " to " + _bakedFBXFilePath + "- error: " + exporter->GetStatus().GetErrorString()); } + _outputFiles.push_back(_bakedFBXFilePath); + // export the scene exporter->Export(_scene); - qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << rewrittenFBXPath; + qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << _bakedFBXFilePath; } void FBXBaker::removeEmbeddedMediaFolder() { // now that the bake is complete, remove the embedded media folder produced by the FBX SDK when it imports an FBX - auto embeddedMediaFolderName = _fbxURL.fileName().replace(".fbx", ".fbm"); - QDir(_uniqueOutputPath + ORIGINAL_OUTPUT_SUBFOLDER + embeddedMediaFolderName).removeRecursively(); -} - -void FBXBaker::possiblyCleanupOriginals() { - if (!_copyOriginals) { - // caller did not ask us to keep the original around, so delete the original output folder now - QDir(_uniqueOutputPath + ORIGINAL_OUTPUT_SUBFOLDER).removeRecursively(); - } + //auto embeddedMediaFolderName = _fbxURL.fileName().replace(".fbx", ".fbm"); + //QDir(_bakedOutputDir + ORIGINAL_OUTPUT_SUBFOLDER + embeddedMediaFolderName).removeRecursively(); } void FBXBaker::checkIfTexturesFinished() { @@ -533,9 +550,6 @@ void FBXBaker::checkIfTexturesFinished() { // remove the embedded media folder that the FBX SDK produces when reading the original removeEmbeddedMediaFolder(); - // cleanup the originals if we weren't asked to keep them around - possiblyCleanupOriginals(); - if (hasErrors()) { // if we're checking for completion but we have errors // that means one or more of our texture baking operations failed diff --git a/tools/oven/src/FBXBaker.h b/tools/oven/src/FBXBaker.h index bcfebbe2a8..faf56ed46b 100644 --- a/tools/oven/src/FBXBaker.h +++ b/tools/oven/src/FBXBaker.h @@ -37,11 +37,12 @@ using TextureBakerThreadGetter = std::function; class FBXBaker : public Baker { Q_OBJECT public: - FBXBaker(const QUrl& fbxURL, const QString& baseOutputPath, - TextureBakerThreadGetter textureThreadGetter, bool copyOriginals = true); + FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, + const QString& bakedOutputDir, const QString& originalOutputDir = ""); QUrl getFBXUrl() const { return _fbxURL; } - QString getBakedFBXRelativePath() const { return _bakedFBXRelativePath; } + QString getBakedFBXFilePath() const { return _bakedFBXFilePath; } + std::vector getOutputFiles() const { return _outputFiles; } public slots: // all calls to FBXBaker::bake for FBXBaker instances must be from the same thread @@ -61,13 +62,10 @@ private: void loadSourceFBX(); - void bakeCopiedFBX(); - void importScene(); void rewriteAndBakeSceneTextures(); void exportScene(); void removeEmbeddedMediaFolder(); - void possiblyCleanupOriginals(); void checkIfTexturesFinished(); @@ -76,14 +74,20 @@ private: void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir); - QString pathToCopyOfOriginal() const; - QUrl _fbxURL; - QString _fbxName; - QString _baseOutputPath; - QString _uniqueOutputPath; - QString _bakedFBXRelativePath; + QString _bakedFBXFilePath; + + QString _bakedOutputDir; + + // If set, the original FBX and textures will also be copied here + QString _originalOutputDir; + + QDir _tempDir; + QString _originalFBXFilePath; + + // List of baked output files, includes the FBX and textures + std::vector _outputFiles; static FBXSDKManagerUniquePointer _sdkManager; fbxsdk::FbxScene* _scene { nullptr }; @@ -93,8 +97,6 @@ private: TextureBakerThreadGetter _textureThreadGetter; - bool _copyOriginals { true }; - bool _pendingErrorEmission { false }; }; diff --git a/tools/oven/src/ui/ModelBakeWidget.cpp b/tools/oven/src/ui/ModelBakeWidget.cpp index c696fbad26..926de8bae0 100644 --- a/tools/oven/src/ui/ModelBakeWidget.cpp +++ b/tools/oven/src/ui/ModelBakeWidget.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -158,12 +159,23 @@ void ModelBakeWidget::bakeButtonClicked() { // make sure we have a valid output directory QDir outputDirectory(_outputDirLineEdit->text()); + outputDirectory.mkdir("."); if (!outputDirectory.exists()) { + QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory."); return; } + QDir bakedOutputDirectory = outputDirectory.absoluteFilePath("baked"); + QDir originalOutputDirectory = outputDirectory.absoluteFilePath("original"); + + bakedOutputDirectory.mkdir("."); + originalOutputDirectory.mkdir("."); + + + // make sure we have a non empty URL to a model to bake if (_modelLineEdit->text().isEmpty()) { + QMessageBox::warning(this, "Model URL unspecified", "A model file is required."); return; } @@ -175,14 +187,17 @@ void ModelBakeWidget::bakeButtonClicked() { // if the URL doesn't have a scheme, assume it is a local file if (modelToBakeURL.scheme() != "http" && modelToBakeURL.scheme() != "https" && modelToBakeURL.scheme() != "ftp") { - modelToBakeURL.setScheme("file"); + qDebug() << modelToBakeURL.toString(); + qDebug() << modelToBakeURL.scheme(); + modelToBakeURL = QUrl::fromLocalFile(fileURLString); + qDebug() << "New url: " << modelToBakeURL; } // everything seems to be in place, kick off a bake for this model now auto baker = std::unique_ptr { - new FBXBaker(modelToBakeURL, outputDirectory.absolutePath(), []() -> QThread* { + new FBXBaker(modelToBakeURL, []() -> QThread* { return qApp->getNextWorkerThread(); - }, false) + }, bakedOutputDirectory.absolutePath(), originalOutputDirectory.absolutePath()) }; // move the baker to the FBX baker thread @@ -211,6 +226,10 @@ void ModelBakeWidget::handleFinishedBaker() { return value.first.get() == baker; }); + for (auto& file : baker->getOutputFiles()) { + qDebug() << "Baked file: " << file; + } + if (it != _bakers.end()) { auto resultRow = it->second; auto resultsWindow = qApp->getMainWindow()->showResultsWindow(); diff --git a/tools/oven/src/ui/ResultsWindow.cpp b/tools/oven/src/ui/ResultsWindow.cpp index 35b5160f9b..3a37a328de 100644 --- a/tools/oven/src/ui/ResultsWindow.cpp +++ b/tools/oven/src/ui/ResultsWindow.cpp @@ -50,6 +50,7 @@ void ResultsWindow::setupUI() { // strech the last column of the table (that holds the results) to fill up the remaining available size _resultsTable->horizontalHeader()->resizeSection(0, 0.25 * FIXED_WINDOW_WIDTH); _resultsTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + _resultsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // make sure we hear about cell clicks so that we can show the output directory for the given row connect(_resultsTable, &QTableWidget::cellClicked, this, &ResultsWindow::handleCellClicked); diff --git a/tools/oven/src/ui/SkyboxBakeWidget.cpp b/tools/oven/src/ui/SkyboxBakeWidget.cpp index d5c280aebd..cbaaa5ec0a 100644 --- a/tools/oven/src/ui/SkyboxBakeWidget.cpp +++ b/tools/oven/src/ui/SkyboxBakeWidget.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -155,7 +156,9 @@ void SkyboxBakeWidget::bakeButtonClicked() { // make sure we have a valid output directory QDir outputDirectory(_outputDirLineEdit->text()); + outputDirectory.mkdir("."); if (!outputDirectory.exists()) { + QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory."); return; } From 6f0229f450538496b63087ecb096c695596d9828 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 22 Aug 2017 23:26:27 +0200 Subject: [PATCH 015/202] Reworked according to designers request. Do not fall back to default audion output --- .../resources/qml/controls-uit/CheckBox2.qml | 5 +- interface/resources/qml/hifi/audio/Audio.qml | 74 +++++++++++-------- .../qml/hifi/audio/AudioTabButton.qml | 1 + .../resources/qml/hifi/audio/CheckBox.qml | 1 + libraries/audio-client/src/AudioClient.cpp | 8 -- 5 files changed, 47 insertions(+), 42 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox2.qml b/interface/resources/qml/controls-uit/CheckBox2.qml index 7bd69f0b19..f845d24868 100644 --- a/interface/resources/qml/controls-uit/CheckBox2.qml +++ b/interface/resources/qml/controls-uit/CheckBox2.qml @@ -23,11 +23,12 @@ CheckBox { property string color: hifi.colors.lightGrayText readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light property bool isRedCheck: false + property bool isRound: false property int boxSize: 14 - property int boxRadius: 3 + property int boxRadius: isRound ? boxSize : 3 property bool wrap: true; readonly property int checkSize: Math.max(boxSize - 8, 10) - readonly property int checkRadius: 2 + readonly property int checkRadius: isRound ? checkSize / 2 : 2 focusPolicy: Qt.ClickFocus indicator: Rectangle { diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index dfecfbb50f..ed4a74b86f 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -37,6 +37,7 @@ Rectangle { } property bool isVR: Audio.context === "VR" + property real rightMostInputLevelPos: 0 //placeholder for control sizes and paddings //recalculates dynamically in case of UI size is changed QtObject { @@ -54,7 +55,7 @@ Rectangle { id: bar spacing: 0 width: parent.width - height: 36 + height: 42 AudioControls.AudioTabButton { height: parent.height @@ -68,21 +69,20 @@ Rectangle { Column { spacing: 12; - anchors.topMargin: 8 anchors.top: bar.bottom anchors.bottom: parent.bottom anchors.bottomMargin: 5 width: parent.width; + Separator { } + RalewayRegular { - x: margins.paddings; + x: margins.paddings + margins.sizeCheckBox; size: 16; color: "white"; - text: qsTr("Mic Settings") + text: qsTr("Input Device Settings") } - Separator { } - ColumnLayout { x: margins.paddings; // padding does not work spacing: 16; @@ -118,28 +118,27 @@ Rectangle { AvatarInputs.showAudioTools = checked; checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding } + onXChanged: rightMostInputLevelPos = x + width } } } Separator {} - RowLayout { + Row { x: margins.paddings; width: parent.width - margins.paddings*2 height: 28 spacing: 0 HiFiGlyphs { - Layout.minimumWidth: margins.sizeCheckBox - Layout.maximumWidth: margins.sizeCheckBox + width: margins.sizeCheckBox text: hifi.glyphs.mic; color: hifi.colors.primaryHighlight; anchors.verticalCenter: parent.verticalCenter; - size: 36; + size: 30; } RalewayRegular { - Layout.minimumWidth: margins.sizeText + margins.sizeLevel - Layout.maximumWidth: margins.sizeText + margins.sizeLevel + width: margins.sizeText + margins.sizeLevel anchors.verticalCenter: parent.verticalCenter; size: 16; color: hifi.colors.lightGrayText; @@ -151,19 +150,22 @@ Rectangle { id: inputView width: parent.width - margins.paddings*2 x: margins.paddings - height: 145; + height: 150 spacing: 4; snapMode: ListView.SnapToItem; clip: true; model: Audio.devices.input; - delegate: RowLayout { - width: inputView.width; - spacing: 5 + delegate: Item { + width: rightMostInputLevelPos + height: margins.sizeCheckBox AudioControls.CheckBox { - Layout.fillWidth: true + anchors.left: parent.left + width: parent.width - inputLevel.width + clip: true checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; - boxRadius: boxSize/2 + boxSize: margins.sizeCheckBox / 2 + isRound: true text: devicename onClicked: { if (checked) { @@ -172,6 +174,9 @@ Rectangle { } } InputLevel { + id: inputLevel + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter visible: (bar.currentIndex === 1 && selectedHMD && isVR) || (bar.currentIndex === 0 && selectedDesktop && !isVR); } @@ -180,22 +185,20 @@ Rectangle { Separator {} - RowLayout { + Row { x: margins.paddings; width: parent.width - margins.paddings*2 - height: 28 + height: 36 spacing: 0 HiFiGlyphs { - Layout.minimumWidth: margins.sizeCheckBox - Layout.maximumWidth: margins.sizeCheckBox + width: margins.sizeCheckBox text: hifi.glyphs.unmuted; color: hifi.colors.primaryHighlight; anchors.verticalCenter: parent.verticalCenter; - size: 28; + size: 36; } RalewayRegular { - Layout.minimumWidth: margins.sizeText + margins.sizeLevel - Layout.maximumWidth: margins.sizeText + margins.sizeLevel + width: margins.sizeText + margins.sizeLevel anchors.verticalCenter: parent.verticalCenter; size: 16; color: hifi.colors.lightGrayText; @@ -207,18 +210,19 @@ Rectangle { id: outputView width: parent.width - margins.paddings*2 x: margins.paddings - height: Math.min(220, contentHeight); + height: Math.min(210, contentHeight); spacing: 4; snapMode: ListView.SnapToItem; clip: true; model: Audio.devices.output; - delegate: RowLayout { - width: outputView.width; - spacing: 0 + delegate: Item { + width: rightMostInputLevelPos + height: margins.sizeCheckBox AudioControls.CheckBox { - Layout.fillWidth: true - boxRadius: boxSize/2 + width: parent.width + boxSize: margins.sizeCheckBox / 2 + isRound: true checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; text: devicename onClicked: { @@ -229,6 +233,12 @@ Rectangle { } } } - PlaySampleSound { anchors { left: parent.left; leftMargin: margins.paddings }} + PlaySampleSound { + x: margins.paddings + + visible: (bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR); + anchors { left: parent.left; leftMargin: margins.paddings } + } } } diff --git a/interface/resources/qml/hifi/audio/AudioTabButton.qml b/interface/resources/qml/hifi/audio/AudioTabButton.qml index bf50f1ba26..2e6e114039 100644 --- a/interface/resources/qml/hifi/audio/AudioTabButton.qml +++ b/interface/resources/qml/hifi/audio/AudioTabButton.qml @@ -16,6 +16,7 @@ import "../../styles-uit" TabButton { id: control + font.pixelSize: height / 2 HifiConstants { id: hifi; } diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml index 77c001816f..806f08f439 100644 --- a/interface/resources/qml/hifi/audio/CheckBox.qml +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -14,5 +14,6 @@ import QtQuick 2.7 import "../../controls-uit" as HifiControls HifiControls.CheckBox2 { + spacing: 8 color: "white" } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 27bab687d5..b8f1bd876d 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1717,14 +1717,6 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) { if (persist) { _outputBufferSizeFrames.set(numFrames); } - - if (_audioOutput) { - // The buffer size can't be adjusted after QAudioOutput::start() has been called, so - // recreate the device by switching to the default. - QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput); - qCDebug(audioclient) << __FUNCTION__ << "about to send changeDevice signal outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; - emit changeDevice(outputDeviceInfo); // On correct thread, please, as setOutputBufferSize can be called from main thread. - } } return numFrames; } From f7a704d9f43468cdf4958f9a010d47e79ebdc3b4 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 22 Aug 2017 23:33:09 +0200 Subject: [PATCH 016/202] Added current tab selection according to context --- interface/resources/qml/hifi/audio/Audio.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index ed4a74b86f..e80f278323 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -56,6 +56,7 @@ Rectangle { spacing: 0 width: parent.width height: 42 + currentIndex: isVR ? 1 : 0 AudioControls.AudioTabButton { height: parent.height From 39f04adc8db1d9861815313af960e33599e77531 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 16 Aug 2017 13:51:40 -0700 Subject: [PATCH 017/202] Improve baking interface and add automatic baking to asset server --- assignment-client/CMakeLists.txt | 2 +- assignment-client/src/assets/AssetServer.cpp | 190 +++++- assignment-client/src/assets/AssetServer.h | 35 +- cmake/modules/FindFBX.cmake | 2 +- libraries/baking/CMakeLists.txt | 17 + .../oven => libraries/baking}/src/Baker.cpp | 0 {tools/oven => libraries/baking}/src/Baker.h | 0 libraries/baking/src/FBXBaker.cpp | 568 ++++++++++++++++++ libraries/baking/src/FBXBaker.h | 105 ++++ .../src/ModelBakingLoggingCategory.cpp | 0 .../baking}/src/ModelBakingLoggingCategory.h | 0 libraries/baking/src/TextureBaker.cpp | 131 ++++ libraries/baking/src/TextureBaker.h | 59 ++ libraries/fbx/CMakeLists.txt | 7 +- libraries/fbx/src/FBXReader.cpp | 4 + .../src/model-networking/ModelCache.cpp | 11 +- .../src/model-networking/ModelCache.h | 1 + .../src/model-networking/TextureCache.cpp | 10 +- .../networking/src/AssetResourceRequest.cpp | 6 +- libraries/networking/src/MappingRequest.cpp | 3 + libraries/networking/src/ResourceCache.cpp | 7 + libraries/networking/src/ResourceCache.h | 1 + libraries/networking/src/ResourceRequest.h | 1 + libraries/networking/src/udt/PacketHeaders.h | 15 +- tools/oven/CMakeLists.txt | 2 +- 25 files changed, 1139 insertions(+), 38 deletions(-) create mode 100644 libraries/baking/CMakeLists.txt rename {tools/oven => libraries/baking}/src/Baker.cpp (100%) rename {tools/oven => libraries/baking}/src/Baker.h (100%) create mode 100644 libraries/baking/src/FBXBaker.cpp create mode 100644 libraries/baking/src/FBXBaker.h rename {tools/oven => libraries/baking}/src/ModelBakingLoggingCategory.cpp (100%) rename {tools/oven => libraries/baking}/src/ModelBakingLoggingCategory.h (100%) create mode 100644 libraries/baking/src/TextureBaker.cpp create mode 100644 libraries/baking/src/TextureBaker.h diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 1a27ddd479..0421195612 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -13,7 +13,7 @@ setup_memory_debugger() link_hifi_libraries( audio avatars octree gpu model fbx entities networking animation recording shared script-engine embedded-webserver - controllers physics plugins midi + controllers physics plugins midi baking image ) if (WIN32) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 2082856f56..6e3195f570 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -33,6 +35,9 @@ #include "SendAssetTask.h" #include "UploadAssetTask.h" #include +#include + +#include static const uint8_t MIN_CORES_FOR_MULTICORE = 4; static const uint8_t CPU_AFFINITY_COUNT_HIGH = 2; @@ -43,6 +48,128 @@ static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000; const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; +static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" }; +static const QList BAKEABLE_TEXTURE_EXTENSIONS = QImageReader::supportedImageFormats(); +static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; +static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; + +BakeAssetTask::BakeAssetTask(const QString& assetHash, const QString& assetPath, const QString& filePath) + : _assetHash(assetHash), _assetPath(assetPath), _filePath(filePath) { +} + +void BakeAssetTask::run() { + qRegisterMetaType >("QVector"); + TextureBakerThreadGetter fn = []() -> QThread* { return QThread::currentThread(); }; + + if (_filePath.endsWith(".fbx")) { + FBXBaker baker(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir()); + + QEventLoop loop; + connect(&baker, &Baker::finished, &loop, &QEventLoop::quit); + QMetaObject::invokeMethod(&baker, "bake", Qt::QueuedConnection); + qDebug() << "Running the bake!"; + loop.exec(); + + qDebug() << "Finished baking: " << _assetHash << _assetPath << baker.getOutputFiles(); + emit bakeComplete(_assetHash, _assetPath, QVector::fromStdVector(baker.getOutputFiles())); + } else { + TextureBaker baker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, PathUtils::generateTemporaryDir()); + + QEventLoop loop; + connect(&baker, &Baker::finished, &loop, &QEventLoop::quit); + QMetaObject::invokeMethod(&baker, "bake", Qt::QueuedConnection); + qDebug() << "Running the bake!"; + loop.exec(); + + qDebug() << "Finished baking: " << _assetHash << _assetPath << baker.getBakedTextureFileName(); + emit bakeComplete(_assetHash, _assetPath, { baker.getDestinationFilePath() }); + } +} + +void AssetServer::bakeAsset(const QString& assetHash, const QString& assetPath, const QString& filePath) { + qDebug() << "Starting bake for: " << assetPath << assetHash; + auto it = _pendingBakes.find(assetHash); + if (it == _pendingBakes.end()) { + auto task = std::make_shared(assetHash, assetPath, filePath); + task->setAutoDelete(false); + _pendingBakes[assetHash] = task; + + connect(task.get(), &BakeAssetTask::bakeComplete, this, [this, assetPath](QString assetHash, QString assetPath, QVector outputFiles) { + handleCompletedBake(assetPath, assetHash, outputFiles); + }); + + _bakingTaskPool.start(task.get()); + } else { + qDebug() << "Already in queue"; + } +} + +QString AssetServer::getPathToAssetHash(const AssetHash& assetHash) { + return _filesDirectory.absoluteFilePath(assetHash); +} + +void AssetServer::bakeAssets() { + auto it = _fileMappings.cbegin(); + for (; it != _fileMappings.cend(); ++it) { + auto path = it.key(); + auto hash = it.value().toString(); + maybeBake(path, hash); + } +} + +void AssetServer::maybeBake(const AssetPath& path, const AssetHash& hash) { + if (needsToBeBaked(path, hash)) { + qDebug() << "Queuing bake of: " << path; + bakeAsset(hash, path, getPathToAssetHash(hash)); + } +} + +void AssetServer::createEmptyMetaFile(const AssetHash& hash) { + QString metaFilePath = "atp:/" + hash + "/meta.json"; + QFile metaFile { metaFilePath }; + + if (!metaFile.exists()) { + qDebug() << "Creating metfaile for " << hash; + if (metaFile.open(QFile::WriteOnly)) { + qDebug() << "Created metfaile for " << hash; + metaFile.write("{}"); + } + } +} + +bool AssetServer::hasMetaFile(const AssetHash& hash) { + QString metaFilePath = "/.baked/" + hash + "/meta.json"; + qDebug() << "in mappings?" << metaFilePath; + + return _fileMappings.contains(metaFilePath); +} + +bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHash) { + if (path.startsWith("/.baked/")) { + return false; + } + + auto dotIndex = path.lastIndexOf("."); + if (dotIndex == -1) { + return false; + } + + auto extension = path.mid(dotIndex + 1); + + QString bakedFilename; + + if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { + bakedFilename = BAKED_MODEL_SIMPLE_NAME; + } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(assetHash)) { + bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; + } else { + return false; + } + + auto bakedPath = "/.baked/" + assetHash + "/" + bakedFilename; + return !_fileMappings.contains(bakedPath); +} + bool interfaceRunning() { bool result = false; @@ -76,13 +203,15 @@ void updateConsumedCores() { AssetServer::AssetServer(ReceivedMessage& message) : ThreadedAssignment(message), - _taskPool(this) + _transferTaskPool(this), + _bakingTaskPool(this) { // Most of the work will be I/O bound, reading from disk and constructing packet objects, // so the ideal is greater than the number of cores on the system. static const int TASK_POOL_THREAD_COUNT = 50; - _taskPool.setMaxThreadCount(TASK_POOL_THREAD_COUNT); + _transferTaskPool.setMaxThreadCount(TASK_POOL_THREAD_COUNT); + _bakingTaskPool.setMaxThreadCount(1); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::AssetGet, this, "handleAssetGet"); @@ -196,6 +325,8 @@ void AssetServer::completeSetup() { } nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer }); + + bakeAssets(); } else { qCritical() << "Asset Server assignment will not continue because mapping file could not be loaded."; setFinished(true); @@ -265,27 +396,27 @@ void AssetServer::handleAssetMappingOperation(QSharedPointer me nodeList->sendPacketList(std::move(replyPacket), *senderNode); } -static const QStringList BAKEABLE_MODEL_EXTENSIONS = { ".fbx" }; -static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; -static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; - void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) { QString assetPath = message.readString(); + QUrl url { assetPath }; + assetPath = url.path(); + auto it = _fileMappings.find(assetPath); if (it != _fileMappings.end()) { // check if we should re-direct to a baked asset // first, figure out from the mapping extension what type of file this is - auto assetPathExtension = assetPath.mid(assetPath.lastIndexOf('.')).toLower(); + auto assetPathExtension = assetPath.mid(assetPath.lastIndexOf('.') + 1).toLower(); QString bakedRootFile; if (BAKEABLE_MODEL_EXTENSIONS.contains(assetPathExtension)) { bakedRootFile = BAKED_MODEL_SIMPLE_NAME; - } else if (QImageReader::supportedImageFormats().contains(assetPathExtension.toLocal8Bit())) { + } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(assetPathExtension.toLocal8Bit())) { bakedRootFile = BAKED_TEXTURE_SIMPLE_NAME; } + qDebug() << bakedRootFile << assetPathExtension; auto originalAssetHash = it->toString(); QString redirectedAssetHash; @@ -298,9 +429,12 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode auto bakedIt = _fileMappings.find(bakedAssetPath); if (bakedIt != _fileMappings.end()) { + qDebug() << "Did find baked version for: " << originalAssetHash << assetPath; // we found a baked version of the requested asset to serve, redirect to that redirectedAssetHash = bakedIt->toString(); wasRedirected = true; + } else { + qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath; } } @@ -319,6 +453,15 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode } else { replyPacket.write(QByteArray::fromHex(originalAssetHash.toUtf8())); replyPacket.writePrimitive(wasRedirected); + + + auto query = QUrlQuery(url.query()); + bool isSkybox = query.hasQueryItem("skybox"); + qDebug() << "Is skybox? " << isSkybox; + if (isSkybox) { + createMetaFile(originalAssetHash); + maybeBake(originalAssetHash, assetPath); + } } } else { replyPacket.writePrimitive(AssetServerError::AssetNotFound); @@ -437,7 +580,7 @@ void AssetServer::handleAssetGet(QSharedPointer message, Shared // Queue task auto task = new SendAssetTask(message, senderNode, _filesDirectory); - _taskPool.start(task); + _transferTaskPool.start(task); } void AssetServer::handleAssetUpload(QSharedPointer message, SharedNodePointer senderNode) { @@ -446,7 +589,7 @@ void AssetServer::handleAssetUpload(QSharedPointer message, Sha qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID()); auto task = new UploadAssetTask(message, senderNode, _filesDirectory); - _taskPool.start(task); + _transferTaskPool.start(task); } else { // this is a node the domain told us is not allowed to rez entities // for now this also means it isn't allowed to add assets @@ -632,6 +775,7 @@ bool AssetServer::setMapping(AssetPath path, AssetHash hash) { if (writeMappingsToFile()) { // persistence succeeded, we are good to go qDebug() << "Set mapping:" << path << "=>" << hash; + maybeBake(path, hash); return true; } else { // failed to persist this mapping to file - put back the old one in our in-memory representation @@ -836,18 +980,17 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { static const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; -void AssetServer::handleCompletedBake(AssetHash originalAssetHash, QDir temporaryOutputDir) { - // enumerate the baking result files in the temporary directory - QDirIterator dirIterator(temporaryOutputDir.absolutePath(), QDir::Files, QDirIterator::Subdirectories); - +void AssetServer::handleCompletedBake(AssetPath originalAssetPath, AssetHash originalAssetHash, QVector bakedFilePaths) { bool errorCompletingBake { false }; - while (dirIterator.hasNext()) { - QString filePath = dirIterator.next(); + qDebug() << "Completing bake for " << originalAssetHash; + for (auto& filePath: bakedFilePaths) { // figure out the hash for the contents of this file QFile file(filePath); + qDebug() << "File path: " << filePath; + AssetHash bakedFileHash; if (file.open(QIODevice::ReadOnly)) { @@ -873,26 +1016,33 @@ void AssetServer::handleCompletedBake(AssetHash originalAssetHash, QDir temporar } // setup the mapping for this bake file - auto relativeFilePath = temporaryOutputDir.relativeFilePath(filePath); - static const QString BAKED_ASSET_SIMPLE_NAME = "asset.fbx"; + auto relativeFilePath = QUrl(filePath).fileName(); + qDebug() << "Relative file path is: " << relativeFilePath; + static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx"; + static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx"; if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) { // for an FBX file, we replace the filename with the simple name // (to handle the case where two mapped assets have the same hash but different names) - relativeFilePath = BAKED_ASSET_SIMPLE_NAME; + relativeFilePath = BAKED_ASSET_SIMPLE_FBX_NAME; + } else if (!originalAssetPath.endsWith(".fbx")) { + relativeFilePath = BAKED_ASSET_SIMPLE_TEXTURE_NAME; + } QString bakeMapping = HIDDEN_BAKED_CONTENT_FOLDER + originalAssetHash + "/" + relativeFilePath; // add a mapping (under the hidden baked folder) for this file resulting from the bake - if (setMapping(bakeMapping , bakedFileHash)) { + if (setMapping(bakeMapping, bakedFileHash)) { qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash; } else { + qDebug() << "Failed to set mapping"; // stop handling this bake, couldn't add a mapping for this bake file errorCompletingBake = true; break; } } else { + qDebug() << "Failed to open baked file: " << filePath; // stop handling this bake, we couldn't open one of the files for reading errorCompletingBake = true; break; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 191ad25b1f..2dfc5f6c35 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -14,12 +14,29 @@ #include #include +#include #include #include "AssetUtils.h" #include "ReceivedMessage.h" +class BakeAssetTask : public QObject, public QRunnable { + Q_OBJECT +public: + BakeAssetTask(const QString& assetHash, const QString& assetPath, const QString& filePath); + + void run() override; + +signals: + void bakeComplete(QString assetHash, QString assetPath, QVector outputFiles); + +private: + QString _assetHash; + QString _assetPath; + QString _filePath; +}; + class AssetServer : public ThreadedAssignment { Q_OBJECT public: @@ -63,8 +80,17 @@ private: /// Delete any unmapped files from the local asset directory void cleanupUnmappedFiles(); + QString getPathToAssetHash(const AssetHash& assetHash); + + void bakeAssets(); + void maybeBake(const AssetPath& path, const AssetHash& hash); + void createEmptyMetaFile(const AssetHash& hash); + bool hasMetaFile(const AssetHash& hash); + bool needsToBeBaked(const AssetPath& path, const AssetHash& assetHash); + void bakeAsset(const QString& assetHash, const QString& assetPath, const QString& filePath); + /// Move baked content for asset to baked directory and update baked status - void handleCompletedBake(AssetHash originalAssetHash, QDir temporaryOutputDir); + void handleCompletedBake(AssetPath assetPath, AssetHash originalAssetHash, QVector bakedFilePaths); /// Create meta file to describe baked content for original asset bool createMetaFile(AssetHash originalAssetHash); @@ -73,7 +99,12 @@ private: QDir _resourcesDirectory; QDir _filesDirectory; - QThreadPool _taskPool; + + /// Task pool for handling uploads and downloads of assets + QThreadPool _transferTaskPool; + + QHash> _pendingBakes; + QThreadPool _bakingTaskPool; }; #endif diff --git a/cmake/modules/FindFBX.cmake b/cmake/modules/FindFBX.cmake index 9a1d08a010..6eb6d11f9d 100644 --- a/cmake/modules/FindFBX.cmake +++ b/cmake/modules/FindFBX.cmake @@ -57,7 +57,7 @@ endif() function(_fbx_find_library _name _lib _suffix) if (MSVC_VERSION EQUAL 1910) - set(VS_PREFIX vs2017) + set(VS_PREFIX vs2015) elseif (MSVC_VERSION EQUAL 1900) set(VS_PREFIX vs2015) elseif (MSVC_VERSION EQUAL 1800) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt new file mode 100644 index 0000000000..44c52ae0d8 --- /dev/null +++ b/libraries/baking/CMakeLists.txt @@ -0,0 +1,17 @@ +set(TARGET_NAME baking) +setup_hifi_library(Concurrent) + +find_package(FBX) +if (FBX_FOUND) + if (CMAKE_THREAD_LIBS_INIT) + target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}") + else () + target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES}) + endif () + target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) +endif () + +set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) + +link_hifi_libraries(shared model networking ktx image) +include_hifi_library_headers(gpu) diff --git a/tools/oven/src/Baker.cpp b/libraries/baking/src/Baker.cpp similarity index 100% rename from tools/oven/src/Baker.cpp rename to libraries/baking/src/Baker.cpp diff --git a/tools/oven/src/Baker.h b/libraries/baking/src/Baker.h similarity index 100% rename from tools/oven/src/Baker.h rename to libraries/baking/src/Baker.h diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp new file mode 100644 index 0000000000..1a11071b5b --- /dev/null +++ b/libraries/baking/src/FBXBaker.cpp @@ -0,0 +1,568 @@ +// +// FBXBaker.cpp +// tools/oven/src +// +// Created by Stephen Birarda on 3/30/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 +// + +#include // need this include so we don't get an error looking for std::isnan + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "ModelBakingLoggingCategory.h" +#include "TextureBaker.h" + +#include "FBXBaker.h" + +std::once_flag onceFlag; +FBXSDKManagerUniquePointer FBXBaker::_sdkManager { nullptr }; + +FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, + const QString& bakedOutputDir, const QString& originalOutputDir) : + _fbxURL(fbxURL), + _bakedOutputDir(bakedOutputDir), + _originalOutputDir(originalOutputDir), + _textureThreadGetter(textureThreadGetter) +{ + std::call_once(onceFlag, [](){ + // create the static FBX SDK manager + _sdkManager = FBXSDKManagerUniquePointer(FbxManager::Create(), [](FbxManager* manager){ + manager->Destroy(); + }); + }); +} + +void FBXBaker::bake() { + auto tempDir = PathUtils::generateTemporaryDir(); + + if (tempDir.isEmpty()) { + handleError("Failed to create a temporary directory."); + return; + } + + _tempDir = tempDir; + + _originalFBXFilePath = _tempDir.filePath(_fbxURL.fileName()); + qDebug() << "Made temporary dir " << _tempDir; + qDebug() << "Origin file path: " << _originalFBXFilePath; + + // setup the output folder for the results of this bake + setupOutputFolder(); + + if (hasErrors()) { + return; + } + + connect(this, &FBXBaker::sourceCopyReadyToLoad, this, &FBXBaker::bakeSourceCopy); + + // make a local copy of the FBX file + loadSourceFBX(); +} + +void FBXBaker::bakeSourceCopy() { + // load the scene from the FBX file + importScene(); + + if (hasErrors()) { + return; + } + + // enumerate the textures found in the scene and start a bake for them + rewriteAndBakeSceneTextures(); + + if (hasErrors()) { + return; + } + + // export the FBX with re-written texture references + exportScene(); + + if (hasErrors()) { + return; + } + + // check if we're already done with textures (in case we had none to re-write) + checkIfTexturesFinished(); +} + +void FBXBaker::setupOutputFolder() { + // make sure there isn't already an output directory using the same name + int iteration = 0; + + if (QDir(_bakedOutputDir).exists()) { + qWarning() << "Output path" << _bakedOutputDir << "already exists. Continuing."; + //_bakedOutputDir = _baseOutputPath + "/" + _fbxName + "-" + QString::number(++iteration) + "/"; + } else { + qCDebug(model_baking) << "Creating FBX output folder" << _bakedOutputDir; + + // attempt to make the output folder + if (!QDir().mkpath(_bakedOutputDir)) { + handleError("Failed to create FBX output folder " + _bakedOutputDir); + return; + } + // attempt to make the output folder + if (!QDir().mkpath(_originalOutputDir)) { + handleError("Failed to create FBX output folder " + _bakedOutputDir); + return; + } + } +} + +void FBXBaker::loadSourceFBX() { + // check if the FBX is local or first needs to be downloaded + if (_fbxURL.isLocalFile()) { + // load up the local file + QFile localFBX { _fbxURL.toLocalFile() }; + + qDebug() << "Local file url: " << _fbxURL << _fbxURL.toString() << _fbxURL.toLocalFile() << ", copying to: " << _originalFBXFilePath; + + if (!localFBX.exists()) { + //QMessageBox::warning(this, "Could not find " + _fbxURL.toString(), ""); + handleError("Could not find " + _fbxURL.toString()); + return; + } + + // make a copy in the output folder + if (!_originalOutputDir.isEmpty()) { + qDebug() << "Copying to: " << _originalOutputDir << "/" << _fbxURL.fileName(); + localFBX.copy(_originalOutputDir + "/" + _fbxURL.fileName()); + } + + localFBX.copy(_originalFBXFilePath); + + // emit our signal to start the import of the FBX source copy + emit sourceCopyReadyToLoad(); + } else { + // remote file, kick off a download + auto& networkAccessManager = NetworkAccessManager::getInstance(); + + QNetworkRequest networkRequest; + + // setup the request to follow re-directs and always hit the network + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + + + networkRequest.setUrl(_fbxURL); + + qCDebug(model_baking) << "Downloading" << _fbxURL; + auto networkReply = networkAccessManager.get(networkRequest); + + connect(networkReply, &QNetworkReply::finished, this, &FBXBaker::handleFBXNetworkReply); + } +} + +void FBXBaker::handleFBXNetworkReply() { + auto requestReply = qobject_cast(sender()); + + if (requestReply->error() == QNetworkReply::NoError) { + qCDebug(model_baking) << "Downloaded" << _fbxURL; + + // grab the contents of the reply and make a copy in the output folder + QFile copyOfOriginal(_originalFBXFilePath); + + qDebug(model_baking) << "Writing copy of original FBX to" << _originalFBXFilePath << copyOfOriginal.fileName(); + + if (!copyOfOriginal.open(QIODevice::WriteOnly)) { + // add an error to the error list for this FBX stating that a duplicate of the original FBX could not be made + handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to open " + _originalFBXFilePath + ")"); + return; + } + if (copyOfOriginal.write(requestReply->readAll()) == -1) { + handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to write)"); + return; + } + + // close that file now that we are done writing to it + copyOfOriginal.close(); + + if (!_originalOutputDir.isEmpty()) { + copyOfOriginal.copy(_originalOutputDir + "/" + _fbxURL.fileName()); + } + + // emit our signal to start the import of the FBX source copy + emit sourceCopyReadyToLoad(); + } else { + // add an error to our list stating that the FBX could not be downloaded + handleError("Failed to download " + _fbxURL.toString()); + } +} + +void FBXBaker::importScene() { + // create an FBX SDK importer + FbxImporter* importer = FbxImporter::Create(_sdkManager.get(), ""); + + qDebug() << "file path: " << _originalFBXFilePath.toLocal8Bit().data() << QDir(_originalFBXFilePath).exists(); + // import the copy of the original FBX file + bool importStatus = importer->Initialize(_originalFBXFilePath.toLocal8Bit().data()); + + if (!importStatus) { + // failed to initialize importer, print an error and return + handleError("Failed to import " + _fbxURL.toString() + " - " + importer->GetStatus().GetErrorString()); + return; + } else { + qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; + } + + // setup a new scene to hold the imported file + _scene = FbxScene::Create(_sdkManager.get(), "bakeScene"); + + // import the file to the created scene + importer->Import(_scene); + + // destroy the importer that is no longer needed + importer->Destroy(); +} + +QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { + auto fbxPath = fbxURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); + auto texturePath = textureURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); + + if (texturePath.startsWith(fbxPath)) { + // texture path is a child of the FBX path, return the texture path without the fbx path + return texturePath.mid(fbxPath.length()); + } else { + // the texture path was not a child of the FBX path, return the empty string + return ""; + } +} + +QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { + // first make sure we have a unique base name for this texture + // in case another texture referenced by this model has the same base name + auto nameMatches = _textureNameMatchCount[textureFileInfo.baseName()]; + + QString bakedTextureFileName { textureFileInfo.completeBaseName() }; + + if (nameMatches > 0) { + // there are already nameMatches texture with this name + // append - and that number to our baked texture file name so that it is unique + bakedTextureFileName += "-" + QString::number(nameMatches); + } + + bakedTextureFileName += BAKED_TEXTURE_EXT; + + // increment the number of name matches + ++nameMatches; + + return bakedTextureFileName; +} + +QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* fileTexture) { + QUrl urlToTexture; + + if (textureFileInfo.exists() && textureFileInfo.isFile()) { + // set the texture URL to the local texture that we have confirmed exists + urlToTexture = QUrl::fromLocalFile(textureFileInfo.absoluteFilePath()); + } else { + // external texture that we'll need to download or find + + // first check if it the RelativePath to the texture in the FBX was relative + QString relativeFileName = fileTexture->GetRelativeFileName(); + auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); + + // this is a relative file path which will require different handling + // depending on the location of the original FBX + if (_fbxURL.isLocalFile() && apparentRelativePath.exists() && apparentRelativePath.isFile()) { + // the absolute path we ran into for the texture in the FBX exists on this machine + // so use that file + urlToTexture = QUrl::fromLocalFile(apparentRelativePath.absoluteFilePath()); + } else { + // we didn't find the texture on this machine at the absolute path + // so assume that it is right beside the FBX to match the behaviour of interface + urlToTexture = _fbxURL.resolved(apparentRelativePath.fileName()); + } + } + + return urlToTexture; +} + +image::TextureUsage::Type textureTypeForMaterialProperty(FbxProperty& property, FbxSurfaceMaterial* material) { + using namespace image::TextureUsage; + + // this is a property we know has a texture, we need to match it to a High Fidelity known texture type + // since that information is passed to the baking process + + // grab the hierarchical name for this property and lowercase it for case-insensitive compare + auto propertyName = QString(property.GetHierarchicalName()).toLower(); + + // figure out the type of the property based on what known value string it matches + if ((propertyName.contains("diffuse") && !propertyName.contains("tex_global_diffuse")) + || propertyName.contains("tex_color_map")) { + return ALBEDO_TEXTURE; + } else if (propertyName.contains("transparentcolor") || propertyName.contains("transparencyfactor")) { + return ALBEDO_TEXTURE; + } else if (propertyName.contains("bump")) { + return BUMP_TEXTURE; + } else if (propertyName.contains("normal")) { + return NORMAL_TEXTURE; + } else if ((propertyName.contains("specular") && !propertyName.contains("tex_global_specular")) + || propertyName.contains("reflection")) { + return SPECULAR_TEXTURE; + } else if (propertyName.contains("tex_metallic_map")) { + return METALLIC_TEXTURE; + } else if (propertyName.contains("shininess")) { + return GLOSS_TEXTURE; + } else if (propertyName.contains("tex_roughness_map")) { + return ROUGHNESS_TEXTURE; + } else if (propertyName.contains("emissive")) { + return EMISSIVE_TEXTURE; + } else if (propertyName.contains("ambientcolor")) { + return LIGHTMAP_TEXTURE; + } else if (propertyName.contains("ambientfactor")) { + // we need to check what the ambient factor is, since that tells Interface to process this texture + // either as an occlusion texture or a light map + auto lambertMaterial = FbxCast(material); + + if (lambertMaterial->AmbientFactor == 0) { + return LIGHTMAP_TEXTURE; + } else if (lambertMaterial->AmbientFactor > 0) { + return OCCLUSION_TEXTURE; + } else { + return UNUSED_TEXTURE; + } + + } else if (propertyName.contains("tex_ao_map")) { + return OCCLUSION_TEXTURE; + } + + return UNUSED_TEXTURE; +} + +void FBXBaker::rewriteAndBakeSceneTextures() { + + // enumerate the surface materials to find the textures used in the scene + int numMaterials = _scene->GetMaterialCount(); + for (int i = 0; i < numMaterials; i++) { + FbxSurfaceMaterial* material = _scene->GetMaterial(i); + + if (material) { + // enumerate the properties of this material to see what texture channels it might have + FbxProperty property = material->GetFirstProperty(); + + while (property.IsValid()) { + // first check if this property has connected textures, if not we don't need to bother with it here + if (property.GetSrcObjectCount() > 0) { + + // figure out the type of texture from the material property + auto textureType = textureTypeForMaterialProperty(property, material); + + if (textureType != image::TextureUsage::UNUSED_TEXTURE) { + int numTextures = property.GetSrcObjectCount(); + + for (int j = 0; j < numTextures; j++) { + FbxFileTexture* fileTexture = property.GetSrcObject(j); + + // use QFileInfo to easily split up the existing texture filename into its components + QString fbxTextureFileName { fileTexture->GetFileName() }; + QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; + + // make sure this texture points to something and isn't one we've already re-mapped + if (!textureFileInfo.filePath().isEmpty() + && textureFileInfo.suffix() != BAKED_TEXTURE_EXT.mid(1)) { + + // construct the new baked texture file name and file path + // ensuring that the baked texture will have a unique name + // even if there was another texture with the same name at a different path + auto bakedTextureFileName = createBakedTextureFileName(textureFileInfo); + QString bakedTextureFilePath { + _bakedOutputDir + "/" + bakedTextureFileName + }; + _outputFiles.push_back(bakedTextureFilePath); + + qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName() + << "to" << bakedTextureFilePath; + + // figure out the URL to this texture, embedded or external + auto urlToTexture = getTextureURL(textureFileInfo, fileTexture); + + // write the new filename into the FBX scene + fileTexture->SetFileName(bakedTextureFilePath.toUtf8().data()); + + // write the relative filename to be the baked texture file name since it will + // be right beside the FBX + fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData()); + + if (!_bakingTextures.contains(urlToTexture)) { + // bake this texture asynchronously + bakeTexture(urlToTexture, textureType, _bakedOutputDir); + } + } + } + } + } + + property = material->GetNextProperty(property); + } + } + } +} + +void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir) { + // start a bake for this texture and add it to our list to keep track of + QSharedPointer bakingTexture { + new TextureBaker(textureURL, textureType, outputDir), + &TextureBaker::deleteLater + }; + + // make sure we hear when the baking texture is done + connect(bakingTexture.data(), &Baker::finished, this, &FBXBaker::handleBakedTexture); + + // keep a shared pointer to the baking texture + _bakingTextures.insert(textureURL, bakingTexture); + + // start baking the texture on one of our available worker threads + bakingTexture->moveToThread(_textureThreadGetter()); + QMetaObject::invokeMethod(bakingTexture.data(), "bake"); +} + +void FBXBaker::handleBakedTexture() { + TextureBaker* bakedTexture = qobject_cast(sender()); + + // make sure we haven't already run into errors, and that this is a valid texture + if (bakedTexture) { + if (!hasErrors()) { + if (!bakedTexture->hasErrors()) { + if (!_originalOutputDir.isEmpty()) { + // we've been asked to make copies of the originals, so we need to make copies of this if it is a linked texture + + // use the path to the texture being baked to determine if this was an embedded or a linked texture + + // it is embeddded if the texure being baked was inside the original output folder + // since that is where the FBX SDK places the .fbm folder it generates when importing the FBX + + auto originalOutputFolder = QUrl::fromLocalFile(_originalOutputDir); + + if (!originalOutputFolder.isParentOf(bakedTexture->getTextureURL())) { + // for linked textures we want to save a copy of original texture beside the original FBX + + qCDebug(model_baking) << "Saving original texture for" << bakedTexture->getTextureURL(); + + // check if we have a relative path to use for the texture + auto relativeTexturePath = texturePathRelativeToFBX(_fbxURL, bakedTexture->getTextureURL()); + + QFile originalTextureFile { + _originalOutputDir + "/" + relativeTexturePath + bakedTexture->getTextureURL().fileName() + }; + + if (relativeTexturePath.length() > 0) { + // make the folders needed by the relative path + } + + if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) { + qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName() + << "for" << _fbxURL; + } else { + handleError("Could not save original external texture " + originalTextureFile.fileName() + + " for " + _fbxURL.toString()); + return; + } + } + } + + + // now that this texture has been baked and handled, we can remove that TextureBaker from our hash + _bakingTextures.remove(bakedTexture->getTextureURL()); + + checkIfTexturesFinished(); + } else { + // there was an error baking this texture - add it to our list of errors + _errorList.append(bakedTexture->getErrors()); + + // we don't emit finished yet so that the other textures can finish baking first + _pendingErrorEmission = true; + + // now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list + _bakingTextures.remove(bakedTexture->getTextureURL()); + + checkIfTexturesFinished(); + } + } else { + // we have errors to attend to, so we don't do extra processing for this texture + // but we do need to remove that TextureBaker from our list + // and then check if we're done with all textures + _bakingTextures.remove(bakedTexture->getTextureURL()); + + checkIfTexturesFinished(); + } + } +} + +void FBXBaker::exportScene() { + // setup the exporter + FbxExporter* exporter = FbxExporter::Create(_sdkManager.get(), ""); + + // save the relative path to this FBX inside our passed output folder + + auto fileName = _fbxURL.fileName(); + auto baseName = fileName.left(fileName.lastIndexOf('.')); + auto bakedFilename = baseName + BAKED_FBX_EXTENSION; + + _bakedFBXFilePath = _bakedOutputDir + "/" + bakedFilename; + + bool exportStatus = exporter->Initialize(_bakedFBXFilePath.toLocal8Bit().data()); + + if (!exportStatus) { + // failed to initialize exporter, print an error and return + handleError("Failed to export FBX file at " + _fbxURL.toString() + " to " + _bakedFBXFilePath + + "- error: " + exporter->GetStatus().GetErrorString()); + } + + _outputFiles.push_back(_bakedFBXFilePath); + + // export the scene + exporter->Export(_scene); + + qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << _bakedFBXFilePath; +} + + +void FBXBaker::removeEmbeddedMediaFolder() { + // now that the bake is complete, remove the embedded media folder produced by the FBX SDK when it imports an FBX + //auto embeddedMediaFolderName = _fbxURL.fileName().replace(".fbx", ".fbm"); + //QDir(_bakedOutputDir + ORIGINAL_OUTPUT_SUBFOLDER + embeddedMediaFolderName).removeRecursively(); +} + +void FBXBaker::checkIfTexturesFinished() { + // check if we're done everything we need to do for this FBX + // and emit our finished signal if we're done + + if (_bakingTextures.isEmpty()) { + // remove the embedded media folder that the FBX SDK produces when reading the original + removeEmbeddedMediaFolder(); + + if (hasErrors()) { + // if we're checking for completion but we have errors + // that means one or more of our texture baking operations failed + + if (_pendingErrorEmission) { + emit finished(); + } + + return; + } else { + qCDebug(model_baking) << "Finished baking, emitting finsihed" << _fbxURL; + + emit finished(); + } + } +} diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h new file mode 100644 index 0000000000..0e52f2e4c3 --- /dev/null +++ b/libraries/baking/src/FBXBaker.h @@ -0,0 +1,105 @@ +// +// FBXBaker.h +// tools/oven/src +// +// Created by Stephen Birarda on 3/30/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 +// + +#ifndef hifi_FBXBaker_h +#define hifi_FBXBaker_h + +#include +#include +#include +#include + +#include "Baker.h" +#include "TextureBaker.h" + +#include "ModelBakingLoggingCategory.h" + +#include + +namespace fbxsdk { + class FbxManager; + class FbxProperty; + class FbxScene; + class FbxFileTexture; +} + +static const QString BAKED_FBX_EXTENSION = ".baked.fbx"; +using FBXSDKManagerUniquePointer = std::unique_ptr>; + +using TextureBakerThreadGetter = std::function; + +class FBXBaker : public Baker { + Q_OBJECT +public: + FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, + const QString& bakedOutputDir, const QString& originalOutputDir = ""); + + QUrl getFBXUrl() const { return _fbxURL; } + QString getBakedFBXFilePath() const { return _bakedFBXFilePath; } + std::vector getOutputFiles() const { return _outputFiles; } + +public slots: + // all calls to FBXBaker::bake for FBXBaker instances must be from the same thread + // because the Autodesk SDK will cause a crash if it is called from multiple threads + virtual void bake() override; + +signals: + void sourceCopyReadyToLoad(); + +private slots: + void bakeSourceCopy(); + void handleFBXNetworkReply(); + void handleBakedTexture(); + +private: + void setupOutputFolder(); + + void loadSourceFBX(); + + void importScene(); + void rewriteAndBakeSceneTextures(); + void exportScene(); + void removeEmbeddedMediaFolder(); + + void checkIfTexturesFinished(); + + QString createBakedTextureFileName(const QFileInfo& textureFileInfo); + QUrl getTextureURL(const QFileInfo& textureFileInfo, fbxsdk::FbxFileTexture* fileTexture); + + void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir); + + QUrl _fbxURL; + + QString _bakedFBXFilePath; + + QString _bakedOutputDir; + + // If set, the original FBX and textures will also be copied here + QString _originalOutputDir; + + QDir _tempDir; + QString _originalFBXFilePath; + + // List of baked output files, includes the FBX and textures + std::vector _outputFiles; + + static FBXSDKManagerUniquePointer _sdkManager; + fbxsdk::FbxScene* _scene { nullptr }; + + QMultiHash> _bakingTextures; + QHash _textureNameMatchCount; + + TextureBakerThreadGetter _textureThreadGetter; + + bool _pendingErrorEmission { false }; +}; + +#endif // hifi_FBXBaker_h diff --git a/tools/oven/src/ModelBakingLoggingCategory.cpp b/libraries/baking/src/ModelBakingLoggingCategory.cpp similarity index 100% rename from tools/oven/src/ModelBakingLoggingCategory.cpp rename to libraries/baking/src/ModelBakingLoggingCategory.cpp diff --git a/tools/oven/src/ModelBakingLoggingCategory.h b/libraries/baking/src/ModelBakingLoggingCategory.h similarity index 100% rename from tools/oven/src/ModelBakingLoggingCategory.h rename to libraries/baking/src/ModelBakingLoggingCategory.h diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp new file mode 100644 index 0000000000..70df511d2c --- /dev/null +++ b/libraries/baking/src/TextureBaker.cpp @@ -0,0 +1,131 @@ +// +// TextureBaker.cpp +// tools/oven/src +// +// Created by Stephen Birarda on 4/5/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 +// + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ModelBakingLoggingCategory.h" + +#include "TextureBaker.h" + +const QString BAKED_TEXTURE_EXT = ".ktx"; + +TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory) : + _textureURL(textureURL), + _textureType(textureType), + _outputDirectory(outputDirectory) +{ + // figure out the baked texture filename + auto originalFilename = textureURL.fileName(); + _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT; +} + +void TextureBaker::bake() { + // once our texture is loaded, kick off a the processing + connect(this, &TextureBaker::originalTextureLoaded, this, &TextureBaker::processTexture); + + // first load the texture (either locally or remotely) + loadTexture(); +} + +void TextureBaker::loadTexture() { + // check if the texture is local or first needs to be downloaded + if (_textureURL.isLocalFile()) { + // load up the local file + QFile localTexture { _textureURL.toLocalFile() }; + + if (!localTexture.open(QIODevice::ReadOnly)) { + handleError("Unable to open texture " + _textureURL.toString()); + return; + } + + _originalTexture = localTexture.readAll(); + + emit originalTextureLoaded(); + } else { + // remote file, kick off a download + auto& networkAccessManager = NetworkAccessManager::getInstance(); + + QNetworkRequest networkRequest; + + // setup the request to follow re-directs and always hit the network + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + + networkRequest.setUrl(_textureURL); + + qCDebug(model_baking) << "Downloading" << _textureURL; + + // kickoff the download, wait for slot to tell us it is done + auto networkReply = networkAccessManager.get(networkRequest); + connect(networkReply, &QNetworkReply::finished, this, &TextureBaker::handleTextureNetworkReply); + } +} + +void TextureBaker::handleTextureNetworkReply() { + auto requestReply = qobject_cast(sender()); + + if (requestReply->error() == QNetworkReply::NoError) { + qCDebug(model_baking) << "Downloaded texture" << _textureURL; + + // store the original texture so it can be passed along for the bake + _originalTexture = requestReply->readAll(); + + emit originalTextureLoaded(); + } else { + // add an error to our list stating that this texture could not be downloaded + handleError("Error downloading " + _textureURL.toString() + " - " + requestReply->errorString()); + } +} + +void TextureBaker::processTexture() { + auto processedTexture = image::processImage(_originalTexture, _textureURL.toString().toStdString(), + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType); + + if (!processedTexture) { + handleError("Could not process texture " + _textureURL.toString()); + return; + } + + // the baked textures need to have the source hash added for cache checks in Interface + // so we add that to the processed texture before handling it off to be serialized + auto hashData = QCryptographicHash::hash(_originalTexture, QCryptographicHash::Md5); + std::string hash = hashData.toHex().toStdString(); + processedTexture->setSourceHash(hash); + + auto memKTX = gpu::Texture::serialize(*processedTexture); + + if (!memKTX) { + handleError("Could not serialize " + _textureURL.toString() + " to KTX"); + return; + } + + const char* data = reinterpret_cast(memKTX->_storage->data()); + const size_t length = memKTX->_storage->size(); + + // attempt to write the baked texture to the destination file path + QFile bakedTextureFile { _outputDirectory.absoluteFilePath(_bakedTextureFileName) }; + + if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { + handleError("Could not write baked texture for " + _textureURL.toString()); + } + + qCDebug(model_baking) << "Baked texture" << _textureURL; + emit finished(); +} diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h new file mode 100644 index 0000000000..ee1e968f20 --- /dev/null +++ b/libraries/baking/src/TextureBaker.h @@ -0,0 +1,59 @@ +// +// TextureBaker.h +// tools/oven/src +// +// Created by Stephen Birarda on 4/5/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 +// + +#ifndef hifi_TextureBaker_h +#define hifi_TextureBaker_h + +#include +#include +#include + +#include + +#include "Baker.h" + +extern const QString BAKED_TEXTURE_EXT; + +class TextureBaker : public Baker { + Q_OBJECT + +public: + TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory); + + const QByteArray& getOriginalTexture() const { return _originalTexture; } + + QUrl getTextureURL() const { return _textureURL; } + + QString getDestinationFilePath() const { return _outputDirectory.absoluteFilePath(_bakedTextureFileName); } + QString getBakedTextureFileName() const { return _bakedTextureFileName; } + +public slots: + virtual void bake() override; + +signals: + void originalTextureLoaded(); + +private slots: + void processTexture(); + +private: + void loadTexture(); + void handleTextureNetworkReply(); + + QUrl _textureURL; + QByteArray _originalTexture; + image::TextureUsage::Type _textureType; + + QDir _outputDirectory; + QString _bakedTextureFileName; +}; + +#endif // hifi_TextureBaker_h diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index d9c073f213..3355ccd06e 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -1,4 +1,7 @@ set(TARGET_NAME fbx) setup_hifi_library() -link_hifi_libraries(shared model networking) -include_hifi_library_headers(gpu) + + + +link_hifi_libraries(shared model networking image) +include_hifi_library_headers(gpu image) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 6d4f586c52..b1d2cf17d4 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -846,12 +846,14 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QByteArray filename = subobject.properties.at(0).toByteArray(); QByteArray filepath = filename.replace('\\', '/'); filename = fileOnUrl(filepath, url); + qDebug() << "Filename" << filepath << filename; _textureFilepaths.insert(getID(object.properties), filepath); _textureFilenames.insert(getID(object.properties), filename); } else if (subobject.name == "TextureName" && subobject.properties.length() >= TEXTURE_NAME_MIN_SIZE) { // trim the name from the timestamp QString name = QString(subobject.properties.at(0).toByteArray()); name = name.left(name.indexOf('[')); + qDebug() << "Filename" << name; _textureNames.insert(getID(object.properties), name); } else if (subobject.name == "Texture_Alpha_Source" && subobject.properties.length() >= TEXTURE_ALPHA_SOURCE_MIN_SIZE) { tex.assign(tex.alphaSource, subobject.properties.at(0).value()); @@ -1840,5 +1842,7 @@ FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QStri reader._loadLightmaps = loadLightmaps; reader._lightmapLevel = lightmapLevel; + qDebug() << "Reading FBX: " << url; + return reader.extractFBXGeometry(mapping, url); } diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 704455c981..468d22ce9e 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -77,6 +77,8 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) { texdir += '/'; } _textureBaseUrl = resolveTextureBaseUrl(url, _url.resolved(texdir)); + } else { + _textureBaseUrl = _effectiveBaseURL; } auto animGraphVariant = mapping.value("animGraphUrl"); @@ -239,7 +241,10 @@ private: }; void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { - QThreadPool::globalInstance()->start(new GeometryReader(_self, _url, _mapping, data, _combineParts)); + qDebug() << "Processing geometry: " << _effectiveBaseURL; + _url = _effectiveBaseURL; + _textureBaseUrl = _effectiveBaseURL; + QThreadPool::globalInstance()->start(new GeometryReader(_self, _effectiveBaseURL, _mapping, data, _combineParts)); } void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxGeometry) { @@ -250,6 +255,7 @@ void GeometryDefinitionResource::setGeometryDefinition(FBXGeometry::Pointer fbxG QHash materialIDAtlas; for (const FBXMaterial& material : _fbxGeometry->materials) { materialIDAtlas[material.materialID] = _materials.size(); + qDebug() << "setGeometryDefinition() " << _textureBaseUrl; _materials.push_back(std::make_shared(material, _textureBaseUrl)); } @@ -342,6 +348,7 @@ Geometry::Geometry(const Geometry& geometry) { _materials.reserve(geometry._materials.size()); for (const auto& material : geometry._materials) { + qDebug() << "Geometry() no base url..."; _materials.push_back(std::make_shared(*material)); } @@ -427,6 +434,7 @@ void GeometryResource::deleter() { void GeometryResource::setTextures() { if (_fbxGeometry) { for (const FBXMaterial& material : _fbxGeometry->materials) { + qDebug() << "setTextures() " << _textureBaseUrl; _materials.push_back(std::make_shared(material, _textureBaseUrl)); } } @@ -528,6 +536,7 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : model::Material(*material._material) { + qDebug() << "Created network material with base url: " << textureBaseUrl; _textures = Textures(MapChannel::NUM_MAP_CHANNELS); if (!material.albedoTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, image::TextureUsage::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 6a1cc4c466..a122e03eb9 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -131,6 +131,7 @@ private: Geometry::Pointer& _geometryRef; }; + /// Stores cached model geometries. class ModelCache : public ResourceCache, public Dependency { Q_OBJECT diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 85bde4c2f1..5b43a4090f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #if DEBUG_DUMP_TEXTURE_LOADS #include @@ -189,8 +190,15 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs if (url.scheme() == RESOURCE_SCHEME) { return getResourceTexture(url); } + auto modifiedUrl = url; + if (type == image::TextureUsage::CUBE_TEXTURE) { + QUrlQuery query { url.query() }; + query.addQueryItem("skybox", ""); + qDebug() << "Updating cubemap texture query from" << url.query() << "to" << query.toString(); + modifiedUrl.setQuery(query.toString()); + } TextureExtra extra = { type, content, maxNumPixels }; - return ResourceCache::getResource(url, QUrl(), &extra).staticCast(); + return ResourceCache::getResource(modifiedUrl, QUrl(), &extra).staticCast(); } gpu::TexturePointer TextureCache::getTextureByHash(const std::string& hash) { diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index e228582ca9..01fe971125 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -63,7 +63,7 @@ void AssetResourceRequest::doSend() { // This is an ATP path, we'll need to figure out what the mapping is. // This may incur a roundtrip to the asset-server, or it may return immediately from the cache in AssetClient. - auto path = _url.path(); + auto path = _url.path() + (_url.hasQuery() ? "?" + _url.query() : ""); requestMappingForPath(path); } } @@ -93,10 +93,8 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { // if we got a redirected path we need to store that with the resource request as relative path URL if (request->wasRedirected()) { + qDebug() << "Request was redirected"; _relativePathURL = ATP_SCHEME + request->getRedirectedPath(); - - // truncate the filename for the re-directed asset so we actually have a path - _relativePathURL = _relativePathURL.adjusted(QUrl::RemoveFilename); } break; diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index bd7c05bdf7..5fb9c32a0b 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -96,6 +96,9 @@ void GetMappingRequest::doStart() { // if it did grab that re-directed path if (_wasRedirected) { _redirectedPath = message->readString(); + qDebug() << "Got redirected from " << _path << " to " << _redirectedPath; + } else { + qDebug() << "Not redirected: " << _path; } } diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index fbdfa4b87a..b7ab1269f9 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -731,6 +731,13 @@ void Resource::handleReplyFinished() { if (result == ResourceRequest::Success) { auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString()); qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); + + auto relativePathURL = _request->getRelativePathUrl(); + qDebug() << "Relative path is: " << relativePathURL; + if (!relativePathURL.isEmpty()) { + qDebug() << "setting effective path"; + _effectiveBaseURL = relativePathURL; + } auto data = _request->getData(); emit loaded(data); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index f94e1e26d2..40df6418a5 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -452,6 +452,7 @@ protected: bool handleFailedRequest(ResourceRequest::Result result); QUrl _url; + QUrl _effectiveBaseURL{ _url }; QUrl _activeUrl; ByteRange _requestByteRange; diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index bae0882e97..0a4dc12293 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -66,6 +66,7 @@ public: Result getResult() const { return _result; } QString getResultString() const; QUrl getUrl() const { return _url; } + QUrl getRelativePathUrl() const { return _relativePathURL; } bool loadedFromCache() const { return _loadedFromCache; } bool getRangeRequestSuccessful() const { return _rangeRequestSuccessful; } bool getTotalSizeOfResource() const { return _totalSizeOfResource; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index ea831bd415..6b57c8a3cb 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -139,11 +139,16 @@ public: const static QSet getNonVerifiedPackets() { const static QSet NON_VERIFIED_PACKETS = QSet() - << PacketTypeEnum::Value::NodeJsonStats << PacketTypeEnum::Value::EntityQuery - << PacketTypeEnum::Value::OctreeDataNack << PacketTypeEnum::Value::EntityEditNack - << PacketTypeEnum::Value::DomainListRequest << PacketTypeEnum::Value::StopNode - << PacketTypeEnum::Value::DomainDisconnectRequest << PacketTypeEnum::Value::UsernameFromIDRequest - << PacketTypeEnum::Value::NodeKickRequest << PacketTypeEnum::Value::NodeMuteRequest; + << PacketTypeEnum::Value::NodeJsonStats + << PacketTypeEnum::Value::EntityQuery + << PacketTypeEnum::Value::OctreeDataNack + << PacketTypeEnum::Value::EntityEditNack + << PacketTypeEnum::Value::DomainListRequest + << PacketTypeEnum::Value::StopNode + << PacketTypeEnum::Value::DomainDisconnectRequest + << PacketTypeEnum::Value::UsernameFromIDRequest + << PacketTypeEnum::Value::NodeKickRequest + << PacketTypeEnum::Value::NodeMuteRequest; return NON_VERIFIED_PACKETS; } diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index f4fca3304c..0d692b5465 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME oven) setup_hifi_project(Widgets Gui Concurrent) -link_hifi_libraries(networking shared image gpu ktx) +link_hifi_libraries(networking shared image gpu ktx fbx baking) setup_memory_debugger() From cefd16ad9585b1c260b2e027cfad7ea36686f721 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Aug 2017 17:12:47 -0700 Subject: [PATCH 018/202] Fix asset server debug messages logging category --- assignment-client/src/assets/AssetServer.cpp | 98 +++++++++---------- .../src/assets/AssetServerLogging.cpp | 14 +++ .../src/assets/AssetServerLogging.h | 19 ++++ 3 files changed, 82 insertions(+), 49 deletions(-) create mode 100644 assignment-client/src/assets/AssetServerLogging.cpp create mode 100644 assignment-client/src/assets/AssetServerLogging.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 6e3195f570..e28ad9ead8 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -13,6 +13,7 @@ #include "AssetServer.h" #include +#include #include #include @@ -27,17 +28,16 @@ #include #include +#include +#include +#include #include #include -#include "NetworkLogging.h" -#include "NodeType.h" +#include "AssetServerLogging.h" #include "SendAssetTask.h" #include "UploadAssetTask.h" -#include -#include -#include static const uint8_t MIN_CORES_FOR_MULTICORE = 4; static const uint8_t CPU_AFFINITY_COUNT_HIGH = 2; @@ -196,7 +196,7 @@ void updateConsumedCores() { if (isInterfaceRunning) { coreCount = coreCount > MIN_CORES_FOR_MULTICORE ? CPU_AFFINITY_COUNT_HIGH : CPU_AFFINITY_COUNT_LOW; } - qDebug() << "Setting max consumed cores to " << coreCount; + qCDebug(asset_server) << "Setting max consumed cores to " << coreCount; setMaxCores(coreCount); } @@ -236,7 +236,7 @@ AssetServer::AssetServer(ReceivedMessage& message) : void AssetServer::run() { - qDebug() << "Waiting for connection to domain to request settings from domain-server."; + qCDebug(asset_server) << "Waiting for connection to domain to request settings from domain-server."; // wait until we have the domain-server settings, otherwise we bail DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); @@ -257,7 +257,7 @@ void AssetServer::completeSetup() { static const QString ASSET_SERVER_SETTINGS_KEY = "asset_server"; if (!settingsObject.contains(ASSET_SERVER_SETTINGS_KEY)) { - qCritical() << "Received settings from the domain-server with no asset-server section. Stopping assignment."; + qCCritical(asset_server) << "Received settings from the domain-server with no asset-server section. Stopping assignment."; setFinished(true); return; } @@ -272,7 +272,7 @@ void AssetServer::completeSetup() { const int BITS_PER_MEGABITS = 1000 * 1000; int maxBandwidth = maxBandwidthFloat * BITS_PER_MEGABITS; nodeList->setConnectionMaxBandwidth(maxBandwidth); - qInfo() << "Set maximum bandwith per connection to" << maxBandwidthFloat << "Mb/s." + qCInfo(asset_server) << "Set maximum bandwith per connection to" << maxBandwidthFloat << "Mb/s." " (" << maxBandwidth << "bits/s)"; } @@ -281,7 +281,7 @@ void AssetServer::completeSetup() { auto assetsJSONValue = assetServerObject[ASSETS_PATH_OPTION]; if (!assetsJSONValue.isString()) { - qCritical() << "Received an assets path from the domain-server that could not be parsed. Stopping assignment."; + qCCritical(asset_server) << "Received an assets path from the domain-server that could not be parsed. Stopping assignment."; setFinished(true); return; } @@ -298,19 +298,19 @@ void AssetServer::completeSetup() { _resourcesDirectory = QDir(absoluteFilePath); - qDebug() << "Creating resources directory"; + qCDebug(asset_server) << "Creating resources directory"; _resourcesDirectory.mkpath("."); _filesDirectory = _resourcesDirectory; if (!_resourcesDirectory.mkpath(ASSET_FILES_SUBDIR) || !_filesDirectory.cd(ASSET_FILES_SUBDIR)) { - qCritical() << "Unable to create file directory for asset-server files. Stopping assignment."; + qCCritical(asset_server) << "Unable to create file directory for asset-server files. Stopping assignment."; setFinished(true); return; } // load whatever mappings we currently have from the local file if (loadMappingsFromFile()) { - qInfo() << "Serving files from: " << _filesDirectory.path(); + qCInfo(asset_server) << "Serving files from: " << _filesDirectory.path(); // Check the asset directory to output some information about what we have auto files = _filesDirectory.entryList(QDir::Files); @@ -318,7 +318,7 @@ void AssetServer::completeSetup() { QRegExp hashFileRegex { ASSET_HASH_REGEX_STRING }; auto hashedFiles = files.filter(hashFileRegex); - qInfo() << "There are" << hashedFiles.size() << "asset files in the asset directory."; + qCInfo(asset_server) << "There are" << hashedFiles.size() << "asset files in the asset directory."; if (_fileMappings.count() > 0) { cleanupUnmappedFiles(); @@ -328,7 +328,7 @@ void AssetServer::completeSetup() { bakeAssets(); } else { - qCritical() << "Asset Server assignment will not continue because mapping file could not be loaded."; + qCCritical(asset_server) << "Asset Server assignment will not continue because mapping file could not be loaded."; setFinished(true); } } @@ -341,7 +341,7 @@ void AssetServer::cleanupUnmappedFiles() { // grab the currently mapped hashes auto mappedHashes = _fileMappings.values(); - qInfo() << "Performing unmapped asset cleanup."; + qCInfo(asset_server) << "Performing unmapped asset cleanup."; for (const auto& fileInfo : files) { if (hashFileRegex.exactMatch(fileInfo.fileName())) { @@ -350,9 +350,9 @@ void AssetServer::cleanupUnmappedFiles() { QFile removeableFile { fileInfo.absoluteFilePath() }; if (removeableFile.remove()) { - qDebug() << "\tDeleted" << fileInfo.fileName() << "from asset files directory since it is unmapped."; + qCDebug(asset_server) << "\tDeleted" << fileInfo.fileName() << "from asset files directory since it is unmapped."; } else { - qDebug() << "\tAttempt to delete unmapped file" << fileInfo.fileName() << "failed"; + qCDebug(asset_server) << "\tAttempt to delete unmapped file" << fileInfo.fileName() << "failed"; } } } @@ -538,7 +538,7 @@ void AssetServer::handleAssetGetInfo(QSharedPointer message, Sh MessageID messageID; if (message->getSize() < qint64(SHA256_HASH_LENGTH + sizeof(messageID))) { - qDebug() << "ERROR bad file request"; + qCDebug(asset_server) << "ERROR bad file request"; return; } @@ -557,11 +557,11 @@ void AssetServer::handleAssetGetInfo(QSharedPointer message, Sh QFileInfo fileInfo { _filesDirectory.filePath(fileName) }; if (fileInfo.exists() && fileInfo.isReadable()) { - qDebug() << "Opening file: " << fileInfo.filePath(); + qCDebug(asset_server) << "Opening file: " << fileInfo.filePath(); replyPacket->writePrimitive(AssetServerError::NoError); replyPacket->writePrimitive(fileInfo.size()); } else { - qDebug() << "Asset not found: " << QString(hexHash); + qCDebug(asset_server) << "Asset not found: " << QString(hexHash); replyPacket->writePrimitive(AssetServerError::AssetNotFound); } @@ -574,7 +574,7 @@ void AssetServer::handleAssetGet(QSharedPointer message, Shared auto minSize = qint64(sizeof(MessageID) + SHA256_HASH_LENGTH + sizeof(DataOffset) + sizeof(DataOffset)); if (message->getSize() < minSize) { - qDebug() << "ERROR bad file request"; + qCDebug(asset_server) << "ERROR bad file request"; return; } @@ -586,7 +586,7 @@ void AssetServer::handleAssetGet(QSharedPointer message, Shared void AssetServer::handleAssetUpload(QSharedPointer message, SharedNodePointer senderNode) { if (senderNode->getCanWriteToAssetServer()) { - qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID()); + qCDebug(asset_server) << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID()); auto task = new UploadAssetTask(message, senderNode, _filesDirectory); _transferTaskPool.start(task); @@ -701,12 +701,12 @@ bool AssetServer::loadMappingsFromFile() { bool shouldDrop = false; if (!isValidFilePath(it.key())) { - qWarning() << "Will not keep mapping for" << it.key() << "since it is not a valid path."; + qCWarning(asset_server) << "Will not keep mapping for" << it.key() << "since it is not a valid path."; shouldDrop = true; } if (!isValidHash(it.value().toString())) { - qWarning() << "Will not keep mapping for" << it.key() << "since it does not have a valid hash."; + qCWarning(asset_server) << "Will not keep mapping for" << it.key() << "since it does not have a valid hash."; shouldDrop = true; } @@ -717,15 +717,15 @@ bool AssetServer::loadMappingsFromFile() { } } - qInfo() << "Loaded" << _fileMappings.count() << "mappings from map file at" << mapFilePath; + qCInfo(asset_server) << "Loaded" << _fileMappings.count() << "mappings from map file at" << mapFilePath; return true; } } - qCritical() << "Failed to read mapping file at" << mapFilePath; + qCCritical(asset_server) << "Failed to read mapping file at" << mapFilePath; return false; } else { - qInfo() << "No existing mappings loaded from file since no file was found at" << mapFilePath; + qCInfo(asset_server) << "No existing mappings loaded from file since no file was found at" << mapFilePath; } return true; @@ -740,13 +740,13 @@ bool AssetServer::writeMappingsToFile() { QJsonDocument jsonDocument { jsonObject }; if (mapFile.write(jsonDocument.toJson()) != -1) { - qDebug() << "Wrote JSON mappings to file at" << mapFilePath; + qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath; return true; } else { - qWarning() << "Failed to write JSON mappings to file at" << mapFilePath; + qCWarning(asset_server) << "Failed to write JSON mappings to file at" << mapFilePath; } } else { - qWarning() << "Failed to open map file at" << mapFilePath; + qCWarning(asset_server) << "Failed to open map file at" << mapFilePath; } return false; @@ -756,12 +756,12 @@ bool AssetServer::setMapping(AssetPath path, AssetHash hash) { path = path.trimmed(); if (!isValidFilePath(path)) { - qWarning() << "Cannot set a mapping for invalid path:" << path << "=>" << hash; + qCWarning(asset_server) << "Cannot set a mapping for invalid path:" << path << "=>" << hash; return false; } if (!isValidHash(hash)) { - qWarning() << "Cannot set a mapping for invalid hash" << path << "=>" << hash; + qCWarning(asset_server) << "Cannot set a mapping for invalid hash" << path << "=>" << hash; return false; } @@ -774,7 +774,7 @@ bool AssetServer::setMapping(AssetPath path, AssetHash hash) { // attempt to write to file if (writeMappingsToFile()) { // persistence succeeded, we are good to go - qDebug() << "Set mapping:" << path << "=>" << hash; + qCDebug(asset_server) << "Set mapping:" << path << "=>" << hash; maybeBake(path, hash); return true; } else { @@ -785,7 +785,7 @@ bool AssetServer::setMapping(AssetPath path, AssetHash hash) { _fileMappings[path] = oldMapping; } - qWarning() << "Failed to persist mapping:" << path << "=>" << hash; + qCWarning(asset_server) << "Failed to persist mapping:" << path << "=>" << hash; return false; } @@ -825,9 +825,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { auto sizeNow = _fileMappings.size(); if (sizeBefore != sizeNow) { - qDebug() << "Deleted" << sizeBefore - sizeNow << "mappings in folder: " << path; + qCDebug(asset_server) << "Deleted" << sizeBefore - sizeNow << "mappings in folder: " << path; } else { - qDebug() << "Did not find any mappings to delete in folder:" << path; + qCDebug(asset_server) << "Did not find any mappings to delete in folder:" << path; } } else { @@ -836,9 +836,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { // add this hash to the list we need to check for asset removal from server hashesToCheckForDeletion << oldMapping.toString(); - qDebug() << "Deleted a mapping:" << path << "=>" << oldMapping.toString(); + qCDebug(asset_server) << "Deleted a mapping:" << path << "=>" << oldMapping.toString(); } else { - qDebug() << "Unable to delete a mapping that was not found:" << path; + qCDebug(asset_server) << "Unable to delete a mapping that was not found:" << path; } } } @@ -864,15 +864,15 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { QFile removeableFile { _filesDirectory.absoluteFilePath(hash) }; if (removeableFile.remove()) { - qDebug() << "\tDeleted" << hash << "from asset files directory since it is now unmapped."; + qCDebug(asset_server) << "\tDeleted" << hash << "from asset files directory since it is now unmapped."; } else { - qDebug() << "\tAttempt to delete unmapped file" << hash << "failed"; + qCDebug(asset_server) << "\tAttempt to delete unmapped file" << hash << "failed"; } } return true; } else { - qWarning() << "Failed to persist deleted mappings, rolling back"; + qCWarning(asset_server) << "Failed to persist deleted mappings, rolling back"; // we didn't delete the previous mapping, put it back in our in-memory representation _fileMappings = oldMappings; @@ -886,7 +886,7 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { newPath = newPath.trimmed(); if (!isValidFilePath(oldPath) || !isValidFilePath(newPath)) { - qWarning() << "Cannot perform rename with invalid paths - both should have leading forward and no ending slashes:" + qCWarning(asset_server) << "Cannot perform rename with invalid paths - both should have leading forward and no ending slashes:" << oldPath << "=>" << newPath; return false; @@ -896,7 +896,7 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { if (pathIsFolder(oldPath)) { if (!pathIsFolder(newPath)) { // we were asked to rename a path to a folder to a path that isn't a folder, this is a fail - qWarning() << "Cannot rename mapping from folder path" << oldPath << "to file path" << newPath; + qCWarning(asset_server) << "Cannot rename mapping from folder path" << oldPath << "to file path" << newPath; return false; } @@ -922,21 +922,21 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { if (writeMappingsToFile()) { // persisted the changed mappings, return success - qDebug() << "Renamed folder mapping:" << oldPath << "=>" << newPath; + qCDebug(asset_server) << "Renamed folder mapping:" << oldPath << "=>" << newPath; return true; } else { // couldn't persist the renamed paths, rollback and return failure _fileMappings = oldMappings; - qWarning() << "Failed to persist renamed folder mapping:" << oldPath << "=>" << newPath; + qCWarning(asset_server) << "Failed to persist renamed folder mapping:" << oldPath << "=>" << newPath; return false; } } else { if (pathIsFolder(newPath)) { // we were asked to rename a path to a file to a path that is a folder, this is a fail - qWarning() << "Cannot rename mapping from file path" << oldPath << "to folder path" << newPath; + qCWarning(asset_server) << "Cannot rename mapping from file path" << oldPath << "to folder path" << newPath; return false; } @@ -952,7 +952,7 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { if (writeMappingsToFile()) { // persisted the renamed mapping, return success - qDebug() << "Renamed mapping:" << oldPath << "=>" << newPath; + qCDebug(asset_server) << "Renamed mapping:" << oldPath << "=>" << newPath; return true; } else { @@ -967,7 +967,7 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { _fileMappings.remove(newPath); } - qDebug() << "Failed to persist renamed mapping:" << oldPath << "=>" << newPath; + qCDebug(asset_server) << "Failed to persist renamed mapping:" << oldPath << "=>" << newPath; return false; } diff --git a/assignment-client/src/assets/AssetServerLogging.cpp b/assignment-client/src/assets/AssetServerLogging.cpp new file mode 100644 index 0000000000..39a02107ea --- /dev/null +++ b/assignment-client/src/assets/AssetServerLogging.cpp @@ -0,0 +1,14 @@ +// +// AssetServerLogging.cpp +// assignment-client/src/assets +// +// Created by Clement Brisset on 8/9/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 +// + +#include "AssetServerLogging.h" + +Q_LOGGING_CATEGORY(asset_server, "hifi.asset-server") diff --git a/assignment-client/src/assets/AssetServerLogging.h b/assignment-client/src/assets/AssetServerLogging.h new file mode 100644 index 0000000000..986e01ecc5 --- /dev/null +++ b/assignment-client/src/assets/AssetServerLogging.h @@ -0,0 +1,19 @@ +// +// AssetServerLogging.h +// assignment-client/src/assets +// +// Created by Clement Brisset on 8/9/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 +// + +#ifndef hifi_AssetServerLogging_h +#define hifi_AssetServerLogging_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(asset_server) + +#endif // hifi_AssetServerLogging_h From fac6015bb0e391850d3d68b28d40b2daf3e6921d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 9 Aug 2017 18:25:58 -0700 Subject: [PATCH 019/202] Asset baking status first pass --- assignment-client/src/assets/AssetServer.cpp | 11 ++++++ assignment-client/src/assets/AssetServer.h | 3 ++ assignment-client/src/assets/AutoBaker.cpp | 38 +++++++++++++++++++ assignment-client/src/assets/AutoBaker.h | 39 ++++++++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 assignment-client/src/assets/AutoBaker.cpp create mode 100644 assignment-client/src/assets/AutoBaker.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index e28ad9ead8..1f74a401e3 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -331,6 +331,17 @@ void AssetServer::completeSetup() { qCCritical(asset_server) << "Asset Server assignment will not continue because mapping file could not be loaded."; setFinished(true); } + + QRegExp hashFileRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}" }; + auto files = _filesDirectory.entryInfoList(QDir::Files); + auto mappedHashes = _fileMappings.values(); + for (const auto& fileInfo : files) { + AssetHash hash = fileInfo.fileName(); + bool isAsset = hashFileRegex.exactMatch(hash); + if (isAsset && _baker.assetNeedsBaking(hash)) { + _baker.addPendingBake(hash); + } + } } void AssetServer::cleanupUnmappedFiles() { diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 2dfc5f6c35..2ce223760f 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -19,6 +19,7 @@ #include #include "AssetUtils.h" +#include "AutoBaker.h" #include "ReceivedMessage.h" class BakeAssetTask : public QObject, public QRunnable { @@ -105,6 +106,8 @@ private: QHash> _pendingBakes; QThreadPool _bakingTaskPool; + + AutoBaker _baker; }; #endif diff --git a/assignment-client/src/assets/AutoBaker.cpp b/assignment-client/src/assets/AutoBaker.cpp new file mode 100644 index 0000000000..c56e00d025 --- /dev/null +++ b/assignment-client/src/assets/AutoBaker.cpp @@ -0,0 +1,38 @@ +// +// AutoBaker.cpp +// assignment-client/src/assets +// +// Created by Clement Brisset on 8/9/17 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AutoBaker.h" + +void AutoBaker::addPendingBake(AssetHash hash) { + _pendingBakes.push_back(hash); +} + +bool AutoBaker::assetNeedsBaking(AssetHash hash) { + return true; +} + +AutoBaker::Status AutoBaker::getAssetStatus(AssetHash hash) { + auto pendingIt = std::find(_pendingBakes.cbegin(), _pendingBakes.cend(), hash); + if (pendingIt != _pendingBakes.cend()) { + return Pending; + } + + auto bakingIt = std::find(_currentlyBaking.cbegin(), _currentlyBaking.cend(), hash); + if (bakingIt != _currentlyBaking.cend()) { + return Baking; + } + + if (assetNeedsBaking(hash)) { + return NotBaked; + } else { + return Baked; + } +} diff --git a/assignment-client/src/assets/AutoBaker.h b/assignment-client/src/assets/AutoBaker.h new file mode 100644 index 0000000000..7b92118aee --- /dev/null +++ b/assignment-client/src/assets/AutoBaker.h @@ -0,0 +1,39 @@ +// +// AutoBaker.h +// assignment-client/src/assets +// +// Created by Clement Brisset on 8/9/17 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AutoBaker_h +#define hifi_AutoBaker_h + +#include + +#include "AssetUtils.h" + +class AutoBaker { +public: + enum Status { + NotBaked, + Pending, + Baking, + Baked + }; + + void addPendingBake(AssetHash hash); + + bool assetNeedsBaking(AssetHash hash); + + Status getAssetStatus(AssetHash hash); + +private: + std::vector _pendingBakes; + std::vector _currentlyBaking; +}; + +#endif /* hifi_AutoBaker_h */ From fd3156b57c2a6df23cbe3ce2b55dd7e0724b84d8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 10 Aug 2017 18:00:16 -0700 Subject: [PATCH 020/202] Move BakingStatus --- assignment-client/src/assets/AssetServer.cpp | 1 + assignment-client/src/assets/AutoBaker.h | 9 +------ libraries/networking/src/AssetUtils.cpp | 13 +++++++++ libraries/networking/src/AssetUtils.h | 9 +++++++ libraries/networking/src/MappingRequest.cpp | 2 ++ libraries/shared/src/shared/Algorithms.h | 28 ++++++++++++++++++++ 6 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 libraries/shared/src/shared/Algorithms.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 1f74a401e3..ea0010cb26 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -489,6 +489,7 @@ void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedN for (auto it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++ it) { replyPacket.writeString(it.key()); replyPacket.write(QByteArray::fromHex(it.value().toString().toUtf8())); + replyPacket.writePrimitive(_baker.getAssetStatus(it.value().toString())); } } diff --git a/assignment-client/src/assets/AutoBaker.h b/assignment-client/src/assets/AutoBaker.h index 7b92118aee..805130bf52 100644 --- a/assignment-client/src/assets/AutoBaker.h +++ b/assignment-client/src/assets/AutoBaker.h @@ -18,18 +18,11 @@ class AutoBaker { public: - enum Status { - NotBaked, - Pending, - Baking, - Baked - }; - void addPendingBake(AssetHash hash); bool assetNeedsBaking(AssetHash hash); - Status getAssetStatus(AssetHash hash); + BakingStatus getAssetStatus(AssetHash hash); private: std::vector _pendingBakes; diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index 7818c8e5ce..7c8ffd6746 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -84,3 +84,16 @@ bool isValidHash(const AssetHash& hash) { QRegExp hashRegex { ASSET_HASH_REGEX_STRING }; return hashRegex.exactMatch(hash); } + +QString bakingStatusToString(BakingStatus status) { + switch (status) { + case NotBaked: + return "Not Baked"; + case Pending: + return "Pending"; + case Baking: + return "Baking"; + case Baked: + return "Baked"; + } +} diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index 4137193274..a94363126c 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -52,6 +52,13 @@ enum AssetMappingOperationType : uint8_t { Rename }; +enum BakingStatus { + NotBaked, + Pending, + Baking, + Baked +}; + QUrl getATPUrl(const QString& hash); QByteArray hashData(const QByteArray& data); @@ -63,4 +70,6 @@ bool isValidFilePath(const AssetPath& path); bool isValidPath(const AssetPath& path); bool isValidHash(const QString& hashString); +QString bakingStatusToString(BakingStatus status); + #endif // hifi_AssetUtils_h diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index 5fb9c32a0b..437c7c1b41 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -136,6 +136,8 @@ void GetAllMappingsRequest::doStart() { for (auto i = 0; i < numberOfMappings; ++i) { auto path = message->readString(); auto hash = message->read(SHA256_HASH_LENGTH).toHex(); + BakingStatus status; + message->readPrimitive(&status); _mappings[path] = hash; } } diff --git a/libraries/shared/src/shared/Algorithms.h b/libraries/shared/src/shared/Algorithms.h new file mode 100644 index 0000000000..4d486efc43 --- /dev/null +++ b/libraries/shared/src/shared/Algorithms.h @@ -0,0 +1,28 @@ +// +// Algorithms.h +// libraries/shared/src/shared +// +// Created by Clement Brisset on 8/9/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 +// + +#ifndef Algorithms_h +#define Algorithms_h + +#include + +namespace alg { + + + + + + + + +} + +#endif /* Algorithms_hpp */ From 859ec57ded4f8925e5201ca1ce59df99c86a44ea Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 11 Aug 2017 15:27:13 -0700 Subject: [PATCH 021/202] Move Baking Status --- assignment-client/src/assets/AutoBaker.cpp | 13 +++++++----- libraries/shared/src/shared/Algorithms.h | 24 +++++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/assets/AutoBaker.cpp b/assignment-client/src/assets/AutoBaker.cpp index c56e00d025..fae895e635 100644 --- a/assignment-client/src/assets/AutoBaker.cpp +++ b/assignment-client/src/assets/AutoBaker.cpp @@ -10,23 +10,26 @@ // #include "AutoBaker.h" +#include + +using namespace alg; void AutoBaker::addPendingBake(AssetHash hash) { _pendingBakes.push_back(hash); + + // Maybe start baking it right away } bool AutoBaker::assetNeedsBaking(AssetHash hash) { return true; } -AutoBaker::Status AutoBaker::getAssetStatus(AssetHash hash) { - auto pendingIt = std::find(_pendingBakes.cbegin(), _pendingBakes.cend(), hash); - if (pendingIt != _pendingBakes.cend()) { +BakingStatus AutoBaker::getAssetStatus(AssetHash hash) { + if (find(_pendingBakes, hash) != std::end(_pendingBakes)) { return Pending; } - auto bakingIt = std::find(_currentlyBaking.cbegin(), _currentlyBaking.cend(), hash); - if (bakingIt != _currentlyBaking.cend()) { + if (find(_currentlyBaking, hash) != std::end(_currentlyBaking)) { return Baking; } diff --git a/libraries/shared/src/shared/Algorithms.h b/libraries/shared/src/shared/Algorithms.h index 4d486efc43..b85a3df6c5 100644 --- a/libraries/shared/src/shared/Algorithms.h +++ b/libraries/shared/src/shared/Algorithms.h @@ -9,20 +9,30 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef Algorithms_h -#define Algorithms_h +#ifndef hifi_Algorithms_h +#define hifi_Algorithms_h #include +#include +#include namespace alg { +template +auto find(const Container& container, const ValueType& value) -> decltype(std::begin(container)) { + return std::find(std::begin(container), std::end(container), value); +} +template +auto find_if(const Container& container, Predicate&& predicate) -> decltype(std::begin(container)) { + return std::find_if(std::begin(container), std::end(container), std::forward(predicate)); +} - - - - +template +auto find_if_not(const Container& container, Predicate&& predicate) -> decltype(std::begin(container)) { + return std::find_if_not(std::begin(container), std::end(container), std::forward(predicate)); +} } -#endif /* Algorithms_hpp */ +#endif // hifi_Algorithms_hpp From 28c9aa031ca8a4cc3fdf2e128324635760703c38 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 21 Aug 2017 15:59:19 -0700 Subject: [PATCH 022/202] Setup script model status --- .../AssetMappingsScriptingInterface.cpp | 47 ++++++++++++------- .../AssetMappingsScriptingInterface.h | 4 ++ libraries/networking/src/AssetUtils.h | 8 +++- libraries/networking/src/MappingRequest.cpp | 2 +- libraries/networking/src/MappingRequest.h | 2 +- tools/atp-client/src/ATPClientApp.cpp | 2 +- 6 files changed, 45 insertions(+), 20 deletions(-) diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 5a81fe8749..b3509075bd 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -21,19 +21,6 @@ #include #include -void AssetMappingModel::clear() { - // make sure we are on the same thread before we touch the hash - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "clear"); - return; - } - - qDebug() << "Clearing loaded asset mappings for Asset Browser"; - - _pathToItemMap.clear(); - QStandardItemModel::clear(); -} - AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() { _proxyModel.setSourceModel(&_assetMappingModel); _proxyModel.setSortRole(Qt::DisplayRole); @@ -154,7 +141,7 @@ void AssetMappingsScriptingInterface::getAllMappings(QJSValue callback) { auto map = callback.engine()->newObject(); for (auto& kv : mappings ) { - map.setProperty(kv.first, kv.second); + map.setProperty(kv.first, kv.second.hash); } if (callback.isCallable()) { @@ -184,6 +171,10 @@ void AssetMappingsScriptingInterface::renameMapping(QString oldPath, QString new request->start(); } +AssetMappingModel::AssetMappingModel() { + setupHeaders(); +} + bool AssetMappingModel::isKnownFolder(QString path) const { if (!path.endsWith("/")) { return false; @@ -228,17 +219,19 @@ void AssetMappingModel::refresh() { if (it == _pathToItemMap.end()) { auto item = new QStandardItem(parts[i]); bool isFolder = i < length - 1; + auto statusString = isFolder ? "--" : bakingStatusToString(mapping.second.status); item->setData(isFolder ? fullPath + "/" : fullPath, Qt::UserRole); item->setData(isFolder, Qt::UserRole + 1); item->setData(parts[i], Qt::UserRole + 2); item->setData("atp:" + fullPath, Qt::UserRole + 3); item->setData(fullPath, Qt::UserRole + 4); + item->setData(statusString, Qt::UserRole + 5); + if (lastItem) { - lastItem->setChild(lastItem->rowCount(), 0, item); + lastItem->appendRow(item); } else { appendRow(item); } - lastItem = item; _pathToItemMap[fullPath] = lastItem; } else { @@ -300,3 +293,25 @@ void AssetMappingModel::refresh() { request->start(); } + +void AssetMappingModel::clear() { + // make sure we are on the same thread before we touch the hash + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "clear"); + return; + } + + qDebug() << "Clearing loaded asset mappings for Asset Browser"; + + _pathToItemMap.clear(); + QStandardItemModel::clear(); + setupHeaders(); // restore headers +} + +void AssetMappingModel::setupHeaders() { + setHorizontalHeaderLabels(QStringList() << "Name" << "Use Baked?"); + QHash roleNames; + roleNames[Qt::DisplayRole] = "name"; + roleNames[Qt::UserRole + 5] = "baked"; + setItemRoleNames(roleNames); +} \ No newline at end of file diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index b7fcea2491..948bc9c9e7 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -26,6 +26,8 @@ class AssetMappingModel : public QStandardItemModel { Q_OBJECT public: + AssetMappingModel(); + Q_INVOKABLE void refresh(); bool isKnownMapping(QString path) const { return _pathToItemMap.contains(path); } @@ -38,6 +40,8 @@ signals: void errorGettingMappings(QString errorString); private: + void setupHeaders(); + QHash _pathToItemMap; }; diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index a94363126c..4b78d8aaaf 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -23,7 +23,6 @@ using DataOffset = int64_t; using AssetPath = QString; using AssetHash = QString; -using AssetMapping = std::map; using AssetPathList = QStringList; const size_t SHA256_HASH_LENGTH = 32; @@ -59,6 +58,13 @@ enum BakingStatus { Baked }; +struct MappingInfo { + AssetHash hash; + BakingStatus status; +}; + +using AssetMapping = std::map; + QUrl getATPUrl(const QString& hash); QByteArray hashData(const QByteArray& data); diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index 437c7c1b41..5ad1a83d2e 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -138,7 +138,7 @@ void GetAllMappingsRequest::doStart() { auto hash = message->read(SHA256_HASH_LENGTH).toHex(); BakingStatus status; message->readPrimitive(&status); - _mappings[path] = hash; + _mappings[path] = { hash, status }; } } emit finished(this); diff --git a/libraries/networking/src/MappingRequest.h b/libraries/networking/src/MappingRequest.h index e1cd8a39e8..3875915721 100644 --- a/libraries/networking/src/MappingRequest.h +++ b/libraries/networking/src/MappingRequest.h @@ -130,7 +130,7 @@ signals: private: virtual void doStart() override; - std::map _mappings; + AssetMapping _mappings; }; diff --git a/tools/atp-client/src/ATPClientApp.cpp b/tools/atp-client/src/ATPClientApp.cpp index 7d091aec74..c5edf27b67 100644 --- a/tools/atp-client/src/ATPClientApp.cpp +++ b/tools/atp-client/src/ATPClientApp.cpp @@ -332,7 +332,7 @@ void ATPClientApp::listAssets() { } else if (result == GetAllMappingsRequest::NoError) { auto mappings = request->getMappings(); for (auto& kv : mappings ) { - qDebug() << kv.first << kv.second; + qDebug() << kv.first << kv.second.hash; } } else { qDebug() << "error -- " << request->getError() << " -- " << request->getErrorString(); From 9e6502fe9222bbc7e9070eef3e3a8b723289fca3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 22 Aug 2017 13:31:28 -0700 Subject: [PATCH 023/202] More UI work --- interface/resources/qml/AssetServer.qml | 110 ++++++++++++------ interface/resources/qml/controls-uit/Tree.qml | 109 ++++++++++++++--- .../qml/hifi/dialogs/RunningScripts.qml | 4 + .../AssetMappingsScriptingInterface.cpp | 8 +- .../AssetMappingsScriptingInterface.h | 3 +- 5 files changed, 178 insertions(+), 56 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index ee37dbd8db..0b307bbc3b 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -48,6 +48,7 @@ ScrollingWindow { Component.onCompleted: { ApplicationInterface.uploadRequest.connect(uploadClicked); assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); + reload(); } @@ -464,20 +465,10 @@ ScrollingWindow { HifiControls.VerticalSpacer {} Row { - id: buttonRow anchors.left: parent.left anchors.right: parent.right spacing: hifi.dimensions.contentSpacing.x - HifiControls.GlyphButton { - glyph: hifi.glyphs.reload - color: hifi.buttons.black - colorScheme: root.colorScheme - width: hifi.dimensions.controlLineHeight - - onClicked: root.reload() - } - HifiControls.Button { text: "Add To World" color: hifi.buttons.black @@ -510,8 +501,64 @@ ScrollingWindow { onClicked: root.deleteFile() enabled: treeView.selection.hasSelection } - } + + HifiControls.GlyphButton { + glyph: hifi.glyphs.reload + color: hifi.buttons.black + colorScheme: root.colorScheme + width: hifi.dimensions.controlLineHeight + + onClicked: root.reload() + } + } + } + + HifiControls.Tree { + id: treeView + anchors.top: assetDirectory.bottom + anchors.bottom: infoRow.top + anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border + anchors.left: parent.left + anchors.right: parent.right + + treeModel: assetProxyModel + selectionMode: SelectionMode.ExtendedSelection + headerVisible: true + sortIndicatorVisible: true + + canEdit: true + colorScheme: root.colorScheme + + modifyEl: renameEl + + TableViewColumn { + id: nameColumn + title: "Name:" + role: "name" + width: treeView.width - bakedColumn.width; + } + TableViewColumn { + id: bakedColumn + title: "Use Baked?" + role: "baked" + width: 120 + } + + MouseArea { + propagateComposedEvents: true + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: { + if (!HMD.active) { // Popup only displays properly on desktop + var index = treeView.indexAt(mouse.x, mouse.y); + treeView.selection.setCurrentIndex(index, 0x0002); + contextMenu.currentIndex = index; + contextMenu.popup(); + } + } + } + Menu { id: contextMenu title: "Edit" @@ -541,36 +588,25 @@ ScrollingWindow { } } - HifiControls.Tree { - id: treeView - anchors.top: assetDirectory.bottom + Row { + id: infoRow + anchors.left: treeView.left + anchors.right: treeView.right anchors.bottom: uploadSection.top - anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border - anchors.left: parent.left - anchors.right: parent.right - - treeModel: assetProxyModel - canEdit: true - colorScheme: root.colorScheme - selectionMode: SelectionMode.ExtendedSelection - - modifyEl: renameEl - - MouseArea { - propagateComposedEvents: true - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: { - if (!HMD.active) { // Popup only displays properly on desktop - var index = treeView.indexAt(mouse.x, mouse.y); - treeView.selection.setCurrentIndex(index, 0x0002); - contextMenu.currentIndex = index; - contextMenu.popup(); - } - } + anchors.bottomMargin: hifi.dimensions.contentSpacing.y + spacing: hifi.dimensions.contentSpacing.x + + HifiControls.Label { + text: treeView.selection.selectedIndexes.length + " ITEMS SELECTED" + colorScheme: root.colorScheme } + HifiControls.CheckBox { + text: "Use baked (optimized) versions" + colorScheme: root.colorScheme + } } + HifiControls.ContentSection { id: uploadSection name: "Upload A File" diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index 8bce092947..711c6b4e5f 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -20,6 +20,7 @@ TreeView { property var treeModel: ListModel { } property var canEdit: false + property bool centerHeaderText: false property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light @@ -30,14 +31,9 @@ TreeView { model: treeModel } - TableViewColumn { - role: "display"; - } - anchors { left: parent.left; right: parent.right } - + headerVisible: false - headerDelegate: Item { } // Fix OSX QML bug that displays scrollbar starting too low. // Use rectangle to draw border with rounded corners. frameVisible: false @@ -60,6 +56,64 @@ TreeView { // Needed in order for rows to keep displaying rows after end of table entries. backgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightEven : hifi.colors.tableRowDarkEven alternateBackgroundColor: parent.isLightColorScheme ? hifi.colors.tableRowLightOdd : hifi.colors.tableRowDarkOdd + + headerDelegate: Rectangle { + height: hifi.dimensions.tableHeaderHeight + color: isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + + RalewayRegular { + id: titleText + text: styleData.value + size: hifi.fontSizes.tableHeading + font.capitalization: Font.AllUppercase + color: hifi.colors.baseGrayHighlight + horizontalAlignment: (centerHeaderText ? Text.AlignHCenter : Text.AlignLeft) + elide: Text.ElideRight + anchors { + left: parent.left + leftMargin: hifi.dimensions.tablePadding + right: sortIndicatorVisible && sortIndicatorColumn === styleData.column ? titleSort.left : parent.right + rightMargin: hifi.dimensions.tablePadding + verticalCenter: parent.verticalCenter + } + } + + HiFiGlyphs { + id: titleSort + text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn + color: isLightColorScheme ? hifi.colors.darkGray : hifi.colors.baseGrayHighlight + opacity: 0.6; + size: hifi.fontSizes.tableHeadingIcon + anchors { + right: parent.right + verticalCenter: titleText.verticalCenter + } + visible: sortIndicatorVisible && sortIndicatorColumn === styleData.column + } + + Rectangle { + width: 1 + anchors { + left: parent.left + top: parent.top + topMargin: 1 + bottom: parent.bottom + bottomMargin: 2 + } + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + visible: styleData.column > 0 + } + + Rectangle { + height: 1 + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + } + } branchDelegate: HiFiGlyphs { text: styleData.isExpanded ? hifi.glyphs.caratDn : hifi.glyphs.caratR @@ -75,28 +129,53 @@ TreeView { handle: Item { id: scrollbarHandle - implicitWidth: 6 + implicitWidth: hifi.dimensions.scrollbarHandleWidth Rectangle { anchors { fill: parent - leftMargin: 2 // Move it right - rightMargin: -2 // "" - topMargin: 3 // Shrink vertically + topMargin: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight + 3 : 3 bottomMargin: 3 // "" + leftMargin: 1 // Move it right + rightMargin: -1 // "" } - radius: 3 - color: hifi.colors.tableScrollHandleDark + radius: hifi.dimensions.scrollbarHandleWidth / 2 + color: treeView.isLightColorScheme ? hifi.colors.tableScrollHandleLight : hifi.colors.tableScrollHandleDark } } scrollBarBackground: Item { - implicitWidth: 9 + implicitWidth: hifi.dimensions.scrollbarBackgroundWidth Rectangle { anchors { fill: parent + topMargin: treeView.headerVisible ? hifi.dimensions.tableHeaderHeight - 1 : -1 margins: -1 // Expand } - color: hifi.colors.tableBackgroundDark + color: treeView.isLightColorScheme ? hifi.colors.tableScrollBackgroundLight : hifi.colors.tableScrollBackgroundDark + + // Extend header color above scrollbar background + Rectangle { + anchors { + top: parent.top + topMargin: -hifi.dimensions.tableHeaderHeight + left: parent.left + right: parent.right + } + height: hifi.dimensions.tableHeaderHeight + color: treeView.isLightColorScheme ? hifi.colors.tableBackgroundLight : hifi.colors.tableBackgroundDark + visible: treeView.headerVisible + } + Rectangle { + // Extend header bottom border + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: 1 + color: treeView.isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight + visible: treeView.headerVisible + } } } @@ -148,6 +227,8 @@ TreeView { color: colorScheme == hifi.colorSchemes.light ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + + elide: Text.ElideRight } } Component { diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 952cc03733..a649c2c4d6 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -371,6 +371,10 @@ ScrollingWindow { colorScheme: hifi.colorSchemes.dark anchors.left: parent.left anchors.right: parent.right + + TableViewColumn { + role: "display"; + } } HifiControls.VerticalSpacer { diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index b3509075bd..f2929501ab 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -172,7 +172,7 @@ void AssetMappingsScriptingInterface::renameMapping(QString oldPath, QString new } AssetMappingModel::AssetMappingModel() { - setupHeaders(); + setupRoles(); } bool AssetMappingModel::isKnownFolder(QString path) const { @@ -288,6 +288,8 @@ void AssetMappingModel::refresh() { emit errorGettingMappings(request->getErrorString()); } + emit updated(); + request->deleteLater(); }); @@ -305,11 +307,9 @@ void AssetMappingModel::clear() { _pathToItemMap.clear(); QStandardItemModel::clear(); - setupHeaders(); // restore headers } -void AssetMappingModel::setupHeaders() { - setHorizontalHeaderLabels(QStringList() << "Name" << "Use Baked?"); +void AssetMappingModel::setupRoles() { QHash roleNames; roleNames[Qt::DisplayRole] = "name"; roleNames[Qt::UserRole + 5] = "baked"; diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index 948bc9c9e7..0615c5e84d 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -38,9 +38,10 @@ public slots: signals: void errorGettingMappings(QString errorString); + void updated(); private: - void setupHeaders(); + void setupRoles(); QHash _pathToItemMap; }; From 348be788f77885d157300a7a95dca0f21b19ce3f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 24 Aug 2017 11:14:09 -0700 Subject: [PATCH 024/202] Add BakingEnabled protocol --- assignment-client/src/assets/AssetServer.cpp | 54 +++++++++++++------ assignment-client/src/assets/AssetServer.h | 3 ++ assignment-client/src/assets/AutoBaker.cpp | 10 ++-- assignment-client/src/assets/AutoBaker.h | 2 +- interface/resources/qml/AssetServer.qml | 11 ++-- interface/resources/qml/controls-uit/Tree.qml | 20 ++++++- libraries/networking/src/AssetClient.cpp | 40 ++++++++++++++ libraries/networking/src/AssetClient.h | 4 ++ libraries/networking/src/AssetUtils.cpp | 4 ++ libraries/networking/src/AssetUtils.h | 7 ++- libraries/networking/src/MappingRequest.cpp | 43 +++++++++++++++ libraries/networking/src/MappingRequest.h | 15 ++++++ .../networking/src/udt/PacketHeaders.cpp | 4 +- libraries/networking/src/udt/PacketHeaders.h | 4 ++ .../src/AssetScriptingInterface.cpp | 14 +++++ .../src/AssetScriptingInterface.h | 2 + 16 files changed, 205 insertions(+), 32 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index ea0010cb26..c5de1005f5 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -334,11 +334,10 @@ void AssetServer::completeSetup() { QRegExp hashFileRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}" }; auto files = _filesDirectory.entryInfoList(QDir::Files); - auto mappedHashes = _fileMappings.values(); - for (const auto& fileInfo : files) { - AssetHash hash = fileInfo.fileName(); - bool isAsset = hashFileRegex.exactMatch(hash); - if (isAsset && _baker.assetNeedsBaking(hash)) { + for (auto& it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++it) { + AssetPath path = it.key(); + AssetHash hash = it.value().toString(); + if (_baker.assetNeedsBaking(path, hash)) { _baker.addPendingBake(hash); } } @@ -381,26 +380,24 @@ void AssetServer::handleAssetMappingOperation(QSharedPointer me replyPacket->writePrimitive(messageID); switch (operationType) { - case AssetMappingOperationType::Get: { + case AssetMappingOperationType::Get: handleGetMappingOperation(*message, senderNode, *replyPacket); break; - } - case AssetMappingOperationType::GetAll: { + case AssetMappingOperationType::GetAll: handleGetAllMappingOperation(*message, senderNode, *replyPacket); break; - } - case AssetMappingOperationType::Set: { + case AssetMappingOperationType::Set: handleSetMappingOperation(*message, senderNode, *replyPacket); break; - } - case AssetMappingOperationType::Delete: { + case AssetMappingOperationType::Delete: handleDeleteMappingsOperation(*message, senderNode, *replyPacket); break; - } - case AssetMappingOperationType::Rename: { + case AssetMappingOperationType::Rename: handleRenameMappingOperation(*message, senderNode, *replyPacket); break; - } + case AssetMappingOperationType::SetBakingEnabled: + handleSetBakingEnabledOperation(*message, senderNode, *replyPacket); + break; } auto nodeList = DependencyManager::get(); @@ -545,6 +542,30 @@ void AssetServer::handleRenameMappingOperation(ReceivedMessage& message, SharedN } } +void AssetServer::handleSetBakingEnabledOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) { + if (senderNode->getCanWriteToAssetServer()) { + bool enabled { true }; + message.readPrimitive(&enabled); + + int numberOfMappings{ 0 }; + message.readPrimitive(&numberOfMappings); + + QStringList mappings; + + for (int i = 0; i < numberOfMappings; ++i) { + mappings << message.readString(); + } + + if (setBakingEnabled(mappings, enabled)) { + replyPacket.writePrimitive(AssetServerError::NoError); + } else { + replyPacket.writePrimitive(AssetServerError::MappingOperationFailed); + } + } else { + replyPacket.writePrimitive(AssetServerError::PermissionDenied); + } +} + void AssetServer::handleAssetGetInfo(QSharedPointer message, SharedNodePointer senderNode) { QByteArray assetHash; MessageID messageID; @@ -1100,4 +1121,7 @@ bool AssetServer::createMetaFile(AssetHash originalAssetHash) { } else { return false; } + +bool AssetServer::setBakingEnabled(AssetPathList& paths, bool enabled) { + return "test"; } diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 2ce223760f..4d2b8da877 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -64,6 +64,7 @@ private: void handleSetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); void handleDeleteMappingsOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); void handleRenameMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); + void handleSetBakingEnabledOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); // Mapping file operations must be called from main assignment thread only bool loadMappingsFromFile(); @@ -78,6 +79,8 @@ private: /// Rename mapping from `oldPath` to `newPath`. Returns true if successful bool renameMapping(AssetPath oldPath, AssetPath newPath); + bool setBakingEnabled(AssetPathList& paths, bool enabled); + /// Delete any unmapped files from the local asset directory void cleanupUnmappedFiles(); diff --git a/assignment-client/src/assets/AutoBaker.cpp b/assignment-client/src/assets/AutoBaker.cpp index fae895e635..38c564425b 100644 --- a/assignment-client/src/assets/AutoBaker.cpp +++ b/assignment-client/src/assets/AutoBaker.cpp @@ -20,8 +20,8 @@ void AutoBaker::addPendingBake(AssetHash hash) { // Maybe start baking it right away } -bool AutoBaker::assetNeedsBaking(AssetHash hash) { - return true; +bool AutoBaker::assetNeedsBaking(AssetPath path, AssetHash hash) { + return path.endsWith(".fbx"); } BakingStatus AutoBaker::getAssetStatus(AssetHash hash) { @@ -33,9 +33,5 @@ BakingStatus AutoBaker::getAssetStatus(AssetHash hash) { return Baking; } - if (assetNeedsBaking(hash)) { - return NotBaked; - } else { - return Baked; - } + return NotBaked; } diff --git a/assignment-client/src/assets/AutoBaker.h b/assignment-client/src/assets/AutoBaker.h index 805130bf52..258fb15931 100644 --- a/assignment-client/src/assets/AutoBaker.h +++ b/assignment-client/src/assets/AutoBaker.h @@ -20,7 +20,7 @@ class AutoBaker { public: void addPendingBake(AssetHash hash); - bool assetNeedsBaking(AssetHash hash); + bool assetNeedsBaking(AssetPath path, AssetHash hash); BakingStatus getAssetStatus(AssetHash hash); diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 0b307bbc3b..a34de72270 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -542,7 +542,7 @@ ScrollingWindow { id: bakedColumn title: "Use Baked?" role: "baked" - width: 120 + width: 140 } MouseArea { @@ -596,14 +596,17 @@ ScrollingWindow { anchors.bottomMargin: hifi.dimensions.contentSpacing.y spacing: hifi.dimensions.contentSpacing.x - HifiControls.Label { - text: treeView.selection.selectedIndexes.length + " ITEMS SELECTED" - colorScheme: root.colorScheme + RalewayRegular { + size: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase + text: selectedItems + " items selected" + color: hifi.colors.lightGrayText } HifiControls.CheckBox { text: "Use baked (optimized) versions" colorScheme: root.colorScheme + enabled: selectedItems > 0 } } diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index 711c6b4e5f..b7d9558cb6 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -212,12 +212,16 @@ TreeView { if (treeView.canEdit && styleData.selected) { return textFieldComponent; } else { - return labelComponent; + if (styleData.value.startsWith("HifiGlyphs#")) { + return glyphComponent; + } else { + return labelComponent; + } } } sourceComponent: getComponent() - + Component { id: labelComponent FiraSansSemiBold { @@ -231,6 +235,18 @@ TreeView { elide: Text.ElideRight } } + Component { + id: glyphComponent + HiFiGlyphs { + text: styleData.value.replace("HifiGlyphs#", "") + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + + elide: Text.ElideRight + } + } Component { id: textFieldComponent diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index cb0b620a54..0d4e7c8388 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -185,6 +185,14 @@ RenameMappingRequest* AssetClient::createRenameMappingRequest(const AssetPath& o return request; } +SetBakingEnabledRequest* AssetClient::createSetBakingEnabledRequest(const AssetPathList& path, bool enabled) { + auto bakingEnabledRequest = new SetBakingEnabledRequest(path, enabled); + + bakingEnabledRequest->moveToThread(thread()); + + return bakingEnabledRequest; +} + AssetRequest* AssetClient::createRequest(const AssetHash& hash, const ByteRange& byteRange) { auto request = new AssetRequest(hash, byteRange); @@ -585,6 +593,38 @@ MessageID AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetP return INVALID_MESSAGE_ID; } +MessageID AssetClient::setBakingEnabled(const AssetPathList& paths, bool enabled, MappingOperationCallback callback) { + auto nodeList = DependencyManager::get(); + SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + + if (assetServer) { + auto packetList = NLPacketList::create(PacketType::AssetMappingOperation, QByteArray(), true, true); + + auto messageID = ++_currentID; + packetList->writePrimitive(messageID); + + packetList->writePrimitive(AssetMappingOperationType::SetBakingEnabled); + + packetList->writePrimitive(enabled); + + packetList->writePrimitive(int(paths.size())); + + for (auto& path : paths) { + packetList->writeString(path); + } + + if (nodeList->sendPacketList(std::move(packetList), *assetServer) != -1) { + _pendingMappingRequests[assetServer][messageID] = callback; + + return messageID; + + } + } + + callback(false, AssetServerError::NoError, QSharedPointer()); + return INVALID_MESSAGE_ID; +} + bool AssetClient::cancelMappingRequest(MessageID id) { Q_ASSERT(QThread::currentThread() == thread()); diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 3f6602b76b..8035aa886e 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -32,6 +32,7 @@ class SetMappingRequest; class GetAllMappingsRequest; class DeleteMappingsRequest; class RenameMappingRequest; +class SetBakingEnabledRequest; class AssetRequest; class AssetUpload; @@ -56,6 +57,7 @@ public: Q_INVOKABLE DeleteMappingsRequest* createDeleteMappingsRequest(const AssetPathList& paths); Q_INVOKABLE SetMappingRequest* createSetMappingRequest(const AssetPath& path, const AssetHash& hash); Q_INVOKABLE RenameMappingRequest* createRenameMappingRequest(const AssetPath& oldPath, const AssetPath& newPath); + Q_INVOKABLE SetBakingEnabledRequest* createSetBakingEnabledRequest(const AssetPathList& path, bool enabled); Q_INVOKABLE AssetRequest* createRequest(const AssetHash& hash, const ByteRange& byteRange = ByteRange()); Q_INVOKABLE AssetUpload* createUpload(const QString& filename); Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data); @@ -81,6 +83,7 @@ private: MessageID setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback); MessageID deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback); MessageID renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback); + MessageID setBakingEnabled(const AssetPathList& paths, bool enabled, MappingOperationCallback callback); MessageID getAssetInfo(const QString& hash, GetInfoCallback callback); MessageID getAsset(const QString& hash, DataOffset start, DataOffset end, @@ -119,6 +122,7 @@ private: friend class SetMappingRequest; friend class DeleteMappingsRequest; friend class RenameMappingRequest; + friend class SetBakingEnabledRequest; }; #endif diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index 7c8ffd6746..af085d6463 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -87,6 +87,8 @@ bool isValidHash(const AssetHash& hash) { QString bakingStatusToString(BakingStatus status) { switch (status) { + case Unrelevant: + return "--"; case NotBaked: return "Not Baked"; case Pending: @@ -95,5 +97,7 @@ QString bakingStatusToString(BakingStatus status) { return "Baking"; case Baked: return "Baked"; + case Error: + return "Error"; } } diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index 4b78d8aaaf..f8f0171a5d 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -48,14 +48,17 @@ enum AssetMappingOperationType : uint8_t { GetAll, Set, Delete, - Rename + Rename, + SetBakingEnabled }; enum BakingStatus { + Unrelevant, NotBaked, Pending, Baking, - Baked + Baked, + Error }; struct MappingInfo { diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index 5ad1a83d2e..258b7c6359 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -273,3 +273,46 @@ void RenameMappingRequest::doStart() { emit finished(this); }); } + +SetBakingEnabledRequest::SetBakingEnabledRequest(const AssetPathList& paths, bool enabled) : _paths(paths), _enabled(enabled) { + for (auto& path : _paths) { + path = path.trimmed(); + } +}; + +void SetBakingEnabledRequest::doStart() { + + // short circuit the request if any of the paths are invalid + for (auto& path : _paths) { + if (!isValidPath(path)) { + _error = MappingRequest::InvalidPath; + emit finished(this); + return; + } + } + + auto assetClient = DependencyManager::get(); + + _mappingRequestID = assetClient->setBakingEnabled(_paths, _enabled, + [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer message) { + + _mappingRequestID = INVALID_MESSAGE_ID; + if (!responseReceived) { + _error = NetworkError; + } else { + switch (error) { + case AssetServerError::NoError: + _error = NoError; + break; + case AssetServerError::PermissionDenied: + _error = PermissionDenied; + break; + default: + _error = UnknownError; + break; + } + } + + emit finished(this); + }); +}; \ No newline at end of file diff --git a/libraries/networking/src/MappingRequest.h b/libraries/networking/src/MappingRequest.h index 3875915721..fc43375469 100644 --- a/libraries/networking/src/MappingRequest.h +++ b/libraries/networking/src/MappingRequest.h @@ -133,5 +133,20 @@ private: AssetMapping _mappings; }; +class SetBakingEnabledRequest : public MappingRequest { + Q_OBJECT +public: + SetBakingEnabledRequest(const AssetPathList& path, bool enabled); + +signals: + void finished(SetBakingEnabledRequest* thisRequest); + +private: + virtual void doStart() override; + + AssetPathList _paths; + bool _enabled; +}; + #endif // hifi_MappingRequest_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index f453d096b6..74d3c46eb3 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -67,7 +67,9 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::MicrophoneAudioWithEcho: case PacketType::AudioStreamStats: return static_cast(AudioVersion::HighDynamicRangeVolume); - + case PacketType::AssetMappingOperation: + case PacketType::AssetMappingOperationReply: + return static_cast(AssetMappingOperationVersion::SetBakingEnabledOperation); default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 6b57c8a3cb..d71470383b 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -336,4 +336,8 @@ enum class MessageDataVersion : PacketVersion { TextOrBinaryData = 18 }; +enum class AssetMappingOperationVersion : PacketVersion { + SetBakingEnabledOperation = 18 +}; + #endif // hifi_PacketHeaders_h diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 51b3d7ffbe..25e8c0dcf3 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -88,6 +88,20 @@ void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callb assetRequest->start(); } +void AssetScriptingInterface::setBakingEnabled(QString path, bool enabled, QScriptValue callback) { + auto setBakingEnabledRequest = DependencyManager::get()->createSetBakingEnabledRequest({ path }, enabled); + + QObject::connect(setBakingEnabledRequest, &SetBakingEnabledRequest::finished, this, [this, callback](SetBakingEnabledRequest* request) mutable { + if (callback.isFunction()) { + QString error = request->getErrorString(); + QScriptValueList args{ error }; + callback.call(_engine->currentContext()->thisObject(), args); + } + request->deleteLater(); + }); + setBakingEnabledRequest->start(); +} + #if (PR_BUILD || DEV_BUILD) void AssetScriptingInterface::sendFakedHandshake() { auto nodeList = DependencyManager::get(); diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 0238329b73..2812be65f9 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -75,6 +75,8 @@ public: * @param {string} error */ Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback); + + Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback); #if (PR_BUILD || DEV_BUILD) Q_INVOKABLE void sendFakedHandshake(); From 7d103d24f4ea038ed4387fb226e2a058731305c6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 25 Aug 2017 15:21:08 -0700 Subject: [PATCH 025/202] Integrate my and Huffman's work --- assignment-client/src/assets/AssetServer.cpp | 115 +++++++++++++++---- assignment-client/src/assets/AssetServer.h | 21 ++-- 2 files changed, 102 insertions(+), 34 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index c5de1005f5..67e192cd65 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -53,11 +53,13 @@ static const QList BAKEABLE_TEXTURE_EXTENSIONS = QImageReader::suppo static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; -BakeAssetTask::BakeAssetTask(const QString& assetHash, const QString& assetPath, const QString& filePath) +BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) : _assetHash(assetHash), _assetPath(assetPath), _filePath(filePath) { } void BakeAssetTask::run() { + _isBaking.store(true); + qRegisterMetaType >("QVector"); TextureBakerThreadGetter fn = []() -> QThread* { return QThread::currentThread(); }; @@ -86,7 +88,7 @@ void BakeAssetTask::run() { } } -void AssetServer::bakeAsset(const QString& assetHash, const QString& assetPath, const QString& filePath) { +void AssetServer::bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) { qDebug() << "Starting bake for: " << assetPath << assetHash; auto it = _pendingBakes.find(assetHash); if (it == _pendingBakes.end()) { @@ -94,9 +96,7 @@ void AssetServer::bakeAsset(const QString& assetHash, const QString& assetPath, task->setAutoDelete(false); _pendingBakes[assetHash] = task; - connect(task.get(), &BakeAssetTask::bakeComplete, this, [this, assetPath](QString assetHash, QString assetPath, QVector outputFiles) { - handleCompletedBake(assetPath, assetHash, outputFiles); - }); + connect(task.get(), &BakeAssetTask::bakeComplete, this, &AssetServer::handleCompletedBake); _bakingTaskPool.start(task.get()); } else { @@ -108,6 +108,46 @@ QString AssetServer::getPathToAssetHash(const AssetHash& assetHash) { return _filesDirectory.absoluteFilePath(assetHash); } +BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& hash) { + auto it = _pendingBakes.find(hash); + if (it == _pendingBakes.end()) { + return (*it)->isBaking() ? Baking : Pending; + } + + if (path.startsWith("/.baked/")) { + return Baked; + } + + auto dotIndex = path.lastIndexOf("."); + if (dotIndex == -1) { + return Unrelevant; + } + + auto extension = path.mid(dotIndex + 1); + + QString bakedFilename; + + if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { + bakedFilename = BAKED_MODEL_SIMPLE_NAME; + } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { + bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; + } else { + return Unrelevant; + } + + auto bakedPath = "/.baked/" + hash + "/" + bakedFilename; + auto jt = _fileMappings.find(bakedPath); + if (jt != _fileMappings.end()) { + if (jt->toString() == hash) { + return NotBaked; + } else { + return Baked; + } + } + + return Pending; +} + void AssetServer::bakeAssets() { auto it = _fileMappings.cbegin(); for (; it != _fileMappings.cend(); ++it) { @@ -129,9 +169,9 @@ void AssetServer::createEmptyMetaFile(const AssetHash& hash) { QFile metaFile { metaFilePath }; if (!metaFile.exists()) { - qDebug() << "Creating metfaile for " << hash; + qDebug() << "Creating metafile for " << hash; if (metaFile.open(QFile::WriteOnly)) { - qDebug() << "Created metfaile for " << hash; + qDebug() << "Created metafile for " << hash; metaFile.write("{}"); } } @@ -331,16 +371,6 @@ void AssetServer::completeSetup() { qCCritical(asset_server) << "Asset Server assignment will not continue because mapping file could not be loaded."; setFinished(true); } - - QRegExp hashFileRegex { "^[a-f0-9]{" + QString::number(SHA256_HASH_HEX_LENGTH) + "}" }; - auto files = _filesDirectory.entryInfoList(QDir::Files); - for (auto& it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++it) { - AssetPath path = it.key(); - AssetHash hash = it.value().toString(); - if (_baker.assetNeedsBaking(path, hash)) { - _baker.addPendingBake(hash); - } - } } void AssetServer::cleanupUnmappedFiles() { @@ -486,7 +516,7 @@ void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedN for (auto it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++ it) { replyPacket.writeString(it.key()); replyPacket.write(QByteArray::fromHex(it.value().toString().toUtf8())); - replyPacket.writePrimitive(_baker.getAssetStatus(it.value().toString())); + replyPacket.writePrimitive(getAssetStatus(it.value().toString())); } } @@ -1012,6 +1042,12 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { } static const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; +static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx"; +static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx"; + +QString getBakeMapping(const AssetHash& hash, const QString& relativeFilePath) { + return HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath; +} void AssetServer::handleCompletedBake(AssetPath originalAssetPath, AssetHash originalAssetHash, QVector bakedFilePaths) { bool errorCompletingBake { false }; @@ -1051,19 +1087,17 @@ void AssetServer::handleCompletedBake(AssetPath originalAssetPath, AssetHash ori // setup the mapping for this bake file auto relativeFilePath = QUrl(filePath).fileName(); qDebug() << "Relative file path is: " << relativeFilePath; - static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx"; - static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx"; if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) { // for an FBX file, we replace the filename with the simple name // (to handle the case where two mapped assets have the same hash but different names) relativeFilePath = BAKED_ASSET_SIMPLE_FBX_NAME; - } else if (!originalAssetPath.endsWith(".fbx")) { + } else if (!originalAssetPath.endsWith(".fbx", Qt::CaseInsensitive)) { relativeFilePath = BAKED_ASSET_SIMPLE_TEXTURE_NAME; } - QString bakeMapping = HIDDEN_BAKED_CONTENT_FOLDER + originalAssetHash + "/" + relativeFilePath; + QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath); // add a mapping (under the hidden baked folder) for this file resulting from the bake if (setMapping(bakeMapping, bakedFileHash)) { @@ -1121,7 +1155,38 @@ bool AssetServer::createMetaFile(AssetHash originalAssetHash) { } else { return false; } - -bool AssetServer::setBakingEnabled(AssetPathList& paths, bool enabled) { - return "test"; +} + +void AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { + for (const auto& path : paths) { + auto it = _fileMappings.find(path); + if (it != _fileMappings.end()) { + auto hash = it->toString(); + + auto dotIndex = path.lastIndexOf("."); + if (dotIndex == -1) { + continue; + } + + auto extension = path.mid(dotIndex + 1); + + QString bakedFilename; + + if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { + bakedFilename = BAKED_MODEL_SIMPLE_NAME; + } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { + bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; + } else { + continue; + } + + auto bakedMapping = getBakeMapping(hash, bakedFilename); + + if (enabled) { + // TODO + } else { + // TODO + } + } + } } diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 4d2b8da877..435e8e8b24 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -25,16 +25,19 @@ class BakeAssetTask : public QObject, public QRunnable { Q_OBJECT public: - BakeAssetTask(const QString& assetHash, const QString& assetPath, const QString& filePath); + BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath); + + bool isBaking() { return _isBaking.load(); } void run() override; signals: - void bakeComplete(QString assetHash, QString assetPath, QVector outputFiles); + void bakeComplete(AssetHash assetHash, AssetPath assetPath, QVector outputFiles); private: - QString _assetHash; - QString _assetPath; + std::atomic _isBaking { false }; + AssetHash _assetHash; + AssetPath _assetPath; QString _filePath; }; @@ -79,19 +82,21 @@ private: /// Rename mapping from `oldPath` to `newPath`. Returns true if successful bool renameMapping(AssetPath oldPath, AssetPath newPath); - bool setBakingEnabled(AssetPathList& paths, bool enabled); + void setBakingEnabled(const AssetPathList& paths, bool enabled); /// Delete any unmapped files from the local asset directory void cleanupUnmappedFiles(); QString getPathToAssetHash(const AssetHash& assetHash); + BakingStatus getAssetStatus(const AssetPath& path, const AssetHash& hash); + void bakeAssets(); void maybeBake(const AssetPath& path, const AssetHash& hash); void createEmptyMetaFile(const AssetHash& hash); bool hasMetaFile(const AssetHash& hash); bool needsToBeBaked(const AssetPath& path, const AssetHash& assetHash); - void bakeAsset(const QString& assetHash, const QString& assetPath, const QString& filePath); + void bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath); /// Move baked content for asset to baked directory and update baked status void handleCompletedBake(AssetPath assetPath, AssetHash originalAssetHash, QVector bakedFilePaths); @@ -107,10 +112,8 @@ private: /// Task pool for handling uploads and downloads of assets QThreadPool _transferTaskPool; - QHash> _pendingBakes; + QHash> _pendingBakes; QThreadPool _bakingTaskPool; - - AutoBaker _baker; }; #endif From eaa2b9fda841d9d460d7bea66a38d8430722172e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 25 Aug 2017 15:31:18 -0700 Subject: [PATCH 026/202] Fix packet version conflict --- assignment-client/src/assets/AssetServer.cpp | 5 +++-- assignment-client/src/assets/AssetServer.h | 2 +- libraries/networking/src/udt/PacketHeaders.cpp | 4 +--- libraries/networking/src/udt/PacketHeaders.h | 4 ---- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 67e192cd65..3dc96caefc 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -516,7 +516,7 @@ void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedN for (auto it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++ it) { replyPacket.writeString(it.key()); replyPacket.write(QByteArray::fromHex(it.value().toString().toUtf8())); - replyPacket.writePrimitive(getAssetStatus(it.value().toString())); + replyPacket.writePrimitive(getAssetStatus(it.key(), it.value().toString())); } } @@ -1157,7 +1157,7 @@ bool AssetServer::createMetaFile(AssetHash originalAssetHash) { } } -void AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { +bool AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { for (const auto& path : paths) { auto it = _fileMappings.find(path); if (it != _fileMappings.end()) { @@ -1189,4 +1189,5 @@ void AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { } } } + return true; } diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 435e8e8b24..fadd6109bf 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -82,7 +82,7 @@ private: /// Rename mapping from `oldPath` to `newPath`. Returns true if successful bool renameMapping(AssetPath oldPath, AssetPath newPath); - void setBakingEnabled(const AssetPathList& paths, bool enabled); + bool setBakingEnabled(const AssetPathList& paths, bool enabled); /// Delete any unmapped files from the local asset directory void cleanupUnmappedFiles(); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 74d3c46eb3..354a95850b 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -42,6 +42,7 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: return 18; // ICE Server Heartbeat signing + case PacketType::AssetMappingOperation: case PacketType::AssetMappingOperationReply: return static_cast(AssetServerPacketVersion::RedirectedMappings); case PacketType::AssetGetInfo: @@ -67,9 +68,6 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::MicrophoneAudioWithEcho: case PacketType::AudioStreamStats: return static_cast(AudioVersion::HighDynamicRangeVolume); - case PacketType::AssetMappingOperation: - case PacketType::AssetMappingOperationReply: - return static_cast(AssetMappingOperationVersion::SetBakingEnabledOperation); default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3a38879a79..c310065517 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -337,8 +337,4 @@ enum class MessageDataVersion : PacketVersion { TextOrBinaryData = 18 }; -enum class AssetMappingOperationVersion : PacketVersion { - SetBakingEnabledOperation = 18 -}; - #endif // hifi_PacketHeaders_h From a36ddf7b6d6e82323fb962e1db62d37f91e3c6c9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 25 Aug 2017 16:13:29 -0700 Subject: [PATCH 027/202] Fix failed connection --- assignment-client/src/assets/AssetServer.cpp | 2 +- assignment-client/src/assets/AssetServer.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 3dc96caefc..7e1175d0a6 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1049,7 +1049,7 @@ QString getBakeMapping(const AssetHash& hash, const QString& relativeFilePath) { return HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath; } -void AssetServer::handleCompletedBake(AssetPath originalAssetPath, AssetHash originalAssetHash, QVector bakedFilePaths) { +void AssetServer::handleCompletedBake(QString originalAssetPath, QString originalAssetHash, QVector bakedFilePaths) { bool errorCompletingBake { false }; qDebug() << "Completing bake for " << originalAssetHash; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index fadd6109bf..19ad23be08 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -32,7 +32,7 @@ public: void run() override; signals: - void bakeComplete(AssetHash assetHash, AssetPath assetPath, QVector outputFiles); + void bakeComplete(QString assetHash, QString assetPath, QVector outputFiles); private: std::atomic _isBaking { false }; @@ -99,7 +99,7 @@ private: void bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath); /// Move baked content for asset to baked directory and update baked status - void handleCompletedBake(AssetPath assetPath, AssetHash originalAssetHash, QVector bakedFilePaths); + void handleCompletedBake(QString assetPath, QString originalAssetHash, QVector bakedFilePaths); /// Create meta file to describe baked content for original asset bool createMetaFile(AssetHash originalAssetHash); From 700b11e28be14c9177c77f8cb40598d0ec7749e8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 25 Aug 2017 16:17:37 -0700 Subject: [PATCH 028/202] Fix bad conditional --- assignment-client/src/assets/AssetServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 7e1175d0a6..aa5224de0d 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -110,7 +110,7 @@ QString AssetServer::getPathToAssetHash(const AssetHash& assetHash) { BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& hash) { auto it = _pendingBakes.find(hash); - if (it == _pendingBakes.end()) { + if (it != _pendingBakes.end()) { return (*it)->isBaking() ? Baking : Pending; } From 7d08a5788f547e869c1914b7d67dd9fc01568d48 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 25 Aug 2017 16:21:00 -0700 Subject: [PATCH 029/202] Move outputFiles from FBXBaker to Baker --- assignment-client/src/assets/AssetServer.cpp | 33 +++++++++----------- libraries/baking/src/Baker.h | 6 ++++ libraries/baking/src/FBXBaker.h | 4 --- libraries/baking/src/TextureBaker.cpp | 5 ++- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 6e3195f570..ac1ef1c3ed 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -61,28 +61,24 @@ void BakeAssetTask::run() { qRegisterMetaType >("QVector"); TextureBakerThreadGetter fn = []() -> QThread* { return QThread::currentThread(); }; - if (_filePath.endsWith(".fbx")) { - FBXBaker baker(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir()); + std::unique_ptr baker; - QEventLoop loop; - connect(&baker, &Baker::finished, &loop, &QEventLoop::quit); - QMetaObject::invokeMethod(&baker, "bake", Qt::QueuedConnection); - qDebug() << "Running the bake!"; - loop.exec(); - - qDebug() << "Finished baking: " << _assetHash << _assetPath << baker.getOutputFiles(); - emit bakeComplete(_assetHash, _assetPath, QVector::fromStdVector(baker.getOutputFiles())); + if (_assetPath.endsWith(".fbx")) { + baker = std::make_unique(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir()); } else { - TextureBaker baker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, PathUtils::generateTemporaryDir()); + baker = std::make_unique(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, PathUtils::generateTemporaryDir()); + } - QEventLoop loop; - connect(&baker, &Baker::finished, &loop, &QEventLoop::quit); - QMetaObject::invokeMethod(&baker, "bake", Qt::QueuedConnection); - qDebug() << "Running the bake!"; - loop.exec(); + QEventLoop loop; + connect(baker.get(), &Baker::finished, &loop, &QEventLoop::quit); + QMetaObject::invokeMethod(baker.get(), "bake", Qt::QueuedConnection); + loop.exec(); - qDebug() << "Finished baking: " << _assetHash << _assetPath << baker.getBakedTextureFileName(); - emit bakeComplete(_assetHash, _assetPath, { baker.getDestinationFilePath() }); + if (baker->hasErrors()) { + qDebug() << "Failed to bake: " << _assetHash << _assetPath; + } else { + qDebug() << "Finished baking: " << _assetHash << _assetPath << baker->getOutputFiles(); + emit bakeComplete(_assetHash, _assetPath, QVector::fromStdVector(baker->getOutputFiles())); } } @@ -454,7 +450,6 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode replyPacket.write(QByteArray::fromHex(originalAssetHash.toUtf8())); replyPacket.writePrimitive(wasRedirected); - auto query = QUrlQuery(url.query()); bool isSkybox = query.hasQueryItem("skybox"); qDebug() << "Is skybox? " << isSkybox; diff --git a/libraries/baking/src/Baker.h b/libraries/baking/src/Baker.h index d7107428bf..fd76246497 100644 --- a/libraries/baking/src/Baker.h +++ b/libraries/baking/src/Baker.h @@ -24,6 +24,8 @@ public: bool hasWarnings() const { return !_warningList.isEmpty(); } QStringList getWarnings() const { return _warningList; } + std::vector getOutputFiles() const { return _outputFiles; } + public slots: virtual void bake() = 0; @@ -36,6 +38,10 @@ protected: void handleErrors(const QStringList& errors); + // List of baked output files. For instance, for an FBX this would + // include the .fbx and all of its texture files. + std::vector _outputFiles; + QStringList _errorList; QStringList _warningList; }; diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 0e52f2e4c3..00e7987422 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -44,7 +44,6 @@ public: QUrl getFBXUrl() const { return _fbxURL; } QString getBakedFBXFilePath() const { return _bakedFBXFilePath; } - std::vector getOutputFiles() const { return _outputFiles; } public slots: // all calls to FBXBaker::bake for FBXBaker instances must be from the same thread @@ -88,9 +87,6 @@ private: QDir _tempDir; QString _originalFBXFilePath; - // List of baked output files, includes the FBX and textures - std::vector _outputFiles; - static FBXSDKManagerUniquePointer _sdkManager; fbxsdk::FbxScene* _scene { nullptr }; diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 70df511d2c..44995fa026 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -120,10 +120,13 @@ void TextureBaker::processTexture() { const size_t length = memKTX->_storage->size(); // attempt to write the baked texture to the destination file path - QFile bakedTextureFile { _outputDirectory.absoluteFilePath(_bakedTextureFileName) }; + auto filePath = _outputDirectory.absoluteFilePath(_bakedTextureFileName); + QFile bakedTextureFile { filePath }; if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { handleError("Could not write baked texture for " + _textureURL.toString()); + } else { + _outputFiles.push_back(filePath); } qCDebug(model_baking) << "Baked texture" << _textureURL; From 0755b77615ebdd3771e4a71ff977240f22d8c0b6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 25 Aug 2017 16:31:45 -0700 Subject: [PATCH 030/202] Fix for mac build --- assignment-client/src/assets/AssetServer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index a3eab7d4d5..88a1eaeab1 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -79,8 +79,9 @@ void BakeAssetTask::run() { if (baker->hasErrors()) { qDebug() << "Failed to bake: " << _assetHash << _assetPath; } else { - qDebug() << "Finished baking: " << _assetHash << _assetPath << baker->getOutputFiles(); - emit bakeComplete(_assetHash, _assetPath, QVector::fromStdVector(baker->getOutputFiles())); + auto vectorOutputFiles = QVector::fromStdVector(baker->getOutputFiles()); + qDebug() << "Finished baking: " << _assetHash << _assetPath << vectorOutputFiles; + emit bakeComplete(_assetHash, _assetPath, vectorOutputFiles); } } From cc7dd2162bf1dca10901cdee8e300db6ddb97cbe Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 25 Aug 2017 17:00:41 -0700 Subject: [PATCH 031/202] fixes for OS X build --- assignment-client/src/assets/AssetServer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 88a1eaeab1..59e0809586 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -66,9 +66,13 @@ void BakeAssetTask::run() { std::unique_ptr baker; if (_assetPath.endsWith(".fbx")) { - baker = std::make_unique(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir()); + baker = std::unique_ptr { + new FBXBaker(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir()) + }; } else { - baker = std::make_unique(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, PathUtils::generateTemporaryDir()); + baker = std::unique_ptr { + new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, PathUtils::generateTemporaryDir()) + }; } QEventLoop loop; From 79231cdee0905fca73c52f37330fb5419330072f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 25 Aug 2017 17:10:09 -0700 Subject: [PATCH 032/202] fix switched signature for completed bake handling --- assignment-client/src/assets/AssetServer.cpp | 2 +- assignment-client/src/assets/AssetServer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 59e0809586..212b9d8d19 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1049,7 +1049,7 @@ QString getBakeMapping(const AssetHash& hash, const QString& relativeFilePath) { return HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath; } -void AssetServer::handleCompletedBake(QString originalAssetPath, QString originalAssetHash, QVector bakedFilePaths) { +void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector bakedFilePaths) { bool errorCompletingBake { false }; qDebug() << "Completing bake for " << originalAssetHash; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 19ad23be08..94576b329d 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -99,7 +99,7 @@ private: void bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath); /// Move baked content for asset to baked directory and update baked status - void handleCompletedBake(QString assetPath, QString originalAssetHash, QVector bakedFilePaths); + void handleCompletedBake(QString originalAssetHash, QString assetPath, QVector bakedFilePaths); /// Create meta file to describe baked content for original asset bool createMetaFile(AssetHash originalAssetHash); From d4332e7776faa5081a1a86e5de697e509b066a04 Mon Sep 17 00:00:00 2001 From: vladest Date: Sat, 26 Aug 2017 20:55:16 +0200 Subject: [PATCH 033/202] Adopting according comments from designers --- .../resources/qml/controls-uit/CheckBox2.qml | 2 ++ interface/resources/qml/hifi/audio/Audio.qml | 34 ++++++++++++++----- .../qml/hifi/audio/AudioTabButton.qml | 2 +- .../resources/qml/hifi/audio/CheckBox.qml | 1 - .../resources/qml/styles-uit/HiFiGlyphs.qml | 2 -- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/interface/resources/qml/controls-uit/CheckBox2.qml b/interface/resources/qml/controls-uit/CheckBox2.qml index f845d24868..92bad04d01 100644 --- a/interface/resources/qml/controls-uit/CheckBox2.qml +++ b/interface/resources/qml/controls-uit/CheckBox2.qml @@ -19,6 +19,8 @@ CheckBox { HifiConstants { id: hifi; } + padding: 0 + leftPadding: 0 property int colorScheme: hifi.colorSchemes.light property string color: hifi.colors.lightGrayText readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index e80f278323..3a9077cbb7 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -78,7 +78,7 @@ Rectangle { Separator { } RalewayRegular { - x: margins.paddings + margins.sizeCheckBox; + x: margins.paddings + muteMic.boxSize + muteMic.spacing; size: 16; color: "white"; text: qsTr("Input Device Settings") @@ -92,7 +92,9 @@ Rectangle { // mute is in its own row RowLayout { AudioControls.CheckBox { + id: muteMic text: qsTr("Mute microphone"); + spacing: 8 isRedCheck: true; checked: Audio.muted; onClicked: { @@ -105,6 +107,7 @@ Rectangle { RowLayout { spacing: 16; AudioControls.CheckBox { + spacing: muteMic.spacing text: qsTr("Enable noise reduction"); checked: Audio.noiseReduction; onClicked: { @@ -113,6 +116,7 @@ Rectangle { } } AudioControls.CheckBox { + spacing: muteMic.spacing text: qsTr("Show audio level meter"); checked: AvatarInputs.showAudioTools; onClicked: { @@ -126,20 +130,25 @@ Rectangle { Separator {} - Row { + Item { x: margins.paddings; width: parent.width - margins.paddings*2 - height: 28 - spacing: 0 + height: 36 + HiFiGlyphs { width: margins.sizeCheckBox text: hifi.glyphs.mic; color: hifi.colors.primaryHighlight; + anchors.left: parent.left + anchors.leftMargin: -size/4 //the glyph has empty space at left about 25% anchors.verticalCenter: parent.verticalCenter; size: 30; } + RalewayRegular { width: margins.sizeText + margins.sizeLevel + anchors.left: parent.left + anchors.leftMargin: margins.sizeCheckBox anchors.verticalCenter: parent.verticalCenter; size: 16; color: hifi.colors.lightGrayText; @@ -151,7 +160,7 @@ Rectangle { id: inputView width: parent.width - margins.paddings*2 x: margins.paddings - height: 150 + height: Math.min(150, contentHeight); spacing: 4; snapMode: ListView.SnapToItem; clip: true; @@ -162,6 +171,7 @@ Rectangle { AudioControls.CheckBox { anchors.left: parent.left + spacing: margins.sizeCheckBox - boxSize width: parent.width - inputLevel.width clip: true checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; @@ -186,20 +196,25 @@ Rectangle { Separator {} - Row { + Item { x: margins.paddings; width: parent.width - margins.paddings*2 height: 36 - spacing: 0 + HiFiGlyphs { + anchors.left: parent.left + anchors.leftMargin: -size/4 //the glyph has empty space at left about 25% + anchors.verticalCenter: parent.verticalCenter; width: margins.sizeCheckBox text: hifi.glyphs.unmuted; color: hifi.colors.primaryHighlight; - anchors.verticalCenter: parent.verticalCenter; size: 36; } + RalewayRegular { width: margins.sizeText + margins.sizeLevel + anchors.left: parent.left + anchors.leftMargin: margins.sizeCheckBox anchors.verticalCenter: parent.verticalCenter; size: 16; color: hifi.colors.lightGrayText; @@ -211,7 +226,7 @@ Rectangle { id: outputView width: parent.width - margins.paddings*2 x: margins.paddings - height: Math.min(210, contentHeight); + height: Math.min(360 - inputView.height, contentHeight); spacing: 4; snapMode: ListView.SnapToItem; clip: true; @@ -222,6 +237,7 @@ Rectangle { AudioControls.CheckBox { width: parent.width + spacing: margins.sizeCheckBox - boxSize boxSize: margins.sizeCheckBox / 2 isRound: true checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; diff --git a/interface/resources/qml/hifi/audio/AudioTabButton.qml b/interface/resources/qml/hifi/audio/AudioTabButton.qml index 2e6e114039..3a3ed90f5e 100644 --- a/interface/resources/qml/hifi/audio/AudioTabButton.qml +++ b/interface/resources/qml/hifi/audio/AudioTabButton.qml @@ -20,7 +20,7 @@ TabButton { HifiConstants { id: hifi; } - contentItem: Text { + contentItem: RalewaySemiBold { text: control.text font: control.font color: "white" diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml index 806f08f439..77c001816f 100644 --- a/interface/resources/qml/hifi/audio/CheckBox.qml +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -14,6 +14,5 @@ import QtQuick 2.7 import "../../controls-uit" as HifiControls HifiControls.CheckBox2 { - spacing: 8 color: "white" } diff --git a/interface/resources/qml/styles-uit/HiFiGlyphs.qml b/interface/resources/qml/styles-uit/HiFiGlyphs.qml index cbd6fa1d68..f78d6c6f59 100644 --- a/interface/resources/qml/styles-uit/HiFiGlyphs.qml +++ b/interface/resources/qml/styles-uit/HiFiGlyphs.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root From 78aa5820d06b9fbe6e4a20bdd9fc5ff08bea9e6d Mon Sep 17 00:00:00 2001 From: vladest Date: Sun, 27 Aug 2017 11:56:28 +0200 Subject: [PATCH 034/202] Added proper destructor call for audio scripting interface. Save settings in destructor. Added logging to check readed audio settings --- interface/src/Application.cpp | 1 + interface/src/scripting/AudioDevices.cpp | 50 +++++++++++++++++++++++- interface/src/scripting/AudioDevices.h | 3 ++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d13b7880fa..3aa3fbf95d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2052,6 +2052,7 @@ void Application::cleanupBeforeQuit() { // this must happen after QML, as there are unexplained audio crashes originating in qtwebengine DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; } diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index e31641389c..cff17c1a5c 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -61,7 +61,52 @@ QHash AudioDeviceList::_roles { Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; -AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) {} +AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) { + auto& setting1 = getSetting(true, QAudio::AudioInput); + if (setting1.isSet()) { + qDebug() << "Device name in settings for HMD, Input" << setting1.get(); + } else { + qDebug() << "Device name in settings for HMD, Input not set"; + } + + auto& setting2 = getSetting(true, QAudio::AudioOutput); + if (setting2.isSet()) { + qDebug() << "Device name in settings for HMD, Output" << setting2.get(); + } else { + qDebug() << "Device name in settings for HMD, Output not set"; + } + + auto& setting3 = getSetting(false, QAudio::AudioInput); + if (setting3.isSet()) { + qDebug() << "Device name in settings for Desktop, Input" << setting3.get(); + } else { + qDebug() << "Device name in settings for Desktop, Input not set"; + } + + auto& setting4 = getSetting(false, QAudio::AudioOutput); + if (setting4.isSet()) { + qDebug() << "Device name in settings for Desktop, Output" << setting4.get(); + } else { + qDebug() << "Device name in settings for Desktop, Output not set"; + } +} + +AudioDeviceList::~AudioDeviceList() { + //save all selected devices + auto& settingHMD = getSetting(true, _mode); + auto& settingDesktop = getSetting(false, _mode); + // store the selected device + foreach(AudioDevice adevice, _devices) { + if (adevice.selectedDesktop) { + qDebug() << "Saving Desktop for" << _mode << "name" << adevice.info.deviceName(); + settingDesktop.set(adevice.info.deviceName()); + } + if (adevice.selectedHMD) { + qDebug() << "Saving HMD for" << _mode << "name" << adevice.info.deviceName(); + settingHMD.set(adevice.info.deviceName()); + } + } +} QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() >= _devices.size()) { @@ -156,6 +201,7 @@ void AudioDeviceList::onDevicesChanged(const QList& devices, b //no selected device for context. fallback to saved isSelected = (device.info.deviceName() == savedDeviceName); } + qDebug() << "adding audio device:" << device.display << device.selectedDesktop << device.selectedHMD << _mode; _devices.push_back(device); } @@ -182,6 +228,8 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { _outputs.onDevicesChanged(devicesOutput, false); } +AudioDevices::~AudioDevices() {} + void AudioDevices::onContextChanged(const QString& context) { _inputs.resetDevice(_contextIsHMD); _outputs.resetDevice(_contextIsHMD); diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 5399123329..9b3b2bce9b 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -38,6 +38,7 @@ class AudioDeviceList : public QAbstractListModel { public: AudioDeviceList(QAudio::Mode mode); + virtual ~AudioDeviceList(); int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); } QHash roleNames() const override { return _roles; } @@ -78,6 +79,8 @@ class AudioDevices : public QObject { public: AudioDevices(bool& contextIsHMD); + virtual ~AudioDevices(); + void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD); void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD); From ba34a0ddef34269b072cc5421473233dccad238d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 28 Aug 2017 15:01:31 -0700 Subject: [PATCH 035/202] fail bake of partially baked FBX that references KTX --- libraries/baking/src/FBXBaker.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 1a11071b5b..81b5de7546 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -105,11 +105,8 @@ void FBXBaker::bakeSourceCopy() { void FBXBaker::setupOutputFolder() { // make sure there isn't already an output directory using the same name - int iteration = 0; - if (QDir(_bakedOutputDir).exists()) { qWarning() << "Output path" << _bakedOutputDir << "already exists. Continuing."; - //_bakedOutputDir = _baseOutputPath + "/" + _fbxName + "-" + QString::number(++iteration) + "/"; } else { qCDebug(model_baking) << "Creating FBX output folder" << _bakedOutputDir; @@ -377,8 +374,15 @@ void FBXBaker::rewriteAndBakeSceneTextures() { QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; // make sure this texture points to something and isn't one we've already re-mapped - if (!textureFileInfo.filePath().isEmpty() - && textureFileInfo.suffix() != BAKED_TEXTURE_EXT.mid(1)) { + if (!textureFileInfo.filePath().isEmpty()) { + + if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { + // re-baking an FBX that already references baked textures is a fail + // so we add an error and return from here + handleError("Cannot re-bake a partially baked FBX file that references baked KTX textures"); + + return; + } // construct the new baked texture file name and file path // ensuring that the baked texture will have a unique name From 6992bd6f48ab71145dd4c1b881682e50f8d30832 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 28 Aug 2017 15:38:31 -0700 Subject: [PATCH 036/202] add deletion of baked content when original removed --- assignment-client/src/assets/AssetServer.cpp | 31 +++++++++++++++----- assignment-client/src/assets/AssetServer.h | 3 ++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 212b9d8d19..753ca118cf 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -53,6 +53,8 @@ static const QList BAKEABLE_TEXTURE_EXTENSIONS = QImageReader::suppo static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; +static const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; + BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) : _assetHash(assetHash), _assetPath(assetPath), _filePath(filePath) { } @@ -115,7 +117,7 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& return (*it)->isBaking() ? Baking : Pending; } - if (path.startsWith("/.baked/")) { + if (path.startsWith(HIDDEN_BAKED_CONTENT_FOLDER)) { return Baked; } @@ -136,7 +138,7 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& return Unrelevant; } - auto bakedPath = "/.baked/" + hash + "/" + bakedFilename; + auto bakedPath = HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + bakedFilename; auto jt = _fileMappings.find(bakedPath); if (jt != _fileMappings.end()) { if (jt->toString() == hash) { @@ -179,14 +181,14 @@ void AssetServer::createEmptyMetaFile(const AssetHash& hash) { } bool AssetServer::hasMetaFile(const AssetHash& hash) { - QString metaFilePath = "/.baked/" + hash + "/meta.json"; + QString metaFilePath = HIDDEN_BAKED_CONTENT_FOLDER + hash + "/meta.json"; qDebug() << "in mappings?" << metaFilePath; return _fileMappings.contains(metaFilePath); } bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHash) { - if (path.startsWith("/.baked/")) { + if (path.startsWith(HIDDEN_BAKED_CONTENT_FOLDER)) { return false; } @@ -207,7 +209,7 @@ bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHa return false; } - auto bakedPath = "/.baked/" + assetHash + "/" + bakedFilename; + auto bakedPath = HIDDEN_BAKED_CONTENT_FOLDER + assetHash + "/" + bakedFilename; return !_fileMappings.contains(bakedPath); } @@ -392,6 +394,8 @@ void AssetServer::cleanupUnmappedFiles() { if (removeableFile.remove()) { qCDebug(asset_server) << "\tDeleted" << fileInfo.fileName() << "from asset files directory since it is unmapped."; + + removeBakedPathsForDeletedAsset(fileInfo.fileName()); } else { qCDebug(asset_server) << "\tAttempt to delete unmapped file" << fileInfo.fileName() << "failed"; } @@ -464,7 +468,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode if (!bakedRootFile.isEmpty()) { // we ran into an asset for which we could have a baked version, let's check if it's ready - bakedAssetPath = "/.baked/" + originalAssetHash + "/" + bakedRootFile; + bakedAssetPath = HIDDEN_BAKED_CONTENT_FOLDER + originalAssetHash + "/" + bakedRootFile; auto bakedIt = _fileMappings.find(bakedAssetPath); if (bakedIt != _fileMappings.end()) { @@ -858,6 +862,18 @@ bool pathIsFolder(const AssetPath& path) { return path.endsWith('/'); } +void AssetServer::removeBakedPathsForDeletedAsset(AssetHash hash) { + // we deleted the file with this hash + + // check if we had baked content for that file that should also now be removed + // by calling deleteMappings for the hidden baked content folder for this hash + AssetPathList hiddenBakedFolder { HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" }; + + qCDebug(asset_server) << "Deleting baked content below" << hiddenBakedFolder << "since" << hash << "was deleted"; + + deleteMappings(hiddenBakedFolder); +} + bool AssetServer::deleteMappings(AssetPathList& paths) { // take a copy of the current mappings in case persistence of these deletes fails auto oldMappings = _fileMappings; @@ -928,6 +944,8 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { if (removeableFile.remove()) { qCDebug(asset_server) << "\tDeleted" << hash << "from asset files directory since it is now unmapped."; + + removeBakedPathsForDeletedAsset(hash); } else { qCDebug(asset_server) << "\tAttempt to delete unmapped file" << hash << "failed"; } @@ -1041,7 +1059,6 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { } } -static const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx"; static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx"; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 94576b329d..cf193ba676 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -104,6 +104,9 @@ private: /// Create meta file to describe baked content for original asset bool createMetaFile(AssetHash originalAssetHash); + /// Remove baked paths when the original asset is deleteds + void removeBakedPathsForDeletedAsset(AssetHash originalAssetHash); + Mappings _fileMappings; QDir _resourcesDirectory; From e9258ec97a6f9718d424ffb075aea456e1ab101b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 28 Aug 2017 15:37:15 -0700 Subject: [PATCH 037/202] Fix loading standalone baked textures from Asset Server NetworkTexture was not properly handling redirected ATP files. For instance, if going from .jpg -> .ktx, the NetworkTexture class needs to be aware of this so it can stop the current request and make multiple requests for the individual mip levels. --- assignment-client/src/assets/AssetServer.cpp | 9 ++++--- libraries/baking/src/TextureBaker.cpp | 8 ++++++ libraries/baking/src/TextureBaker.h | 3 +++ .../src/model-networking/TextureCache.cpp | 21 +++++++++++++-- .../src/model-networking/TextureCache.h | 2 ++ .../networking/src/AssetResourceRequest.cpp | 27 ++++++++++++------- libraries/networking/src/ResourceCache.cpp | 3 ++- libraries/networking/src/ResourceCache.h | 3 ++- libraries/networking/src/ResourceRequest.h | 5 +++- 9 files changed, 63 insertions(+), 18 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 212b9d8d19..ce9e49326d 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -49,7 +49,7 @@ static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000; const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" }; -static const QList BAKEABLE_TEXTURE_EXTENSIONS = QImageReader::supportedImageFormats(); +static QStringList BAKEABLE_TEXTURE_EXTENSIONS; static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; @@ -71,7 +71,8 @@ void BakeAssetTask::run() { }; } else { baker = std::unique_ptr { - new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, PathUtils::generateTemporaryDir()) + new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, + PathUtils::generateTemporaryDir()) }; } @@ -198,7 +199,7 @@ bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHa auto extension = path.mid(dotIndex + 1); QString bakedFilename; - + if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { bakedFilename = BAKED_MODEL_SIMPLE_NAME; } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(assetHash)) { @@ -247,6 +248,8 @@ AssetServer::AssetServer(ReceivedMessage& message) : _transferTaskPool(this), _bakingTaskPool(this) { + BAKEABLE_TEXTURE_EXTENSIONS = TextureBaker::getSupportedFormats(); + qDebug() << "Supported baking texture formats:" << BAKEABLE_MODEL_EXTENSIONS; // Most of the work will be I/O bound, reading from disk and constructing packet objects, // so the ideal is greater than the number of cores on the system. diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 44995fa026..f80af66aa2 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -43,6 +43,14 @@ void TextureBaker::bake() { loadTexture(); } +const QStringList TextureBaker::getSupportedFormats() { + auto formats = QImageReader::supportedImageFormats(); + QStringList stringFormats; + std::transform(formats.begin(), formats.end(), std::back_inserter(stringFormats), + [](auto& format) -> QString { return format; }); + return stringFormats; +} + void TextureBaker::loadTexture() { // check if the texture is local or first needs to be downloaded if (_textureURL.isLocalFile()) { diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h index ee1e968f20..76d0a69823 100644 --- a/libraries/baking/src/TextureBaker.h +++ b/libraries/baking/src/TextureBaker.h @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -28,6 +29,8 @@ class TextureBaker : public Baker { public: TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory); + static const QStringList getSupportedFormats(); + const QByteArray& getOriginalTexture() const { return _originalTexture; } QUrl getTextureURL() const { return _textureURL; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 5b43a4090f..cc29e841ce 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -299,6 +299,8 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, _textureSource = std::make_shared(); _lowestRequestedMipLevel = 0; + _shouldFailOnRedirect = !_sourceIsKTX; + if (type == image::TextureUsage::CUBE_TEXTURE) { setLoadPriority(this, SKYBOX_LOAD_PRIORITY); } else if (_sourceIsKTX) { @@ -428,6 +430,21 @@ void NetworkTexture::makeRequest() { } +bool NetworkTexture::handleFailedRequest(ResourceRequest::Result result) { + if (!_sourceIsKTX && result == ResourceRequest::Result::RedirectFail) { + auto newPath = _request->getRelativePathUrl(); + if (newPath.fileName().endsWith(".ktx")) { + qDebug() << "Redirecting to" << newPath << "from" << _url; + _sourceIsKTX = true; + _activeUrl = newPath; + _shouldFailOnRedirect = false; + makeRequest(); + return true; + } + } + return Resource::handleFailedRequest(result); +} + void NetworkTexture::startRequestForNextMipLevel() { auto self = _self.lock(); if (!self) { @@ -527,7 +544,7 @@ void NetworkTexture::ktxInitialDataRequestFinished() { _ktxHighMipData = _ktxMipRequest->getData(); handleFinishedInitialLoad(); } else { - if (handleFailedRequest(result)) { + if (Resource::handleFailedRequest(result)) { _ktxResourceState = PENDING_INITIAL_LOAD; } else { _ktxResourceState = FAILED_TO_LOAD; @@ -616,7 +633,7 @@ void NetworkTexture::ktxMipRequestFinished() { finishedLoading(false); } } else { - if (handleFailedRequest(result)) { + if (Resource::handleFailedRequest(result)) { _ktxResourceState = PENDING_MIP_REQUEST; } else { _ktxResourceState = FAILED_TO_LOAD; diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index f5a0ec5215..5bc5aa7d96 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -75,6 +75,8 @@ protected: virtual void downloadFinished(const QByteArray& data) override; + bool handleFailedRequest(ResourceRequest::Result result) override; + Q_INVOKABLE void loadContent(const QByteArray& content); Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 01fe971125..c2e0bc1215 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -82,19 +82,23 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { Q_ASSERT(_state == InProgress); Q_ASSERT(request == _assetMappingRequest); + bool failed = false; + switch (request->getError()) { case MappingRequest::NoError: // we have no error, we should have a resulting hash - use that to send of a request for that asset qCDebug(networking) << "Got mapping for:" << path << "=>" << request->getHash(); - requestHash(request->getHash()); - statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_SUCCESS); // if we got a redirected path we need to store that with the resource request as relative path URL - if (request->wasRedirected()) { - qDebug() << "Request was redirected"; + if (request->wasRedirected() && _failOnRedirect) { _relativePathURL = ATP_SCHEME + request->getRedirectedPath(); + _result = RedirectFail; + + failed = true; + } else { + requestHash(request->getHash()); } break; @@ -113,17 +117,20 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { break; } - // since we've failed we know we are finished - _state = Finished; - emit finished(); - - statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_FAILED); - statTracker->incrementStat(STAT_ATP_REQUEST_FAILED); + failed = true; break; } } + if (failed) { + _state = Finished; + emit finished(); + + statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_FAILED); + statTracker->incrementStat(STAT_ATP_REQUEST_FAILED); + } + _assetMappingRequest->deleteLater(); _assetMappingRequest = nullptr; }); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index b7ab1269f9..fbe7c05801 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -687,8 +687,9 @@ void Resource::makeRequest() { PROFILE_ASYNC_END(resource, "Resource:" + getType(), QString::number(_requestID)); return; } - + _request->setByteRange(_requestByteRange); + _request->setFailOnRedirect(_shouldFailOnRedirect); qCDebug(resourceLog).noquote() << "Starting request for:" << _url.toDisplayString(); emit loading(); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 40df6418a5..17531f45b0 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -449,12 +449,13 @@ protected: Q_INVOKABLE void allReferencesCleared(); /// Return true if the resource will be retried - bool handleFailedRequest(ResourceRequest::Result result); + virtual bool handleFailedRequest(ResourceRequest::Result result); QUrl _url; QUrl _effectiveBaseURL{ _url }; QUrl _activeUrl; ByteRange _requestByteRange; + bool _shouldFailOnRedirect { false }; // _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 diff --git a/libraries/networking/src/ResourceRequest.h b/libraries/networking/src/ResourceRequest.h index 0a4dc12293..cf852c1e1b 100644 --- a/libraries/networking/src/ResourceRequest.h +++ b/libraries/networking/src/ResourceRequest.h @@ -57,7 +57,8 @@ public: AccessDenied, InvalidByteRange, InvalidURL, - NotFound + NotFound, + RedirectFail }; Q_ENUM(Result) @@ -70,6 +71,7 @@ public: bool loadedFromCache() const { return _loadedFromCache; } bool getRangeRequestSuccessful() const { return _rangeRequestSuccessful; } bool getTotalSizeOfResource() const { return _totalSizeOfResource; } + void setFailOnRedirect(bool failOnRedirect) { _failOnRedirect = failOnRedirect; } void setCacheEnabled(bool value) { _cacheEnabled = value; } void setByteRange(ByteRange byteRange) { _byteRange = byteRange; } @@ -89,6 +91,7 @@ protected: State _state { NotStarted }; Result _result; QByteArray _data; + bool _failOnRedirect { false }; bool _cacheEnabled { true }; bool _loadedFromCache { false }; ByteRange _byteRange; From 31345e4264a85bf7e297dd2e91b0f19145762557 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Aug 2017 13:15:14 -0700 Subject: [PATCH 038/202] Fix ATP requests not keeping track of all mapping redirects --- libraries/networking/src/AssetResourceRequest.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index c2e0bc1215..c9ca6ebd43 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -92,10 +92,12 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_SUCCESS); // if we got a redirected path we need to store that with the resource request as relative path URL - if (request->wasRedirected() && _failOnRedirect) { + if (request->wasRedirected()) { _relativePathURL = ATP_SCHEME + request->getRedirectedPath(); - _result = RedirectFail; + } + if (request->wasRedirected() && _failOnRedirect) { + _result = RedirectFail; failed = true; } else { requestHash(request->getHash()); From f2997c0997d137d85b958208cc7377dd072c31ed Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Aug 2017 16:40:43 -0700 Subject: [PATCH 039/202] refuse to perform mapping operations in /.baked/ --- assignment-client/src/assets/AssetServer.cpp | 35 ++++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 10aadc33cc..f7a8ecf453 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -533,11 +533,18 @@ void AssetServer::handleSetMappingOperation(ReceivedMessage& message, SharedNode auto assetHash = message.read(SHA256_HASH_LENGTH).toHex(); - if (setMapping(assetPath, assetHash)) { - replyPacket.writePrimitive(AssetServerError::NoError); + // don't process a set mapping operation that is inside the hidden baked folder + if (assetPath.startsWith(HIDDEN_BAKED_CONTENT_FOLDER)) { + qCDebug(asset_server) << "Refusing to process a set mapping operation inside" << HIDDEN_BAKED_CONTENT_FOLDER; + replyPacket.writePrimitive(AssetServerError::PermissionDenied); } else { - replyPacket.writePrimitive(AssetServerError::MappingOperationFailed); + if (setMapping(assetPath, assetHash)) { + replyPacket.writePrimitive(AssetServerError::NoError); + } else { + replyPacket.writePrimitive(AssetServerError::MappingOperationFailed); + } } + } else { replyPacket.writePrimitive(AssetServerError::PermissionDenied); } @@ -551,7 +558,14 @@ void AssetServer::handleDeleteMappingsOperation(ReceivedMessage& message, Shared QStringList mappingsToDelete; for (int i = 0; i < numberOfDeletedMappings; ++i) { - mappingsToDelete << message.readString(); + auto mapping = message.readString(); + + if (!mapping.startsWith(HIDDEN_BAKED_CONTENT_FOLDER)) { + mappingsToDelete << mapping; + } else { + qCDebug(asset_server) << "Refusing to delete mapping" << mapping + << "that is inside" << HIDDEN_BAKED_CONTENT_FOLDER; + } } if (deleteMappings(mappingsToDelete)) { @@ -569,11 +583,18 @@ void AssetServer::handleRenameMappingOperation(ReceivedMessage& message, SharedN QString oldPath = message.readString(); QString newPath = message.readString(); - if (renameMapping(oldPath, newPath)) { - replyPacket.writePrimitive(AssetServerError::NoError); + if (oldPath.startsWith(HIDDEN_BAKED_CONTENT_FOLDER) || newPath.startsWith(HIDDEN_BAKED_CONTENT_FOLDER)) { + qCDebug(asset_server) << "Cannot rename" << oldPath << "to" << newPath + << "since one of the paths is inside" << HIDDEN_BAKED_CONTENT_FOLDER; + replyPacket.writePrimitive(AssetServerError::PermissionDenied); } else { - replyPacket.writePrimitive(AssetServerError::MappingOperationFailed); + if (renameMapping(oldPath, newPath)) { + replyPacket.writePrimitive(AssetServerError::NoError); + } else { + replyPacket.writePrimitive(AssetServerError::MappingOperationFailed); + } } + } else { replyPacket.writePrimitive(AssetServerError::PermissionDenied); } From 3abca67f1a48426162e3c56e5e29743d4bea0522 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 29 Aug 2017 16:41:02 -0700 Subject: [PATCH 040/202] remove use of auto in TextureBaker for OS X compile --- libraries/baking/src/TextureBaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index f80af66aa2..548e3921e4 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -47,7 +47,7 @@ const QStringList TextureBaker::getSupportedFormats() { auto formats = QImageReader::supportedImageFormats(); QStringList stringFormats; std::transform(formats.begin(), formats.end(), std::back_inserter(stringFormats), - [](auto& format) -> QString { return format; }); + [](QByteArray& format) -> QString { return format; }); return stringFormats; } From 70430c009d2ac68a7e6c2a448c57b547f4f1cb92 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Aug 2017 13:15:14 -0700 Subject: [PATCH 041/202] Fix ATP requests not keeping track of all mapping redirects --- libraries/networking/src/AssetResourceRequest.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index c2e0bc1215..c9ca6ebd43 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -92,10 +92,12 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { statTracker->incrementStat(STAT_ATP_MAPPING_REQUEST_SUCCESS); // if we got a redirected path we need to store that with the resource request as relative path URL - if (request->wasRedirected() && _failOnRedirect) { + if (request->wasRedirected()) { _relativePathURL = ATP_SCHEME + request->getRedirectedPath(); - _result = RedirectFail; + } + if (request->wasRedirected() && _failOnRedirect) { + _result = RedirectFail; failed = true; } else { requestHash(request->getHash()); From ed21ad11d27f60eacb8f94b34addca4031259ef5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Aug 2017 15:14:53 -0700 Subject: [PATCH 042/202] add baking to default build, add default to baking status --- libraries/baking/CMakeLists.txt | 2 -- libraries/networking/src/AssetUtils.cpp | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 44c52ae0d8..ee2d861539 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -11,7 +11,5 @@ if (FBX_FOUND) target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) endif () -set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) - link_hifi_libraries(shared model networking ktx image) include_hifi_library_headers(gpu) diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index af085d6463..df3aa7ad0b 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -87,8 +87,6 @@ bool isValidHash(const AssetHash& hash) { QString bakingStatusToString(BakingStatus status) { switch (status) { - case Unrelevant: - return "--"; case NotBaked: return "Not Baked"; case Pending: @@ -99,5 +97,8 @@ QString bakingStatusToString(BakingStatus status) { return "Baked"; case Error: return "Error"; + default: + case Unrelevant: + return "--"; } } From 81fdc27ca871afacbff9e60c5b9f2c4ac5f611b8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Aug 2017 15:34:20 -0700 Subject: [PATCH 043/202] use 2017.1 FBX SDK on all platforms, change linux location --- cmake/modules/FindFBX.cmake | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cmake/modules/FindFBX.cmake b/cmake/modules/FindFBX.cmake index 6eb6d11f9d..3963f4d2e6 100644 --- a/cmake/modules/FindFBX.cmake +++ b/cmake/modules/FindFBX.cmake @@ -17,11 +17,7 @@ # which uses the MIT license (https://github.com/ufz-vislab/VtkFbxConverter/blob/master/LICENSE.txt) if (NOT FBX_VERSION) - if (WIN32) - set(FBX_VERSION 2017.1) - else() - set(FBX_VERSION 2017.0.1) - endif() + set(FBX_VERSION 2017.1) endif() string(REGEX REPLACE "^([0-9]+).*$" "\\1" FBX_VERSION_MAJOR "${FBX_VERSION}") @@ -29,7 +25,7 @@ string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*$" "\\1" FBX_VERSION_MINOR "${FBX_VER string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" FBX_VERSION_PATCH "${FBX_VERSION}") set(FBX_MAC_LOCATIONS "/Applications/Autodesk/FBX\ SDK/${FBX_VERSION}") -set(FBX_LINUX_LOCATIONS "/usr/local/lib/gcc4/x64/debug/") +set(FBX_LINUX_LOCATIONS "/usr/local/fbxsdk") if (WIN32) string(REGEX REPLACE "\\\\" "/" WIN_PROGRAM_FILES_X64_DIRECTORY $ENV{ProgramW6432}) @@ -73,7 +69,7 @@ function(_fbx_find_library _name _lib _suffix) find_library(${_name} NAMES ${_lib} HINTS ${FBX_SEARCH_LOCATIONS} - PATH_SUFFIXES lib/${fbx_compiler}/${_suffix} lib/${fbx_compiler}/ub/${_suffix} lib/${VS_PREFIX}/x64/${_suffix} + PATH_SUFFIXES lib/${fbx_compiler}/${_suffix} lib/${fbx_compiler}/x64/${_suffix} lib/${fbx_compiler}/ub/${_suffix} lib/${VS_PREFIX}/x64/${_suffix} ) mark_as_advanced(${_name}) From 4e1e483e27a9f87b83f5bdaf989f1cf773dcaf30 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Aug 2017 16:50:20 -0700 Subject: [PATCH 044/202] link DL libs with FBX SDK for baking --- libraries/baking/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index ee2d861539..806220ec30 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -9,6 +9,10 @@ if (FBX_FOUND) target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES}) endif () target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) + + if (UNIX) + target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) + endif (UNIX) endif () link_hifi_libraries(shared model networking ktx image) From d68338f0b02f19dd747e954413ac8d4957cbd079 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Aug 2017 16:53:31 -0700 Subject: [PATCH 045/202] Add error handling to asset server baking --- assignment-client/src/assets/AssetServer.cpp | 251 +++++++++++++------ assignment-client/src/assets/AssetServer.h | 24 +- 2 files changed, 192 insertions(+), 83 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index ce9e49326d..b91c8795c2 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -83,6 +83,7 @@ void BakeAssetTask::run() { if (baker->hasErrors()) { qDebug() << "Failed to bake: " << _assetHash << _assetPath; + emit bakeFailed(_assetHash, _assetPath); } else { auto vectorOutputFiles = QVector::fromStdVector(baker->getOutputFiles()); qDebug() << "Finished baking: " << _assetHash << _assetPath << vectorOutputFiles; @@ -99,6 +100,7 @@ void AssetServer::bakeAsset(const AssetHash& assetHash, const AssetPath& assetPa _pendingBakes[assetHash] = task; connect(task.get(), &BakeAssetTask::bakeComplete, this, &AssetServer::handleCompletedBake); + connect(task.get(), &BakeAssetTask::bakeFailed, this, &AssetServer::handleFailedBake); _bakingTaskPool.start(task.get()); } else { @@ -140,7 +142,7 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& auto bakedPath = "/.baked/" + hash + "/" + bakedFilename; auto jt = _fileMappings.find(bakedPath); if (jt != _fileMappings.end()) { - if (jt->toString() == hash) { + if (jt->first == hash) { return NotBaked; } else { return Baked; @@ -153,8 +155,8 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& void AssetServer::bakeAssets() { auto it = _fileMappings.cbegin(); for (; it != _fileMappings.cend(); ++it) { - auto path = it.key(); - auto hash = it.value().toString(); + auto path = it->first; + auto hash = it->second; maybeBake(path, hash); } } @@ -181,9 +183,8 @@ void AssetServer::createEmptyMetaFile(const AssetHash& hash) { bool AssetServer::hasMetaFile(const AssetHash& hash) { QString metaFilePath = "/.baked/" + hash + "/meta.json"; - qDebug() << "in mappings?" << metaFilePath; - return _fileMappings.contains(metaFilePath); + return _fileMappings.find(metaFilePath) != _fileMappings.end(); } bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHash) { @@ -200,16 +201,25 @@ bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHa QString bakedFilename; + bool loaded; + AssetMeta meta; + std::tie(loaded, meta) = readMetaFile(assetHash); + + // TODO: Allow failed bakes that happened on old versions to be re-baked + if (loaded && meta.failedLastBake) { + return false; + } + if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { bakedFilename = BAKED_MODEL_SIMPLE_NAME; - } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(assetHash)) { + } else if (loaded && BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit())) { bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; } else { return false; } auto bakedPath = "/.baked/" + assetHash + "/" + bakedFilename; - return !_fileMappings.contains(bakedPath); + return _fileMappings.find(bakedPath) == _fileMappings.end(); } bool interfaceRunning() { @@ -364,7 +374,7 @@ void AssetServer::completeSetup() { qCInfo(asset_server) << "There are" << hashedFiles.size() << "asset files in the asset directory."; - if (_fileMappings.count() > 0) { + if (_fileMappings.size() > 0) { cleanupUnmappedFiles(); } @@ -382,21 +392,26 @@ void AssetServer::cleanupUnmappedFiles() { auto files = _filesDirectory.entryInfoList(QDir::Files); - // grab the currently mapped hashes - auto mappedHashes = _fileMappings.values(); - qCInfo(asset_server) << "Performing unmapped asset cleanup."; for (const auto& fileInfo : files) { - if (hashFileRegex.exactMatch(fileInfo.fileName())) { - if (!mappedHashes.contains(fileInfo.fileName())) { + auto filename = fileInfo.fileName(); + if (hashFileRegex.exactMatch(filename)) { + bool matched { false }; + for (auto& pair : _fileMappings) { + if (pair.second == filename) { + matched = true; + break; + } + } + if (!matched) { // remove the unmapped file QFile removeableFile { fileInfo.absoluteFilePath() }; if (removeableFile.remove()) { - qCDebug(asset_server) << "\tDeleted" << fileInfo.fileName() << "from asset files directory since it is unmapped."; + qCDebug(asset_server) << "\tDeleted" << filename << "from asset files directory since it is unmapped."; } else { - qCDebug(asset_server) << "\tAttempt to delete unmapped file" << fileInfo.fileName() << "failed"; + qCDebug(asset_server) << "\tAttempt to delete unmapped file" << filename << "failed"; } } } @@ -458,9 +473,8 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(assetPathExtension.toLocal8Bit())) { bakedRootFile = BAKED_TEXTURE_SIMPLE_NAME; } - qDebug() << bakedRootFile << assetPathExtension; - auto originalAssetHash = it->toString(); + auto originalAssetHash = it->second; QString redirectedAssetHash; QString bakedAssetPath; quint8 wasRedirected = false; @@ -473,7 +487,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode if (bakedIt != _fileMappings.end()) { qDebug() << "Did find baked version for: " << originalAssetHash << assetPath; // we found a baked version of the requested asset to serve, redirect to that - redirectedAssetHash = bakedIt->toString(); + redirectedAssetHash = bakedIt->second; wasRedirected = true; } else { qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath; @@ -498,9 +512,8 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode auto query = QUrlQuery(url.query()); bool isSkybox = query.hasQueryItem("skybox"); - qDebug() << "Is skybox? " << isSkybox; if (isSkybox) { - createMetaFile(originalAssetHash); + writeMetaFile(originalAssetHash); maybeBake(originalAssetHash, assetPath); } } @@ -512,14 +525,14 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) { replyPacket.writePrimitive(AssetServerError::NoError); - auto count = _fileMappings.size(); + uint32_t count = _fileMappings.size(); replyPacket.writePrimitive(count); - for (auto it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++ it) { - replyPacket.writeString(it.key()); - replyPacket.write(QByteArray::fromHex(it.value().toString().toUtf8())); - replyPacket.writePrimitive(getAssetStatus(it.key(), it.value().toString())); + for (auto it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++it) { + replyPacket.writeString(it->first); + replyPacket.write(QByteArray::fromHex(it->second.toUtf8())); + replyPacket.writePrimitive(getAssetStatus(it->first, it->second)); } } @@ -759,31 +772,38 @@ bool AssetServer::loadMappingsFromFile() { auto jsonDocument = QJsonDocument::fromJson(mapFile.readAll(), &error); if (error.error == QJsonParseError::NoError) { - _fileMappings = jsonDocument.object().toVariantHash(); - - // remove any mappings that don't match the expected format - auto it = _fileMappings.begin(); - while (it != _fileMappings.end()) { - bool shouldDrop = false; - - if (!isValidFilePath(it.key())) { - qCWarning(asset_server) << "Will not keep mapping for" << it.key() << "since it is not a valid path."; - shouldDrop = true; - } - - if (!isValidHash(it.value().toString())) { - qCWarning(asset_server) << "Will not keep mapping for" << it.key() << "since it does not have a valid hash."; - shouldDrop = true; - } - - if (shouldDrop) { - it = _fileMappings.erase(it); - } else { - ++it; - } + if (!jsonDocument.isObject()) { + qCWarning(asset_server) << "Failed to read mapping file, root value in" << mapFilePath << "is not an object"; + return false; } - qCInfo(asset_server) << "Loaded" << _fileMappings.count() << "mappings from map file at" << mapFilePath; + //_fileMappings = jsonDocument.object().toVariantHash(); + auto root = jsonDocument.object(); + for (auto it = root.begin(); it != root.end(); ++it) { + auto key = it.key(); + auto value = it.value(); + + if (!value.isString()) { + qCWarning(asset_server) << "Skipping" << key << ":" << value << "because it is not a string"; + continue; + } + + if (!isValidFilePath(key)) { + qCWarning(asset_server) << "Will not keep mapping for" << key << "since it is not a valid path."; + continue; + } + + if (!isValidHash(value.toString())) { + qCWarning(asset_server) << "Will not keep mapping for" << key << "since it does not have a valid hash."; + continue; + } + + + qDebug() << "Added " << key << value.toString(); + _fileMappings[key] = value.toString(); + } + + qCInfo(asset_server) << "Loaded" << _fileMappings.size() << "mappings from map file at" << mapFilePath; return true; } } @@ -802,8 +822,13 @@ bool AssetServer::writeMappingsToFile() { QFile mapFile { mapFilePath }; if (mapFile.open(QIODevice::WriteOnly)) { - auto jsonObject = QJsonObject::fromVariantHash(_fileMappings); - QJsonDocument jsonDocument { jsonObject }; + QJsonObject root; + + for (auto it : _fileMappings) { + root[it.first] = it.second; + } + + QJsonDocument jsonDocument { root }; if (mapFile.write(jsonDocument.toJson()) != -1) { qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath; @@ -832,7 +857,8 @@ bool AssetServer::setMapping(AssetPath path, AssetHash hash) { } // remember what the old mapping was in case persistence fails - auto oldMapping = _fileMappings.value(path).toString(); + auto it = _fileMappings.find(path); + auto oldMapping = it != _fileMappings.end() ? it->second : ""; // update the in memory QHash _fileMappings[path] = hash; @@ -846,7 +872,7 @@ bool AssetServer::setMapping(AssetPath path, AssetHash hash) { } else { // failed to persist this mapping to file - put back the old one in our in-memory representation if (oldMapping.isEmpty()) { - _fileMappings.remove(path); + _fileMappings.erase(_fileMappings.find(path)); } else { _fileMappings[path] = oldMapping; } @@ -879,9 +905,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { auto sizeBefore = _fileMappings.size(); while (it != _fileMappings.end()) { - if (it.key().startsWith(path)) { + if (it->first.startsWith(path)) { // add this hash to the list we need to check for asset removal from the server - hashesToCheckForDeletion << it.value().toString(); + hashesToCheckForDeletion << it->second; it = _fileMappings.erase(it); } else { @@ -897,12 +923,14 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { } } else { - auto oldMapping = _fileMappings.take(path); - if (!oldMapping.isNull()) { - // add this hash to the list we need to check for asset removal from server - hashesToCheckForDeletion << oldMapping.toString(); + auto it = _fileMappings.find(path); + if (it != _fileMappings.end()) { + _fileMappings.erase(it); - qCDebug(asset_server) << "Deleted a mapping:" << path << "=>" << oldMapping.toString(); + // add this hash to the list we need to check for asset removal from server + hashesToCheckForDeletion << it->second; + + qCDebug(asset_server) << "Deleted a mapping:" << path << "=>" << it->second; } else { qCDebug(asset_server) << "Unable to delete a mapping that was not found:" << path; } @@ -913,12 +941,9 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { if (writeMappingsToFile()) { // persistence succeeded we are good to go - // grab the current mapped hashes - auto mappedHashes = _fileMappings.values(); - - // enumerate the mapped hashes and clear the list of hashes to check for anything that's present - for (auto& hashVariant : mappedHashes) { - auto it = hashesToCheckForDeletion.find(hashVariant.toString()); + // TODO iterate through hashesToCheckForDeletion instead + for (auto& pair : _fileMappings) { + auto it = hashesToCheckForDeletion.find(pair.second); if (it != hashesToCheckForDeletion.end()) { hashesToCheckForDeletion.erase(it); } @@ -974,16 +999,17 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { auto it = oldMappings.begin(); while (it != oldMappings.end()) { - if (it.key().startsWith(oldPath)) { - auto newKey = it.key(); + auto& oldKey = it->first; + if (oldKey.startsWith(oldPath)) { + auto newKey = oldKey; newKey.replace(0, oldPath.size(), newPath); // remove the old version from the in memory file mappings - _fileMappings.remove(it.key()); - _fileMappings.insert(newKey, it.value()); + _fileMappings.erase(_fileMappings.find(oldKey)); + _fileMappings[newKey] = it->second; } - ++it; + it++; } if (writeMappingsToFile()) { @@ -1008,10 +1034,12 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { } // take the old hash to remove the old mapping - auto oldSourceMapping = _fileMappings.take(oldPath).toString(); + auto it = _fileMappings.find(oldPath); + auto oldSourceMapping = it->second; + _fileMappings.erase(it); // in case we're overwriting, keep the current destination mapping for potential rollback - auto oldDestinationMapping = _fileMappings.value(newPath); + auto oldDestinationMapping = _fileMappings.find(newPath)->second; if (!oldSourceMapping.isEmpty()) { _fileMappings[newPath] = oldSourceMapping; @@ -1027,10 +1055,10 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { if (!oldDestinationMapping.isNull()) { // put back the overwritten mapping for the destination path - _fileMappings[newPath] = oldDestinationMapping.toString(); + _fileMappings[newPath] = oldDestinationMapping; } else { // clear the new mapping - _fileMappings.remove(newPath); + _fileMappings.erase(_fileMappings.find(newPath)); } qCDebug(asset_server) << "Failed to persist renamed mapping:" << oldPath << "=>" << newPath; @@ -1052,6 +1080,19 @@ QString getBakeMapping(const AssetHash& hash, const QString& relativeFilePath) { return HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath; } +void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath) { + qDebug() << "Failed: " << originalAssetHash << assetPath; + + bool loaded; + AssetMeta meta; + + std::tie(loaded, meta) = readMetaFile(originalAssetHash); + + meta.failedLastBake = true; + + writeMetaFile(originalAssetHash, meta); +} + void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector bakedFilePaths) { bool errorCompletingBake { false }; @@ -1121,20 +1162,68 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina if (!errorCompletingBake) { // create the meta file to store which version of the baking process we just completed - createMetaFile(originalAssetHash); + writeMetaFile(originalAssetHash); } else { qWarning() << "Could not complete bake for" << originalAssetHash; } } -bool AssetServer::createMetaFile(AssetHash originalAssetHash) { +static const QString BAKE_VERSION_KEY = "bake_version"; +static const QString APP_VERSION_KEY = "app_version"; +static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake"; + +std::pair AssetServer::readMetaFile(AssetHash hash) { + auto metaFilePath = HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + "meta.json"; + + auto it = _fileMappings.find(metaFilePath); + if (it == _fileMappings.end()) { + return { false, {} }; + } + + auto metaFileHash = it->second; + + QFile metaFile(_filesDirectory.absoluteFilePath(metaFileHash)); + + if (metaFile.open(QIODevice::ReadOnly)) { + auto data = metaFile.readAll(); + metaFile.close(); + + QJsonParseError error; + auto doc = QJsonDocument::fromJson(data, &error); + + if (error.error == QJsonParseError::NoError && doc.isObject()) { + auto root = doc.object(); + + auto bakeVersion = root[BAKE_VERSION_KEY].toInt(-1); + auto appVersion = root[APP_VERSION_KEY].toInt(-1); + auto failedLastBake = root[FAILED_LAST_BAKE_KEY]; + + if (bakeVersion != -1 + && appVersion != -1 + && failedLastBake.isBool()) { + + AssetMeta meta; + meta.bakeVersion = bakeVersion; + meta.applicationVersion = appVersion; + meta.failedLastBake = failedLastBake.toBool(); + + return { true, meta }; + } else { + qCWarning(asset_server) << "Metafile for" << hash << "has either missing or malformed data."; + } + } + } + + return { false, {} }; +} + +bool AssetServer::writeMetaFile(AssetHash originalAssetHash, AssetMeta& meta) { // construct the JSON that will be in the meta file QJsonObject metaFileObject; - static const int BAKE_VERSION = 1; - static const QString VERSION_KEY = "version"; - - metaFileObject[VERSION_KEY] = BAKE_VERSION; + metaFileObject[BAKE_VERSION_KEY] = meta.bakeVersion; + metaFileObject[APP_VERSION_KEY] = meta.applicationVersion; + metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake; QJsonDocument metaFileDoc; metaFileDoc.setObject(metaFileObject); @@ -1164,7 +1253,7 @@ bool AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { for (const auto& path : paths) { auto it = _fileMappings.find(path); if (it != _fileMappings.end()) { - auto hash = it->toString(); + auto hash = it->second; auto dotIndex = path.lastIndexOf("."); if (dotIndex == -1) { diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 94576b329d..f3fff466a3 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -22,6 +22,14 @@ #include "AutoBaker.h" #include "ReceivedMessage.h" + +namespace std { + template <> + struct hash { + size_t operator()(const QString& v) const { return qHash(v); } + }; +} + class BakeAssetTask : public QObject, public QRunnable { Q_OBJECT public: @@ -33,6 +41,7 @@ public: signals: void bakeComplete(QString assetHash, QString assetPath, QVector outputFiles); + void bakeFailed(QString assetHash, QString assetPath); private: std::atomic _isBaking { false }; @@ -41,6 +50,15 @@ private: QString _filePath; }; +struct AssetMeta { + AssetMeta() { + } + + int bakeVersion { 0 }; + int applicationVersion { 0 }; + bool failedLastBake { false }; +}; + class AssetServer : public ThreadedAssignment { Q_OBJECT public: @@ -60,7 +78,7 @@ private slots: void sendStatsPacket() override; private: - using Mappings = QVariantHash; + using Mappings = std::unordered_map; void handleGetMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); void handleGetAllMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket); @@ -100,9 +118,11 @@ private: /// Move baked content for asset to baked directory and update baked status void handleCompletedBake(QString originalAssetHash, QString assetPath, QVector bakedFilePaths); + void handleFailedBake(QString originalAssetHash, QString assetPath); /// Create meta file to describe baked content for original asset - bool createMetaFile(AssetHash originalAssetHash); + std::pair readMetaFile(AssetHash hash); + bool writeMetaFile(AssetHash originalAssetHash, AssetMeta& meta = AssetMeta()); Mappings _fileMappings; From 2aa39e7da488dae25f90baac8f2b2e64b9efc0c3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 30 Aug 2017 16:53:51 -0700 Subject: [PATCH 046/202] Fix baking library not automatically being built --- libraries/baking/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 44c52ae0d8..ee2d861539 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -11,7 +11,5 @@ if (FBX_FOUND) target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) endif () -set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) - link_hifi_libraries(shared model networking ktx image) include_hifi_library_headers(gpu) From 84b5afaa4dd7244efb93a423ff8559e25977bd3c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 30 Aug 2017 17:28:17 -0700 Subject: [PATCH 047/202] mark task dispatcher as override --- libraries/image/src/image/Image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index eaa55d0315..c6baee56c9 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -449,7 +449,7 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { class SequentialTaskDispatcher : public nvtt::TaskDispatcher { public: - virtual void dispatch(nvtt::Task* task, void* context, int count) { + virtual void dispatch(nvtt::Task* task, void* context, int count) override { for (int i = 0; i < count; i++) { task(context, i); } From da8ae237565abe0bf21496ef25bdff09d2b5ce90 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Aug 2017 11:52:42 -0700 Subject: [PATCH 048/202] move default to the bottom of bakingStatusToString --- libraries/networking/src/AssetUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index df3aa7ad0b..4c26d2d633 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -97,8 +97,8 @@ QString bakingStatusToString(BakingStatus status) { return "Baked"; case Error: return "Error"; - default: case Unrelevant: + default: return "--"; } } From 9ce0f03aa2b6f3c7f061271cd0022362256894e6 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Aug 2017 11:58:03 -0700 Subject: [PATCH 049/202] Fix mapping comparision in getAssetStatus being done against path --- assignment-client/src/assets/AssetServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index b91c8795c2..5bdaec8d54 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -142,7 +142,7 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& auto bakedPath = "/.baked/" + hash + "/" + bakedFilename; auto jt = _fileMappings.find(bakedPath); if (jt != _fileMappings.end()) { - if (jt->first == hash) { + if (jt->second == hash) { return NotBaked; } else { return Baked; From 3aee6db1e8c20c99fa3d9b858d720fd1bffad359 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 29 Aug 2017 16:43:09 -0700 Subject: [PATCH 050/202] Log baker errors --- assignment-client/src/assets/AssetServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index f7a8ecf453..568891adc3 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -84,7 +84,7 @@ void BakeAssetTask::run() { loop.exec(); if (baker->hasErrors()) { - qDebug() << "Failed to bake: " << _assetHash << _assetPath; + qDebug() << "Failed to bake: " << _assetHash << _assetPath << baker->getErrors(); } else { auto vectorOutputFiles = QVector::fromStdVector(baker->getOutputFiles()); qDebug() << "Finished baking: " << _assetHash << _assetPath << vectorOutputFiles; From da2bede6fe897bda05f5088fdaf9963aaa15c1c8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 29 Aug 2017 16:43:25 -0700 Subject: [PATCH 051/202] Hide .baked items from the Asset Browser --- assignment-client/src/assets/AssetServer.cpp | 8 +++++--- .../src/scripting/AssetMappingsScriptingInterface.cpp | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 568891adc3..c26600f0d9 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -521,9 +521,11 @@ void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedN replyPacket.writePrimitive(count); for (auto it = _fileMappings.cbegin(); it != _fileMappings.cend(); ++ it) { - replyPacket.writeString(it.key()); - replyPacket.write(QByteArray::fromHex(it.value().toString().toUtf8())); - replyPacket.writePrimitive(getAssetStatus(it.key(), it.value().toString())); + auto mapping = it.key(); + auto hash = it.value().toString(); + replyPacket.writeString(mapping); + replyPacket.write(QByteArray::fromHex(hash.toUtf8())); + replyPacket.writePrimitive(getAssetStatus(mapping, hash)); } } diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index f2929501ab..71ea2de4c5 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -202,6 +202,13 @@ void AssetMappingModel::refresh() { auto existingPaths = _pathToItemMap.keys(); for (auto& mapping : mappings) { auto& path = mapping.first; + + const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; + if (path.startsWith(HIDDEN_BAKED_CONTENT_FOLDER)) { + // Hide baked mappings + continue; + } + auto parts = path.split("/"); auto length = parts.length(); From 764e5d2e4c8720006cfca98e3cd4d2b0926e0619 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 31 Aug 2017 13:01:56 -0700 Subject: [PATCH 052/202] Move constant to central location --- assignment-client/src/assets/AssetServer.cpp | 2 -- interface/src/scripting/AssetMappingsScriptingInterface.cpp | 1 - libraries/networking/src/AssetUtils.h | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index c26600f0d9..1565415600 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -53,8 +53,6 @@ static QStringList BAKEABLE_TEXTURE_EXTENSIONS; static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; -static const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; - BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) : _assetHash(assetHash), _assetPath(assetPath), _filePath(filePath) { } diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 71ea2de4c5..d116c336d7 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -203,7 +203,6 @@ void AssetMappingModel::refresh() { for (auto& mapping : mappings) { auto& path = mapping.first; - const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; if (path.startsWith(HIDDEN_BAKED_CONTENT_FOLDER)) { // Hide baked mappings continue; diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index f8f0171a5d..48a2d2510d 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -33,6 +33,8 @@ const QString ASSET_FILE_PATH_REGEX_STRING = "^(\\/[^\\/\\0]+)+$"; const QString ASSET_PATH_REGEX_STRING = "^\\/([^\\/\\0]+(\\/)?)+$"; const QString ASSET_HASH_REGEX_STRING = QString("^[a-fA-F0-9]{%1}$").arg(SHA256_HASH_HEX_LENGTH); +const QString HIDDEN_BAKED_CONTENT_FOLDER = "/.baked/"; + enum AssetServerError : uint8_t { NoError = 0, AssetNotFound, From aad0be269380c161fa2872ce163ccb0ec5a96aa6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 29 Aug 2017 17:28:06 -0700 Subject: [PATCH 053/202] Set baking enabled mappings --- assignment-client/src/assets/AssetServer.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index f7a8ecf453..c5f52b4a82 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1224,9 +1224,11 @@ bool AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { auto bakedMapping = getBakeMapping(hash, bakedFilename); if (enabled) { - // TODO - } else { - // TODO + removeBakedPathsForDeletedAsset(hash); + setMapping(bakedMapping, hash); + } else if (_fileMappings.value(bakedMapping) == hash) { + deleteMappings({ bakedMapping }); + maybeBake(path, hash); } } } From a9cfc01df36a25e45f3f9c0f943179955c65d7ab Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 31 Aug 2017 09:49:46 -0700 Subject: [PATCH 054/202] Set Baking Enabled API --- assignment-client/src/assets/AssetServer.cpp | 17 +++++++----- interface/resources/qml/AssetServer.qml | 13 +++++++++- .../AssetMappingsScriptingInterface.cpp | 26 ++++++++++++++++--- .../AssetMappingsScriptingInterface.h | 3 ++- libraries/networking/src/AssetUtils.cpp | 1 - libraries/networking/src/AssetUtils.h | 2 +- 6 files changed, 48 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index c5f52b4a82..a044ea172b 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -124,7 +124,7 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& auto dotIndex = path.lastIndexOf("."); if (dotIndex == -1) { - return Unrelevant; + return Irrelevant; } auto extension = path.mid(dotIndex + 1); @@ -136,7 +136,7 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; } else { - return Unrelevant; + return Irrelevant; } auto bakedPath = HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + bakedFilename; @@ -1223,12 +1223,17 @@ bool AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { auto bakedMapping = getBakeMapping(hash, bakedFilename); - if (enabled) { + bool currentlyDisabled = (_fileMappings.value(bakedMapping) == hash); + + if (enabled && currentlyDisabled) { + QStringList bakedMappings{ bakedMapping }; + deleteMappings(bakedMappings); + maybeBake(path, hash); + qDebug() << "Enabled baking for" << path; + } else if (!enabled && !currentlyDisabled) { removeBakedPathsForDeletedAsset(hash); setMapping(bakedMapping, hash); - } else if (_fileMappings.value(bakedMapping) == hash) { - deleteMappings({ bakedMapping }); - maybeBake(path, hash); + qDebug() << "Disabled baking for" << path; } } } diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index a34de72270..294ad1a36c 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -606,7 +606,18 @@ ScrollingWindow { HifiControls.CheckBox { text: "Use baked (optimized) versions" colorScheme: root.colorScheme - enabled: selectedItems > 0 + enabled: selectedItems === 1 && assetProxyModel.data(treeView.selection.currentIndex, 0x105) !== "--" + checked: selectedItems === 1 && assetProxyModel.data(treeView.selection.currentIndex, 0x105) === "Baked"; + onClicked: { + var mappings = []; + for (var i in treeView.selection.selectedIndexes) { + var index = treeView.selection.selectedIndexes[i]; + var path = assetProxyModel.data(index, 0x100); + mappings.push(path); + } + print("Setting baking enabled:" + mappings + checked); + Assets.setBakingEnabled(mappings, checked); + } } } diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index f2929501ab..076a9f5ada 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -161,7 +161,23 @@ void AssetMappingsScriptingInterface::renameMapping(QString oldPath, QString new connect(request, &RenameMappingRequest::finished, this, [this, callback](RenameMappingRequest* request) mutable { if (callback.isCallable()) { - QJSValueList args { request->getErrorString() }; + QJSValueList args{ request->getErrorString() }; + callback.call(args); + } + + request->deleteLater(); + }); + + request->start(); +} + +void AssetMappingsScriptingInterface::setBakingEnabled(QStringList paths, bool enabled, QJSValue callback) { + auto assetClient = DependencyManager::get(); + auto request = assetClient->createSetBakingEnabledRequest(paths, enabled); + + connect(request, &SetBakingEnabledRequest::finished, this, [this, callback](SetBakingEnabledRequest* request) mutable { + if (callback.isCallable()) { + QJSValueList args{ request->getErrorString() }; callback.call(args); } @@ -214,18 +230,16 @@ void AssetMappingModel::refresh() { // start index at 1 to avoid empty string from leading slash for (int i = 1; i < length; ++i) { fullPath += (i == 1 ? "" : "/") + parts[i]; + bool isFolder = i < length - 1; auto it = _pathToItemMap.find(fullPath); if (it == _pathToItemMap.end()) { auto item = new QStandardItem(parts[i]); - bool isFolder = i < length - 1; - auto statusString = isFolder ? "--" : bakingStatusToString(mapping.second.status); item->setData(isFolder ? fullPath + "/" : fullPath, Qt::UserRole); item->setData(isFolder, Qt::UserRole + 1); item->setData(parts[i], Qt::UserRole + 2); item->setData("atp:" + fullPath, Qt::UserRole + 3); item->setData(fullPath, Qt::UserRole + 4); - item->setData(statusString, Qt::UserRole + 5); if (lastItem) { lastItem->appendRow(item); @@ -237,6 +251,10 @@ void AssetMappingModel::refresh() { } else { lastItem = it.value(); } + + // update status + auto statusString = isFolder ? "--" : bakingStatusToString(mapping.second.status); + lastItem->setData(statusString, Qt::UserRole + 5); } Q_ASSERT(fullPath == path); diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index 0615c5e84d..e4059c1ff7 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -66,10 +66,11 @@ public: Q_INVOKABLE void setMapping(QString path, QString hash, QJSValue callback = QJSValue()); Q_INVOKABLE void getMapping(QString path, QJSValue callback = QJSValue()); Q_INVOKABLE void uploadFile(QString path, QString mapping, QJSValue startedCallback = QJSValue(), QJSValue completedCallback = QJSValue(), bool dropEvent = false); - Q_INVOKABLE void deleteMappings(QStringList paths, QJSValue callback); + Q_INVOKABLE void deleteMappings(QStringList paths, QJSValue callback = QJSValue()); Q_INVOKABLE void deleteMapping(QString path, QJSValue callback) { deleteMappings(QStringList(path), callback = QJSValue()); } Q_INVOKABLE void getAllMappings(QJSValue callback = QJSValue()); Q_INVOKABLE void renameMapping(QString oldPath, QString newPath, QJSValue callback = QJSValue()); + Q_INVOKABLE void setBakingEnabled(QStringList paths, bool enabled, QJSValue callback = QJSValue()); protected: QSet _pendingRequests; diff --git a/libraries/networking/src/AssetUtils.cpp b/libraries/networking/src/AssetUtils.cpp index 4c26d2d633..3af0b1df47 100644 --- a/libraries/networking/src/AssetUtils.cpp +++ b/libraries/networking/src/AssetUtils.cpp @@ -97,7 +97,6 @@ QString bakingStatusToString(BakingStatus status) { return "Baked"; case Error: return "Error"; - case Unrelevant: default: return "--"; } diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index f8f0171a5d..be403e93b9 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -53,7 +53,7 @@ enum AssetMappingOperationType : uint8_t { }; enum BakingStatus { - Unrelevant, + Irrelevant, NotBaked, Pending, Baking, From d2c7235fed3d1346693cc38c2f4431488bc4fd93 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Aug 2017 14:30:00 -0700 Subject: [PATCH 055/202] Fix update to count width in GetAllMappings --- assignment-client/src/assets/AssetServer.cpp | 2 +- libraries/networking/src/MappingRequest.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 07239fbec4..a4211d4aa8 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -529,7 +529,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedNodePointer senderNode, NLPacketList& replyPacket) { replyPacket.writePrimitive(AssetServerError::NoError); - uint32_t count = _fileMappings.size(); + uint32_t count = (uint32_t)_fileMappings.size(); replyPacket.writePrimitive(count); diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index 258b7c6359..2d6684ec88 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -131,7 +131,7 @@ void GetAllMappingsRequest::doStart() { if (!_error) { - int numberOfMappings; + uint32_t numberOfMappings; message->readPrimitive(&numberOfMappings); for (auto i = 0; i < numberOfMappings; ++i) { auto path = message->readString(); From cdfae714b55bb1fffdcb12d529f2a4378c12a09f Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 31 Aug 2017 15:51:47 -0700 Subject: [PATCH 056/202] Remove baking tasks for hash once done --- assignment-client/src/assets/AssetServer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index a044ea172b..5b0343ca72 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1163,6 +1163,8 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina } else { qWarning() << "Could not complete bake for" << originalAssetHash; } + + _pendingBakes.remove(originalAssetHash); } bool AssetServer::createMetaFile(AssetHash originalAssetHash) { From bf53161a0b21dee4d0da0081967f669b63a1ba2c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Aug 2017 15:55:27 -0700 Subject: [PATCH 057/202] Fix comparison between signed and unsigned int in MappingRequest --- libraries/networking/src/MappingRequest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index 2d6684ec88..588d8ecc88 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -133,7 +133,7 @@ void GetAllMappingsRequest::doStart() { if (!_error) { uint32_t numberOfMappings; message->readPrimitive(&numberOfMappings); - for (auto i = 0; i < numberOfMappings; ++i) { + for (uint32_t i = 0; i < numberOfMappings; ++i) { auto path = message->readString(); auto hash = message->read(SHA256_HASH_LENGTH).toHex(); BakingStatus status; From 2fd2bb1e92c4327b0445065b3e523e4de8db3cef Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 31 Aug 2017 16:00:55 -0700 Subject: [PATCH 058/202] Auto reload after changing baking enabled --- interface/resources/qml/AssetServer.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 294ad1a36c..36ef3e69f8 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -616,7 +616,9 @@ ScrollingWindow { mappings.push(path); } print("Setting baking enabled:" + mappings + checked); - Assets.setBakingEnabled(mappings, checked); + Assets.setBakingEnabled(mappings, checked, function() { + reload(); + }); } } } From 9b857eb53bd2b136d9ea5f62916934540e1297b6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 31 Aug 2017 16:16:43 -0700 Subject: [PATCH 059/202] Ensure "enabled" binding stays in place --- interface/resources/qml/AssetServer.qml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 36ef3e69f8..f4b434ae5a 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -604,10 +604,16 @@ ScrollingWindow { } HifiControls.CheckBox { + function isChecked() { + var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105); + var bakingDisabled = (status === "Not Baked" || status === "--"); + return selectedItems === 1 && !bakingDisabled; + } + text: "Use baked (optimized) versions" colorScheme: root.colorScheme enabled: selectedItems === 1 && assetProxyModel.data(treeView.selection.currentIndex, 0x105) !== "--" - checked: selectedItems === 1 && assetProxyModel.data(treeView.selection.currentIndex, 0x105) === "Baked"; + checked: isChecked() onClicked: { var mappings = []; for (var i in treeView.selection.selectedIndexes) { @@ -619,6 +625,8 @@ ScrollingWindow { Assets.setBakingEnabled(mappings, checked, function() { reload(); }); + + checked = Qt.binding(isChecked); } } } From 7a68788c8c9b8646c3fe72f1efa84ac950b693ae Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 31 Aug 2017 16:22:46 -0700 Subject: [PATCH 060/202] Fix warning for non-const default in AssetServer --- assignment-client/src/assets/AssetServer.cpp | 2 +- assignment-client/src/assets/AssetServer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 5b9cec9346..6653842032 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1255,7 +1255,7 @@ std::pair AssetServer::readMetaFile(AssetHash hash) { return { false, {} }; } -bool AssetServer::writeMetaFile(AssetHash originalAssetHash, AssetMeta& meta) { +bool AssetServer::writeMetaFile(AssetHash originalAssetHash, const AssetMeta& meta) { // construct the JSON that will be in the meta file QJsonObject metaFileObject; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 531c588762..44e16272b2 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -122,7 +122,7 @@ private: /// Create meta file to describe baked content for original asset std::pair readMetaFile(AssetHash hash); - bool writeMetaFile(AssetHash originalAssetHash, AssetMeta& meta = AssetMeta()); + bool writeMetaFile(AssetHash originalAssetHash, const AssetMeta& meta = AssetMeta()); /// Remove baked paths when the original asset is deleteds void removeBakedPathsForDeletedAsset(AssetHash originalAssetHash); From 5ee33085a571661a8757bfdfc73275a035be97a5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 1 Sep 2017 10:37:28 -0700 Subject: [PATCH 061/202] Add handling of failedLastBake error in getAssetStatus --- assignment-client/src/assets/AssetServer.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 6653842032..903322a8ea 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -145,6 +145,14 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& auto jt = _fileMappings.find(bakedPath); if (jt != _fileMappings.end()) { if (jt->second == hash) { + bool loaded; + AssetMeta meta; + + std::tie(loaded, meta) = readMetaFile(hash); + if (loaded && meta.failedLastBake) { + return Error; + } + return NotBaked; } else { return Baked; From c67fcc13844c9b28e8eb2b304e87aa5f3033e2ae Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 1 Sep 2017 10:41:31 -0700 Subject: [PATCH 062/202] Add RedirectFail to ResourceRequest::getResultString() --- libraries/networking/src/ResourceRequest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/ResourceRequest.cpp b/libraries/networking/src/ResourceRequest.cpp index aeeab2232a..f028535e2f 100644 --- a/libraries/networking/src/ResourceRequest.cpp +++ b/libraries/networking/src/ResourceRequest.cpp @@ -36,6 +36,7 @@ QString ResourceRequest::getResultString() const { case AccessDenied: return "Access Denied"; case InvalidURL: return "Invalid URL"; case NotFound: return "Not Found"; + case RedirectFail: return "Redirect Fail"; default: return "Unspecified Error"; } } From eb2e87a7982897f1612257e5e111bf9f49e3fe0e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 1 Sep 2017 13:02:25 -0700 Subject: [PATCH 063/202] Change it++ to ++it --- assignment-client/src/assets/AssetServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 903322a8ea..489e94f3e7 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1056,7 +1056,7 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { _fileMappings[newKey] = it->second; } - it++; + ++it; } if (writeMappingsToFile()) { From 28402be37c7345cda5a2f3b3f1f4f9b3834fa02d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 31 Aug 2017 17:39:36 -0700 Subject: [PATCH 064/202] Fix status glyphs --- interface/resources/qml/AssetServer.qml | 126 +++++++++++++++++- interface/resources/qml/controls-uit/Tree.qml | 105 +-------------- 2 files changed, 131 insertions(+), 100 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index f4b434ae5a..5e564f8626 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -10,6 +10,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 @@ -527,7 +528,6 @@ ScrollingWindow { headerVisible: true sortIndicatorVisible: true - canEdit: true colorScheme: root.colorScheme modifyEl: renameEl @@ -542,8 +542,130 @@ ScrollingWindow { id: bakedColumn title: "Use Baked?" role: "baked" - width: 140 + width: 100 } + + itemDelegate: Loader { + id: itemDelegateLoader + + anchors { + left: parent ? parent.left : undefined + leftMargin: (styleData.column === 0 ? (2 + styleData.depth) : 1) * hifi.dimensions.tablePadding + right: parent ? parent.right : undefined + rightMargin: hifi.dimensions.tablePadding + verticalCenter: parent ? parent.verticalCenter : undefined + } + + function convertToGlyph(text) { + switch (text) { + case "Not Baked": + return hifi.glyphs.circleSlash; + case "Baked": + return hifi.glyphs.placemark; + case "Error": + return hifi.glyphs.alert; + default: + return ""; + } + } + + function getComponent() { + if ((styleData.column === 0) && styleData.selected) { + return textFieldComponent; + } else if (convertToGlyph(styleData.value) != "") { + return glyphComponent; + } else { + return labelComponent; + } + + } + sourceComponent: getComponent() + + Component { + id: labelComponent + FiraSansSemiBold { + text: styleData.value + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + + elide: Text.ElideRight + horizontalAlignment: styleData.column === 1 ? TextInput.AlignHCenter : TextInput.AlignLeft + } + } + Component { + id: glyphComponent + + HiFiGlyphs { + text: convertToGlyph(styleData.value) + size: hifi.dimensions.frameIconSize + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + + elide: Text.ElideRight + horizontalAlignment: TextInput.AlignHCenter + } + } + Component { + id: textFieldComponent + + TextField { + id: textField + readOnly: !activeFocus + + text: styleData.value + + FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + font.family: firaSansSemiBold.name + font.pixelSize: hifi.fontSizes.textFieldInput + height: hifi.dimensions.tableRowHeight + + style: TextFieldStyle { + textColor: readOnly + ? hifi.colors.black + : (treeView.isLightColorScheme ? hifi.colors.black : hifi.colors.white) + background: Rectangle { + visible: !readOnly + color: treeView.isLightColorScheme ? hifi.colors.white : hifi.colors.black + border.color: hifi.colors.primaryHighlight + border.width: 1 + } + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + padding.left: readOnly ? 0 : hifi.dimensions.textPadding + padding.right: readOnly ? 0 : hifi.dimensions.textPadding + } + + validator: RegExpValidator { + regExp: /[^/]+/ + } + + Keys.onPressed: { + if (event.key == Qt.Key_Escape) { + text = styleData.value; + unfocusHelper.forceActiveFocus(); + event.accepted = true; + } + } + onAccepted: { + if (acceptableInput && styleData.selected) { + if (!modifyEl(selection.currentIndex, text)) { + text = styleData.value; + } + unfocusHelper.forceActiveFocus(); + } + } + + onReadOnlyChanged: { + // Have to explicily set keyboardRaised because automatic setting fails because readOnly is true at the time. + keyboardRaised = activeFocus; + } + } + } + } + MouseArea { propagateComposedEvents: true diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index ce7edfcaab..d94a8cdceb 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -19,7 +19,6 @@ TreeView { id: treeView property var treeModel: ListModel { } - property var canEdit: false property bool centerHeaderText: false property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light @@ -198,9 +197,7 @@ TreeView { : (styleData.alternate ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd) } - itemDelegate: Loader { - id: itemDelegateLoader - + itemDelegate: FiraSansSemiBold { anchors { left: parent ? parent.left : undefined leftMargin: (2 + styleData.depth) * hifi.dimensions.tablePadding @@ -209,101 +206,13 @@ TreeView { verticalCenter: parent ? parent.verticalCenter : undefined } - function getComponent() { - if (treeView.canEdit && styleData.selected) { - return textFieldComponent; - } else { - if (styleData.value.startsWith("HifiGlyphs#")) { - return glyphComponent; - } else { - return labelComponent; - } - } - - } - sourceComponent: getComponent() - - Component { - id: labelComponent - FiraSansSemiBold { - - text: styleData.value - size: hifi.fontSizes.tableText - color: colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + text: styleData.value + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - elide: Text.ElideRight - } - } - Component { - id: glyphComponent - HiFiGlyphs { - text: styleData.value.replace("HifiGlyphs#", "") - size: hifi.fontSizes.tableText - color: colorScheme == hifi.colorSchemes.light - ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) - : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) - - elide: Text.ElideRight - } - } - Component { - id: textFieldComponent - - TextField { - id: textField - readOnly: !activeFocus - - text: styleData.value - - FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } - font.family: firaSansSemiBold.name - font.pixelSize: hifi.fontSizes.textFieldInput - height: hifi.dimensions.tableRowHeight - - style: TextFieldStyle { - textColor: readOnly - ? hifi.colors.black - : (treeView.isLightColorScheme ? hifi.colors.black : hifi.colors.white) - background: Rectangle { - visible: !readOnly - color: treeView.isLightColorScheme ? hifi.colors.white : hifi.colors.black - border.color: hifi.colors.primaryHighlight - border.width: 1 - } - selectedTextColor: hifi.colors.black - selectionColor: hifi.colors.primaryHighlight - padding.left: readOnly ? 0 : hifi.dimensions.textPadding - padding.right: readOnly ? 0 : hifi.dimensions.textPadding - } - - validator: RegExpValidator { - regExp: /[^/]+/ - } - - Keys.onPressed: { - if (event.key == Qt.Key_Escape) { - text = styleData.value; - unfocusHelper.forceActiveFocus(); - event.accepted = true; - } - } - onAccepted: { - if (acceptableInput && styleData.selected) { - if (!modifyEl(selection.currentIndex, text)) { - text = styleData.value; - } - unfocusHelper.forceActiveFocus(); - } - } - - onReadOnlyChanged: { - // Have to explicily set keyboardRaised because automatic setting fails because readOnly is true at the time. - keyboardRaised = activeFocus; - } - } - } + elide: Text.ElideRight } Item { From e17f05a33764af13fae631b13f3bc2e56ccc3e12 Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 6 Sep 2017 14:52:08 +0200 Subject: [PATCH 065/202] Correct UI --- interface/resources/qml/hifi/audio/Audio.qml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 3a9077cbb7..dd9fcdb533 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -85,7 +85,7 @@ Rectangle { } ColumnLayout { - x: margins.paddings; // padding does not work + x: margins.paddings; spacing: 16; width: parent.width; @@ -94,7 +94,7 @@ Rectangle { AudioControls.CheckBox { id: muteMic text: qsTr("Mute microphone"); - spacing: 8 + spacing: margins.sizeCheckBox - boxSize isRedCheck: true; checked: Audio.muted; onClicked: { @@ -105,7 +105,7 @@ Rectangle { } RowLayout { - spacing: 16; + spacing: muteMic.spacing*2; //make it visually distinguish AudioControls.CheckBox { spacing: muteMic.spacing text: qsTr("Enable noise reduction"); @@ -167,11 +167,14 @@ Rectangle { model: Audio.devices.input; delegate: Item { width: rightMostInputLevelPos - height: margins.sizeCheckBox + height: margins.sizeCheckBox > checkBoxInput.implicitHeight ? + margins.sizeCheckBox : checkBoxInput.implicitHeight AudioControls.CheckBox { + id: checkBoxInput anchors.left: parent.left spacing: margins.sizeCheckBox - boxSize + anchors.verticalCenter: parent.verticalCenter width: parent.width - inputLevel.width clip: true checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; @@ -233,9 +236,11 @@ Rectangle { model: Audio.devices.output; delegate: Item { width: rightMostInputLevelPos - height: margins.sizeCheckBox + height: margins.sizeCheckBox > checkBoxOutput.implicitHeight ? + margins.sizeCheckBox : checkBoxOutput.implicitHeight AudioControls.CheckBox { + id: checkBoxOutput width: parent.width spacing: margins.sizeCheckBox - boxSize boxSize: margins.sizeCheckBox / 2 From 26b74db8641da504eca295c5b73d3dec2ea9abff Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Aug 2017 11:31:58 -0700 Subject: [PATCH 066/202] enable compression in image library from Asset Server --- assignment-client/src/assets/AssetServer.cpp | 6 ++++++ tools/oven/src/Oven.cpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 43689a5b08..cacbb25e5b 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -266,6 +266,12 @@ AssetServer::AssetServer(ReceivedMessage& message) : _transferTaskPool(this), _bakingTaskPool(this) { + // enable compression in image library + image::setColorTexturesCompressionEnabled(true); + image::setGrayscaleTexturesCompressionEnabled(true); + image::setNormalTexturesCompressionEnabled(true); + image::setCubeTexturesCompressionEnabled(true); + BAKEABLE_TEXTURE_EXTENSIONS = TextureBaker::getSupportedFormats(); qDebug() << "Supported baking texture formats:" << BAKEABLE_MODEL_EXTENSIONS; diff --git a/tools/oven/src/Oven.cpp b/tools/oven/src/Oven.cpp index d0b8c3cd65..c9d3b4e5f0 100644 --- a/tools/oven/src/Oven.cpp +++ b/tools/oven/src/Oven.cpp @@ -44,7 +44,7 @@ Oven::Oven(int argc, char* argv[]) : parser.addHelpOption(); parser.process(*this); - // enable compression in image library, except for cube maps + // enable compression in image library image::setColorTexturesCompressionEnabled(true); image::setGrayscaleTexturesCompressionEnabled(true); image::setNormalTexturesCompressionEnabled(true); From e25b4700d99b8e8093af370d17aa6c5187573e47 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 31 Aug 2017 11:48:05 -0700 Subject: [PATCH 067/202] re-set defaults in texture baking when AssetServer done --- assignment-client/src/assets/AssetServer.cpp | 14 ++++++++++++++ assignment-client/src/assets/AssetServer.h | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index cacbb25e5b..b2ebdaccc9 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -266,6 +266,12 @@ AssetServer::AssetServer(ReceivedMessage& message) : _transferTaskPool(this), _bakingTaskPool(this) { + // store the current state of image compression so we can reset it when this assignment is complete + _wasColorTextureCompressionEnabled = image::isColorTexturesCompressionEnabled(); + _wasGrayscaleTextureCompressionEnabled = image::isGrayscaleTexturesCompressionEnabled(); + _wasNormalTextureCompressionEnabled = image::isNormalTexturesCompressionEnabled(); + _wasCubeTextureCompressionEnabled = image::isCubeTexturesCompressionEnabled(); + // enable compression in image library image::setColorTexturesCompressionEnabled(true); image::setGrayscaleTexturesCompressionEnabled(true); @@ -302,6 +308,14 @@ AssetServer::AssetServer(ReceivedMessage& message) : #endif } +void AssetServer::aboutToFinish() { + // re-set defaults in image library + image::setColorTexturesCompressionEnabled(_wasCubeTextureCompressionEnabled); + image::setGrayscaleTexturesCompressionEnabled(_wasGrayscaleTextureCompressionEnabled); + image::setNormalTexturesCompressionEnabled(_wasNormalTextureCompressionEnabled); + image::setCubeTexturesCompressionEnabled(_wasCubeTextureCompressionEnabled); +} + void AssetServer::run() { qCDebug(asset_server) << "Waiting for connection to domain to request settings from domain-server."; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 44e16272b2..907726e1ab 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -64,6 +64,8 @@ class AssetServer : public ThreadedAssignment { public: AssetServer(ReceivedMessage& message); + void aboutToFinish() override; + public slots: void run() override; @@ -137,6 +139,11 @@ private: QHash> _pendingBakes; QThreadPool _bakingTaskPool; + + bool _wasColorTextureCompressionEnabled { false }; + bool _wasGrayscaleTextureCompressionEnabled { false }; + bool _wasNormalTextureCompressionEnabled { false }; + bool _wasCubeTextureCompressionEnabled { false }; }; #endif From 7859b3b11f58ec72737e00037941cba1bdd8423b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 1 Sep 2017 15:34:42 -0700 Subject: [PATCH 068/202] remove image compression choices from Interface settings --- interface/src/ui/PreferencesDialog.cpp | 24 ----------------- libraries/image/src/image/Image.cpp | 36 ++++++++++---------------- 2 files changed, 13 insertions(+), 47 deletions(-) diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 1c3df3210c..287abd9033 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -333,30 +333,6 @@ void setupPreferences() { preferences->addPreference(preference); } } - { - auto getter = []()->bool { return image::isColorTexturesCompressionEnabled(); }; - auto setter = [](bool value) { return image::setColorTexturesCompressionEnabled(value); }; - auto preference = new CheckPreference(RENDER, "Compress Color Textures", getter, setter); - preferences->addPreference(preference); - } - { - auto getter = []()->bool { return image::isNormalTexturesCompressionEnabled(); }; - auto setter = [](bool value) { return image::setNormalTexturesCompressionEnabled(value); }; - auto preference = new CheckPreference(RENDER, "Compress Normal Textures", getter, setter); - preferences->addPreference(preference); - } - { - auto getter = []()->bool { return image::isGrayscaleTexturesCompressionEnabled(); }; - auto setter = [](bool value) { return image::setGrayscaleTexturesCompressionEnabled(value); }; - auto preference = new CheckPreference(RENDER, "Compress Grayscale Textures", getter, setter); - preferences->addPreference(preference); - } - { - auto getter = []()->bool { return image::isCubeTexturesCompressionEnabled(); }; - auto setter = [](bool value) { return image::setCubeTexturesCompressionEnabled(value); }; - auto preference = new CheckPreference(RENDER, "Compress Cube Textures", getter, setter); - preferences->addPreference(preference); - } } { static const QString RENDER("Networking"); diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index c6baee56c9..d3f40ead05 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include "ImageLogging.h" @@ -30,18 +29,17 @@ using namespace gpu; #define CPU_MIPMAPS 1 -static std::mutex settingsMutex; -static Setting::Handle compressColorTextures("hifi.graphics.compressColorTextures", false); -static Setting::Handle compressNormalTextures("hifi.graphics.compressNormalTextures", false); -static Setting::Handle compressGrayscaleTextures("hifi.graphics.compressGrayscaleTextures", false); -static Setting::Handle compressCubeTextures("hifi.graphics.compressCubeTextures", false); - static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 MAX_TEXTURE_SIZE(4096); bool DEV_DECIMATE_TEXTURES = false; std::atomic DECIMATED_TEXTURE_COUNT{ 0 }; std::atomic RECTIFIED_TEXTURE_COUNT{ 0 }; +static std::atomic compressColorTextures { false }; +static std::atomic compressNormalTextures { false }; +static std::atomic compressGrayscaleTextures { false }; +static std::atomic compressCubeTextures { false }; + bool needsSparseRectification(const glm::uvec2& size) { // Don't attempt to rectify small textures (textures less than the sparse page size in any dimension) if (glm::any(glm::lessThan(size, SPARSE_PAGE_SIZE))) { @@ -150,8 +148,7 @@ gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(co bool isColorTexturesCompressionEnabled() { #if CPU_MIPMAPS - std::lock_guard guard(settingsMutex); - return compressColorTextures.get(); + return compressColorTextures; #else return false; #endif @@ -159,8 +156,7 @@ bool isColorTexturesCompressionEnabled() { bool isNormalTexturesCompressionEnabled() { #if CPU_MIPMAPS - std::lock_guard guard(settingsMutex); - return compressNormalTextures.get(); + return compressNormalTextures; #else return false; #endif @@ -168,8 +164,7 @@ bool isNormalTexturesCompressionEnabled() { bool isGrayscaleTexturesCompressionEnabled() { #if CPU_MIPMAPS - std::lock_guard guard(settingsMutex); - return compressGrayscaleTextures.get(); + return compressGrayscaleTextures; #else return false; #endif @@ -177,31 +172,26 @@ bool isGrayscaleTexturesCompressionEnabled() { bool isCubeTexturesCompressionEnabled() { #if CPU_MIPMAPS - std::lock_guard guard(settingsMutex); - return compressCubeTextures.get(); + return compressCubeTextures; #else return false; #endif } void setColorTexturesCompressionEnabled(bool enabled) { - std::lock_guard guard(settingsMutex); - compressColorTextures.set(enabled); + compressColorTextures = enabled; } void setNormalTexturesCompressionEnabled(bool enabled) { - std::lock_guard guard(settingsMutex); - compressNormalTextures.set(enabled); + compressNormalTextures = enabled; } void setGrayscaleTexturesCompressionEnabled(bool enabled) { - std::lock_guard guard(settingsMutex); - compressGrayscaleTextures.set(enabled); + compressGrayscaleTextures = enabled; } void setCubeTexturesCompressionEnabled(bool enabled) { - std::lock_guard guard(settingsMutex); - compressCubeTextures.set(enabled); + compressCubeTextures = enabled; } From 1508edb459fdea1fa4ad8375559a2654d0df091d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 1 Sep 2017 15:41:20 -0700 Subject: [PATCH 069/202] use store/load to be clearer about atomics --- libraries/image/src/image/Image.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index d3f40ead05..30299663de 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -148,7 +148,7 @@ gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(co bool isColorTexturesCompressionEnabled() { #if CPU_MIPMAPS - return compressColorTextures; + return compressColorTextures.load(); #else return false; #endif @@ -156,7 +156,7 @@ bool isColorTexturesCompressionEnabled() { bool isNormalTexturesCompressionEnabled() { #if CPU_MIPMAPS - return compressNormalTextures; + return compressNormalTextures.load(); #else return false; #endif @@ -164,7 +164,7 @@ bool isNormalTexturesCompressionEnabled() { bool isGrayscaleTexturesCompressionEnabled() { #if CPU_MIPMAPS - return compressGrayscaleTextures; + return compressGrayscaleTextures.load(); #else return false; #endif @@ -172,26 +172,26 @@ bool isGrayscaleTexturesCompressionEnabled() { bool isCubeTexturesCompressionEnabled() { #if CPU_MIPMAPS - return compressCubeTextures; + return compressCubeTextures.load(); #else return false; #endif } void setColorTexturesCompressionEnabled(bool enabled) { - compressColorTextures = enabled; + compressColorTextures.store(enabled); } void setNormalTexturesCompressionEnabled(bool enabled) { - compressNormalTextures = enabled; + compressNormalTextures.store(enabled); } void setGrayscaleTexturesCompressionEnabled(bool enabled) { - compressGrayscaleTextures = enabled; + compressGrayscaleTextures.store(enabled); } void setCubeTexturesCompressionEnabled(bool enabled) { - compressCubeTextures = enabled; + compressCubeTextures.store(enabled); } From e4de869db6b6df2840b3fa5bb3eb928767347105 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 6 Sep 2017 13:56:50 -0700 Subject: [PATCH 070/202] fix iterator find from merge --- assignment-client/src/assets/AssetServer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index b2ebdaccc9..9e65ea3d96 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1342,7 +1342,8 @@ bool AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { auto bakedMapping = getBakeMapping(hash, bakedFilename); - bool currentlyDisabled = (_fileMappings.value(bakedMapping) == hash); + auto it = _fileMappings.find(bakedMapping); + bool currentlyDisabled = (it != _fileMappings.end() && it->second == hash); if (enabled && currentlyDisabled) { QStringList bakedMappings{ bakedMapping }; From bd36ba19b32775b3e4079091452b1bd8bea14321 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Sep 2017 12:02:07 -0700 Subject: [PATCH 071/202] fix dereference of end iterator --- assignment-client/src/assets/AssetServer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 9e65ea3d96..cee4c77170 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1106,7 +1106,7 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { _fileMappings.erase(it); // in case we're overwriting, keep the current destination mapping for potential rollback - auto oldDestinationMapping = _fileMappings.find(newPath)->second; + auto oldDestinationIt = _fileMappings.find(newPath); if (!oldSourceMapping.isEmpty()) { _fileMappings[newPath] = oldSourceMapping; @@ -1120,9 +1120,9 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { // we couldn't persist the renamed mapping, rollback and return failure _fileMappings[oldPath] = oldSourceMapping; - if (!oldDestinationMapping.isNull()) { + if (oldDestinationIt != _fileMappings.end()) { // put back the overwritten mapping for the destination path - _fileMappings[newPath] = oldDestinationMapping; + _fileMappings[newPath] = oldDestinationIt->second; } else { // clear the new mapping _fileMappings.erase(_fileMappings.find(newPath)); From aa1aad0a0995fbe7db54209aa7a347e5bf228033 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 10:36:19 -0700 Subject: [PATCH 072/202] Add FBXWriter for serializing FBXNode --- libraries/fbx/src/FBX.cpp | 10 + libraries/fbx/src/FBX.h | 333 +++++++++++++++++++++++++++ libraries/fbx/src/FBXReader.cpp | 11 +- libraries/fbx/src/FBXReader.h | 303 +----------------------- libraries/fbx/src/FBXReader_Node.cpp | 20 +- libraries/fbx/src/FBXWriter.cpp | 253 ++++++++++++++++++++ libraries/fbx/src/FBXWriter.h | 28 +++ 7 files changed, 641 insertions(+), 317 deletions(-) create mode 100644 libraries/fbx/src/FBX.cpp create mode 100644 libraries/fbx/src/FBX.h create mode 100644 libraries/fbx/src/FBXWriter.cpp create mode 100644 libraries/fbx/src/FBXWriter.h diff --git a/libraries/fbx/src/FBX.cpp b/libraries/fbx/src/FBX.cpp new file mode 100644 index 0000000000..3f18b3e678 --- /dev/null +++ b/libraries/fbx/src/FBX.cpp @@ -0,0 +1,10 @@ +// +// FBX.cpp +// libraries/fbx/src +// +// Created by Ryan Huffman on 9/5/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 +// diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h new file mode 100644 index 0000000000..9e1982f9e0 --- /dev/null +++ b/libraries/fbx/src/FBX.h @@ -0,0 +1,333 @@ +// +// FBX.h +// libraries/fbx/src +// +// Created by Ryan Huffman on 9/5/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 +// + +#ifndef hifi_FBX_h_ +#define hifi_FBX_h_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary "; +static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; +static const quint32 FBX_VERSION_2016 = 7500; + +class FBXNode; +using FBXNodeList = QList; + + +/// A node within an FBX document. +class FBXNode { +public: + QByteArray name; + QVariantList properties; + FBXNodeList children; +}; + + +/// A single blendshape extracted from an FBX document. +class FBXBlendshape { +public: + QVector indices; + QVector vertices; + QVector normals; +}; + +struct FBXJointShapeInfo { + // same units and frame as FBXJoint.translation + glm::vec3 avgPoint; + std::vector dots; + std::vector points; + std::vector debugLines; +}; + +/// A single joint (transformation node) extracted from an FBX document. +class FBXJoint { +public: + + FBXJointShapeInfo shapeInfo; + QVector freeLineage; + bool isFree; + int parentIndex; + float distanceToParent; + + // http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html + + glm::vec3 translation; // T + glm::mat4 preTransform; // Roff * Rp + glm::quat preRotation; // Rpre + glm::quat rotation; // R + glm::quat postRotation; // Rpost + glm::mat4 postTransform; // Rp-1 * Soff * Sp * S * Sp-1 + + // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) + + glm::mat4 transform; + glm::vec3 rotationMin; // radians + glm::vec3 rotationMax; // radians + glm::quat inverseDefaultRotation; + glm::quat inverseBindRotation; + glm::mat4 bindTransform; + QString name; + bool isSkeletonJoint; + bool bindTransformFoundInCluster; + + // geometric offset is applied in local space but does NOT affect children. + bool hasGeometricOffset; + glm::vec3 geometricTranslation; + glm::quat geometricRotation; + glm::vec3 geometricScaling; +}; + + +/// A single binding to a joint in an FBX document. +class FBXCluster { +public: + + int jointIndex; + glm::mat4 inverseBindMatrix; +}; + +const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; + +/// A texture map in an FBX document. +class FBXTexture { +public: + QString name; + QByteArray filename; + QByteArray content; + + Transform transform; + int maxNumPixels { MAX_NUM_PIXELS_FOR_FBX_TEXTURE }; + int texcoordSet; + QString texcoordSetName; + + bool isBumpmap{ false }; + + bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); } +}; + +/// A single part of a mesh (with the same material). +class FBXMeshPart { +public: + + QVector quadIndices; // original indices from the FBX mesh + QVector quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles + QVector triangleIndices; // original indices from the FBX mesh + + QString materialID; +}; + +class FBXMaterial { +public: + FBXMaterial() {}; + FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, + float shininess, float opacity) : + diffuseColor(diffuseColor), + specularColor(specularColor), + emissiveColor(emissiveColor), + shininess(shininess), + opacity(opacity) {} + + void getTextureNames(QSet& textureList) const; + void setMaxNumPixelsPerTexture(int maxNumPixels); + + glm::vec3 diffuseColor{ 1.0f }; + float diffuseFactor{ 1.0f }; + glm::vec3 specularColor{ 0.02f }; + float specularFactor{ 1.0f }; + + glm::vec3 emissiveColor{ 0.0f }; + float emissiveFactor{ 0.0f }; + + float shininess{ 23.0f }; + float opacity{ 1.0f }; + + float metallic{ 0.0f }; + float roughness{ 1.0f }; + float emissiveIntensity{ 1.0f }; + float ambientFactor{ 1.0f }; + + QString materialID; + QString name; + QString shadingModel; + model::MaterialPointer _material; + + FBXTexture normalTexture; + FBXTexture albedoTexture; + FBXTexture opacityTexture; + FBXTexture glossTexture; + FBXTexture roughnessTexture; + FBXTexture specularTexture; + FBXTexture metallicTexture; + FBXTexture emissiveTexture; + FBXTexture occlusionTexture; + FBXTexture scatteringTexture; + FBXTexture lightmapTexture; + glm::vec2 lightmapParams{ 0.0f, 1.0f }; + + + bool isPBSMaterial{ false }; + // THe use XXXMap are not really used to drive which map are going or not, debug only + bool useNormalMap{ false }; + bool useAlbedoMap{ false }; + bool useOpacityMap{ false }; + bool useRoughnessMap{ false }; + bool useSpecularMap{ false }; + bool useMetallicMap{ false }; + bool useEmissiveMap{ false }; + bool useOcclusionMap{ false }; + + bool needTangentSpace() const; +}; + +/// A single mesh (with optional blendshapes) extracted from an FBX document. +class FBXMesh { +public: + + QVector parts; + + QVector vertices; + QVector normals; + QVector tangents; + QVector colors; + QVector texCoords; + QVector texCoords1; + QVector clusterIndices; + QVector clusterWeights; + + QVector clusters; + + Extents meshExtents; + glm::mat4 modelTransform; + + QVector blendshapes; + + unsigned int meshIndex; // the order the meshes appeared in the object file + + model::MeshPointer _mesh; +}; + +class ExtractedMesh { +public: + FBXMesh mesh; + QMultiHash newIndices; + QVector > blendshapeIndexMaps; + QVector > partMaterialTextures; + QHash texcoordSetMap; +}; + +/// A single animation frame extracted from an FBX document. +class FBXAnimationFrame { +public: + QVector rotations; + QVector translations; +}; + +/// A light in an FBX document. +class FBXLight { +public: + QString name; + Transform transform; + float intensity; + float fogValue; + glm::vec3 color; + + FBXLight() : + name(), + transform(), + intensity(1.0f), + fogValue(0.0f), + color(1.0f) + {} +}; + +Q_DECLARE_METATYPE(FBXAnimationFrame) +Q_DECLARE_METATYPE(QVector) + +/// A set of meshes extracted from an FBX document. +class FBXGeometry { +public: + using Pointer = std::shared_ptr; + + QString originalURL; + QString author; + QString applicationName; ///< the name of the application that generated the model + + QVector joints; + QHash jointIndices; ///< 1-based, so as to more easily detect missing indices + bool hasSkeletonJoints; + + QVector meshes; + + QHash materials; + + glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file + + int leftEyeJointIndex = -1; + int rightEyeJointIndex = -1; + int neckJointIndex = -1; + int rootJointIndex = -1; + int leanJointIndex = -1; + int headJointIndex = -1; + int leftHandJointIndex = -1; + int rightHandJointIndex = -1; + int leftToeJointIndex = -1; + int rightToeJointIndex = -1; + + float leftEyeSize = 0.0f; // Maximum mesh extents dimension + float rightEyeSize = 0.0f; + + QVector humanIKJointIndices; + + glm::vec3 palmDirection; + + glm::vec3 neckPivot; + + Extents bindExtents; + Extents meshExtents; + + QVector animationFrames; + + int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } + QStringList getJointNames() const; + + bool hasBlendedMeshes() const; + + /// Returns the unscaled extents of the model's mesh + Extents getUnscaledMeshExtents() const; + + bool convexHullContains(const glm::vec3& point) const; + + QHash meshIndicesToModelNames; + + /// given a meshIndex this will return the name of the model that mesh belongs to if known + QString getModelNameOfMesh(int meshIndex) const; + + QList blendshapeChannelNames; +}; + +Q_DECLARE_METATYPE(FBXGeometry) +Q_DECLARE_METATYPE(FBXGeometry::Pointer) + +#endif // hifi_FBX_h_ diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 450aa296d8..ea4b00523e 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -168,7 +168,8 @@ QString getID(const QVariantList& properties, int index = 0) { return processID(properties.at(index).toString()); } -const char* HUMANIK_JOINTS[] = { +/// The names of the joints in the Maya HumanIK rig +static const std::array HUMANIK_JOINTS = { "RightHand", "RightForeArm", "RightArm", @@ -184,8 +185,7 @@ const char* HUMANIK_JOINTS[] = { "RightLeg", "LeftLeg", "RightFoot", - "LeftFoot", - "" + "LeftFoot" }; class FBXModel { @@ -512,11 +512,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QVector humanIKJointNames; - for (int i = 0;; i++) { + for (int i = 0; i < HUMANIK_JOINTS.size(); i++) { QByteArray jointName = HUMANIK_JOINTS[i]; - if (jointName.isEmpty()) { - break; - } humanIKJointNames.append(processID(getString(joints.value(jointName, jointName)))); } QVector humanIKJointIDs(humanIKJointNames.size()); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 170bbbf366..a600ac6bab 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -12,6 +12,8 @@ #ifndef hifi_FBXReader_h #define hifi_FBXReader_h +#include "FBX.h" + #include #include #include @@ -31,305 +33,6 @@ class QIODevice; class FBXNode; -typedef QList FBXNodeList; - -/// The names of the joints in the Maya HumanIK rig, terminated with an empty string. -extern const char* HUMANIK_JOINTS[]; - -/// A node within an FBX document. -class FBXNode { -public: - - QByteArray name; - QVariantList properties; - FBXNodeList children; -}; - -/// A single blendshape extracted from an FBX document. -class FBXBlendshape { -public: - - QVector indices; - QVector vertices; - QVector normals; -}; - -struct FBXJointShapeInfo { - // same units and frame as FBXJoint.translation - glm::vec3 avgPoint; - std::vector dots; - std::vector points; - std::vector debugLines; -}; - -/// A single joint (transformation node) extracted from an FBX document. -class FBXJoint { -public: - - FBXJointShapeInfo shapeInfo; - QVector freeLineage; - bool isFree; - int parentIndex; - float distanceToParent; - - // http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/SDKRef/a00209.html - - glm::vec3 translation; // T - glm::mat4 preTransform; // Roff * Rp - glm::quat preRotation; // Rpre - glm::quat rotation; // R - glm::quat postRotation; // Rpost - glm::mat4 postTransform; // Rp-1 * Soff * Sp * S * Sp-1 - - // World = ParentWorld * T * (Roff * Rp) * Rpre * R * Rpost * (Rp-1 * Soff * Sp * S * Sp-1) - - glm::mat4 transform; - glm::vec3 rotationMin; // radians - glm::vec3 rotationMax; // radians - glm::quat inverseDefaultRotation; - glm::quat inverseBindRotation; - glm::mat4 bindTransform; - QString name; - bool isSkeletonJoint; - bool bindTransformFoundInCluster; - - // geometric offset is applied in local space but does NOT affect children. - bool hasGeometricOffset; - glm::vec3 geometricTranslation; - glm::quat geometricRotation; - glm::vec3 geometricScaling; -}; - - -/// A single binding to a joint in an FBX document. -class FBXCluster { -public: - - int jointIndex; - glm::mat4 inverseBindMatrix; -}; - -const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; - -/// A texture map in an FBX document. -class FBXTexture { -public: - QString name; - QByteArray filename; - QByteArray content; - - Transform transform; - int maxNumPixels { MAX_NUM_PIXELS_FOR_FBX_TEXTURE }; - int texcoordSet; - QString texcoordSetName; - - bool isBumpmap{ false }; - - bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); } -}; - -/// A single part of a mesh (with the same material). -class FBXMeshPart { -public: - - QVector quadIndices; // original indices from the FBX mesh - QVector quadTrianglesIndices; // original indices from the FBX mesh of the quad converted as triangles - QVector triangleIndices; // original indices from the FBX mesh - - QString materialID; -}; - -class FBXMaterial { -public: - FBXMaterial() {}; - FBXMaterial(const glm::vec3& diffuseColor, const glm::vec3& specularColor, const glm::vec3& emissiveColor, - float shininess, float opacity) : - diffuseColor(diffuseColor), - specularColor(specularColor), - emissiveColor(emissiveColor), - shininess(shininess), - opacity(opacity) {} - - void getTextureNames(QSet& textureList) const; - void setMaxNumPixelsPerTexture(int maxNumPixels); - - glm::vec3 diffuseColor{ 1.0f }; - float diffuseFactor{ 1.0f }; - glm::vec3 specularColor{ 0.02f }; - float specularFactor{ 1.0f }; - - glm::vec3 emissiveColor{ 0.0f }; - float emissiveFactor{ 0.0f }; - - float shininess{ 23.0f }; - float opacity{ 1.0f }; - - float metallic{ 0.0f }; - float roughness{ 1.0f }; - float emissiveIntensity{ 1.0f }; - float ambientFactor{ 1.0f }; - - QString materialID; - QString name; - QString shadingModel; - model::MaterialPointer _material; - - FBXTexture normalTexture; - FBXTexture albedoTexture; - FBXTexture opacityTexture; - FBXTexture glossTexture; - FBXTexture roughnessTexture; - FBXTexture specularTexture; - FBXTexture metallicTexture; - FBXTexture emissiveTexture; - FBXTexture occlusionTexture; - FBXTexture scatteringTexture; - FBXTexture lightmapTexture; - glm::vec2 lightmapParams{ 0.0f, 1.0f }; - - - bool isPBSMaterial{ false }; - // THe use XXXMap are not really used to drive which map are going or not, debug only - bool useNormalMap{ false }; - bool useAlbedoMap{ false }; - bool useOpacityMap{ false }; - bool useRoughnessMap{ false }; - bool useSpecularMap{ false }; - bool useMetallicMap{ false }; - bool useEmissiveMap{ false }; - bool useOcclusionMap{ false }; - - bool needTangentSpace() const; -}; - -/// A single mesh (with optional blendshapes) extracted from an FBX document. -class FBXMesh { -public: - - QVector parts; - - QVector vertices; - QVector normals; - QVector tangents; - QVector colors; - QVector texCoords; - QVector texCoords1; - QVector clusterIndices; - QVector clusterWeights; - - QVector clusters; - - Extents meshExtents; - glm::mat4 modelTransform; - - QVector blendshapes; - - unsigned int meshIndex; // the order the meshes appeared in the object file - - model::MeshPointer _mesh; -}; - -class ExtractedMesh { -public: - FBXMesh mesh; - QMultiHash newIndices; - QVector > blendshapeIndexMaps; - QVector > partMaterialTextures; - QHash texcoordSetMap; -}; - -/// A single animation frame extracted from an FBX document. -class FBXAnimationFrame { -public: - QVector rotations; - QVector translations; -}; - -/// A light in an FBX document. -class FBXLight { -public: - QString name; - Transform transform; - float intensity; - float fogValue; - glm::vec3 color; - - FBXLight() : - name(), - transform(), - intensity(1.0f), - fogValue(0.0f), - color(1.0f) - {} -}; - -Q_DECLARE_METATYPE(FBXAnimationFrame) -Q_DECLARE_METATYPE(QVector) - -/// A set of meshes extracted from an FBX document. -class FBXGeometry { -public: - using Pointer = std::shared_ptr; - - QString originalURL; - QString author; - QString applicationName; ///< the name of the application that generated the model - - QVector joints; - QHash jointIndices; ///< 1-based, so as to more easily detect missing indices - bool hasSkeletonJoints; - - QVector meshes; - - QHash materials; - - glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file - - int leftEyeJointIndex = -1; - int rightEyeJointIndex = -1; - int neckJointIndex = -1; - int rootJointIndex = -1; - int leanJointIndex = -1; - int headJointIndex = -1; - int leftHandJointIndex = -1; - int rightHandJointIndex = -1; - int leftToeJointIndex = -1; - int rightToeJointIndex = -1; - - float leftEyeSize = 0.0f; // Maximum mesh extents dimension - float rightEyeSize = 0.0f; - - QVector humanIKJointIndices; - - glm::vec3 palmDirection; - - glm::vec3 neckPivot; - - Extents bindExtents; - Extents meshExtents; - - QVector animationFrames; - - int getJointIndex(const QString& name) const { return jointIndices.value(name) - 1; } - QStringList getJointNames() const; - - bool hasBlendedMeshes() const; - - /// Returns the unscaled extents of the model's mesh - Extents getUnscaledMeshExtents() const; - - bool convexHullContains(const glm::vec3& point) const; - - QHash meshIndicesToModelNames; - - /// given a meshIndex this will return the name of the model that mesh belongs to if known - QString getModelNameOfMesh(int meshIndex) const; - - QList blendshapeChannelNames; -}; - -Q_DECLARE_METATYPE(FBXGeometry) -Q_DECLARE_METATYPE(FBXGeometry::Pointer) /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing @@ -402,7 +105,7 @@ class FBXReader { public: FBXGeometry* _fbxGeometry; - FBXNode _fbxNode; + FBXNode _rootNode; static FBXNode parseFBX(QIODevice* device); FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index d987f885eb..111b4a295a 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -24,15 +24,18 @@ #include #include "ModelFormatLogging.h" -template int streamSize() { +template +int streamSize() { return sizeof(T); } -template int streamSize() { +template +int streamSize() { return 1; } -template QVariant readBinaryArray(QDataStream& in, int& position) { +template +QVariant readBinaryArray(QDataStream& in, int& position) { quint32 arrayLength; quint32 encoding; quint32 compressedLength; @@ -350,8 +353,7 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) { FBXNode FBXReader::parseFBX(QIODevice* device) { PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xff0000ff, device); // verify the prolog - const QByteArray BINARY_PROLOG = "Kaydara FBX Binary "; - if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) { + if (device->peek(FBX_BINARY_PROLOG.size()) != FBX_BINARY_PROLOG) { // parse as a text file FBXNode top; Tokenizer tokenizer(device); @@ -377,15 +379,13 @@ FBXNode FBXReader::parseFBX(QIODevice* device) { // Bytes 0 - 20: Kaydara FBX Binary \x00(file - magic, with 2 spaces at the end, then a NULL terminator). // Bytes 21 - 22: [0x1A, 0x00](unknown but all observed files show these bytes). // Bytes 23 - 26 : unsigned int, the version number. 7300 for version 7.3 for example. - const int HEADER_BEFORE_VERSION = 23; - const quint32 VERSION_FBX2016 = 7500; - in.skipRawData(HEADER_BEFORE_VERSION); - int position = HEADER_BEFORE_VERSION; + in.skipRawData(FBX_HEADER_BYTES_BEFORE_VERSION); + int position = FBX_HEADER_BYTES_BEFORE_VERSION; quint32 fileVersion; in >> fileVersion; position += sizeof(fileVersion); qCDebug(modelformat) << "fileVersion:" << fileVersion; - bool has64BitPositions = (fileVersion >= VERSION_FBX2016); + bool has64BitPositions = (fileVersion >= FBX_VERSION_2016); // parse the top-level node FBXNode top; diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp new file mode 100644 index 0000000000..a084d94a46 --- /dev/null +++ b/libraries/fbx/src/FBXWriter.cpp @@ -0,0 +1,253 @@ +// +// FBXWriter.cpp +// libraries/fbx/src +// +// Created by Ryan Huffman on 9/5/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 +// + +#include "FBXWriter.h" + +#include + + +QByteArray FBXWriter::encodeFBX(const FBXNode& root) { + QByteArray data; + QDataStream out(&data, QIODevice::WriteOnly); + out.setByteOrder(QDataStream::LittleEndian); + out.setVersion(QDataStream::Qt_4_5); + + out.writeRawData(FBX_BINARY_PROLOG, FBX_BINARY_PROLOG.size()); + auto bytes = QByteArray(FBX_HEADER_BYTES_BEFORE_VERSION - FBX_BINARY_PROLOG.size(), '\0'); + out.writeRawData(bytes, bytes.size()); + + out << FBX_VERSION_2016; + + for (auto& child : root.children) { + encodeNode(out, child); + } + encodeNode(out, FBXNode()); + + return data; +} + +void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { + qDebug() << "Encoding " << node.name; + + auto device = out.device(); + auto nodeStartPos = device->pos(); + + // endOffset (temporary, updated later) + out << (qint64)0; + + // Property count + out << (quint64)node.properties.size(); + + // Property list length (temporary, updated later) + out << (quint64)0; + + out << (quint8)node.name.size(); + out.writeRawData(node.name, node.name.size()); + + if (node.name == "Vertices") { + for (auto& prop : node.properties) { + qDebug() << "Properties: " << prop; + } + } + + auto nodePropertiesStartPos = device->pos(); + + for (const auto& prop : node.properties) { + encodeFBXProperty(out, prop); + } + + // Go back and write property list length + auto nodePropertiesEndPos = device->pos(); + device->seek(nodeStartPos + sizeof(qint64) + sizeof(quint64)); + out << (quint64)(nodePropertiesEndPos - nodePropertiesStartPos); + + device->seek(nodePropertiesEndPos); + + for (auto& child : node.children) { + encodeNode(out, child); + } + + if (node.children.length() > 0) { + encodeNode(out, FBXNode()); + } + + // Go back and write actual endOffset + auto nodeEndPos = device->pos(); + device->seek(nodeStartPos); + out << (qint64)(nodeEndPos); + + device->seek(nodeEndPos); +} + +void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { + auto type = prop.userType(); + switch (type) { + case QVariant::Type::Bool: + + out.device()->write("C", 1); + out << prop.toBool(); + break; + + case QMetaType::Int: + out.device()->write("I", 1); + out << prop.toInt(); + break; + + encodeNode(out, FBXNode()); + case QMetaType::Float: + out.device()->write("F", 1); + out << prop.toFloat(); + break; + + case QMetaType::Double: + out.device()->write("D", 1); + out << prop.toDouble(); + break; + + case QMetaType::LongLong: + out.device()->write("L", 1); + out << prop.toLongLong(); + break; + + case QMetaType::QString: + { + auto& bytes = prop.toString().toUtf8(); + out << 'S'; + out << bytes.length(); + out << bytes; + out << (int32_t)bytes.size(); + out.writeRawData(bytes, bytes.size()); + break; + } + + case QMetaType::QByteArray: + { + auto& bytes = prop.toByteArray(); + out.device()->write("S", 1); + out << (int32_t)bytes.size(); + out.writeRawData(bytes, bytes.size()); + break; + } + + // TODO Delete? Do we ever use QList instead of QVector? + case QVariant::Type::List: + { + auto& list = prop.toList(); + auto listType = prop.userType(); + + switch (listType) { + case QMetaType::Float: + out.device()->write("f", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toFloat(); + } + break; + + case QMetaType::Double: + out.device()->write("d", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toDouble(); + } + break; + + case QMetaType::LongLong: + out.device()->write("l", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toLongLong(); + } + break; + + case QMetaType::Int: + out.device()->write("i", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toInt(); + } + break; + + case QMetaType::Bool: + out.device()->write("b", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& innerProp : list) { + out << prop.toBool(); + } + break; + } + } + break; + + default: + { + if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("f", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("d", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("l", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("i", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else if (prop.canConvert>()) { + auto list = prop.value>(); + out.device()->write("b", 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } + } else { + qDebug() << "Unsupported property type in FBXWriter::encodeNode: " << type << prop; + } + } + + } +} diff --git a/libraries/fbx/src/FBXWriter.h b/libraries/fbx/src/FBXWriter.h new file mode 100644 index 0000000000..fa33983345 --- /dev/null +++ b/libraries/fbx/src/FBXWriter.h @@ -0,0 +1,28 @@ +// +// FBXWriter.h +// libraries/fbx/src +// +// Created by Ryan Huffman on 9/5/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 +// + +#ifndef hifi_FBXWriter_h +#define hifi_FBXWriter_h + +#include "FBX.h" + +#include +#include + +class FBXWriter { +public: + static QByteArray encodeFBX(const FBXNode& root); + + static void encodeNode(QDataStream& out, const FBXNode& node); + static void encodeFBXProperty(QDataStream& out, const QVariant& property); +}; + +#endif // hifi_FBXWriter_h From 7a55c867be3ee6b7a3c55455ff1533abf8daed3e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 11:49:37 -0700 Subject: [PATCH 073/202] Fix _fbxNode rename to _rootNode in FBXReader --- libraries/fbx/src/FBXReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index ea4b00523e..d212ec820f 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -468,7 +468,7 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) { } FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) { - const FBXNode& node = _fbxNode; + const FBXNode& node = _rootNode; QMap meshes; QHash modelIDsToNames; QHash meshIDsToMeshIndices; @@ -1839,7 +1839,7 @@ FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) { FBXReader reader; - reader._fbxNode = FBXReader::parseFBX(device); + reader._rootNode = FBXReader::parseFBX(device); reader._loadLightmaps = loadLightmaps; reader._lightmapLevel = lightmapLevel; From 49e09f483872c5ccca2d1137470a87099a29abfc Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 13:12:07 -0700 Subject: [PATCH 074/202] Remove FBXBaker and TextureBaker from oven --- tools/oven/src/FBXBaker.cpp | 568 -------------------------------- tools/oven/src/FBXBaker.h | 103 ------ tools/oven/src/TextureBaker.cpp | 131 -------- tools/oven/src/TextureBaker.h | 59 ---- 4 files changed, 861 deletions(-) delete mode 100644 tools/oven/src/FBXBaker.cpp delete mode 100644 tools/oven/src/FBXBaker.h delete mode 100644 tools/oven/src/TextureBaker.cpp delete mode 100644 tools/oven/src/TextureBaker.h diff --git a/tools/oven/src/FBXBaker.cpp b/tools/oven/src/FBXBaker.cpp deleted file mode 100644 index 8ece76b6c4..0000000000 --- a/tools/oven/src/FBXBaker.cpp +++ /dev/null @@ -1,568 +0,0 @@ -// -// FBXBaker.cpp -// tools/oven/src -// -// Created by Stephen Birarda on 3/30/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 -// - -#include // need this include so we don't get an error looking for std::isnan - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include "ModelBakingLoggingCategory.h" -#include "TextureBaker.h" - -#include "FBXBaker.h" - -std::once_flag onceFlag; -FBXSDKManagerUniquePointer FBXBaker::_sdkManager { nullptr }; - -FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, - const QString& bakedOutputDir, const QString& originalOutputDir) : - _fbxURL(fbxURL), - _bakedOutputDir(bakedOutputDir), - _originalOutputDir(originalOutputDir), - _textureThreadGetter(textureThreadGetter) -{ - std::call_once(onceFlag, [](){ - // create the static FBX SDK manager - _sdkManager = FBXSDKManagerUniquePointer(FbxManager::Create(), [](FbxManager* manager){ - manager->Destroy(); - }); - }); -} - -void FBXBaker::bake() { - auto tempDir = PathUtils::generateTemporaryDir(); - - if (tempDir.isEmpty()) { - handleError("Failed to create a temporary directory."); - return; - } - - _tempDir = tempDir; - - _originalFBXFilePath = _tempDir.filePath(_fbxURL.fileName()); - qDebug() << "Made temporary dir " << _tempDir; - qDebug() << "Origin file path: " << _originalFBXFilePath; - - // setup the output folder for the results of this bake - setupOutputFolder(); - - if (hasErrors()) { - return; - } - - connect(this, &FBXBaker::sourceCopyReadyToLoad, this, &FBXBaker::bakeSourceCopy); - - // make a local copy of the FBX file - loadSourceFBX(); -} - -void FBXBaker::bakeSourceCopy() { - // load the scene from the FBX file - importScene(); - - if (hasErrors()) { - return; - } - - // enumerate the textures found in the scene and start a bake for them - rewriteAndBakeSceneTextures(); - - if (hasErrors()) { - return; - } - - // export the FBX with re-written texture references - exportScene(); - - if (hasErrors()) { - return; - } - - // check if we're already done with textures (in case we had none to re-write) - checkIfTexturesFinished(); -} - -void FBXBaker::setupOutputFolder() { - // make sure there isn't already an output directory using the same name - int iteration = 0; - - if (QDir(_bakedOutputDir).exists()) { - qWarning() << "Output path" << _bakedOutputDir << "already exists. Continuing."; - //_bakedOutputDir = _baseOutputPath + "/" + _fbxName + "-" + QString::number(++iteration) + "/"; - } else { - qCDebug(model_baking) << "Creating FBX output folder" << _bakedOutputDir; - - // attempt to make the output folder - if (!QDir().mkpath(_bakedOutputDir)) { - handleError("Failed to create FBX output folder " + _bakedOutputDir); - return; - } - // attempt to make the output folder - if (!QDir().mkpath(_originalOutputDir)) { - handleError("Failed to create FBX output folder " + _bakedOutputDir); - return; - } - } -} - -void FBXBaker::loadSourceFBX() { - // check if the FBX is local or first needs to be downloaded - if (_fbxURL.isLocalFile()) { - // load up the local file - QFile localFBX { _fbxURL.toLocalFile() }; - - qDebug() << "Local file url: " << _fbxURL << _fbxURL.toString() << _fbxURL.toLocalFile() << ", copying to: " << _originalFBXFilePath; - - if (!localFBX.exists()) { - //QMessageBox::warning(this, "Could not find " + _fbxURL.toString(), ""); - handleError("Could not find " + _fbxURL.toString()); - return; - } - - // make a copy in the output folder - if (!_originalOutputDir.isEmpty()) { - qDebug() << "Copying to: " << _originalOutputDir << "/" << _fbxURL.fileName(); - localFBX.copy(_originalOutputDir + "/" + _fbxURL.fileName()); - } - - localFBX.copy(_originalFBXFilePath); - - // emit our signal to start the import of the FBX source copy - emit sourceCopyReadyToLoad(); - } else { - // remote file, kick off a download - auto& networkAccessManager = NetworkAccessManager::getInstance(); - - QNetworkRequest networkRequest; - - // setup the request to follow re-directs and always hit the network - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - - - networkRequest.setUrl(_fbxURL); - - qCDebug(model_baking) << "Downloading" << _fbxURL; - auto networkReply = networkAccessManager.get(networkRequest); - - connect(networkReply, &QNetworkReply::finished, this, &FBXBaker::handleFBXNetworkReply); - } -} - -void FBXBaker::handleFBXNetworkReply() { - auto requestReply = qobject_cast(sender()); - - if (requestReply->error() == QNetworkReply::NoError) { - qCDebug(model_baking) << "Downloaded" << _fbxURL; - - // grab the contents of the reply and make a copy in the output folder - QFile copyOfOriginal(_originalFBXFilePath); - - qDebug(model_baking) << "Writing copy of original FBX to" << _originalFBXFilePath << copyOfOriginal.fileName(); - - if (!copyOfOriginal.open(QIODevice::WriteOnly)) { - // add an error to the error list for this FBX stating that a duplicate of the original FBX could not be made - handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to open " + _originalFBXFilePath + ")"); - return; - } - if (copyOfOriginal.write(requestReply->readAll()) == -1) { - handleError("Could not create copy of " + _fbxURL.toString() + " (Failed to write)"); - return; - } - - // close that file now that we are done writing to it - copyOfOriginal.close(); - - if (!_originalOutputDir.isEmpty()) { - copyOfOriginal.copy(_originalOutputDir + "/" + _fbxURL.fileName()); - } - - // emit our signal to start the import of the FBX source copy - emit sourceCopyReadyToLoad(); - } else { - // add an error to our list stating that the FBX could not be downloaded - handleError("Failed to download " + _fbxURL.toString()); - } -} - -void FBXBaker::importScene() { - // create an FBX SDK importer - FbxImporter* importer = FbxImporter::Create(_sdkManager.get(), ""); - - qDebug() << "file path: " << _originalFBXFilePath.toLocal8Bit().data() << QDir(_originalFBXFilePath).exists(); - // import the copy of the original FBX file - bool importStatus = importer->Initialize(_originalFBXFilePath.toLocal8Bit().data()); - - if (!importStatus) { - // failed to initialize importer, print an error and return - handleError("Failed to import " + _fbxURL.toString() + " - " + importer->GetStatus().GetErrorString()); - return; - } else { - qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; - } - - // setup a new scene to hold the imported file - _scene = FbxScene::Create(_sdkManager.get(), "bakeScene"); - - // import the file to the created scene - importer->Import(_scene); - - // destroy the importer that is no longer needed - importer->Destroy(); -} - -QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { - auto fbxPath = fbxURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); - auto texturePath = textureURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment); - - if (texturePath.startsWith(fbxPath)) { - // texture path is a child of the FBX path, return the texture path without the fbx path - return texturePath.mid(fbxPath.length()); - } else { - // the texture path was not a child of the FBX path, return the empty string - return ""; - } -} - -QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { - // first make sure we have a unique base name for this texture - // in case another texture referenced by this model has the same base name - auto nameMatches = _textureNameMatchCount[textureFileInfo.baseName()]; - - QString bakedTextureFileName { textureFileInfo.completeBaseName() }; - - if (nameMatches > 0) { - // there are already nameMatches texture with this name - // append - and that number to our baked texture file name so that it is unique - bakedTextureFileName += "-" + QString::number(nameMatches); - } - - bakedTextureFileName += BAKED_TEXTURE_EXT; - - // increment the number of name matches - ++nameMatches; - - return bakedTextureFileName; -} - -QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* fileTexture) { - QUrl urlToTexture; - - if (textureFileInfo.exists() && textureFileInfo.isFile()) { - // set the texture URL to the local texture that we have confirmed exists - urlToTexture = QUrl::fromLocalFile(textureFileInfo.absoluteFilePath()); - } else { - // external texture that we'll need to download or find - - // first check if it the RelativePath to the texture in the FBX was relative - QString relativeFileName = fileTexture->GetRelativeFileName(); - auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); - - // this is a relative file path which will require different handling - // depending on the location of the original FBX - if (_fbxURL.isLocalFile() && apparentRelativePath.exists() && apparentRelativePath.isFile()) { - // the absolute path we ran into for the texture in the FBX exists on this machine - // so use that file - urlToTexture = QUrl::fromLocalFile(apparentRelativePath.absoluteFilePath()); - } else { - // we didn't find the texture on this machine at the absolute path - // so assume that it is right beside the FBX to match the behaviour of interface - urlToTexture = _fbxURL.resolved(apparentRelativePath.fileName()); - } - } - - return urlToTexture; -} - -image::TextureUsage::Type textureTypeForMaterialProperty(FbxProperty& property, FbxSurfaceMaterial* material) { - using namespace image::TextureUsage; - - // this is a property we know has a texture, we need to match it to a High Fidelity known texture type - // since that information is passed to the baking process - - // grab the hierarchical name for this property and lowercase it for case-insensitive compare - auto propertyName = QString(property.GetHierarchicalName()).toLower(); - - // figure out the type of the property based on what known value string it matches - if ((propertyName.contains("diffuse") && !propertyName.contains("tex_global_diffuse")) - || propertyName.contains("tex_color_map")) { - return ALBEDO_TEXTURE; - } else if (propertyName.contains("transparentcolor") || propertyName.contains("transparencyfactor")) { - return ALBEDO_TEXTURE; - } else if (propertyName.contains("bump")) { - return BUMP_TEXTURE; - } else if (propertyName.contains("normal")) { - return NORMAL_TEXTURE; - } else if ((propertyName.contains("specular") && !propertyName.contains("tex_global_specular")) - || propertyName.contains("reflection")) { - return SPECULAR_TEXTURE; - } else if (propertyName.contains("tex_metallic_map")) { - return METALLIC_TEXTURE; - } else if (propertyName.contains("shininess")) { - return GLOSS_TEXTURE; - } else if (propertyName.contains("tex_roughness_map")) { - return ROUGHNESS_TEXTURE; - } else if (propertyName.contains("emissive")) { - return EMISSIVE_TEXTURE; - } else if (propertyName.contains("ambientcolor")) { - return LIGHTMAP_TEXTURE; - } else if (propertyName.contains("ambientfactor")) { - // we need to check what the ambient factor is, since that tells Interface to process this texture - // either as an occlusion texture or a light map - auto lambertMaterial = FbxCast(material); - - if (lambertMaterial->AmbientFactor == 0) { - return LIGHTMAP_TEXTURE; - } else if (lambertMaterial->AmbientFactor > 0) { - return OCCLUSION_TEXTURE; - } else { - return UNUSED_TEXTURE; - } - - } else if (propertyName.contains("tex_ao_map")) { - return OCCLUSION_TEXTURE; - } - - return UNUSED_TEXTURE; -} - -void FBXBaker::rewriteAndBakeSceneTextures() { - - // enumerate the surface materials to find the textures used in the scene - int numMaterials = _scene->GetMaterialCount(); - for (int i = 0; i < numMaterials; i++) { - FbxSurfaceMaterial* material = _scene->GetMaterial(i); - - if (material) { - // enumerate the properties of this material to see what texture channels it might have - FbxProperty property = material->GetFirstProperty(); - - while (property.IsValid()) { - // first check if this property has connected textures, if not we don't need to bother with it here - if (property.GetSrcObjectCount() > 0) { - - // figure out the type of texture from the material property - auto textureType = textureTypeForMaterialProperty(property, material); - - if (textureType != image::TextureUsage::UNUSED_TEXTURE) { - int numTextures = property.GetSrcObjectCount(); - - for (int j = 0; j < numTextures; j++) { - FbxFileTexture* fileTexture = property.GetSrcObject(j); - - // use QFileInfo to easily split up the existing texture filename into its components - QString fbxTextureFileName { fileTexture->GetFileName() }; - QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; - - // make sure this texture points to something and isn't one we've already re-mapped - if (!textureFileInfo.filePath().isEmpty() - && textureFileInfo.suffix() != BAKED_TEXTURE_EXT.mid(1)) { - - // construct the new baked texture file name and file path - // ensuring that the baked texture will have a unique name - // even if there was another texture with the same name at a different path - auto bakedTextureFileName = createBakedTextureFileName(textureFileInfo); - QString bakedTextureFilePath { - _bakedOutputDir + "/" + bakedTextureFileName - }; - _outputFiles.push_back(bakedTextureFilePath); - - qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName() - << "to" << bakedTextureFilePath; - - // figure out the URL to this texture, embedded or external - auto urlToTexture = getTextureURL(textureFileInfo, fileTexture); - - // write the new filename into the FBX scene - fileTexture->SetFileName(bakedTextureFilePath.toUtf8().data()); - - // write the relative filename to be the baked texture file name since it will - // be right beside the FBX - fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData()); - - if (!_bakingTextures.contains(urlToTexture)) { - // bake this texture asynchronously - bakeTexture(urlToTexture, textureType, _bakedOutputDir); - } - } - } - } - } - - property = material->GetNextProperty(property); - } - } - } -} - -void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir) { - // start a bake for this texture and add it to our list to keep track of - QSharedPointer bakingTexture { - new TextureBaker(textureURL, textureType, outputDir), - &TextureBaker::deleteLater - }; - - // make sure we hear when the baking texture is done - connect(bakingTexture.data(), &Baker::finished, this, &FBXBaker::handleBakedTexture); - - // keep a shared pointer to the baking texture - _bakingTextures.insert(textureURL, bakingTexture); - - // start baking the texture on one of our available worker threads - bakingTexture->moveToThread(_textureThreadGetter()); - QMetaObject::invokeMethod(bakingTexture.data(), "bake"); -} - -void FBXBaker::handleBakedTexture() { - TextureBaker* bakedTexture = qobject_cast(sender()); - - // make sure we haven't already run into errors, and that this is a valid texture - if (bakedTexture) { - if (!hasErrors()) { - if (!bakedTexture->hasErrors()) { - if (!_originalOutputDir.isEmpty()) { - // we've been asked to make copies of the originals, so we need to make copies of this if it is a linked texture - - // use the path to the texture being baked to determine if this was an embedded or a linked texture - - // it is embeddded if the texure being baked was inside the original output folder - // since that is where the FBX SDK places the .fbm folder it generates when importing the FBX - - auto originalOutputFolder = QUrl::fromLocalFile(_originalOutputDir); - - if (!originalOutputFolder.isParentOf(bakedTexture->getTextureURL())) { - // for linked textures we want to save a copy of original texture beside the original FBX - - qCDebug(model_baking) << "Saving original texture for" << bakedTexture->getTextureURL(); - - // check if we have a relative path to use for the texture - auto relativeTexturePath = texturePathRelativeToFBX(_fbxURL, bakedTexture->getTextureURL()); - - QFile originalTextureFile { - _originalOutputDir + "/" + relativeTexturePath + bakedTexture->getTextureURL().fileName() - }; - - if (relativeTexturePath.length() > 0) { - // make the folders needed by the relative path - } - - if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) { - qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName() - << "for" << _fbxURL; - } else { - handleError("Could not save original external texture " + originalTextureFile.fileName() - + " for " + _fbxURL.toString()); - return; - } - } - } - - - // now that this texture has been baked and handled, we can remove that TextureBaker from our hash - _bakingTextures.remove(bakedTexture->getTextureURL()); - - checkIfTexturesFinished(); - } else { - // there was an error baking this texture - add it to our list of errors - _errorList.append(bakedTexture->getErrors()); - - // we don't emit finished yet so that the other textures can finish baking first - _pendingErrorEmission = true; - - // now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list - _bakingTextures.remove(bakedTexture->getTextureURL()); - - checkIfTexturesFinished(); - } - } else { - // we have errors to attend to, so we don't do extra processing for this texture - // but we do need to remove that TextureBaker from our list - // and then check if we're done with all textures - _bakingTextures.remove(bakedTexture->getTextureURL()); - - checkIfTexturesFinished(); - } - } -} - -void FBXBaker::exportScene() { - // setup the exporter - FbxExporter* exporter = FbxExporter::Create(_sdkManager.get(), ""); - - // save the relative path to this FBX inside our passed output folder - - auto fileName = _fbxURL.fileName(); - auto baseName = fileName.left(fileName.lastIndexOf('.')); - auto bakedFilename = baseName + BAKED_FBX_EXTENSION; - - _bakedFBXFilePath = _bakedOutputDir + "/" + bakedFilename; - - bool exportStatus = exporter->Initialize(_bakedFBXFilePath.toLocal8Bit().data()); - - if (!exportStatus) { - // failed to initialize exporter, print an error and return - handleError("Failed to export FBX file at " + _fbxURL.toString() + " to " + _bakedFBXFilePath - + "- error: " + exporter->GetStatus().GetErrorString()); - } - - _outputFiles.push_back(_bakedFBXFilePath); - - // export the scene - exporter->Export(_scene); - - qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << _bakedFBXFilePath; -} - - -void FBXBaker::removeEmbeddedMediaFolder() { - // now that the bake is complete, remove the embedded media folder produced by the FBX SDK when it imports an FBX - //auto embeddedMediaFolderName = _fbxURL.fileName().replace(".fbx", ".fbm"); - //QDir(_bakedOutputDir + ORIGINAL_OUTPUT_SUBFOLDER + embeddedMediaFolderName).removeRecursively(); -} - -void FBXBaker::checkIfTexturesFinished() { - // check if we're done everything we need to do for this FBX - // and emit our finished signal if we're done - - if (_bakingTextures.isEmpty()) { - // remove the embedded media folder that the FBX SDK produces when reading the original - removeEmbeddedMediaFolder(); - - if (hasErrors()) { - // if we're checking for completion but we have errors - // that means one or more of our texture baking operations failed - - if (_pendingErrorEmission) { - emit finished(); - } - - return; - } else { - qCDebug(model_baking) << "Finished baking" << _fbxURL; - - emit finished(); - } - } -} diff --git a/tools/oven/src/FBXBaker.h b/tools/oven/src/FBXBaker.h deleted file mode 100644 index faf56ed46b..0000000000 --- a/tools/oven/src/FBXBaker.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// FBXBaker.h -// tools/oven/src -// -// Created by Stephen Birarda on 3/30/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 -// - -#ifndef hifi_FBXBaker_h -#define hifi_FBXBaker_h - -#include -#include -#include -#include - -#include "Baker.h" -#include "TextureBaker.h" - -#include - -namespace fbxsdk { - class FbxManager; - class FbxProperty; - class FbxScene; - class FbxFileTexture; -} - -static const QString BAKED_FBX_EXTENSION = ".baked.fbx"; -using FBXSDKManagerUniquePointer = std::unique_ptr>; - -using TextureBakerThreadGetter = std::function; - -class FBXBaker : public Baker { - Q_OBJECT -public: - FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, - const QString& bakedOutputDir, const QString& originalOutputDir = ""); - - QUrl getFBXUrl() const { return _fbxURL; } - QString getBakedFBXFilePath() const { return _bakedFBXFilePath; } - std::vector getOutputFiles() const { return _outputFiles; } - -public slots: - // all calls to FBXBaker::bake for FBXBaker instances must be from the same thread - // because the Autodesk SDK will cause a crash if it is called from multiple threads - virtual void bake() override; - -signals: - void sourceCopyReadyToLoad(); - -private slots: - void bakeSourceCopy(); - void handleFBXNetworkReply(); - void handleBakedTexture(); - -private: - void setupOutputFolder(); - - void loadSourceFBX(); - - void importScene(); - void rewriteAndBakeSceneTextures(); - void exportScene(); - void removeEmbeddedMediaFolder(); - - void checkIfTexturesFinished(); - - QString createBakedTextureFileName(const QFileInfo& textureFileInfo); - QUrl getTextureURL(const QFileInfo& textureFileInfo, fbxsdk::FbxFileTexture* fileTexture); - - void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir); - - QUrl _fbxURL; - - QString _bakedFBXFilePath; - - QString _bakedOutputDir; - - // If set, the original FBX and textures will also be copied here - QString _originalOutputDir; - - QDir _tempDir; - QString _originalFBXFilePath; - - // List of baked output files, includes the FBX and textures - std::vector _outputFiles; - - static FBXSDKManagerUniquePointer _sdkManager; - fbxsdk::FbxScene* _scene { nullptr }; - - QMultiHash> _bakingTextures; - QHash _textureNameMatchCount; - - TextureBakerThreadGetter _textureThreadGetter; - - bool _pendingErrorEmission { false }; -}; - -#endif // hifi_FBXBaker_h diff --git a/tools/oven/src/TextureBaker.cpp b/tools/oven/src/TextureBaker.cpp deleted file mode 100644 index 70df511d2c..0000000000 --- a/tools/oven/src/TextureBaker.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// -// TextureBaker.cpp -// tools/oven/src -// -// Created by Stephen Birarda on 4/5/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 -// - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ModelBakingLoggingCategory.h" - -#include "TextureBaker.h" - -const QString BAKED_TEXTURE_EXT = ".ktx"; - -TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory) : - _textureURL(textureURL), - _textureType(textureType), - _outputDirectory(outputDirectory) -{ - // figure out the baked texture filename - auto originalFilename = textureURL.fileName(); - _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT; -} - -void TextureBaker::bake() { - // once our texture is loaded, kick off a the processing - connect(this, &TextureBaker::originalTextureLoaded, this, &TextureBaker::processTexture); - - // first load the texture (either locally or remotely) - loadTexture(); -} - -void TextureBaker::loadTexture() { - // check if the texture is local or first needs to be downloaded - if (_textureURL.isLocalFile()) { - // load up the local file - QFile localTexture { _textureURL.toLocalFile() }; - - if (!localTexture.open(QIODevice::ReadOnly)) { - handleError("Unable to open texture " + _textureURL.toString()); - return; - } - - _originalTexture = localTexture.readAll(); - - emit originalTextureLoaded(); - } else { - // remote file, kick off a download - auto& networkAccessManager = NetworkAccessManager::getInstance(); - - QNetworkRequest networkRequest; - - // setup the request to follow re-directs and always hit the network - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); - networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - - networkRequest.setUrl(_textureURL); - - qCDebug(model_baking) << "Downloading" << _textureURL; - - // kickoff the download, wait for slot to tell us it is done - auto networkReply = networkAccessManager.get(networkRequest); - connect(networkReply, &QNetworkReply::finished, this, &TextureBaker::handleTextureNetworkReply); - } -} - -void TextureBaker::handleTextureNetworkReply() { - auto requestReply = qobject_cast(sender()); - - if (requestReply->error() == QNetworkReply::NoError) { - qCDebug(model_baking) << "Downloaded texture" << _textureURL; - - // store the original texture so it can be passed along for the bake - _originalTexture = requestReply->readAll(); - - emit originalTextureLoaded(); - } else { - // add an error to our list stating that this texture could not be downloaded - handleError("Error downloading " + _textureURL.toString() + " - " + requestReply->errorString()); - } -} - -void TextureBaker::processTexture() { - auto processedTexture = image::processImage(_originalTexture, _textureURL.toString().toStdString(), - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType); - - if (!processedTexture) { - handleError("Could not process texture " + _textureURL.toString()); - return; - } - - // the baked textures need to have the source hash added for cache checks in Interface - // so we add that to the processed texture before handling it off to be serialized - auto hashData = QCryptographicHash::hash(_originalTexture, QCryptographicHash::Md5); - std::string hash = hashData.toHex().toStdString(); - processedTexture->setSourceHash(hash); - - auto memKTX = gpu::Texture::serialize(*processedTexture); - - if (!memKTX) { - handleError("Could not serialize " + _textureURL.toString() + " to KTX"); - return; - } - - const char* data = reinterpret_cast(memKTX->_storage->data()); - const size_t length = memKTX->_storage->size(); - - // attempt to write the baked texture to the destination file path - QFile bakedTextureFile { _outputDirectory.absoluteFilePath(_bakedTextureFileName) }; - - if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) { - handleError("Could not write baked texture for " + _textureURL.toString()); - } - - qCDebug(model_baking) << "Baked texture" << _textureURL; - emit finished(); -} diff --git a/tools/oven/src/TextureBaker.h b/tools/oven/src/TextureBaker.h deleted file mode 100644 index ee1e968f20..0000000000 --- a/tools/oven/src/TextureBaker.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// TextureBaker.h -// tools/oven/src -// -// Created by Stephen Birarda on 4/5/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 -// - -#ifndef hifi_TextureBaker_h -#define hifi_TextureBaker_h - -#include -#include -#include - -#include - -#include "Baker.h" - -extern const QString BAKED_TEXTURE_EXT; - -class TextureBaker : public Baker { - Q_OBJECT - -public: - TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory); - - const QByteArray& getOriginalTexture() const { return _originalTexture; } - - QUrl getTextureURL() const { return _textureURL; } - - QString getDestinationFilePath() const { return _outputDirectory.absoluteFilePath(_bakedTextureFileName); } - QString getBakedTextureFileName() const { return _bakedTextureFileName; } - -public slots: - virtual void bake() override; - -signals: - void originalTextureLoaded(); - -private slots: - void processTexture(); - -private: - void loadTexture(); - void handleTextureNetworkReply(); - - QUrl _textureURL; - QByteArray _originalTexture; - image::TextureUsage::Type _textureType; - - QDir _outputDirectory; - QString _bakedTextureFileName; -}; - -#endif // hifi_TextureBaker_h From b60d68c7149487e5d824ee9ffd0705df6401c3a5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 14:00:09 -0700 Subject: [PATCH 075/202] Replace FBX SDK loading of file in FBXBaker with FBXReader --- libraries/baking/CMakeLists.txt | 2 +- libraries/baking/src/FBXBaker.cpp | 28 +++++++++------------------- libraries/baking/src/FBXBaker.h | 6 +++++- tools/oven/CMakeLists.txt | 2 +- tools/oven/src/ui/BakeWidget.h | 2 +- tools/oven/src/ui/ModelBakeWidget.h | 2 +- tools/oven/src/ui/SkyboxBakeWidget.h | 2 +- 7 files changed, 19 insertions(+), 25 deletions(-) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 806220ec30..da3389c862 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -15,5 +15,5 @@ if (FBX_FOUND) endif (UNIX) endif () -link_hifi_libraries(shared model networking ktx image) +link_hifi_libraries(shared model networking ktx image fbx) include_hifi_library_headers(gpu) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 81b5de7546..9a28e6a788 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -1,6 +1,6 @@ // // FBXBaker.cpp -// tools/oven/src +// tools/baking/src // // Created by Stephen Birarda on 3/30/17. // Copyright 2017 High Fidelity, Inc. @@ -27,6 +27,9 @@ #include +#include +#include + #include "ModelBakingLoggingCategory.h" #include "TextureBaker.h" @@ -205,29 +208,16 @@ void FBXBaker::handleFBXNetworkReply() { } void FBXBaker::importScene() { - // create an FBX SDK importer - FbxImporter* importer = FbxImporter::Create(_sdkManager.get(), ""); - qDebug() << "file path: " << _originalFBXFilePath.toLocal8Bit().data() << QDir(_originalFBXFilePath).exists(); - // import the copy of the original FBX file - bool importStatus = importer->Initialize(_originalFBXFilePath.toLocal8Bit().data()); - if (!importStatus) { - // failed to initialize importer, print an error and return - handleError("Failed to import " + _fbxURL.toString() + " - " + importer->GetStatus().GetErrorString()); + QFile fbxFile(_originalFBXFilePath); + if (!fbxFile.open(QIODevice::ReadOnly)) { + handleError("Error opening " + _originalFBXFilePath); return; - } else { - qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; } - // setup a new scene to hold the imported file - _scene = FbxScene::Create(_sdkManager.get(), "bakeScene"); - - // import the file to the created scene - importer->Import(_scene); - - // destroy the importer that is no longer needed - importer->Destroy(); + qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; + _rootNode = FBXReader::parseFBX(&fbxFile); } QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 00e7987422..0eb25f510b 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -1,6 +1,6 @@ // // FBXBaker.h -// tools/oven/src +// tools/baking/src // // Created by Stephen Birarda on 3/30/17. // Copyright 2017 High Fidelity, Inc. @@ -24,6 +24,8 @@ #include +#include + namespace fbxsdk { class FbxManager; class FbxProperty; @@ -76,6 +78,8 @@ private: void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir); QUrl _fbxURL; + + FBXNode _rootNode; QString _bakedFBXFilePath; diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 0d692b5465..010b1c25b1 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -2,7 +2,7 @@ set(TARGET_NAME oven) setup_hifi_project(Widgets Gui Concurrent) -link_hifi_libraries(networking shared image gpu ktx fbx baking) +link_hifi_libraries(networking shared image gpu ktx fbx baking model) setup_memory_debugger() diff --git a/tools/oven/src/ui/BakeWidget.h b/tools/oven/src/ui/BakeWidget.h index e7ab8d1840..00996128ed 100644 --- a/tools/oven/src/ui/BakeWidget.h +++ b/tools/oven/src/ui/BakeWidget.h @@ -14,7 +14,7 @@ #include -#include "../Baker.h" +#include class BakeWidget : public QWidget { Q_OBJECT diff --git a/tools/oven/src/ui/ModelBakeWidget.h b/tools/oven/src/ui/ModelBakeWidget.h index ed08990ba5..b42b8725f6 100644 --- a/tools/oven/src/ui/ModelBakeWidget.h +++ b/tools/oven/src/ui/ModelBakeWidget.h @@ -16,7 +16,7 @@ #include -#include "../FBXBaker.h" +#include #include "BakeWidget.h" diff --git a/tools/oven/src/ui/SkyboxBakeWidget.h b/tools/oven/src/ui/SkyboxBakeWidget.h index 4063a5459b..f00ab07f33 100644 --- a/tools/oven/src/ui/SkyboxBakeWidget.h +++ b/tools/oven/src/ui/SkyboxBakeWidget.h @@ -16,7 +16,7 @@ #include -#include "../TextureBaker.h" +#include #include "BakeWidget.h" From 7214f5737625d777ebdecb7bd6046e71f7d6f0bb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 7 Sep 2017 14:12:15 -0700 Subject: [PATCH 076/202] Update FBXBaker::exportScene with FBXWriter --- libraries/baking/src/FBXBaker.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 9a28e6a788..35d981f337 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -212,7 +212,7 @@ void FBXBaker::importScene() { QFile fbxFile(_originalFBXFilePath); if (!fbxFile.open(QIODevice::ReadOnly)) { - handleError("Error opening " + _originalFBXFilePath); + handleError("Error opening " + _originalFBXFilePath + " for reading"); return; } @@ -502,29 +502,25 @@ void FBXBaker::handleBakedTexture() { } void FBXBaker::exportScene() { - // setup the exporter - FbxExporter* exporter = FbxExporter::Create(_sdkManager.get(), ""); - // save the relative path to this FBX inside our passed output folder - auto fileName = _fbxURL.fileName(); auto baseName = fileName.left(fileName.lastIndexOf('.')); auto bakedFilename = baseName + BAKED_FBX_EXTENSION; _bakedFBXFilePath = _bakedOutputDir + "/" + bakedFilename; - bool exportStatus = exporter->Initialize(_bakedFBXFilePath.toLocal8Bit().data()); + auto fbxData = FBXWriter::encodeFBX(_rootNode); - if (!exportStatus) { - // failed to initialize exporter, print an error and return - handleError("Failed to export FBX file at " + _fbxURL.toString() + " to " + _bakedFBXFilePath - + "- error: " + exporter->GetStatus().GetErrorString()); + QFile bakedFile(_bakedFBXFilePath); + + if (!bakedFile.open(QIODevice::WriteOnly)) { + handleError("Error opening " + _bakedFBXFilePath + " for writing"); + return; } - _outputFiles.push_back(_bakedFBXFilePath); + bakedFile.write(fbxData); - // export the scene - exporter->Export(_scene); + _outputFiles.push_back(_bakedFBXFilePath); qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << _bakedFBXFilePath; } From b153d1e1779aa53372a22e7e6f8a247b4cf0b675 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Sep 2017 18:38:29 -0700 Subject: [PATCH 077/202] use FBXReader/FBXWriter for texture baking in FBXBaker --- libraries/baking/src/FBXBaker.cpp | 108 ++++++++++++++--------- libraries/baking/src/FBXBaker.h | 18 ++-- libraries/baking/src/TextureBaker.cpp | 13 ++- libraries/baking/src/TextureBaker.h | 3 +- libraries/fbx/src/FBX.h | 1 + libraries/fbx/src/FBXReader.cpp | 2 +- libraries/fbx/src/FBXReader_Material.cpp | 1 + libraries/fbx/src/FBXWriter.cpp | 6 +- 8 files changed, 87 insertions(+), 65 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 35d981f337..73209ec7cf 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -35,9 +35,6 @@ #include "FBXBaker.h" -std::once_flag onceFlag; -FBXSDKManagerUniquePointer FBXBaker::_sdkManager { nullptr }; - FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, const QString& bakedOutputDir, const QString& originalOutputDir) : _fbxURL(fbxURL), @@ -45,12 +42,7 @@ FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGet _originalOutputDir(originalOutputDir), _textureThreadGetter(textureThreadGetter) { - std::call_once(onceFlag, [](){ - // create the static FBX SDK manager - _sdkManager = FBXSDKManagerUniquePointer(FbxManager::Create(), [](FbxManager* manager){ - manager->Destroy(); - }); - }); + } void FBXBaker::bake() { @@ -216,8 +208,12 @@ void FBXBaker::importScene() { return; } - qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene"; - _rootNode = FBXReader::parseFBX(&fbxFile); + FBXReader reader; + + qCDebug(model_baking) << "Parsing" << _fbxURL; + _rootNode = reader._rootNode = reader.parseFBX(&fbxFile); + _geometry = *reader.extractFBXGeometry({}, _fbxURL.toString()); + _textureContent = reader._textureContent; } QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { @@ -254,7 +250,7 @@ QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { return bakedTextureFileName; } -QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* fileTexture) { +QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName) { QUrl urlToTexture; if (textureFileInfo.exists() && textureFileInfo.isFile()) { @@ -264,7 +260,6 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* f // external texture that we'll need to download or find // first check if it the RelativePath to the texture in the FBX was relative - QString relativeFileName = fileTexture->GetRelativeFileName(); auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); // this is a relative file path which will require different handling @@ -336,31 +331,44 @@ image::TextureUsage::Type textureTypeForMaterialProperty(FbxProperty& property, } void FBXBaker::rewriteAndBakeSceneTextures() { + using namespace image::TextureUsage; + QHash textureTypes; - // enumerate the surface materials to find the textures used in the scene - int numMaterials = _scene->GetMaterialCount(); - for (int i = 0; i < numMaterials; i++) { - FbxSurfaceMaterial* material = _scene->GetMaterial(i); + // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID + for (const auto& material : _geometry.materials) { + if (material.normalTexture.isBumpmap) { + textureTypes[material.normalTexture.id] = BUMP_TEXTURE; + } else { + textureTypes[material.normalTexture.id] = NORMAL_TEXTURE; + } - if (material) { - // enumerate the properties of this material to see what texture channels it might have - FbxProperty property = material->GetFirstProperty(); + textureTypes[material.albedoTexture.id] = ALBEDO_TEXTURE; + textureTypes[material.glossTexture.id] = GLOSS_TEXTURE; + textureTypes[material.roughnessTexture.id] = ROUGHNESS_TEXTURE; + textureTypes[material.specularTexture.id] = SPECULAR_TEXTURE; + textureTypes[material.metallicTexture.id] = METALLIC_TEXTURE; + textureTypes[material.emissiveTexture.id] = EMISSIVE_TEXTURE; + textureTypes[material.occlusionTexture.id] = OCCLUSION_TEXTURE; + textureTypes[material.lightmapTexture.id] = LIGHTMAP_TEXTURE; + } - while (property.IsValid()) { - // first check if this property has connected textures, if not we don't need to bother with it here - if (property.GetSrcObjectCount() > 0) { + // enumerate the children of the root node + for (FBXNode& rootChild : _rootNode.children) { - // figure out the type of texture from the material property - auto textureType = textureTypeForMaterialProperty(property, material); + if (rootChild.name == "Objects") { - if (textureType != image::TextureUsage::UNUSED_TEXTURE) { - int numTextures = property.GetSrcObjectCount(); + // enumerate the objects + auto object = rootChild.children.begin(); + while (object != rootChild.children.end()) { + if (object->name == "Texture") { - for (int j = 0; j < numTextures; j++) { - FbxFileTexture* fileTexture = property.GetSrcObject(j); + // enumerate the texture children + for (FBXNode& textureChild : object->children) { + + if (textureChild.name == "RelativeFilename") { // use QFileInfo to easily split up the existing texture filename into its components - QString fbxTextureFileName { fileTexture->GetFileName() }; + QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() }; QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; // make sure this texture points to something and isn't one we've already re-mapped @@ -383,38 +391,50 @@ void FBXBaker::rewriteAndBakeSceneTextures() { }; _outputFiles.push_back(bakedTextureFilePath); - qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName() - << "to" << bakedTextureFilePath; + qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName + << "to" << bakedTextureFileName; // figure out the URL to this texture, embedded or external - auto urlToTexture = getTextureURL(textureFileInfo, fileTexture); + auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName); // write the new filename into the FBX scene - fileTexture->SetFileName(bakedTextureFilePath.toUtf8().data()); - - // write the relative filename to be the baked texture file name since it will - // be right beside the FBX - fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData()); + textureChild.properties[0] = bakedTextureFileName.toLocal8Bit(); if (!_bakingTextures.contains(urlToTexture)) { + + // grab the ID for this texture so we can figure out the + // texture type from the loaded materials + QString textureID { object->properties[0].toByteArray() }; + auto textureType = textureTypes[textureID]; + + // check if this was an embedded texture we have already have in-memory content for + auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit()); + // bake this texture asynchronously - bakeTexture(urlToTexture, textureType, _bakedOutputDir); + bakeTexture(urlToTexture, textureType, _bakedOutputDir, textureContent); } } } } - } - property = material->GetNextProperty(property); + ++object; + + } else if (object->name == "Video") { + // this is an embedded texture, we need to remove it from the FBX + object = rootChild.children.erase(object); + } else { + ++object; + } } } } } -void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir) { +void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, + const QDir& outputDir, const QByteArray& textureContent) { // start a bake for this texture and add it to our list to keep track of QSharedPointer bakingTexture { - new TextureBaker(textureURL, textureType, outputDir), + new TextureBaker(textureURL, textureType, outputDir, textureContent), &TextureBaker::deleteLater }; @@ -464,7 +484,7 @@ void FBXBaker::handleBakedTexture() { if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) { qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName() - << "for" << _fbxURL; + << "for" << _fbxURL; } else { handleError("Could not save original external texture " + originalTextureFile.fileName() + " for " + _fbxURL.toString()); diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 0eb25f510b..2dc31828cb 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -26,15 +26,7 @@ #include -namespace fbxsdk { - class FbxManager; - class FbxProperty; - class FbxScene; - class FbxFileTexture; -} - static const QString BAKED_FBX_EXTENSION = ".baked.fbx"; -using FBXSDKManagerUniquePointer = std::unique_ptr>; using TextureBakerThreadGetter = std::function; @@ -73,13 +65,16 @@ private: void checkIfTexturesFinished(); QString createBakedTextureFileName(const QFileInfo& textureFileInfo); - QUrl getTextureURL(const QFileInfo& textureFileInfo, fbxsdk::FbxFileTexture* fileTexture); + QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName); - void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir); + void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir, + const QByteArray& textureContent = QByteArray()); QUrl _fbxURL; FBXNode _rootNode; + FBXGeometry _geometry; + QHash _textureContent; QString _bakedFBXFilePath; @@ -91,9 +86,6 @@ private: QDir _tempDir; QString _originalFBXFilePath; - static FBXSDKManagerUniquePointer _sdkManager; - fbxsdk::FbxScene* _scene { nullptr }; - QMultiHash> _bakingTextures; QHash _textureNameMatchCount; diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 548e3921e4..cdf21a0290 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -25,8 +25,10 @@ const QString BAKED_TEXTURE_EXT = ".ktx"; -TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory) : +TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, + const QDir& outputDirectory, const QByteArray& textureContent) : _textureURL(textureURL), + _originalTexture(textureContent), _textureType(textureType), _outputDirectory(outputDirectory) { @@ -39,8 +41,13 @@ void TextureBaker::bake() { // once our texture is loaded, kick off a the processing connect(this, &TextureBaker::originalTextureLoaded, this, &TextureBaker::processTexture); - // first load the texture (either locally or remotely) - loadTexture(); + if (_originalTexture.isEmpty()) { + // first load the texture (either locally or remotely) + loadTexture(); + } else { + // we already have a texture passed to us, use that + emit originalTextureLoaded(); + } } const QStringList TextureBaker::getSupportedFormats() { diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h index 76d0a69823..5ea696dda0 100644 --- a/libraries/baking/src/TextureBaker.h +++ b/libraries/baking/src/TextureBaker.h @@ -27,7 +27,8 @@ class TextureBaker : public Baker { Q_OBJECT public: - TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory); + TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, + const QDir& outputDirectory, const QByteArray& textureContent = QByteArray()); static const QStringList getSupportedFormats(); diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 9e1982f9e0..fc45e9a792 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -113,6 +113,7 @@ const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; /// A texture map in an FBX document. class FBXTexture { public: + QString id; QString name; QByteArray filename; QByteArray content; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index d212ec820f..d4a58a1126 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -939,7 +939,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QByteArray content; foreach (const FBXNode& subobject, object.children) { if (subobject.name == "RelativeFilename") { - filepath= subobject.properties.at(0).toByteArray(); + filepath = subobject.properties.at(0).toByteArray(); filepath = filepath.replace('\\', '/'); } else if (subobject.name == "Content" && !subobject.properties.isEmpty()) { diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index ca2ec557b4..ef6496cd10 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -92,6 +92,7 @@ FBXTexture FBXReader::getTexture(const QString& textureID) { texture.filename = filepath; } + texture.id = textureID; texture.name = _textureNames.value(textureID); texture.transform.setIdentity(); texture.texcoordSet = 0; diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index a084d94a46..f6bb92a8b5 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -119,7 +119,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QMetaType::QString: { - auto& bytes = prop.toString().toUtf8(); + auto bytes = prop.toString().toUtf8(); out << 'S'; out << bytes.length(); out << bytes; @@ -130,7 +130,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QMetaType::QByteArray: { - auto& bytes = prop.toByteArray(); + auto bytes = prop.toByteArray(); out.device()->write("S", 1); out << (int32_t)bytes.size(); out.writeRawData(bytes, bytes.size()); @@ -140,7 +140,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { // TODO Delete? Do we ever use QList instead of QVector? case QVariant::Type::List: { - auto& list = prop.toList(); + auto list = prop.toList(); auto listType = prop.userType(); switch (listType) { From 3cf77f377c149717545eea83fe9e988f792dd24f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Sep 2017 18:46:44 -0700 Subject: [PATCH 078/202] remove FBX SDK from CMake files --- cmake/modules/FindFBX.cmake | 111 -------------------------------- libraries/baking/CMakeLists.txt | 14 ---- tools/oven/CMakeLists.txt | 12 ---- 3 files changed, 137 deletions(-) delete mode 100644 cmake/modules/FindFBX.cmake diff --git a/cmake/modules/FindFBX.cmake b/cmake/modules/FindFBX.cmake deleted file mode 100644 index 3963f4d2e6..0000000000 --- a/cmake/modules/FindFBX.cmake +++ /dev/null @@ -1,111 +0,0 @@ -# Locate the FBX SDK -# -# Defines the following variables: -# -# FBX_FOUND - Found the FBX SDK -# FBX_VERSION - Version number -# FBX_INCLUDE_DIRS - Include directories -# FBX_LIBRARIES - The libraries to link to -# -# Accepts the following variables as input: -# -# FBX_VERSION - as a CMake variable, e.g. 2017.0.1 -# FBX_ROOT - (as a CMake or environment variable) -# The root directory of the FBX SDK install - -# adapted from https://github.com/ufz-vislab/VtkFbxConverter/blob/master/FindFBX.cmake -# which uses the MIT license (https://github.com/ufz-vislab/VtkFbxConverter/blob/master/LICENSE.txt) - -if (NOT FBX_VERSION) - set(FBX_VERSION 2017.1) -endif() - -string(REGEX REPLACE "^([0-9]+).*$" "\\1" FBX_VERSION_MAJOR "${FBX_VERSION}") -string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*$" "\\1" FBX_VERSION_MINOR "${FBX_VERSION}") -string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" FBX_VERSION_PATCH "${FBX_VERSION}") - -set(FBX_MAC_LOCATIONS "/Applications/Autodesk/FBX\ SDK/${FBX_VERSION}") -set(FBX_LINUX_LOCATIONS "/usr/local/fbxsdk") - -if (WIN32) - string(REGEX REPLACE "\\\\" "/" WIN_PROGRAM_FILES_X64_DIRECTORY $ENV{ProgramW6432}) -endif() - -set(FBX_WIN_LOCATIONS "${WIN_PROGRAM_FILES_X64_DIRECTORY}/Autodesk/FBX/FBX SDK/${FBX_VERSION}") - -set(FBX_SEARCH_LOCATIONS $ENV{FBX_ROOT} ${FBX_ROOT} ${FBX_MAC_LOCATIONS} ${FBX_WIN_LOCATIONS} ${FBX_LINUX_LOCATIONS}) - -function(_fbx_append_debugs _endvar _library) - if (${_library} AND ${_library}_DEBUG) - set(_output optimized ${${_library}} debug ${${_library}_DEBUG}) - else() - set(_output ${${_library}}) - endif() - - set(${_endvar} ${_output} PARENT_SCOPE) -endfunction() - -if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") - set(fbx_compiler clang) -elseif (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") - set(fbx_compiler gcc4) -endif() - -function(_fbx_find_library _name _lib _suffix) - if (MSVC_VERSION EQUAL 1910) - set(VS_PREFIX vs2015) - elseif (MSVC_VERSION EQUAL 1900) - set(VS_PREFIX vs2015) - elseif (MSVC_VERSION EQUAL 1800) - set(VS_PREFIX vs2013) - elseif (MSVC_VERSION EQUAL 1700) - set(VS_PREFIX vs2012) - elseif (MSVC_VERSION EQUAL 1600) - set(VS_PREFIX vs2010) - elseif (MSVC_VERSION EQUAL 1500) - set(VS_PREFIX vs2008) - endif() - - find_library(${_name} - NAMES ${_lib} - HINTS ${FBX_SEARCH_LOCATIONS} - PATH_SUFFIXES lib/${fbx_compiler}/${_suffix} lib/${fbx_compiler}/x64/${_suffix} lib/${fbx_compiler}/ub/${_suffix} lib/${VS_PREFIX}/x64/${_suffix} - ) - - mark_as_advanced(${_name}) -endfunction() - -find_path(FBX_INCLUDE_DIR fbxsdk.h - PATHS ${FBX_SEARCH_LOCATIONS} - PATH_SUFFIXES include -) -mark_as_advanced(FBX_INCLUDE_DIR) - -if (WIN32) - _fbx_find_library(FBX_LIBRARY libfbxsdk-md release) - _fbx_find_library(FBX_LIBRARY_DEBUG libfbxsdk-md debug) -elseif (APPLE) - find_library(CARBON NAMES Carbon) - find_library(SYSTEM_CONFIGURATION NAMES SystemConfiguration) - _fbx_find_library(FBX_LIBRARY libfbxsdk.a release) - _fbx_find_library(FBX_LIBRARY_DEBUG libfbxsdk.a debug) -else () - _fbx_find_library(FBX_LIBRARY libfbxsdk.a release) -endif() - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(FBX DEFAULT_MSG FBX_LIBRARY FBX_INCLUDE_DIR) - -if (FBX_FOUND) - set(FBX_INCLUDE_DIRS ${FBX_INCLUDE_DIR}) - _fbx_append_debugs(FBX_LIBRARIES FBX_LIBRARY) - add_definitions(-DFBXSDK_NEW_API) - - if (WIN32) - add_definitions(-DK_PLUGIN -DK_FBXSDK -DK_NODLL) - set(CMAKE_EXE_LINKER_FLAGS /NODEFAULTLIB:\"LIBCMT\") - set(FBX_LIBRARIES ${FBX_LIBRARIES} Wininet.lib) - elseif (APPLE) - set(FBX_LIBRARIES ${FBX_LIBRARIES} ${CARBON} ${SYSTEM_CONFIGURATION}) - endif() -endif() diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index da3389c862..0805c0198c 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -1,19 +1,5 @@ set(TARGET_NAME baking) setup_hifi_library(Concurrent) -find_package(FBX) -if (FBX_FOUND) - if (CMAKE_THREAD_LIBS_INIT) - target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}") - else () - target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES}) - endif () - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) - - if (UNIX) - target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS}) - endif (UNIX) -endif () - link_hifi_libraries(shared model networking ktx image fbx) include_hifi_library_headers(gpu) diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 010b1c25b1..1022c204c5 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -17,16 +17,4 @@ if (UNIX) endif() endif () -# try to find the FBX SDK but fail silently if we don't -# because this tool is not built by default -find_package(FBX) -if (FBX_FOUND) - if (CMAKE_THREAD_LIBS_INIT) - target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES} "${CMAKE_THREAD_LIBS_INIT}") - else () - target_link_libraries(${TARGET_NAME} ${FBX_LIBRARIES}) - endif () - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${FBX_INCLUDE_DIR}) -endif () - set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE) From 0d7b50cfb66370ddcca5b379b16f70430a92a573 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 8 Sep 2017 09:44:19 -0700 Subject: [PATCH 079/202] remove old texture type method --- libraries/baking/src/FBXBaker.cpp | 52 ------------------------------- 1 file changed, 52 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 73209ec7cf..e3b1269d37 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -278,58 +278,6 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeF return urlToTexture; } -image::TextureUsage::Type textureTypeForMaterialProperty(FbxProperty& property, FbxSurfaceMaterial* material) { - using namespace image::TextureUsage; - - // this is a property we know has a texture, we need to match it to a High Fidelity known texture type - // since that information is passed to the baking process - - // grab the hierarchical name for this property and lowercase it for case-insensitive compare - auto propertyName = QString(property.GetHierarchicalName()).toLower(); - - // figure out the type of the property based on what known value string it matches - if ((propertyName.contains("diffuse") && !propertyName.contains("tex_global_diffuse")) - || propertyName.contains("tex_color_map")) { - return ALBEDO_TEXTURE; - } else if (propertyName.contains("transparentcolor") || propertyName.contains("transparencyfactor")) { - return ALBEDO_TEXTURE; - } else if (propertyName.contains("bump")) { - return BUMP_TEXTURE; - } else if (propertyName.contains("normal")) { - return NORMAL_TEXTURE; - } else if ((propertyName.contains("specular") && !propertyName.contains("tex_global_specular")) - || propertyName.contains("reflection")) { - return SPECULAR_TEXTURE; - } else if (propertyName.contains("tex_metallic_map")) { - return METALLIC_TEXTURE; - } else if (propertyName.contains("shininess")) { - return GLOSS_TEXTURE; - } else if (propertyName.contains("tex_roughness_map")) { - return ROUGHNESS_TEXTURE; - } else if (propertyName.contains("emissive")) { - return EMISSIVE_TEXTURE; - } else if (propertyName.contains("ambientcolor")) { - return LIGHTMAP_TEXTURE; - } else if (propertyName.contains("ambientfactor")) { - // we need to check what the ambient factor is, since that tells Interface to process this texture - // either as an occlusion texture or a light map - auto lambertMaterial = FbxCast(material); - - if (lambertMaterial->AmbientFactor == 0) { - return LIGHTMAP_TEXTURE; - } else if (lambertMaterial->AmbientFactor > 0) { - return OCCLUSION_TEXTURE; - } else { - return UNUSED_TEXTURE; - } - - } else if (propertyName.contains("tex_ao_map")) { - return OCCLUSION_TEXTURE; - } - - return UNUSED_TEXTURE; -} - void FBXBaker::rewriteAndBakeSceneTextures() { using namespace image::TextureUsage; QHash textureTypes; From 91f455159808cbfc3b5b953b622f3048f22ee121 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 8 Aug 2017 11:24:16 -0700 Subject: [PATCH 080/202] add draco to the oven tool --- cmake/externals/draco/CMakeLists.txt | 35 ++++++++++++++++++++++++++++ cmake/modules/FindDraco.cmake | 30 ++++++++++++++++++++++++ tools/oven/CMakeLists.txt | 5 ++++ 3 files changed, 70 insertions(+) create mode 100644 cmake/externals/draco/CMakeLists.txt create mode 100644 cmake/modules/FindDraco.cmake diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt new file mode 100644 index 0000000000..8ef1593dc1 --- /dev/null +++ b/cmake/externals/draco/CMakeLists.txt @@ -0,0 +1,35 @@ +set(EXTERNAL_NAME draco) + +if (ANDROID) + set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") +endif () + +include(ExternalProject) +ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://hifi-public.s3.amazonaws.com/dependencies/draco-master-c8b6219.zip + URL_MD5 d60ed5fb2e1792445078f931875e3ee1 + CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# Hide this external target (for ide users) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") + +if (UNIX) + set(LIB_PREFIX "lib") + set(LIB_EXT "a") +elseif (WIN32) + set(LIB_EXT "lib") +endif () + +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}draco.${LIB_EXT} CACHE FILEPATH "Path to Draco release library") +set(${EXTERNAL_NAME_UPPER}_ENCODER_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}dracoenc.${LIB_EXT} CACHE FILEPATH "Path to Draco encoder release library") +set(${EXTERNAL_NAME_UPPER}_DECODER_LIBRARY ${INSTALL_DIR}/lib/${LIB_PREFIX}dracodec.${LIB_EXT} CACHE FILEPATH "Path to Draco decoder release library") diff --git a/cmake/modules/FindDraco.cmake b/cmake/modules/FindDraco.cmake new file mode 100644 index 0000000000..f549d410f9 --- /dev/null +++ b/cmake/modules/FindDraco.cmake @@ -0,0 +1,30 @@ +# +# FindDraco.cmake +# +# Try to find Draco libraries and include path. +# Once done this will define +# +# DRACO_FOUND +# DRACO_INCLUDE_DIRS +# DRACO_LIBRARY +# DRACO_ENCODER_LIBRARY +# DRACO_DECODER_LIBRARY +# +# Created on 8/8/2017 by Stephen Birarda +# 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 +# + +include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") +hifi_library_search_hints("draco") + +find_path(DRACO_INCLUDE_DIRS draco/core/draco_types.h PATH_SUFFIXES include/draco/src HINTS ${DRACO_SEARCH_DIRS}) + +find_library(DRACO_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) +find_library(DRACO_ENCODER_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) +find_library(DRACO_DECODER_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DRACO DEFAULT_MSG DRACO_INCLUDE_DIRS DRACO_LIBRARY DRACO_ENCODER_LIBRARY DRACO_DECODER_LIBRARY) diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 1022c204c5..9d5d6c1aad 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -6,6 +6,11 @@ link_hifi_libraries(networking shared image gpu ktx fbx baking model) setup_memory_debugger() +add_dependency_external_projects(draco) +find_package(Draco REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) + if (WIN32) package_libraries_for_deployment() endif () From 09d18b5ba91fb67774a759acf92cda0d93acd0fe Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 8 Aug 2017 12:17:27 -0700 Subject: [PATCH 081/202] handle special include path for Draco on WIN --- cmake/externals/draco/CMakeLists.txt | 7 ++++++- cmake/modules/FindDraco.cmake | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 8ef1593dc1..03a6622a1b 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -21,7 +21,12 @@ set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") + +if (UNIX) + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") +else () + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories") +endif () if (UNIX) set(LIB_PREFIX "lib") diff --git a/cmake/modules/FindDraco.cmake b/cmake/modules/FindDraco.cmake index f549d410f9..342797b62e 100644 --- a/cmake/modules/FindDraco.cmake +++ b/cmake/modules/FindDraco.cmake @@ -20,7 +20,7 @@ include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") hifi_library_search_hints("draco") -find_path(DRACO_INCLUDE_DIRS draco/core/draco_types.h PATH_SUFFIXES include/draco/src HINTS ${DRACO_SEARCH_DIRS}) +find_path(DRACO_INCLUDE_DIRS draco/core/draco_types.h PATH_SUFFIXES include/draco/src include HINTS ${DRACO_SEARCH_DIRS}) find_library(DRACO_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) find_library(DRACO_ENCODER_LIBRARY draco PATH_SUFFIXES "lib" HINTS ${DRACO_SEARCH_DIRS}) From 9b462171f623b28cadf1c34d4f42bac3521eaaed Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 8 Sep 2017 16:56:23 -0700 Subject: [PATCH 082/202] Add draco support to FBXBaker --- libraries/baking/CMakeLists.txt | 5 + libraries/baking/src/FBXBaker.cpp | 136 ++++++++++++++++++++++++++- libraries/baking/src/FBXBaker.h | 3 +- libraries/fbx/src/FBX.h | 7 ++ libraries/fbx/src/FBXReader.h | 2 +- libraries/fbx/src/FBXReader_Mesh.cpp | 3 +- tools/oven/CMakeLists.txt | 5 - 7 files changed, 148 insertions(+), 13 deletions(-) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 0805c0198c..66cf791776 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -3,3 +3,8 @@ setup_hifi_library(Concurrent) link_hifi_libraries(shared model networking ktx image fbx) include_hifi_library_headers(gpu) + +add_dependency_external_projects(draco) +find_package(Draco REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index e3b1269d37..f504b3f03c 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -11,8 +11,6 @@ #include // need this include so we don't get an error looking for std::isnan -#include - #include #include #include @@ -35,6 +33,9 @@ #include "FBXBaker.h" +#include +#include + FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, const QString& bakedOutputDir, const QString& originalOutputDir) : _fbxURL(fbxURL), @@ -80,7 +81,8 @@ void FBXBaker::bakeSourceCopy() { return; } - // enumerate the textures found in the scene and start a bake for them + // enumerate the models and textures found in the scene and start a bake for them + rewriteAndBakeSceneModels(); rewriteAndBakeSceneTextures(); if (hasErrors()) { @@ -212,7 +214,7 @@ void FBXBaker::importScene() { qCDebug(model_baking) << "Parsing" << _fbxURL; _rootNode = reader._rootNode = reader.parseFBX(&fbxFile); - _geometry = *reader.extractFBXGeometry({}, _fbxURL.toString()); + _geometry = reader.extractFBXGeometry({}, _fbxURL.toString()); _textureContent = reader._textureContent; } @@ -278,12 +280,136 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeF return urlToTexture; } +void FBXBaker::rewriteAndBakeSceneModels() { + unsigned int meshIndex = 0; + for (FBXNode& rootChild : _rootNode.children) { + if (rootChild.name == "Objects") { + for (FBXNode& objectChild : rootChild.children) { + if (objectChild.name == "Geometry") { + + // TODO Pull this out of _geometry instead so we don't have to reprocess it + auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex); + auto mesh = extractedMesh.mesh; + + Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); + Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); + Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); + + int64_t numTriangles { 0 }; + for (auto& part : mesh.parts) { + Q_ASSERT(part.quadTrianglesIndices.size() % 3 == 0); + Q_ASSERT(part.triangleIndices.size() % 3 == 0); + + numTriangles += part.quadTrianglesIndices.size() / 3; + numTriangles += part.triangleIndices.size() / 3; + } + + draco::TriangleSoupMeshBuilder meshBuilder; + + meshBuilder.Start(numTriangles); + + bool hasNormals { mesh.normals.size() > 0 }; + bool hasColors { mesh.colors.size() > 0 }; + bool hasTexCoords { mesh.texCoords.size() > 0 }; + //bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; + + int normalsAttributeID { -1 }; + int colorsAttributeID { -1 }; + int texCoordsAttributeID { -1 }; + //int texCoords1AttributeID { -1 }; + + const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, + 3, draco::DT_FLOAT32); + + const int faceMaterialAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, + 1, draco::DT_INT64); + + if (hasNormals) { + normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, + 3, draco::DT_FLOAT32); + } + if (hasColors) { + colorsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::COLOR, + 3, draco::DT_FLOAT32); + } + if (hasTexCoords) { + texCoordsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::TEX_COORD, + 2, draco::DT_FLOAT32); + } + + + for (auto& part : mesh.parts) { + //Q_ASSERT(part.quadTrianglesIndices % 3 == 0); + //Q_ASSERT(part.triangleIndices % 3 == 0); + + int64_t materialID = 0; + + for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { + auto idx0 = part.quadTrianglesIndices[i]; + auto idx1 = part.quadTrianglesIndices[i + 1]; + auto idx2 = part.quadTrianglesIndices[i + 2]; + + auto face = draco::FaceIndex(i / 3); + + meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); + + meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, + &mesh.vertices[idx0], &mesh.vertices[idx1], + &mesh.vertices[idx2]); + + if (hasNormals) { + meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, + &mesh.normals[idx0],&mesh.normals[idx1], + &mesh.normals[idx2]); + } + if (hasColors) { + meshBuilder.SetAttributeValuesForFace(colorsAttributeID, face, + &mesh.colors[idx0], &mesh.colors[idx1], + &mesh.colors[idx2]); + } + if (hasTexCoords) { + meshBuilder.SetAttributeValuesForFace(texCoordsAttributeID, face, + &mesh.texCoords[idx0], &mesh.texCoords[idx1], + &mesh.texCoords[idx2]); + } + } + } + + auto dracoMesh = meshBuilder.Finalize(); + + draco::Encoder encoder; + draco::EncoderBuffer buffer; + encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); + + FBXNode dracoMeshNode; + dracoMeshNode.name = "DracoMesh"; + auto value = QVariant::fromValue(QByteArray(buffer.data(), buffer.size())); + dracoMeshNode.properties.append(value); + + + QFile file("C:/Users/huffm/encodedFBX/" + this->_fbxURL.fileName() + "-" + QString::number(meshIndex) + ".drc"); + if (file.open(QIODevice::WriteOnly)) { + file.write(buffer.data(), buffer.size()); + file.close(); + } else { + qWarning() << "Failed to write to: " << file.fileName(); + + } + + objectChild.children.push_back(dracoMeshNode); + } + } + } + } +} + void FBXBaker::rewriteAndBakeSceneTextures() { using namespace image::TextureUsage; QHash textureTypes; // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID - for (const auto& material : _geometry.materials) { + for (const auto& material : _geometry->materials) { if (material.normalTexture.isBumpmap) { textureTypes[material.normalTexture.id] = BUMP_TEXTURE; } else { diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 2dc31828cb..26c1ff2dcc 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -58,6 +58,7 @@ private: void loadSourceFBX(); void importScene(); + void rewriteAndBakeSceneModels(); void rewriteAndBakeSceneTextures(); void exportScene(); void removeEmbeddedMediaFolder(); @@ -73,7 +74,7 @@ private: QUrl _fbxURL; FBXNode _rootNode; - FBXGeometry _geometry; + FBXGeometry* _geometry; QHash _textureContent; QString _bakedFBXFilePath; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index fc45e9a792..9f5fad7d66 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -32,6 +32,13 @@ static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary "; static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; static const quint32 FBX_VERSION_2016 = 7500; + +// TODO Convert to GeometryAttribute type +static const int DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES = 1000; +static const int DRACO_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES; +static const int DRACO_ATTRIBUTE_TEX_COORD_1 = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 1; + + class FBXNode; using FBXNodeList = QList; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index a600ac6bab..8a5e394da9 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -110,7 +110,7 @@ public: FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); - ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex); + static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex); QHash meshes; static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 4e153dfe3a..14f1c09b75 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -304,8 +304,9 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn if (subdata.name == "Materials") { materials = getIntVector(subdata); } else if (subdata.name == "MappingInformationType") { - if (subdata.properties.at(0) == BY_POLYGON) + if (subdata.properties.at(0) == BY_POLYGON) { isMaterialPerPolygon = true; + } } else { isMaterialPerPolygon = false; } diff --git a/tools/oven/CMakeLists.txt b/tools/oven/CMakeLists.txt index 9d5d6c1aad..1022c204c5 100644 --- a/tools/oven/CMakeLists.txt +++ b/tools/oven/CMakeLists.txt @@ -6,11 +6,6 @@ link_hifi_libraries(networking shared image gpu ktx fbx baking model) setup_memory_debugger() -add_dependency_external_projects(draco) -find_package(Draco REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) - if (WIN32) package_libraries_for_deployment() endif () From 3d6d383a150e3eb92287e0b0e120c2b384677337 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 8 Sep 2017 16:58:57 -0700 Subject: [PATCH 083/202] Clean up duplicate list code in encodeFBXProperty --- libraries/fbx/src/FBXWriter.cpp | 57 ++++++++++----------------------- 1 file changed, 17 insertions(+), 40 deletions(-) diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index f6bb92a8b5..9f389fca88 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -13,6 +13,17 @@ #include +template +void writeVector(QDataStream& out, char ch, QVector list) { + out.device()->write(&ch, 1); + out << (int32_t)list.length(); + out << (int32_t)0; + out << (int32_t)0; + for (auto& value : list) { + out << value; + } +} + QByteArray FBXWriter::encodeFBX(const FBXNode& root) { QByteArray data; @@ -93,6 +104,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QVariant::Type::Bool: out.device()->write("C", 1); + //out.device()->write(prop.toBool() ? 1 : 0, 1); out << prop.toBool(); break; @@ -200,50 +212,15 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { default: { if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("f", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'f', prop.value>()); } else if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("d", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'd', prop.value>()); } else if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("l", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'l', prop.value>()); } else if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("i", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'i', prop.value>()); } else if (prop.canConvert>()) { - auto list = prop.value>(); - out.device()->write("b", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& value : list) { - out << value; - } + writeVector(out, 'b', prop.value>()); } else { qDebug() << "Unsupported property type in FBXWriter::encodeNode: " << type << prop; } From ebd925b966ecdb56d34401f7604651c9263d69e0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 Sep 2017 10:52:59 -0700 Subject: [PATCH 084/202] Add proper per face materials and texCoords1 to fbx baking --- libraries/baking/src/FBXBaker.cpp | 35 ++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index f504b3f03c..b5cd94cec7 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -311,20 +311,18 @@ void FBXBaker::rewriteAndBakeSceneModels() { bool hasNormals { mesh.normals.size() > 0 }; bool hasColors { mesh.colors.size() > 0 }; bool hasTexCoords { mesh.texCoords.size() > 0 }; - //bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; + bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; + bool hasPerFaceMaterials { mesh.parts.size() > 0 }; int normalsAttributeID { -1 }; int colorsAttributeID { -1 }; int texCoordsAttributeID { -1 }; - //int texCoords1AttributeID { -1 }; + int texCoords1AttributeID { -1 }; + int faceMaterialAttributeID { -1 }; const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32); - const int faceMaterialAttributeID = meshBuilder.AddAttribute( - (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, - 1, draco::DT_INT64); - if (hasNormals) { normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, 3, draco::DT_FLOAT32); @@ -337,13 +335,25 @@ void FBXBaker::rewriteAndBakeSceneModels() { texCoordsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::TEX_COORD, 2, draco::DT_FLOAT32); } + if (hasTexCoords1) { + texCoords1AttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_TEX_COORD_1, + 2, draco::DT_FLOAT32); + } + if (hasPerFaceMaterials) { + faceMaterialAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, + 2, draco::DT_INT64); + } + auto partIndex = 0; for (auto& part : mesh.parts) { //Q_ASSERT(part.quadTrianglesIndices % 3 == 0); //Q_ASSERT(part.triangleIndices % 3 == 0); - int64_t materialID = 0; + const auto matTex = extractedMesh.partMaterialTextures[partIndex]; + const int64_t matTexData[2] = { matTex.first, matTex.second }; for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { auto idx0 = part.quadTrianglesIndices[i]; @@ -352,7 +362,9 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto face = draco::FaceIndex(i / 3); - meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); + if (hasPerFaceMaterials) { + meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &matTexData); + } meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, &mesh.vertices[idx0], &mesh.vertices[idx1], @@ -373,7 +385,14 @@ void FBXBaker::rewriteAndBakeSceneModels() { &mesh.texCoords[idx0], &mesh.texCoords[idx1], &mesh.texCoords[idx2]); } + if (hasTexCoords1) { + meshBuilder.SetAttributeValuesForFace(texCoords1AttributeID, face, + &mesh.texCoords1[idx0], &mesh.texCoords1[idx1], + &mesh.texCoords1[idx2]); + } } + + partIndex++; } auto dracoMesh = meshBuilder.Finalize(); From b1d0df06d98b4348d37271e11f353a4641b2de39 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 11 Sep 2017 12:07:41 -0700 Subject: [PATCH 085/202] Add error handling in FBXBaker for empty nodes and failed draco compression --- libraries/baking/src/FBXBaker.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index b5cd94cec7..30c9510a52 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -304,6 +304,11 @@ void FBXBaker::rewriteAndBakeSceneModels() { numTriangles += part.triangleIndices.size() / 3; } + if (numTriangles == 0) { + qDebug() << "Skipping compression of mesh because no triangles were found"; + continue; + } + draco::TriangleSoupMeshBuilder meshBuilder; meshBuilder.Start(numTriangles); @@ -397,6 +402,11 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto dracoMesh = meshBuilder.Finalize(); + if (!dracoMesh) { + qWarning() << "Failed to finalize the baking of a draco Geometry node"; + continue; + } + draco::Encoder encoder; draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); From 1b2ba3acb686792a2a34a07a1579c98f80eaed4b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 11 Sep 2017 12:07:12 -0700 Subject: [PATCH 086/202] force libc++ for OS X draco build --- cmake/externals/draco/CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 03a6622a1b..41af6646db 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -4,12 +4,14 @@ if (ANDROID) set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") endif () +set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) + include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/draco-master-c8b6219.zip - URL_MD5 d60ed5fb2e1792445078f931875e3ee1 - CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= + URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.0.1.zip + URL_MD5 f1826d5ba1ffd413311d78346f4c114b + CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= ${EXTRA_CMAKE_FLAGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 @@ -22,7 +24,7 @@ ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -if (UNIX) +if (UNIX AND NOT APPLE) set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") else () set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories") From 8fc8b8100d31bff229560ee7efbbef6b3c494c5c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 09:44:44 -0700 Subject: [PATCH 087/202] Fix FBXBaker failing on bad mesh data and draco meshes that can't be encoded --- libraries/baking/src/FBXBaker.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 30c9510a52..45f3836dfc 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -297,15 +297,16 @@ void FBXBaker::rewriteAndBakeSceneModels() { int64_t numTriangles { 0 }; for (auto& part : mesh.parts) { - Q_ASSERT(part.quadTrianglesIndices.size() % 3 == 0); - Q_ASSERT(part.triangleIndices.size() % 3 == 0); - + if ((part.quadTrianglesIndices.size() % 3) != 0 || (part.triangleIndices.size() % 3) != 0) { + handleWarning("Found a mesh part with invalid index data, skipping"); + continue; + } numTriangles += part.quadTrianglesIndices.size() / 3; numTriangles += part.triangleIndices.size() / 3; } if (numTriangles == 0) { - qDebug() << "Skipping compression of mesh because no triangles were found"; + handleWarning("Skipping compression of mesh because no triangles were found"); continue; } @@ -317,7 +318,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { bool hasColors { mesh.colors.size() > 0 }; bool hasTexCoords { mesh.texCoords.size() > 0 }; bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; - bool hasPerFaceMaterials { mesh.parts.size() > 0 }; + bool hasPerFaceMaterials { mesh.parts.size() > 1 }; int normalsAttributeID { -1 }; int colorsAttributeID { -1 }; @@ -403,7 +404,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto dracoMesh = meshBuilder.Finalize(); if (!dracoMesh) { - qWarning() << "Failed to finalize the baking of a draco Geometry node"; + handleWarning("Failed to finalize the baking of a draco Geometry node"); continue; } From 2d9d4322215e3953fa8c66296699a0cf5b3018f4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 09:45:24 -0700 Subject: [PATCH 088/202] Update FBXBaker to remove unused nodes from the Geometry node --- libraries/baking/src/FBXBaker.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 45f3836dfc..760141f029 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -428,6 +428,34 @@ void FBXBaker::rewriteAndBakeSceneModels() { } objectChild.children.push_back(dracoMeshNode); + + static const std::vector nodeNamesToDelete { + // Node data that is packed into the draco mesh + "Vertices", + "PolygonVertexIndex", + "LayerElementNormal", + "LayerElementColor", + "LayerElementUV", + "LayerElementMaterial", + "LayerElementTexture", + + // Node data that we don't support + "Edges", + "LayerElementTangent", + "LayerElementBinormal", + "LayerElementSmoothing" + }; + auto& children = objectChild.children; + auto it = children.begin(); + while (it != children.end()) { + auto begin = nodeNamesToDelete.begin(); + auto end = nodeNamesToDelete.end(); + if (find(begin, end, it->name) != end) { + it = children.erase(it); + } else { + ++it; + } + } } } } From 9243cf75907677f7a92b39c8f4c1aea4e5d659c0 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 09:45:46 -0700 Subject: [PATCH 089/202] Remove extraneous logging in FBXWriter --- libraries/fbx/src/FBXWriter.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index 9f389fca88..c14e383402 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -63,12 +63,6 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { out << (quint8)node.name.size(); out.writeRawData(node.name, node.name.size()); - if (node.name == "Vertices") { - for (auto& prop : node.properties) { - qDebug() << "Properties: " << prop; - } - } - auto nodePropertiesStartPos = device->pos(); for (const auto& prop : node.properties) { From f9cc82c992a3703d8e51ff2ebbb56154a741b2e1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 11 Sep 2017 18:09:51 -0700 Subject: [PATCH 090/202] use draco 1.1.0 in external --- cmake/externals/draco/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 41af6646db..07ce861820 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -9,8 +9,8 @@ set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS= include(ExternalProject) ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.0.1.zip - URL_MD5 f1826d5ba1ffd413311d78346f4c114b + URL http://hifi-public.s3.amazonaws.com/dependencies/draco-1.1.0.zip + URL_MD5 208f8b04c91d5f1c73d731a3ea37c5bb CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= ${EXTRA_CMAKE_FLAGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 From 2105f2da9210bb4206c95c235e38184bd95e364d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 11 Sep 2017 18:21:44 -0700 Subject: [PATCH 091/202] change unique ID for material and second tex coord --- libraries/baking/src/FBXBaker.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 760141f029..18e7dbbd76 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -408,6 +408,16 @@ void FBXBaker::rewriteAndBakeSceneModels() { continue; } + // we need to modify unique attribute IDs for custom attributes + // so the attributes are easily retrievable on the other side + if (hasPerFaceMaterials) { + dracoMesh->attribute(faceMaterialAttributeID)->set_unique_id(DRACO_ATTRIBUTE_MATERIAL_ID); + } + + if (hasTexCoords1) { + dracoMesh->attribute(texCoords1AttributeID)->set_unique_id(DRACO_ATTRIBUTE_TEX_COORD_1); + } + draco::Encoder encoder; draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); From 61314949ec1fa45dc5868f102a1445a71d21facb Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 12:54:45 -0700 Subject: [PATCH 092/202] add draco as external to FBX library --- libraries/fbx/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 3355ccd06e..7cead5aa4f 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -1,7 +1,10 @@ set(TARGET_NAME fbx) setup_hifi_library() - - link_hifi_libraries(shared model networking image) include_hifi_library_headers(gpu image) + +add_dependency_external_projects(draco) +find_package(Draco REQUIRED) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) From cf282dd3fc992d5f311c98294b6249ef634131a3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 13:42:45 -0700 Subject: [PATCH 093/202] add code to read draco mesh from FBX --- libraries/fbx/src/FBXReader_Mesh.cpp | 236 ++++++++++++++++++++------- 1 file changed, 178 insertions(+), 58 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 14f1c09b75..4af68b8878 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include #include @@ -168,11 +170,17 @@ void appendIndex(MeshData& data, QVector& indices, int index) { ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIndex) { MeshData data; data.extracted.mesh.meshIndex = meshIndex++; + QVector materials; QVector textures; + bool isMaterialPerPolygon = false; + static const QVariant BY_VERTICE = QByteArray("ByVertice"); static const QVariant INDEX_TO_DIRECT = QByteArray("IndexToDirect"); + + bool isDracoMesh = false; + foreach (const FBXNode& child, object.children) { if (child.name == "Vertices") { data.vertices = createVec3Vector(getDoubleVector(child)); @@ -319,70 +327,182 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn textures = getIntVector(subdata); } } - } - } + } else if (child.name == "DracoMesh") { + isDracoMesh = true; - bool isMultiMaterial = false; - if (isMaterialPerPolygon) { - isMultiMaterial = true; - } - // TODO: make excellent use of isMultiMaterial - Q_UNUSED(isMultiMaterial); + // load the draco mesh from the FBX and create a draco::Mesh + draco::Decoder decoder; + draco::DecoderBuffer decodedBuffer; + QByteArray dracoArray = child.properties.at(0).value(); + decodedBuffer.Init(dracoArray.data(), dracoArray.size()); - // convert the polygons to quads and triangles - int polygonIndex = 0; - QHash, int> materialTextureParts; - for (int beginIndex = 0; beginIndex < data.polygonIndices.size(); polygonIndex++) { - int endIndex = beginIndex; - while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0); + std::unique_ptr dracoMesh(new draco::Mesh()); + decoder.DecodeBufferToGeometry(&decodedBuffer, dracoMesh.get()); - QPair materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0, - (polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0); - int& partIndex = materialTextureParts[materialTexture]; - if (partIndex == 0) { - data.extracted.partMaterialTextures.append(materialTexture); - data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); - partIndex = data.extracted.mesh.parts.size(); - } - FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; - - if (endIndex - beginIndex == 4) { - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); + // read positions from draco mesh to extracted mesh + auto positionAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::POSITION); + if (positionAttribute) { + std::array positionValue; - int quadStartIndex = part.quadIndices.size() - 4; - int i0 = part.quadIndices[quadStartIndex + 0]; - int i1 = part.quadIndices[quadStartIndex + 1]; - int i2 = part.quadIndices[quadStartIndex + 2]; - int i3 = part.quadIndices[quadStartIndex + 3]; - - // Sam's recommended triangle slices - // Triangle tri1 = { v0, v1, v3 }; - // Triangle tri2 = { v1, v2, v3 }; - // NOTE: Random guy on the internet's recommended triangle slices - // Triangle tri1 = { v0, v1, v2 }; - // Triangle tri2 = { v2, v3, v0 }; - - part.quadTrianglesIndices.append(i0); - part.quadTrianglesIndices.append(i1); - part.quadTrianglesIndices.append(i3); - - part.quadTrianglesIndices.append(i1); - part.quadTrianglesIndices.append(i2); - part.quadTrianglesIndices.append(i3); - - } else { - for (int nextIndex = beginIndex + 1;; ) { - appendIndex(data, part.triangleIndices, beginIndex); - appendIndex(data, part.triangleIndices, nextIndex++); - appendIndex(data, part.triangleIndices, nextIndex); - if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) { - break; + for (draco::AttributeValueIndex i (0); i < positionAttribute->size(); ++i) { + positionAttribute->ConvertValue(i, &positionValue[0]); + data.extracted.mesh.vertices.append({ positionValue[0], positionValue[1], positionValue[2] }); } } - beginIndex = endIndex; + + // enumerate the faces from draco mesh to collect vertex indices + for (int i = 0; i < dracoMesh->num_faces() * 3; ++i) { + auto vertexIndex = dracoMesh->face(draco::FaceIndex(i / 3))[i % 3]; + auto mappedIndex = positionAttribute->mapped_index(vertexIndex).value(); + data.extracted.newIndices.insert(mappedIndex, mappedIndex); + } + + // read normals from draco mesh to extracted mesh + auto normalAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::NORMAL); + if (normalAttribute) { + std::array normalValue; + + for (draco::AttributeValueIndex i (0); i < normalAttribute->size(); ++i) { + normalAttribute->ConvertValue(i, &normalValue[0]); + data.extracted.mesh.normals.append({ normalValue[0], normalValue[1], normalValue[2] }); + } + } + + // read UVs from draco mesh to extracted mesh + auto texCoordAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::TEX_COORD); + if (texCoordAttribute) { + std::array texCoordValue; + + for (draco::AttributeValueIndex i (0); i < texCoordAttribute->size(); ++i) { + texCoordAttribute->ConvertValue(i, &texCoordValue[0]); + data.extracted.mesh.texCoords.append({ texCoordValue[0], texCoordValue[1] }); + } + } + + // some meshes have a second set of UVs, read those to extracted mesh + auto extraTexCoordAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_TEX_COORD_1); + if (extraTexCoordAttribute) { + std::array texCoordValue; + + for (draco::AttributeValueIndex i (0); i < extraTexCoordAttribute->size(); ++i) { + extraTexCoordAttribute->ConvertValue(i, &texCoordValue[0]); + data.extracted.mesh.texCoords1.append({ texCoordValue[0], texCoordValue[1] }); + } + } + + // read vertex colors from draco mesh to extracted mesh + auto colorAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::COLOR); + if (colorAttribute) { + std::array colorValue; + + for (draco::AttributeValueIndex i (0); i < colorAttribute->size(); ++i) { + colorAttribute->ConvertValue(i, &colorValue[0]); + data.extracted.mesh.colors.append({ colorValue[0], colorValue[1], colorValue[2] }); + } + } + + // read material ID and texture ID mappings into materials and texture vectors + auto matTexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID); + if (matTexAttribute) { + std::array matTexValue; + + for (draco::AttributeValueIndex i (0); i < matTexAttribute->size(); ++i) { + matTexAttribute->ConvertValue(i, &matTexValue[0]); + materials.append(matTexValue[0]); + textures.append(matTexValue[1]); + } + } + + // enumerate the faces and construct the extracted mesh + auto vertexIndices = data.extracted.newIndices.keys(); + QHash, int> materialTextureParts; + + for (auto i = 0; i < vertexIndices.size(); i += 3) { + // grab the material ID and texture ID for this face, if we have it + QPair materialTexture(materials.at(i), textures.at(i)); + + // grab or setup the FBXMeshPart for the part this face belongs to + int& partIndex = materialTextureParts[materialTexture]; + if (partIndex == 0) { + data.extracted.partMaterialTextures.append(materialTexture); + data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); + partIndex = data.extracted.mesh.parts.size() - 1; + } + + FBXMeshPart& part = data.extracted.mesh.parts[partIndex]; + + // give the mesh part its indices + part.triangleIndices.append({ vertexIndices[i], vertexIndices[i + 1], vertexIndices[i + 2]}); + } + + } + } + + // when we have a draco mesh, we've already built the extracted mesh, so we don't need to do the + // processing we do for normal meshes below + if (!isDracoMesh) { + bool isMultiMaterial = false; + if (isMaterialPerPolygon) { + isMultiMaterial = true; + } + // TODO: make excellent use of isMultiMaterial + Q_UNUSED(isMultiMaterial); + + // convert the polygons to quads and triangles + int polygonIndex = 0; + QHash, int> materialTextureParts; + for (int beginIndex = 0; beginIndex < data.polygonIndices.size(); polygonIndex++) { + int endIndex = beginIndex; + while (endIndex < data.polygonIndices.size() && data.polygonIndices.at(endIndex++) >= 0); + + QPair materialTexture((polygonIndex < materials.size()) ? materials.at(polygonIndex) : 0, + (polygonIndex < textures.size()) ? textures.at(polygonIndex) : 0); + int& partIndex = materialTextureParts[materialTexture]; + if (partIndex == 0) { + data.extracted.partMaterialTextures.append(materialTexture); + data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); + partIndex = data.extracted.mesh.parts.size(); + } + FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; + + if (endIndex - beginIndex == 4) { + appendIndex(data, part.quadIndices, beginIndex++); + appendIndex(data, part.quadIndices, beginIndex++); + appendIndex(data, part.quadIndices, beginIndex++); + appendIndex(data, part.quadIndices, beginIndex++); + + int quadStartIndex = part.quadIndices.size() - 4; + int i0 = part.quadIndices[quadStartIndex + 0]; + int i1 = part.quadIndices[quadStartIndex + 1]; + int i2 = part.quadIndices[quadStartIndex + 2]; + int i3 = part.quadIndices[quadStartIndex + 3]; + + // Sam's recommended triangle slices + // Triangle tri1 = { v0, v1, v3 }; + // Triangle tri2 = { v1, v2, v3 }; + // NOTE: Random guy on the internet's recommended triangle slices + // Triangle tri1 = { v0, v1, v2 }; + // Triangle tri2 = { v2, v3, v0 }; + + part.quadTrianglesIndices.append(i0); + part.quadTrianglesIndices.append(i1); + part.quadTrianglesIndices.append(i3); + + part.quadTrianglesIndices.append(i1); + part.quadTrianglesIndices.append(i2); + part.quadTrianglesIndices.append(i3); + + } else { + for (int nextIndex = beginIndex + 1;; ) { + appendIndex(data, part.triangleIndices, beginIndex); + appendIndex(data, part.triangleIndices, nextIndex++); + appendIndex(data, part.triangleIndices, nextIndex); + if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) { + break; + } + } + beginIndex = endIndex; + } } } From 5d9ed7836915b240e289ad6bccd9f249c830819c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 15:57:00 -0700 Subject: [PATCH 094/202] fixes for extracted mesh construction --- libraries/fbx/src/FBXReader_Mesh.cpp | 145 +++++++++++++-------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 4af68b8878..2d4e722227 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -339,87 +339,86 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::unique_ptr dracoMesh(new draco::Mesh()); decoder.DecodeBufferToGeometry(&decodedBuffer, dracoMesh.get()); - // read positions from draco mesh to extracted mesh + // prepare attributes for this mesh auto positionAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::POSITION); - if (positionAttribute) { - std::array positionValue; - - for (draco::AttributeValueIndex i (0); i < positionAttribute->size(); ++i) { - positionAttribute->ConvertValue(i, &positionValue[0]); - data.extracted.mesh.vertices.append({ positionValue[0], positionValue[1], positionValue[2] }); - } - } - - // enumerate the faces from draco mesh to collect vertex indices - for (int i = 0; i < dracoMesh->num_faces() * 3; ++i) { - auto vertexIndex = dracoMesh->face(draco::FaceIndex(i / 3))[i % 3]; - auto mappedIndex = positionAttribute->mapped_index(vertexIndex).value(); - data.extracted.newIndices.insert(mappedIndex, mappedIndex); - } - - // read normals from draco mesh to extracted mesh auto normalAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::NORMAL); - if (normalAttribute) { - std::array normalValue; - - for (draco::AttributeValueIndex i (0); i < normalAttribute->size(); ++i) { - normalAttribute->ConvertValue(i, &normalValue[0]); - data.extracted.mesh.normals.append({ normalValue[0], normalValue[1], normalValue[2] }); - } - } - - // read UVs from draco mesh to extracted mesh auto texCoordAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::TEX_COORD); - if (texCoordAttribute) { - std::array texCoordValue; - - for (draco::AttributeValueIndex i (0); i < texCoordAttribute->size(); ++i) { - texCoordAttribute->ConvertValue(i, &texCoordValue[0]); - data.extracted.mesh.texCoords.append({ texCoordValue[0], texCoordValue[1] }); - } - } - - // some meshes have a second set of UVs, read those to extracted mesh auto extraTexCoordAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_TEX_COORD_1); - if (extraTexCoordAttribute) { - std::array texCoordValue; - - for (draco::AttributeValueIndex i (0); i < extraTexCoordAttribute->size(); ++i) { - extraTexCoordAttribute->ConvertValue(i, &texCoordValue[0]); - data.extracted.mesh.texCoords1.append({ texCoordValue[0], texCoordValue[1] }); - } - } - - // read vertex colors from draco mesh to extracted mesh auto colorAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::COLOR); - if (colorAttribute) { - std::array colorValue; - - for (draco::AttributeValueIndex i (0); i < colorAttribute->size(); ++i) { - colorAttribute->ConvertValue(i, &colorValue[0]); - data.extracted.mesh.colors.append({ colorValue[0], colorValue[1], colorValue[2] }); - } - } - - // read material ID and texture ID mappings into materials and texture vectors auto matTexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID); - if (matTexAttribute) { - std::array matTexValue; - for (draco::AttributeValueIndex i (0); i < matTexAttribute->size(); ++i) { - matTexAttribute->ConvertValue(i, &matTexValue[0]); - materials.append(matTexValue[0]); - textures.append(matTexValue[1]); - } - } + // setup extracted mesh data structures given number of points + auto numVertices = dracoMesh->num_points(); - // enumerate the faces and construct the extracted mesh - auto vertexIndices = data.extracted.newIndices.keys(); QHash, int> materialTextureParts; - for (auto i = 0; i < vertexIndices.size(); i += 3) { + data.extracted.mesh.vertices.reserve(numVertices); + data.extracted.mesh.normals.reserve(numVertices); + data.extracted.mesh.texCoords.reserve(numVertices); + data.extracted.mesh.texCoords1.reserve(numVertices); + data.extracted.mesh.colors.reserve(numVertices); + + // enumerate the vertices and construct the extracted mesh + for (int i = 0; i < numVertices; ++i) { + draco::PointIndex vertexIndex(i); + + if (positionAttribute) { + // read position from draco mesh to extracted mesh + auto mappedIndex = positionAttribute->mapped_index(vertexIndex); + + std::array positionValue; + positionAttribute->ConvertValue(mappedIndex, &positionValue[0]); + data.extracted.mesh.vertices.append({ positionValue[0], positionValue[1], positionValue[2] }); + } + + if (normalAttribute) { + // read normals from draco mesh to extracted mesh + auto mappedIndex = normalAttribute->mapped_index(vertexIndex); + + std::array normalValue; + normalAttribute->ConvertValue(mappedIndex, &normalValue[0]); + data.extracted.mesh.normals.append({ normalValue[0], normalValue[1], normalValue[2] }); + } + + if (texCoordAttribute) { + // read UVs from draco mesh to extracted mesh + auto mappedIndex = texCoordAttribute->mapped_index(vertexIndex); + + std::array texCoordValue; + texCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); + data.extracted.mesh.texCoords.append({ texCoordValue[0], texCoordValue[1] }); + } + + if (extraTexCoordAttribute) { + // some meshes have a second set of UVs, read those to extracted mesh + auto mappedIndex = extraTexCoordAttribute->mapped_index(vertexIndex); + + std::array texCoordValue; + extraTexCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); + data.extracted.mesh.texCoords1.append({ texCoordValue[0], texCoordValue[1] }); + } + + if (colorAttribute) { + // read vertex colors from draco mesh to extracted mesh + auto mappedIndex = colorAttribute->mapped_index(vertexIndex); + + std::array colorValue; + + colorAttribute->ConvertValue(mappedIndex, &colorValue[0]); + data.extracted.mesh.colors.append({ colorValue[0], colorValue[1], colorValue[2] }); + } + + int64_t matTexValue[2] = { 0, 0 }; + + if (matTexAttribute) { + // read material ID and texture ID mappings into materials and texture vectors + auto mappedIndex = matTexAttribute->mapped_index(vertexIndex); + + matTexAttribute->ConvertValue(mappedIndex, &matTexValue[0]); + } + // grab the material ID and texture ID for this face, if we have it - QPair materialTexture(materials.at(i), textures.at(i)); + QPair materialTexture(matTexValue[0], matTexValue[1]); // grab or setup the FBXMeshPart for the part this face belongs to int& partIndex = materialTextureParts[materialTexture]; @@ -429,12 +428,12 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn partIndex = data.extracted.mesh.parts.size() - 1; } + // give the mesh part this index FBXMeshPart& part = data.extracted.mesh.parts[partIndex]; + part.triangleIndices.append(i); - // give the mesh part its indices - part.triangleIndices.append({ vertexIndices[i], vertexIndices[i + 1], vertexIndices[i + 2]}); + data.extracted.newIndices.insert(i, i); } - } } From bb7cd58e9a12dd3a04975b8decfd627a6f987c47 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 16:28:12 -0700 Subject: [PATCH 095/202] fix reference to part index --- libraries/fbx/src/FBXReader_Mesh.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 2d4e722227..30167d9eb5 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -421,15 +421,15 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QPair materialTexture(matTexValue[0], matTexValue[1]); // grab or setup the FBXMeshPart for the part this face belongs to - int& partIndex = materialTextureParts[materialTexture]; - if (partIndex == 0) { + int& partIndexPlusOne = materialTextureParts[materialTexture]; + if (partIndexPlusOne == 0) { data.extracted.partMaterialTextures.append(materialTexture); data.extracted.mesh.parts.resize(data.extracted.mesh.parts.size() + 1); - partIndex = data.extracted.mesh.parts.size() - 1; + partIndexPlusOne = data.extracted.mesh.parts.size(); } // give the mesh part this index - FBXMeshPart& part = data.extracted.mesh.parts[partIndex]; + FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; part.triangleIndices.append(i); data.extracted.newIndices.insert(i, i); From a25e5796dc2d40fbf6850dc51ae125154e7f39bc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 16:35:21 -0700 Subject: [PATCH 096/202] use push_back instead of append for extracted mesh vectors --- libraries/fbx/src/FBXReader_Mesh.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 30167d9eb5..2ff93545ff 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -368,7 +368,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array positionValue; positionAttribute->ConvertValue(mappedIndex, &positionValue[0]); - data.extracted.mesh.vertices.append({ positionValue[0], positionValue[1], positionValue[2] }); + data.extracted.mesh.vertices.push_back({ positionValue[0], positionValue[1], positionValue[2] }); } if (normalAttribute) { @@ -377,7 +377,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array normalValue; normalAttribute->ConvertValue(mappedIndex, &normalValue[0]); - data.extracted.mesh.normals.append({ normalValue[0], normalValue[1], normalValue[2] }); + data.extracted.mesh.normals.push_back({ normalValue[0], normalValue[1], normalValue[2] }); } if (texCoordAttribute) { @@ -386,7 +386,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array texCoordValue; texCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); - data.extracted.mesh.texCoords.append({ texCoordValue[0], texCoordValue[1] }); + data.extracted.mesh.texCoords.push_back({ texCoordValue[0], texCoordValue[1] }); } if (extraTexCoordAttribute) { @@ -395,7 +395,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array texCoordValue; extraTexCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); - data.extracted.mesh.texCoords1.append({ texCoordValue[0], texCoordValue[1] }); + data.extracted.mesh.texCoords1.push_back({ texCoordValue[0], texCoordValue[1] }); } if (colorAttribute) { @@ -405,7 +405,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn std::array colorValue; colorAttribute->ConvertValue(mappedIndex, &colorValue[0]); - data.extracted.mesh.colors.append({ colorValue[0], colorValue[1], colorValue[2] }); + data.extracted.mesh.colors.push_back({ colorValue[0], colorValue[1], colorValue[2] }); } int64_t matTexValue[2] = { 0, 0 }; From b253d3b57c8f59a39729aa3e14934368f86ccb8d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 17:14:48 -0700 Subject: [PATCH 097/202] fix material triangle index insertion --- libraries/fbx/src/FBXReader_Mesh.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 2ff93545ff..f1524b1a81 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -408,16 +408,22 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn data.extracted.mesh.colors.push_back({ colorValue[0], colorValue[1], colorValue[2] }); } + data.extracted.newIndices.insert(i, i); + } + + for (int i = 0; i < dracoMesh->num_faces(); ++i) { + // grab the material ID and texture ID for this face, if we have it + auto firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; + int64_t matTexValue[2] = { 0, 0 }; if (matTexAttribute) { // read material ID and texture ID mappings into materials and texture vectors - auto mappedIndex = matTexAttribute->mapped_index(vertexIndex); + auto mappedIndex = matTexAttribute->mapped_index(firstCorner); matTexAttribute->ConvertValue(mappedIndex, &matTexValue[0]); } - // grab the material ID and texture ID for this face, if we have it QPair materialTexture(matTexValue[0], matTexValue[1]); // grab or setup the FBXMeshPart for the part this face belongs to @@ -430,9 +436,9 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn // give the mesh part this index FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; - part.triangleIndices.append(i); - - data.extracted.newIndices.insert(i, i); + part.triangleIndices.append(firstCorner.value()); + part.triangleIndices.append(dracoMesh->face(draco::FaceIndex(i))[1].value()); + part.triangleIndices.append(dracoMesh->face(draco::FaceIndex(i))[2].value()); } } } From c9024f5e8754c0e22bef7ab2c527f7857815d946 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 19:51:49 -0700 Subject: [PATCH 098/202] Update FBXBaker to bake triangleIndices and fix faces being lost --- libraries/baking/src/FBXBaker.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 18e7dbbd76..3407579e9e 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -354,19 +354,15 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto partIndex = 0; + draco::FaceIndex face; for (auto& part : mesh.parts) { - //Q_ASSERT(part.quadTrianglesIndices % 3 == 0); - //Q_ASSERT(part.triangleIndices % 3 == 0); - const auto matTex = extractedMesh.partMaterialTextures[partIndex]; const int64_t matTexData[2] = { matTex.first, matTex.second }; - for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { - auto idx0 = part.quadTrianglesIndices[i]; - auto idx1 = part.quadTrianglesIndices[i + 1]; - auto idx2 = part.quadTrianglesIndices[i + 2]; - - auto face = draco::FaceIndex(i / 3); + auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { + auto idx0 = indices[index]; + auto idx1 = indices[index + 1]; + auto idx2 = indices[index + 2]; if (hasPerFaceMaterials) { meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &matTexData); @@ -378,7 +374,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { if (hasNormals) { meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, - &mesh.normals[idx0],&mesh.normals[idx1], + &mesh.normals[idx0], &mesh.normals[idx1], &mesh.normals[idx2]); } if (hasColors) { @@ -396,6 +392,13 @@ void FBXBaker::rewriteAndBakeSceneModels() { &mesh.texCoords1[idx0], &mesh.texCoords1[idx1], &mesh.texCoords1[idx2]); } + }; + + for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { + addFace(part.quadTrianglesIndices, i, face++); + } + for (int i = 0; (i + 2) < part.triangleIndices.size(); i += 3) { + addFace(part.triangleIndices, i, face++); } partIndex++; From 88b8fb4c5ecce95f298737ff731013e75667870e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 20:02:27 -0700 Subject: [PATCH 099/202] use general worker threads for fbx baking --- libraries/baking/src/FBXBaker.cpp | 1 + tools/oven/src/BakerCLI.cpp | 4 ++-- tools/oven/src/DomainBaker.cpp | 2 +- tools/oven/src/Oven.cpp | 26 +------------------------- tools/oven/src/Oven.h | 2 -- tools/oven/src/ui/ModelBakeWidget.cpp | 2 +- 6 files changed, 6 insertions(+), 31 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 3407579e9e..52c63c26d0 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -397,6 +397,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { for (int i = 0; (i + 2) < part.quadTrianglesIndices.size(); i += 3) { addFace(part.quadTrianglesIndices, i, face++); } + for (int i = 0; (i + 2) < part.triangleIndices.size(); i += 3) { addFace(part.triangleIndices, i, face++); } diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index 7b65f8bbf0..5ab995be95 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -42,7 +42,7 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString outputPath) { // create our appropiate baker if (isFBX) { _baker = std::unique_ptr { new FBXBaker(inputUrl, []() -> QThread* { return qApp->getNextWorkerThread(); }, outputPath) }; - _baker->moveToThread(qApp->getFBXBakerThread()); + _baker->moveToThread(qApp->getNextWorkerThread()); } else if (isSupportedImage) { _baker = std::unique_ptr { new TextureBaker(inputUrl, image::TextureUsage::CUBE_TEXTURE, outputPath) }; _baker->moveToThread(qApp->getNextWorkerThread()); @@ -61,4 +61,4 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString outputPath) { void BakerCLI::handleFinishedBaker() { qCDebug(model_baking) << "Finished baking file."; QApplication::exit(_baker.get()->hasErrors()); -} \ No newline at end of file +} diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 5dd7c20b2e..535d9a49a9 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -214,7 +214,7 @@ void DomainBaker::enumerateEntities() { // move the baker to the baker thread // and kickoff the bake - baker->moveToThread(qApp->getFBXBakerThread()); + baker->moveToThread(qApp->getNextWorkerThread()); QMetaObject::invokeMethod(baker.data(), "bake"); // keep track of the total number of baking entities diff --git a/tools/oven/src/Oven.cpp b/tools/oven/src/Oven.cpp index c9d3b4e5f0..d91206a592 100644 --- a/tools/oven/src/Oven.cpp +++ b/tools/oven/src/Oven.cpp @@ -51,11 +51,7 @@ Oven::Oven(int argc, char* argv[]) : image::setCubeTexturesCompressionEnabled(true); // setup our worker threads - setupWorkerThreads(QThread::idealThreadCount() - 1); - - // Autodesk's SDK means that we need a single thread for all FBX importing/exporting in the same process - // setup the FBX Baker thread - setupFBXBakerThread(); + setupWorkerThreads(QThread::idealThreadCount()); // check if we were passed any command line arguments that would tell us just to run without the GUI if (parser.isSet(CLI_INPUT_PARAMETER) || parser.isSet(CLI_OUTPUT_PARAMETER)) { @@ -81,10 +77,6 @@ Oven::~Oven() { _workerThreads[i]->quit(); _workerThreads[i]->wait(); } - - // cleanup the FBX Baker thread - _fbxBakerThread->quit(); - _fbxBakerThread->wait(); } void Oven::setupWorkerThreads(int numWorkerThreads) { @@ -97,22 +89,6 @@ void Oven::setupWorkerThreads(int numWorkerThreads) { } } -void Oven::setupFBXBakerThread() { - // we're being asked for the FBX baker thread, but we don't have one yet - // so set that up now - _fbxBakerThread = new QThread(this); - _fbxBakerThread->setObjectName("Oven FBX Baker Thread"); -} - -QThread* Oven::getFBXBakerThread() { - if (!_fbxBakerThread->isRunning()) { - // start the FBX baker thread if it isn't running yet - _fbxBakerThread->start(); - } - - return _fbxBakerThread; -} - QThread* Oven::getNextWorkerThread() { // Here we replicate some of the functionality of QThreadPool by giving callers an available worker thread to use. // We can't use QThreadPool because we want to put QObjects with signals/slots on these threads. diff --git a/tools/oven/src/Oven.h b/tools/oven/src/Oven.h index 569b73a3e2..928ba4eb11 100644 --- a/tools/oven/src/Oven.h +++ b/tools/oven/src/Oven.h @@ -34,7 +34,6 @@ public: OvenMainWindow* getMainWindow() const { return _mainWindow; } - QThread* getFBXBakerThread(); QThread* getNextWorkerThread(); private: @@ -42,7 +41,6 @@ private: void setupFBXBakerThread(); OvenMainWindow* _mainWindow; - QThread* _fbxBakerThread; QList _workerThreads; std::atomic _nextWorkerThreadIndex; diff --git a/tools/oven/src/ui/ModelBakeWidget.cpp b/tools/oven/src/ui/ModelBakeWidget.cpp index 926de8bae0..7829993740 100644 --- a/tools/oven/src/ui/ModelBakeWidget.cpp +++ b/tools/oven/src/ui/ModelBakeWidget.cpp @@ -201,7 +201,7 @@ void ModelBakeWidget::bakeButtonClicked() { }; // move the baker to the FBX baker thread - baker->moveToThread(qApp->getFBXBakerThread()); + baker->moveToThread(qApp->getNextWorkerThread()); // invoke the bake method on the baker thread QMetaObject::invokeMethod(baker.get(), "bake"); From c445d914d7d4704edd7bcbad2716b7144bdbbfce Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 21:59:46 -0700 Subject: [PATCH 100/202] make sure material IDs are de-duplicated --- libraries/baking/src/FBXBaker.cpp | 6 +++--- libraries/fbx/src/FBXReader_Mesh.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 52c63c26d0..271a25e9dd 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -349,7 +349,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { if (hasPerFaceMaterials) { faceMaterialAttributeID = meshBuilder.AddAttribute( (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_MATERIAL_ID, - 2, draco::DT_INT64); + 1, draco::DT_UINT16); } @@ -357,7 +357,6 @@ void FBXBaker::rewriteAndBakeSceneModels() { draco::FaceIndex face; for (auto& part : mesh.parts) { const auto matTex = extractedMesh.partMaterialTextures[partIndex]; - const int64_t matTexData[2] = { matTex.first, matTex.second }; auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { auto idx0 = indices[index]; @@ -365,7 +364,8 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto idx2 = indices[index + 2]; if (hasPerFaceMaterials) { - meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &matTexData); + uint16_t materialID = matTex.first; + meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); } meshBuilder.SetAttributeValuesForFace(positionAttributeID, face, diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index f1524b1a81..323ecac37b 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -415,16 +415,16 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn // grab the material ID and texture ID for this face, if we have it auto firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; - int64_t matTexValue[2] = { 0, 0 }; + uint16_t materialID { 0 }; if (matTexAttribute) { // read material ID and texture ID mappings into materials and texture vectors auto mappedIndex = matTexAttribute->mapped_index(firstCorner); - matTexAttribute->ConvertValue(mappedIndex, &matTexValue[0]); + matTexAttribute->ConvertValue(mappedIndex, &materialID); } - QPair materialTexture(matTexValue[0], matTexValue[1]); + QPair materialTexture(materialID, 0); // grab or setup the FBXMeshPart for the part this face belongs to int& partIndexPlusOne = materialTextureParts[materialTexture]; From 087a95a6259c122dc0a8c543c26e0f13a54db3af Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 12 Sep 2017 21:32:37 -0700 Subject: [PATCH 101/202] Add explicit draco encoder options to FBXBaker --- libraries/baking/src/FBXBaker.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 271a25e9dd..1abd411e29 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -423,6 +423,15 @@ void FBXBaker::rewriteAndBakeSceneModels() { } draco::Encoder encoder; + + encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, + 14); + encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, + 12); + encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, + 10); + encoder.SetSpeedOptions(0, 0); + draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); From 4efd9b845379f2a1cf19a4eebb45505a40cbfa69 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 12 Sep 2017 23:45:32 -0700 Subject: [PATCH 102/202] set explicit draco encoding options --- libraries/baking/src/FBXBaker.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 1abd411e29..29e5dc6a81 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -424,13 +424,10 @@ void FBXBaker::rewriteAndBakeSceneModels() { draco::Encoder encoder; - encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, - 14); - encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, - 12); - encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, - 10); - encoder.SetSpeedOptions(0, 0); + encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14); + encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, 12); + encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, 10); + encoder.SetSpeedOptions(0, 5); draco::EncoderBuffer buffer; encoder.EncodeMeshToBuffer(*dracoMesh, &buffer); From 8a9e4029ae935e08cc8c6ce50400c51cf7ff3fee Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 00:23:52 -0700 Subject: [PATCH 103/202] use explicit conversion from size_t to int --- libraries/baking/src/FBXBaker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 29e5dc6a81..79a5b9157b 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -434,7 +434,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { FBXNode dracoMeshNode; dracoMeshNode.name = "DracoMesh"; - auto value = QVariant::fromValue(QByteArray(buffer.data(), buffer.size())); + auto value = QVariant::fromValue(QByteArray(buffer.data(), (int) buffer.size())); dracoMeshNode.properties.append(value); @@ -653,13 +653,13 @@ void FBXBaker::handleBakedTexture() { } else { // there was an error baking this texture - add it to our list of errors _errorList.append(bakedTexture->getErrors()); - + // we don't emit finished yet so that the other textures can finish baking first _pendingErrorEmission = true; - + // now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list _bakingTextures.remove(bakedTexture->getTextureURL()); - + checkIfTexturesFinished(); } } else { From dc86c6fe73584fd7165194f37c619d1773d593a4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 08:37:36 -0700 Subject: [PATCH 104/202] fixes for warnings in FBXReader/FBXWriter --- libraries/baking/CMakeLists.txt | 2 +- libraries/fbx/CMakeLists.txt | 2 +- libraries/fbx/src/FBXReader.cpp | 6 +++--- libraries/fbx/src/FBXWriter.cpp | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 66cf791776..2304a5e0c0 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -6,5 +6,5 @@ include_hifi_library_headers(gpu) add_dependency_external_projects(draco) find_package(Draco REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +include_directories(SYSTEM ${DRACO_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 7cead5aa4f..4c81f10302 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -6,5 +6,5 @@ include_hifi_library_headers(gpu image) add_dependency_external_projects(draco) find_package(Draco REQUIRED) -target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) +include_directories(SYSTEM ${DRACO_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index d4a58a1126..e4fea00a34 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -169,7 +169,7 @@ QString getID(const QVariantList& properties, int index = 0) { } /// The names of the joints in the Maya HumanIK rig -static const std::array HUMANIK_JOINTS = { +static const std::array HUMANIK_JOINTS = {{ "RightHand", "RightForeArm", "RightArm", @@ -186,7 +186,7 @@ static const std::array HUMANIK_JOINTS = { "LeftLeg", "RightFoot", "LeftFoot" -}; +}}; class FBXModel { public: @@ -512,7 +512,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QVector humanIKJointNames; - for (int i = 0; i < HUMANIK_JOINTS.size(); i++) { + for (int i = 0; i < (int) HUMANIK_JOINTS.size(); i++) { QByteArray jointName = HUMANIK_JOINTS[i]; humanIKJointNames.append(processID(getString(joints.value(jointName, jointName)))); } diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index c14e383402..cc34696f92 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -143,7 +143,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { break; } - // TODO Delete? Do we ever use QList instead of QVector? + // TODO Delete? Do we ever use QList instead of QVector? case QVariant::Type::List: { auto list = prop.toList(); @@ -156,7 +156,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toFloat(); + out << innerProp.toFloat(); } break; @@ -166,7 +166,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toDouble(); + out << innerProp.toDouble(); } break; @@ -176,7 +176,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toLongLong(); + out << innerProp.toLongLong(); } break; @@ -186,7 +186,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toInt(); + out << innerProp.toInt(); } break; @@ -196,7 +196,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { out << (int32_t)0; out << (int32_t)0; for (auto& innerProp : list) { - out << prop.toBool(); + out << innerProp.toBool(); } break; } From 48e58febc6402651f0bcc36b03cb9fae684aac49 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 12 Sep 2017 12:56:23 -0700 Subject: [PATCH 105/202] Remove deprecated AutoBaker --- assignment-client/src/assets/AssetServer.h | 1 - assignment-client/src/assets/AutoBaker.cpp | 37 ---------------------- assignment-client/src/assets/AutoBaker.h | 32 ------------------- 3 files changed, 70 deletions(-) delete mode 100644 assignment-client/src/assets/AutoBaker.cpp delete mode 100644 assignment-client/src/assets/AutoBaker.h diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 907726e1ab..4a36825764 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -19,7 +19,6 @@ #include #include "AssetUtils.h" -#include "AutoBaker.h" #include "ReceivedMessage.h" diff --git a/assignment-client/src/assets/AutoBaker.cpp b/assignment-client/src/assets/AutoBaker.cpp deleted file mode 100644 index 38c564425b..0000000000 --- a/assignment-client/src/assets/AutoBaker.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// AutoBaker.cpp -// assignment-client/src/assets -// -// Created by Clement Brisset on 8/9/17 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "AutoBaker.h" -#include - -using namespace alg; - -void AutoBaker::addPendingBake(AssetHash hash) { - _pendingBakes.push_back(hash); - - // Maybe start baking it right away -} - -bool AutoBaker::assetNeedsBaking(AssetPath path, AssetHash hash) { - return path.endsWith(".fbx"); -} - -BakingStatus AutoBaker::getAssetStatus(AssetHash hash) { - if (find(_pendingBakes, hash) != std::end(_pendingBakes)) { - return Pending; - } - - if (find(_currentlyBaking, hash) != std::end(_currentlyBaking)) { - return Baking; - } - - return NotBaked; -} diff --git a/assignment-client/src/assets/AutoBaker.h b/assignment-client/src/assets/AutoBaker.h deleted file mode 100644 index 258fb15931..0000000000 --- a/assignment-client/src/assets/AutoBaker.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// AutoBaker.h -// assignment-client/src/assets -// -// Created by Clement Brisset on 8/9/17 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AutoBaker_h -#define hifi_AutoBaker_h - -#include - -#include "AssetUtils.h" - -class AutoBaker { -public: - void addPendingBake(AssetHash hash); - - bool assetNeedsBaking(AssetPath path, AssetHash hash); - - BakingStatus getAssetStatus(AssetHash hash); - -private: - std::vector _pendingBakes; - std::vector _currentlyBaking; -}; - -#endif /* hifi_AutoBaker_h */ From 7d9daea0f6346a6274fb3654312aea1249b59228 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 12 Sep 2017 13:43:00 -0700 Subject: [PATCH 106/202] Transmit baking errors to interface for display. --- assignment-client/src/assets/AssetServer.cpp | 56 ++++++++++++------- assignment-client/src/assets/AssetServer.h | 7 ++- .../AssetMappingsScriptingInterface.cpp | 1 + libraries/networking/src/AssetUtils.h | 1 + libraries/networking/src/MappingRequest.cpp | 6 +- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index cee4c77170..6da299ea87 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -83,7 +83,8 @@ void BakeAssetTask::run() { if (baker->hasErrors()) { qDebug() << "Failed to bake: " << _assetHash << _assetPath << baker->getErrors(); - emit bakeFailed(_assetHash, _assetPath); + auto errors = baker->getErrors().join('\n'); // Join error list into a single string for convenience + emit bakeFailed(_assetHash, _assetPath, errors); } else { auto vectorOutputFiles = QVector::fromStdVector(baker->getOutputFiles()); qDebug() << "Finished baking: " << _assetHash << _assetPath << vectorOutputFiles; @@ -112,19 +113,19 @@ QString AssetServer::getPathToAssetHash(const AssetHash& assetHash) { return _filesDirectory.absoluteFilePath(assetHash); } -BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& hash) { +std::pair AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& hash) { auto it = _pendingBakes.find(hash); if (it != _pendingBakes.end()) { - return (*it)->isBaking() ? Baking : Pending; + return { (*it)->isBaking() ? Baking : Pending, "" }; } if (path.startsWith(HIDDEN_BAKED_CONTENT_FOLDER)) { - return Baked; + return { Baked, "" }; } auto dotIndex = path.lastIndexOf("."); if (dotIndex == -1) { - return Irrelevant; + return { Irrelevant, "" }; } auto extension = path.mid(dotIndex + 1); @@ -136,28 +137,28 @@ BakingStatus AssetServer::getAssetStatus(const AssetPath& path, const AssetHash& } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; } else { - return Irrelevant; + return { Irrelevant, "" }; } auto bakedPath = HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + bakedFilename; auto jt = _fileMappings.find(bakedPath); if (jt != _fileMappings.end()) { if (jt->second == hash) { - bool loaded; - AssetMeta meta; - - std::tie(loaded, meta) = readMetaFile(hash); - if (loaded && meta.failedLastBake) { - return Error; - } - - return NotBaked; + return { NotBaked, "" }; } else { - return Baked; + return { Baked, "" }; + } + } else { + bool loaded; + AssetMeta meta; + + std::tie(loaded, meta) = readMetaFile(hash); + if (loaded && meta.failedLastBake) { + return { Error, meta.lastBakeErrors }; } } - return Pending; + return { Pending, "" }; } void AssetServer::bakeAssets() { @@ -564,7 +565,14 @@ void AssetServer::handleGetAllMappingOperation(ReceivedMessage& message, SharedN auto hash = it->second; replyPacket.writeString(mapping); replyPacket.write(QByteArray::fromHex(hash.toUtf8())); - replyPacket.writePrimitive(getAssetStatus(mapping, hash)); + + BakingStatus status; + QString lastBakeErrors; + std::tie(status, lastBakeErrors) = getAssetStatus(mapping, hash); + replyPacket.writePrimitive(status); + if (status == Error) { + replyPacket.writeString(lastBakeErrors); + } } } @@ -1146,8 +1154,8 @@ QString getBakeMapping(const AssetHash& hash, const QString& relativeFilePath) { return HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath; } -void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath) { - qDebug() << "Failed: " << originalAssetHash << assetPath; +void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, QString errors) { + qDebug() << "Failed: " << originalAssetHash << assetPath << errors; bool loaded; AssetMeta meta; @@ -1155,6 +1163,7 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath) std::tie(loaded, meta) = readMetaFile(originalAssetHash); meta.failedLastBake = true; + meta.lastBakeErrors = errors; writeMetaFile(originalAssetHash, meta); } @@ -1239,6 +1248,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina static const QString BAKE_VERSION_KEY = "bake_version"; static const QString APP_VERSION_KEY = "app_version"; static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake"; +static const QString LAST_BAKE_ERRORS_KEY = "last_bake_errors"; std::pair AssetServer::readMetaFile(AssetHash hash) { auto metaFilePath = HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + "meta.json"; @@ -1265,15 +1275,18 @@ std::pair AssetServer::readMetaFile(AssetHash hash) { auto bakeVersion = root[BAKE_VERSION_KEY].toInt(-1); auto appVersion = root[APP_VERSION_KEY].toInt(-1); auto failedLastBake = root[FAILED_LAST_BAKE_KEY]; + auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY]; if (bakeVersion != -1 && appVersion != -1 - && failedLastBake.isBool()) { + && failedLastBake.isBool() + && lastBakeErrors.isString()) { AssetMeta meta; meta.bakeVersion = bakeVersion; meta.applicationVersion = appVersion; meta.failedLastBake = failedLastBake.toBool(); + meta.lastBakeErrors = failedLastBake.toString(); return { true, meta }; } else { @@ -1292,6 +1305,7 @@ bool AssetServer::writeMetaFile(AssetHash originalAssetHash, const AssetMeta& me metaFileObject[BAKE_VERSION_KEY] = meta.bakeVersion; metaFileObject[APP_VERSION_KEY] = meta.applicationVersion; metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake; + metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors; QJsonDocument metaFileDoc; metaFileDoc.setObject(metaFileObject); diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 4a36825764..8641f5498f 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -40,7 +40,7 @@ public: signals: void bakeComplete(QString assetHash, QString assetPath, QVector outputFiles); - void bakeFailed(QString assetHash, QString assetPath); + void bakeFailed(QString assetHash, QString assetPath, QString errors); private: std::atomic _isBaking { false }; @@ -56,6 +56,7 @@ struct AssetMeta { int bakeVersion { 0 }; int applicationVersion { 0 }; bool failedLastBake { false }; + QString lastBakeErrors; }; class AssetServer : public ThreadedAssignment { @@ -108,7 +109,7 @@ private: QString getPathToAssetHash(const AssetHash& assetHash); - BakingStatus getAssetStatus(const AssetPath& path, const AssetHash& hash); + std::pair getAssetStatus(const AssetPath& path, const AssetHash& hash); void bakeAssets(); void maybeBake(const AssetPath& path, const AssetHash& hash); @@ -119,7 +120,7 @@ private: /// Move baked content for asset to baked directory and update baked status void handleCompletedBake(QString originalAssetHash, QString assetPath, QVector bakedFilePaths); - void handleFailedBake(QString originalAssetHash, QString assetPath); + void handleFailedBake(QString originalAssetHash, QString assetPath, QString errors); /// Create meta file to describe baked content for original asset std::pair readMetaFile(AssetHash hash); diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 70deabd412..14e0ef5b28 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -261,6 +261,7 @@ void AssetMappingModel::refresh() { // update status auto statusString = isFolder ? "--" : bakingStatusToString(mapping.second.status); lastItem->setData(statusString, Qt::UserRole + 5); + lastItem->setData(mapping.second.bakingErrors, Qt::UserRole + 6); } Q_ASSERT(fullPath == path); diff --git a/libraries/networking/src/AssetUtils.h b/libraries/networking/src/AssetUtils.h index c09c725f22..a7c053c3d6 100644 --- a/libraries/networking/src/AssetUtils.h +++ b/libraries/networking/src/AssetUtils.h @@ -66,6 +66,7 @@ enum BakingStatus { struct MappingInfo { AssetHash hash; BakingStatus status; + QString bakingErrors; }; using AssetMapping = std::map; diff --git a/libraries/networking/src/MappingRequest.cpp b/libraries/networking/src/MappingRequest.cpp index 588d8ecc88..a79105e3ab 100644 --- a/libraries/networking/src/MappingRequest.cpp +++ b/libraries/networking/src/MappingRequest.cpp @@ -137,8 +137,12 @@ void GetAllMappingsRequest::doStart() { auto path = message->readString(); auto hash = message->read(SHA256_HASH_LENGTH).toHex(); BakingStatus status; + QString lastBakeErrors; message->readPrimitive(&status); - _mappings[path] = { hash, status }; + if (status == BakingStatus::Error) { + lastBakeErrors = message->readString(); + } + _mappings[path] = { hash, status, lastBakeErrors }; } } emit finished(this); From 944a4fd49d6737b2140046c0c1bac49f8a1d79b4 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 12 Sep 2017 13:47:52 -0700 Subject: [PATCH 107/202] Update hifi glyphs + font reference --- interface/resources/fonts/hifi-glyphs.ttf | Bin 29160 -> 29268 bytes .../fonts/hifi-glyphs/icons-reference.html | 1056 +++++++++++++++++ .../resources/fonts/hifi-glyphs/styles.css | 421 +++++++ 3 files changed, 1477 insertions(+) create mode 100644 interface/resources/fonts/hifi-glyphs/icons-reference.html create mode 100644 interface/resources/fonts/hifi-glyphs/styles.css diff --git a/interface/resources/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs.ttf index 8733349227646b207ec15315e9eab9ee7ae9e252..558562cec52168a9025db4b7c4dd20280a5a5d74 100644 GIT binary patch delta 641 zcmYL{Ur1AN6vsd3?zg+u)^xe1g4P6aQOw)|b&v*O3+&H?LL#blnakm}Tu#tl{Pz&_ zCfzsbAqslz5BJ3fjp!{9>LFjENUDdDf`odIh{e4QI)}sgoR{=pXKcrnMHs- z%;91vmAY_tYWwyeO!NXRrv^@@c!CZSO+X}l{zxLdawZ3I5g5*vjPjdnm0zH(fHpL0 zRLa#VE|{nXyraci*Zcpzyaaj(MwatAW5oZieF}IFbm#NASwnTn21p2K&zB~rTYJM# zLG1Du$Fs)Z{iBb-?FZ3I#&nsFauviD7`b7TazA@s48zzOh*ci1OfEh={0WM4;9hjh z3HUbm_hssi{l=$Z$NGn@)t#w6!`#{efga+ca0Q&F0Oqj%buSJ4#UacvWLJK7Q0#$0tGa z`B+=@97$WP+Jz#_vl_bzvtDDJFxzIP&}g<6GM+dIVkFJ^!XXt;#FFOYLdA>}+hoBy zQPf0}8Ec@FQR&^;d@fsvC1PE1K&n+V=j{O4?!cK{%p)jgvctYF*7_d2w)_8&-&*|# DhsuO) delta 504 zcmX|+OK1~u5XOJAFQeNRSQa0tiJ&d0@fENYv!F+w;@W$_t;HANQ)Cnh0N?bQ*`p^8t z)`;puBj4^$E#H1DXQipe<(8zvCz6sA(MFFu*|9(_pm!*VAxENYB + + + + + + Font Reference - HiFi Glyphs + + + + + +
+

HiFi Glyphs

+

This font was created inHigh Fidelity

+

CSS mapping

+
    +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • + + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • + + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
+

Character mapping

+
    +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
  • +
    + +
  • +
+
+ + + diff --git a/interface/resources/fonts/hifi-glyphs/styles.css b/interface/resources/fonts/hifi-glyphs/styles.css new file mode 100644 index 0000000000..66277740fc --- /dev/null +++ b/interface/resources/fonts/hifi-glyphs/styles.css @@ -0,0 +1,421 @@ +@charset "UTF-8"; + +@font-face { + font-family: "hifi-glyphs"; + src:url("fonts/hifi-glyphs.eot"); + src:url("fonts/hifi-glyphs.eot?#iefix") format("embedded-opentype"), + url("fonts/hifi-glyphs.woff") format("woff"), + url("fonts/hifi-glyphs.ttf") format("truetype"), + url("fonts/hifi-glyphs.svg#hifi-glyphs") format("svg"); + font-weight: normal; + font-style: normal; + +} + +[data-icon]:before { + font-family: "hifi-glyphs" !important; + content: attr(data-icon); + font-style: normal !important; + font-weight: normal !important; + font-variant: normal !important; + text-transform: none !important; + speak: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +[class^="icon-"]:before, +[class*=" icon-"]:before { + font-family: "hifi-glyphs" !important; + font-style: normal !important; + font-weight: normal !important; + font-variant: normal !important; + text-transform: none !important; + speak: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-hmd:before { + content: "\62"; +} +.icon-2d-screen:before { + content: "\63"; +} +.icon-keyboard:before { + content: "\64"; +} +.icon-hand-controllers:before { + content: "\65"; +} +.icon-headphones-mic:before { + content: "\66"; +} +.icon-gamepad:before { + content: "\67"; +} +.icon-headphones:before { + content: "\68"; +} +.icon-mic:before { + content: "\69"; +} +.icon-upload:before { + content: "\6a"; +} +.icon-script:before { + content: "\6b"; +} +.icon-text:before { + content: "\6c"; +} +.icon-cube:before { + content: "\6d"; +} +.icon-sphere:before { + content: "\6e"; +} +.icon-zone:before { + content: "\6f"; +} +.icon-light:before { + content: "\70"; +} +.icon-web:before { + content: "\71"; +} +.icon-web-2:before { + content: "\72"; +} +.icon-edit:before { + content: "\73"; +} +.icon-market:before { + content: "\74"; +} +.icon-directory:before { + content: "\75"; +} +.icon-menu:before { + content: "\76"; +} +.icon-close:before { + content: "\77"; +} +.icon-close-inverted:before { + content: "\78"; +} +.icon-pin:before { + content: "\79"; +} +.icon-pin-inverted:before { + content: "\7a"; +} +.icon-resize-handle:before { + content: "\41"; +} +.icon-diclosure-expand:before { + content: "\42"; +} +.icon-reload-small:before { + content: "\61"; +} +.icon-close-small:before { + content: "\43"; +} +.icon-backward:before { + content: "\45"; +} +.icon-reload:before { + content: "\46"; +} +.icon-minimize:before { + content: "\49"; +} +.icon-maximize:before { + content: "\4a"; +} +.icon-maximize-inverted:before { + content: "\4b"; +} +.icon-disclosure-button-expand:before { + content: "\4c"; +} +.icon-disclosure-button-collapse:before { + content: "\4d"; +} +.icon-script-stop:before { + content: "\4e"; +} +.icon-script-reload:before { + content: "\4f"; +} +.icon-script-run:before { + content: "\50"; +} +.icon-script-new:before { + content: "\51"; +} +.icon-hifi-forum:before { + content: "\32"; +} +.icon-hifi-logo-small:before { + content: "\53"; +} +.icon-avatar-1:before { + content: "\54"; +} +.icon-placemark:before { + content: "\55"; +} +.icon-box:before { + content: "\56"; +} +.icon-community:before { + content: "\30"; +} +.icon-grab-handle:before { + content: "\58"; +} +.icon-search:before { + content: "\59"; +} +.icon-disclosure-collapse:before { + content: "\5a"; +} +.icon-script-upload:before { + content: "\52"; +} +.icon-code:before { + content: "\57"; +} +.icon-avatar:before { + content: "\3c"; +} +.icon-arrows-h:before { + content: "\3a"; +} +.icon-arrows-v:before { + content: "\3b"; +} +.icon-arrows:before { + content: "\60"; +} +.icon-compress:before { + content: "\21"; +} +.icon-expand:before { + content: "\22"; +} +.icon-placemark-1:before { + content: "\23"; +} +.icon-circle:before { + content: "\24"; +} +.icon-hand-pointer:before { + content: "\39"; +} +.icon-plus-square-o:before { + content: "\25"; +} +.icon-square:before { + content: "\27"; +} +.icon-align-center:before { + content: "\38"; +} +.icon-align-justify:before { + content: "\29"; +} +.icon-align-left:before { + content: "\2a"; +} +.icon-align-right:before { + content: "\5e"; +} +.icon-bars:before { + content: "\37"; +} +.icon-circle-slash:before { + content: "\2c"; +} +.icon-sync:before { + content: "\28"; +} +.icon-key:before { + content: "\2d"; +} +.icon-link:before { + content: "\2e"; +} +.icon-location:before { + content: "\2f"; +} +.icon-carat-r:before { + content: "\33"; +} +.icon-carat-l:before { + content: "\34"; +} +.icon-folder-lg:before { + content: "\3e"; +} +.icon-folder-sm:before { + content: "\3f"; +} +.icon-level-up:before { + content: "\31"; +} +.icon-info:before { + content: "\5b"; +} +.icon-question:before { + content: "\5d"; +} +.icon-alert:before { + content: "\2b"; +} +.icon-home:before { + content: "\5f"; +} +.icon-error:before { + content: "\3d"; +} +.icon-settings:before { + content: "\40"; +} +.icon-trash:before { + content: "\7b"; +} +.icon-object-group:before { + content: "\e000"; +} +.icon-cm:before { + content: "\7d"; +} +.icon-msvg:before { + content: "\7e"; +} +.icon-deg:before { + content: "\5c"; +} +.icon-px:before { + content: "\7c"; +} +.icon-m-sq:before { + content: "\e001"; +} +.icon-m-cubed:before { + content: "\e002"; +} +.icon-acceleration:before { + content: "\e003"; +} +.icon-particles:before { + content: "\e004"; +} +.icon-voxels:before { + content: "\e005"; +} +.icon-lock:before { + content: "\e006"; +} +.icon-visible:before { + content: "\e007"; +} +.icon-model:before { + content: "\e008"; +} +.icon-forward:before { + content: "\44"; +} +.icon-avatar-2:before { + content: "\e009"; +} +.icon-arrow-dn:before { + content: "\35"; +} +.icon-arrow-up:before { + content: "\36"; +} +.icon-time:before { + content: "\e00a"; +} +.icon-transparency:before { + content: "\e00b"; +} +.icon-unmuted:before { + content: "\47"; +} +.icon-user:before { + content: "\e00c"; +} +.icon-edit-pencil:before { + content: "\e00d"; +} +.icon-muted:before { + content: "\48"; +} +.icon-vol-0:before { + content: "\e00e"; +} +.icon-vol-1:before { + content: "\e00f"; +} +.icon-vol-2:before { + content: "\e010"; +} +.icon-vol-3:before { + content: "\e011"; +} +.icon-vol-4:before { + content: "\e012"; +} +.icon-vol-x-0:before { + content: "\e013"; +} +.icon-vol-x-1:before { + content: "\e014"; +} +.icon-vol-x-2:before { + content: "\e015"; +} +.icon-vol-x-3:before { + content: "\e016"; +} +.icon-vol-x-4:before { + content: "\e017"; +} +.icon-share-ext:before { + content: "\e018"; +} +.icon-ellipsis:before { + content: "\e019"; +} +.icon-check:before { + content: "\e01a"; +} +.icon-sliders:before { + content: "\26"; +} +.icon-polyline:before { + content: "\e01b"; +} +.icon-source:before { + content: "\e01c"; +} +.icon-playback-play:before { + content: "\e01d"; +} +.icon-stop-square:before { + content: "\e01e"; +} +.icon-avatar-t-pose:before { + content: "\e01f"; +} +.icon-check-2-01:before { + content: "\e020"; +} From 9f2b0258e95829ad7a70a3127197fe95f6379339 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 12 Sep 2017 14:10:17 -0700 Subject: [PATCH 108/202] Update baking status with the correct checkmark glyph --- interface/resources/qml/AssetServer.qml | 2 +- interface/resources/qml/styles-uit/HifiConstants.qml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 5e564f8626..44b85b0898 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -561,7 +561,7 @@ ScrollingWindow { case "Not Baked": return hifi.glyphs.circleSlash; case "Baked": - return hifi.glyphs.placemark; + return hifi.glyphs.check_2_01; case "Error": return hifi.glyphs.alert; default: diff --git a/interface/resources/qml/styles-uit/HifiConstants.qml b/interface/resources/qml/styles-uit/HifiConstants.qml index 4a26d11128..a091c6d5cf 100644 --- a/interface/resources/qml/styles-uit/HifiConstants.qml +++ b/interface/resources/qml/styles-uit/HifiConstants.qml @@ -338,5 +338,6 @@ Item { readonly property string stop_square: "\ue01e" readonly property string avatarTPose: "\ue01f" readonly property string lock: "\ue006" + readonly property string check_2_01: "\ue020" } } From 2e84f48539255d6cc515f9767e5e53df1b25ecd2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 12 Sep 2017 15:49:49 -0700 Subject: [PATCH 109/202] Fix QML errors --- interface/resources/qml/AssetServer.qml | 2 +- interface/resources/qml/controls-uit/Tree.qml | 7 ------- interface/resources/qml/hifi/dialogs/RunningScripts.qml | 7 +++++++ .../resources/qml/hifi/dialogs/TabletRunningScripts.qml | 7 +++++++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 44b85b0898..5d6426e3a7 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -651,7 +651,7 @@ ScrollingWindow { } onAccepted: { if (acceptableInput && styleData.selected) { - if (!modifyEl(selection.currentIndex, text)) { + if (!treeView.modifyEl(treeView.selection.currentIndex, text)) { text = styleData.value; } unfocusHelper.forceActiveFocus(); diff --git a/interface/resources/qml/controls-uit/Tree.qml b/interface/resources/qml/controls-uit/Tree.qml index d94a8cdceb..79d3b958ea 100644 --- a/interface/resources/qml/controls-uit/Tree.qml +++ b/interface/resources/qml/controls-uit/Tree.qml @@ -225,11 +225,4 @@ TreeView { onClicked: { selectionModel.setCurrentIndex(index, ItemSelectionModel.ClearAndSelect); } - - onActivated: { - var path = scriptsModel.data(index, 0x100) - if (path) { - loadScript(path) - } - } } diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index a649c2c4d6..5263f0fa8c 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -375,6 +375,13 @@ ScrollingWindow { TableViewColumn { role: "display"; } + + onActivated: { + var path = scriptsModel.data(index, 0x100) + if (path) { + loadScript(path) + } + } } HifiControls.VerticalSpacer { diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 5677cc92a1..c5be38eed5 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -426,6 +426,13 @@ Rectangle { } } } + + onActivated: { + var path = scriptsModel.data(index, 0x100) + if (path) { + loadScript(path) + } + } } Item { From 9e11ae311e9c01b537df38ddf6a3ec38976dea63 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 12 Sep 2017 18:07:26 -0700 Subject: [PATCH 110/202] Fix error reporting --- assignment-client/src/assets/AssetServer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 6da299ea87..f1e921c768 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1166,6 +1166,8 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, meta.lastBakeErrors = errors; writeMetaFile(originalAssetHash, meta); + + _pendingBakes.remove(originalAssetHash); } void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector bakedFilePaths) { @@ -1286,7 +1288,7 @@ std::pair AssetServer::readMetaFile(AssetHash hash) { meta.bakeVersion = bakeVersion; meta.applicationVersion = appVersion; meta.failedLastBake = failedLastBake.toBool(); - meta.lastBakeErrors = failedLastBake.toString(); + meta.lastBakeErrors = lastBakeErrors.toString(); return { true, meta }; } else { From c04a6b5b88714bee948117e3f2d789cab9a7e4db Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 12 Sep 2017 18:20:04 -0700 Subject: [PATCH 111/202] Add error tooltip --- interface/resources/qml/AssetServer.qml | 8 +++ .../resources/qml/controls-uit/ToolTip.qml | 51 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 interface/resources/qml/controls-uit/ToolTip.qml diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 5d6426e3a7..0c4b952331 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -606,6 +606,14 @@ ScrollingWindow { elide: Text.ElideRight horizontalAlignment: TextInput.AlignHCenter + + HifiControls.ToolTip { + anchors.fill: parent + + visible: styleData.value === "Error" + + toolTip: assetProxyModel.data(styleData.index, 0x106) + } } } Component { diff --git a/interface/resources/qml/controls-uit/ToolTip.qml b/interface/resources/qml/controls-uit/ToolTip.qml new file mode 100644 index 0000000000..4fe36adcd5 --- /dev/null +++ b/interface/resources/qml/controls-uit/ToolTip.qml @@ -0,0 +1,51 @@ +// +// ToolTip.qml +// +// Created by Clement on 9/12/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 +// + +import QtQuick 2.5 + +Item { + property string toolTip + property bool showToolTip: false + + Rectangle { + id: toolTipRectangle + anchors.right: parent.right + + width: toolTipText.width + 4 + height: toolTipText.height + 4 + opacity: (toolTip != "" && showToolTip) ? 1 : 0 + color: "#ffffaa" + border.color: "#0a0a0a" + Text { + id: toolTipText + text: toolTip + color: "black" + anchors.centerIn: parent + } + Behavior on opacity { + PropertyAnimation { + easing.type: Easing.InOutQuad + duration: 250 + } + } + } + MouseArea { + id: mouseArea + anchors.fill: parent + onEntered: showTimer.start() + onExited: { showToolTip = false; showTimer.stop(); } + hoverEnabled: true + } + Timer { + id: showTimer + interval: 250 + onTriggered: { showToolTip = true; } + } +} \ No newline at end of file From 4066c9392d309b0532f4e4bb7fe62acbd76a6e77 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 13:34:15 -0700 Subject: [PATCH 112/202] output from oven into folder with model name --- tools/oven/src/ui/ModelBakeWidget.cpp | 44 +++++++++++++++++---------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/tools/oven/src/ui/ModelBakeWidget.cpp b/tools/oven/src/ui/ModelBakeWidget.cpp index 7829993740..7963b3f3c4 100644 --- a/tools/oven/src/ui/ModelBakeWidget.cpp +++ b/tools/oven/src/ui/ModelBakeWidget.cpp @@ -156,22 +156,6 @@ void ModelBakeWidget::outputDirectoryChanged(const QString& newDirectory) { } void ModelBakeWidget::bakeButtonClicked() { - // make sure we have a valid output directory - QDir outputDirectory(_outputDirLineEdit->text()); - - outputDirectory.mkdir("."); - if (!outputDirectory.exists()) { - QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory."); - return; - } - - QDir bakedOutputDirectory = outputDirectory.absoluteFilePath("baked"); - QDir originalOutputDirectory = outputDirectory.absoluteFilePath("original"); - - bakedOutputDirectory.mkdir("."); - originalOutputDirectory.mkdir("."); - - // make sure we have a non empty URL to a model to bake if (_modelLineEdit->text().isEmpty()) { @@ -193,6 +177,34 @@ void ModelBakeWidget::bakeButtonClicked() { qDebug() << "New url: " << modelToBakeURL; } + auto modelName = modelToBakeURL.fileName().left(modelToBakeURL.fileName().lastIndexOf('.')); + + // make sure we have a valid output directory + QDir outputDirectory(_outputDirLineEdit->text()); + QString subFolderName = modelName + "/"; + + // output in a sub-folder with the name of the fbx, potentially suffixed by a number to make it unique + int iteration = 0; + + while (outputDirectory.exists(subFolderName)) { + subFolderName = modelName + "-" + QString::number(++iteration) + "/"; + } + + outputDirectory.mkdir(subFolderName); + + if (!outputDirectory.exists()) { + QMessageBox::warning(this, "Unable to create directory", "Unable to create output directory. Please create it manually or choose a different directory."); + return; + } + + outputDirectory.cd(subFolderName); + + QDir bakedOutputDirectory = outputDirectory.absoluteFilePath("baked"); + QDir originalOutputDirectory = outputDirectory.absoluteFilePath("original"); + + bakedOutputDirectory.mkdir("."); + originalOutputDirectory.mkdir("."); + // everything seems to be in place, kick off a bake for this model now auto baker = std::unique_ptr { new FBXBaker(modelToBakeURL, []() -> QThread* { From 1e6154fbc8a500278da067bd18594aee0d291c56 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 13:45:26 -0700 Subject: [PATCH 113/202] make draco compile flags OS X only --- cmake/externals/draco/CMakeLists.txt | 4 +++- libraries/baking/CMakeLists.txt | 2 +- libraries/fbx/CMakeLists.txt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 07ce861820..3534461443 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -4,7 +4,9 @@ if (ANDROID) set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") endif () -set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) +if (APPLE) + set(EXTRA_CMAKE_FLAGS -DCMAKE_CXX_FLAGS=-stdlib=libc++ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++) +endif () include(ExternalProject) ExternalProject_Add( diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 2304a5e0c0..66cf791776 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -6,5 +6,5 @@ include_hifi_library_headers(gpu) add_dependency_external_projects(draco) find_package(Draco REQUIRED) -include_directories(SYSTEM ${DRACO_INCLUDE_DIRS}) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 4c81f10302..7cead5aa4f 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -6,5 +6,5 @@ include_hifi_library_headers(gpu image) add_dependency_external_projects(draco) find_package(Draco REQUIRED) -include_directories(SYSTEM ${DRACO_INCLUDE_DIRS}) +target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${DRACO_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${DRACO_LIBRARY} ${DRACO_ENCODER_LIBRARY}) From c365e1718ee3d999a146db41d2860d1f70a2f3a5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 13:51:26 -0700 Subject: [PATCH 114/202] set include dir for draco the same across the board --- cmake/externals/draco/CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmake/externals/draco/CMakeLists.txt b/cmake/externals/draco/CMakeLists.txt index 3534461443..44ddd6d3de 100644 --- a/cmake/externals/draco/CMakeLists.txt +++ b/cmake/externals/draco/CMakeLists.txt @@ -26,11 +26,7 @@ ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -if (UNIX AND NOT APPLE) - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include/draco/src CACHE PATH "List of Draco include directories") -else () - set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories") -endif () +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of Draco include directories") if (UNIX) set(LIB_PREFIX "lib") From bfb4dd0cdb00b23133fa7be12f4cba16ea32a75d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 13 Sep 2017 14:00:41 -0700 Subject: [PATCH 115/202] disable warning c4267 when including draco --- libraries/baking/src/FBXBaker.cpp | 10 ++++++++++ libraries/fbx/src/FBXReader_Mesh.cpp | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 79a5b9157b..5abb3feb0d 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -33,9 +33,19 @@ #include "FBXBaker.h" +#ifdef _WIN32 +#pragma warning( push ) +#pragma warning( disable : 4267 ) +#endif + #include #include +#ifdef _WIN32 +#pragma warning( pop ) +#endif + + FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter, const QString& bakedOutputDir, const QString& originalOutputDir) : _fbxURL(fbxURL), diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 323ecac37b..fd9756b4c0 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -9,8 +9,17 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#ifdef _WIN32 +#pragma warning( push ) +#pragma warning( disable : 4267 ) +#endif + #include +#ifdef _WIN32 +#pragma warning( pop ) +#endif + #include #include #include From 72ca62c4afdcbd07e5664d18e6a94ce82fda8bb1 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Thu, 14 Sep 2017 00:25:11 +0100 Subject: [PATCH 116/202] Initial Commit - more to do. --- interface/src/avatar/MyAvatar.cpp | 6 ++++++ interface/src/avatar/MyAvatar.h | 7 ++++++- libraries/physics/src/CharacterController.cpp | 8 +++++--- libraries/physics/src/CharacterController.h | 10 ++++++++-- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8592ff3d74..d7fc56275d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -60,6 +60,8 @@ #include "MovingEntitiesOperator.h" #include "SceneScriptingInterface.h" +#include "CharacterController.h" + using namespace std; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; @@ -2259,6 +2261,10 @@ float MyAvatar::getDomainMaxScale() { return _domainMaximumScale; } +void MyAvatar::setGravity(float gravity) { + emit gravityChanged(gravity); +} + void MyAvatar::increaseSize() { // make sure we're starting from an allowable scale clampTargetScaleToDomainLimits(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 65dcc12e7d..dbdf8f82fb 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -145,7 +145,9 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdRollControlEnabled READ getHMDRollControlEnabled WRITE setHMDRollControlEnabled) Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) - + + Q_PROPERTY(float gravity WRITE updateGravity NOTIFY gravityChanged); + const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; @@ -540,6 +542,8 @@ public slots: float getDomainMinScale(); float getDomainMaxScale(); + void setGravity(float gravity); + void goToLocation(const glm::vec3& newPosition, bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), bool shouldFaceLocation = false); @@ -592,6 +596,7 @@ signals: void wentActive(); void skeletonChanged(); void dominantHandChanged(const QString& hand); + void gravityChanged(const float gravity); private: diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 9a7abc4e98..36a4c75168 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -20,6 +20,7 @@ const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float JUMP_SPEED = 3.5f; const float MAX_FALL_HEIGHT = 20.0f; +const float DEFAULT_CHARACTER_GRAVITY = -5.0f; #ifdef DEBUG_STATE_CHANGE #define SET_STATE(desiredState, reason) setState(desiredState, reason) @@ -371,19 +372,20 @@ void CharacterController::setState(State desiredState) { } } -void CharacterController::updateGravity() { +void CharacterController::updateGravity(float gravity = DEFAULT_CHARACTER_GRAVITY) { int16_t collisionGroup = computeCollisionGroup(); if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { _gravity = 0.0f; } else { - const float DEFAULT_CHARACTER_GRAVITY = -5.0f; - _gravity = DEFAULT_CHARACTER_GRAVITY; + _gravity = gravity; } if (_rigidBody) { _rigidBody->setGravity(_gravity * _currentUp); } + emit gravityChanged(); } + void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) { float x = scale.x; float z = scale.z; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index bf84d318d4..f412e09956 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -42,10 +42,16 @@ const btScalar MAX_CHARACTER_MOTOR_TIMESCALE = 60.0f; // one minute const btScalar MIN_CHARACTER_MOTOR_TIMESCALE = 0.05f; class CharacterController : public btCharacterControllerInterface { + + Q_PROPERTY(float gravity WRITE updateGravity NOTIFY gravityChanged); + +signals: + void gravityChanged(); + public: CharacterController(); virtual ~CharacterController(); - + void updateGravity(float gravity); bool needsRemoval() const; bool needsAddition() const; virtual void setDynamicsWorld(btDynamicsWorld* world); @@ -130,7 +136,7 @@ protected: #endif virtual void updateMassProperties() = 0; - void updateGravity(); + void updateGravity(float gravity); void updateUpAxis(const glm::quat& rotation); bool checkForSupport(btCollisionWorld* collisionWorld); From e595541da0cebf3c443c3854c4821cbbc4fa4d8e Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 13 Sep 2017 17:21:54 -0700 Subject: [PATCH 117/202] Remove old versions of the VC Runtime from the installation directory --- cmake/templates/NSIS.template.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index 5eedbb06ed..ca05294984 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -836,6 +836,8 @@ Section "-Core installation" Delete "$INSTDIR\ui_resources_200_percent.pak" Delete "$INSTDIR\vccorlib120.dll" Delete "$INSTDIR\version" + Delete "$INSTDIR\msvcr140.dll" + Delete "$INSTDIR\msvcp140.dll" Delete "$INSTDIR\xinput1_3.dll" ; Delete old desktop shortcuts before they were renamed during Sandbox rename From 9cb4e2c5f2ba61b409f5ae7efa2e32dd8daab4b5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 14 Sep 2017 13:54:04 -0700 Subject: [PATCH 118/202] address code review comments --- libraries/baking/src/FBXBaker.cpp | 14 ++-------- libraries/fbx/src/FBXReader_Mesh.cpp | 38 ++++++++++++---------------- libraries/fbx/src/FBXWriter.cpp | 6 ++--- 3 files changed, 20 insertions(+), 38 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 5abb3feb0d..791d89e503 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -299,7 +299,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { // TODO Pull this out of _geometry instead so we don't have to reprocess it auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex); - auto mesh = extractedMesh.mesh; + auto& mesh = extractedMesh.mesh; Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); @@ -366,7 +366,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto partIndex = 0; draco::FaceIndex face; for (auto& part : mesh.parts) { - const auto matTex = extractedMesh.partMaterialTextures[partIndex]; + const auto& matTex = extractedMesh.partMaterialTextures[partIndex]; auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { auto idx0 = indices[index]; @@ -447,16 +447,6 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto value = QVariant::fromValue(QByteArray(buffer.data(), (int) buffer.size())); dracoMeshNode.properties.append(value); - - QFile file("C:/Users/huffm/encodedFBX/" + this->_fbxURL.fileName() + "-" + QString::number(meshIndex) + ".drc"); - if (file.open(QIODevice::WriteOnly)) { - file.write(buffer.data(), buffer.size()); - file.close(); - } else { - qWarning() << "Failed to write to: " << file.fileName(); - - } - objectChild.children.push_back(dracoMeshNode); static const std::vector nodeNamesToDelete { diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index fd9756b4c0..bb35447adb 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -361,11 +361,11 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QHash, int> materialTextureParts; - data.extracted.mesh.vertices.reserve(numVertices); - data.extracted.mesh.normals.reserve(numVertices); - data.extracted.mesh.texCoords.reserve(numVertices); - data.extracted.mesh.texCoords1.reserve(numVertices); - data.extracted.mesh.colors.reserve(numVertices); + data.extracted.mesh.vertices.resize(numVertices); + data.extracted.mesh.normals.resize(numVertices); + data.extracted.mesh.texCoords.resize(numVertices); + data.extracted.mesh.texCoords1.resize(numVertices); + data.extracted.mesh.colors.resize(numVertices); // enumerate the vertices and construct the extracted mesh for (int i = 0; i < numVertices; ++i) { @@ -375,46 +375,40 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn // read position from draco mesh to extracted mesh auto mappedIndex = positionAttribute->mapped_index(vertexIndex); - std::array positionValue; - positionAttribute->ConvertValue(mappedIndex, &positionValue[0]); - data.extracted.mesh.vertices.push_back({ positionValue[0], positionValue[1], positionValue[2] }); + positionAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.vertices[i])); } if (normalAttribute) { // read normals from draco mesh to extracted mesh auto mappedIndex = normalAttribute->mapped_index(vertexIndex); - std::array normalValue; - normalAttribute->ConvertValue(mappedIndex, &normalValue[0]); - data.extracted.mesh.normals.push_back({ normalValue[0], normalValue[1], normalValue[2] }); + normalAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.normals[i])); } if (texCoordAttribute) { // read UVs from draco mesh to extracted mesh auto mappedIndex = texCoordAttribute->mapped_index(vertexIndex); - std::array texCoordValue; - texCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); - data.extracted.mesh.texCoords.push_back({ texCoordValue[0], texCoordValue[1] }); + texCoordAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.texCoords[i])); } if (extraTexCoordAttribute) { // some meshes have a second set of UVs, read those to extracted mesh auto mappedIndex = extraTexCoordAttribute->mapped_index(vertexIndex); - std::array texCoordValue; - extraTexCoordAttribute->ConvertValue(mappedIndex, &texCoordValue[0]); - data.extracted.mesh.texCoords1.push_back({ texCoordValue[0], texCoordValue[1] }); + extraTexCoordAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.texCoords1[i])); } if (colorAttribute) { // read vertex colors from draco mesh to extracted mesh auto mappedIndex = colorAttribute->mapped_index(vertexIndex); - std::array colorValue; - - colorAttribute->ConvertValue(mappedIndex, &colorValue[0]); - data.extracted.mesh.colors.push_back({ colorValue[0], colorValue[1], colorValue[2] }); + colorAttribute->ConvertValue(mappedIndex, + reinterpret_cast(&data.extracted.mesh.colors[i])); } data.extracted.newIndices.insert(i, i); @@ -422,7 +416,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn for (int i = 0; i < dracoMesh->num_faces(); ++i) { // grab the material ID and texture ID for this face, if we have it - auto firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; + auto& firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; uint16_t materialID { 0 }; diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index cc34696f92..7195d2abb4 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -19,9 +19,8 @@ void writeVector(QDataStream& out, char ch, QVector list) { out << (int32_t)list.length(); out << (int32_t)0; out << (int32_t)0; - for (auto& value : list) { - out << value; - } + + out.writeBytes(reinterpret_cast(list.constData()), list.length() * sizeof(T)); } @@ -98,7 +97,6 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { case QVariant::Type::Bool: out.device()->write("C", 1); - //out.device()->write(prop.toBool() ? 1 : 0, 1); out << prop.toBool(); break; From bb98df38d4c9f4add6b320e2bbbaaa1295b0b96b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 14 Sep 2017 14:42:40 -0700 Subject: [PATCH 119/202] remove re-grabbing of draco faces --- libraries/fbx/src/FBXReader_Mesh.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index bb35447adb..163a92e3bd 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -416,7 +416,8 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn for (int i = 0; i < dracoMesh->num_faces(); ++i) { // grab the material ID and texture ID for this face, if we have it - auto& firstCorner = dracoMesh->face(draco::FaceIndex(i))[0]; + auto& dracoFace = dracoMesh->face(draco::FaceIndex(i)); + auto& firstCorner = dracoFace[0]; uint16_t materialID { 0 }; @@ -440,8 +441,8 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn // give the mesh part this index FBXMeshPart& part = data.extracted.mesh.parts[partIndexPlusOne - 1]; part.triangleIndices.append(firstCorner.value()); - part.triangleIndices.append(dracoMesh->face(draco::FaceIndex(i))[1].value()); - part.triangleIndices.append(dracoMesh->face(draco::FaceIndex(i))[2].value()); + part.triangleIndices.append(dracoFace[1].value()); + part.triangleIndices.append(dracoFace[2].value()); } } } From 43196cd0a66cb048d7198a225ebe396ed61b3bc4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 14 Sep 2017 14:46:48 -0700 Subject: [PATCH 120/202] fix buffer resizing to only occur when attribute is present --- libraries/fbx/src/FBXReader_Mesh.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 163a92e3bd..bef36770a0 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -362,10 +362,22 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn QHash, int> materialTextureParts; data.extracted.mesh.vertices.resize(numVertices); - data.extracted.mesh.normals.resize(numVertices); - data.extracted.mesh.texCoords.resize(numVertices); - data.extracted.mesh.texCoords1.resize(numVertices); - data.extracted.mesh.colors.resize(numVertices); + + if (normalAttribute) { + data.extracted.mesh.normals.resize(numVertices); + } + + if (texCoordAttribute) { + data.extracted.mesh.texCoords.resize(numVertices); + } + + if (extraTexCoordAttribute) { + data.extracted.mesh.texCoords1.resize(numVertices); + } + + if (colorAttribute) { + data.extracted.mesh.colors.resize(numVertices); + } // enumerate the vertices and construct the extracted mesh for (int i = 0; i < numVertices; ++i) { From c0fb9dcf2e39452040a321844acf5d8b3b85b87a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 6 Sep 2017 09:49:33 -0700 Subject: [PATCH 121/202] Fix polyline crash and flicker --- .../entities-renderer/src/RenderablePolyLineEntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 3db7fb2c30..1cecb0b036 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -126,6 +126,7 @@ void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& auto textures = entity->getTextures(); QString path = textures.isEmpty() ? PathUtils::resourcesPath() + "images/paintStroke.png" : textures; if (!_texture || _lastTextures != path) { + _lastTextures = path; _texture = DependencyManager::get()->getTexture(QUrl(path)); } } @@ -220,7 +221,7 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { batch.setModelTransform(Transform{ _modelTransform }.setScale(vec3(1))); batch.setUniformBuffer(PAINTSTROKE_UNIFORM_SLOT, _uniformBuffer); - if (_texture->isLoaded()) { + if (_texture && _texture->isLoaded()) { batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, _texture->getGPUTexture()); } else { batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, nullptr); From 4c4df97c42b547689ff389eaeeef1a651862726f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 6 Sep 2017 23:38:02 -0700 Subject: [PATCH 122/202] Fix transform of polylines with high update rates --- .../entities-renderer/src/RenderablePolyLineEntityItem.cpp | 6 +++++- .../entities-renderer/src/RenderablePolyLineEntityItem.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 1cecb0b036..50226ef8ae 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -141,6 +141,10 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo auto normalsChanged = entity->normalsChanged(); entity->resetPolyLineChanged(); + _polylineTransform = Transform(); + _polylineTransform.setTranslation(entity->getPosition()); + _polylineTransform.setRotation(entity->getRotation()); + if (pointsChanged) { _lastPoints = entity->getLinePoints(); } @@ -218,7 +222,7 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; - batch.setModelTransform(Transform{ _modelTransform }.setScale(vec3(1))); + batch.setModelTransform(_polylineTransform); batch.setUniformBuffer(PAINTSTROKE_UNIFORM_SLOT, _uniformBuffer); if (_texture && _texture->isLoaded()) { diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 88b5ebdb9f..b0bdcf545b 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -47,6 +47,7 @@ protected: void updateGeometry(const std::vector& vertices); static std::vector updateVertices(const QVector& points, const QVector& normals, const QVector& strokeWidths); + Transform _polylineTransform; QVector _lastPoints; QVector _lastNormals; QVector _lastStrokeWidths; From d3ac7a388c8cd53718aab05f6d4209a7fa54b4dc Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 14 Sep 2017 10:48:14 -0700 Subject: [PATCH 123/202] Fix polyline rendering using default texture --- .../src/RenderablePolyLineEntityItem.cpp | 14 +++++++++----- .../src/RenderablePolyLineEntityItem.h | 1 - 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 50226ef8ae..8c1a6318f9 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -121,15 +121,19 @@ bool PolyLineEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityP } void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { + static const QUrl DEFAULT_POLYLINE_TEXTURE = QUrl(PathUtils::resourcesPath() + "images/paintStroke.png"); + QUrl entityTextures = DEFAULT_POLYLINE_TEXTURE; if (entity->texturesChanged()) { entity->resetTexturesChanged(); auto textures = entity->getTextures(); - QString path = textures.isEmpty() ? PathUtils::resourcesPath() + "images/paintStroke.png" : textures; - if (!_texture || _lastTextures != path) { - _lastTextures = path; - _texture = DependencyManager::get()->getTexture(QUrl(path)); + if (!textures.isEmpty()) { + entityTextures = QUrl(textures); } } + + if (!_texture || _texture->getURL() != entityTextures) { + _texture = DependencyManager::get()->getTexture(entityTextures); + } } void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { @@ -228,7 +232,7 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { if (_texture && _texture->isLoaded()) { batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, _texture->getGPUTexture()); } else { - batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, nullptr); + batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, DependencyManager::get()->getWhiteTexture()); } batch.setInputFormat(polylineFormat); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index b0bdcf545b..610ee53cf7 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -55,7 +55,6 @@ protected: gpu::BufferView _uniformBuffer; uint32_t _numVertices { 0 }; bool _empty{ true }; - QString _lastTextures; NetworkTexturePointer _texture; }; From 2a62bac4f92960b7b5e57622940cc041228d8160 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 14 Sep 2017 16:00:20 -0700 Subject: [PATCH 124/202] refuse to re-bake an already baked FBX --- libraries/baking/src/FBXBaker.cpp | 7 ++++++- libraries/fbx/src/FBX.h | 1 + libraries/fbx/src/FBXReader_Mesh.cpp | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 791d89e503..2a9deb44f1 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -301,6 +301,11 @@ void FBXBaker::rewriteAndBakeSceneModels() { auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex); auto& mesh = extractedMesh.mesh; + if (mesh.wasCompressed) { + handleError("Cannot re-bake a file that contains compressed mesh"); + return; + } + Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); @@ -529,7 +534,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { // re-baking an FBX that already references baked textures is a fail // so we add an error and return from here - handleError("Cannot re-bake a partially baked FBX file that references baked KTX textures"); + handleError("Cannot re-bake a file that references compressed textures"); return; } diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 9f5fad7d66..89bc893df2 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -234,6 +234,7 @@ public: unsigned int meshIndex; // the order the meshes appeared in the object file model::MeshPointer _mesh; + bool wasCompressed { false }; }; class ExtractedMesh { diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index bef36770a0..93976afe90 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -338,6 +338,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } } else if (child.name == "DracoMesh") { isDracoMesh = true; + data.extracted.mesh.wasCompressed = true; // load the draco mesh from the FBX and create a draco::Mesh draco::Decoder decoder; From 379306204530c1afa5e81649ee61ec329c973637 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 13 Sep 2017 09:51:06 -0700 Subject: [PATCH 125/202] Add optional FBX 2015 encoding to FBXWriter --- libraries/fbx/src/FBX.h | 1 + libraries/fbx/src/FBXWriter.cpp | 26 ++++++++++++++++++++------ libraries/fbx/src/FBXWriter.h | 2 ++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 9f5fad7d66..6af23a0151 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -30,6 +30,7 @@ static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary "; static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; +static const quint32 FBX_VERSION_2015 = 7400; static const quint32 FBX_VERSION_2016 = 7500; diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index 7195d2abb4..764d8df6cf 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -13,6 +13,16 @@ #include +#ifdef USE_FBX_2016_FORMAT + using FBXEndOffset = int64_t; + using FBXPropertyCount = uint64_t; + using FBXListLength = uint64_t; +#else + using FBXEndOffset = int32_t; + using FBXPropertyCount = uint32_t; + using FBXListLength = uint32_t; +#endif + template void writeVector(QDataStream& out, char ch, QVector list) { out.device()->write(&ch, 1); @@ -34,7 +44,11 @@ QByteArray FBXWriter::encodeFBX(const FBXNode& root) { auto bytes = QByteArray(FBX_HEADER_BYTES_BEFORE_VERSION - FBX_BINARY_PROLOG.size(), '\0'); out.writeRawData(bytes, bytes.size()); +#ifdef USE_FBX_2016_FORMAT out << FBX_VERSION_2016; +#else + out << FBX_VERSION_2015; +#endif for (auto& child : root.children) { encodeNode(out, child); @@ -51,13 +65,13 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { auto nodeStartPos = device->pos(); // endOffset (temporary, updated later) - out << (qint64)0; + out << (FBXEndOffset)0; // Property count - out << (quint64)node.properties.size(); + out << (FBXPropertyCount)node.properties.size(); // Property list length (temporary, updated later) - out << (quint64)0; + out << (FBXListLength)0; out << (quint8)node.name.size(); out.writeRawData(node.name, node.name.size()); @@ -70,8 +84,8 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { // Go back and write property list length auto nodePropertiesEndPos = device->pos(); - device->seek(nodeStartPos + sizeof(qint64) + sizeof(quint64)); - out << (quint64)(nodePropertiesEndPos - nodePropertiesStartPos); + device->seek(nodeStartPos + sizeof(FBXEndOffset) + sizeof(FBXPropertyCount)); + out << (FBXListLength)(nodePropertiesEndPos - nodePropertiesStartPos); device->seek(nodePropertiesEndPos); @@ -86,7 +100,7 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { // Go back and write actual endOffset auto nodeEndPos = device->pos(); device->seek(nodeStartPos); - out << (qint64)(nodeEndPos); + out << (FBXEndOffset)(nodeEndPos); device->seek(nodeEndPos); } diff --git a/libraries/fbx/src/FBXWriter.h b/libraries/fbx/src/FBXWriter.h index fa33983345..57c33e0e1c 100644 --- a/libraries/fbx/src/FBXWriter.h +++ b/libraries/fbx/src/FBXWriter.h @@ -17,6 +17,8 @@ #include #include +#define USE_FBX_2016_FORMAT + class FBXWriter { public: static QByteArray encodeFBX(const FBXNode& root); From 7db8d1dcbc3f66c1da75281cadd27fc121d0b7b7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 14 Sep 2017 14:29:54 -0700 Subject: [PATCH 126/202] Add support for baked skinned meshes --- libraries/baking/src/FBXBaker.cpp | 21 +++++++++++++---- libraries/fbx/src/FBX.h | 2 ++ libraries/fbx/src/FBXReader.h | 2 +- libraries/fbx/src/FBXReader_Mesh.cpp | 34 +++++++++++++++++++--------- libraries/fbx/src/FBXWriter.cpp | 9 +++++--- 5 files changed, 48 insertions(+), 20 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 791d89e503..380e893c9b 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -298,7 +298,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { if (objectChild.name == "Geometry") { // TODO Pull this out of _geometry instead so we don't have to reprocess it - auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex); + auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false); auto& mesh = extractedMesh.mesh; Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size()); @@ -338,6 +338,9 @@ void FBXBaker::rewriteAndBakeSceneModels() { const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32); + const int originalIndexAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_ORIGINAL_INDEX, + 1, draco::DT_INT32); if (hasNormals) { normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, @@ -367,14 +370,14 @@ void FBXBaker::rewriteAndBakeSceneModels() { draco::FaceIndex face; for (auto& part : mesh.parts) { const auto& matTex = extractedMesh.partMaterialTextures[partIndex]; + uint16_t materialID = matTex.first; auto addFace = [&](QVector& indices, int index, draco::FaceIndex face) { - auto idx0 = indices[index]; - auto idx1 = indices[index + 1]; - auto idx2 = indices[index + 2]; + int32_t idx0 = indices[index]; + int32_t idx1 = indices[index + 1]; + int32_t idx2 = indices[index + 2]; if (hasPerFaceMaterials) { - uint16_t materialID = matTex.first; meshBuilder.SetPerFaceAttributeValueForFace(faceMaterialAttributeID, face, &materialID); } @@ -382,6 +385,12 @@ void FBXBaker::rewriteAndBakeSceneModels() { &mesh.vertices[idx0], &mesh.vertices[idx1], &mesh.vertices[idx2]); + if (originalIndexAttributeID) { + meshBuilder.SetAttributeValuesForFace(originalIndexAttributeID, face, + &mesh.originalIndex[idx0], + &mesh.originalIndex[idx1], + &mesh.originalIndex[idx2]); + } if (hasNormals) { meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, &mesh.normals[idx0], &mesh.normals[idx1], @@ -432,6 +441,8 @@ void FBXBaker::rewriteAndBakeSceneModels() { dracoMesh->attribute(texCoords1AttributeID)->set_unique_id(DRACO_ATTRIBUTE_TEX_COORD_1); } + dracoMesh->attribute(originalIndexAttributeID)->set_unique_id(DRACO_ATTRIBUTE_ORIGINAL_INDEX); + draco::Encoder encoder; encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, 14); diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 6af23a0151..78d35889cf 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -38,6 +38,7 @@ static const quint32 FBX_VERSION_2016 = 7500; static const int DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES = 1000; static const int DRACO_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES; static const int DRACO_ATTRIBUTE_TEX_COORD_1 = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 1; +static const int DRACO_ATTRIBUTE_ORIGINAL_INDEX = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 2; class FBXNode; @@ -217,6 +218,7 @@ public: QVector parts; QVector vertices; + QVector originalIndex; QVector normals; QVector tangents; QVector colors; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 8a5e394da9..843a874a62 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -110,7 +110,7 @@ public: FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url); - static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex); + static ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate = true); QHash meshes; static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index bef36770a0..3d70fa45ae 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -84,7 +84,7 @@ public: }; -void appendIndex(MeshData& data, QVector& indices, int index) { +void appendIndex(MeshData& data, QVector& indices, int index, bool deduplicate) { if (index >= data.polygonIndices.size()) { return; } @@ -156,12 +156,13 @@ void appendIndex(MeshData& data, QVector& indices, int index) { } QHash::const_iterator it = data.indices.find(vertex); - if (it == data.indices.constEnd()) { + if (!deduplicate || it == data.indices.constEnd()) { int newIndex = data.extracted.mesh.vertices.size(); indices.append(newIndex); data.indices.insert(vertex, newIndex); data.extracted.newIndices.insert(vertexIndex, newIndex); data.extracted.mesh.vertices.append(position); + data.extracted.mesh.originalIndices.append(vertexIndex); data.extracted.mesh.normals.append(normal); data.extracted.mesh.texCoords.append(vertex.texCoord); if (hasColors) { @@ -176,7 +177,7 @@ void appendIndex(MeshData& data, QVector& indices, int index) { } } -ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIndex) { +ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIndex, bool deduplicate) { MeshData data; data.extracted.mesh.meshIndex = meshIndex++; @@ -355,6 +356,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn auto extraTexCoordAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_TEX_COORD_1); auto colorAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::COLOR); auto matTexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID); + auto originalIndexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_ORIGINAL_INDEX); // setup extracted mesh data structures given number of points auto numVertices = dracoMesh->num_points(); @@ -423,7 +425,17 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn reinterpret_cast(&data.extracted.mesh.colors[i])); } - data.extracted.newIndices.insert(i, i); + if (originalIndexAttribute) { + auto mappedIndex = originalIndexAttribute->mapped_index(vertexIndex); + + int32_t originalIndex; + + originalIndexAttribute->ConvertValue(mappedIndex, &originalIndex); + + data.extracted.newIndices.insert(originalIndex, i); + } else { + data.extracted.newIndices.insert(i, i); + } } for (int i = 0; i < dracoMesh->num_faces(); ++i) { @@ -487,10 +499,10 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn FBXMeshPart& part = data.extracted.mesh.parts[partIndex - 1]; if (endIndex - beginIndex == 4) { - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); - appendIndex(data, part.quadIndices, beginIndex++); + appendIndex(data, part.quadIndices, beginIndex++, deduplicate); + appendIndex(data, part.quadIndices, beginIndex++, deduplicate); + appendIndex(data, part.quadIndices, beginIndex++, deduplicate); + appendIndex(data, part.quadIndices, beginIndex++, deduplicate); int quadStartIndex = part.quadIndices.size() - 4; int i0 = part.quadIndices[quadStartIndex + 0]; @@ -515,9 +527,9 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn } else { for (int nextIndex = beginIndex + 1;; ) { - appendIndex(data, part.triangleIndices, beginIndex); - appendIndex(data, part.triangleIndices, nextIndex++); - appendIndex(data, part.triangleIndices, nextIndex); + appendIndex(data, part.triangleIndices, beginIndex, deduplicate); + appendIndex(data, part.triangleIndices, nextIndex++, deduplicate); + appendIndex(data, part.triangleIndices, nextIndex, deduplicate); if (nextIndex >= data.polygonIndices.size() || data.polygonIndices.at(nextIndex) < 0) { break; } diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index 764d8df6cf..f7bf3517f6 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -59,8 +59,6 @@ QByteArray FBXWriter::encodeFBX(const FBXNode& root) { } void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { - qDebug() << "Encoding " << node.name; - auto device = out.device(); auto nodeStartPos = device->pos(); @@ -108,8 +106,12 @@ void FBXWriter::encodeNode(QDataStream& out, const FBXNode& node) { void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { auto type = prop.userType(); switch (type) { - case QVariant::Type::Bool: + case QMetaType::Short: + out.device()->write("Y", 1); + out << prop.value(); + break; + case QVariant::Type::Bool: out.device()->write("C", 1); out << prop.toBool(); break; @@ -229,6 +231,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { writeVector(out, 'b', prop.value>()); } else { qDebug() << "Unsupported property type in FBXWriter::encodeNode: " << type << prop; + throw("Unsupported property type in FBXWriter::encodeNode: " + QString::number(type) + " " + prop.toString()); } } From fd1d4b9bd1e9a518e48e48c0971b3501c82cea66 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 14 Sep 2017 16:49:53 -0700 Subject: [PATCH 127/202] Update FBXBaker to only pack original indices if needed --- libraries/baking/src/FBXBaker.cpp | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 380e893c9b..46f3df7493 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -292,6 +292,20 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeF void FBXBaker::rewriteAndBakeSceneModels() { unsigned int meshIndex = 0; + bool hasDeformers { false }; + for (FBXNode& rootChild : _rootNode.children) { + if (rootChild.name == "Objects") { + for (FBXNode& objectChild : rootChild.children) { + if (objectChild.name == "Deformer") { + hasDeformers = true; + break; + } + } + } + if (hasDeformers) { + break; + } + } for (FBXNode& rootChild : _rootNode.children) { if (rootChild.name == "Objects") { for (FBXNode& objectChild : rootChild.children) { @@ -329,18 +343,22 @@ void FBXBaker::rewriteAndBakeSceneModels() { bool hasTexCoords { mesh.texCoords.size() > 0 }; bool hasTexCoords1 { mesh.texCoords1.size() > 0 }; bool hasPerFaceMaterials { mesh.parts.size() > 1 }; + bool needsOriginalIndices { hasDeformers }; int normalsAttributeID { -1 }; int colorsAttributeID { -1 }; int texCoordsAttributeID { -1 }; int texCoords1AttributeID { -1 }; int faceMaterialAttributeID { -1 }; + int originalIndexAttributeID { -1 }; const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION, 3, draco::DT_FLOAT32); - const int originalIndexAttributeID = meshBuilder.AddAttribute( - (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_ORIGINAL_INDEX, - 1, draco::DT_INT32); + if (needsOriginalIndices) { + originalIndexAttributeID = meshBuilder.AddAttribute( + (draco::GeometryAttribute::Type)DRACO_ATTRIBUTE_ORIGINAL_INDEX, + 1, draco::DT_INT32); + } if (hasNormals) { normalsAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::NORMAL, @@ -385,7 +403,7 @@ void FBXBaker::rewriteAndBakeSceneModels() { &mesh.vertices[idx0], &mesh.vertices[idx1], &mesh.vertices[idx2]); - if (originalIndexAttributeID) { + if (needsOriginalIndices) { meshBuilder.SetAttributeValuesForFace(originalIndexAttributeID, face, &mesh.originalIndex[idx0], &mesh.originalIndex[idx1], @@ -441,7 +459,9 @@ void FBXBaker::rewriteAndBakeSceneModels() { dracoMesh->attribute(texCoords1AttributeID)->set_unique_id(DRACO_ATTRIBUTE_TEX_COORD_1); } - dracoMesh->attribute(originalIndexAttributeID)->set_unique_id(DRACO_ATTRIBUTE_ORIGINAL_INDEX); + if (needsOriginalIndices) { + dracoMesh->attribute(originalIndexAttributeID)->set_unique_id(DRACO_ATTRIBUTE_ORIGINAL_INDEX); + } draco::Encoder encoder; From c2e342ddac9cab175d124a2bb97cb2b1ff8ac1e5 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Fri, 15 Sep 2017 06:03:56 +0100 Subject: [PATCH 128/202] Gravity Implementation --- interface/src/avatar/MyAvatar.cpp | 6 ++- interface/src/avatar/MyAvatar.h | 4 +- libraries/physics/src/CharacterController.cpp | 37 +++++++++++-------- libraries/physics/src/CharacterController.h | 11 ++---- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d7fc56275d..ab48138822 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2262,7 +2262,11 @@ float MyAvatar::getDomainMaxScale() { } void MyAvatar::setGravity(float gravity) { - emit gravityChanged(gravity); + _characterController.setGravity(gravity); +} + +float MyAvatar::getGravity() { + return _characterController.getGravity(); } void MyAvatar::increaseSize() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index dbdf8f82fb..724a313296 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -146,8 +146,6 @@ class MyAvatar : public Avatar { Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) - Q_PROPERTY(float gravity WRITE updateGravity NOTIFY gravityChanged); - const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; @@ -543,6 +541,7 @@ public slots: float getDomainMaxScale(); void setGravity(float gravity); + float getGravity(); void goToLocation(const glm::vec3& newPosition, bool hasOrientation = false, const glm::quat& newOrientation = glm::quat(), @@ -596,7 +595,6 @@ signals: void wentActive(); void skeletonChanged(); void dominantHandChanged(const QString& hand); - void gravityChanged(const float gravity); private: diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 36a4c75168..a81e1d8624 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -20,7 +20,7 @@ const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float JUMP_SPEED = 3.5f; const float MAX_FALL_HEIGHT = 20.0f; -const float DEFAULT_CHARACTER_GRAVITY = -5.0f; +float DEFAULT_CHARACTER_GRAVITY = -5.0f; #ifdef DEBUG_STATE_CHANGE #define SET_STATE(desiredState, reason) setState(desiredState, reason) @@ -354,6 +354,27 @@ static const char* stateToStr(CharacterController::State state) { } #endif // #ifdef DEBUG_STATE_CHANGE +void CharacterController::updateGravity() { + int16_t collisionGroup = computeCollisionGroup(); + if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { + _gravity = 0.0f; + } else { + _gravity = DEFAULT_CHARACTER_GRAVITY; + } + if (_rigidBody) { + _rigidBody->setGravity(_gravity * _currentUp); + } +} + + +void CharacterController::setGravity(float gravity) { + DEFAULT_CHARACTER_GRAVITY = gravity; +} + +float CharacterController::getGravity() { + return DEFAULT_CHARACTER_GRAVITY; +} + #ifdef DEBUG_STATE_CHANGE void CharacterController::setState(State desiredState, const char* reason) { #else @@ -372,20 +393,6 @@ void CharacterController::setState(State desiredState) { } } -void CharacterController::updateGravity(float gravity = DEFAULT_CHARACTER_GRAVITY) { - int16_t collisionGroup = computeCollisionGroup(); - if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _gravity = 0.0f; - } else { - _gravity = gravity; - } - if (_rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); - } - emit gravityChanged(); -} - - void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) { float x = scale.x; float z = scale.z; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index f412e09956..b7ea17fc71 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -43,20 +43,17 @@ const btScalar MIN_CHARACTER_MOTOR_TIMESCALE = 0.05f; class CharacterController : public btCharacterControllerInterface { - Q_PROPERTY(float gravity WRITE updateGravity NOTIFY gravityChanged); - -signals: - void gravityChanged(); - public: CharacterController(); virtual ~CharacterController(); - void updateGravity(float gravity); bool needsRemoval() const; bool needsAddition() const; virtual void setDynamicsWorld(btDynamicsWorld* world); btCollisionObject* getCollisionObject() { return _rigidBody; } + void setGravity(float gravity); + float getGravity(); + virtual void updateShapeIfNecessary() = 0; // overrides from btCharacterControllerInterface @@ -136,7 +133,7 @@ protected: #endif virtual void updateMassProperties() = 0; - void updateGravity(float gravity); + void updateGravity(); void updateUpAxis(const glm::quat& rotation); bool checkForSupport(btCollisionWorld* collisionWorld); From e86b37f949122822d2711c296ff01f4ec31248af Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Fri, 15 Sep 2017 06:07:50 +0100 Subject: [PATCH 129/202] Added script for testing --- scripts/developer/tests/gravityScript.js | 47 ++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 scripts/developer/tests/gravityScript.js diff --git a/scripts/developer/tests/gravityScript.js b/scripts/developer/tests/gravityScript.js new file mode 100644 index 0000000000..9d3868c078 --- /dev/null +++ b/scripts/developer/tests/gravityScript.js @@ -0,0 +1,47 @@ +// +// Gravity Script 1.0 +// ************ +// +// Created by Cain Kilgore on 9/14/2017 + +// Javascript for the Gravity Modifier Implementation to test +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +function menuParameters(menuNameSelection, menuItemNameSelection) { + Menu.addMenuItem({ + menuName: menuNameSelection, + menuItemName: menuItemNameSelection, + isCheckable: false + }); +} + +function setupMenu() { + if(!Menu.menuExists("Gravity")) { + Menu.addMenu("Gravity"); + for(var i = -5; i <= 5; i++) { + menuParameters("Gravity", i); + } + } +} + +function menuItemEvent(menuItem) { + for(var i = -5; i <= 5; i++) { + if(menuItem == i) { + MyAvatar.setGravity(i); + } + } +} + +function onScriptEnding() { + Menu.removeMenu("Gravity"); +} + +setupMenu(); + +Menu.menuItemEvent.connect(menuItemEvent); + +Script.scriptEnding.connect(onScriptEnding); From 67a5c75a0d92ba4dc811924f6799d509c79483d5 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Fri, 15 Sep 2017 06:08:22 +0100 Subject: [PATCH 130/202] Doesn't need to be here anymore --- interface/src/avatar/MyAvatar.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ab48138822..3a84ffd49f 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -60,8 +60,6 @@ #include "MovingEntitiesOperator.h" #include "SceneScriptingInterface.h" -#include "CharacterController.h" - using namespace std; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; From 794f3ca69ffc812ad3829998c2f77dcbc105cdb6 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Fri, 15 Sep 2017 06:09:03 +0100 Subject: [PATCH 131/202] Indentation --- scripts/developer/tests/gravityScript.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/developer/tests/gravityScript.js b/scripts/developer/tests/gravityScript.js index 9d3868c078..3468de72c3 100644 --- a/scripts/developer/tests/gravityScript.js +++ b/scripts/developer/tests/gravityScript.js @@ -20,17 +20,17 @@ function menuParameters(menuNameSelection, menuItemNameSelection) { } function setupMenu() { - if(!Menu.menuExists("Gravity")) { + if (!Menu.menuExists("Gravity")) { Menu.addMenu("Gravity"); - for(var i = -5; i <= 5; i++) { + for (var i = -5; i <= 5; i++) { menuParameters("Gravity", i); } } } function menuItemEvent(menuItem) { - for(var i = -5; i <= 5; i++) { - if(menuItem == i) { + for (var i = -5; i <= 5; i++) { + if (menuItem == i) { MyAvatar.setGravity(i); } } @@ -41,7 +41,5 @@ function onScriptEnding() { } setupMenu(); - Menu.menuItemEvent.connect(menuItemEvent); - Script.scriptEnding.connect(onScriptEnding); From 8711823a9b320915fcb0ba1ea92b25c700fc6d42 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 15 Sep 2017 11:35:59 -0700 Subject: [PATCH 132/202] Sync up AssetServer.qml and TabletAssetServer.qml --- interface/resources/qml/AssetServer.qml | 8 +- .../qml/hifi/dialogs/TabletAssetServer.qml | 190 +++++++++--------- 2 files changed, 99 insertions(+), 99 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 0c4b952331..23fec03ac2 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -72,11 +72,11 @@ ScrollingWindow { function doRenameFile(oldPath, newPath) { - if (newPath[0] != "/") { + if (newPath[0] !== "/") { newPath = "/" + newPath; } - if (oldPath[oldPath.length - 1] == '/' && newPath[newPath.length - 1] != '/') { + if (oldPath[oldPath.length - 1] === '/' && newPath[newPath.length - 1] != '/') { // this is a folder rename but the user neglected to add a trailing slash when providing a new path newPath = newPath + "/"; } @@ -302,7 +302,7 @@ ScrollingWindow { object.selected.connect(function(destinationPath) { destinationPath = destinationPath.trim(); - if (path == destinationPath) { + if (path === destinationPath) { return; } if (fileExists(destinationPath)) { @@ -363,7 +363,7 @@ ScrollingWindow { running: false } - property var uploadOpen: false; + property bool uploadOpen: false; Timer { id: timer } diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index a7b4ad7a53..250c13c405 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -105,12 +105,12 @@ Rectangle { function askForOverwrite(path, callback) { var object = tabletRoot.messageBox({ - icon: hifi.icons.question, - buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, - defaultButton: OriginalDialogs.StandardButton.No, - title: "Overwrite File", - text: path + "\n" + "This file already exists. Do you want to overwrite it?" - }); + icon: hifi.icons.question, + buttons: OriginalDialogs.StandardButton.Yes | OriginalDialogs.StandardButton.No, + defaultButton: OriginalDialogs.StandardButton.No, + title: "Overwrite File", + text: path + "\n" + "This file already exists. Do you want to overwrite it?" + }); object.selected.connect(function(button) { if (button === OriginalDialogs.StandardButton.Yes) { callback(); @@ -149,9 +149,9 @@ Rectangle { function handleGetMappingsError(errorString) { errorMessageBox( - "There was a problem retreiving the list of assets from your Asset Server.\n" - + errorString - ); + "There was a problem retreiving the list of assets from your Asset Server.\n" + + errorString + ); } function addToWorld() { @@ -179,25 +179,25 @@ Rectangle { var SHAPE_TYPE_DEFAULT = SHAPE_TYPE_STATIC_MESH; var DYNAMIC_DEFAULT = false; var prompt = tabletRoot.customInputDialog({ - textInput: { - label: "Model URL", - text: defaultURL - }, - comboBox: { - label: "Automatic Collisions", - index: SHAPE_TYPE_DEFAULT, - items: SHAPE_TYPES - }, - checkBox: { - label: "Dynamic", - checked: DYNAMIC_DEFAULT, - disableForItems: [ - SHAPE_TYPE_STATIC_MESH - ], - checkStateOnDisable: false, - warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors" - } - }); + textInput: { + label: "Model URL", + text: defaultURL + }, + comboBox: { + label: "Automatic Collisions", + index: SHAPE_TYPE_DEFAULT, + items: SHAPE_TYPES + }, + checkBox: { + label: "Dynamic", + checked: DYNAMIC_DEFAULT, + disableForItems: [ + SHAPE_TYPE_STATIC_MESH + ], + checkStateOnDisable: false, + warningOnDisable: "Models with 'Exact' automatic collisions cannot be dynamic, and should not be used as floors" + } + }); prompt.selected.connect(function (jsonResult) { if (jsonResult) { @@ -205,23 +205,23 @@ Rectangle { var url = result.textInput.trim(); var shapeType; switch (result.comboBox) { - case SHAPE_TYPE_SIMPLE_HULL: - shapeType = "simple-hull"; - break; - case SHAPE_TYPE_SIMPLE_COMPOUND: - shapeType = "simple-compound"; - break; - case SHAPE_TYPE_STATIC_MESH: - shapeType = "static-mesh"; - break; - case SHAPE_TYPE_BOX: - shapeType = "box"; - break; - case SHAPE_TYPE_SPHERE: - shapeType = "sphere"; - break; - default: - shapeType = "none"; + case SHAPE_TYPE_SIMPLE_HULL: + shapeType = "simple-hull"; + break; + case SHAPE_TYPE_SIMPLE_COMPOUND: + shapeType = "simple-compound"; + break; + case SHAPE_TYPE_STATIC_MESH: + shapeType = "static-mesh"; + break; + case SHAPE_TYPE_BOX: + shapeType = "box"; + break; + case SHAPE_TYPE_SPHERE: + shapeType = "sphere"; + break; + default: + shapeType = "none"; } var dynamic = result.checkBox !== null ? result.checkBox : DYNAMIC_DEFAULT; @@ -230,7 +230,7 @@ Rectangle { print("Error: model cannot be both static mesh and dynamic. This should never happen."); } else if (url) { var name = assetProxyModel.data(treeView.selection.currentIndex); - var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getFront(MyAvatar.orientation))); + var addPosition = Vec3.sum(MyAvatar.position, Vec3.multiply(2, Quat.getForward(MyAvatar.orientation))); var gravity; if (dynamic) { // Create a vector <0, -10, 0>. { x: 0, y: -10, z: 0 } won't work because Qt is dumb and this is a @@ -293,10 +293,10 @@ Rectangle { } var object = tabletRoot.inputDialog({ - label: "Enter new path:", - current: path, - placeholderText: "Enter path here" - }); + label: "Enter new path:", + current: path, + placeholderText: "Enter path here" + }); object.selected.connect(function(destinationPath) { destinationPath = destinationPath.trim(); @@ -339,12 +339,12 @@ Rectangle { } var object = tabletRoot.messageBox({ - icon: hifi.icons.question, - buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No, - defaultButton: OriginalDialogs.StandardButton.Yes, - title: "Delete", - text: modalMessage - }); + icon: hifi.icons.question, + buttons: OriginalDialogs.StandardButton.Yes + OriginalDialogs.StandardButton.No, + defaultButton: OriginalDialogs.StandardButton.Yes, + title: "Delete", + text: modalMessage + }); object.selected.connect(function(button) { if (button === OriginalDialogs.StandardButton.Yes) { doDeleteFile(path); @@ -379,38 +379,38 @@ Rectangle { var filename = fileUrl.slice(fileUrl.lastIndexOf('/') + 1); Assets.uploadFile(fileUrl, directory + filename, - function() { - // Upload started - uploadSpinner.visible = true; - uploadButton.enabled = false; - uploadProgressLabel.text = "In progress..."; - }, - function(err, path) { - print(err, path); - if (err === "") { - uploadProgressLabel.text = "Upload Complete"; - timer.interval = 1000; - timer.repeat = false; - timer.triggered.connect(function() { - uploadSpinner.visible = false; - uploadButton.enabled = true; - uploadOpen = false; - }); - timer.start(); - console.log("Asset Browser - finished uploading: ", fileUrl); - reload(); - } else { - uploadSpinner.visible = false; - uploadButton.enabled = true; - uploadOpen = false; + function() { + // Upload started + uploadSpinner.visible = true; + uploadButton.enabled = false; + uploadProgressLabel.text = "In progress..."; + }, + function(err, path) { + print(err, path); + if (err === "") { + uploadProgressLabel.text = "Upload Complete"; + timer.interval = 1000; + timer.repeat = false; + timer.triggered.connect(function() { + uploadSpinner.visible = false; + uploadButton.enabled = true; + uploadOpen = false; + }); + timer.start(); + console.log("Asset Browser - finished uploading: ", fileUrl); + reload(); + } else { + uploadSpinner.visible = false; + uploadButton.enabled = true; + uploadOpen = false; - if (err !== -1) { - console.log("Asset Browser - error uploading: ", fileUrl, " - error ", err); - var box = errorMessageBox("There was an error uploading:\n" + fileUrl + "\n" + err); - box.selected.connect(reload); - } - } - }, dropping); + if (err !== -1) { + console.log("Asset Browser - error uploading: ", fileUrl, " - error ", err); + var box = errorMessageBox("There was an error uploading:\n" + fileUrl + "\n" + err); + box.selected.connect(reload); + } + } + }, dropping); } function initiateUpload(url) { @@ -421,9 +421,9 @@ Rectangle { doUpload(fileUrl, true); } else { var browser = tabletRoot.fileDialog({ - selectDirectory: false, - dir: currentDirectory - }); + selectDirectory: false, + dir: currentDirectory + }); browser.canceled.connect(function() { uploadOpen = false; @@ -445,11 +445,11 @@ Rectangle { function errorMessageBox(message) { return tabletRoot.messageBox({ - icon: hifi.icons.warning, - defaultButton: OriginalDialogs.StandardButton.Ok, - title: "Error", - text: message - }); + icon: hifi.icons.warning, + defaultButton: OriginalDialogs.StandardButton.Ok, + title: "Error", + text: message + }); } Column { From c4d018504bf28de8e37207da57ff0cc6ffca06da Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 15 Sep 2017 11:46:28 -0700 Subject: [PATCH 133/202] Pull Auto-Baking changes to TabletAssetServer.qml --- .../qml/hifi/dialogs/TabletAssetServer.qml | 253 +++++++++++++++--- 1 file changed, 221 insertions(+), 32 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 250c13c405..cb2635a2ab 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -10,6 +10,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 as OriginalDialogs import Qt.labs.settings 1.0 @@ -469,15 +470,6 @@ Rectangle { height: 30 spacing: hifi.dimensions.contentSpacing.x - HifiControls.GlyphButton { - glyph: hifi.glyphs.reload - color: hifi.buttons.black - colorScheme: root.colorScheme - width: hifi.dimensions.controlLineHeight - - onClicked: root.reload() - } - HifiControls.Button { text: "Add To World" color: hifi.buttons.black @@ -510,8 +502,193 @@ Rectangle { onClicked: root.deleteFile() enabled: treeView.selection.hasSelection } + + HifiControls.GlyphButton { + + glyph: hifi.glyphs.reload + color: hifi.buttons.black + colorScheme: root.colorScheme + width: hifi.dimensions.controlLineHeight + + onClicked: root.reload() + } + } + } + + HifiControls.Tree { + id: treeView + anchors.top: assetDirectory.bottom + anchors.bottom: infoRow.top + anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border + anchors.left: parent.left + anchors.right: parent.right + + treeModel: assetProxyModel + selectionMode: SelectionMode.ExtendedSelection + headerVisible: true + sortIndicatorVisible: true + + colorScheme: root.colorScheme + + modifyEl: renameEl + + TableViewColumn { + id: nameColumn + title: "Name:" + role: "name" + width: treeView.width - bakedColumn.width; + } + TableViewColumn { + id: bakedColumn + title: "Use Baked?" + role: "baked" + width: 100 + } + + itemDelegate: Loader { + id: itemDelegateLoader + + anchors { + left: parent ? parent.left : undefined + leftMargin: (styleData.column === 0 ? (2 + styleData.depth) : 1) * hifi.dimensions.tablePadding + right: parent ? parent.right : undefined + rightMargin: hifi.dimensions.tablePadding + verticalCenter: parent ? parent.verticalCenter : undefined + } + + function convertToGlyph(text) { + switch (text) { + case "Not Baked": + return hifi.glyphs.circleSlash; + case "Baked": + return hifi.glyphs.check_2_01; + case "Error": + return hifi.glyphs.alert; + default: + return ""; + } + } + + function getComponent() { + if ((styleData.column === 0) && styleData.selected) { + return textFieldComponent; + } else if (convertToGlyph(styleData.value) != "") { + return glyphComponent; + } else { + return labelComponent; + } + + } + sourceComponent: getComponent() + + Component { + id: labelComponent + FiraSansSemiBold { + text: styleData.value + size: hifi.fontSizes.tableText + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + + elide: Text.ElideRight + horizontalAlignment: styleData.column === 1 ? TextInput.AlignHCenter : TextInput.AlignLeft + } + } + Component { + id: glyphComponent + + HiFiGlyphs { + text: convertToGlyph(styleData.value) + size: hifi.dimensions.frameIconSize + color: colorScheme == hifi.colorSchemes.light + ? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight) + : (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText) + + elide: Text.ElideRight + horizontalAlignment: TextInput.AlignHCenter + + HifiControls.ToolTip { + anchors.fill: parent + + visible: styleData.value === "Error" + + toolTip: assetProxyModel.data(styleData.index, 0x106) + } + } + } + Component { + id: textFieldComponent + + TextField { + id: textField + readOnly: !activeFocus + + text: styleData.value + + FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + font.family: firaSansSemiBold.name + font.pixelSize: hifi.fontSizes.textFieldInput + height: hifi.dimensions.tableRowHeight + + style: TextFieldStyle { + textColor: readOnly + ? hifi.colors.black + : (treeView.isLightColorScheme ? hifi.colors.black : hifi.colors.white) + background: Rectangle { + visible: !readOnly + color: treeView.isLightColorScheme ? hifi.colors.white : hifi.colors.black + border.color: hifi.colors.primaryHighlight + border.width: 1 + } + selectedTextColor: hifi.colors.black + selectionColor: hifi.colors.primaryHighlight + padding.left: readOnly ? 0 : hifi.dimensions.textPadding + padding.right: readOnly ? 0 : hifi.dimensions.textPadding + } + + validator: RegExpValidator { + regExp: /[^/]+/ + } + + Keys.onPressed: { + if (event.key == Qt.Key_Escape) { + text = styleData.value; + unfocusHelper.forceActiveFocus(); + event.accepted = true; + } + } + onAccepted: { + if (acceptableInput && styleData.selected) { + if (!treeView.modifyEl(treeView.selection.currentIndex, text)) { + text = styleData.value; + } + unfocusHelper.forceActiveFocus(); + } + } + + onReadOnlyChanged: { + // Have to explicily set keyboardRaised because automatic setting fails because readOnly is true at the time. + keyboardRaised = activeFocus; + } + } + } } + + MouseArea { + propagateComposedEvents: true + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: { + if (!HMD.active) { // Popup only displays properly on desktop + var index = treeView.indexAt(mouse.x, mouse.y); + treeView.selection.setCurrentIndex(index, 0x0002); + contextMenu.currentIndex = index; + contextMenu.popup(); + } + } + } + Menu { id: contextMenu title: "Edit" @@ -539,39 +716,51 @@ Rectangle { } } } - } - HifiControls.Tree { - id: treeView - 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 - anchors.right: parent.right - treeModel: assetProxyModel - canEdit: true - colorScheme: root.colorScheme - selectionMode: SelectionMode.ExtendedSelection + Row { + id: infoRow + anchors.left: treeView.left + anchors.right: treeView.right + anchors.bottom: uploadSection.top + anchors.bottomMargin: hifi.dimensions.contentSpacing.y + spacing: hifi.dimensions.contentSpacing.x + + RalewayRegular { + size: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase + text: selectedItems + " items selected" + color: hifi.colors.lightGrayText + } - modifyEl: renameEl + HifiControls.CheckBox { + function isChecked() { + var status = assetProxyModel.data(treeView.selection.currentIndex, 0x105); + var bakingDisabled = (status === "Not Baked" || status === "--"); + return selectedItems === 1 && !bakingDisabled; + } - MouseArea { - propagateComposedEvents: true - anchors.fill: parent - acceptedButtons: Qt.RightButton + text: "Use baked (optimized) versions" + colorScheme: root.colorScheme + enabled: selectedItems === 1 && assetProxyModel.data(treeView.selection.currentIndex, 0x105) !== "--" + checked: isChecked() onClicked: { - if (!HMD.active) { // Popup only displays properly on desktop - var index = treeView.indexAt(mouse.x, mouse.y); - treeView.selection.setCurrentIndex(index, 0x0002); - contextMenu.currentIndex = index; - contextMenu.popup(); + var mappings = []; + for (var i in treeView.selection.selectedIndexes) { + var index = treeView.selection.selectedIndexes[i]; + var path = assetProxyModel.data(index, 0x100); + mappings.push(path); } + print("Setting baking enabled:" + mappings + checked); + Assets.setBakingEnabled(mappings, checked, function() { + reload(); + }); + + checked = Qt.binding(isChecked); } } } - HifiControls.TabletContentSection { id: uploadSection name: "Upload A File" From c5852dfbe81ca620c0bdf6c2b8178e91241ba3f2 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 15 Sep 2017 11:43:06 -0700 Subject: [PATCH 134/202] Add list compression to FBXWriter --- libraries/baking/src/FBXBaker.cpp | 6 ++--- libraries/fbx/src/FBX.h | 6 ++--- libraries/fbx/src/FBXReader_Mesh.cpp | 8 +++---- libraries/fbx/src/FBXReader_Node.cpp | 6 ++--- libraries/fbx/src/FBXWriter.cpp | 33 +++++++++++++++++++++++----- libraries/fbx/src/FBXWriter.h | 2 +- 6 files changed, 40 insertions(+), 21 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 46f3df7493..aa05a135fd 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -405,9 +405,9 @@ void FBXBaker::rewriteAndBakeSceneModels() { if (needsOriginalIndices) { meshBuilder.SetAttributeValuesForFace(originalIndexAttributeID, face, - &mesh.originalIndex[idx0], - &mesh.originalIndex[idx1], - &mesh.originalIndex[idx2]); + &mesh.originalIndices[idx0], + &mesh.originalIndices[idx1], + &mesh.originalIndices[idx2]); } if (hasNormals) { meshBuilder.SetAttributeValuesForFace(normalsAttributeID, face, diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 78d35889cf..836195f125 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -33,13 +33,13 @@ static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; static const quint32 FBX_VERSION_2015 = 7400; static const quint32 FBX_VERSION_2016 = 7500; - -// TODO Convert to GeometryAttribute type static const int DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES = 1000; static const int DRACO_ATTRIBUTE_MATERIAL_ID = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES; static const int DRACO_ATTRIBUTE_TEX_COORD_1 = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 1; static const int DRACO_ATTRIBUTE_ORIGINAL_INDEX = DRACO_BEGIN_CUSTOM_HIFI_ATTRIBUTES + 2; +static const int32_t FBX_PROPERTY_UNCOMPRESSED_FLAG = 0; +static const int32_t FBX_PROPERTY_COMPRESSED_FLAG = 1; class FBXNode; using FBXNodeList = QList; @@ -218,7 +218,6 @@ public: QVector parts; QVector vertices; - QVector originalIndex; QVector normals; QVector tangents; QVector colors; @@ -226,6 +225,7 @@ public: QVector texCoords1; QVector clusterIndices; QVector clusterWeights; + QVector originalIndices; QVector clusters; diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index 3d70fa45ae..d4b6dde5fe 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -355,7 +355,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn auto texCoordAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::TEX_COORD); auto extraTexCoordAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_TEX_COORD_1); auto colorAttribute = dracoMesh->GetNamedAttribute(draco::GeometryAttribute::COLOR); - auto matTexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID); + auto materialIDAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_MATERIAL_ID); auto originalIndexAttribute = dracoMesh->GetAttributeByUniqueId(DRACO_ATTRIBUTE_ORIGINAL_INDEX); // setup extracted mesh data structures given number of points @@ -445,11 +445,11 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn uint16_t materialID { 0 }; - if (matTexAttribute) { + if (materialIDAttribute) { // read material ID and texture ID mappings into materials and texture vectors - auto mappedIndex = matTexAttribute->mapped_index(firstCorner); + auto mappedIndex = materialIDAttribute->mapped_index(firstCorner); - matTexAttribute->ConvertValue(mappedIndex, &materialID); + materialIDAttribute->ConvertValue(mappedIndex, &materialID); } QPair materialTexture(materialID, 0); diff --git a/libraries/fbx/src/FBXReader_Node.cpp b/libraries/fbx/src/FBXReader_Node.cpp index 111b4a295a..c4454421b6 100644 --- a/libraries/fbx/src/FBXReader_Node.cpp +++ b/libraries/fbx/src/FBXReader_Node.cpp @@ -48,9 +48,8 @@ QVariant readBinaryArray(QDataStream& in, int& position) { QVector values; if ((int)QSysInfo::ByteOrder == (int)in.byteOrder()) { values.resize(arrayLength); - const unsigned int DEFLATE_ENCODING = 1; QByteArray arrayData; - if (encoding == DEFLATE_ENCODING) { + if (encoding == FBX_PROPERTY_COMPRESSED_FLAG) { // preface encoded data with uncompressed length QByteArray compressed(sizeof(quint32) + compressedLength, 0); *((quint32*)compressed.data()) = qToBigEndian(arrayLength * sizeof(T)); @@ -72,8 +71,7 @@ QVariant readBinaryArray(QDataStream& in, int& position) { } } else { values.reserve(arrayLength); - const unsigned int DEFLATE_ENCODING = 1; - if (encoding == DEFLATE_ENCODING) { + if (encoding == FBX_PROPERTY_COMPRESSED_FLAG) { // preface encoded data with uncompressed length QByteArray compressed(sizeof(quint32) + compressedLength, 0); *((quint32*)compressed.data()) = qToBigEndian(arrayLength * sizeof(T)); diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index f7bf3517f6..bc0013b9ac 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -24,13 +24,34 @@ #endif template -void writeVector(QDataStream& out, char ch, QVector list) { - out.device()->write(&ch, 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; +void writeVector(QDataStream& out, char ch, QVector vec) { + // Minimum number of bytes to consider compressing + const int ATTEMPT_COMPRESSION_THRESHOLD_BYTES = 2000; - out.writeBytes(reinterpret_cast(list.constData()), list.length() * sizeof(T)); + out.device()->write(&ch, 1); + out << (int32_t)vec.length(); + + auto data { QByteArray::fromRawData((const char*)vec.constData(), vec.length() * sizeof(T)) }; + + if (data.size() >= ATTEMPT_COMPRESSION_THRESHOLD_BYTES) { + auto compressedDataWithLength { qCompress(data) }; + + // qCompress packs a length uint32 at the beginning of the buffer, but the FBX format + // does not expect it. This removes it. + auto compressedData = QByteArray::fromRawData( + compressedDataWithLength.constData() + sizeof(uint32_t), compressedDataWithLength.size() - sizeof(uint32_t)); + + if (compressedData.size() < data.size()) { + out << (int32_t)1; + out << (int32_t)compressedData.size(); + out.writeRawData(compressedData.constData(), compressedData.size()); + return; + } + } + + out << FBX_PROPERTY_UNCOMPRESSED_FLAG; + out << (int32_t)0; + out.writeRawData(data.constData(), data.size()); } diff --git a/libraries/fbx/src/FBXWriter.h b/libraries/fbx/src/FBXWriter.h index 57c33e0e1c..f20d208cb1 100644 --- a/libraries/fbx/src/FBXWriter.h +++ b/libraries/fbx/src/FBXWriter.h @@ -17,7 +17,7 @@ #include #include -#define USE_FBX_2016_FORMAT +//#define USE_FBX_2016_FORMAT class FBXWriter { public: From 2a4f7712c32988ada4b5862f442199fefd837452 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 15 Sep 2017 14:41:38 -0700 Subject: [PATCH 135/202] Replace magic number for compressed flag in FBXWriter --- libraries/fbx/src/FBXWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index bc0013b9ac..900fddc0a2 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -42,7 +42,7 @@ void writeVector(QDataStream& out, char ch, QVector vec) { compressedDataWithLength.constData() + sizeof(uint32_t), compressedDataWithLength.size() - sizeof(uint32_t)); if (compressedData.size() < data.size()) { - out << (int32_t)1; + out << FBX_PROPERTY_COMPRESSED_FLAG; out << (int32_t)compressedData.size(); out.writeRawData(compressedData.constData(), compressedData.size()); return; From a37642ba44b10701c404e39176f942a9362320c1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 15 Sep 2017 11:46:28 -0700 Subject: [PATCH 136/202] Pull Auto-Baking changes to TabletAssetServer.qml --- interface/resources/qml/hifi/dialogs/TabletAssetServer.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index cb2635a2ab..61f5a382ad 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -517,8 +517,6 @@ Rectangle { HifiControls.Tree { id: treeView - anchors.top: assetDirectory.bottom - anchors.bottom: infoRow.top anchors.margins: hifi.dimensions.contentMargin.x + 2 // Extra for border anchors.left: parent.left anchors.right: parent.right @@ -722,7 +720,6 @@ Rectangle { id: infoRow anchors.left: treeView.left anchors.right: treeView.right - anchors.bottom: uploadSection.top anchors.bottomMargin: hifi.dimensions.contentSpacing.y spacing: hifi.dimensions.contentSpacing.x From 871b169128ef7658aba174751b5fa7faea311328 Mon Sep 17 00:00:00 2001 From: beholder Date: Sat, 16 Sep 2017 16:17:36 +0300 Subject: [PATCH 137/202] 6618 [Worklist] Add Input Field On Top of Keyboard --- interface/resources/qml/controls-uit/Key.qml | 3 + .../resources/qml/controls-uit/Keyboard.qml | 111 ++++++++---------- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 4 +- libraries/ui/src/ui/OffscreenQmlSurface.h | 2 +- 4 files changed, 57 insertions(+), 63 deletions(-) diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index 2218474936..0c888d1a0a 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -34,7 +34,10 @@ Item { onClicked: { mouse.accepted = true; + webEntity.synthesizeKeyPress(glyph); + webEntity.synthesizeKeyPress(glyph, mirrorText); + if (toggle) { toggled = !toggled; } diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml index 07488fe3e6..4739534fcd 100644 --- a/interface/resources/qml/controls-uit/Keyboard.qml +++ b/interface/resources/qml/controls-uit/Keyboard.qml @@ -9,6 +9,7 @@ // import QtQuick 2.0 +import "." Item { id: keyboardBase @@ -16,9 +17,15 @@ Item { property bool raised: false property bool numeric: false + readonly property int keyboardRowHeight: 50 + readonly property int keyboardWidth: 480 + + readonly property int mirrorTextHeight: keyboardRowHeight + + property bool showMirrorText: true readonly property int raisedHeight: 200 - height: enabled && raised ? raisedHeight : 0 + height: enabled && raised ? raisedHeight + (showMirrorText ? keyboardRowHeight : 0) : 0 visible: enabled && raised property bool shiftMode: false @@ -93,24 +100,35 @@ Item { } Rectangle { - id: leftRect y: 0 - height: 200 + x: 0 + height: showMirrorText ? mirrorTextHeight : 0 + width: keyboardWidth color: "#252525" - anchors.right: keyboardRect.left - anchors.rightMargin: 0 - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 + + TextEdit { + id: mirrorText + visible: showMirrorText + size: 13.5 + horizontalAlignment: Text.AlignHCenter + color: "#FFFFFF"; + anchors.fill: parent + wrapMode: Text.WordWrap + readOnly: false // we need to leave this property read-only to allow control to accept QKeyEvent + selectByMouse: false + } + + MouseArea { // ... and we need this mouse area to prevent mirrorText from getting mouse events to ensure it will never get focus + anchors.fill: parent + } } Rectangle { id: keyboardRect - x: 206 - y: 0 - width: 480 - height: 200 + x: 0 + y: showMirrorText ? mirrorTextHeight : 0 + width: keyboardWidth + height: raisedHeight color: "#252525" anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom @@ -118,13 +136,13 @@ Item { Column { id: columnAlpha - width: 480 - height: 200 + width: keyboardWidth + height: raisedHeight visible: !numeric Row { - width: 480 - height: 50 + width: keyboardWidth + height: keyboardRowHeight anchors.left: parent.left anchors.leftMargin: 4 @@ -142,8 +160,8 @@ Item { } Row { - width: 480 - height: 50 + width: keyboardWidth + height: keyboardRowHeight anchors.left: parent.left anchors.leftMargin: 20 @@ -160,8 +178,8 @@ Item { } Row { - width: 480 - height: 50 + width: keyboardWidth + height: keyboardRowHeight anchors.left: parent.left anchors.leftMargin: 4 @@ -185,8 +203,8 @@ Item { } Row { - width: 480 - height: 50 + width: keyboardWidth + height: keyboardRowHeight anchors.left: parent.left anchors.leftMargin: 4 @@ -205,13 +223,13 @@ Item { Column { id: columnNumeric - width: 480 - height: 200 + width: keyboardWidth + height: raisedHeight visible: numeric Row { - width: 480 - height: 50 + width: keyboardWidth + height: keyboardRowHeight anchors.left: parent.left anchors.leftMargin: 4 @@ -229,8 +247,8 @@ Item { } Row { - width: 480 - height: 50 + width: keyboardWidth + height: keyboardRowHeight anchors.left: parent.left anchors.leftMargin: 4 @@ -248,8 +266,8 @@ Item { } Row { - width: 480 - height: 50 + width: keyboardWidth + height: keyboardRowHeight anchors.left: parent.left anchors.leftMargin: 4 @@ -273,8 +291,8 @@ Item { } Row { - width: 480 - height: 50 + width: keyboardWidth + height: keyboardRowHeight anchors.left: parent.left anchors.leftMargin: 4 @@ -291,31 +309,4 @@ Item { } } } - - Rectangle { - id: rightRect - y: 280 - height: 200 - color: "#252525" - border.width: 0 - anchors.left: keyboardRect.right - anchors.leftMargin: 0 - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - } - - Rectangle { - id: rectangle1 - color: "#ffffff" - anchors.bottom: keyboardRect.top - anchors.bottomMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.top: parent.top - anchors.topMargin: 0 - } } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index d03842d45a..b4590fdca9 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -987,8 +987,8 @@ static bool equals(const QByteArray& byteArray, const uint8_t* ptr) { return ptr[i] == 0x00; } -void OffscreenQmlSurface::synthesizeKeyPress(QString key) { - auto eventHandler = getEventHandler(); +void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* target) { + auto eventHandler = target ? target : getEventHandler(); if (eventHandler) { auto utf8Key = key.toUtf8(); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 95dabdef0f..5b57c6ea80 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -79,7 +79,7 @@ public: bool eventFilter(QObject* originalDestination, QEvent* event) override; void setKeyboardRaised(QObject* object, bool raised, bool numeric = false); - Q_INVOKABLE void synthesizeKeyPress(QString key); + Q_INVOKABLE void synthesizeKeyPress(QString key, QObject* target = nullptr); using TextureAndFence = std::pair; // Checks to see if a new texture is available. If one is, the function returns true and From d80aa50e79332899e3cb7f3a8837eeee39322173 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 18 Sep 2017 09:03:57 -0700 Subject: [PATCH 138/202] Remove support for QList in FBXWriter --- libraries/fbx/src/FBXWriter.cpp | 60 --------------------------------- 1 file changed, 60 deletions(-) diff --git a/libraries/fbx/src/FBXWriter.cpp b/libraries/fbx/src/FBXWriter.cpp index 900fddc0a2..5029b489bc 100644 --- a/libraries/fbx/src/FBXWriter.cpp +++ b/libraries/fbx/src/FBXWriter.cpp @@ -178,66 +178,6 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) { break; } - // TODO Delete? Do we ever use QList instead of QVector? - case QVariant::Type::List: - { - auto list = prop.toList(); - auto listType = prop.userType(); - - switch (listType) { - case QMetaType::Float: - out.device()->write("f", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& innerProp : list) { - out << innerProp.toFloat(); - } - break; - - case QMetaType::Double: - out.device()->write("d", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& innerProp : list) { - out << innerProp.toDouble(); - } - break; - - case QMetaType::LongLong: - out.device()->write("l", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& innerProp : list) { - out << innerProp.toLongLong(); - } - break; - - case QMetaType::Int: - out.device()->write("i", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& innerProp : list) { - out << innerProp.toInt(); - } - break; - - case QMetaType::Bool: - out.device()->write("b", 1); - out << (int32_t)list.length(); - out << (int32_t)0; - out << (int32_t)0; - for (auto& innerProp : list) { - out << innerProp.toBool(); - } - break; - } - } - break; - default: { if (prop.canConvert>()) { From 8b39c56aa1ef63c741c376e45423d253dc900c7e Mon Sep 17 00:00:00 2001 From: beholder Date: Mon, 18 Sep 2017 20:40:22 +0300 Subject: [PATCH 139/202] rename 'target' to 'targetOverride' to indicate that parameter overrides default target --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 4 ++-- libraries/ui/src/ui/OffscreenQmlSurface.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index b4590fdca9..faf7933d1a 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -987,8 +987,8 @@ static bool equals(const QByteArray& byteArray, const uint8_t* ptr) { return ptr[i] == 0x00; } -void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* target) { - auto eventHandler = target ? target : getEventHandler(); +void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverride) { + auto eventHandler = targetOverride ? targetOverride : getEventHandler(); if (eventHandler) { auto utf8Key = key.toUtf8(); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 5b57c6ea80..990f81848d 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -79,7 +79,7 @@ public: bool eventFilter(QObject* originalDestination, QEvent* event) override; void setKeyboardRaised(QObject* object, bool raised, bool numeric = false); - Q_INVOKABLE void synthesizeKeyPress(QString key, QObject* target = nullptr); + Q_INVOKABLE void synthesizeKeyPress(QString key, QObject* targetOverride = nullptr); using TextureAndFence = std::pair; // Checks to see if a new texture is available. If one is, the function returns true and From 5187ac4761b422371d4cd388e97ea952983a44b6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 15 Sep 2017 11:23:09 -0700 Subject: [PATCH 140/202] Avatar Attachments now render correctly * moved Avatar::fixupModelsInScene off the newly created render thread back to the main thread. * updated Avatar::postUpdate to take a scene pointer, necessary for Avatar::fixupModelsInScene * updated developer/tests/avatarAttachementTest.js to account for recent changes in avatar attachment scale, commit 712aff7ad. * updated initialization of anim graph to use Model::isLoaded() instead of Model::initWhenReady() to know when the model was completely loaded. --- interface/src/Application.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 4 ++-- interface/src/avatar/AvatarManager.h | 2 +- interface/src/avatar/MyAvatar.cpp | 6 +++--- interface/src/avatar/MyAvatar.h | 2 +- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 8 +++----- libraries/avatars-renderer/src/avatars-renderer/Avatar.h | 2 +- scripts/developer/tests/avatarAttachmentTest.js | 4 ++-- 8 files changed, 14 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3650c495f2..e6524271cf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5209,7 +5209,7 @@ void Application::update(float deltaTime) { } } - avatarManager->postUpdate(deltaTime); + avatarManager->postUpdate(deltaTime, getMain3DScene()); { PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e7a83b5abc..a6d77c8d03 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -259,12 +259,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { simulateAvatarFades(deltaTime); } -void AvatarManager::postUpdate(float deltaTime) { +void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scene) { auto hashCopy = getHashCopy(); AvatarHash::iterator avatarIterator = hashCopy.begin(); for (avatarIterator = hashCopy.begin(); avatarIterator != hashCopy.end(); avatarIterator++) { auto avatar = std::static_pointer_cast(avatarIterator.value()); - avatar->postUpdate(deltaTime); + avatar->postUpdate(deltaTime, scene); } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 810d419a55..cef6bd4c9c 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -55,7 +55,7 @@ public: void updateMyAvatar(float deltaTime); void updateOtherAvatars(float deltaTime); - void postUpdate(float deltaTime); + void postUpdate(float deltaTime, const render::ScenePointer& scene); void clearOtherAvatars(); void deleteAllAvatars(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8592ff3d74..075e471cf5 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1814,11 +1814,11 @@ void MyAvatar::destroyAnimGraph() { _skeletonModel->getRig().destroyAnimGraph(); } -void MyAvatar::postUpdate(float deltaTime) { +void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { - Avatar::postUpdate(deltaTime); + Avatar::postUpdate(deltaTime, scene); - if (DependencyManager::get()->shouldRenderAvatars() && _skeletonModel->initWhenReady(qApp->getMain3DScene())) { + if (_skeletonModel->isLoaded() && !_skeletonModel->getRig().getAnimNode()) { initHeadBones(); _skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 65dcc12e7d..ce70eaa9dc 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -196,7 +196,7 @@ public: Q_INVOKABLE void clearIKJointLimitHistory(); // thread-safe void update(float deltaTime); - virtual void postUpdate(float deltaTime) override; + virtual void postUpdate(float deltaTime, const render::ScenePointer& scene) override; void preDisplaySide(RenderArgs* renderArgs); const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index bc6bb2a0ec..fc51501161 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -553,7 +553,7 @@ void Avatar::updateRenderItem(render::Transaction& transaction) { } } -void Avatar::postUpdate(float deltaTime) { +void Avatar::postUpdate(float deltaTime, const render::ScenePointer& scene) { if (isMyAvatar() ? showMyLookAtVectors : showOtherLookAtVectors) { const float EYE_RAY_LENGTH = 10.0; @@ -577,6 +577,8 @@ void Avatar::postUpdate(float deltaTime) { DebugDraw::getInstance().drawRay(rightEyePosition, rightEyePosition + rightEyeRotation * Vectors::UNIT_Z * EYE_RAY_LENGTH, RED); } } + + fixupModelsInScene(scene); } void Avatar::render(RenderArgs* renderArgs) { @@ -648,10 +650,6 @@ void Avatar::render(RenderArgs* renderArgs) { return; } - if (!isMyAvatar()) { - fixupModelsInScene(renderArgs->_scene); - } - if (showCollisionShapes && shouldRenderHead(renderArgs) && _skeletonModel->isRenderable()) { PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes"); const float BOUNDING_SHAPE_ALPHA = 0.7f; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index d35d6ceba0..c262ce2009 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -98,7 +98,7 @@ public: void updateRenderItem(render::Transaction& transaction); - virtual void postUpdate(float deltaTime); + virtual void postUpdate(float deltaTime, const render::ScenePointer& scene); //setters void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; } diff --git a/scripts/developer/tests/avatarAttachmentTest.js b/scripts/developer/tests/avatarAttachmentTest.js index f287013979..c9f2e1615b 100644 --- a/scripts/developer/tests/avatarAttachmentTest.js +++ b/scripts/developer/tests/avatarAttachmentTest.js @@ -89,9 +89,9 @@ var coatButton = new ToggleButtonBuddy(buttonPositionX, buttonPositionY, BUTTON_ var HAT_ATTACHMENT = { modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx", jointName: "Head", - translation: {"x": 0, "y": 0.2, "z": 0}, + translation: {"x": 0, "y": 0.25, "z": 0.03}, rotation: {"x": 0, "y": 0, "z": 0, "w": 1}, - scale: 1, + scale: 0.052, isSoft: false }; From a8bf4efc8b44d576e341d63dcaa8703671d606da Mon Sep 17 00:00:00 2001 From: samcake Date: Thu, 14 Sep 2017 18:36:20 -0700 Subject: [PATCH 141/202] Brining the changes just to fix the tablet issue --- interface/src/ui/overlays/Base3DOverlay.cpp | 63 ++++++++++++++++++- interface/src/ui/overlays/Base3DOverlay.h | 9 +++ interface/src/ui/overlays/Planar3DOverlay.cpp | 8 +++ interface/src/ui/overlays/Planar3DOverlay.h | 4 +- interface/src/ui/overlays/Web3DOverlay.cpp | 17 +---- 5 files changed, 84 insertions(+), 17 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index c0278a6496..b450fbdc05 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -256,15 +256,74 @@ bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3 void Base3DOverlay::locationChanged(bool tellPhysics) { SpatiallyNestable::locationChanged(tellPhysics); - auto itemID = getRenderItemID(); + // Force the actual update of the render transform now that we notify for the change + // so it s captured for the time of rendering + notifyRenderTransformChange(); + + /* auto itemID = getRenderItemID(); if (render::Item::isValidID(itemID)) { render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; transaction.updateItem(itemID); scene->enqueueTransaction(transaction); - } + }*/ } void Base3DOverlay::parentDeleted() { qApp->getOverlays().deleteOverlay(getOverlayID()); } + +void Base3DOverlay::update(float duration) { + if (_renderTransformDirty) { + auto self = this; + // queue up this work for later processing, at the end of update and just before rendering. + // the application will ensure only the last lambda is actually invoked. + /* void* key = (void*)this; + std::weak_ptr weakSelf = shared_from_this(); + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() { + // do nothing, if the model has already been destroyed. + auto spatiallyNestableSelf = weakSelf.lock(); + if (!spatiallyNestableSelf) { + return; + } + auto self = std::dynamic_pointer_cast(spatiallyNestableSelf); + */ + #ifdef UpdateInMain + self->setRenderTransform(self->evalRenderTransform()); + #else + auto renderTransform = self->evalRenderTransform(); + #endif + auto itemID = self->getRenderItemID(); + if (render::Item::isValidID(itemID)) { + render::ScenePointer scene = qApp->getMain3DScene(); + render::Transaction transaction; + #ifdef UpdateInMain + transaction.updateItem(itemID); + #else + transaction.updateItem(itemID, [renderTransform](Overlay& data) { + auto overlay3D = dynamic_cast(&data); + if (overlay3D) { + auto latestTransform = overlay3D->evalRenderTransform(); + overlay3D->setRenderTransform(latestTransform);// evalRenderTransform(); + } + }); + #endif + scene->enqueueTransaction(transaction); + + } + // }); + _renderTransformDirty = false; + } +} + +void Base3DOverlay::notifyRenderTransformChange() const { + _renderTransformDirty = true; +} + +Transform Base3DOverlay::evalRenderTransform() const { + return getTransform(); +} + +void Base3DOverlay::setRenderTransform(const Transform& transform) { + _renderTransform = transform; +} \ No newline at end of file diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 6377b46d7d..b2167852d4 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -52,6 +52,12 @@ public: virtual AABox getBounds() const override = 0; + void update(float deltatime) override; + + void notifyRenderTransformChange() const; + virtual Transform evalRenderTransform() const; + void setRenderTransform(const Transform& transform); + void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; @@ -67,12 +73,15 @@ protected: virtual void locationChanged(bool tellPhysics = true) override; virtual void parentDeleted() override; + mutable Transform _renderTransform; + float _lineWidth; bool _isSolid; bool _isDashedLine; bool _ignoreRayIntersection; bool _drawInFront; bool _isGrabbable { false }; + mutable bool _renderTransformDirty{ true }; QString _name; }; diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index 58d72b100b..e2877e1e07 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -66,3 +66,11 @@ bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve // FIXME - face and surfaceNormal not being returned return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), getDimensions(), distance); } + +Transform Planar3DOverlay::evalRenderTransform() const { + auto transform = getTransform(); + if (glm::length2(getDimensions()) != 1.0f) { + transform.postScale(vec3(getDimensions(), 1.0f)); + } + return transform; +} \ No newline at end of file diff --git a/interface/src/ui/overlays/Planar3DOverlay.h b/interface/src/ui/overlays/Planar3DOverlay.h index 8127d4ebb9..2ed90ab4ed 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.h +++ b/interface/src/ui/overlays/Planar3DOverlay.h @@ -32,7 +32,9 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) override; - + + Transform evalRenderTransform() const override; + protected: glm::vec2 _dimensions; }; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 104082dee4..fd3763624e 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -184,6 +184,7 @@ void Web3DOverlay::update(float deltatime) { // update globalPosition _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition())); } + Billboard3DOverlay::update(deltatime); } QString Web3DOverlay::pickURL() { @@ -306,19 +307,6 @@ void Web3DOverlay::render(RenderArgs* args) { vec2 halfSize = getSize() / 2.0f; vec4 color(toGlm(getColor()), getAlpha()); - Transform transform = getTransform(); - - // FIXME: applyTransformTo causes tablet overlay to detach from tablet entity. - // Perhaps rather than deleting the following code it should be run only if isFacingAvatar() is true? - /* - applyTransformTo(transform, true); - setTransform(transform); - */ - - if (glm::length2(getDimensions()) != 1.0f) { - transform.postScale(vec3(getDimensions(), 1.0f)); - } - if (!_texture) { _texture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()); _texture->setSource(__FUNCTION__); @@ -332,7 +320,8 @@ void Web3DOverlay::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setResourceTexture(0, _texture); - batch.setModelTransform(transform); + batch.setModelTransform(_renderTransform); + auto geometryCache = DependencyManager::get(); if (color.a < OPAQUE_ALPHA_THRESHOLD) { geometryCache->bindWebBrowserProgram(batch, true); From f82a507d09f8cdf4df45f840a219c5257cec5f69 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 14 Sep 2017 20:33:08 -0700 Subject: [PATCH 142/202] Cleanup --- interface/src/ui/overlays/Base3DOverlay.cpp | 52 +++++---------------- interface/src/ui/overlays/Base3DOverlay.h | 5 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- 3 files changed, 16 insertions(+), 43 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index b450fbdc05..53af2ea882 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -256,17 +256,8 @@ bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3 void Base3DOverlay::locationChanged(bool tellPhysics) { SpatiallyNestable::locationChanged(tellPhysics); - // Force the actual update of the render transform now that we notify for the change - // so it s captured for the time of rendering + // Force the actual update of the render transform through the notify call notifyRenderTransformChange(); - - /* auto itemID = getRenderItemID(); - if (render::Item::isValidID(itemID)) { - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; - transaction.updateItem(itemID); - scene->enqueueTransaction(transaction); - }*/ } void Base3DOverlay::parentDeleted() { @@ -274,44 +265,25 @@ void Base3DOverlay::parentDeleted() { } void Base3DOverlay::update(float duration) { + + // In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true. + // then the correct transform used for rendering is computed in the update transaction and assigned. + // TODO: Fix the value to be computed in main thread now and passed by value to the render item. + // This is the simplest fix for the web overlay of the tablet for now if (_renderTransformDirty) { - auto self = this; - // queue up this work for later processing, at the end of update and just before rendering. - // the application will ensure only the last lambda is actually invoked. - /* void* key = (void*)this; - std::weak_ptr weakSelf = shared_from_this(); - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() { - // do nothing, if the model has already been destroyed. - auto spatiallyNestableSelf = weakSelf.lock(); - if (!spatiallyNestableSelf) { - return; - } - auto self = std::dynamic_pointer_cast(spatiallyNestableSelf); - */ - #ifdef UpdateInMain - self->setRenderTransform(self->evalRenderTransform()); - #else - auto renderTransform = self->evalRenderTransform(); - #endif - auto itemID = self->getRenderItemID(); - if (render::Item::isValidID(itemID)) { + auto itemID = getRenderItemID(); + if (render::Item::isValidID(itemID)) { render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; - #ifdef UpdateInMain - transaction.updateItem(itemID); - #else - transaction.updateItem(itemID, [renderTransform](Overlay& data) { + transaction.updateItem(itemID, [](Overlay& data) { auto overlay3D = dynamic_cast(&data); if (overlay3D) { auto latestTransform = overlay3D->evalRenderTransform(); - overlay3D->setRenderTransform(latestTransform);// evalRenderTransform(); + overlay3D->setRenderTransform(latestTransform); } }); - #endif - scene->enqueueTransaction(transaction); - + scene->enqueueTransaction(transaction); } - // }); _renderTransformDirty = false; } } @@ -326,4 +298,4 @@ Transform Base3DOverlay::evalRenderTransform() const { void Base3DOverlay::setRenderTransform(const Transform& transform) { _renderTransform = transform; -} \ No newline at end of file +} diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index b2167852d4..55b55ed16f 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -55,8 +55,6 @@ public: void update(float deltatime) override; void notifyRenderTransformChange() const; - virtual Transform evalRenderTransform() const; - void setRenderTransform(const Transform& transform); void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; @@ -74,6 +72,9 @@ protected: virtual void parentDeleted() override; mutable Transform _renderTransform; + virtual Transform evalRenderTransform() const; + virtual void setRenderTransform(const Transform& transform); + const Transform& getRenderTransform() const { return _renderTransform; } float _lineWidth; bool _isSolid; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index fd3763624e..809536add9 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -320,7 +320,7 @@ void Web3DOverlay::render(RenderArgs* args) { Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setResourceTexture(0, _texture); - batch.setModelTransform(_renderTransform); + batch.setModelTransform(getRenderTransform()); auto geometryCache = DependencyManager::get(); if (color.a < OPAQUE_ALPHA_THRESHOLD) { From fd440aeeaae6179980e559cd995ab6915ab62386 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 15 Sep 2017 11:10:25 -0700 Subject: [PATCH 143/202] addressing review comments --- interface/src/ui/overlays/Base3DOverlay.cpp | 4 ++-- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.h | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 53af2ea882..6f55260133 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -271,11 +271,12 @@ void Base3DOverlay::update(float duration) { // TODO: Fix the value to be computed in main thread now and passed by value to the render item. // This is the simplest fix for the web overlay of the tablet for now if (_renderTransformDirty) { + _renderTransformDirty = false; auto itemID = getRenderItemID(); if (render::Item::isValidID(itemID)) { render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; - transaction.updateItem(itemID, [](Overlay& data) { + transaction.updateItem(itemID, [](Overlay& data) { auto overlay3D = dynamic_cast(&data); if (overlay3D) { auto latestTransform = overlay3D->evalRenderTransform(); @@ -284,7 +285,6 @@ void Base3DOverlay::update(float duration) { }); scene->enqueueTransaction(transaction); } - _renderTransformDirty = false; } } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 809536add9..eb8451e5c3 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -184,7 +184,7 @@ void Web3DOverlay::update(float deltatime) { // update globalPosition _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition())); } - Billboard3DOverlay::update(deltatime); + Parent::update(deltatime); } QString Web3DOverlay::pickURL() { diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 2eae7f33da..18cac96d65 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -21,6 +21,8 @@ class Web3DOverlay : public Billboard3DOverlay { Q_OBJECT public: + using Parent = Billboard3DOverlay; + static const QString QML; static QString const TYPE; virtual QString getType() const override { return TYPE; } From 0e83065a6db8471cdeed35cff39294d27c97113a Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 15 Sep 2017 11:44:35 -0700 Subject: [PATCH 144/202] One more: --- interface/src/ui/overlays/Web3DOverlay.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 18cac96d65..6bd540d120 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -19,9 +19,9 @@ class OffscreenQmlSurface; class Web3DOverlay : public Billboard3DOverlay { Q_OBJECT + using Parent = Billboard3DOverlay; public: - using Parent = Billboard3DOverlay; static const QString QML; static QString const TYPE; From 93d6c3a1aaeb23a45a2e9bbb468287feb4fb35ee Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 15 Sep 2017 14:35:45 -0700 Subject: [PATCH 145/202] Adding more comments for future fixmes --- interface/src/ui/overlays/Circle3DOverlay.cpp | 1 + interface/src/ui/overlays/Cube3DOverlay.cpp | 1 + interface/src/ui/overlays/Grid3DOverlay.cpp | 1 + interface/src/ui/overlays/Image3DOverlay.cpp | 1 + interface/src/ui/overlays/Line3DOverlay.cpp | 1 + interface/src/ui/overlays/ModelOverlay.cpp | 1 + interface/src/ui/overlays/Rectangle3DOverlay.cpp | 1 + interface/src/ui/overlays/Shape3DOverlay.cpp | 1 + interface/src/ui/overlays/Sphere3DOverlay.cpp | 1 + interface/src/ui/overlays/Text3DOverlay.cpp | 1 + 10 files changed, 10 insertions(+) diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 242021d698..57911c0786 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -85,6 +85,7 @@ void Circle3DOverlay::render(RenderArgs* args) { } // FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround + // FIXME Start using the _renderTransform instead of calling for Transform from here, do the custom things needed in evalRenderTransform() auto transform = getTransform(); transform.postScale(glm::vec3(getDimensions(), 1.0f)); diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 5ab32d21fe..ca7355b86f 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -54,6 +54,7 @@ void Cube3DOverlay::render(RenderArgs* args) { glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); // TODO: handle registration point?? + // FIXME Start using the _renderTransform instead of calling for Transform from here, do the custom things needed in evalRenderTransform() glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 1c2b4c162d..3172403731 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -79,6 +79,7 @@ void Grid3DOverlay::render(RenderArgs* args) { position += glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z); } + // FIXME Start using the _renderTransform instead of calling for Transform from here, do the custom things needed in evalRenderTransform() Transform transform; transform.setRotation(getRotation()); transform.setScale(glm::vec3(getDimensions(), 1.0f)); diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 6949848208..c79d363811 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -117,6 +117,7 @@ void Image3DOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); + // FIXME Start using the _renderTransform instead of calling for Transform from here, do the custom things needed in evalRenderTransform() Transform transform = getTransform(); bool transformChanged = applyTransformTo(transform, true); // If the transform is not modified, setting the transform to diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index cc8ed8e1a8..534261c839 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -132,6 +132,7 @@ void Line3DOverlay::render(RenderArgs* args) { glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); auto batch = args->_batch; if (batch) { + // FIXME Start using the _renderTransform instead of calling for Transform and start and end from here, do the custom things needed in evalRenderTransform() batch->setModelTransform(Transform()); glm::vec3 start = getStart(); glm::vec3 end = getEnd(); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 0bed07891e..0cdcf012b8 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -281,6 +281,7 @@ ModelOverlay* ModelOverlay::createClone() const { void ModelOverlay::locationChanged(bool tellPhysics) { Base3DOverlay::locationChanged(tellPhysics); + // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() if (_model && _model->isActive()) { _model->setRotation(getRotation()); _model->setTranslation(getPosition()); diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 22124a0a97..dc8badbbd1 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -66,6 +66,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { + // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() Transform transform; transform.setTranslation(position); transform.setRotation(rotation); diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index df0ecba307..fc54cc19ff 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -33,6 +33,7 @@ void Shape3DOverlay::render(RenderArgs* args) { const float MAX_COLOR = 255.0f; glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() // TODO: handle registration point?? glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 9309316d6e..d01da231cf 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -39,6 +39,7 @@ void Sphere3DOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { + // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() Transform transform = getTransform(); transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE); batch->setModelTransform(transform); diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index bb8c24aa11..43a2854206 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -96,6 +96,7 @@ void Text3DOverlay::render(RenderArgs* args) { Q_ASSERT(args->_batch); auto& batch = *args->_batch; + // FIXME Start using the _renderTransform instead of calling for Transform and Dimensions from here, do the custom things needed in evalRenderTransform() Transform transform = getTransform(); applyTransformTo(transform, true); setTransform(transform); From 02a529923ed4bed757a262a39a85c0f5c8797dd9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 18 Sep 2017 14:00:03 -0700 Subject: [PATCH 146/202] don't export embedded textures, remove triangle warning --- libraries/baking/src/FBXBaker.cpp | 60 ++++++++++++++++--------------- libraries/baking/src/FBXBaker.h | 2 +- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 3a37a3f31e..120ea3fa80 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -244,7 +244,7 @@ QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) { QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { // first make sure we have a unique base name for this texture // in case another texture referenced by this model has the same base name - auto nameMatches = _textureNameMatchCount[textureFileInfo.baseName()]; + auto& nameMatches = _textureNameMatchCount[textureFileInfo.baseName()]; QString bakedTextureFileName { textureFileInfo.completeBaseName() }; @@ -262,28 +262,32 @@ QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) { return bakedTextureFileName; } -QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName) { +QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded) { + QUrl urlToTexture; - if (textureFileInfo.exists() && textureFileInfo.isFile()) { - // set the texture URL to the local texture that we have confirmed exists - urlToTexture = QUrl::fromLocalFile(textureFileInfo.absoluteFilePath()); - } else { - // external texture that we'll need to download or find + auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); - // first check if it the RelativePath to the texture in the FBX was relative - auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/")); - - // this is a relative file path which will require different handling - // depending on the location of the original FBX - if (_fbxURL.isLocalFile() && apparentRelativePath.exists() && apparentRelativePath.isFile()) { - // the absolute path we ran into for the texture in the FBX exists on this machine - // so use that file - urlToTexture = QUrl::fromLocalFile(apparentRelativePath.absoluteFilePath()); + if (isEmbedded) { + urlToTexture = _fbxURL.toString() + "/" + apparentRelativePath.filePath(); + } else { + if (textureFileInfo.exists() && textureFileInfo.isFile()) { + // set the texture URL to the local texture that we have confirmed exists + urlToTexture = QUrl::fromLocalFile(textureFileInfo.absoluteFilePath()); } else { - // we didn't find the texture on this machine at the absolute path - // so assume that it is right beside the FBX to match the behaviour of interface - urlToTexture = _fbxURL.resolved(apparentRelativePath.fileName()); + // external texture that we'll need to download or find + + // this is a relative file path which will require different handling + // depending on the location of the original FBX + if (_fbxURL.isLocalFile() && apparentRelativePath.exists() && apparentRelativePath.isFile()) { + // the absolute path we ran into for the texture in the FBX exists on this machine + // so use that file + urlToTexture = QUrl::fromLocalFile(apparentRelativePath.absoluteFilePath()); + } else { + // we didn't find the texture on this machine at the absolute path + // so assume that it is right beside the FBX to match the behaviour of interface + urlToTexture = _fbxURL.resolved(apparentRelativePath.fileName()); + } } } @@ -335,7 +339,6 @@ void FBXBaker::rewriteAndBakeSceneModels() { } if (numTriangles == 0) { - handleWarning("Skipping compression of mesh because no triangles were found"); continue; } @@ -582,8 +585,12 @@ void FBXBaker::rewriteAndBakeSceneTextures() { qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName << "to" << bakedTextureFileName; + // check if this was an embedded texture we have already have in-memory content for + auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit()); + // figure out the URL to this texture, embedded or external - auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName); + auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName, + !textureContent.isNull()); // write the new filename into the FBX scene textureChild.properties[0] = bakedTextureFileName.toLocal8Bit(); @@ -595,9 +602,6 @@ void FBXBaker::rewriteAndBakeSceneTextures() { QString textureID { object->properties[0].toByteArray() }; auto textureType = textureTypes[textureID]; - // check if this was an embedded texture we have already have in-memory content for - auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit()); - // bake this texture asynchronously bakeTexture(urlToTexture, textureType, _bakedOutputDir, textureContent); } @@ -649,12 +653,10 @@ void FBXBaker::handleBakedTexture() { // use the path to the texture being baked to determine if this was an embedded or a linked texture - // it is embeddded if the texure being baked was inside the original output folder - // since that is where the FBX SDK places the .fbm folder it generates when importing the FBX + // it is embeddded if the texure being baked was inside a folder with the name of the FBX + // since that is the fake URL we provide when baking external textures - auto originalOutputFolder = QUrl::fromLocalFile(_originalOutputDir); - - if (!originalOutputFolder.isParentOf(bakedTexture->getTextureURL())) { + if (!_fbxURL.isParentOf(bakedTexture->getTextureURL())) { // for linked textures we want to save a copy of original texture beside the original FBX qCDebug(model_baking) << "Saving original texture for" << bakedTexture->getTextureURL(); diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 26c1ff2dcc..c611fb712f 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -66,7 +66,7 @@ private: void checkIfTexturesFinished(); QString createBakedTextureFileName(const QFileInfo& textureFileInfo); - QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName); + QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false); void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir, const QByteArray& textureContent = QByteArray()); From 608f8196c62cde260d052d7ba7f3a33178538740 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 18 Sep 2017 14:01:48 -0700 Subject: [PATCH 147/202] remove commented out removal of embedded media folder --- libraries/baking/src/FBXBaker.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 120ea3fa80..0f31b878ee 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -736,20 +736,11 @@ void FBXBaker::exportScene() { } -void FBXBaker::removeEmbeddedMediaFolder() { - // now that the bake is complete, remove the embedded media folder produced by the FBX SDK when it imports an FBX - //auto embeddedMediaFolderName = _fbxURL.fileName().replace(".fbx", ".fbm"); - //QDir(_bakedOutputDir + ORIGINAL_OUTPUT_SUBFOLDER + embeddedMediaFolderName).removeRecursively(); -} - void FBXBaker::checkIfTexturesFinished() { // check if we're done everything we need to do for this FBX // and emit our finished signal if we're done if (_bakingTextures.isEmpty()) { - // remove the embedded media folder that the FBX SDK produces when reading the original - removeEmbeddedMediaFolder(); - if (hasErrors()) { // if we're checking for completion but we have errors // that means one or more of our texture baking operations failed From dbe7cc5d1af8a2a5702f1c6051f2ff13d6addce4 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Mon, 18 Sep 2017 23:38:38 +0100 Subject: [PATCH 148/202] 21540 - Make Rotation using Vive Controller Work --- interface/resources/controllers/standard.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 0d5c095585..28f15605e0 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -25,7 +25,7 @@ }, { "from": "Standard.RX", - "when": [ "Application.InHMD", "Application.SnapTurn" ], + "when": [ "Application.SnapTurn" ], "to": "Actions.StepYaw", "filters": [ @@ -128,4 +128,4 @@ { "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" }, { "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" } ] -} +} \ No newline at end of file From 57a92adc73ad08df6e43057ab7d8b3e893975104 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 18 Sep 2017 15:39:38 -0700 Subject: [PATCH 149/202] flag node as local by sending address, not by local from packet --- domain-server/src/DomainGatekeeper.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 620d593ebc..dbbcc004ca 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -272,22 +272,22 @@ void DomainGatekeeper::updateNodePermissions() { userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer; userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent; } else { - // this node is an agent - const QHostAddress& addr = node->getLocalSocket().getAddress(); - bool isLocalUser = (addr == limitedNodeList->getLocalSockAddr().getAddress() || - addr == QHostAddress::LocalHost); - // at this point we don't have a sending socket for packets from this node - assume it is the active socket // or the public socket if we haven't activated a socket for the node yet HifiSockAddr connectingAddr = node->getActiveSocket() ? *node->getActiveSocket() : node->getPublicSocket(); QString hardwareAddress; QUuid machineFingerprint; + bool isLocalUser { false }; DomainServerNodeData* nodeData = static_cast(node->getLinkedData()); if (nodeData) { hardwareAddress = nodeData->getHardwareAddress(); machineFingerprint = nodeData->getMachineFingerprint(); + + auto sendingAddress = nodeData->getSendingSockAddr().getAddress(); + isLocalUser = (sendingAddress == limitedNodeList->getLocalSockAddr().getAddress() || + sendingAddress == QHostAddress::LocalHost); } userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress, machineFingerprint); From b9e5957c9f350ad74c47f59fbb7362ace2b54f87 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 18 Sep 2017 16:14:06 -0700 Subject: [PATCH 150/202] Remove unused Algorithms.h --- libraries/shared/src/shared/Algorithms.h | 38 ------------------------ 1 file changed, 38 deletions(-) delete mode 100644 libraries/shared/src/shared/Algorithms.h diff --git a/libraries/shared/src/shared/Algorithms.h b/libraries/shared/src/shared/Algorithms.h deleted file mode 100644 index b85a3df6c5..0000000000 --- a/libraries/shared/src/shared/Algorithms.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Algorithms.h -// libraries/shared/src/shared -// -// Created by Clement Brisset on 8/9/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 -// - -#ifndef hifi_Algorithms_h -#define hifi_Algorithms_h - -#include -#include -#include - -namespace alg { - -template -auto find(const Container& container, const ValueType& value) -> decltype(std::begin(container)) { - return std::find(std::begin(container), std::end(container), value); -} - -template -auto find_if(const Container& container, Predicate&& predicate) -> decltype(std::begin(container)) { - return std::find_if(std::begin(container), std::end(container), std::forward(predicate)); -} - -template -auto find_if_not(const Container& container, Predicate&& predicate) -> decltype(std::begin(container)) { - return std::find_if_not(std::begin(container), std::end(container), std::forward(predicate)); -} - -} - -#endif // hifi_Algorithms_hpp From 2eff3dcbd6dfd54a8d787cb96523ebbb7d9b2022 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 19 Sep 2017 09:18:40 -0700 Subject: [PATCH 151/202] Add additional DLL cleanup --- cmake/templates/NSIS.template.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in index ca05294984..8abb202bd4 100644 --- a/cmake/templates/NSIS.template.in +++ b/cmake/templates/NSIS.template.in @@ -838,6 +838,7 @@ Section "-Core installation" Delete "$INSTDIR\version" Delete "$INSTDIR\msvcr140.dll" Delete "$INSTDIR\msvcp140.dll" + Delete "$INSTDIR\vcruntime140.dll" Delete "$INSTDIR\xinput1_3.dll" ; Delete old desktop shortcuts before they were renamed during Sandbox rename From 6297448e907660bf508db14ff2f635775346c9e7 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 19 Sep 2017 19:49:19 +0200 Subject: [PATCH 152/202] Build fix --- interface/resources/qml/hifi/audio/Audio.qml | 7 ++- interface/src/scripting/AudioDevices.cpp | 46 ++++++++++++-------- interface/src/scripting/AudioDevices.h | 4 +- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 0abd166b06..5a2ee86c09 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -70,6 +70,7 @@ Rectangle { } property bool showPeaks: true; + function enablePeakValues() { Audio.devices.input.peakValuesEnabled = true; Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) { @@ -78,6 +79,7 @@ Rectangle { } }); } + function disablePeakValues() { root.showPeaks = false; Audio.devices.input.peakValuesEnabled = false; @@ -202,12 +204,13 @@ Rectangle { } } } - InputLevel { + InputPeak { id: inputLevel anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter visible: (bar.currentIndex === 1 && selectedHMD && isVR) || - (bar.currentIndex === 0 && selectedDesktop && !isVR); + (bar.currentIndex === 0 && selectedDesktop && !isVR) && + Audio.devices.input.peakValuesAvailable; } } } diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index c79db91dfc..a130b46877 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -105,14 +105,14 @@ AudioDeviceList::~AudioDeviceList() { auto& settingHMD = getSetting(true, _mode); auto& settingDesktop = getSetting(false, _mode); // store the selected device - foreach(AudioDevice adevice, _devices) { - if (adevice.selectedDesktop) { - qDebug() << "Saving Desktop for" << _mode << "name" << adevice.info.deviceName(); - settingDesktop.set(adevice.info.deviceName()); + foreach(std::shared_ptr adevice, _devices) { + if (adevice->selectedDesktop) { + qDebug() << "Saving Desktop for" << _mode << "name" << adevice->info.deviceName(); + settingDesktop.set(adevice->info.deviceName()); } - if (adevice.selectedHMD) { - qDebug() << "Saving HMD for" << _mode << "name" << adevice.info.deviceName(); - settingHMD.set(adevice.info.deviceName()); + if (adevice->selectedHMD) { + qDebug() << "Saving HMD for" << _mode << "name" << adevice->info.deviceName(); + settingHMD.set(adevice->info.deviceName()); } } } @@ -123,18 +123,30 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { } if (role == DeviceNameRole) { - return _devices.at(index.row()).display; + return _devices.at(index.row())->display; } else if (role == SelectedDesktopRole) { - return _devices.at(index.row()).selectedDesktop; + return _devices.at(index.row())->selectedDesktop; } else if (role == SelectedHMDRole) { - return _devices.at(index.row()).selectedHMD; - } else if (role == DeviceInfoRole) { - return QVariant::fromValue(_devices.at(index.row()).info); + return _devices.at(index.row())->selectedHMD; + } else if (role == InfoRole) { + return QVariant::fromValue(_devices.at(index.row())->info); } else { return QVariant(); } } +QVariant AudioInputDeviceList::data(const QModelIndex& index, int role) const { + if (!index.isValid() || index.row() >= rowCount()) { + return QVariant(); + } + + if (role == PeakRole) { + return std::static_pointer_cast(_devices.at(index.row()))->peak; + } else { + return AudioDeviceList::data(index, role); + } +} + void AudioDeviceList::resetDevice(bool contextIsHMD) { auto client = DependencyManager::get().data(); QString deviceName = getTargetDevice(contextIsHMD, _mode); @@ -175,11 +187,11 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD selectedDevice = device; for (auto i = 0; i < _devices.size(); ++i) { - AudioDevice& device = _devices[i]; - bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; - if (isSelected && device.info != selectedDevice) { + std::shared_ptr device = _devices[i]; + bool &isSelected = isHMD ? device->selectedHMD : device->selectedDesktop; + if (isSelected && device->info != selectedDevice) { isSelected = false; - } else if (device.info == selectedDevice) { + } else if (device->info == selectedDevice) { isSelected = true; } } @@ -211,7 +223,7 @@ void AudioDeviceList::onDevicesChanged(const QList& devices, b isSelected = (device.info.deviceName() == savedDeviceName); } qDebug() << "adding audio device:" << device.display << device.selectedDesktop << device.selectedHMD << _mode; - _devices.push_back(device); + _devices.push_back(newDevice(device)); } endResetModel(); diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 162092f358..36f1653e38 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -33,8 +33,8 @@ class AudioDeviceList : public QAbstractListModel { Q_OBJECT public: - AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput) : _mode(mode) {} - ~AudioDeviceList() = default; + AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput); + virtual ~AudioDeviceList(); virtual std::shared_ptr newDevice(const AudioDevice& device) { return std::make_shared(device); } From 641addf39772a7c41bd06b0ce535453eb547a446 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 19 Sep 2017 11:13:39 -0700 Subject: [PATCH 153/202] Remove FBX.cpp --- libraries/fbx/src/FBX.cpp | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 libraries/fbx/src/FBX.cpp diff --git a/libraries/fbx/src/FBX.cpp b/libraries/fbx/src/FBX.cpp deleted file mode 100644 index 3f18b3e678..0000000000 --- a/libraries/fbx/src/FBX.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// -// FBX.cpp -// libraries/fbx/src -// -// Created by Ryan Huffman on 9/5/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 -// From 59500061116f856b3b8c6a45bd01ec0a6dc35368 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 19 Sep 2017 11:16:59 -0700 Subject: [PATCH 154/202] Remove extraneous logging --- .../model-networking/src/model-networking/TextureCache.cpp | 1 - libraries/networking/src/AssetClient.cpp | 1 - libraries/networking/src/ResourceCache.cpp | 2 -- 3 files changed, 4 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index cc29e841ce..e0d00ecd08 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -194,7 +194,6 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs if (type == image::TextureUsage::CUBE_TEXTURE) { QUrlQuery query { url.query() }; query.addQueryItem("skybox", ""); - qDebug() << "Updating cubemap texture query from" << url.query() << "to" << query.toString(); modifiedUrl.setQuery(query.toString()); } TextureExtra extra = { type, content, maxNumPixels }; diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 0d4e7c8388..940daf4d19 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -617,7 +617,6 @@ MessageID AssetClient::setBakingEnabled(const AssetPathList& paths, bool enabled _pendingMappingRequests[assetServer][messageID] = callback; return messageID; - } } diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index fbe7c05801..a3ac995bcf 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -734,9 +734,7 @@ void Resource::handleReplyFinished() { qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo); auto relativePathURL = _request->getRelativePathUrl(); - qDebug() << "Relative path is: " << relativePathURL; if (!relativePathURL.isEmpty()) { - qDebug() << "setting effective path"; _effectiveBaseURL = relativePathURL; } From 1f485ac8f80e176c209945130f45d46b56c860c9 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 19 Sep 2017 12:05:15 -0700 Subject: [PATCH 155/202] Fix OpenSSL build instructions --- BUILD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILD.md b/BUILD.md index 30302d611b..4d321146c3 100644 --- a/BUILD.md +++ b/BUILD.md @@ -2,7 +2,7 @@ - [cmake](https://cmake.org/download/): 3.9 - [Qt](https://www.qt.io/download-open-source): 5.9.1 -- [OpenSSL](https://www.openssl.org/): Use the latest available version of OpenSSL to avoid security vulnerabilities. +- [OpenSSL](https://www.openssl.org/): Use the latest available 1.0 version (**NOT** 1.1) of OpenSSL to avoid security vulnerabilities. - [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) ### CMake External Project Dependencies From 10f7f081713e491549bb9d5ceb4471cc262f98ef Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 19 Sep 2017 21:42:52 +0200 Subject: [PATCH 156/202] Added peak model, missed during merging --- interface/resources/qml/hifi/audio/Audio.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 5a2ee86c09..bc55175f18 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -207,6 +207,7 @@ Rectangle { InputPeak { id: inputLevel anchors.right: parent.right + peak: model.peak; anchors.verticalCenter: parent.verticalCenter visible: (bar.currentIndex === 1 && selectedHMD && isVR) || (bar.currentIndex === 0 && selectedDesktop && !isVR) && From e1d79ee8f4a8ed0e1f605d57771897366314d94c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 19 Sep 2017 11:04:01 -0700 Subject: [PATCH 157/202] add abort to Baker and subclasses --- assignment-client/src/assets/AssetServer.cpp | 68 +++++------- assignment-client/src/assets/AssetServer.h | 23 +--- .../src/assets/BakeAssetTask.cpp | 79 ++++++++++++++ assignment-client/src/assets/BakeAssetTask.h | 50 +++++++++ libraries/baking/src/Baker.cpp | 33 +++++- libraries/baking/src/Baker.h | 16 +++ libraries/baking/src/FBXBaker.cpp | 79 +++++++++++--- libraries/baking/src/FBXBaker.h | 6 +- libraries/baking/src/TextureBaker.cpp | 21 +++- libraries/baking/src/TextureBaker.h | 5 + libraries/image/src/image/Image.cpp | 101 +++++++++++------- libraries/image/src/image/Image.h | 54 ++++++---- .../src/model-networking/TextureCache.cpp | 2 +- 13 files changed, 403 insertions(+), 134 deletions(-) create mode 100644 assignment-client/src/assets/BakeAssetTask.cpp create mode 100644 assignment-client/src/assets/BakeAssetTask.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index f1e921c768..17aae74dfd 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -35,6 +35,7 @@ #include #include "AssetServerLogging.h" +#include "BakeAssetTask.h" #include "SendAssetTask.h" #include "UploadAssetTask.h" @@ -53,45 +54,6 @@ static QStringList BAKEABLE_TEXTURE_EXTENSIONS; static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; -BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) - : _assetHash(assetHash), _assetPath(assetPath), _filePath(filePath) { -} - -void BakeAssetTask::run() { - _isBaking.store(true); - - qRegisterMetaType >("QVector"); - TextureBakerThreadGetter fn = []() -> QThread* { return QThread::currentThread(); }; - - std::unique_ptr baker; - - if (_assetPath.endsWith(".fbx")) { - baker = std::unique_ptr { - new FBXBaker(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir()) - }; - } else { - baker = std::unique_ptr { - new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, - PathUtils::generateTemporaryDir()) - }; - } - - QEventLoop loop; - connect(baker.get(), &Baker::finished, &loop, &QEventLoop::quit); - QMetaObject::invokeMethod(baker.get(), "bake", Qt::QueuedConnection); - loop.exec(); - - if (baker->hasErrors()) { - qDebug() << "Failed to bake: " << _assetHash << _assetPath << baker->getErrors(); - auto errors = baker->getErrors().join('\n'); // Join error list into a single string for convenience - emit bakeFailed(_assetHash, _assetPath, errors); - } else { - auto vectorOutputFiles = QVector::fromStdVector(baker->getOutputFiles()); - qDebug() << "Finished baking: " << _assetHash << _assetPath << vectorOutputFiles; - emit bakeComplete(_assetHash, _assetPath, vectorOutputFiles); - } -} - void AssetServer::bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) { qDebug() << "Starting bake for: " << assetPath << assetHash; auto it = _pendingBakes.find(assetHash); @@ -102,6 +64,7 @@ void AssetServer::bakeAsset(const AssetHash& assetHash, const AssetPath& assetPa connect(task.get(), &BakeAssetTask::bakeComplete, this, &AssetServer::handleCompletedBake); connect(task.get(), &BakeAssetTask::bakeFailed, this, &AssetServer::handleFailedBake); + connect(task.get(), &BakeAssetTask::bakeAborted, this, &AssetServer::handleAbortedBake); _bakingTaskPool.start(task.get()); } else { @@ -310,6 +273,28 @@ AssetServer::AssetServer(ReceivedMessage& message) : } void AssetServer::aboutToFinish() { + + // remove pending transfer tasks + _transferTaskPool.clear(); + + // abort each of our still running bake tasks, remove pending bakes that were never put on the thread pool + auto it = _pendingBakes.begin(); + while (it != _pendingBakes.end()) { + auto pendingRunnable = _bakingTaskPool.tryTake(it.value().get()); + + if (pendingRunnable) { + _pendingBakes.erase(it); + } else { + it.value()->abort(); + ++it; + } + } + + // make sure all bakers are finished or aborted + while (_pendingBakes.size() > 0) { + QCoreApplication::processEvents(); + } + // re-set defaults in image library image::setColorTexturesCompressionEnabled(_wasCubeTextureCompressionEnabled); image::setGrayscaleTexturesCompressionEnabled(_wasGrayscaleTextureCompressionEnabled); @@ -1247,6 +1232,11 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina _pendingBakes.remove(originalAssetHash); } +void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath) { + // for an aborted bake we don't do anything but remove the BakeAssetTask from our pending bakes + _pendingBakes.remove(originalAssetHash); +} + static const QString BAKE_VERSION_KEY = "bake_version"; static const QString APP_VERSION_KEY = "app_version"; static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake"; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 8641f5498f..10ea067ee5 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -29,26 +29,6 @@ namespace std { }; } -class BakeAssetTask : public QObject, public QRunnable { - Q_OBJECT -public: - BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath); - - bool isBaking() { return _isBaking.load(); } - - void run() override; - -signals: - void bakeComplete(QString assetHash, QString assetPath, QVector outputFiles); - void bakeFailed(QString assetHash, QString assetPath, QString errors); - -private: - std::atomic _isBaking { false }; - AssetHash _assetHash; - AssetPath _assetPath; - QString _filePath; -}; - struct AssetMeta { AssetMeta() { } @@ -59,6 +39,8 @@ struct AssetMeta { QString lastBakeErrors; }; +class BakeAssetTask; + class AssetServer : public ThreadedAssignment { Q_OBJECT public: @@ -121,6 +103,7 @@ private: /// Move baked content for asset to baked directory and update baked status void handleCompletedBake(QString originalAssetHash, QString assetPath, QVector bakedFilePaths); void handleFailedBake(QString originalAssetHash, QString assetPath, QString errors); + void handleAbortedBake(QString originalAssetHash, QString assetPath); /// Create meta file to describe baked content for original asset std::pair readMetaFile(AssetHash hash); diff --git a/assignment-client/src/assets/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp new file mode 100644 index 0000000000..9073510f79 --- /dev/null +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -0,0 +1,79 @@ +// +// BakeAssetTask.cpp +// assignment-client/src/assets +// +// Created by Stephen Birarda on 9/18/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 +// + +#include "BakeAssetTask.h" + +#include + +#include +#include + +BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) : + _assetHash(assetHash), + _assetPath(assetPath), + _filePath(filePath) +{ + +} + +void BakeAssetTask::run() { + _isBaking.store(true); + + qRegisterMetaType >("QVector"); + TextureBakerThreadGetter fn = []() -> QThread* { return QThread::currentThread(); }; + + if (_assetPath.endsWith(".fbx")) { + _baker = std::unique_ptr { + new FBXBaker(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir()) + }; + } else { + _baker = std::unique_ptr { + new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE, + PathUtils::generateTemporaryDir()) + }; + } + + QEventLoop loop; + connect(_baker.get(), &Baker::finished, &loop, &QEventLoop::quit); + connect(_baker.get(), &Baker::aborted, &loop, &QEventLoop::quit); + QMetaObject::invokeMethod(_baker.get(), "bake", Qt::QueuedConnection); + loop.exec(); + + if (_baker->wasAborted()) { + qDebug() << "Aborted baking: " << _assetHash << _assetPath; + + _wasAborted.store(true); + + emit bakeAborted(_assetHash, _assetPath); + } else if (_baker->hasErrors()) { + qDebug() << "Failed to bake: " << _assetHash << _assetPath << _baker->getErrors(); + + auto errors = _baker->getErrors().join('\n'); // Join error list into a single string for convenience + + _didFinish.store(true); + + emit bakeFailed(_assetHash, _assetPath, errors); + } else { + auto vectorOutputFiles = QVector::fromStdVector(_baker->getOutputFiles()); + + qDebug() << "Finished baking: " << _assetHash << _assetPath << vectorOutputFiles; + + _didFinish.store(true); + + emit bakeComplete(_assetHash, _assetPath, vectorOutputFiles); + } +} + +void BakeAssetTask::abort() { + if (_baker) { + _baker->abort(); + } +} diff --git a/assignment-client/src/assets/BakeAssetTask.h b/assignment-client/src/assets/BakeAssetTask.h new file mode 100644 index 0000000000..5c456c4521 --- /dev/null +++ b/assignment-client/src/assets/BakeAssetTask.h @@ -0,0 +1,50 @@ +// +// BakeAssetTask.h +// assignment-client/src/assets +// +// Created by Stephen Birarda on 9/18/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 +// + +#ifndef hifi_BakeAssetTask_h +#define hifi_BakeAssetTask_h + +#include +#include +#include + +#include +#include + +class BakeAssetTask : public QObject, public QRunnable { + Q_OBJECT +public: + BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath); + + bool isBaking() { return _isBaking.load(); } + + void run() override; + + void abort(); + bool wasAborted() const { return _wasAborted.load(); } + bool didFinish() const { return _didFinish.load(); } + +signals: + void bakeComplete(QString assetHash, QString assetPath, QVector outputFiles); + void bakeFailed(QString assetHash, QString assetPath, QString errors); + void bakeAborted(QString assetHash, QString assetPath); + +private: + std::atomic _isBaking { false }; + AssetHash _assetHash; + AssetPath _assetPath; + QString _filePath; + std::unique_ptr _baker; + std::atomic _wasAborted { false }; + std::atomic _didFinish { false }; +}; + +#endif // hifi_BakeAssetTask_h diff --git a/libraries/baking/src/Baker.cpp b/libraries/baking/src/Baker.cpp index c0cbd8d124..2adedf08a1 100644 --- a/libraries/baking/src/Baker.cpp +++ b/libraries/baking/src/Baker.cpp @@ -13,20 +13,49 @@ #include "Baker.h" +bool Baker::shouldStop() { + if (_shouldAbort) { + setWasAborted(true); + return true; + } + + if (hasErrors()) { + return true; + } + + return false; +} + void Baker::handleError(const QString& error) { qCCritical(model_baking).noquote() << error; _errorList.append(error); - emit finished(); + setIsFinished(true); } void Baker::handleErrors(const QStringList& errors) { // we're appending errors, presumably from a baking operation we called // add those to our list and emit that we are finished _errorList.append(errors); - emit finished(); + setIsFinished(true); } void Baker::handleWarning(const QString& warning) { qCWarning(model_baking).noquote() << warning; _warningList.append(warning); } + +void Baker::setIsFinished(bool isFinished) { + _isFinished.store(isFinished); + + if (isFinished) { + emit finished(); + } +} + +void Baker::setWasAborted(bool wasAborted) { + _wasAborted.store(wasAborted); + + if (wasAborted) { + emit aborted(); + } +} diff --git a/libraries/baking/src/Baker.h b/libraries/baking/src/Baker.h index fd76246497..2da315c9fc 100644 --- a/libraries/baking/src/Baker.h +++ b/libraries/baking/src/Baker.h @@ -18,6 +18,8 @@ class Baker : public QObject { Q_OBJECT public: + bool shouldStop(); + bool hasErrors() const { return !_errorList.isEmpty(); } QStringList getErrors() const { return _errorList; } @@ -26,11 +28,20 @@ public: std::vector getOutputFiles() const { return _outputFiles; } + virtual void setIsFinished(bool isFinished); + bool isFinished() const { return _isFinished.load(); } + + virtual void setWasAborted(bool wasAborted); + + bool wasAborted() const { return _wasAborted.load(); } + public slots: virtual void bake() = 0; + virtual void abort() { _shouldAbort.store(true); } signals: void finished(); + void aborted(); protected: void handleError(const QString& error); @@ -44,6 +55,11 @@ protected: QStringList _errorList; QStringList _warningList; + + std::atomic _isFinished { false }; + + std::atomic _shouldAbort { false }; + std::atomic _wasAborted { false }; }; #endif // hifi_Baker_h diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 0f31b878ee..b5b6570599 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -56,7 +56,19 @@ FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGet } +void FBXBaker::abort() { + Baker::abort(); + + // tell our underlying TextureBaker instances to abort + // the FBXBaker will wait until all are aborted before emitting its own abort signal + for (auto& textureBaker : _bakingTextures) { + textureBaker->abort(); + } +} + void FBXBaker::bake() { + qDebug() << "FBXBaker" << _fbxURL << "bake starting"; + auto tempDir = PathUtils::generateTemporaryDir(); if (tempDir.isEmpty()) { @@ -73,7 +85,7 @@ void FBXBaker::bake() { // setup the output folder for the results of this bake setupOutputFolder(); - if (hasErrors()) { + if (shouldStop()) { return; } @@ -87,22 +99,27 @@ void FBXBaker::bakeSourceCopy() { // load the scene from the FBX file importScene(); - if (hasErrors()) { + if (shouldStop()) { return; } // enumerate the models and textures found in the scene and start a bake for them - rewriteAndBakeSceneModels(); rewriteAndBakeSceneTextures(); - if (hasErrors()) { + if (shouldStop()) { + return; + } + + rewriteAndBakeSceneModels(); + + if (shouldStop()) { return; } // export the FBX with re-written texture references exportScene(); - if (hasErrors()) { + if (shouldStop()) { return; } @@ -165,7 +182,6 @@ void FBXBaker::loadSourceFBX() { networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - networkRequest.setUrl(_fbxURL); qCDebug(model_baking) << "Downloading" << _fbxURL; @@ -553,6 +569,11 @@ void FBXBaker::rewriteAndBakeSceneTextures() { while (object != rootChild.children.end()) { if (object->name == "Texture") { + // double check that we didn't get an abort while baking the last texture + if (shouldStop()) { + return; + } + // enumerate the texture children for (FBXNode& textureChild : object->children) { @@ -630,8 +651,9 @@ void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type tex &TextureBaker::deleteLater }; - // make sure we hear when the baking texture is done + // make sure we hear when the baking texture is done or aborted connect(bakingTexture.data(), &Baker::finished, this, &FBXBaker::handleBakedTexture); + connect(bakingTexture.data(), &TextureBaker::aborted, this, &FBXBaker::handleAbortedTexture); // keep a shared pointer to the baking texture _bakingTextures.insert(textureURL, bakingTexture); @@ -646,7 +668,7 @@ void FBXBaker::handleBakedTexture() { // make sure we haven't already run into errors, and that this is a valid texture if (bakedTexture) { - if (!hasErrors()) { + if (!shouldStop()) { if (!bakedTexture->hasErrors()) { if (!_originalOutputDir.isEmpty()) { // we've been asked to make copies of the originals, so we need to make copies of this if it is a linked texture @@ -698,6 +720,11 @@ void FBXBaker::handleBakedTexture() { // now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list _bakingTextures.remove(bakedTexture->getTextureURL()); + // abort any other ongoing texture bakes since we know we'll end up failing + for (auto& bakingTexture : _bakingTextures) { + bakingTexture->abort(); + } + checkIfTexturesFinished(); } } else { @@ -711,6 +738,25 @@ void FBXBaker::handleBakedTexture() { } } +void FBXBaker::handleAbortedTexture() { + // grab the texture bake that was aborted and remove it from our hash since we don't need to track it anymore + TextureBaker* bakedTexture = qobject_cast(sender()); + + if (bakedTexture) { + _bakingTextures.remove(bakedTexture->getTextureURL()); + } + + // since a texture we were baking aborted, our status is also aborted + _shouldAbort.store(true); + + // abort any other ongoing texture bakes since we know we'll end up failing + for (auto& bakingTexture : _bakingTextures) { + bakingTexture->abort(); + } + + checkIfTexturesFinished(); +} + void FBXBaker::exportScene() { // save the relative path to this FBX inside our passed output folder auto fileName = _fbxURL.fileName(); @@ -735,25 +781,34 @@ void FBXBaker::exportScene() { qCDebug(model_baking) << "Exported" << _fbxURL << "with re-written paths to" << _bakedFBXFilePath; } - void FBXBaker::checkIfTexturesFinished() { // check if we're done everything we need to do for this FBX // and emit our finished signal if we're done if (_bakingTextures.isEmpty()) { - if (hasErrors()) { + if (shouldStop()) { // if we're checking for completion but we have errors // that means one or more of our texture baking operations failed if (_pendingErrorEmission) { - emit finished(); + setIsFinished(true); } return; } else { qCDebug(model_baking) << "Finished baking, emitting finsihed" << _fbxURL; - emit finished(); + setIsFinished(true); + } + } +} + +void FBXBaker::setWasAborted(bool wasAborted) { + if (wasAborted != _wasAborted.load()) { + Baker::setWasAborted(wasAborted); + + if (wasAborted) { + qCDebug(model_baking) << "Aborted baking" << _fbxURL; } } } diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index c611fb712f..26471a29b3 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -39,10 +39,11 @@ public: QUrl getFBXUrl() const { return _fbxURL; } QString getBakedFBXFilePath() const { return _bakedFBXFilePath; } + virtual void setWasAborted(bool wasAborted) override; + public slots: - // all calls to FBXBaker::bake for FBXBaker instances must be from the same thread - // because the Autodesk SDK will cause a crash if it is called from multiple threads virtual void bake() override; + virtual void abort() override; signals: void sourceCopyReadyToLoad(); @@ -51,6 +52,7 @@ private slots: void bakeSourceCopy(); void handleFBXNetworkReply(); void handleBakedTexture(); + void handleAbortedTexture(); private: void setupOutputFolder(); diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index cdf21a0290..febc7ea092 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -50,6 +50,13 @@ void TextureBaker::bake() { } } +void TextureBaker::abort() { + Baker::abort(); + + // flip our atomic bool so any ongoing texture processing is stopped + _abortProcessing.store(true); +} + const QStringList TextureBaker::getSupportedFormats() { auto formats = QImageReader::supportedImageFormats(); QStringList stringFormats; @@ -111,7 +118,11 @@ void TextureBaker::handleTextureNetworkReply() { void TextureBaker::processTexture() { auto processedTexture = image::processImage(_originalTexture, _textureURL.toString().toStdString(), - ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType); + ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, _abortProcessing); + + if (shouldStop()) { + return; + } if (!processedTexture) { handleError("Could not process texture " + _textureURL.toString()); @@ -145,5 +156,11 @@ void TextureBaker::processTexture() { } qCDebug(model_baking) << "Baked texture" << _textureURL; - emit finished(); + setIsFinished(true); +} + +void TextureBaker::setWasAborted(bool wasAborted) { + Baker::setWasAborted(wasAborted); + + qCDebug(model_baking) << "Aborted baking" << _textureURL; } diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h index 5ea696dda0..e5bd41cf0d 100644 --- a/libraries/baking/src/TextureBaker.h +++ b/libraries/baking/src/TextureBaker.h @@ -39,8 +39,11 @@ public: QString getDestinationFilePath() const { return _outputDirectory.absoluteFilePath(_bakedTextureFileName); } QString getBakedTextureFileName() const { return _bakedTextureFileName; } + virtual void setWasAborted(bool wasAborted) override; + public slots: virtual void bake() override; + virtual void abort() override; signals: void originalTextureLoaded(); @@ -58,6 +61,8 @@ private: QDir _outputDirectory; QString _bakedTextureFileName; + + std::atomic _abortProcessing { false }; }; #endif // hifi_TextureBaker_h diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 30299663de..9d49efcba0 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -97,52 +97,64 @@ TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, con } } -gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, srcImageName, true); +gpu::TexturePointer TextureUsage::createStrict2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(srcImage, srcImageName, true, abortProcessing); } -gpu::TexturePointer TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, srcImageName, false); +gpu::TexturePointer TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(srcImage, srcImageName, false, abortProcessing); } -gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, srcImageName, false); +gpu::TexturePointer TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(srcImage, srcImageName, false, abortProcessing); } -gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, srcImageName, false); +gpu::TexturePointer TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(srcImage, srcImageName, false, abortProcessing); } -gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureColorFromImage(srcImage, srcImageName, false); +gpu::TexturePointer TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureColorFromImage(srcImage, srcImageName, false, abortProcessing); } -gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureNormalMapFromImage(srcImage, srcImageName, false); +gpu::TexturePointer TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureNormalMapFromImage(srcImage, srcImageName, false, abortProcessing); } -gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureNormalMapFromImage(srcImage, srcImageName, true); +gpu::TexturePointer TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureNormalMapFromImage(srcImage, srcImageName, true, abortProcessing); } -gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureGrayscaleFromImage(srcImage, srcImageName, false); +gpu::TexturePointer TextureUsage::createRoughnessTextureFromImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(srcImage, srcImageName, false, abortProcessing); } -gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureGrayscaleFromImage(srcImage, srcImageName, true); +gpu::TexturePointer TextureUsage::createRoughnessTextureFromGlossImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(srcImage, srcImageName, true, abortProcessing); } -gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return process2DTextureGrayscaleFromImage(srcImage, srcImageName, false); +gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return process2DTextureGrayscaleFromImage(srcImage, srcImageName, false, abortProcessing); } -gpu::TexturePointer TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - return processCubeTextureColorFromImage(srcImage, srcImageName, true); +gpu::TexturePointer TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return processCubeTextureColorFromImage(srcImage, srcImageName, true, abortProcessing); } -gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(const QImage& srcImage, const std::string& srcImageName) { - return processCubeTextureColorFromImage(srcImage, srcImageName, false); +gpu::TexturePointer TextureUsage::createCubeTextureFromImageWithoutIrradiance(const QImage& srcImage, const std::string& srcImageName, + const std::atomic& abortProcessing) { + return processCubeTextureColorFromImage(srcImage, srcImageName, false, abortProcessing); } @@ -194,8 +206,9 @@ void setCubeTexturesCompressionEnabled(bool enabled) { compressCubeTextures.store(enabled); } - -gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename, int maxNumPixels, TextureUsage::Type textureType) { +gpu::TexturePointer processImage(const QByteArray& content, const std::string& filename, + int maxNumPixels, TextureUsage::Type textureType, + const std::atomic& abortProcessing) { // Help the QImage loader by extracting the image file format from the url filename ext. // Some tga are not created properly without it. auto filenameExtension = filename.substr(filename.find_last_of('.') + 1); @@ -245,7 +258,7 @@ gpu::TexturePointer processImage(const QByteArray& content, const std::string& f } auto loader = TextureUsage::getTextureLoaderForType(textureType); - auto texture = loader(image, filename); + auto texture = loader(image, filename, abortProcessing); return texture; } @@ -321,7 +334,7 @@ struct MyErrorHandler : public nvtt::ErrorHandler { } }; -void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { +void generateMips(gpu::Texture* texture, QImage& image, const std::atomic& abortProcessing = false, int face = -1) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateMips"); @@ -439,14 +452,20 @@ void generateMips(gpu::Texture* texture, QImage& image, int face = -1) { class SequentialTaskDispatcher : public nvtt::TaskDispatcher { public: + SequentialTaskDispatcher(const std::atomic& abortProcessing) : _abortProcessing(abortProcessing) {}; + + const std::atomic& _abortProcessing; + virtual void dispatch(nvtt::Task* task, void* context, int count) override { for (int i = 0; i < count; i++) { - task(context, i); + if (!_abortProcessing.load()) { + task(context, i); + } } } }; - SequentialTaskDispatcher dispatcher; + SequentialTaskDispatcher dispatcher(abortProcessing); nvtt::Compressor compressor; compressor.setTaskDispatcher(&dispatcher); compressor.process(inputOptions, compressionOptions, outputOptions); @@ -482,7 +501,8 @@ void processTextureAlpha(const QImage& srcImage, bool& validAlpha, bool& alphaAs validAlpha = (numOpaques != NUM_PIXELS); } -gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isStrict) { +gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, + bool isStrict, const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "process2DTextureColorFromImage"); QImage image = processSourceImage(srcImage, false); bool validAlpha = image.hasAlphaChannel(); @@ -530,7 +550,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& s } theTexture->setUsage(usage.build()); theTexture->setStoredMipFormat(formatMip); - generateMips(theTexture.get(), image); + generateMips(theTexture.get(), image, abortProcessing); } return theTexture; @@ -606,7 +626,8 @@ QImage processBumpMap(QImage& image) { return result; } -gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImage& srcImage, const std::string& srcImageName, bool isBumpMap) { +gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImage& srcImage, const std::string& srcImageName, + bool isBumpMap, const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "process2DTextureNormalMapFromImage"); QImage image = processSourceImage(srcImage, false); @@ -631,13 +652,15 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(const QImag theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); - generateMips(theTexture.get(), image); + generateMips(theTexture.get(), image, abortProcessing); } return theTexture; } -gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(const QImage& srcImage, const std::string& srcImageName, bool isInvertedPixels) { +gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(const QImage& srcImage, const std::string& srcImageName, + bool isInvertedPixels, + const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "process2DTextureGrayscaleFromImage"); QImage image = processSourceImage(srcImage, false); @@ -665,7 +688,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(const QImag theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); - generateMips(theTexture.get(), image); + generateMips(theTexture.get(), image, abortProcessing); } return theTexture; @@ -927,7 +950,9 @@ const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = { }; const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout); -gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool generateIrradiance) { +gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, + bool generateIrradiance, + const std::atomic& abortProcessing) { PROFILE_RANGE(resource_parse, "processCubeTextureColorFromImage"); gpu::TexturePointer theTexture = nullptr; @@ -986,7 +1011,7 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& theTexture->setStoredMipFormat(formatMip); for (uint8 face = 0; face < faces.size(); ++face) { - generateMips(theTexture.get(), faces[face], face); + generateMips(theTexture.get(), faces[face], abortProcessing, face); } // Generate irradiance while we are at it diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index 3bf45ace98..856dc009cf 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -41,26 +41,42 @@ enum Type { UNUSED_TEXTURE }; -using TextureLoader = std::function; +using TextureLoader = std::function&)>; TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap()); -gpu::TexturePointer create2DTextureFromImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createRoughnessTextureFromGlossImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createMetallicTextureFromImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(const QImage& image, const std::string& srcImageName); -gpu::TexturePointer createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName); +gpu::TexturePointer create2DTextureFromImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createStrict2DTextureFromImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createAlbedoTextureFromImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createEmissiveTextureFromImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createRoughnessTextureFromGlossImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createMetallicTextureFromImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createCubeTextureFromImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); +gpu::TexturePointer createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName, + const std::atomic& abortProcessing); -gpu::TexturePointer process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isStrict); -gpu::TexturePointer process2DTextureNormalMapFromImage(const QImage& srcImage, const std::string& srcImageName, bool isBumpMap); -gpu::TexturePointer process2DTextureGrayscaleFromImage(const QImage& srcImage, const std::string& srcImageName, bool isInvertedPixels); -gpu::TexturePointer processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool generateIrradiance); +gpu::TexturePointer process2DTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isStrict, + const std::atomic& abortProcessing); +gpu::TexturePointer process2DTextureNormalMapFromImage(const QImage& srcImage, const std::string& srcImageName, bool isBumpMap, + const std::atomic& abortProcessing); +gpu::TexturePointer process2DTextureGrayscaleFromImage(const QImage& srcImage, const std::string& srcImageName, bool isInvertedPixels, + const std::atomic& abortProcessing); +gpu::TexturePointer processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool generateIrradiance, + const std::atomic& abortProcessing); } // namespace TextureUsage @@ -74,7 +90,9 @@ void setNormalTexturesCompressionEnabled(bool enabled); void setGrayscaleTexturesCompressionEnabled(bool enabled); void setCubeTexturesCompressionEnabled(bool enabled); -gpu::TexturePointer processImage(const QByteArray& content, const std::string& url, int maxNumPixels, TextureUsage::Type textureType); +gpu::TexturePointer processImage(const QByteArray& content, const std::string& url, + int maxNumPixels, TextureUsage::Type textureType, + const std::atomic& abortProcessing = false); } // namespace image diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index cc29e841ce..fed5c69ce2 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -265,7 +265,7 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) { QImage image = QImage(path); auto loader = image::TextureUsage::getTextureLoaderForType(type, options); - return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString())); + return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString(), false)); } QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, From a8ac015bc4e350ffc768699633f757af51a1f03c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 19 Sep 2017 13:56:42 -0700 Subject: [PATCH 158/202] address code review comments --- assignment-client/src/assets/AssetServer.cpp | 4 ++-- libraries/image/src/image/Image.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 17aae74dfd..fea9d8329d 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -280,10 +280,10 @@ void AssetServer::aboutToFinish() { // abort each of our still running bake tasks, remove pending bakes that were never put on the thread pool auto it = _pendingBakes.begin(); while (it != _pendingBakes.end()) { - auto pendingRunnable = _bakingTaskPool.tryTake(it.value().get()); + auto pendingRunnable = _bakingTaskPool.tryTake(it->get()); if (pendingRunnable) { - _pendingBakes.erase(it); + it = _pendingBakes.erase(it); } else { it.value()->abort(); ++it; diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 9d49efcba0..6081355eaa 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -460,6 +460,8 @@ void generateMips(gpu::Texture* texture, QImage& image, const std::atomic& for (int i = 0; i < count; i++) { if (!_abortProcessing.load()) { task(context, i); + } else { + break; } } } From 7a36894f76e9f17b754e4e2c4e6aab9961769739 Mon Sep 17 00:00:00 2001 From: vladest Date: Tue, 19 Sep 2017 23:25:41 +0200 Subject: [PATCH 159/202] Make sure audio device not gets unchecked --- interface/resources/qml/hifi/audio/Audio.qml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index bc55175f18..b1f80ac5e8 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -194,12 +194,13 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter width: parent.width - inputLevel.width clip: true + checkable: !checked checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; boxSize: margins.sizeCheckBox / 2 isRound: true text: devicename - onClicked: { - if (checked) { + onPressed: { + if (!checked) { Audio.setInputDevice(info, bar.currentIndex === 1); } } @@ -265,9 +266,10 @@ Rectangle { boxSize: margins.sizeCheckBox / 2 isRound: true checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; + checkable: !checked text: devicename - onClicked: { - if (checked) { + onPressed: { + if (!checked) { Audio.setOutputDevice(info, bar.currentIndex === 1); } } From d4e35edc84f300f83f5352b0e4257a5f576409a4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 19 Sep 2017 15:08:44 -0700 Subject: [PATCH 160/202] add memory for unique_ptr in BakeAssetTask --- assignment-client/src/assets/BakeAssetTask.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assignment-client/src/assets/BakeAssetTask.h b/assignment-client/src/assets/BakeAssetTask.h index 5c456c4521..45e7ec8702 100644 --- a/assignment-client/src/assets/BakeAssetTask.h +++ b/assignment-client/src/assets/BakeAssetTask.h @@ -12,6 +12,8 @@ #ifndef hifi_BakeAssetTask_h #define hifi_BakeAssetTask_h +#include + #include #include #include From 9c66a25d837268615ce7d6cbf670377f438452eb Mon Sep 17 00:00:00 2001 From: druiz17 Date: Tue, 19 Sep 2017 17:03:53 -0700 Subject: [PATCH 161/202] fixing and cleanup tablet inputs --- .../controllers/controllerDispatcher.js | 6 +- .../controllerModules/overlayLaserInput.js | 344 ++++-------------- .../controllerModules/tabletStylusInput.js | 268 ++------------ .../libraries/controllerDispatcherUtils.js | 4 +- scripts/system/libraries/touchEventUtils.js | 270 ++++++++++++++ 5 files changed, 375 insertions(+), 517 deletions(-) create mode 100644 scripts/system/libraries/touchEventUtils.js diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 63657e9b6f..22987245a4 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -140,10 +140,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.setIgnoreTablet = function() { - if (HMD.tabletID !== _this.tabletID) { - RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); - RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); - } + RayPick.setIgnoreOverlays(_this.leftControllerRayPick, [HMD.tabletID]); + RayPick.setIgnoreOverlays(_this.rightControllerRayPick, [HMD.tabletID]); }; this.update = function () { diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 218122e876..9bf7ed23da 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -16,6 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { + var touchEvent = Script.require("/~/system/libraries/touchEventUtils.js"); var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -88,186 +89,6 @@ Script.include("/~/system/libraries/controllers.js"); var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; - function laserTargetHasKeyboardFocus(laserTarget) { - if (laserTarget && laserTarget !== NULL_UUID) { - return Overlays.keyboardFocusOverlay === laserTarget; - } - } - - function setKeyboardFocusOnLaserTarget(laserTarget) { - if (laserTarget && laserTarget !== NULL_UUID) { - Overlays.keyboardFocusOverlay = laserTarget; - Entities.keyboardFocusEntity = NULL_UUID; - } - } - - function sendHoverEnterEventToLaserTarget(hand, laserTarget) { - if (!laserTarget) { - return; - } - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "None" - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendHoverEnterOverlay(laserTarget.overlayID, pointerEvent); - } - } - - function sendHoverOverEventToLaserTarget(hand, laserTarget) { - - if (!laserTarget) { - return; - } - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "None" - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseMoveOnOverlay(laserTarget.overlayID, pointerEvent); - Overlays.sendHoverOverOverlay(laserTarget.overlayID, pointerEvent); - } - } - - function sendTouchStartEventToLaserTarget(hand, laserTarget) { - if (!laserTarget) { - return; - } - - var pointerEvent = { - type: "Press", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "Primary", - isPrimaryHeld: true - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendMousePressOnOverlay(laserTarget.overlayID, pointerEvent); - } - } - - function sendTouchEndEventToLaserTarget(hand, laserTarget) { - if (!laserTarget) { - return; - } - var pointerEvent = { - type: "Release", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "Primary" - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); - Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); - } - } - - function sendTouchMoveEventToLaserTarget(hand, laserTarget) { - if (!laserTarget) { - return; - } - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: laserTarget.position2D, - pos3D: laserTarget.position, - normal: laserTarget.normal, - direction: Vec3.subtract(ZERO_VEC, laserTarget.normal), - button: "Primary", - isPrimaryHeld: true - }; - - if (laserTarget.overlayID && laserTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseReleaseOnOverlay(laserTarget.overlayID, pointerEvent); - } - } - - // will return undefined if overlayID does not exist. - function calculateLaserTargetFromOverlay(worldPos, overlayID) { - var overlayPosition = Overlays.getProperty(overlayID, "position"); - if (overlayPosition === undefined) { - return null; - } - - // project stylusTip onto overlay plane. - var overlayRotation = Overlays.getProperty(overlayID, "rotation"); - if (overlayRotation === undefined) { - return null; - } - var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); - var distance = Vec3.dot(Vec3.subtract(worldPos, overlayPosition), normal); - - // calclulate normalized position - var invRot = Quat.inverse(overlayRotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, overlayPosition)); - var dpi = Overlays.getProperty(overlayID, "dpi"); - - var dimensions; - if (dpi) { - // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property - // is used as a scale. - var resolution = Overlays.getProperty(overlayID, "resolution"); - if (resolution === undefined) { - return null; - } - resolution.z = 1;// Circumvent divide-by-zero. - var scale = Overlays.getProperty(overlayID, "dimensions"); - if (scale === undefined) { - return null; - } - scale.z = 0.01;// overlay dimensions are 2D, not 3D. - dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); - } else { - dimensions = Overlays.getProperty(overlayID, "dimensions"); - if (dimensions === undefined) { - return null; - } - if (!dimensions.z) { - dimensions.z = 0.01;// sometimes overlay dimensions are 2D, not 3D. - } - } - var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; - var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); - - // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { - x: normalizedPosition.x * dimensions.x, - y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis - }; - - return { - entityID: null, - overlayID: overlayID, - distance: distance, - position: worldPos, - position2D: position2D, - normal: normal, - normalizedPosition: normalizedPosition, - dimensions: dimensions, - valid: true - }; - } - function distance2D(a, b) { var dx = (a.x - b.x); var dy = (a.y - b.y); @@ -277,16 +98,11 @@ Script.include("/~/system/libraries/controllers.js"); function OverlayLaserInput(hand) { this.hand = hand; this.active = false; - this.previousLaserClikcedTarget = false; + this.previousLaserClickedTarget = false; this.laserPressingTarget = false; - this.tabletScreenID = null; this.mode = "none"; - this.laserTargetID = null; this.laserTarget = null; this.pressEnterLaserTarget = null; - this.hover = false; - this.target = null; - this.lastValidTargetID = this.tabletTargetID; this.parameters = makeDispatcherModuleParameters( @@ -307,23 +123,35 @@ Script.include("/~/system/libraries/controllers.js"); return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; }; - this.stealTouchFocus = function(laserTarget) { - this.requestTouchFocus(laserTarget); + this.hasTouchFocus = function(laserTarget) { + return (laserTarget.overlayID === this.hoverOverlay); }; this.requestTouchFocus = function(laserTarget) { - if (laserTarget !== null || laserTarget !== undefined) { - sendHoverEnterEventToLaserTarget(this.hand, this.laserTarget); - this.lastValidTargetID = laserTarget; + if (laserTarget.overlayID && + laserTarget.overlayID !== this.hoverOverlay) { + this.hoverOverlay = laserTarget.overlayID; + touchEvent.sendHoverEnterEventToTouchTarget(this.hand, laserTarget); } }; this.relinquishTouchFocus = function() { // send hover leave event. - var pointerEvent = { type: "Move", id: this.hand + 1 }; - Overlays.sendMouseMoveOnOverlay(this.lastValidTargetID, pointerEvent); - Overlays.sendHoverOverOverlay(this.lastValidTargetID, pointerEvent); - Overlays.sendHoverLeaveOverlay(this.lastValidID, pointerEvent); + if (this.hoverOverlay) { + var pointerEvent = { type: "Move", id: this.hand + 1 }; + Overlays.sendMouseMoveOnOverlay(this.hoverOverlay, pointerEvent); + Overlays.sendHoverOverOverlay(this.hoverOverlay, pointerEvent); + Overlays.sendHoverLeaveOverlay(this.hoverOverlay, pointerEvent); + this.hoverOverlay = null; + } + }; + + this.stealTouchFocus = function(laserTarget) { + if (laserTarget.overlayID === this.getOtherModule().hoverOverlay) { + this.getOtherModule().relinquishTouchFocus(); + } + + this.requestTouchFocus(laserTarget); }; this.updateLaserPointer = function(controllerData) { @@ -345,38 +173,23 @@ Script.include("/~/system/libraries/controllers.js"); this.processControllerTriggers = function(controllerData) { if (controllerData.triggerClicks[this.hand]) { this.mode = "full"; - this.laserPressingTarget = true; - this.hover = false; } else if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { this.mode = "half"; - this.laserPressingTarget = false; - this.hover = true; - this.requestTouchFocus(this.laserTargetID); } else { this.mode = "none"; - this.laserPressingTarget = false; - this.hover = false; - this.relinquishTouchFocus(); - } }; - this.hovering = function() { - if (!laserTargetHasKeyboardFocus(this.laserTagetID)) { - setKeyboardFocusOnLaserTarget(this.laserTargetID); - } - sendHoverOverEventToLaserTarget(this.hand, this.laserTarget); - }; - this.laserPressEnter = function () { - sendTouchStartEventToLaserTarget(this.hand, this.laserTarget); + this.stealTouchFocus(this.laserTarget); + touchEvent.sendTouchStartEventToTouchTarget(this.hand, this.laserTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); this.touchingEnterTimer = 0; this.pressEnterLaserTarget = this.laserTarget; this.deadspotExpired = false; - var LASER_PRESS_TO_MOVE_DEADSPOT = 0.026; + var LASER_PRESS_TO_MOVE_DEADSPOT = 0.094; this.deadspotRadius = Math.tan(LASER_PRESS_TO_MOVE_DEADSPOT) * this.laserTarget.distance; }; @@ -386,15 +199,15 @@ Script.include("/~/system/libraries/controllers.js"); } // special case to handle home button. - if (this.laserTargetID === HMD.homeButtonID) { - Messages.sendLocalMessage("home", this.laserTargetID); + if (this.laserTarget.overlayID === HMD.homeButtonID) { + Messages.sendLocalMessage("home", this.laserTarget.overlayID); } // send press event if (this.deadspotExpired) { - sendTouchEndEventToLaserTarget(this.hand, this.laserTarget); + touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.laserTarget); } else { - sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); + touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.pressEnterLaserTarget); } }; @@ -402,27 +215,49 @@ Script.include("/~/system/libraries/controllers.js"); this.touchingEnterTimer += dt; if (this.laserTarget) { - var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds - if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || - distance2D( this.laserTarget.position2D, - this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { - sendTouchMoveEventToLaserTarget(this.hand, this.laserTarget); - this.deadspotExpired = true; + if (controllerData.triggerClicks[this.hand]) { + var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds + if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || + distance2D(this.laserTarget.position2D, + this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { + touchEvent.sendTouchMoveEventToTouchTarget(this.hand, this.laserTarget); + this.deadspotExpired = true; + } + } else { + this.laserPressingTarget = false; } } else { this.laserPressingTarget = false; } }; - this.releaseTouchEvent = function() { - sendTouchEndEventToLaserTarget(this.hand, this.pressEnterLaserTarget); - }; - - - this.updateLaserTargets = function(controllerData) { + this.processLaser = function(controllerData) { + if (this.shouldExit(controllerData) || this.getOtherModule().active) { + this.exitModule(); + return false; + } var intersection = controllerData.rayPicks[this.hand]; - this.laserTargetID = intersection.objectID; - this.laserTarget = calculateLaserTargetFromOverlay(intersection.intersection, intersection.objectID); + var laserTarget = touchEvent.composeTouchTargetFromIntersection(intersection); + + if (controllerData.triggerClicks[this.hand]) { + this.laserTarget = laserTarget; + this.laserPressingTarget = true; + } else { + this.requestTouchFocus(laserTarget); + + if (!touchEvent.touchTargetHasKeyboardFocus(laserTarget)) { + touchEvent.setKeyboardFocusOnTouchTarget(laserTarget); + } + + if (this.hasTouchFocus(laserTarget) && !this.laserPressingTarget) { + touchEvent.sendHoverOverEventToTouchTarget(this.hand, laserTarget); + } + } + + this.processControllerTriggers(controllerData); + this.updateLaserPointer(controllerData); + this.active = true; + return true; }; this.shouldExit = function(controllerData) { @@ -436,7 +271,12 @@ Script.include("/~/system/libraries/controllers.js"); }; this.exitModule = function() { - this.releaseTouchEvent(); + if (this.laserPressingTarget) { + this.deadspotExpired = true; + this.laserPressExit(); + this.laserPressingTarget = false; + } + this.deleteContextOverlay(); this.relinquishTouchFocus(); this.reset(); this.updateLaserPointer(); @@ -444,12 +284,6 @@ Script.include("/~/system/libraries/controllers.js"); }; this.reset = function() { - this.hover = false; - this.pressEnterLaserTarget = null; - this.laserTarget = null; - this.laserTargetID = null; - this.laserPressingTarget = false; - this.previousLaserClickedTarget = null; this.mode = "none"; this.active = false; }; @@ -467,35 +301,13 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData) { - this.target = null; - var intersection = controllerData.rayPicks[this.hand]; - if (intersection.type === RayPick.INTERSECTED_OVERLAY) { - if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE && !this.getOtherModule().active) { - this.target = intersection.objectID; - this.active = true; - return makeRunningValues(true, [], []); - } else { - this.deleteContextOverlay(); - } + if (this.processLaser(controllerData)) { + return makeRunningValues(true, [], []); } - this.reset(); return makeRunningValues(false, [], []); }; this.run = function (controllerData, deltaTime) { - if (this.shouldExit(controllerData)) { - this.exitModule(); - return makeRunningValues(false, [], []); - } - - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE) { - this.deleteContextOverlay(); - } - - this.updateLaserTargets(controllerData); - this.processControllerTriggers(controllerData); - this.updateLaserPointer(controllerData); - if (!this.previousLaserClickedTarget && this.laserPressingTarget) { this.laserPressEnter(); } @@ -508,11 +320,11 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressing(controllerData, deltaTime); } - if (this.hover) { - this.hovering(); + if (this.processLaser(controllerData)) { + return makeRunningValues(true, [], []); + } else { + return makeRunningValues(false, [], []); } - - return makeRunningValues(true, [], []); }; this.cleanup = function () { diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 230038adb5..52706fc6d7 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -16,7 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - + var touchEvent = Script.require("/~/system/libraries/touchEventUtils.js"); // triggered when stylus presses a web overlay/entity var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; @@ -25,230 +25,6 @@ Script.include("/~/system/libraries/controllers.js"); var WEB_STYLUS_LENGTH = 0.2; var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative number) to slide stylus in hand - - function stylusTargetHasKeyboardFocus(stylusTarget) { - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - return Entities.keyboardFocusEntity === stylusTarget.entityID; - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - return Overlays.keyboardFocusOverlay === stylusTarget.overlayID; - } - } - - function setKeyboardFocusOnStylusTarget(stylusTarget) { - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID && - Entities.wantsHandControllerPointerEvents(stylusTarget.entityID)) { - Overlays.keyboardFocusOverlay = NULL_UUID; - Entities.keyboardFocusEntity = stylusTarget.entityID; - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.keyboardFocusOverlay = stylusTarget.overlayID; - Entities.keyboardFocusEntity = NULL_UUID; - } - } - - function sendHoverEnterEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "None" - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendHoverEnterEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendHoverEnterOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - function sendHoverOverEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "None" - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoverOverEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); - Overlays.sendHoverOverOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - function sendTouchStartEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Press", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "Primary", - isPrimaryHeld: true - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMousePressOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendClickDownOnEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMousePressOnOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - function sendTouchEndEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Release", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "Primary" - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseReleaseOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendClickReleaseOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoverLeaveEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseReleaseOnOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - function sendTouchMoveEventToStylusTarget(hand, stylusTarget) { - var pointerEvent = { - type: "Move", - id: hand + 1, // 0 is reserved for hardware mouse - pos2D: stylusTarget.position2D, - pos3D: stylusTarget.position, - normal: stylusTarget.normal, - direction: Vec3.subtract(ZERO_VEC, stylusTarget.normal), - button: "Primary", - isPrimaryHeld: true - }; - - if (stylusTarget.entityID && stylusTarget.entityID !== NULL_UUID) { - Entities.sendMouseMoveOnEntity(stylusTarget.entityID, pointerEvent); - Entities.sendHoldingClickOnEntity(stylusTarget.entityID, pointerEvent); - } else if (stylusTarget.overlayID && stylusTarget.overlayID !== NULL_UUID) { - Overlays.sendMouseMoveOnOverlay(stylusTarget.overlayID, pointerEvent); - } - } - - // will return undefined if overlayID does not exist. - function calculateStylusTargetFromOverlay(stylusTip, overlayID) { - var overlayPosition = Overlays.getProperty(overlayID, "position"); - if (overlayPosition === undefined) { - return; - } - - // project stylusTip onto overlay plane. - var overlayRotation = Overlays.getProperty(overlayID, "rotation"); - if (overlayRotation === undefined) { - return; - } - var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); - var distance = Vec3.dot(Vec3.subtract(stylusTip.position, overlayPosition), normal); - var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); - - // calclulate normalized position - var invRot = Quat.inverse(overlayRotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); - var dpi = Overlays.getProperty(overlayID, "dpi"); - - var dimensions; - if (dpi) { - // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property - // is used as a scale. - var resolution = Overlays.getProperty(overlayID, "resolution"); - if (resolution === undefined) { - return; - } - resolution.z = 1; // Circumvent divide-by-zero. - var scale = Overlays.getProperty(overlayID, "dimensions"); - if (scale === undefined) { - return; - } - scale.z = 0.01; // overlay dimensions are 2D, not 3D. - dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); - } else { - dimensions = Overlays.getProperty(overlayID, "dimensions"); - if (dimensions === undefined) { - return; - } - if (!dimensions.z) { - dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. - } - } - var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; - var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); - - // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { - x: normalizedPosition.x * dimensions.x, - y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis - }; - - return { - entityID: null, - overlayID: overlayID, - distance: distance, - position: position, - position2D: position2D, - normal: normal, - normalizedPosition: normalizedPosition, - dimensions: dimensions, - valid: true - }; - } - - // will return undefined if entity does not exist. - function calculateStylusTargetFromEntity(stylusTip, props) { - if (props.rotation === undefined) { - // if rotation is missing from props object, then this entity has probably been deleted. - return; - } - - // project stylus tip onto entity plane. - var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1}); - Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); - var distance = Vec3.dot(Vec3.subtract(stylusTip.position, props.position), normal); - var position = Vec3.subtract(stylusTip.position, Vec3.multiply(normal, distance)); - - // generate normalized coordinates - var invRot = Quat.inverse(props.rotation); - var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); - var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; - var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); - - // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. - var position2D = { - x: normalizedPosition.x * props.dimensions.x, - y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis - }; - - return { - entityID: props.id, - entityProps: props, - overlayID: null, - distance: distance, - position: position, - position2D: position2D, - normal: normal, - normalizedPosition: normalizedPosition, - dimensions: props.dimensions, - valid: true - }; - } - function isNearStylusTarget(stylusTargets, edgeBorder, minNormalDistance, maxNormalDistance) { for (var i = 0; i < stylusTargets.length; i++) { var stylusTarget = stylusTargets[i]; @@ -330,7 +106,7 @@ Script.include("/~/system/libraries/controllers.js"); 100); this.getOtherHandController = function() { - return (this.hand === RIGHT_HAND) ? Controller.Standard.LeftHand : Controller.Standard.RightHand; + return (this.hand === RIGHT_HAND) ? leftTabletStylusInput : rightTabletStylusInput; }; this.handToController = function() { @@ -430,12 +206,12 @@ Script.include("/~/system/libraries/controllers.js"); stylusTarget.entityID !== this.hoverEntity && stylusTarget.entityID !== this.getOtherHandController().hoverEntity) { this.hoverEntity = stylusTarget.entityID; - sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); + touchEvent.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget); } else if (stylusTarget.overlayID && stylusTarget.overlayID !== this.hoverOverlay && stylusTarget.overlayID !== this.getOtherHandController().hoverOverlay) { this.hoverOverlay = stylusTarget.overlayID; - sendHoverEnterEventToStylusTarget(this.hand, stylusTarget); + touchEvent.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget); } }; @@ -492,7 +268,7 @@ Script.include("/~/system/libraries/controllers.js"); for (i = 0; i < candidateEntities.length; i++) { props = candidateEntities[i]; if (props && props.type === "Web") { - stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, candidateEntities[i]); + stylusTarget = touchEvent.calculateTouchTargetFromEntity(this.stylusTip, candidateEntities[i]); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -502,7 +278,7 @@ Script.include("/~/system/libraries/controllers.js"); // add the tabletScreen, if it is valid if (HMD.tabletScreenID && HMD.tabletScreenID !== NULL_UUID && Overlays.getProperty(HMD.tabletScreenID, "visible")) { - stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, HMD.tabletScreenID); + stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, HMD.tabletScreenID); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -511,7 +287,7 @@ Script.include("/~/system/libraries/controllers.js"); // add the tablet home button. if (HMD.homeButtonID && HMD.homeButtonID !== NULL_UUID && Overlays.getProperty(HMD.homeButtonID, "visible")) { - stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, HMD.homeButtonID); + stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, HMD.homeButtonID); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -530,9 +306,9 @@ Script.include("/~/system/libraries/controllers.js"); var sensorScaleFactor = MyAvatar.sensorToWorldScale; this.isNearStylusTarget = isNearStylusTarget(stylusTargets, - (EDGE_BORDER + hysteresisOffset) * sensorScaleFactor, - (TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset) * sensorScaleFactor, - (WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset) * sensorScaleFactor); + (EDGE_BORDER + hysteresisOffset) * sensorScaleFactor, + (TABLET_MIN_TOUCH_DISTANCE - hysteresisOffset) * sensorScaleFactor, + (WEB_DISPLAY_STYLUS_DISTANCE + hysteresisOffset) * sensorScaleFactor); if (this.isNearStylusTarget) { if (!this.useFingerInsteadOfStylus) { @@ -556,12 +332,12 @@ Script.include("/~/system/libraries/controllers.js"); this.requestTouchFocus(nearestStylusTarget); - if (!stylusTargetHasKeyboardFocus(nearestStylusTarget)) { - setKeyboardFocusOnStylusTarget(nearestStylusTarget); + if (!touchEvent.touchTargetHasKeyboardFocus(nearestStylusTarget)) { + touchEvent.setKeyboardFocusOnTouchTarget(nearestStylusTarget); } - if (this.hasTouchFocus(nearestStylusTarget)) { - sendHoverOverEventToStylusTarget(this.hand, nearestStylusTarget); + if (this.hasTouchFocus(nearestStylusTarget) && !this.stylusTouchingTarget) { + touchEvent.sendHoverOverEventToTouchTarget(this.hand, nearestStylusTarget); } // filter out presses when tip is moving away from tablet. @@ -592,14 +368,14 @@ Script.include("/~/system/libraries/controllers.js"); this.stylusTouchingEnter = function () { this.stealTouchFocus(this.stylusTarget); - sendTouchStartEventToStylusTarget(this.hand, this.stylusTarget); + touchEvent.sendTouchStartEventToTouchTarget(this.hand, this.stylusTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); this.touchingEnterTimer = 0; this.touchingEnterStylusTarget = this.stylusTarget; this.deadspotExpired = false; - var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0381; + var TOUCH_PRESS_TO_MOVE_DEADSPOT = 0.0481; this.deadspotRadius = TOUCH_PRESS_TO_MOVE_DEADSPOT; }; @@ -616,9 +392,9 @@ Script.include("/~/system/libraries/controllers.js"); // send press event if (this.deadspotExpired) { - sendTouchEndEventToStylusTarget(this.hand, this.stylusTarget); + touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.stylusTarget); } else { - sendTouchEndEventToStylusTarget(this.hand, this.touchingEnterStylusTarget); + touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.touchingEnterStylusTarget); } }; @@ -627,9 +403,9 @@ Script.include("/~/system/libraries/controllers.js"); this.touchingEnterTimer += dt; if (this.stylusTarget.entityID) { - this.stylusTarget = calculateStylusTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); + this.stylusTarget = touchEvent.calculateTouchTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); } else if (this.stylusTarget.overlayID) { - this.stylusTarget = calculateStylusTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); + this.stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); } var TABLET_MIN_TOUCH_DISTANCE = -0.1; @@ -642,7 +418,7 @@ Script.include("/~/system/libraries/controllers.js"); if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || distance2D(this.stylusTarget.position2D, this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { - sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); + touchEvent.sendTouchMoveEventToTouchTarget(this.hand, this.stylusTarget); this.deadspotExpired = true; } } else { @@ -657,7 +433,7 @@ Script.include("/~/system/libraries/controllers.js"); var overlayLaserModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); if (overlayLaserModule) { - return overlayLaserModule.isReady(controllerData).active; + return !overlayLaserModule.shouldExit(controllerData); } return false; }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 33eec74111..10931e4e93 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -318,6 +318,8 @@ if (typeof module !== 'undefined') { makeRunningValues: makeRunningValues, LEFT_HAND: LEFT_HAND, RIGHT_HAND: RIGHT_HAND, - BUMPER_ON_VALUE: BUMPER_ON_VALUE + BUMPER_ON_VALUE: BUMPER_ON_VALUE, + projectOntoOverlayXYPlane: projectOntoOverlayXYPlane, + projectOntoEntityXYPlane: projectOntoEntityXYPlane }; } diff --git a/scripts/system/libraries/touchEventUtils.js b/scripts/system/libraries/touchEventUtils.js new file mode 100644 index 0000000000..fbd56e16ae --- /dev/null +++ b/scripts/system/libraries/touchEventUtils.js @@ -0,0 +1,270 @@ +"use strict"; + +// touchEventUtils.js +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, + controllerDispatcher.NULL_UUID, enableDispatcherModule, disableDispatcherModule, makeRunningValues, + Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, controllerDispatcher.ZERO_VEC, + AVATAR_SELF_ID, HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset +*/ + +var controllerDispatcher = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); +function touchTargetHasKeyboardFocus(touchTarget) { + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + return Entities.keyboardFocusEntity === touchTarget.entityID; + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + return Overlays.keyboardFocusOverlay === touchTarget.overlayID; + } +} + +function setKeyboardFocusOnTouchTarget(touchTarget) { + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID && + Entities.wantsHandControllerPointerEvents(touchTarget.entityID)) { + Overlays.keyboardFocusOverlay = controllerDispatcher.NULL_UUID; + Entities.keyboardFocusEntity = touchTarget.entityID; + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.keyboardFocusOverlay = touchTarget.overlayID; + Entities.keyboardFocusEntity = controllerDispatcher.NULL_UUID; + } +} + +function sendHoverEnterEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "None" + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendHoverEnterEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendHoverEnterOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function sendHoverOverEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "None" + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendMouseMoveOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendHoverOverEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(touchTarget.overlayID, pointerEvent); + Overlays.sendHoverOverOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function sendTouchStartEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Press", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendMousePressOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendClickDownOnEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendMousePressOnOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function sendTouchEndEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Release", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "Primary" + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendMouseReleaseOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendClickReleaseOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendHoverLeaveEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendMouseReleaseOnOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function sendTouchMoveEventToTouchTarget(hand, touchTarget) { + var pointerEvent = { + type: "Move", + id: hand + 1, // 0 is reserved for hardware mouse + pos2D: touchTarget.position2D, + pos3D: touchTarget.position, + normal: touchTarget.normal, + direction: Vec3.subtract(controllerDispatcher.ZERO_VEC, touchTarget.normal), + button: "Primary", + isPrimaryHeld: true + }; + + if (touchTarget.entityID && touchTarget.entityID !== controllerDispatcher.NULL_UUID) { + Entities.sendMouseMoveOnEntity(touchTarget.entityID, pointerEvent); + Entities.sendHoldingClickOnEntity(touchTarget.entityID, pointerEvent); + } else if (touchTarget.overlayID && touchTarget.overlayID !== controllerDispatcher.NULL_UUID) { + Overlays.sendMouseMoveOnOverlay(touchTarget.overlayID, pointerEvent); + } +} + +function composeTouchTargetFromIntersection(intersection) { + var isEntity = (intersection.type === RayPick.INTERSECTED_ENTITY); + var objectID = intersection.objectID; + var worldPos = intersection.intersection; + var props = null; + if (isEntity) { + props = Entities.getProperties(intersection.objectID); + } + + var position2D =(isEntity ? controllerDispatcher.projectOntoEntityXYPlane(objectID, worldPos, props) : + controllerDispatcher.projectOntoOverlayXYPlane(objectID, worldPos)); + return { + entityID: isEntity ? objectID : null, + overlayID: isEntity ? null : objectID, + distance: intersection.distance, + position: worldPos, + position2D: position2D, + normal: intersection.surfaceNormal + }; +} + +// will return undefined if overlayID does not exist. +function calculateTouchTargetFromOverlay(touchTip, overlayID) { + var overlayPosition = Overlays.getProperty(overlayID, "position"); + if (overlayPosition === undefined) { + return; + } + + // project touchTip onto overlay plane. + var overlayRotation = Overlays.getProperty(overlayID, "rotation"); + if (overlayRotation === undefined) { + return; + } + var normal = Vec3.multiplyQbyV(overlayRotation, {x: 0, y: 0, z: 1}); + var distance = Vec3.dot(Vec3.subtract(touchTip.position, overlayPosition), normal); + var position = Vec3.subtract(touchTip.position, Vec3.multiply(normal, distance)); + + // calclulate normalized position + var invRot = Quat.inverse(overlayRotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, overlayPosition)); + var dpi = Overlays.getProperty(overlayID, "dpi"); + + var dimensions; + if (dpi) { + // Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property + // is used as a scale. + var resolution = Overlays.getProperty(overlayID, "resolution"); + if (resolution === undefined) { + return; + } + resolution.z = 1; // Circumvent divide-by-zero. + var scale = Overlays.getProperty(overlayID, "dimensions"); + if (scale === undefined) { + return; + } + scale.z = 0.01; // overlay dimensions are 2D, not 3D. + dimensions = Vec3.multiplyVbyV(Vec3.multiply(resolution, INCHES_TO_METERS / dpi), scale); + } else { + dimensions = Overlays.getProperty(overlayID, "dimensions"); + if (dimensions === undefined) { + return; + } + if (!dimensions.z) { + dimensions.z = 0.01; // sometimes overlay dimensions are 2D, not 3D. + } + } + var invDimensions = { x: 1 / dimensions.x, y: 1 / dimensions.y, z: 1 / dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), DEFAULT_REGISTRATION_POINT); + + // 2D position on overlay plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { + x: normalizedPosition.x * dimensions.x, + y: (1 - normalizedPosition.y) * dimensions.y // flip y-axis + }; + + return { + entityID: null, + overlayID: overlayID, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: dimensions, + valid: true + }; +} + +// will return undefined if entity does not exist. +function calculateTouchTargetFromEntity(touchTip, props) { + if (props.rotation === undefined) { + // if rotation is missing from props object, then this entity has probably been deleted. + return; + } + + // project touch tip onto entity plane. + var normal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1}); + Vec3.multiplyQbyV(props.rotation, {x: 0, y: 1, z: 0}); + var distance = Vec3.dot(Vec3.subtract(touchTip.position, props.position), normal); + var position = Vec3.subtract(touchTip.position, Vec3.multiply(normal, distance)); + + // generate normalized coordinates + var invRot = Quat.inverse(props.rotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(position, props.position)); + var invDimensions = { x: 1 / props.dimensions.x, y: 1 / props.dimensions.y, z: 1 / props.dimensions.z }; + var normalizedPosition = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); + + // 2D position on entity plane in meters, relative to the bounding box upper-left hand corner. + var position2D = { + x: normalizedPosition.x * props.dimensions.x, + y: (1 - normalizedPosition.y) * props.dimensions.y // flip y-axis + }; + + return { + entityID: props.id, + entityProps: props, + overlayID: null, + distance: distance, + position: position, + position2D: position2D, + normal: normal, + normalizedPosition: normalizedPosition, + dimensions: props.dimensions, + valid: true + }; +} + +module.exports = { + calculateTouchTargetFromEntity: calculateTouchTargetFromEntity, + calculateTouchTargetFromOverlay: calculateTouchTargetFromOverlay, + touchTargetHasKeyboardFocus: touchTargetHasKeyboardFocus, + setKeyboardFocusOnTouchTarget: setKeyboardFocusOnTouchTarget, + sendHoverEnterEventToTouchTarget: sendHoverEnterEventToTouchTarget, + sendHoverOverEventToTouchTarget: sendHoverOverEventToTouchTarget, + sendTouchStartEventToTouchTarget: sendTouchStartEventToTouchTarget, + sendTouchEndEventToTouchTarget: sendTouchEndEventToTouchTarget, + sendTouchMoveEventToTouchTarget: sendTouchMoveEventToTouchTarget, + composeTouchTargetFromIntersection: composeTouchTargetFromIntersection +}; From 708698f07913a887ea674b3d5a1b90ecd6118a03 Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 19 Sep 2017 23:20:23 -0400 Subject: [PATCH 162/202] potential fix for the doppleganger "scrunching into a ball" issue --- .../dist/app-doppleganger-marketplace.js | 20 ++++++++++++++++--- .../doppleganger-attachments/doppleganger.js | 16 ++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js index c2cf2a2353..bc5368ba5b 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/dist/app-doppleganger-marketplace.js @@ -599,6 +599,20 @@ Doppleganger.prototype = { return this.active; }, + // @private @method - get an avatar's "absolute joint translations in object frame" as local translations + // @param {AvatarData} - avatar to read translations from + // @return {glm::vec3[]} - the adapted translations + _getLocalAvatarJointTranslations: function(avatar) { + // NOTE: avatar.getJointTranslations() seems to return meters and avatar.getJointTranslation(jointIndex) centimeters... + // adapting meters -> centimeters on this side seems to fix the "scrunching into ball" problem (~Beta 54) + // and perform slightly faster than calling getJointTranslation(jointIndex) N times. + const CENTIMETERS_PER_METER = 100.0; + function scaleToMeters(v) { + return Vec3.multiply(CENTIMETERS_PER_METER, v); + } + return avatar.getJointTranslations().map(scaleToMeters); + }, + // @public @method - synchronize the joint data between Avatar / doppleganger update: function() { this.frame++; @@ -612,7 +626,7 @@ Doppleganger.prototype = { } var rotations = this.avatar.getJointRotations(); - var translations = this.avatar.getJointTranslations(); + var translations = this._getLocalAvatarJointTranslations(this.avatar); var size = rotations.length; // note: this mismatch can happen when the avatar's model is actively changing @@ -1488,13 +1502,13 @@ DebugControls.prototype = { /* 6 */ /***/ (function(module, exports) { -module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n"; +module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n" /***/ }), /* 7 */ /***/ (function(module, exports) { -module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n"; +module.exports = "data:image/svg+xml;xml,\n\n\nimage/svg+xml\n\t.st0{fill:#FFFFFF;}\n" /***/ }) /******/ ]); \ No newline at end of file diff --git a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js index 190a8aa69e..9e35d791d6 100644 --- a/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js +++ b/unpublishedScripts/marketplace/doppleganger-attachments/doppleganger.js @@ -78,6 +78,20 @@ Doppleganger.prototype = { return this.active; }, + // @private @method - get an avatar's "absolute joint translations in object frame" as local translations + // @param {AvatarData} - avatar to read translations from + // @return {glm::vec3[]} - the adapted translations + _getLocalAvatarJointTranslations: function(avatar) { + // NOTE: avatar.getJointTranslations() seems to return meters and avatar.getJointTranslation(jointIndex) centimeters... + // adapting meters -> centimeters on this side seems to fix the "scrunching into ball" problem (~Beta 54) + // and perform slightly faster than calling getJointTranslation(jointIndex) N times. + const CENTIMETERS_PER_METER = 100.0; + function scaleToMeters(v) { + return Vec3.multiply(CENTIMETERS_PER_METER, v); + } + return avatar.getJointTranslations().map(scaleToMeters); + }, + // @public @method - synchronize the joint data between Avatar / doppleganger update: function() { this.frame++; @@ -91,7 +105,7 @@ Doppleganger.prototype = { } var rotations = this.avatar.getJointRotations(); - var translations = this.avatar.getJointTranslations(); + var translations = this._getLocalAvatarJointTranslations(this.avatar); var size = rotations.length; // note: this mismatch can happen when the avatar's model is actively changing From 80f56e48caff308426c4a06cbf36aa24e924a93a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 20 Sep 2017 16:50:19 +1200 Subject: [PATCH 163/202] Fix Record app's toolbar button not highlighting when app dialog open --- unpublishedScripts/marketplace/record/record.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unpublishedScripts/marketplace/record/record.js b/unpublishedScripts/marketplace/record/record.js index 5439d68c9a..6110721ff9 100644 --- a/unpublishedScripts/marketplace/record/record.js +++ b/unpublishedScripts/marketplace/record/record.js @@ -571,7 +571,7 @@ function onTabletScreenChanged(type, url) { // Opened/closed dialog in tablet or window. - var RECORD_URL = "/scripts/system/html/record.html"; + var RECORD_URL = "/html/record.html"; if (type === "Web" && url.slice(-RECORD_URL.length) === RECORD_URL) { if (Dialog.finishOnOpen()) { From e78d8bc13259cd991fafcaf0f7179daaee79e4ce Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 20 Sep 2017 09:11:06 -0700 Subject: [PATCH 164/202] tablet on wiki planet --- scripts/system/libraries/WebTablet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index d1182f197c..21f0fac01a 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -51,7 +51,7 @@ function calcSpawnInfo(hand, landscape) { var forward = Quat.getForward(headRot); var FORWARD_OFFSET = 0.6 * MyAvatar.sensorToWorldScale; finalPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward)); - var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, {x: 0, y: 1, z: 0}); + var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y)); return { position: finalPosition, rotation: landscape ? Quat.multiply(orientation, ROT_LANDSCAPE) : Quat.multiply(orientation, ROT_Y_180) From 4d904bd5ce7d7b1a805c2f8fbdda630afc27576f Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Wed, 20 Sep 2017 18:43:16 +0100 Subject: [PATCH 165/202] Changed naming of avatarGrav --- libraries/physics/src/CharacterController.cpp | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 0dcc0bcbe8..6c78b46b48 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -21,7 +21,8 @@ const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float JUMP_SPEED = 3.5f; const float MAX_FALL_HEIGHT = 20.0f; -float DEFAULT_CHARACTER_GRAVITY = -5.0f; +const float DEFAULT_CHARACTER_GRAVITY = -5.0f; +float currentAvatarGravity = DEFAULT_CHARACTER_GRAVITY; #ifdef DEBUG_STATE_CHANGE #define SET_STATE(desiredState, reason) setState(desiredState, reason) @@ -359,7 +360,7 @@ void CharacterController::updateGravity() { if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { _gravity = 0.0f; } else { - _gravity = DEFAULT_CHARACTER_GRAVITY; + _gravity = currentAvatarGravity; } if (_rigidBody) { _rigidBody->setGravity(_gravity * _currentUp); @@ -368,11 +369,11 @@ void CharacterController::updateGravity() { void CharacterController::setGravity(float gravity) { - DEFAULT_CHARACTER_GRAVITY = gravity; + currentAvatarGravity = gravity; } float CharacterController::getGravity() { - return DEFAULT_CHARACTER_GRAVITY; + return currentAvatarGravity; } #ifdef DEBUG_STATE_CHANGE @@ -393,18 +394,6 @@ void CharacterController::setState(State desiredState) { } } -void CharacterController::updateGravity() { - int16_t collisionGroup = computeCollisionGroup(); - if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _gravity = 0.0f; - } else { - _gravity = DEFAULT_AVATAR_GRAVITY; - } - if (_rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); - } -} - void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) { float x = scale.x; float z = scale.z; From 5d87036f321aaefc6c5d55ef0aa3779d14973792 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 20 Sep 2017 11:41:31 -0700 Subject: [PATCH 166/202] make changes requests and slight fixes --- .../controllerModules/overlayLaserInput.js | 40 ++++++++++++------ .../controllerModules/tabletStylusInput.js | 41 +++++++++---------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 9bf7ed23da..a5057f98cd 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -16,7 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - var touchEvent = Script.require("/~/system/libraries/touchEventUtils.js"); + var TouchEventUtils = Script.require("/~/system/libraries/touchEventUtils.js"); var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -131,7 +131,7 @@ Script.include("/~/system/libraries/controllers.js"); if (laserTarget.overlayID && laserTarget.overlayID !== this.hoverOverlay) { this.hoverOverlay = laserTarget.overlayID; - touchEvent.sendHoverEnterEventToTouchTarget(this.hand, laserTarget); + TouchEventUtils.sendHoverEnterEventToTouchTarget(this.hand, laserTarget); } }; @@ -146,11 +146,27 @@ Script.include("/~/system/libraries/controllers.js"); } }; + this.relinquishStylusTargetTouchFocus = function(laserTarget) { + var stylusModuleNames = ["LeftTabletStylusInput", "RightTabletStylusError"]; + for (var i = 0; i < stylusModuleNames.length; i++) { + var stylusModule = getEnabledModuleByName(stylusModuleNames[i]); + if (stylusModule) { + if (stylusModule.hoverOverlay === laserTarget.overlayID) { + stylusModule.relinquishTouchFocus(); + } + } + } + }; + this.stealTouchFocus = function(laserTarget) { if (laserTarget.overlayID === this.getOtherModule().hoverOverlay) { this.getOtherModule().relinquishTouchFocus(); } + // If the focus target we want to request is the same of one of the stylus + // tell the stylus to relinquish it focus on out target + this.relinquishStylusTargetTouchFocus(laserTarget); + this.requestTouchFocus(laserTarget); }; @@ -182,7 +198,7 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressEnter = function () { this.stealTouchFocus(this.laserTarget); - touchEvent.sendTouchStartEventToTouchTarget(this.hand, this.laserTarget); + TouchEventUtils.sendTouchStartEventToTouchTarget(this.hand, this.laserTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); this.touchingEnterTimer = 0; @@ -205,9 +221,9 @@ Script.include("/~/system/libraries/controllers.js"); // send press event if (this.deadspotExpired) { - touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.laserTarget); + TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.laserTarget); } else { - touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.pressEnterLaserTarget); + TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.pressEnterLaserTarget); } }; @@ -219,8 +235,8 @@ Script.include("/~/system/libraries/controllers.js"); var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || distance2D(this.laserTarget.position2D, - this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { - touchEvent.sendTouchMoveEventToTouchTarget(this.hand, this.laserTarget); + this.pressEnterLaserTarget.position2D) > this.deadspotRadius) { + TouchEventUtils.sendTouchMoveEventToTouchTarget(this.hand, this.laserTarget); this.deadspotExpired = true; } } else { @@ -237,7 +253,7 @@ Script.include("/~/system/libraries/controllers.js"); return false; } var intersection = controllerData.rayPicks[this.hand]; - var laserTarget = touchEvent.composeTouchTargetFromIntersection(intersection); + var laserTarget = TouchEventUtils.composeTouchTargetFromIntersection(intersection); if (controllerData.triggerClicks[this.hand]) { this.laserTarget = laserTarget; @@ -245,12 +261,12 @@ Script.include("/~/system/libraries/controllers.js"); } else { this.requestTouchFocus(laserTarget); - if (!touchEvent.touchTargetHasKeyboardFocus(laserTarget)) { - touchEvent.setKeyboardFocusOnTouchTarget(laserTarget); + if (!TouchEventUtils.touchTargetHasKeyboardFocus(laserTarget)) { + TouchEventUtils.setKeyboardFocusOnTouchTarget(laserTarget); } - + if (this.hasTouchFocus(laserTarget) && !this.laserPressingTarget) { - touchEvent.sendHoverOverEventToTouchTarget(this.hand, laserTarget); + TouchEventUtils.sendHoverOverEventToTouchTarget(this.hand, laserTarget); } } diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 52706fc6d7..c61a236bb6 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -16,7 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - var touchEvent = Script.require("/~/system/libraries/touchEventUtils.js"); + var TouchEventUtils = Script.require("/~/system/libraries/touchEventUtils.js"); // triggered when stylus presses a web overlay/entity var HAPTIC_STYLUS_STRENGTH = 1.0; var HAPTIC_STYLUS_DURATION = 20.0; @@ -206,12 +206,12 @@ Script.include("/~/system/libraries/controllers.js"); stylusTarget.entityID !== this.hoverEntity && stylusTarget.entityID !== this.getOtherHandController().hoverEntity) { this.hoverEntity = stylusTarget.entityID; - touchEvent.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget); + TouchEventUtils.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget); } else if (stylusTarget.overlayID && stylusTarget.overlayID !== this.hoverOverlay && stylusTarget.overlayID !== this.getOtherHandController().hoverOverlay) { this.hoverOverlay = stylusTarget.overlayID; - touchEvent.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget); + TouchEventUtils.sendHoverEnterEventToTouchTarget(this.hand, stylusTarget); } }; @@ -268,7 +268,7 @@ Script.include("/~/system/libraries/controllers.js"); for (i = 0; i < candidateEntities.length; i++) { props = candidateEntities[i]; if (props && props.type === "Web") { - stylusTarget = touchEvent.calculateTouchTargetFromEntity(this.stylusTip, candidateEntities[i]); + stylusTarget = TouchEventUtils.calculateTouchTargetFromEntity(this.stylusTip, candidateEntities[i]); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -278,7 +278,7 @@ Script.include("/~/system/libraries/controllers.js"); // add the tabletScreen, if it is valid if (HMD.tabletScreenID && HMD.tabletScreenID !== NULL_UUID && Overlays.getProperty(HMD.tabletScreenID, "visible")) { - stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, HMD.tabletScreenID); + stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, HMD.tabletScreenID); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -287,7 +287,7 @@ Script.include("/~/system/libraries/controllers.js"); // add the tablet home button. if (HMD.homeButtonID && HMD.homeButtonID !== NULL_UUID && Overlays.getProperty(HMD.homeButtonID, "visible")) { - stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, HMD.homeButtonID); + stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, HMD.homeButtonID); if (stylusTarget) { stylusTargets.push(stylusTarget); } @@ -332,12 +332,12 @@ Script.include("/~/system/libraries/controllers.js"); this.requestTouchFocus(nearestStylusTarget); - if (!touchEvent.touchTargetHasKeyboardFocus(nearestStylusTarget)) { - touchEvent.setKeyboardFocusOnTouchTarget(nearestStylusTarget); + if (!TouchEventUtils.touchTargetHasKeyboardFocus(nearestStylusTarget)) { + TouchEventUtils.setKeyboardFocusOnTouchTarget(nearestStylusTarget); } if (this.hasTouchFocus(nearestStylusTarget) && !this.stylusTouchingTarget) { - touchEvent.sendHoverOverEventToTouchTarget(this.hand, nearestStylusTarget); + TouchEventUtils.sendHoverOverEventToTouchTarget(this.hand, nearestStylusTarget); } // filter out presses when tip is moving away from tablet. @@ -368,7 +368,7 @@ Script.include("/~/system/libraries/controllers.js"); this.stylusTouchingEnter = function () { this.stealTouchFocus(this.stylusTarget); - touchEvent.sendTouchStartEventToTouchTarget(this.hand, this.stylusTarget); + TouchEventUtils.sendTouchStartEventToTouchTarget(this.hand, this.stylusTarget); Controller.triggerHapticPulse(HAPTIC_STYLUS_STRENGTH, HAPTIC_STYLUS_DURATION, this.hand); this.touchingEnterTimer = 0; @@ -392,9 +392,9 @@ Script.include("/~/system/libraries/controllers.js"); // send press event if (this.deadspotExpired) { - touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.stylusTarget); + TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.stylusTarget); } else { - touchEvent.sendTouchEndEventToTouchTarget(this.hand, this.touchingEnterStylusTarget); + TouchEventUtils.sendTouchEndEventToTouchTarget(this.hand, this.touchingEnterStylusTarget); } }; @@ -403,9 +403,9 @@ Script.include("/~/system/libraries/controllers.js"); this.touchingEnterTimer += dt; if (this.stylusTarget.entityID) { - this.stylusTarget = touchEvent.calculateTouchTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); + this.stylusTarget = TouchEventUtils.calculateTouchTargetFromEntity(this.stylusTip, this.stylusTarget.entityProps); } else if (this.stylusTarget.overlayID) { - this.stylusTarget = touchEvent.calculateTouchTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); + this.stylusTarget = TouchEventUtils.calculateTouchTargetFromOverlay(this.stylusTip, this.stylusTarget.overlayID); } var TABLET_MIN_TOUCH_DISTANCE = -0.1; @@ -418,7 +418,7 @@ Script.include("/~/system/libraries/controllers.js"); if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || distance2D(this.stylusTarget.position2D, this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { - touchEvent.sendTouchMoveEventToTouchTarget(this.hand, this.stylusTarget); + TouchEventUtils.sendTouchMoveEventToTouchTarget(this.hand, this.stylusTarget); this.deadspotExpired = true; } } else { @@ -430,12 +430,11 @@ Script.include("/~/system/libraries/controllers.js"); }; this.overlayLaserActive = function(controllerData) { - var overlayLaserModule = - getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightOverlayLaserInput" : "LeftOverlayLaserInput"); - if (overlayLaserModule) { - return !overlayLaserModule.shouldExit(controllerData); - } - return false; + var rightOverlayLaserModule = getEnabledModuleByName("RightOverlayLaserInput"); + var leftOverlayLaserModule = getEnabledModuleByName("LeftOverlayLaserInput"); + var rightModuleRunning = rightOverlayLaserModule ? !rightOverlayLaserModule.shouldExit(controllerData) : false; + var leftModuleRunning = leftOverlayLaserModule ? !leftOverlayLaserModule.shouldExit(controllerData) : false; + return leftModuleRunning || rightModuleRunning; }; this.isReady = function (controllerData) { From 2ce6dcf89dbb8a2368c1a9fcd486b15384e3a55d Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 20 Sep 2017 11:45:42 -0700 Subject: [PATCH 167/202] fixing spelling error --- .../system/controllers/controllerModules/overlayLaserInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index a5057f98cd..e21776ab0a 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -164,7 +164,7 @@ Script.include("/~/system/libraries/controllers.js"); } // If the focus target we want to request is the same of one of the stylus - // tell the stylus to relinquish it focus on out target + // tell the stylus to relinquish it focus on our target this.relinquishStylusTargetTouchFocus(laserTarget); this.requestTouchFocus(laserTarget); From 29a9331f145e4b72fe9f6ba68e14fade548784be Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 20 Sep 2017 12:55:50 -0700 Subject: [PATCH 168/202] change include for QUrlQuery to be consistent --- assignment-client/src/assets/AssetServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index fea9d8329d..b4287a0161 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include From c48aba423ac7c168377d3c73fac0e5bca8155d4b Mon Sep 17 00:00:00 2001 From: vladest Date: Wed, 20 Sep 2017 22:48:39 +0200 Subject: [PATCH 169/202] Rename check box --- .../qml/controls-uit/{CheckBox2.qml => CheckBoxQQC2.qml} | 0 interface/resources/qml/hifi/audio/CheckBox.qml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename interface/resources/qml/controls-uit/{CheckBox2.qml => CheckBoxQQC2.qml} (100%) diff --git a/interface/resources/qml/controls-uit/CheckBox2.qml b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml similarity index 100% rename from interface/resources/qml/controls-uit/CheckBox2.qml rename to interface/resources/qml/controls-uit/CheckBoxQQC2.qml diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml index 77c001816f..3a954d4004 100644 --- a/interface/resources/qml/hifi/audio/CheckBox.qml +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -13,6 +13,6 @@ import QtQuick 2.7 import "../../controls-uit" as HifiControls -HifiControls.CheckBox2 { +HifiControls.CheckBoxQQC2 { color: "white" } From c20c5ee2b5e812c9c4c8322945abec8ec99b6788 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 20 Sep 2017 14:10:10 -0700 Subject: [PATCH 170/202] fixed tablet grabbing issue --- .../controllerModules/overlayLaserInput.js | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index e21776ab0a..d67672ca7c 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -276,14 +276,30 @@ Script.include("/~/system/libraries/controllers.js"); return true; }; + this.grabModuleWantsNearbyOverlay = function(controllerData) { + if (controllerData.triggerValues[this.hand] > TRIGGER_ON_VALUE) { + var nearGrabName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"; + var nearGrabModule = getEnabledModuleByName(nearGrabName); + if (nearGrabModule) { + var candidateOverlays = controllerData.nearbyOverlayIDs[this.hand]; + var grabbableOverlays = candidateOverlays.filter(function(overlayID) { + return Overlays.getProperty(overlayID, "grabbable"); + }); + var target = nearGrabModule.getTargetID(grabbableOverlays, controllerData); + if (target) { + return true; + } + } + } + return false; + }; + this.shouldExit = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var nearGrabName = this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"; - var nearGrabModule = getEnabledModuleByName(nearGrabName); - var status = nearGrabModule ? nearGrabModule.isReady(controllerData) : makeRunningValues(false, [], []); var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY); var triggerOff = (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE); - return offOverlay || status.active || triggerOff; + var grabbingOverlay = this.grabModuleWantsNearbyOverlay(controllerData); + return offOverlay || grabbingOverlay || triggerOff; }; this.exitModule = function() { From 92f3dfe2ab02159c13203f148e414672ddc3bb9b Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 20 Sep 2017 14:23:43 -0700 Subject: [PATCH 171/202] move the stylus back a bit --- .../system/controllers/controllerModules/tabletStylusInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 230038adb5..4fe1f7a23f 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -23,7 +23,7 @@ Script.include("/~/system/libraries/controllers.js"); var WEB_DISPLAY_STYLUS_DISTANCE = 0.5; var WEB_STYLUS_LENGTH = 0.2; - var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative number) to slide stylus in hand + var WEB_TOUCH_Y_OFFSET = 0.105; // how far forward (or back with a negative number) to slide stylus in hand function stylusTargetHasKeyboardFocus(stylusTarget) { From f21c99257e24c8b6dcfc22890ab2800ef07b6379 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 20 Sep 2017 14:35:29 -0700 Subject: [PATCH 172/202] don't let scripts control visibility of laser pointers --- interface/src/Application.cpp | 12 ++++++------ interface/src/raypick/LaserPointer.cpp | 4 +++- .../src/raypick/LaserPointerScriptingInterface.cpp | 3 +++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5700bb6a72..a5353a58ad 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5159,12 +5159,6 @@ void Application::update(float deltaTime) { } } - { - PROFILE_RANGE_EX(app, "Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); - PerformanceTimer perfTimer("overlays"); - _overlays.update(deltaTime); - } - { PROFILE_RANGE(app, "RayPickManager"); _rayPickManager.update(); @@ -5175,6 +5169,12 @@ void Application::update(float deltaTime) { _laserPointerManager.update(); } + { + PROFILE_RANGE_EX(app, "Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); + PerformanceTimer perfTimer("overlays"); + _overlays.update(deltaTime); + } + // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the // loadViewFrumstum() method will get the correct details from the camera diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 9d64bcc476..b696afa8e5 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -86,7 +86,9 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) { if (!id.isNull() && props.isValid()) { - qApp->getOverlays().editOverlay(id, props); + QVariantMap propMap = props.toMap(); + propMap.remove("visible"); + qApp->getOverlays().editOverlay(id, propMap); } } diff --git a/interface/src/raypick/LaserPointerScriptingInterface.cpp b/interface/src/raypick/LaserPointerScriptingInterface.cpp index a976a00893..d5e435f490 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.cpp +++ b/interface/src/raypick/LaserPointerScriptingInterface.cpp @@ -95,6 +95,7 @@ const RenderState LaserPointerScriptingInterface::buildRenderState(const QVarian if (propMap["start"].isValid()) { QVariantMap startMap = propMap["start"].toMap(); if (startMap["type"].isValid()) { + startMap.remove("visible"); startID = qApp->getOverlays().addOverlay(startMap["type"].toString(), startMap); } } @@ -104,6 +105,7 @@ const RenderState LaserPointerScriptingInterface::buildRenderState(const QVarian QVariantMap pathMap = propMap["path"].toMap(); // right now paths must be line3ds if (pathMap["type"].isValid() && pathMap["type"].toString() == "line3d") { + pathMap.remove("visible"); pathID = qApp->getOverlays().addOverlay(pathMap["type"].toString(), pathMap); } } @@ -112,6 +114,7 @@ const RenderState LaserPointerScriptingInterface::buildRenderState(const QVarian if (propMap["end"].isValid()) { QVariantMap endMap = propMap["end"].toMap(); if (endMap["type"].isValid()) { + endMap.remove("visible"); endID = qApp->getOverlays().addOverlay(endMap["type"].toString(), endMap); } } From 60a566768a1cf96ca485309202a1218d0de0e081 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 20 Sep 2017 14:39:13 -0700 Subject: [PATCH 173/202] removed debug spam from makeUserConnection, to reactivate it set `Settings.setValue('MAKE_USER_CONNECTION_DEBUG', true);` and reload the script --- scripts/system/makeUserConnection.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index d95ad919b6..51390d4a78 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -16,6 +16,7 @@ var request = Script.require('request').request; + var WANT_DEBUG = Settings.getValue('MAKE_USER_CONNECTION_DEBUG', false); var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m var GRIP_MIN = 0.75; // goes from 0-1, so 75% pressed is pressed @@ -120,6 +121,9 @@ var successfulHandshakeSound; function debug() { + if (!WANT_DEBUG) { + return; + } var stateString = "<" + STATE_STRINGS[state] + ">"; var connecting = "[" + connectingId + "/" + connectingHandJointIndex + "]"; var current = "[" + currentHand + "/" + currentHandJointIndex + "]" @@ -372,7 +376,7 @@ var myHeadIndex = MyAvatar.getJointIndex("Head"); var otherHeadIndex = avatar.getJointIndex("Head"); var diff = (avatar.getJointPosition(otherHeadIndex).y - MyAvatar.getJointPosition(myHeadIndex).y) / 2; - print("head height difference: " + diff); + debug("head height difference: " + diff); updateAnimationData(diff); } } From 7ea1968b337f0e32c9d9e60cdc4c003fa1c6d562 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 20 Sep 2017 14:59:15 -0700 Subject: [PATCH 174/202] Tip, `Code Writer` which comes standard with Windows 10, does enter tabs by default when using the tab key. >=) --- scripts/system/makeUserConnection.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 51390d4a78..bfad959ffc 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -16,7 +16,7 @@ var request = Script.require('request').request; - var WANT_DEBUG = Settings.getValue('MAKE_USER_CONNECTION_DEBUG', false); + var WANT_DEBUG = Settings.getValue('MAKE_USER_CONNECTION_DEBUG', false); var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m var GRIP_MIN = 0.75; // goes from 0-1, so 75% pressed is pressed @@ -121,9 +121,9 @@ var successfulHandshakeSound; function debug() { - if (!WANT_DEBUG) { - return; - } + if (!WANT_DEBUG) { + return; + } var stateString = "<" + STATE_STRINGS[state] + ">"; var connecting = "[" + connectingId + "/" + connectingHandJointIndex + "]"; var current = "[" + currentHand + "/" + currentHandJointIndex + "]" From 949c343604f6c2aca87a1db0b7c4110bb1d4f42f Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 20 Sep 2017 15:34:01 -0700 Subject: [PATCH 175/202] fixing nearGrabParentModule switching grabbed overlays --- .../controllerModules/nearParentGrabOverlay.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index 4ad17e6de9..8d093afe2c 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -26,6 +26,7 @@ Script.include("/~/system/libraries/utils.js"); this.previousParentID = {}; this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; + this.robbed = false; this.parameters = makeDispatcherModuleParameters( 90, @@ -111,9 +112,9 @@ Script.include("/~/system/libraries/utils.js"); } else if (this.otherHandIsParent(grabbedProperties)) { // the other hand is parent. Steal the object and information var otherModule = this.getOtherModule(); - this.previousParentID[this.grabbedThingID] = otherModule.previousParentID[this.garbbedThingID]; + this.previousParentID[this.grabbedThingID] = otherModule.previousParentID[this.grabbedThingID]; this.previousParentJointIndex[this.grabbedThingID] = otherModule.previousParentJointIndex[this.grabbedThingID]; - + otherModule.robbed = true; } else { this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID; this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex; @@ -134,12 +135,12 @@ Script.include("/~/system/libraries/utils.js"); this.endNearParentingGrabOverlay = function () { var previousParentID = this.previousParentID[this.grabbedThingID]; - if (previousParentID === NULL_UUID || previousParentID === null || previousParentID === undefined) { + if ((previousParentID === NULL_UUID || previousParentID === null) && !this.robbed) { Overlays.editOverlay(this.grabbedThingID, { parentID: NULL_UUID, parentJointIndex: -1 }); - } else { + } else if (!this.robbed){ // before we grabbed it, overlay was a child of something; put it back. Overlays.editOverlay(this.grabbedThingID, { parentID: this.previousParentID[this.grabbedThingID], @@ -170,7 +171,9 @@ Script.include("/~/system/libraries/utils.js"); this.isReady = function (controllerData) { - if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { + if ((controllerData.triggerClicks[this.hand] === 0 && + controllerData.secondaryValues[this.hand] === 0)) { + this.robbed = false; return makeRunningValues(false, [], []); } @@ -182,7 +185,7 @@ Script.include("/~/system/libraries/utils.js"); }); var targetID = this.getTargetID(grabbableOverlays, controllerData); - if (targetID) { + if (targetID && !this.robbed) { this.grabbedThingID = targetID; this.startNearParentingGrabOverlay(controllerData); return makeRunningValues(true, [this.grabbedThingID], []); @@ -194,6 +197,7 @@ Script.include("/~/system/libraries/utils.js"); this.run = function (controllerData) { if (controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) { this.endNearParentingGrabOverlay(); + this.robbed = false; return makeRunningValues(false, [], []); } else { // check if someone stole the target from us From 322b7fc0609dd193e9c17807c31914b24a646b04 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Wed, 20 Sep 2017 23:37:33 +0100 Subject: [PATCH 176/202] Cleanup from Merge --- interface/src/avatar/MyAvatar.h | 3 +++ libraries/physics/src/CharacterController.cpp | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b1351dc498..9620d61a49 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -150,6 +150,9 @@ class MyAvatar : public Avatar { Q_PROPERTY(float hmdRollControlDeadZone READ getHMDRollControlDeadZone WRITE setHMDRollControlDeadZone) Q_PROPERTY(float hmdRollControlRate READ getHMDRollControlRate WRITE setHMDRollControlRate) + Q_PROPERTY(float userHeight READ getUserHeight WRITE setUserHeight) + Q_PROPERTY(float userEyeHeight READ getUserEyeHeight) + const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 0904e39d25..5e446a52a9 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -393,9 +393,6 @@ void CharacterController::setState(State desiredState) { } } -<<<<<<< HEAD -<<<<<<< HEAD -======= void CharacterController::updateGravity() { int16_t collisionGroup = computeCollisionGroup(); if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { @@ -408,9 +405,6 @@ void CharacterController::updateGravity() { } } ->>>>>>> 5e5b77fbaaeff61a26144a240329eca70765c1a9 -======= ->>>>>>> 4d904bd5ce7d7b1a805c2f8fbdda630afc27576f void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) { float x = scale.x; float z = scale.z; From f6d3960160bde08c156230fd6052649115177981 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 20 Sep 2017 15:58:52 -0700 Subject: [PATCH 177/202] fix call to maybeBake when looking at skybox --- assignment-client/src/assets/AssetServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index b4287a0161..8f361515c0 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -530,7 +530,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode bool isSkybox = query.hasQueryItem("skybox"); if (isSkybox) { writeMetaFile(originalAssetHash); - maybeBake(originalAssetHash, assetPath); + maybeBake(assetPath, originalAssetHash); } } } else { From 45f79b43412344f2eff7a9c66a2072e5f594599e Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Thu, 21 Sep 2017 00:00:24 +0100 Subject: [PATCH 178/202] Small fix --- libraries/physics/src/CharacterController.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 5e446a52a9..8d32535632 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -393,18 +393,6 @@ void CharacterController::setState(State desiredState) { } } -void CharacterController::updateGravity() { - int16_t collisionGroup = computeCollisionGroup(); - if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _gravity = 0.0f; - } else { - _gravity = DEFAULT_AVATAR_GRAVITY; - } - if (_rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); - } -} - void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale) { float x = scale.x; float z = scale.z; From 2c55419c603856336b7ef5efeb5d27b3049977c2 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Wed, 20 Sep 2017 16:49:45 -0700 Subject: [PATCH 179/202] change tablet forward offset --- scripts/system/libraries/WebTablet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 21f0fac01a..c5f8168c30 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -49,7 +49,7 @@ function calcSpawnInfo(hand, landscape) { var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation; var forward = Quat.getForward(headRot); - var FORWARD_OFFSET = 0.6 * MyAvatar.sensorToWorldScale; + var FORWARD_OFFSET = 0.5 * MyAvatar.sensorToWorldScale; finalPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward)); var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y)); return { From 27350bd7deb3334420aa8ff17ce87544c18aeeef Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 20 Sep 2017 15:39:42 -0700 Subject: [PATCH 180/202] Don't add items to the scene graph until they have a valid parent chain --- .../src/EntityTreeRenderer.cpp | 118 ++++++++++++------ .../src/EntityTreeRenderer.h | 5 +- .../src/RenderableEntityItem.cpp | 4 +- .../src/RenderableEntityItem.h | 2 +- libraries/render/src/render/Forward.h | 1 + libraries/shared/src/SpatiallyNestable.cpp | 16 +++ libraries/shared/src/SpatiallyNestable.h | 4 + 7 files changed, 107 insertions(+), 43 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e8ad163964..a79e29f003 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -159,6 +159,78 @@ void EntityTreeRenderer::shutdown() { clear(); // always clear() on shutdown } +void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) { + // Clear any expired entities + // FIXME should be able to use std::remove_if, but it fails due to some + // weird compilation error related to EntityItemID assignment operators + for (auto itr = _entitiesToAdd.begin(); _entitiesToAdd.end() != itr; ) { + if (itr->second.expired()) { + _entitiesToAdd.erase(itr++); + } else { + ++itr; + } + } + + if (!_entitiesToAdd.empty()) { + std::unordered_set processedIds; + for (const auto& entry : _entitiesToAdd) { + auto entity = entry.second.lock(); + if (!entity) { + continue; + } + + // Path to the parent transforms is not valid, + // don't add to the scene graph yet + if (!entity->isParentPathComplete()) { + continue; + } + + auto entityID = entity->getEntityItemID(); + processedIds.insert(entityID); + auto renderable = EntityRenderer::addToScene(*this, entity, scene, transaction); + if (renderable) { + _entitiesInScene.insert({ entityID, renderable }); + } + } + + + if (!processedIds.empty()) { + for (const auto& processedId : processedIds) { + _entitiesToAdd.erase(processedId); + } + } + } +} + +void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction) { + std::unordered_set changedEntities; + _changedEntitiesGuard.withWriteLock([&] { +#if 0 + // FIXME Weird build failure in latest VC update that fails to compile when using std::swap + changedEntities.swap(_changedEntities); +#else + changedEntities.insert(_changedEntities.begin(), _changedEntities.end()); + _changedEntities.clear(); +#endif + }); + + for (const auto& entityId : changedEntities) { + auto renderable = renderableForEntityId(entityId); + if (!renderable) { + continue; + } + _renderablesToUpdate.insert({ entityId, renderable }); + } + + if (!_renderablesToUpdate.empty()) { + for (const auto& entry : _renderablesToUpdate) { + const auto& renderable = entry.second; + renderable->updateInScene(scene, transaction); + } + _renderablesToUpdate.clear(); + } +} + void EntityTreeRenderer::update(bool simulate) { PerformanceTimer perfTimer("ETRupdate"); if (_tree && !_shuttingDown) { @@ -178,30 +250,11 @@ void EntityTreeRenderer::update(bool simulate) { } } - std::unordered_set changedEntities; - // FIXME Weird build failure in latest VC update that fails to compile when using std::swap - _changedEntitiesGuard.withWriteLock([&] { - changedEntities.insert(_changedEntities.begin(), _changedEntities.end()); - _changedEntities.clear(); - }); - - for (const auto& entityId : changedEntities) { - auto renderable = renderableForEntityId(entityId); - if (!renderable) { - continue; - } - - _renderablesToUpdate.insert({ entityId, renderable }); - } - auto scene = _viewState->getMain3DScene(); - if (scene && !_renderablesToUpdate.empty()) { + if (scene) { render::Transaction transaction; - for (const auto& entry : _renderablesToUpdate) { - const auto& renderable = entry.second; - renderable->updateInScene(scene, transaction); - } - _renderablesToUpdate.clear(); + addPendingEntities(scene, transaction); + updateChangedEntities(scene, transaction); scene->enqueueTransaction(transaction); } } @@ -689,8 +742,12 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { } void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { + // If it's in the pending queue, remove it + _entitiesToAdd.erase(entityID); + auto itr = _entitiesInScene.find(entityID); if (_entitiesInScene.end() == itr) { + // Not in the scene, and no longer potentially in the pending queue, we're done return; } @@ -725,25 +782,10 @@ void EntityTreeRenderer::addingEntity(const EntityItemID& entityID) { checkAndCallPreload(entityID); auto entity = std::static_pointer_cast(_tree)->findEntityByID(entityID); if (entity) { - addEntityToScene(entity); + _entitiesToAdd.insert({ entity->getEntityItemID(), entity }); } } -void EntityTreeRenderer::addEntityToScene(const EntityItemPointer& entity) { - // here's where we add the entity payload to the scene - auto scene = _viewState->getMain3DScene(); - if (!scene) { - qCWarning(entitiesrenderer) << "EntityTreeRenderer::addEntityToScene(), Unexpected null scene, possibly during application shutdown"; - return; - } - - auto renderable = EntityRenderer::addToScene(*this, entity, scene); - if (renderable) { - _entitiesInScene[entity->getEntityItemID()] = renderable; - } -} - - void EntityTreeRenderer::entityScriptChanging(const EntityItemID& entityID, bool reload) { checkAndCallPreload(entityID, reload, true); } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 05ddaa6b0a..1b1d46d50c 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -36,6 +36,7 @@ namespace render { namespace entities { class EntityRenderer; using EntityRendererPointer = std::shared_ptr; using EntityRendererWeakPointer = std::weak_ptr; + } } // Allow the use of std::unordered_map with QUuid keys @@ -157,12 +158,13 @@ protected: } private: + void addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction); + void updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction); EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); } render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); } void resetEntitiesScriptEngine(); - void addEntityToScene(const EntityItemPointer& entity); bool findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar = nullptr); bool applyLayeredZones(); @@ -260,6 +262,7 @@ private: std::unordered_map _renderablesToUpdate; std::unordered_map _entitiesInScene; + std::unordered_map _entitiesToAdd; // For Scene.shouldRenderEntities QList _entityIDsLastInScene; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index ceaf073e08..3f1e89b86c 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -187,7 +187,7 @@ void EntityRenderer::render(RenderArgs* args) { // Methods called by the EntityTreeRenderer // -EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, const EntityItemPointer& entity, const ScenePointer& scene) { +EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, const EntityItemPointer& entity, const ScenePointer& scene, Transaction& transaction) { EntityRenderer::Pointer result; if (!entity) { return result; @@ -245,9 +245,7 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, } if (result) { - Transaction transaction; result->addToScene(scene, transaction); - scene->enqueueTransaction(transaction); } return result; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 629841d76e..6b47ff8b1d 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -30,7 +30,7 @@ class EntityRenderer : public QObject, public std::enable_shared_from_this; class ShapePipeline; + class Transaction; } using RenderArgs = render::Args; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index db78144100..8ea38f5f13 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1182,3 +1182,19 @@ void SpatiallyNestable::dump(const QString& prefix) const { parent->dump(prefix + " "); } } + +bool SpatiallyNestable::isParentPathComplete() const { + static const QUuid IDENTITY; + QUuid parentID = getParentID(); + if (parentID.isNull() || parentID == IDENTITY) { + return true; + } + + bool success = false; + SpatiallyNestablePointer parent = getParentPointer(success); + if (!success || !parent) { + return false; + } + + return parent->isParentPathComplete(); +} diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 90d2e33016..b6be4dc056 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -66,6 +66,10 @@ public: static QString nestableTypeToString(NestableType nestableType); + + virtual bool isParentPathComplete() const; + + // world frame virtual const Transform getTransform(bool& success, int depth = 0) const; virtual const Transform getTransform() const; From 4ce5e4ac46c5b2b4e5cdeb13a55f4ba773ac52cf Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 21 Sep 2017 12:19:32 +1200 Subject: [PATCH 181/202] Fix recording just made not playing back if reload it --- .../src/RecordingScriptingInterface.cpp | 22 ++++++++++++++----- .../src/RecordingScriptingInterface.h | 3 +++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index b51e9cd529..164f2322a4 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -54,9 +54,24 @@ float RecordingScriptingInterface::playerLength() const { return _player->length(); } +void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, const QString& url, QScriptValue callback) { + _player->queueClip(clipLoader->getClip()); + + if (callback.isFunction()) { + QScriptValueList args{ true, url }; + callback.call(_scriptEngine->globalObject(), args); + } +} + void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue callback) { auto clipLoader = DependencyManager::get()->getClipLoader(url); + if (clipLoader->isLoaded()) { + qCDebug(scriptengine) << "Recording already loaded from" << url; + playClip(clipLoader, url, callback); + return; + } + // hold a strong pointer to the loading clip so that it has a chance to load _clipLoaders.insert(clipLoader); @@ -69,12 +84,7 @@ void RecordingScriptingInterface::loadRecording(const QString& url, QScriptValue if (auto clipLoader = weakClipLoader.toStrongRef()) { qCDebug(scriptengine) << "Loaded recording from" << url; - _player->queueClip(clipLoader->getClip()); - - if (callback.isFunction()) { - QScriptValueList args { true, url }; - callback.call(_scriptEngine->globalObject(), args); - } + playClip(clipLoader, url, callback); // drop our strong pointer to this clip so it is cleaned up _clipLoaders.remove(clipLoader); diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h index bc0b019251..22e4d30830 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.h +++ b/libraries/script-engine/src/RecordingScriptingInterface.h @@ -88,6 +88,9 @@ protected: QSharedPointer _scriptEngine; QSet _clipLoaders; + +private: + void playClip(recording::NetworkClipLoaderPointer clipLoader, const QString& url, QScriptValue callback); }; #endif // hifi_RecordingScriptingInterface_h From dd050e103f5e35c9bcc29eaa1646fd7577206ab1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 20 Sep 2017 18:23:28 -0700 Subject: [PATCH 182/202] Don't redirect to the same hash. --- assignment-client/src/assets/AssetServer.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 8f361515c0..6e3db69c63 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -494,6 +494,7 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode QString redirectedAssetHash; QString bakedAssetPath; quint8 wasRedirected = false; + bool bakingDisabled = false; if (!bakedRootFile.isEmpty()) { // we ran into an asset for which we could have a baked version, let's check if it's ready @@ -501,10 +502,15 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode auto bakedIt = _fileMappings.find(bakedAssetPath); if (bakedIt != _fileMappings.end()) { - qDebug() << "Did find baked version for: " << originalAssetHash << assetPath; - // we found a baked version of the requested asset to serve, redirect to that - redirectedAssetHash = bakedIt->second; - wasRedirected = true; + if (bakedIt->second != originalAssetHash) { + qDebug() << "Did find baked version for: " << originalAssetHash << assetPath; + // we found a baked version of the requested asset to serve, redirect to that + redirectedAssetHash = bakedIt->second; + wasRedirected = true; + } else { + qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath << " (disabled)"; + bakingDisabled = true; + } } else { qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath; } @@ -530,7 +536,9 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode bool isSkybox = query.hasQueryItem("skybox"); if (isSkybox) { writeMetaFile(originalAssetHash); - maybeBake(assetPath, originalAssetHash); + if (!bakingDisabled) { + maybeBake(assetPath, originalAssetHash); + } } } } else { From 81be62b6f70de4598aa10f845a8ddaaa1ee1f6bc Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 21 Sep 2017 09:34:15 -0700 Subject: [PATCH 183/202] Check if ContextOverlayInterface is enabled before highlighting --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 34aae37175..fed6267c63 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -237,13 +237,13 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& } void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (contextOverlayFilterPassed(entityID)) { + if (contextOverlayFilterPassed(entityID) && _enabled) { enableEntityHighlight(entityID); } } void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (_currentEntityWithContextOverlay != entityID) { + if (_currentEntityWithContextOverlay != entityID && _enabled) { disableEntityHighlight(entityID); } } From 57b5a233739d8dd2af24a84572089ab94000c2fa Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 21 Sep 2017 11:18:21 -0700 Subject: [PATCH 184/202] Enable Inspection Mode for all; change Commerce setting name --- .../src/ui/overlays/ContextOverlayInterface.cpp | 13 +------------ interface/src/ui/overlays/ContextOverlayInterface.h | 2 -- libraries/ui/src/ui/types/RequestFilters.cpp | 2 +- scripts/system/commerce/wallet.js | 2 +- scripts/system/html/js/marketplacesInject.js | 4 ++-- scripts/system/marketplaces/marketplaces.js | 4 ++-- 6 files changed, 7 insertions(+), 20 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index fed6267c63..509e201d47 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -38,11 +38,6 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; - // initially, set _enabled to match the switch. Later we enable/disable via the getter/setters - // if we are in edit or pal (for instance). Note this is temporary, as we expect to enable this all - // the time after getting edge highlighting, etc... - _enabled = _settingSwitch.get(); - auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); connect(entityTreeRenderer, SIGNAL(hoverEnterEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(contextOverlays_hoverEnterEntity(const EntityItemID&, const PointerEvent&))); @@ -81,13 +76,7 @@ static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; void ContextOverlayInterface::setEnabled(bool enabled) { - // only enable/disable if the setting in 'on'. If it is 'off', - // make sure _enabled is always false. - if (_settingSwitch.get()) { - _enabled = enabled; - } else { - _enabled = false; - } + _enabled = enabled; } bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index c14262029e..fddd1fcdb5 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -76,8 +76,6 @@ private: bool _isInMarketplaceInspectionMode { false }; - Setting::Handle _settingSwitch { "inspectionMode", false }; - void openMarketplace(); void enableEntityHighlight(const EntityItemID& entityItemID); void disableEntityHighlight(const EntityItemID& entityItemID); diff --git a/libraries/ui/src/ui/types/RequestFilters.cpp b/libraries/ui/src/ui/types/RequestFilters.cpp index 6ef3effa4c..233a9458fe 100644 --- a/libraries/ui/src/ui/types/RequestFilters.cpp +++ b/libraries/ui/src/ui/types/RequestFilters.cpp @@ -61,7 +61,7 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) // During the period in which we have HFC commerce in the system, but not applied everywhere: const QString tokenStringCommerce{ "Chrome/48.0 (HighFidelityInterface WithHFC)" }; - static Setting::Handle _settingSwitch{ "inspectionMode", false }; + static Setting::Handle _settingSwitch{ "commerce", false }; bool isMoney = _settingSwitch.get(); const QString tokenString = !isAuthable ? tokenStringMobile : (isMoney ? tokenStringCommerce : tokenStringMetaverse); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 5a668a3d6e..107160154a 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -131,7 +131,7 @@ var button; var buttonName = "WALLET"; var tablet = null; - var walletEnabled = Settings.getValue("inspectionMode", false); + var walletEnabled = Settings.getValue("commerce", false); function startup() { if (walletEnabled) { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 84c26d482b..138e3a3956 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -450,7 +450,7 @@ var parsedJsonMessage = JSON.parse(message); if (parsedJsonMessage.type === "marketplaces") { - if (parsedJsonMessage.action === "inspectionModeSetting") { + if (parsedJsonMessage.action === "commerceSetting") { confirmAllPurchases = !!parsedJsonMessage.data; injectCode(); } @@ -458,7 +458,7 @@ } }); - // Request inspection mode setting + // Request commerce setting // Code is injected into the webpage after the setting comes back. EventBridge.emitWebEvent(JSON.stringify({ type: "REQUEST_SETTING" diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 2eaefe7565..7ae0aa3390 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -138,8 +138,8 @@ } else if (parsedJsonMessage.type === "REQUEST_SETTING") { tablet.emitScriptEvent(JSON.stringify({ type: "marketplaces", - action: "inspectionModeSetting", - data: Settings.getValue("inspectionMode", false) + action: "commerceSetting", + data: Settings.getValue("commerce", false) })); } else if (parsedJsonMessage.type === "PURCHASES") { tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); From a2d8152a3a8031a810fd445038d7b9d8fe5ae3f5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 22 Sep 2017 07:13:32 +1200 Subject: [PATCH 185/202] Coding standard --- libraries/script-engine/src/RecordingScriptingInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index 164f2322a4..55895e31a4 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -58,7 +58,7 @@ void RecordingScriptingInterface::playClip(NetworkClipLoaderPointer clipLoader, _player->queueClip(clipLoader->getClip()); if (callback.isFunction()) { - QScriptValueList args{ true, url }; + QScriptValueList args { true, url }; callback.call(_scriptEngine->globalObject(), args); } } From abbe2631424554be66d116d6e53283470ff7abbb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 21 Sep 2017 13:40:39 -0700 Subject: [PATCH 186/202] Fix FBXBaker not properly handling textures with the same name --- assignment-client/src/assets/AssetServer.cpp | 16 +++--- assignment-client/src/assets/AssetServer.h | 1 - libraries/baking/src/FBXBaker.cpp | 55 +++++++++++--------- libraries/baking/src/FBXBaker.h | 3 +- libraries/baking/src/TextureBaker.cpp | 14 +++-- libraries/baking/src/TextureBaker.h | 3 +- 6 files changed, 54 insertions(+), 38 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 6e3db69c63..1f86b65029 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1165,10 +1165,11 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QVector bakedFilePaths) { bool errorCompletingBake { false }; + QString errorReason; qDebug() << "Completing bake for " << originalAssetHash; - for (auto& filePath: bakedFilePaths) { + for (auto& filePath : bakedFilePaths) { // figure out the hash for the contents of this file QFile file(filePath); @@ -1184,6 +1185,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina } else { // stop handling this bake, couldn't hash the contents of the file errorCompletingBake = true; + errorReason = "Failed to finalize bake"; break; } @@ -1194,6 +1196,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) { // stop handling this bake, couldn't copy the bake file into our files directory errorCompletingBake = true; + errorReason = "Failed to copy baked assets to asset server"; break; } } @@ -1220,12 +1223,14 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina qDebug() << "Failed to set mapping"; // stop handling this bake, couldn't add a mapping for this bake file errorCompletingBake = true; + errorReason = "Failed to finalize bake"; break; } } else { qDebug() << "Failed to open baked file: " << filePath; // stop handling this bake, we couldn't open one of the files for reading errorCompletingBake = true; + errorReason = "Failed to finalize bake"; break; } } @@ -1235,6 +1240,10 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina writeMetaFile(originalAssetHash); } else { qWarning() << "Could not complete bake for" << originalAssetHash; + AssetMeta meta; + meta.failedLastBake = true; + meta.lastBakeErrors = errorReason; + writeMetaFile(originalAssetHash, meta); } _pendingBakes.remove(originalAssetHash); @@ -1246,7 +1255,6 @@ void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath } static const QString BAKE_VERSION_KEY = "bake_version"; -static const QString APP_VERSION_KEY = "app_version"; static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake"; static const QString LAST_BAKE_ERRORS_KEY = "last_bake_errors"; @@ -1273,18 +1281,15 @@ std::pair AssetServer::readMetaFile(AssetHash hash) { auto root = doc.object(); auto bakeVersion = root[BAKE_VERSION_KEY].toInt(-1); - auto appVersion = root[APP_VERSION_KEY].toInt(-1); auto failedLastBake = root[FAILED_LAST_BAKE_KEY]; auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY]; if (bakeVersion != -1 - && appVersion != -1 && failedLastBake.isBool() && lastBakeErrors.isString()) { AssetMeta meta; meta.bakeVersion = bakeVersion; - meta.applicationVersion = appVersion; meta.failedLastBake = failedLastBake.toBool(); meta.lastBakeErrors = lastBakeErrors.toString(); @@ -1303,7 +1308,6 @@ bool AssetServer::writeMetaFile(AssetHash originalAssetHash, const AssetMeta& me QJsonObject metaFileObject; metaFileObject[BAKE_VERSION_KEY] = meta.bakeVersion; - metaFileObject[APP_VERSION_KEY] = meta.applicationVersion; metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake; metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors; diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 10ea067ee5..1589dde3e3 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -34,7 +34,6 @@ struct AssetMeta { } int bakeVersion { 0 }; - int applicationVersion { 0 }; bool failedLastBake { false }; QString lastBakeErrors; }; diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index b5b6570599..3ef291af22 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -583,29 +583,17 @@ void FBXBaker::rewriteAndBakeSceneTextures() { QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() }; QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") }; + if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { + // re-baking an FBX that already references baked textures is a fail + // so we add an error and return from here + handleError("Cannot re-bake a file that references compressed textures"); + + return; + } + + // make sure this texture points to something and isn't one we've already re-mapped if (!textureFileInfo.filePath().isEmpty()) { - - if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) { - // re-baking an FBX that already references baked textures is a fail - // so we add an error and return from here - handleError("Cannot re-bake a file that references compressed textures"); - - return; - } - - // construct the new baked texture file name and file path - // ensuring that the baked texture will have a unique name - // even if there was another texture with the same name at a different path - auto bakedTextureFileName = createBakedTextureFileName(textureFileInfo); - QString bakedTextureFilePath { - _bakedOutputDir + "/" + bakedTextureFileName - }; - _outputFiles.push_back(bakedTextureFilePath); - - qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName - << "to" << bakedTextureFileName; - // check if this was an embedded texture we have already have in-memory content for auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit()); @@ -613,10 +601,29 @@ void FBXBaker::rewriteAndBakeSceneTextures() { auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName, !textureContent.isNull()); + QString bakedTextureFileName; + if (_remappedTexturePaths.contains(urlToTexture)) { + bakedTextureFileName = _remappedTexturePaths[urlToTexture]; + } else { + // construct the new baked texture file name and file path + // ensuring that the baked texture will have a unique name + // even if there was another texture with the same name at a different path + bakedTextureFileName = createBakedTextureFileName(textureFileInfo); + _remappedTexturePaths[urlToTexture] = bakedTextureFileName; + } + + qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName + << "to" << bakedTextureFileName; + + QString bakedTextureFilePath { + _bakedOutputDir + "/" + bakedTextureFileName + }; + // write the new filename into the FBX scene textureChild.properties[0] = bakedTextureFileName.toLocal8Bit(); if (!_bakingTextures.contains(urlToTexture)) { + _outputFiles.push_back(bakedTextureFilePath); // grab the ID for this texture so we can figure out the // texture type from the loaded materials @@ -624,7 +631,7 @@ void FBXBaker::rewriteAndBakeSceneTextures() { auto textureType = textureTypes[textureID]; // bake this texture asynchronously - bakeTexture(urlToTexture, textureType, _bakedOutputDir, textureContent); + bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent); } } } @@ -644,10 +651,10 @@ void FBXBaker::rewriteAndBakeSceneTextures() { } void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, - const QDir& outputDir, const QByteArray& textureContent) { + const QDir& outputDir, const QString& bakedFilename, const QByteArray& textureContent) { // start a bake for this texture and add it to our list to keep track of QSharedPointer bakingTexture { - new TextureBaker(textureURL, textureType, outputDir, textureContent), + new TextureBaker(textureURL, textureType, outputDir, bakedFilename, textureContent), &TextureBaker::deleteLater }; diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h index 26471a29b3..ad8284bfa8 100644 --- a/libraries/baking/src/FBXBaker.h +++ b/libraries/baking/src/FBXBaker.h @@ -71,7 +71,7 @@ private: QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false); void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir, - const QByteArray& textureContent = QByteArray()); + const QString& bakedFilename, const QByteArray& textureContent = QByteArray()); QUrl _fbxURL; @@ -91,6 +91,7 @@ private: QMultiHash> _bakingTextures; QHash _textureNameMatchCount; + QHash _remappedTexturePaths; TextureBakerThreadGetter _textureThreadGetter; diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index febc7ea092..1a320efabc 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -26,15 +26,19 @@ const QString BAKED_TEXTURE_EXT = ".ktx"; TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, - const QDir& outputDirectory, const QByteArray& textureContent) : + const QDir& outputDirectory, const QString& bakedFilename, + const QByteArray& textureContent) : _textureURL(textureURL), _originalTexture(textureContent), _textureType(textureType), - _outputDirectory(outputDirectory) + _outputDirectory(outputDirectory), + _bakedTextureFileName(bakedFilename) { - // figure out the baked texture filename - auto originalFilename = textureURL.fileName(); - _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT; + if (bakedFilename.isEmpty()) { + // figure out the baked texture filename + auto originalFilename = textureURL.fileName(); + _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT; + } } void TextureBaker::bake() { diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h index e5bd41cf0d..b2e86b2b5b 100644 --- a/libraries/baking/src/TextureBaker.h +++ b/libraries/baking/src/TextureBaker.h @@ -28,7 +28,8 @@ class TextureBaker : public Baker { public: TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, - const QDir& outputDirectory, const QByteArray& textureContent = QByteArray()); + const QDir& outputDirectory, const QString& bakedFilename = QString(), + const QByteArray& textureContent = QByteArray()); static const QStringList getSupportedFormats(); From 1a571d099125265f18c731063e79fd6c34061710 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 20 Sep 2017 11:12:08 -0700 Subject: [PATCH 187/202] Add Asset Server auto refresh --- interface/resources/qml/AssetServer.qml | 18 +++++-------- .../qml/hifi/dialogs/TabletAssetServer.qml | 19 +++++--------- .../AssetMappingsScriptingInterface.cpp | 26 ++++++++++++++++--- .../AssetMappingsScriptingInterface.h | 6 +++++ 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 23fec03ac2..649ea49153 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -49,9 +49,14 @@ ScrollingWindow { Component.onCompleted: { ApplicationInterface.uploadRequest.connect(uploadClicked); assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); + assetMappingsModel.autoRefreshEnabled = true; reload(); } + + Component.onDestruction: { + assetMappingsModel.autoRefreshEnabled = false; + } function doDeleteFile(path) { console.log("Deleting " + path); @@ -146,7 +151,6 @@ ScrollingWindow { function reload() { Assets.mappingModel.refresh(); - treeView.selection.clear(); } function handleGetMappingsError(errorString) { @@ -502,16 +506,6 @@ ScrollingWindow { onClicked: root.deleteFile() enabled: treeView.selection.hasSelection } - - HifiControls.GlyphButton { - - glyph: hifi.glyphs.reload - color: hifi.buttons.black - colorScheme: root.colorScheme - width: hifi.dimensions.controlLineHeight - - onClicked: root.reload() - } } } @@ -751,7 +745,7 @@ ScrollingWindow { var path = assetProxyModel.data(index, 0x100); mappings.push(path); } - print("Setting baking enabled:" + mappings + checked); + print("Setting baking enabled:" + mappings + " " + checked); Assets.setBakingEnabled(mappings, checked, function() { reload(); }); diff --git a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml index 61f5a382ad..3f1fcf6bda 100644 --- a/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml +++ b/interface/resources/qml/hifi/dialogs/TabletAssetServer.qml @@ -49,9 +49,15 @@ Rectangle { isHMD = HMD.active; ApplicationInterface.uploadRequest.connect(uploadClicked); assetMappingsModel.errorGettingMappings.connect(handleGetMappingsError); + assetMappingsModel.autoRefreshEnabled = true; + reload(); } + Component.onDestruction: { + assetMappingsModel.autoRefreshEnabled = false; + } + function doDeleteFile(path) { console.log("Deleting " + path); @@ -145,7 +151,6 @@ Rectangle { function reload() { Assets.mappingModel.refresh(); - treeView.selection.clear(); } function handleGetMappingsError(errorString) { @@ -502,16 +507,6 @@ Rectangle { onClicked: root.deleteFile() enabled: treeView.selection.hasSelection } - - HifiControls.GlyphButton { - - glyph: hifi.glyphs.reload - color: hifi.buttons.black - colorScheme: root.colorScheme - width: hifi.dimensions.controlLineHeight - - onClicked: root.reload() - } } } @@ -748,7 +743,7 @@ Rectangle { var path = assetProxyModel.data(index, 0x100); mappings.push(path); } - print("Setting baking enabled:" + mappings + checked); + print("Setting baking enabled:" + mappings + " " + checked); Assets.setBakingEnabled(mappings, checked, function() { reload(); }); diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 14e0ef5b28..abe31932c9 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -21,6 +21,10 @@ #include #include +static const int AUTO_REFRESH_INTERVAL = 1000; + +const int assetMappingModelMetatypeId = qRegisterMetaType("AssetMappingModel*"); + AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() { _proxyModel.setSourceModel(&_assetMappingModel); _proxyModel.setSortRole(Qt::DisplayRole); @@ -189,6 +193,25 @@ void AssetMappingsScriptingInterface::setBakingEnabled(QStringList paths, bool e AssetMappingModel::AssetMappingModel() { setupRoles(); + + connect(&_autoRefreshTimer, &QTimer::timeout, this, [this] { + refresh(); + }); + _autoRefreshTimer.setInterval(AUTO_REFRESH_INTERVAL); +} + +bool AssetMappingModel::isAutoRefreshEnabled() { + return _autoRefreshTimer.isActive(); +} + +void AssetMappingModel::setAutoRefreshEnabled(bool enabled) { + if (enabled != _autoRefreshTimer.isActive()) { + if (enabled) { + _autoRefreshTimer.start(); + } else { + _autoRefreshTimer.stop(); + } + } } bool AssetMappingModel::isKnownFolder(QString path) const { @@ -205,10 +228,7 @@ bool AssetMappingModel::isKnownFolder(QString path) const { return false; } -int assetMappingModelMetatypeId = qRegisterMetaType("AssetMappingModel*"); - void AssetMappingModel::refresh() { - qDebug() << "Refreshing asset mapping model"; auto assetClient = DependencyManager::get(); auto request = assetClient->createGetAllMappingsRequest(); diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.h b/interface/src/scripting/AssetMappingsScriptingInterface.h index e4059c1ff7..04ab488838 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.h +++ b/interface/src/scripting/AssetMappingsScriptingInterface.h @@ -25,11 +25,16 @@ class AssetMappingModel : public QStandardItemModel { Q_OBJECT + Q_PROPERTY(bool autoRefreshEnabled READ isAutoRefreshEnabled WRITE setAutoRefreshEnabled) + public: AssetMappingModel(); Q_INVOKABLE void refresh(); + bool isAutoRefreshEnabled(); + void setAutoRefreshEnabled(bool enabled); + bool isKnownMapping(QString path) const { return _pathToItemMap.contains(path); } bool isKnownFolder(QString path) const; @@ -44,6 +49,7 @@ private: void setupRoles(); QHash _pathToItemMap; + QTimer _autoRefreshTimer; }; Q_DECLARE_METATYPE(AssetMappingModel*) From 6cdbf0c44a971efb836843967e0a8a1a5dfa7f9c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 21 Sep 2017 15:53:27 -0700 Subject: [PATCH 188/202] Fix RunningScripts in tablet mode --- .../qml/hifi/dialogs/TabletRunningScripts.qml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml index 83b48094e3..80c1b58444 100644 --- a/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/TabletRunningScripts.qml @@ -400,6 +400,17 @@ Rectangle { colorScheme: hifi.colorSchemes.dark anchors.left: parent.left anchors.right: parent.right + + TableViewColumn { + role: "display"; + } + + onActivated: { + var path = scriptsModel.data(index, 0x100) + if (path) { + loadScript(path) + } + } } HifiControls.VerticalSpacer { @@ -426,13 +437,6 @@ Rectangle { } } } - - onActivated: { - var path = scriptsModel.data(index, 0x100) - if (path) { - loadScript(path) - } - } } Item { From a4b9945226bd4fea9c5b9d11a44a3d2d24f65cc8 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 21 Sep 2017 16:17:54 -0700 Subject: [PATCH 189/202] Fix use after free in deleteMappings --- assignment-client/src/assets/AssetServer.cpp | 11 +++++------ assignment-client/src/assets/AssetServer.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 1f86b65029..9df606c227 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -953,16 +953,15 @@ void AssetServer::removeBakedPathsForDeletedAsset(AssetHash hash) { deleteMappings(hiddenBakedFolder); } -bool AssetServer::deleteMappings(AssetPathList& paths) { +bool AssetServer::deleteMappings(const AssetPathList& paths) { // take a copy of the current mappings in case persistence of these deletes fails auto oldMappings = _fileMappings; QSet hashesToCheckForDeletion; // enumerate the paths to delete and remove them all - for (auto& path : paths) { - - path = path.trimmed(); + for (const auto& rawPath : paths) { + auto path = rawPath.trimmed(); // figure out if this path will delete a file or folder if (pathIsFolder(path)) { @@ -991,12 +990,12 @@ bool AssetServer::deleteMappings(AssetPathList& paths) { } else { auto it = _fileMappings.find(path); if (it != _fileMappings.end()) { - _fileMappings.erase(it); - // add this hash to the list we need to check for asset removal from server hashesToCheckForDeletion << it->second; qCDebug(asset_server) << "Deleted a mapping:" << path << "=>" << it->second; + + _fileMappings.erase(it); } else { qCDebug(asset_server) << "Unable to delete a mapping that was not found:" << path; } diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 1589dde3e3..94be560c9b 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -78,7 +78,7 @@ private: bool setMapping(AssetPath path, AssetHash hash); /// Delete mapping `path`. Returns `true` if deletion of mappings succeeds, else `false`. - bool deleteMappings(AssetPathList& paths); + bool deleteMappings(const AssetPathList& paths); /// Rename mapping from `oldPath` to `newPath`. Returns true if successful bool renameMapping(AssetPath oldPath, AssetPath newPath); From 0d33976035fd2e96a0aec69e3993f9cbbf5be328 Mon Sep 17 00:00:00 2001 From: Cain Kilgore Date: Fri, 22 Sep 2017 00:38:30 +0100 Subject: [PATCH 190/202] Real time changes --- libraries/physics/src/CharacterController.cpp | 26 +++++++++---------- libraries/physics/src/CharacterController.h | 6 +++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 8d32535632..32e764bd10 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -20,9 +20,6 @@ const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); -const float DEFAULT_CHARACTER_GRAVITY = -5.0f; -float currentAvatarGravity = DEFAULT_CHARACTER_GRAVITY; - #ifdef DEBUG_STATE_CHANGE #define SET_STATE(desiredState, reason) setState(desiredState, reason) #else @@ -123,7 +120,7 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addAction(this); // restore gravity settings because adding an object to the world overwrites its gravity setting - _rigidBody->setGravity(_gravity * _currentUp); + _rigidBody->setGravity(_currentGravity * _currentUp); btCollisionShape* shape = _rigidBody->getCollisionShape(); assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); _ghost.setCharacterShape(static_cast(shape)); @@ -305,7 +302,7 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar // add minimum velocity to counteract gravity's displacement during one step // Note: the 0.5 factor comes from the fact that we really want the // average velocity contribution from gravity during the step - stepUpSpeed -= 0.5f * _gravity * timeToStep; // remember: _gravity is negative scalar + stepUpSpeed -= 0.5f * _currentGravity * timeToStep; // remember: _gravity is negative scalar btScalar vDotUp = velocity.dot(_currentUp); if (vDotUp < stepUpSpeed) { @@ -354,25 +351,26 @@ static const char* stateToStr(CharacterController::State state) { } #endif // #ifdef DEBUG_STATE_CHANGE -void CharacterController::updateGravity() { +void CharacterController::updateCurrentGravity() { int16_t collisionGroup = computeCollisionGroup(); if (_state == State::Hover || collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _gravity = 0.0f; + _currentGravity = 0.0f; } else { - _gravity = currentAvatarGravity; + _currentGravity = _gravity; } if (_rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); + _rigidBody->setGravity(_currentGravity * _currentUp); } } void CharacterController::setGravity(float gravity) { - currentAvatarGravity = gravity; + _gravity = gravity; + updateCurrentGravity(); } float CharacterController::getGravity() { - return currentAvatarGravity; + return _gravity; } #ifdef DEBUG_STATE_CHANGE @@ -389,7 +387,7 @@ void CharacterController::setState(State desiredState) { qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; #endif _state = desiredState; - updateGravity(); + updateCurrentGravity(); } } @@ -448,14 +446,14 @@ void CharacterController::handleChangedCollisionGroup() { _dynamicsWorld->addRigidBody(_rigidBody, collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); } _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; - updateGravity(); + updateCurrentGravity(); } } void CharacterController::updateUpAxis(const glm::quat& rotation) { _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); if (_rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); + _rigidBody->setGravity(_currentGravity * _currentUp); } } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 49db3b9bf4..0f97cc7c16 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -24,6 +24,7 @@ #include "BulletUtil.h" #include "CharacterGhostObject.h" +#include "AvatarConstants.h" const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; @@ -134,7 +135,7 @@ protected: #endif virtual void updateMassProperties() = 0; - void updateGravity(); + void updateCurrentGravity(); void updateUpAxis(const glm::quat& rotation); bool checkForSupport(btCollisionWorld* collisionWorld); @@ -187,7 +188,8 @@ protected: bool _stepUpEnabled { true }; bool _hasSupport; - btScalar _gravity { 0.0f }; + btScalar _currentGravity { 0.0f }; + btScalar _gravity { DEFAULT_AVATAR_GRAVITY }; btScalar _followTime; btVector3 _followLinearDisplacement; From 9f12d3a3658298c6afb7d6a27b65735fdd4e4548 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 21 Sep 2017 17:51:00 -0700 Subject: [PATCH 191/202] fix missing models --- .../src/EntityTreeRenderer.cpp | 8 ++++++ .../src/RenderableEntityItem.cpp | 17 ++++++++++++ .../src/RenderableEntityItem.h | 26 +++++++++++++++++++ .../src/RenderableModelEntityItem.cpp | 16 +++++------- .../src/RenderableModelEntityItem.h | 6 ++--- .../src/model-networking/ModelCache.cpp | 2 +- .../render-utils/src/MeshPartPayload.cpp | 4 +-- libraries/render-utils/src/Model.cpp | 8 ++---- 8 files changed, 66 insertions(+), 21 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index a79e29f003..f303617be0 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -222,6 +222,14 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene _renderablesToUpdate.insert({ entityId, renderable }); } + if (!_entitiesInScene.empty()) { + for (const auto& entry : _entitiesInScene) { + const auto& renderable = entry.second; + if (renderable) { + renderable->update(scene, transaction); + } + } + } if (!_renderablesToUpdate.empty()) { for (const auto& entry : _renderablesToUpdate) { const auto& renderable = entry.second; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 3f1e89b86c..ea514d3181 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -291,6 +291,18 @@ void EntityRenderer::updateInScene(const ScenePointer& scene, Transaction& trans }); } +void EntityRenderer::update(const ScenePointer& scene, Transaction& transaction) { + if (!isValidRenderItem()) { + return; + } + + if (!needsUpdate()) { + return; + } + + doUpdate(scene, transaction, _entity); +} + // // Internal methods // @@ -304,6 +316,11 @@ bool EntityRenderer::needsRenderUpdate() const { return needsRenderUpdateFromEntity(_entity); } +// Returns true if the item needs to have update called +bool EntityRenderer::needsUpdate() const { + return needsUpdateFromEntity(_entity); +} + // Returns true if the item in question needs to have updateInScene called because of changes in the entity bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity) const { bool success = false; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 6b47ff8b1d..56cb39252f 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -49,6 +49,8 @@ public: virtual bool addToScene(const ScenePointer& scene, Transaction& transaction) final; virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction); + virtual void update(const ScenePointer& scene, Transaction& transaction); + protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } virtual void onAddToScene(const EntityItemPointer& entity); @@ -71,6 +73,12 @@ protected: // Returns true if the item in question needs to have updateInScene called because of changes in the entity virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const; + // Returns true if the item in question needs to have update called + virtual bool needsUpdate() const; + + // Returns true if the item in question needs to have update called because of changes in the entity + virtual bool needsUpdateFromEntity(const EntityItemPointer& entity) const { return false; } + // Will be called on the main thread from updateInScene. This can be used to fetch things like // network textures or model geometry from resource caches virtual void doRenderUpdateSynchronous(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { } @@ -80,6 +88,8 @@ protected: // data in this method if using multi-threaded rendering virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity); + virtual void doUpdate(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) { } + // Called by the `render` method after `needsRenderUpdate` virtual void doRender(RenderArgs* args) = 0; @@ -148,6 +158,15 @@ protected: onRemoveFromSceneTyped(_typedEntity); } + using Parent::needsUpdateFromEntity; + // Returns true if the item in question needs to have update called because of changes in the entity + virtual bool needsUpdateFromEntity(const EntityItemPointer& entity) const override final { + if (Parent::needsUpdateFromEntity(entity)) { + return true; + } + return needsUpdateFromTypedEntity(_typedEntity); + } + using Parent::needsRenderUpdateFromEntity; // Returns true if the item in question needs to have updateInScene called because of changes in the entity virtual bool needsRenderUpdateFromEntity(const EntityItemPointer& entity) const override final { @@ -162,6 +181,11 @@ protected: doRenderUpdateSynchronousTyped(scene, transaction, _typedEntity); } + virtual void doUpdate(const ScenePointer& scene, Transaction& transaction, const EntityItemPointer& entity) override final { + Parent::doUpdate(scene, transaction, entity); + doUpdateTyped(scene, transaction, _typedEntity); + } + virtual void doRenderUpdateAsynchronous(const EntityItemPointer& entity) override final { Parent::doRenderUpdateAsynchronous(entity); doRenderUpdateAsynchronousTyped(_typedEntity); @@ -170,6 +194,8 @@ protected: virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { return false; } virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { } virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { } + virtual bool needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const { return false; } + virtual void doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { } virtual void onAddToSceneTyped(const TypedEntityPointer& entity) { } virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) { } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 2508b598af..a8a7dcdcf7 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -904,7 +904,7 @@ using namespace render; using namespace render::entities; ItemKey ModelEntityRenderer::getKey() { - return ItemKey::Builder::opaqueShape().withTypeMeta(); + return ItemKey::Builder().withTypeMeta(); } uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { @@ -1026,7 +1026,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { entity->copyAnimationJointDataToModel(); } -bool ModelEntityRenderer::needsRenderUpdate() const { +bool ModelEntityRenderer::needsUpdate() const { ModelPointer model; withReadLock([&] { model = _model; @@ -1057,10 +1057,10 @@ bool ModelEntityRenderer::needsRenderUpdate() const { return true; } } - return Parent::needsRenderUpdate(); + return Parent::needsUpdate(); } -bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { +bool ModelEntityRenderer::needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const { if (resultWithReadLock([&] { if (entity->hasModel() != _hasModel) { return true; @@ -1122,7 +1122,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin return false; } -void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { +void ModelEntityRenderer::doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { if (_hasModel != entity->hasModel()) { _hasModel = entity->hasModel(); } @@ -1175,8 +1175,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it auto extents = model->getMeshExtents(); properties.setDimensions(extents.maximum - extents.minimum); - qCDebug(entitiesrenderer) << "Autoresizing" - << (!entity->getName().isEmpty() ? entity->getName() : entity->getModelURL()) + qCDebug(entitiesrenderer) << "Autoresizing" + << (!entity->getName().isEmpty() ? entity->getName() : entity->getModelURL()) << "from mesh extents"; QMetaObject::invokeMethod(DependencyManager::get().data(), "editEntity", @@ -1203,7 +1203,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce entity->updateModelBounds(); } - if (model->isVisible() != _visible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in // the renderable item. As it stands now the model checks it's visible/invisible state @@ -1234,7 +1233,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce }); } - if (_animating) { if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index b9c751761d..e2c7939845 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -138,10 +138,10 @@ protected: virtual ItemKey getKey() override; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override; - virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; - virtual bool needsRenderUpdate() const override; + virtual bool needsUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; + virtual bool needsUpdate() const override; virtual void doRender(RenderArgs* args) override; - virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; + virtual void doUpdateTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; private: void animate(const TypedEntityPointer& entity); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 468d22ce9e..74c8d06736 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -464,7 +464,7 @@ void GeometryResourceWatcher::setResource(GeometryResource::Pointer resource) { _resource = resource; if (_resource) { if (_resource->isLoaded()) { - _geometryRef = std::make_shared(*_resource); + resourceFinished(true); } else { startWatching(); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 517fe97dba..924c199239 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -321,8 +321,8 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren } -ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : - _meshIndex(_meshIndex), +ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : + _meshIndex(meshIndex), _shapeID(shapeIndex) { assert(model && model->isLoaded()); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 42bb91ce94..e729a9519b 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -209,11 +209,6 @@ void Model::updateRenderItems() { return; } - glm::vec3 scale = getScale(); - if (_collisionGeometry) { - // _collisionGeometry is already scaled - scale = glm::vec3(1.0f); - } _needsUpdateClusterMatrices = true; _renderItemsNeedUpdate = false; @@ -221,7 +216,7 @@ void Model::updateRenderItems() { // the application will ensure only the last lambda is actually invoked. void* key = (void*)this; std::weak_ptr weakSelf = shared_from_this(); - AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() { + AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() { // do nothing, if the model has already been destroyed. auto self = weakSelf.lock(); @@ -1219,6 +1214,7 @@ const render::ItemIDs& Model::fetchRenderItemIDs() const { } void Model::createRenderItemSet() { + updateClusterMatrices(); if (_collisionGeometry) { if (_collisionRenderItems.empty()) { createCollisionRenderItemSet(); From 8583be41ce5a46abd199b3e9b5a54426ee69f27b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 21 Sep 2017 13:23:29 -0700 Subject: [PATCH 192/202] Don't refresh if not connected to the Asset Server --- .../src/scripting/AssetMappingsScriptingInterface.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index abe31932c9..5031016c3f 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -19,11 +19,12 @@ #include #include #include +#include #include static const int AUTO_REFRESH_INTERVAL = 1000; -const int assetMappingModelMetatypeId = qRegisterMetaType("AssetMappingModel*"); +int assetMappingModelMetatypeId = qRegisterMetaType("AssetMappingModel*"); AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() { _proxyModel.setSourceModel(&_assetMappingModel); @@ -195,7 +196,11 @@ AssetMappingModel::AssetMappingModel() { setupRoles(); connect(&_autoRefreshTimer, &QTimer::timeout, this, [this] { - refresh(); + auto nodeList = DependencyManager::get(); + auto assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); + if (assetServer) { + refresh(); + } }); _autoRefreshTimer.setInterval(AUTO_REFRESH_INTERVAL); } From 549cb7790228992c52f51ee8b6ffdd1625829b1f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 21 Sep 2017 20:47:36 -0700 Subject: [PATCH 193/202] Fix dimensions of rendered web entities --- .../src/RenderableWebEntityItem.cpp | 5 ++++ .../src/RenderableWebEntityItem.h | 1 + libraries/ui/src/ui/OffscreenQmlSurface.cpp | 4 +-- tests/render-perf/CMakeLists.txt | 2 +- tests/render-perf/src/main.cpp | 28 +++++++++++++++++-- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 8b5feb15f0..5dc75dad08 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -140,6 +140,11 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _webSurface->resize(QSize(windowSize.x, windowSize.y)); } +void WebEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { + Parent::doRenderUpdateAsynchronousTyped(entity); + _modelTransform.postScale(entity->getDimensions()); +} + void WebEntityRenderer::doRender(RenderArgs* args) { withWriteLock([&] { _lastRenderTime = usecTimestampNow(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 4b7e7e25a1..a67eb39670 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -29,6 +29,7 @@ protected: virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; + virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; virtual void doRender(RenderArgs* args) override; virtual bool isTransparent() const override; diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index faf7933d1a..071ccd46b1 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -211,12 +211,12 @@ class UrlHandler : public QObject { public: Q_INVOKABLE bool canHandleUrl(const QString& url) { static auto handler = dynamic_cast(qApp); - return handler->canAcceptURL(url); + return handler && handler->canAcceptURL(url); } Q_INVOKABLE bool handleUrl(const QString& url) { static auto handler = dynamic_cast(qApp); - return handler->acceptURL(url); + return handler && handler->acceptURL(url); } }; diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 77c8ab2177..5b83ff313b 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -12,7 +12,7 @@ setup_hifi_project(Quick Gui OpenGL) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") # link in the shared libraries -link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi) +link_hifi_libraries(shared networking model fbx ktx image octree gl gpu gpu-gl render model-networking networking render-utils entities entities-renderer animation audio avatars script-engine physics procedural midi ui) package_libraries_for_deployment() diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 3be6ba2f6c..58eb4d16f9 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -42,11 +42,15 @@ #include #include +#include + #include #include #include #include +#include + #include #include #include @@ -427,6 +431,10 @@ namespace render { } } +OffscreenGLCanvas* _chromiumShareContext{ nullptr }; +Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); + + // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow, public AbstractViewStateInterface { @@ -506,8 +514,6 @@ public: AbstractViewStateInterface::setInstance(this); _octree = DependencyManager::set(false, this, nullptr); _octree->init(); - // Prevent web entities from rendering - REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory); DependencyManager::set(_octree->getTree()); auto nodeList = DependencyManager::get(); @@ -535,6 +541,23 @@ public: _renderThread.initialize(this, _initContext); _initContext.makeCurrent(); + if (nsightActive()) { + // Prevent web entities from rendering + REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory); + } else { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(_initContext.qglContext()); + _chromiumShareContext->makeCurrent(); + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + + // Make sure all QML surfaces share the main thread GL context + OffscreenQmlSurface::setSharedContext(_initContext.qglContext()); + + _initContext.makeCurrent(); + } + + // FIXME use a wait condition QThread::msleep(1000); _renderThread.submitFrame(gpu::FramePointer()); @@ -679,6 +702,7 @@ private: _renderCount = _renderThread._presentCount.load(); update(); + _initContext.makeCurrent(); RenderArgs renderArgs(_renderThread._gpuContext, DEFAULT_OCTREE_SIZE_SCALE, 0, RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); From d3c60f3057eecaf2038215ccd1fd6d4e16a84a3e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 22 Sep 2017 09:44:51 -0700 Subject: [PATCH 194/202] Fix for TextEntity rendering, the translation was incorrect This was caused by the removal of _dimensions from the SpatiallyNestable scale component. --- libraries/entities-renderer/src/RenderableTextEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 8757bcbb0f..f4e4c0ea8f 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -102,7 +102,7 @@ void TextEntityRenderer::doRender(RenderArgs* args) { glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); transformToTopLeft.setRotation(orientation); } - transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left + transformToTopLeft.postTranslate(dimensions * glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed batch.setModelTransform(transformToTopLeft); From be27376c52d3b37106383a8cab1ab458f3f4d6e1 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 22 Sep 2017 10:02:41 -0700 Subject: [PATCH 195/202] add comment about bottleneck --- .../entities-renderer/src/EntityTreeRenderer.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index f303617be0..c29d92bae9 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -222,12 +222,14 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene _renderablesToUpdate.insert({ entityId, renderable }); } - if (!_entitiesInScene.empty()) { - for (const auto& entry : _entitiesInScene) { - const auto& renderable = entry.second; - if (renderable) { - renderable->update(scene, transaction); - } + // NOTE: Looping over all the entity renderers is likely to be a bottleneck in the future + // Currently, this is necessary because the model entity loading logic requires constant polling + // This was working fine because the entity server used to send repeated updates as your view changed, + // but with the improved entity server logic (PR 11141), updateInScene (below) would not be triggered enough + for (const auto& entry : _entitiesInScene) { + const auto& renderable = entry.second; + if (renderable) { + renderable->update(scene, transaction); } } if (!_renderablesToUpdate.empty()) { From bb9db3ef6d7ea6aed0c4dac2f636f93b08029545 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 22 Sep 2017 11:24:30 -0700 Subject: [PATCH 196/202] Updates to behavior and shader appearance --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 9 +++++++-- libraries/render-utils/src/OutlineEffect.h | 8 ++++---- .../controllers/controllerModules/farActionGrabEntity.js | 4 +++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 509e201d47..bf42b300ca 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -82,6 +82,10 @@ void ContextOverlayInterface::setEnabled(bool enabled) { bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { if (contextOverlayFilterPassed(entityItemID)) { + if (event.getID() == 0) { + enableEntityHighlight(entityItemID); + } + qCDebug(context_overlay) << "Creating Context Overlay on top of entity with ID: " << entityItemID; // Add all necessary variables to the stack @@ -165,6 +169,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& } } else { if (!_currentEntityWithContextOverlay.isNull()) { + disableEntityHighlight(_currentEntityWithContextOverlay); return destroyContextOverlay(_currentEntityWithContextOverlay, event); } return false; @@ -226,13 +231,13 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& } void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (contextOverlayFilterPassed(entityID) && _enabled) { + if (contextOverlayFilterPassed(entityID) && _enabled && event.getID() != 0) { enableEntityHighlight(entityID); } } void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (_currentEntityWithContextOverlay != entityID && _enabled) { + if (_currentEntityWithContextOverlay != entityID && _enabled && event.getID() != 0) { disableEntityHighlight(entityID); } } diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index 36dc59f29e..f260345c24 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -81,10 +81,10 @@ public: float getColorB() const { return color.b; } glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 3.f }; - float intensity{ 1.f }; - float fillOpacityUnoccluded{ 0.35f }; - float fillOpacityOccluded{ 0.1f }; + float width{ 1.5f }; + float intensity{ 0.9f }; + float fillOpacityUnoccluded{ 0.0f }; + float fillOpacityOccluded{ 0.0f }; bool glow{ false }; signals: diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index eb73b0f908..d2b5f92fde 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -148,7 +148,9 @@ Script.include("/~/system/libraries/controllers.js"); if (mode === "full") { var fullEndToEdit = PICK_WITH_HAND_RAY ? this.fullEnd : fullEnd; fullEndToEdit.dimensions = dim; - LaserPointers.editRenderState(laserPointerID, mode, {path: fullPath, end: fullEndToEdit}); + LaserPointers.editRenderState(laserPointerID, mode, { path: fullPath, end: fullEndToEdit }); + this.contextOverlayTimer = false; + this.destroyContextOverlay(); } else if (mode === "half") { var halfEndToEdit = PICK_WITH_HAND_RAY ? this.halfEnd : halfEnd; halfEndToEdit.dimensions = dim; From 430d1b22bb3f85c4f168bf7e00bd046c9565c22a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 22 Sep 2017 11:41:28 -0700 Subject: [PATCH 197/202] Update angular size and i behavior --- .../ui/overlays/ContextOverlayInterface.cpp | 27 +++++-------------- libraries/render-utils/src/OutlineEffect.h | 2 +- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index bf42b300ca..7513fdd6a7 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -63,10 +63,8 @@ ContextOverlayInterface::ContextOverlayInterface() { static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters -static const float CONTEXT_OVERLAY_CLOSE_DISTANCE = 1.5f; // in meters -static const float CONTEXT_OVERLAY_CLOSE_SIZE = 0.12f; // in meters, same x and y dims -static const float CONTEXT_OVERLAY_FAR_SIZE = 0.08f; // in meters, same x and y dims -static const float CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE = 20.0f; +static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_OFFSET_ANGLE = 10.0f; static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f; static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; @@ -91,7 +89,6 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& // Add all necessary variables to the stack EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); glm::vec3 cameraPosition = qApp->getCamera().getPosition(); - float distanceFromCameraToEntity = glm::distance(entityProperties.getPosition(), cameraPosition); glm::vec3 entityDimensions = entityProperties.getDimensions(); glm::vec3 entityPosition = entityProperties.getPosition(); glm::vec3 contextOverlayPosition = entityProperties.getPosition(); @@ -124,27 +121,17 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& // If the camera is inside the box... // ...position the Context Overlay 1 meter in front of the camera. contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else if (distanceFromCameraToEntity < CONTEXT_OVERLAY_CLOSE_DISTANCE) { + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + } else { // Else if the entity is too close to the camera... - // ...rotate the Context Overlay to the right of the entity. + // ...rotate the Context Overlay some number of degrees offset from the entity. // This makes it easy to inspect things you're holding. - float offsetAngle = -CONTEXT_OVERLAY_CLOSE_OFFSET_ANGLE; + float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE; if (event.getID() == LEFT_HAND_HW_ID) { offsetAngle *= -1; } contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityPosition - cameraPosition)) + cameraPosition; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_CLOSE_SIZE, CONTEXT_OVERLAY_CLOSE_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); - } else { - // Else, place the Context Overlay some offset away from the entity's bounding - // box in the direction of the camera. - glm::vec3 direction = glm::normalize(entityPosition - cameraPosition); - float distance; - BoxFace face; - glm::vec3 normal; - boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); - contextOverlayPosition = (cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_FAR_OFFSET; - contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_FAR_SIZE, CONTEXT_OVERLAY_FAR_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); + contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } // Finally, setup and draw the Context Overlay diff --git a/libraries/render-utils/src/OutlineEffect.h b/libraries/render-utils/src/OutlineEffect.h index f260345c24..f88092429f 100644 --- a/libraries/render-utils/src/OutlineEffect.h +++ b/libraries/render-utils/src/OutlineEffect.h @@ -81,7 +81,7 @@ public: float getColorB() const { return color.b; } glm::vec3 color{ 1.f, 0.7f, 0.2f }; - float width{ 1.5f }; + float width{ 2.0f }; float intensity{ 0.9f }; float fillOpacityUnoccluded{ 0.0f }; float fillOpacityOccluded{ 0.0f }; From e3f2e70bca4b728595e874d9882d5bb67f9d2f27 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 22 Sep 2017 11:43:11 -0700 Subject: [PATCH 198/202] No magic numbers --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 7513fdd6a7..e103e85544 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -60,6 +60,7 @@ ContextOverlayInterface::ContextOverlayInterface() { connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged); } +static const uint32_t MOUSE_HW_ID = 0; static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters @@ -80,7 +81,7 @@ void ContextOverlayInterface::setEnabled(bool enabled) { bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { if (contextOverlayFilterPassed(entityItemID)) { - if (event.getID() == 0) { + if (event.getID() == MOUSE_HW_ID) { enableEntityHighlight(entityItemID); } @@ -218,13 +219,13 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& } void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (contextOverlayFilterPassed(entityID) && _enabled && event.getID() != 0) { + if (contextOverlayFilterPassed(entityID) && _enabled && event.getID() != MOUSE_HW_ID) { enableEntityHighlight(entityID); } } void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (_currentEntityWithContextOverlay != entityID && _enabled && event.getID() != 0) { + if (_currentEntityWithContextOverlay != entityID && _enabled && event.getID() != MOUSE_HW_ID) { disableEntityHighlight(entityID); } } From bc477608ee873b78f77ff246605a2b6ddc471841 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 22 Sep 2017 13:22:08 -0700 Subject: [PATCH 199/202] fix alpha on glowLines --- libraries/render-utils/src/glowLine.slf | 10 +++++----- libraries/render-utils/src/glowLine.slv | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/glowLine.slf b/libraries/render-utils/src/glowLine.slf index c0af97930a..580a49dd3e 100644 --- a/libraries/render-utils/src/glowLine.slf +++ b/libraries/render-utils/src/glowLine.slf @@ -9,7 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -layout(location = 0) in vec4 inColor; +in vec4 _color; +in float distanceFromCenter; out vec4 _fragColor; @@ -17,10 +18,10 @@ void main(void) { // The incoming value actually ranges from -1 to 1, so modify it // so that it goes from 0 -> 1 -> 0 with the solid alpha being at // the center of the line - float alpha = 1.0 - abs(inColor.a); + float alpha = 1.0 - abs(distanceFromCenter); // Convert from a linear alpha curve to a sharp peaked one - alpha = pow(alpha, 10); + alpha = _color.a * pow(alpha, 10); // Drop everything where the curve falls off to nearly nothing if (alpha <= 0.05) { @@ -28,6 +29,5 @@ void main(void) { } // Emit the color - _fragColor = vec4(inColor.rgb, alpha); - return; + _fragColor = vec4(_color.rgb, alpha); } diff --git a/libraries/render-utils/src/glowLine.slv b/libraries/render-utils/src/glowLine.slv index e856edc787..fd3a85d254 100644 --- a/libraries/render-utils/src/glowLine.slv +++ b/libraries/render-utils/src/glowLine.slv @@ -18,7 +18,9 @@ layout(std140) uniform lineData { vec4 color; }; -layout(location = 0) out vec4 _color; +out vec4 _color; +// the distance from the center in 'quad space' +out float distanceFromCenter; void main(void) { _color = color; @@ -45,11 +47,10 @@ void main(void) { // Add or subtract the orthogonal vector based on a different vertex ID // calculation if (gl_VertexID < 2) { - // Use the alpha channel to store the distance from the center in 'quad space' - _color.a = -1.0; + distanceFromCenter = -1.0; eye.xyz -= orthogonal; } else { - _color.a = 1.0; + distanceFromCenter = 1.0; eye.xyz += orthogonal; } From 0eafdeab14267c078a4944b9d6cc2e7fa249342b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 22 Sep 2017 13:19:52 -0700 Subject: [PATCH 200/202] Fix hover -> unhover overlay behavior --- .../src/ui/overlays/ContextOverlayInterface.cpp | 14 +++++++++----- .../controllerModules/overlayLaserInput.js | 4 +++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e103e85544..3c0dc982a3 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -65,6 +65,7 @@ static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims +static const float CONTEXT_OVERLAY_OFFSET_DISTANCE = 0.1f; static const float CONTEXT_OVERLAY_OFFSET_ANGLE = 10.0f; static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f; static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; @@ -72,7 +73,6 @@ static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMAX = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEPERIOD = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_COLORPULSE = 1.0f; -static const float CONTEXT_OVERLAY_FAR_OFFSET = 0.1f; void ContextOverlayInterface::setEnabled(bool enabled) { _enabled = enabled; @@ -124,14 +124,18 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& contextOverlayPosition = cameraPosition + CONTEXT_OVERLAY_INSIDE_DISTANCE * (qApp->getCamera().getOrientation() * Vectors::FRONT); contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } else { - // Else if the entity is too close to the camera... - // ...rotate the Context Overlay some number of degrees offset from the entity. - // This makes it easy to inspect things you're holding. + // Rotate the Context Overlay some number of degrees offset from the entity + // along the line cast from your head to the entity's bounding box. + glm::vec3 direction = glm::normalize(entityPosition - cameraPosition); + float distance; + BoxFace face; + glm::vec3 normal; + boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE; if (event.getID() == LEFT_HAND_HW_ID) { offsetAngle *= -1; } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f))) * (entityPosition - cameraPosition)) + cameraPosition; + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f)))) * ((cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_OFFSET_DISTANCE); contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index d67672ca7c..7dace85ec4 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -298,6 +298,9 @@ Script.include("/~/system/libraries/controllers.js"); var intersection = controllerData.rayPicks[this.hand]; var offOverlay = (intersection.type !== RayPick.INTERSECTED_OVERLAY); var triggerOff = (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE); + if (triggerOff) { + this.deleteContextOverlay(); + } var grabbingOverlay = this.grabModuleWantsNearbyOverlay(controllerData); return offOverlay || grabbingOverlay || triggerOff; }; @@ -308,7 +311,6 @@ Script.include("/~/system/libraries/controllers.js"); this.laserPressExit(); this.laserPressingTarget = false; } - this.deleteContextOverlay(); this.relinquishTouchFocus(); this.reset(); this.updateLaserPointer(); From 702a4e9adeeff9fa0f6e4d4c0e601f1324fe83fb Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 22 Sep 2017 15:32:38 -0700 Subject: [PATCH 201/202] More tweaking to overlay position --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 3c0dc982a3..2366b888e7 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -15,6 +15,10 @@ #include #include +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + static const float CONTEXT_OVERLAY_TABLET_OFFSET = 30.0f; // Degrees static const float CONTEXT_OVERLAY_TABLET_ORIENTATION = 210.0f; // Degrees static const float CONTEXT_OVERLAY_TABLET_DISTANCE = 0.65F; // Meters @@ -66,7 +70,7 @@ static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims static const float CONTEXT_OVERLAY_OFFSET_DISTANCE = 0.1f; -static const float CONTEXT_OVERLAY_OFFSET_ANGLE = 10.0f; +static const float CONTEXT_OVERLAY_OFFSET_ANGLE = 5.0f; static const float CONTEXT_OVERLAY_UNHOVERED_ALPHA = 0.85f; static const float CONTEXT_OVERLAY_HOVERED_ALPHA = 1.0f; static const float CONTEXT_OVERLAY_UNHOVERED_PULSEMIN = 0.6f; @@ -135,7 +139,8 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& if (event.getID() == LEFT_HAND_HW_ID) { offsetAngle *= -1; } - contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f)))) * ((cameraPosition + direction * distance) - direction * CONTEXT_OVERLAY_OFFSET_DISTANCE); + contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f)))) * + ((cameraPosition + direction * (distance - CONTEXT_OVERLAY_OFFSET_DISTANCE))); contextOverlayDimensions = glm::vec2(CONTEXT_OVERLAY_SIZE, CONTEXT_OVERLAY_SIZE) * glm::distance(contextOverlayPosition, cameraPosition); } From 86198606e036f7f5cafbc75295983fd0c5421a5a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 22 Sep 2017 11:46:24 -0700 Subject: [PATCH 202/202] add _modelJustLoaded and updateTransforForSkinnedMesh on creation --- .../src/RenderableModelEntityItem.cpp | 12 ++++++++++++ .../src/RenderableModelEntityItem.h | 4 ++++ libraries/render-utils/src/MeshPartPayload.cpp | 8 ++++++++ libraries/render-utils/src/Model.cpp | 1 - 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a8a7dcdcf7..799a84aaee 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1032,6 +1032,10 @@ bool ModelEntityRenderer::needsUpdate() const { model = _model; }); + if (_modelJustLoaded) { + return true; + } + if (model) { if (_needsJointSimulation || _moving || _animating) { return true; @@ -1148,9 +1152,11 @@ void ModelEntityRenderer::doUpdateTyped(const ScenePointer& scene, Transaction& return; } + _modelJustLoaded = false; // Check for addition if (_hasModel && !(bool)_model) { model = std::make_shared(nullptr, entity.get()); + connect(model.get(), &Model::setURLFinished, this, &ModelEntityRenderer::handleModelLoaded); model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity)); model->init(); entity->setModel(model); @@ -1241,6 +1247,12 @@ void ModelEntityRenderer::doUpdateTyped(const ScenePointer& scene, Transaction& } } +void ModelEntityRenderer::handleModelLoaded(bool success) { + if (success) { + _modelJustLoaded = true; + } +} + // NOTE: this only renders the "meta" portion of the Model, namely it renders debugging items void ModelEntityRenderer::doRender(RenderArgs* args) { PROFILE_RANGE(render_detail, "MetaModelRender"); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index e2c7939845..ad0afeee0a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -151,6 +151,7 @@ private: // Transparency is handled in ModelMeshPartPayload virtual bool isTransparent() const override { return false; } + bool _modelJustLoaded { false }; bool _hasModel { false }; ::ModelPointer _model; GeometryResource::Pointer _compoundShapeResource; @@ -178,6 +179,9 @@ private: bool _animating { false }; uint64_t _lastAnimated { 0 }; float _currentFrame { 0 }; + +private slots: + void handleModelLoaded(bool success); }; } } // namespace diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 924c199239..3f57a1779a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -328,10 +328,18 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in assert(model && model->isLoaded()); _model = model; auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); + const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); + computeAdjustedLocalBound(state.clusterMatrices); updateTransform(transform, offsetTransform); + Transform renderTransform = transform; + if (state.clusterMatrices.size() == 1) { + renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); + } + updateTransformForSkinnedMesh(renderTransform, transform, state.clusterBuffer); + initCache(); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e729a9519b..9948a8bddd 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1265,7 +1265,6 @@ void Model::createVisibleRenderItemSet() { shapeID++; } } - computeMeshPartLocalBounds(); } void Model::createCollisionRenderItemSet() {