Merge branch 'master' of github.com:highfidelity/hifi into groups

This commit is contained in:
Seth Alves 2016-07-25 13:19:13 -07:00
commit e3b4612283
37 changed files with 947 additions and 537 deletions

View file

@ -0,0 +1,25 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
import "../../qml/dialogs"
QtObject {
id: root
signal accepted;
property var text;
property var messageDialogBuilder: Component { MessageDialog { } }
function open() {
console.log("prompt text " + text)
var dialog = messageDialogBuilder.createObject(desktop, {
text: root.text
});
dialog.selected.connect(function(button){
accepted();
dialog.destroy();
});
}
}

View file

@ -0,0 +1,31 @@
import QtQuick 2.4
import QtQuick.Dialogs 1.1 as OriginalDialogs
import "../../qml/dialogs"
QtObject {
id: root
signal accepted;
signal rejected;
property var text;
property var messageDialogBuilder: Component { MessageDialog { } }
function open() {
var dialog = messageDialogBuilder.createObject(desktop, {
text: root.text,
icon: OriginalDialogs.StandardIcon.Question,
buttons: OriginalDialogs.StandardButton.Ok | OriginalDialogs.StandardButton.Cancel
});
dialog.selected.connect(function(button){
if (button === OriginalDialogs.StandardButton.Ok) {
accepted()
} else {
rejected();
}
dialog.destroy();
});
}
}

View file

@ -0,0 +1,39 @@
import QtQuick 2.4
import QtQuick.Dialogs 1.1
import QtQuick.Controls 1.4
import "../../qml/dialogs"
QtObject {
id: root
signal filesSelected(var fileList);
signal rejected();
property var text;
property url fileUrl;
property var fileUrls;
property url folder;
property var nameFilters;
property bool selectExisting;
property bool selectFolder;
property bool selectMultiple;
property string selectedNameFilter;
property string title;
property var fileDialogBuilder: Component { FileDialog { } }
function open() {
var foo = root;
var dialog = fileDialogBuilder.createObject(desktop, {
});
dialog.canceled.connect(function(){
root.filesSelected([]);
dialog.destroy();
});
dialog.selectedFile.connect(function(file){
root.filesSelected(fileDialogHelper.urlToList(file));
});
}
}

View file

@ -0,0 +1,68 @@
import QtQuick 2.5
import QtQuick.Controls 1.4 as Controls
import "../../qml/menus"
import "../../qml/controls-uit"
import "../../qml/styles-uit"
Item {
id: menu
HifiConstants { id: hifi }
signal done()
implicitHeight: column.height
implicitWidth: column.width
Rectangle {
id: background
anchors {
fill: parent
margins: -16
}
radius: hifi.dimensions.borderRadius
border.width: hifi.dimensions.borderWidth
border.color: hifi.colors.lightGrayText80
color: hifi.colors.faintGray80
}
MouseArea {
id: closer
width: 8192
height: 8192
x: -4096
y: -4096
propagateComposedEvents: true
acceptedButtons: "AllButtons"
onClicked: {
menu.done();
mouse.accepted = false;
}
}
Column {
id: column
}
function popup() {
var position = Reticle.position;
var localPosition = menu.parent.mapFromItem(desktop, position.x, position.y);
x = localPosition.x
y = localPosition.y
console.log("Popup at " + x + " x " + y)
var moveChildren = [];
for (var i = 0; i < children.length; ++i) {
var child = children[i];
if (child.objectName !== "MenuItem") {
continue;
}
moveChildren.push(child);
}
for (i = 0; i < moveChildren.length; ++i) {
child = moveChildren[i];
child.parent = column;
child.menu = menu
}
}
}

View file

@ -0,0 +1,39 @@
import QtQuick 2.5
import QtQuick.Controls 1.4 as Controls
import "../../qml/controls-uit"
import "../../qml/styles-uit"
Item {
id: root
objectName: "MenuItem"
property alias text: label.text
property var menu;
property var shortcut;
signal triggered();
HifiConstants { id: hifi }
implicitHeight: 2 * label.implicitHeight
implicitWidth: 2 * hifi.dimensions.menuPadding.x + label.width
RalewaySemiBold {
id: label
size: hifi.fontSizes.rootMenu
anchors.left: parent.left
anchors.leftMargin: hifi.dimensions.menuPadding.x
verticalAlignment: Text.AlignVCenter
color: enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50
enabled: root.enabled
}
MouseArea {
anchors.fill: parent
onClicked: {
root.triggered();
menu.done();
}
}
}

View file

@ -0,0 +1,6 @@
import QtQuick 2.5
Item {
width: 100
height: 20
}

View file

@ -0,0 +1,4 @@
import QtQuick 2.5
Item {
}

View file

@ -0,0 +1,42 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.0
import "../../qml/controls-uit"
import "../../qml/styles-uit"
import "../../qml/dialogs"
QtObject {
id: root
signal input(string text);
signal accepted;
signal rejected;
signal closing(var close)
property var titleWidth;
property var text;
property var prompt;
property var inputDialogBuilder: Component { QueryDialog { } }
function open() {
console.log("prompt text " + text)
console.log("prompt prompt " + prompt)
var dialog = inputDialogBuilder.createObject(desktop, {
label: root.text,
current: root.prompt
});
dialog.selected.connect(function(result){
root.input(dialog.result)
root.accepted();
dialog.destroy();
});
dialog.canceled.connect(function(){
root.rejected();
dialog.destroy();
});
}
}

View file

@ -0,0 +1,8 @@
module QtWebEngine.UIDelegates
AlertDialog 1.0 AlertDialog.qml
ConfirmDialog 1.0 ConfirmDialog.qml
FilePicker 1.0 FilePicker.qml
PromptDialog 1.0 PromptDialog.qml
Menu 1.0 Menu.qml
MenuItem 1.0 MenuItem.qml
MenuSeparator 1.0 MenuSeparator.qml

View file

@ -1,4 +1,4 @@
import QtQuick 2.3
import QtQuick 2.5
import QtQuick.Controls 1.2
import QtWebEngine 1.1
@ -16,6 +16,8 @@ ScrollingWindow {
destroyOnHidden: true
width: 800
height: 600
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
property alias url: webview.url
property alias webView: webview
x: 100
y: 100
@ -32,6 +34,19 @@ ScrollingWindow {
}
}
function showPermissionsBar(){
permissionsContainer.visible=true;
}
function hidePermissionsBar(){
permissionsContainer.visible=false;
}
function allowPermissions(){
webview.grantFeaturePermission(permissionsBar.securityOrigin, permissionsBar.feature, true);
hidePermissionsBar();
}
Item {
id:item
width: pane.contentWidth
@ -70,6 +85,7 @@ ScrollingWindow {
size: 48
MouseArea { anchors.fill: parent; onClicked: webview.goForward() }
}
}
Item {
@ -116,6 +132,7 @@ ScrollingWindow {
if (text.indexOf("http") != 0) {
text = "http://" + text
}
root.hidePermissionsBar();
webview.url = text
break;
}
@ -123,14 +140,76 @@ ScrollingWindow {
}
}
Rectangle {
id:permissionsContainer
visible:false
color: "#000000"
width: parent.width
anchors.top: buttons.bottom
height:40
z:100
gradient: Gradient {
GradientStop { position: 0.0; color: "black" }
GradientStop { position: 1.0; color: "grey" }
}
RalewayLight {
id: permissionsInfo
anchors.right:permissionsRow.left
anchors.rightMargin: 32
anchors.topMargin:8
anchors.top:parent.top
text: "This site wants to use your microphone/camera"
size: 18
color: hifi.colors.white
}
Row {
id: permissionsRow
spacing: 4
anchors.top:parent.top
anchors.topMargin: 8
anchors.right: parent.right
visible: true
z:101
Button {
id:allow
text: "Allow"
color: hifi.buttons.blue
colorScheme: root.colorScheme
width: 120
enabled: true
onClicked: root.allowPermissions();
z:101
}
Button {
id:block
text: "Block"
color: hifi.buttons.red
colorScheme: root.colorScheme
width: 120
enabled: true
onClicked: root.hidePermissionsBar();
z:101
}
}
}
WebEngineView {
id: webview
url: "http://highfidelity.com"
url: "https://highfidelity.com"
anchors.top: buttons.bottom
anchors.topMargin: 8
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
onFeaturePermissionRequested: {
permissionsBar.securityOrigin = securityOrigin;
permissionsBar.feature = feature;
root.showPermissionsBar();
}
onLoadingChanged: {
if (loadRequest.status === WebEngineView.LoadSucceededStatus) {
addressBar.text = loadRequest.url
@ -139,9 +218,12 @@ ScrollingWindow {
onIconChanged: {
console.log("New icon: " + icon)
}
onNewViewRequested:{
var component = Qt.createComponent("Browser.qml");
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView)
}
//profile: desktop.browserProfile
}
} // item
@ -157,4 +239,4 @@ ScrollingWindow {
break;
}
}
} // dialog
} // dialog

View file

@ -15,7 +15,7 @@ WebEngineView {
id: root
property var newUrl;
profile.httpUserAgent: "Mozilla/5.0 Chrome (HighFidelityInterface)"
profile.httpUserAgent: "Mozilla/5.0 Chrome/38.0 (HighFidelityInterface)"
Component.onCompleted: {
console.log("Connecting JS messaging to Hifi Logging")
@ -48,10 +48,6 @@ WebEngineView {
}
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onLoadingChanged: {
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {

View file

@ -720,7 +720,9 @@ void MyAvatar::saveData() {
_fullAvatarURLFromPreferences.toString());
settings.setValue("fullAvatarModelName", _fullAvatarModelName);
settings.setValue("animGraphURL", _animGraphUrl);
QUrl animGraphUrl = _prefOverrideAnimGraphUrl.get();
settings.setValue("animGraphURL", animGraphUrl);
settings.beginWriteArray("attachmentData");
for (int i = 0; i < _attachmentData.size(); i++) {
@ -833,7 +835,7 @@ void MyAvatar::loadData() {
_targetScale = loadSetting(settings, "scale", 1.0f);
setScale(glm::vec3(_targetScale));
_animGraphUrl = settings.value("animGraphURL", "").toString();
_prefOverrideAnimGraphUrl.set(QUrl(settings.value("animGraphURL", "").toString()));
_fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl();
_fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString();
@ -1412,21 +1414,55 @@ void MyAvatar::initHeadBones() {
}
}
QUrl MyAvatar::getAnimGraphOverrideUrl() const {
return _prefOverrideAnimGraphUrl.get();
}
void MyAvatar::setAnimGraphOverrideUrl(QUrl value) {
_prefOverrideAnimGraphUrl.set(value);
if (!value.isEmpty()) {
setAnimGraphUrl(value);
} else {
initAnimGraph();
}
}
QUrl MyAvatar::getAnimGraphUrl() const {
return _currentAnimGraphUrl.get();
}
void MyAvatar::setAnimGraphUrl(const QUrl& url) {
if (_animGraphUrl == url) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setAnimGraphUrl", Q_ARG(QUrl, url));
return;
}
if (_currentAnimGraphUrl.get() == url) {
return;
}
destroyAnimGraph();
_skeletonModel->reset(); // Why is this necessary? Without this, we crash in the next render.
_animGraphUrl = url;
initAnimGraph();
_currentAnimGraphUrl.set(url);
_rig->initAnimGraph(url);
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
}
void MyAvatar::initAnimGraph() {
auto graphUrl =_animGraphUrl.isEmpty() ?
QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json") :
QUrl(_animGraphUrl);
QUrl graphUrl;
if (!_prefOverrideAnimGraphUrl.get().isEmpty()) {
graphUrl = _prefOverrideAnimGraphUrl.get();
} else if (!_fstAnimGraphOverrideUrl.isEmpty()) {
graphUrl = _fstAnimGraphOverrideUrl;
} else {
graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json");
}
_rig->initAnimGraph(graphUrl);
_currentAnimGraphUrl.set(graphUrl);
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
@ -1444,6 +1480,7 @@ void MyAvatar::postUpdate(float deltaTime) {
if (_skeletonModel->initWhenReady(scene)) {
initHeadBones();
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
initAnimGraph();
}

View file

@ -292,8 +292,6 @@ public slots:
Q_INVOKABLE void updateMotionBehaviorFromMenu();
Q_INVOKABLE QUrl getAnimGraphUrl() const { return _animGraphUrl; }
void setEnableDebugDrawDefaultPose(bool isEnabled);
void setEnableDebugDrawAnimPose(bool isEnabled);
void setEnableDebugDrawPosition(bool isEnabled);
@ -303,7 +301,11 @@ public slots:
void setEnableMeshVisible(bool isEnabled);
void setUseAnimPreAndPostRotations(bool isEnabled);
void setEnableInverseKinematics(bool isEnabled);
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
QUrl getAnimGraphOverrideUrl() const; // thread-safe
void setAnimGraphOverrideUrl(QUrl value); // thread-safe
QUrl getAnimGraphUrl() const; // thread-safe
void setAnimGraphUrl(const QUrl& url); // thread-safe
glm::vec3 getPositionForAudio();
glm::quat getOrientationForAudio();
@ -407,7 +409,9 @@ private:
// Avatar Preferences
QUrl _fullAvatarURLFromPreferences;
QString _fullAvatarModelName;
QUrl _animGraphUrl {""};
ThreadSafeValueCache<QUrl> _currentAnimGraphUrl;
ThreadSafeValueCache<QUrl> _prefOverrideAnimGraphUrl;
QUrl _fstAnimGraphOverrideUrl;
bool _useSnapTurn { true };
bool _clearOverlayWhenMoving { true };

View file

@ -161,8 +161,8 @@ void setupPreferences() {
preferences->addPreference(preference);
}
{
auto getter = [=]()->QString { return myAvatar->getAnimGraphUrl().toString(); };
auto setter = [=](const QString& value) { myAvatar->setAnimGraphUrl(value); };
auto getter = [=]()->QString { return myAvatar->getAnimGraphOverrideUrl().toString(); };
auto setter = [=](const QString& value) { myAvatar->setAnimGraphOverrideUrl(QUrl(value)); };
auto preference = new EditPreference(AVATAR_TUNING, "Avatar animation JSON", getter, setter);
preference->setPlaceholderText("default");
preferences->addPreference(preference);

View file

@ -234,6 +234,29 @@ bool Overlays::editOverlay(unsigned int id, const QVariant& properties) {
return false;
}
bool Overlays::editOverlays(const QVariant& propertiesById) {
QVariantMap map = propertiesById.toMap();
bool success = true;
QWriteLocker lock(&_lock);
for (const auto& key : map.keys()) {
bool convertSuccess;
unsigned int id = key.toUInt(&convertSuccess);
if (!convertSuccess) {
success = false;
continue;
}
Overlay::Pointer thisOverlay = getOverlay(id);
if (!thisOverlay) {
success = false;
continue;
}
QVariant properties = map[key];
thisOverlay->setProperties(properties.toMap());
}
return success;
}
void Overlays::deleteOverlay(unsigned int id) {
Overlay::Pointer overlayToDelete;

View file

@ -89,6 +89,10 @@ public slots:
/// successful edit, if the input id is for an unknown overlay this function will have no effect
bool editOverlay(unsigned int id, const QVariant& properties);
/// edits an overlay updating only the included properties, will return the identified OverlayID in case of
/// successful edit, if the input id is for an unknown overlay this function will have no effect
bool editOverlays(const QVariant& propertiesById);
/// deletes a particle
void deleteOverlay(unsigned int id);

View file

@ -428,7 +428,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
// shift hips according to the _hipsOffset from the previous frame
float offsetLength = glm::length(_hipsOffset);
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
if (offsetLength > MIN_HIPS_OFFSET_LENGTH) {
if (offsetLength > MIN_HIPS_OFFSET_LENGTH && _hipsIndex >= 0) {
// but only if offset is long enough
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
if (_hipsParentIndex == -1) {
@ -861,7 +861,11 @@ void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skele
_hipsIndex = _skeleton->nameToJointIndex("Hips");
// also cache the _hipsParentIndex for later
_hipsParentIndex = _skeleton->getParentIndex(_hipsIndex);
if (_hipsIndex >= 0) {
_hipsParentIndex = _skeleton->getParentIndex(_hipsIndex);
} else {
_hipsParentIndex = -1;
}
} else {
clearConstraints();
_headIndex = -1;

View file

@ -203,8 +203,8 @@ bool AudioInjector::injectLocally() {
}
if (!success) {
// we never started so we are finished, call our stop method
stop();
// we never started so we are finished with local injection
finishLocalInjection();
}
return success;
@ -217,8 +217,15 @@ static const int64_t NEXT_FRAME_DELTA_IMMEDIATELY = 0;
qint64 writeStringToStream(const QString& string, QDataStream& stream) {
QByteArray data = string.toUtf8();
uint32_t length = data.length();
stream << static_cast<quint32>(length);
stream << data;
if (length == 0) {
stream << static_cast<quint32>(length);
} else {
// http://doc.qt.io/qt-5/datastreamformat.html
// QDataStream << QByteArray -
// If the byte array is null : 0xFFFFFFFF (quint32)
// Otherwise : the array size(quint32) followed by the array bytes, i.e.size bytes
stream << data;
}
return length + sizeof(uint32_t);
}
@ -232,7 +239,7 @@ int64_t AudioInjector::injectNextFrame() {
static int positionOptionOffset = -1;
static int volumeOptionOffset = -1;
static int audioDataOffset = -1;
if (!_currentPacket) {
if (_currentSendOffset < 0 ||
_currentSendOffset >= _audioData.size()) {
@ -270,7 +277,7 @@ int64_t AudioInjector::injectNextFrame() {
// current injectors don't use codecs, so pack in the unknown codec name
QString noCodecForInjectors("");
writeStringToStream(noCodecForInjectors, audioPacketStream);
writeStringToStream(noCodecForInjectors, audioPacketStream);
// pack stream identifier (a generated UUID)
audioPacketStream << QUuid::createUuid();
@ -301,7 +308,6 @@ int64_t AudioInjector::injectNextFrame() {
volumeOptionOffset = _currentPacket->pos();
quint8 volume = MAX_INJECTOR_VOLUME;
audioPacketStream << volume;
audioPacketStream << _options.ignorePenumbra;
audioDataOffset = _currentPacket->pos();
@ -312,7 +318,6 @@ int64_t AudioInjector::injectNextFrame() {
return NEXT_FRAME_DELTA_ERROR_OR_FINISHED;
}
}
if (!_frameTimer->isValid()) {
// in the case where we have been restarted, the frame timer will be invalid and we need to start it back over here
_frameTimer->restart();
@ -418,7 +423,7 @@ void AudioInjector::triggerDeleteAfterFinish() {
return;
}
if (_state == AudioInjectorState::Finished) {
if (stateHas(AudioInjectorState::Finished)) {
stopAndDeleteLater();
} else {
_state |= AudioInjectorState::PendingDelete;
@ -484,23 +489,17 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj
// setup parameters required for injection
injector->setupInjection();
// we always inject locally
//
if (!injector->injectLocally()) {
// failed, so don't bother sending to server
qDebug() << "AudioInjector::playSound failed to inject locally";
return nullptr;
}
// we always inject locally, except when there is no localInterface
injector->injectLocally();
// if localOnly, we are done, just return injector.
if (options.localOnly) {
return injector;
}
if (!options.localOnly) {
// send off to server for everyone else
if (!injectorManager->threadInjector(injector)) {
// we failed to thread the new injector (we are at the max number of injector threads)
qDebug() << "AudioInjector::playSound failed to thread injector";
// send off to server for everyone else
if (!injectorManager->threadInjector(injector)) {
// we failed to thread the new injector (we are at the max number of injector threads)
qDebug() << "AudioInjector::playSound failed to thread injector";
}
}
return injector;
}

View file

@ -256,6 +256,8 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
destinationBuffer += _headData->_blendshapeCoefficients.size() * sizeof(float);
}
QReadLocker readLock(&_jointDataLock);
// joint rotation data
*destinationBuffer++ = _jointData.size();
unsigned char* validityPosition = destinationBuffer;
@ -378,6 +380,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
void AvatarData::doneEncoding(bool cullSmallChanges) {
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
QReadLocker readLock(&_jointDataLock);
_lastSentJointData.resize(_jointData.size());
for (int i = 0; i < _jointData.size(); i ++) {
const JointData& data = _jointData[ i ];
@ -551,8 +554,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
PACKET_READ_CHECK(NumJoints, sizeof(uint8_t));
int numJoints = *sourceBuffer++;
_jointData.resize(numJoints);
const int bytesOfValidity = (int)ceil((float)numJoints / (float)BITS_IN_BYTE);
PACKET_READ_CHECK(JointRotationValidityBits, bytesOfValidity);
@ -576,6 +577,9 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
}
// each joint rotation is stored in 6 bytes.
QWriteLocker writeLock(&_jointDataLock);
_jointData.resize(numJoints);
const int COMPRESSED_QUATERNION_SIZE = 6;
PACKET_READ_CHECK(JointRotations, numValidJointRotations * COMPRESSED_QUATERNION_SIZE);
for (int i = 0; i < numJoints; i++) {
@ -653,6 +657,7 @@ void AvatarData::setRawJointData(QVector<JointData> data) {
QMetaObject::invokeMethod(this, "setRawJointData", Q_ARG(QVector<JointData>, data));
return;
}
QWriteLocker writeLock(&_jointDataLock);
_jointData = data;
}
@ -664,6 +669,7 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
QMetaObject::invokeMethod(this, "setJointData", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
return;
}
QWriteLocker writeLock(&_jointDataLock);
if (_jointData.size() <= index) {
_jointData.resize(index + 1);
}
@ -682,6 +688,8 @@ void AvatarData::clearJointData(int index) {
QMetaObject::invokeMethod(this, "clearJointData", Q_ARG(int, index));
return;
}
QWriteLocker writeLock(&_jointDataLock);
// FIXME: I don't understand how this "clears" the joint data at index
if (_jointData.size() <= index) {
_jointData.resize(index + 1);
}
@ -710,6 +718,7 @@ glm::quat AvatarData::getJointRotation(int index) const {
Q_RETURN_ARG(glm::quat, result), Q_ARG(int, index));
return result;
}
QReadLocker readLock(&_jointDataLock);
return index < _jointData.size() ? _jointData.at(index).rotation : glm::quat();
}
@ -724,6 +733,7 @@ glm::vec3 AvatarData::getJointTranslation(int index) const {
Q_RETURN_ARG(glm::vec3, result), Q_ARG(int, index));
return result;
}
QReadLocker readLock(&_jointDataLock);
return index < _jointData.size() ? _jointData.at(index).translation : glm::vec3();
}
@ -771,6 +781,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
QMetaObject::invokeMethod(this, "setJointRotation", Q_ARG(int, index), Q_ARG(const glm::quat&, rotation));
return;
}
QWriteLocker writeLock(&_jointDataLock);
if (_jointData.size() <= index) {
_jointData.resize(index + 1);
}
@ -787,6 +798,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
QMetaObject::invokeMethod(this, "setJointTranslation", Q_ARG(int, index), Q_ARG(const glm::vec3&, translation));
return;
}
QWriteLocker writeLock(&_jointDataLock);
if (_jointData.size() <= index) {
_jointData.resize(index + 1);
}
@ -831,6 +843,7 @@ QVector<glm::quat> AvatarData::getJointRotations() const {
Q_RETURN_ARG(QVector<glm::quat>, result));
return result;
}
QReadLocker readLock(&_jointDataLock);
QVector<glm::quat> jointRotations(_jointData.size());
for (int i = 0; i < _jointData.size(); ++i) {
jointRotations[i] = _jointData[i].rotation;
@ -845,6 +858,7 @@ void AvatarData::setJointRotations(QVector<glm::quat> jointRotations) {
"setJointRotations", Qt::BlockingQueuedConnection,
Q_ARG(QVector<glm::quat>, jointRotations));
}
QWriteLocker writeLock(&_jointDataLock);
if (_jointData.size() < jointRotations.size()) {
_jointData.resize(jointRotations.size());
}
@ -862,6 +876,7 @@ void AvatarData::setJointTranslations(QVector<glm::vec3> jointTranslations) {
"setJointTranslations", Qt::BlockingQueuedConnection,
Q_ARG(QVector<glm::vec3>, jointTranslations));
}
QWriteLocker writeLock(&_jointDataLock);
if (_jointData.size() < jointTranslations.size()) {
_jointData.resize(jointTranslations.size());
}
@ -873,11 +888,23 @@ void AvatarData::setJointTranslations(QVector<glm::vec3> jointTranslations) {
}
void AvatarData::clearJointsData() {
// FIXME: this method is terribly inefficient and probably doesn't even work
// (see implementation of clearJointData(index))
for (int i = 0; i < _jointData.size(); ++i) {
clearJointData(i);
}
}
int AvatarData::getJointIndex(const QString& name) const {
QReadLocker readLock(&_jointDataLock);
return _jointIndices.value(name) - 1;
}
QStringList AvatarData::getJointNames() const {
QReadLocker readLock(&_jointDataLock);
return _jointNames;
}
void AvatarData::parseAvatarIdentityPacket(const QByteArray& data, Identity& identityOut) {
QDataStream packetStream(data);
@ -1027,38 +1054,41 @@ void AvatarData::detachAll(const QString& modelURL, const QString& jointName) {
void AvatarData::setJointMappingsFromNetworkReply() {
QNetworkReply* networkReply = static_cast<QNetworkReply*>(sender());
QByteArray line;
while (!(line = networkReply->readLine()).isEmpty()) {
line = line.trimmed();
if (line.startsWith("filename")) {
int filenameIndex = line.indexOf('=') + 1;
if (filenameIndex > 0) {
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
{
QWriteLocker writeLock(&_jointDataLock);
QByteArray line;
while (!(line = networkReply->readLine()).isEmpty()) {
line = line.trimmed();
if (line.startsWith("filename")) {
int filenameIndex = line.indexOf('=') + 1;
if (filenameIndex > 0) {
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
}
}
if (!line.startsWith("jointIndex")) {
continue;
}
if (!line.startsWith("jointIndex")) {
continue;
}
int jointNameIndex = line.indexOf('=') + 1;
if (jointNameIndex == 0) {
continue;
}
int secondSeparatorIndex = line.indexOf('=', jointNameIndex);
if (secondSeparatorIndex == -1) {
continue;
}
QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed();
bool ok;
int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok);
if (ok) {
while (_jointNames.size() < jointIndex + 1) {
_jointNames.append(QString());
int jointNameIndex = line.indexOf('=') + 1;
if (jointNameIndex == 0) {
continue;
}
int secondSeparatorIndex = line.indexOf('=', jointNameIndex);
if (secondSeparatorIndex == -1) {
continue;
}
QString jointName = line.mid(jointNameIndex, secondSeparatorIndex - jointNameIndex).trimmed();
bool ok;
int jointIndex = line.mid(secondSeparatorIndex + 1).trimmed().toInt(&ok);
if (ok) {
while (_jointNames.size() < jointIndex + 1) {
_jointNames.append(QString());
}
_jointNames[jointIndex] = jointName;
}
_jointNames[jointIndex] = jointName;
}
}
for (int i = 0; i < _jointNames.size(); i++) {
_jointIndices.insert(_jointNames.at(i), i + 1);
for (int i = 0; i < _jointNames.size(); i++) {
_jointIndices.insert(_jointNames.at(i), i + 1);
}
}
networkReply->deleteLater();
@ -1101,16 +1131,19 @@ void AvatarData::sendIdentityPacket() {
}
void AvatarData::updateJointMappings() {
_jointIndices.clear();
_jointNames.clear();
_jointData.clear();
{
QWriteLocker writeLock(&_jointDataLock);
_jointIndices.clear();
_jointNames.clear();
_jointData.clear();
}
if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
connect(networkReply, SIGNAL(finished()), this, SLOT(setJointMappingsFromNetworkReply()));
connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply);
}
}

View file

@ -271,9 +271,9 @@ public:
Q_INVOKABLE virtual void clearJointsData();
/// Returns the index of the joint with the specified name, or -1 if not found/unknown.
Q_INVOKABLE virtual int getJointIndex(const QString& name) const { return _jointIndices.value(name) - 1; }
Q_INVOKABLE virtual int getJointIndex(const QString& name) const;
Q_INVOKABLE virtual QStringList getJointNames() const { return _jointNames; }
Q_INVOKABLE virtual QStringList getJointNames() const;
Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); }
@ -374,6 +374,7 @@ protected:
QVector<JointData> _jointData; ///< the state of the skeleton joints
QVector<JointData> _lastSentJointData; ///< the state of the skeleton joints last time we transmitted
mutable QReadWriteLock _jointDataLock;
// key state
KeyState _keyState;

View file

@ -113,10 +113,12 @@ void HmdDisplayPlugin::customizeContext() {
updateReprojectionProgram();
updateOverlayProgram();
#ifdef HMD_HAND_LASER_SUPPORT
updateLaserProgram();
_laserGeometry = loadLaser(_laserProgram);
#endif
}
//#define LIVE_SHADER_RELOAD 1
static QString readFile(const QString& filename) {
@ -162,6 +164,7 @@ void HmdDisplayPlugin::updateReprojectionProgram() {
}
#ifdef HMD_HAND_LASER_SUPPORT
void HmdDisplayPlugin::updateLaserProgram() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert";
static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom";
@ -202,6 +205,7 @@ void HmdDisplayPlugin::updateLaserProgram() {
}
}
}
#endif
void HmdDisplayPlugin::updateOverlayProgram() {
static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert";
@ -249,8 +253,10 @@ void HmdDisplayPlugin::uncustomizeContext() {
_compositeFramebuffer.reset();
_previewProgram.reset();
_reprojectionProgram.reset();
#ifdef HMD_HAND_LASER_SUPPORT
_laserProgram.reset();
_laserGeometry.reset();
#endif
Parent::uncustomizeContext();
}
@ -516,6 +522,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve
}
void HmdDisplayPlugin::compositeExtra() {
#ifdef HMD_HAND_LASER_SUPPORT
// If neither hand laser is activated, exit
if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) {
return;
@ -584,4 +591,5 @@ void HmdDisplayPlugin::compositeExtra() {
}
});
glDisable(GL_BLEND);
#endif
}

View file

@ -14,6 +14,10 @@
#include "../OpenGLDisplayPlugin.h"
#ifdef Q_OS_WIN
#define HMD_HAND_LASER_SUPPORT
#endif
class HmdDisplayPlugin : public OpenGLDisplayPlugin {
using Parent = OpenGLDisplayPlugin;
public:
@ -93,7 +97,9 @@ protected:
private:
void updateOverlayProgram();
#ifdef HMD_HAND_LASER_SUPPORT
void updateLaserProgram();
#endif
void updateReprojectionProgram();
bool _enablePreview { false };
@ -130,11 +136,13 @@ private:
ShapeWrapperPtr _sphereSection;
#ifdef HMD_HAND_LASER_SUPPORT
ProgramPtr _laserProgram;
struct LaserUniforms {
int32_t mvp { -1 };
int32_t color { -1 };
} _laserUniforms;
ShapeWrapperPtr _laserGeometry;
#endif
};

View file

@ -24,6 +24,7 @@
#include <DependencyManager.h>
#include <NumericalConstants.h>
#include <Finally.h>
#include <PathUtils.h>
#include "OffscreenGLCanvas.h"
#include "GLEscrow.h"
@ -400,6 +401,10 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
// Create a QML engine.
_qmlEngine = new QQmlEngine;
auto importList = _qmlEngine->importPathList();
importList.insert(importList.begin(), PathUtils::resourcesPath());
_qmlEngine->setImportPathList(importList);
if (!_qmlEngine->incubationController()) {
_qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController());
}

View file

@ -67,6 +67,18 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
_textureBaseUrl = resolveTextureBaseUrl(url, _url.resolved(texdir));
}
auto animGraphVariant = mapping.value("animGraphUrl");
if (animGraphVariant.isValid()) {
QUrl fstUrl(animGraphVariant.toString());
if (fstUrl.isValid()) {
_animGraphOverrideUrl = _url.resolved(fstUrl);
} else {
_animGraphOverrideUrl = QUrl();
}
} else {
_animGraphOverrideUrl = QUrl();
}
auto modelCache = DependencyManager::get<ModelCache>();
GeometryExtra extra{ mapping, _textureBaseUrl };
@ -284,6 +296,8 @@ Geometry::Geometry(const Geometry& geometry) {
for (const auto& material : geometry._materials) {
_materials.push_back(std::make_shared<NetworkMaterial>(*material));
}
_animGraphOverrideUrl = geometry._animGraphOverrideUrl;
}
void Geometry::setTextures(const QVariantMap& textureMap) {

View file

@ -52,6 +52,7 @@ public:
void setTextures(const QVariantMap& textureMap);
virtual bool areTexturesLoaded() const;
const QUrl& getAnimGraphOverrideUrl() const { return _animGraphOverrideUrl; }
protected:
friend class GeometryMappingResource;
@ -64,6 +65,8 @@ protected:
// Copied to each geometry, mutable throughout lifetime via setTextures
NetworkMaterials _materials;
QUrl _animGraphOverrideUrl;
private:
mutable bool _areTexturesLoaded { false };
};

View file

@ -53,7 +53,6 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact));
multipart->append(detailsPart);
}
qCDebug(networking) << "Logging activity" << action;
// if no callbacks specified, call our owns
if (params.isEmpty()) {

View file

@ -1552,6 +1552,12 @@ void GeometryCache::renderLine(gpu::Batch& batch, const glm::vec2& p1, const glm
void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2,
const glm::vec4& color, float glowIntensity, float glowWidth, int id) {
// Disable glow lines on OSX
#ifndef Q_OS_WIN
glowIntensity = 0.0f;
#endif
if (glowIntensity <= 0) {
renderLine(batch, p1, p2, color, id);
return;

View file

@ -1192,6 +1192,8 @@ void Model::deleteGeometry() {
_meshStates.clear();
_rig->destroyAnimGraph();
_blendedBlendshapeCoefficients.clear();
_renderGeometry.reset();
_collisionGeometry.reset();
}
AABox Model::getRenderableMeshBound() const {

View file

@ -20,7 +20,7 @@ struct Grid {
};
uniform gridBuffer { Grid grid; };
Grid getGrid() { return grid; };
Grid getGrid() { return grid; }
in vec2 varTexCoord0;
in vec4 varColor;

View file

@ -108,3 +108,10 @@ QStringList FileDialogHelper::drives() {
void FileDialogHelper::openDirectory(const QString& path) {
QDesktopServices::openUrl(path);
}
QList<QUrl> FileDialogHelper::urlToList(const QUrl& url) {
QList<QUrl> results;
results.push_back(url);
return results;
}

View file

@ -58,6 +58,7 @@ public:
Q_INVOKABLE bool validFolder(const QString& path);
Q_INVOKABLE QUrl pathToUrl(const QString& path);
Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters);
Q_INVOKABLE QList<QUrl> urlToList(const QUrl& url);
Q_INVOKABLE void openDirectory(const QString& path);
};

File diff suppressed because it is too large Load diff

View file

@ -45,6 +45,19 @@ var TARGET_MODEL_DIMENSIONS = {
};
var COLORS_TELEPORT_CAN_TELEPORT = {
red: 97,
green: 247,
blue: 255
}
var COLORS_TELEPORT_CANNOT_TELEPORT = {
red: 0,
green: 121,
blue: 141
};
function ThumbPad(hand) {
this.hand = hand;
var _thisPad = this;
@ -269,17 +282,13 @@ function Teleporter() {
this.rightPickRay = rightPickRay;
var location = Vec3.sum(rightPickRay.origin, Vec3.multiply(rightPickRay.direction, 500));
var location = Vec3.sum(rightPickRay.origin, Vec3.multiply(rightPickRay.direction, 50));
var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]);
if (rightIntersection.intersects) {
this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, {
red: 7,
green: 36,
blue: 44
});
this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT);
if (this.targetOverlay !== null) {
this.updateTargetOverlay(rightIntersection);
} else {
@ -289,11 +298,7 @@ function Teleporter() {
} else {
this.deleteTargetOverlay();
this.rightLineOn(rightPickRay.origin, location, {
red: 7,
green: 36,
blue: 44
});
this.rightLineOn(rightPickRay.origin, location, COLORS_TELEPORT_CANNOT_TELEPORT);
}
}
@ -318,18 +323,14 @@ function Teleporter() {
this.leftPickRay = leftPickRay;
var location = Vec3.sum(MyAvatar.position, Vec3.multiply(leftPickRay.direction, 500));
var location = Vec3.sum(MyAvatar.position, Vec3.multiply(leftPickRay.direction, 50));
var leftIntersection = Entities.findRayIntersection(teleporter.leftPickRay, true, [], [this.targetEntity]);
if (leftIntersection.intersects) {
this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, {
red: 7,
green: 36,
blue: 44
});
this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT);
if (this.targetOverlay !== null) {
this.updateTargetOverlay(leftIntersection);
} else {
@ -339,13 +340,8 @@ function Teleporter() {
} else {
this.deleteTargetOverlay();
this.leftLineOn(leftPickRay.origin, location, {
red: 7,
green: 36,
blue: 44
});
this.leftLineOn(leftPickRay.origin, location, COLORS_TELEPORT_CANNOT_TELEPORT);
}
};
@ -622,4 +618,4 @@ function cleanup() {
if (teleporter.updateConnected !== null) {
Script.update.disconnect(teleporter.update);
}
}
}

View file

@ -39,6 +39,7 @@
#include <gpu/gl/GLFramebuffer.h>
#include <gpu/gl/GLTexture.h>
#include <WebEntityItem.h>
#include <OctreeUtils.h>
#include <render/Engine.h>
#include <Model.h>
@ -143,6 +144,19 @@ public:
}
};
static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits<size_t>::max()) {
static const std::vector<QString> SUFFIXES{ { "B", "KB", "MB", "GB", "TB", "PB" } };
const size_t maxIndex = std::min(maxUnit, SUFFIXES.size() - 1);
size_t suffixIndex = 0;
while (suffixIndex < maxIndex && size > 1024) {
size >>= 10;
++suffixIndex;
}
return QString("%1 %2").arg(size).arg(SUFFIXES[suffixIndex]);
}
// Create a simple OpenGL window that renders text in various ways
@ -211,6 +225,9 @@ public:
AbstractViewStateInterface::setInstance(this);
_octree = DependencyManager::set<EntityTreeRenderer>(false, this, nullptr);
_octree->init();
// Prevent web entities from rendering
REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory)
DependencyManager::set<ParentFinder>(_octree->getTree());
getEntities()->setViewFrustum(_viewFrustum);
auto nodeList = DependencyManager::get<LimitedNodeList>();
@ -296,6 +313,10 @@ protected:
reloadScene();
return;
case Qt::Key_F4:
toggleStereo();
return;
case Qt::Key_F5:
goTo();
return;
@ -365,6 +386,17 @@ private:
renderArgs.setViewFrustum(_viewFrustum);
renderArgs._context->enableStereo(_stereoEnabled);
if (_stereoEnabled) {
mat4 eyeOffsets[2];
mat4 eyeProjections[2];
for (size_t i = 0; i < 2; ++i) {
eyeProjections[i] = _viewFrustum.getProjection();
}
renderArgs._context->setStereoProjections(eyeProjections);
renderArgs._context->setStereoViews(eyeOffsets);
}
// Final framebuffer that will be handled to the display-plugin
{
auto finalFramebuffer = framebufferCache->getFramebuffer();
@ -388,7 +420,7 @@ private:
}
{
_textOverlay->render();
//_textOverlay->render();
}
_context.swapBuffers(this);
@ -429,6 +461,9 @@ private:
const qint64& now;
};
void updateText() {
//qDebug() << "FPS " << fps.rate();
{
@ -438,6 +473,11 @@ private:
infoTextBlock.push_back({ vec2(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft });
infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight });
infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft });
setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4")
.arg(_fps).arg(_cullingEnabled)
.arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2))
.arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2)));
}
_textOverlay->beginTextUpdate();
@ -561,14 +601,29 @@ private:
void importScene(const QString& fileName) {
auto assetClient = DependencyManager::get<AssetClient>();
QFileInfo fileInfo(fileName);
//assetClient->loadLocalMappings(fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".atp");
QString atpPath = fileInfo.absolutePath() + "/" + fileInfo.baseName() + ".atp";
qDebug() << atpPath;
QFileInfo atpPathInfo(atpPath);
if (atpPathInfo.exists()) {
QString atpUrl = QUrl::fromLocalFile(atpPath).toString();
ResourceManager::setUrlPrefixOverride("atp:/", atpUrl + "/");
}
_settings.setValue(LAST_SCENE_KEY, fileName);
_octree->clear();
_octree->getTree()->readFromURL(fileName);
}
void importScene() {
QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), "/home", tr("Hifi Exports (*.json *.svo)"));
auto lastScene = _settings.value(LAST_SCENE_KEY);
QString openDir;
if (lastScene.isValid()) {
QFileInfo lastSceneInfo(lastScene.toString());
if (lastSceneInfo.absoluteDir().exists()) {
openDir = lastSceneInfo.absolutePath();
}
}
QString fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), openDir, tr("Hifi Exports (*.json *.svo)"));
if (fileName.isNull()) {
return;
}
@ -617,6 +672,10 @@ private:
_cullingEnabled = !_cullingEnabled;
}
void toggleStereo() {
_stereoEnabled = !_stereoEnabled;
}
QSharedPointer<EntityTreeRenderer> getEntities() {
return _octree;
}
@ -665,6 +724,7 @@ private:
float _fps { 0 };
TextOverlay* _textOverlay;
bool _cullingEnabled { true };
bool _stereoEnabled { false };
QSharedPointer<EntityTreeRenderer> _octree;
};

View file

@ -13,6 +13,7 @@ import "../../../interface/resources/qml/styles-uit"
ApplicationWindow {
id: appWindow
objectName: "MainWindow"
visible: true
width: 1280
height: 800
@ -93,9 +94,6 @@ ApplicationWindow {
onClicked: testButtons.lastButton.visible = !testButtons.lastButton.visible
}
// Error alerts
/*
Button {
@ -350,6 +348,11 @@ ApplicationWindow {
}
*/
Browser {
url: "http://s3.amazonaws.com/DreamingContent/testUiDelegates.html"
}
Window {
id: blue
closable: true

View file

@ -16,6 +16,8 @@ QML_IMPORT_PATH = ../../interface/resources/qml
DISTFILES += \
qml/*.qml \
../../interface/resources/QtWebEngine/UIDelegates/original/*.qml \
../../interface/resources/QtWebEngine/UIDelegates/*.qml \
../../interface/resources/qml/*.qml \
../../interface/resources/qml/controls/*.qml \
../../interface/resources/qml/controls-uit/*.qml \

View file

@ -33,6 +33,28 @@ protected:
const QString _name;
};
class Reticle : public QObject {
Q_OBJECT
Q_PROPERTY(QPoint position READ getPosition CONSTANT)
public:
Reticle(QObject* parent) : QObject(parent) {
}
QPoint getPosition() {
if (!_window) {
return QPoint(0, 0);
}
return _window->mapFromGlobal(QCursor::pos());
}
void setWindow(QWindow* window) {
_window = window;
}
private:
QWindow* _window{nullptr};
};
QString getRelativeDir(const QString& relativePath = ".") {
QDir path(__FILE__); path.cdUp();
@ -61,10 +83,8 @@ void setChild(QQmlApplicationEngine& engine, const char* name) {
qWarning() << "Could not find object named " << name;
}
void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath) {
QString resolvedPath = getRelativeDir("../qml");
QUrl resolvedUrl = QUrl::fromLocalFile(resolvedPath);
resolvedPath = resolvedUrl.toString();
void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath, bool insert = false) {
QString resolvedPath = getRelativeDir(relativePath);
engine.addImportPath(resolvedPath);
}
@ -79,8 +99,9 @@ int main(int argc, char *argv[]) {
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
QQmlApplicationEngine engine;
addImportPath(engine, "../qml");
addImportPath(engine, "../../../interface/resources/qml");
addImportPath(engine, "qml");
addImportPath(engine, "../../interface/resources/qml");
addImportPath(engine, "../../interface/resources");
engine.load(QUrl(QStringLiteral("qml/Stubs.qml")));
setChild(engine, "offscreenFlags");
@ -99,6 +120,15 @@ int main(int argc, char *argv[]) {
//engine.load(QUrl(QStringLiteral("qrc:/qml/gallery/main.qml")));
engine.load(QUrl(QStringLiteral("qml/main.qml")));
for (QObject* rootObject : engine.rootObjects()) {
if (rootObject->objectName() == "MainWindow") {
Reticle* reticle = new Reticle(rootObject);
reticle->setWindow((QWindow*)rootObject);
engine.rootContext()->setContextProperty("Reticle", reticle);
engine.rootContext()->setContextProperty("Window", rootObject);
break;
}
}
return app.exec();
}