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

This commit is contained in:
ZappoMan 2018-01-18 15:08:27 -08:00
commit 8364f64d4d
346 changed files with 6771 additions and 3537 deletions
android/app
assignment-client
interface
libraries

View file

@ -11,7 +11,7 @@ link_hifi_libraries(
physics
audio audio-client
ui midi controllers pointers
model model-networking fbx animation
graphics model-networking fbx animation
entities entities-renderer
avatars avatars-renderer
ui-plugins input-plugins

View file

@ -11,7 +11,7 @@ setup_memory_debugger()
# link in the shared libraries
link_hifi_libraries(
audio avatars octree gpu model fbx entities
audio avatars octree gpu graphics fbx entities
networking animation recording shared script-engine embedded-webserver
controllers physics plugins midi image
)

View file

@ -343,7 +343,6 @@ void Agent::scriptRequestFinished() {
void Agent::executeScript() {
_scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload);
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine);
@ -439,7 +438,7 @@ void Agent::executeScript() {
encodedBuffer = audio;
}
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber,
AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false,
audioTransform, scriptedAvatar->getWorldPosition(), glm::vec3(0),
packetType, _selectedCodecName);
});

View file

@ -275,17 +275,28 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
if (micStreamIt == _audioStreams.end()) {
// we don't have a mic stream yet, so add it
// read the channel flag to see if our stream is stereo or not
// hop past the sequence number that leads the packet
message.seek(sizeof(quint16));
quint8 channelFlag;
message.readPrimitive(&channelFlag);
// pull the codec string from the packet
auto codecString = message.readString();
bool isStereo = channelFlag == 1;
// determine if the stream is stereo or not
bool isStereo;
if (packetType == PacketType::SilentAudioFrame
|| packetType == PacketType::ReplicatedSilentAudioFrame) {
quint16 numSilentSamples;
message.readPrimitive(&numSilentSamples);
isStereo = numSilentSamples == AudioConstants::NETWORK_FRAME_SAMPLES_STEREO;
} else {
quint8 channelFlag;
message.readPrimitive(&channelFlag);
isStereo = channelFlag == 1;
}
auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames());
avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO);
qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName;
avatarAudioStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec,
this, &AudioMixerClientData::handleMismatchAudioFormat);
@ -324,7 +335,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) {
#if INJECTORS_SUPPORT_CODECS
injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName;
qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
#endif
auto emplaced = _audioStreams.emplace(
@ -567,7 +578,8 @@ void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& c
auto avatarAudioStream = getAvatarAudioStream();
if (avatarAudioStream) {
avatarAudioStream->setupCodec(codec, codecName, AudioConstants::MONO);
avatarAudioStream->setupCodec(codec, codecName, avatarAudioStream->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO);
qCDebug(audio) << "setting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << avatarAudioStream->isStereo();
}
#if INJECTORS_SUPPORT_CODECS

View file

@ -11,6 +11,7 @@
#include <udt/PacketHeaders.h>
#include "AudioLogging.h"
#include "AvatarAudioStream.h"
AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) :
@ -41,6 +42,15 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray&
_ringBuffer.resizeForFrameSize(isStereo
? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
// restart the codec
if (_codec) {
if (_decoder) {
_codec->releaseDecoder(_decoder);
}
_decoder = _codec->createDecoder(AudioConstants::SAMPLE_RATE, isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
}
qCDebug(audio) << "resetting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo;
_isStereo = isStereo;
}

View file

@ -116,8 +116,9 @@ public:
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time);
QVector<JointData>& getLastOtherAvatarSentJoints(QUuid otherAvatar) {
_lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount());
return _lastOtherAvatarSentJoints[otherAvatar];
auto& lastOtherAvatarSentJoints = _lastOtherAvatarSentJoints[otherAvatar];
lastOtherAvatarSentJoints.resize(_avatar->getJointCount());
return lastOtherAvatarSentJoints;
}
void queuePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);

View file

@ -201,7 +201,7 @@ endif()
# link required hifi libraries
link_hifi_libraries(
shared octree ktx gpu gl procedural model render
shared octree ktx gpu gl procedural graphics render
pointers
recording fbx networking model-networking entities avatars trackers
audio audio-client animation script-engine physics

View file

@ -0,0 +1,642 @@
//
// ScriptAPI.qml
//
// Created by Luis Cuenca on 12/18/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.5
import QtQuick.Controls 1.4
import "styles-uit"
import "controls-uit" as HifiControls
Item {
id: root
width: parent.width
height: parent.height
property var hideQtMethods: true
property var maxUpdateValues: 20
property var maxReloadValues: 200
property var apiMembers: []
property var membersWithValues: []
property var isReloading: false
property var evaluatingIdx: -1
property Component scrollSlider
property Component keyboard
Rectangle {
color: "white"
width: parent.width
height: parent.height
}
FontLoader { id: ralewayRegular; source: pathToFonts + "fonts/Raleway-Regular.ttf"; }
Timer {
id: updateList
interval: 200
repeat: false
running: false
onTriggered: {
scrollSlider.y = 0;
list.contentY = 0;
}
}
Row {
id: topBar
anchors.left: parent.left
anchors.leftMargin: 8
width: parent.width
height: 50
HifiControls.GlyphButton {
id: search
enabled: true
glyph: hifi.glyphs.search
color: hifi.colors.text
size: 48
width: 50
height: 50
onClicked: {
addListElements(searchBar.text);
focus = true;
}
}
HifiControls.GlyphButton {
id: back;
enabled: true;
glyph: hifi.glyphs.backward
color: hifi.colors.text
size: 48
width: 30
height: 50
anchors.margins: 2
onClicked: {
var text = searchBar.text;
var chain = text.split(".");
if (chain.length > 2) {
var result = chain[0]+".";
for (var i = 1; i < chain.length-2; i++) {
result += chain[i] + ".";
}
result += chain[chain.length-2];
searchBar.text = result;
} else {
searchBar.text = (chain.length > 1) ? chain[0] : "";
}
if (chain.length > 1) {
addListElements(searchBar.text);
} else {
addListElements();
}
focus = true;
}
}
TextField {
id: searchBar
focus: true
font.pixelSize: 16
width: 2*(parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3
height: parent.height
font.family: ralewayRegular.name
placeholderText: "Search"
onAccepted: {
console.log("Enter Pressed");
search.clicked();
}
onActiveFocusChanged: {
if (activeFocus && HMD.mounted) {
keyboard.raised = true;
} else {
keyboard.raised = false;
}
}
}
HifiControls.Button {
id: addMember;
enabled: true;
text: "+"
width: 50
height: 50
anchors.margins: 2
onClicked: {
addNewMember();
updateList.start();
focus = true;
}
}
HifiControls.Button {
id: evaluate;
enabled: true;
text: "Eval"
width: 50
height: 50
anchors.margins: 2
onClicked: {
evaluateMember();
focus = true;
}
}
TextField {
id: valueBar
focus: true
font.pixelSize: 16
width: (parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3
height: parent.height
font.family: ralewayRegular.name
placeholderText: "Value"
textColor: "#4466DD"
anchors.margins: 2
}
HifiControls.GlyphButton {
id: reload;
enabled: false;
glyph: hifi.glyphs.reload
color: hifi.colors.text
size: 48
width: 50
height: 50
anchors.margins: 2
onClicked: {
reloadListValues();
focus = true;
}
}
HifiControls.GlyphButton {
id: update;
enabled: false;
glyph: hifi.glyphs.playback_play
size: 48
width: 50
height: 50
anchors.margins: 2
onClicked: {
if (isReloading) {
update.glyph = hifi.glyphs.playback_play
isReloading = false;
stopReload();
} else {
update.glyph = hifi.glyphs.stop_square
isReloading = true;
startReload();
}
focus = true;
}
}
}
ListModel {
id: memberModel
}
Component {
id: memberDelegate
Row {
id: memberRow
property var isMainKey: apiType === "class";
spacing: 10
Rectangle {
width: isMainKey ? 20 : 40;
height: parent.height
}
RalewayRegular {
text: apiMember
size: !isMainKey ? 16 : 22
MouseArea {
width: list.width
height: parent.height
onClicked: {
searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember;
valueBar.text = !apiValue ? "" : apiValue;
list.currentIndex = index;
evaluatingIdx = index;
}
onDoubleClicked: {
if (apiType === "class") {
addListElements(apiMember+".");
} else {
isolateElement(evaluatingIdx);
}
}
}
}
RalewayRegular {
text: apiType
size: 14
color: hifi.colors.baseGrayHighlight
}
RalewayRegular {
text: !apiValue ? "" : apiValue;
size: 16
color: "#4466DD"
}
}
}
Rectangle {
id: membersBackground
anchors {
left: parent.left; right: parent.right; top: topBar.bottom; bottom: parent.bottom;
margins: hifi.dimensions.contentMargin.x
bottomMargin: hifi.dimensions.contentSpacing.y + 40
}
color: "white"
radius: 4
ListView {
id: list
anchors {
top: parent.top
left: parent.left
right: scrollBar.left
bottom: parent.bottom
margins: 4
}
clip: true
cacheBuffer: 4000
model: memberModel
delegate: memberDelegate
highlightMoveDuration: 0
highlight: Rectangle {
anchors {
left: parent ? parent.left : undefined
right: parent ? parent.right : undefined
leftMargin: hifi.dimensions.borderWidth
rightMargin: hifi.dimensions.borderWidth
}
color: "#BBDDFF"
}
onMovementStarted: {
scrollSlider.manual = true;
}
onMovementEnded: {
if (list.contentHeight > list.height) {
var range = list.contentY/(list.contentHeight-list.height);
range = range > 1 ? 1 : range;
var idx = Math.round((list.count-1)*range);
scrollSlider.positionSlider(idx);
}
scrollSlider.manual = false;
returnToBounds()
}
}
Rectangle {
id: scrollBar
property bool scrolling: list.contentHeight > list.height
anchors {
top: parent.top
right: parent.right
bottom: parent.bottom
topMargin: 4
bottomMargin: 4
}
width: scrolling ? 18 : 0
radius: 4
color: hifi.colors.baseGrayShadow
MouseArea {
anchors.fill: parent
onClicked: {
var index = scrollSlider.y * (list.count - 1) / (scrollBar.height - scrollSlider.height);
index = Math.round(index);
var scrollAmount = Math.round(list.count/10);
index = index + (mouse.y <= scrollSlider.y ? -scrollAmount : scrollAmount);
if (index < 0) {
index = 0;
}
if (index > list.count - 1) {
index = list.count - 1;
}
scrollSlider.positionSlider(index);
}
}
Rectangle {
id: scrollSlider
property var manual: false
function positionSlider(index){
y = index*(scrollBar.height - scrollSlider.height)/(list.count - 1);
}
anchors {
right: parent.right
rightMargin: 3
}
width: 12
height: (list.height / list.contentHeight) * list.height
radius: width / 4
color: "white"
visible: scrollBar.scrolling;
onYChanged: {
var index = y * (list.count - 1) / (scrollBar.height - scrollSlider.height);
index = Math.round(index);
if (!manual) {
list.positionViewAtIndex(index, ListView.Visible);
}
}
MouseArea {
anchors.fill: parent
drag.target: scrollSlider
drag.axis: Drag.YAxis
drag.minimumY: 0
drag.maximumY: scrollBar.height - scrollSlider.height
}
}
}
}
HifiControls.GlyphButton {
id: clipboard;
enabled: true;
glyph: hifi.glyphs.scriptNew
size: 38
width: 50
height: 50
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: 2
anchors.leftMargin: 8
onClicked: {
var buffer = "";
for (var i = 0; i < memberModel.count; i++) {
var datarow = memberModel.get(i);
buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue;
}
Window.copyToClipboard(buffer);
focus = true;
}
}
HifiControls.Button {
id: debug;
enabled: true;
text: "Debug Script"
width: 120
height: 50
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 2
anchors.rightMargin: 8
onClicked: {
sendToScript({type: "selectScript"});
}
}
HifiControls.CheckBox {
id: hideQt
boxSize: 25
boxRadius: 3
checked: true
anchors.left: clipboard.right
anchors.leftMargin: 8
anchors.verticalCenter: clipboard.verticalCenter
anchors.margins: 2
onClicked: {
hideQtMethods = checked;
addListElements();
}
}
HifiControls.Label {
id: hideLabel
anchors.left: hideQt.right
anchors.verticalCenter: clipboard.verticalCenter
anchors.margins: 2
font.pixelSize: 15
text: "Hide Qt Methods"
}
HifiControls.Keyboard {
id: keyboard;
raised: false;
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
}
Keys.onPressed: {
console.log(event.nativeScanCode);
if (event.key == Qt.Key_Left) {
keyboard.raised = false;
}
}
}
function addNewMember() {
apiMembers.push({member: searchBar.text, type: "user", value: valueBar.text, hasValue: true});
var data = {'memberIndex': apiMembers.length-1, 'apiMember': searchBar.text, 'apiType':"user", 'apiValue': valueBar.text};
memberModel.insert(0, data);
computeMembersWithValues();
}
function evaluateMember() {
sendToScript({type: "evaluateMember", data:{member: searchBar.text, index: evaluatingIdx}});
}
function getValuesToRefresh() {
var valuesToRefresh = [];
for (var i = 0; i < membersWithValues.length; i++) {
var index = membersWithValues[i];
var row = memberModel.get(index);
valuesToRefresh.push({index: index, member: (row.apiType == "function()") ? row.apiMember+"()" : row.apiMember, value: row.apiValue});
}
return valuesToRefresh;
}
function reloadListValues(){
var valuesToRefresh = getValuesToRefresh();
sendToScript({type: "refreshValues", data:valuesToRefresh});
}
function startReload(){
var valuesToRefresh = getValuesToRefresh();
sendToScript({type: "startRefreshValues", data:valuesToRefresh});
}
function stopReload(){
sendToScript({type: "stopRefreshValues"});
}
function refreshValues(data) {
var buffer = "";
for (var i = 0; i < data.length; i++) {
var row = memberModel.get(data[i].index);
row.apiValue = data[i].value;
apiMembers[row.memberIndex].value = data[i].value;
memberModel.set(data[i].index, row);
buffer += "\n" + apiMembers[row.memberIndex].member + " : " + data[i].value;
}
print(buffer);
}
function fromScript(message) {
if (message.type === "methods") {
apiMembers = message.data;
if (ScriptDiscoveryService.debugScriptUrl != "") {
addListElements("GlobalDebugger");
if (memberModel.count == 0) {
addListElements();
}
} else {
addListElements();
}
} else if (message.type === "debugMethods") {
addListElements("GlobalDebugger");
} else if (message.type === "refreshValues") {
refreshValues(message.data);
} else if (message.type === "evaluateMember") {
valueBar.text = message.data.value;
var selrow = memberModel.get(message.data.index);
if (selrow.apiMember === searchBar.text || selrow.apiMember === searchBar.text + "()") {
selrow.apiValue = message.data.value;
apiMembers[selrow.memberIndex].value = message.data.value;
apiMembers[selrow.memberIndex].hasValue = true;
memberModel.set(message.data.index, selrow);
}
} else if (message.type === "selectScript") {
if (message.data.length > 0) {
ScriptDiscoveryService.debugScriptUrl = message.data;
}
}
}
function getFilterPairs(filter){
var filteredArray = [];
var filterChain;
filterChain = filter.split(" ");
for (var i = 0; i < filterChain.length; i++) {
filterChain[i] = filterChain[i].toUpperCase();
}
var matchPairs = [];
for (var i = 0; i < apiMembers.length; i++) {
if (filterChain != undefined) {
var found = 0;
var memberComp = apiMembers[i].member.toUpperCase();
for (var j = 0; j < filterChain.length; j++) {
found += memberComp.indexOf(filterChain[j]) >= 0 ? 1 : 0;
}
if (found === 0) {
continue;
}
matchPairs.push({index: i, found: found, member: apiMembers[i].member});
}
}
matchPairs.sort(function(a, b){
if(a.found > b.found) return -1;
if(a.found < b.found) return 1;
if(a.member > b.member) return 1;
if(a.member < b.member) return -1;
return 0;
});
return matchPairs;
}
function isolateElement(index) {
var oldElement = memberModel.get(index);
var newElement = {memberIndex: oldElement.memberIndex, apiMember: oldElement.apiMember, apiType: oldElement.apiType, apiValue: oldElement.apiValue};
membersWithValues = apiMembers[oldElement.memberIndex].hasValue ? [0] : [];
memberModel.remove(0, memberModel.count);
memberModel.append(newElement);
}
function computeMembersWithValues() {
membersWithValues = [];
for (var i = 0; i < memberModel.count; i++) {
var idx = memberModel.get(i).memberIndex;
if (apiMembers[idx].hasValue) {
membersWithValues.push(i);
}
}
update.enabled = membersWithValues.length <= maxUpdateValues;
reload.enabled = membersWithValues.length <= maxReloadValues;
}
function addListElements(filter) {
valueBar.text = "";
memberModel.remove(0, memberModel.count);
var filteredArray = (filter != undefined) ? [] : apiMembers;
var matchPairs;
if (filter != undefined) {
matchPairs = getFilterPairs(filter);
for (var i = 0; i < matchPairs.length; i++) {
if (matchPairs[i].found < matchPairs[0].found) {
break;
}
var idx = matchPairs[i].index;
filteredArray.push(apiMembers[idx]);
}
}
for (var i = 0; i < filteredArray.length; i++) {
var data = {'memberIndex': matchPairs ? matchPairs[i].index : i,
'apiMember': filteredArray[i].member,
'apiType': filteredArray[i].type,
'apiValue': filteredArray[i].value};
if (hideQtMethods) {
var chain = data.apiMember.split(".");
var method = chain[chain.length-1];
if (method != "destroyed" &&
method != "objectName" &&
method != "objectNameChanged") {
memberModel.append(data);
}
} else {
memberModel.append(data);
}
}
computeMembersWithValues();
if (isReloading) {
update.glyph = hifi.glyphs.playback_play
isReloading = false;
stopReload();
}
if (memberModel.count > 0) {
scrollSlider.y = 0;
list.contentY = 0;
}
}
signal sendToScript(var message);
}

View file

@ -1,256 +0,0 @@
//
// ToolWindow.qml
//
// Created by Bradley Austin Davis on 12 Jan 2016
// Copyright 2016 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
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtWebEngine 1.1
import QtWebChannel 1.0
import Qt.labs.settings 1.0
import "windows"
import "controls-uit"
import "styles-uit"
ScrollingWindow {
id: toolWindow
resizable: true
objectName: "ToolWindow"
destroyOnCloseButton: false
destroyOnHidden: false
closable: true
shown: false
title: "Edit"
property alias tabView: tabView
implicitWidth: 520; implicitHeight: 695
minSize: Qt.vector2d(456, 500)
HifiConstants { id: hifi }
onParentChanged: {
if (parent) {
x = 120;
y = 120;
}
}
onShownChanged: {
keyboardEnabled = HMD.active;
}
Settings {
category: "ToolWindow.Position"
property alias x: toolWindow.x
property alias y: toolWindow.y
}
Item {
id: toolWindowTabViewItem
height: pane.scrollHeight
width: pane.contentWidth
anchors.left: parent.left
anchors.top: parent.top
TabView {
id: tabView
width: pane.contentWidth
// Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view.
height: pane.scrollHeight
property int tabCount: 0
Repeater {
model: 4
Tab {
// Force loading of the content even if the tab is not visible
// (required for letting the C++ code access the webview)
active: true
enabled: false
property string originalUrl: ""
WebView {
id: webView
anchors.fill: parent
enabled: false
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
}
onEnabledChanged: toolWindow.updateVisiblity()
}
}
}
style: TabViewStyle {
frame: Rectangle { // Background shown before content loads.
anchors.fill: parent
color: hifi.colors.baseGray
}
frameOverlap: 0
tab: Rectangle {
implicitWidth: text.width
implicitHeight: 3 * text.height
color: styleData.selected ? hifi.colors.black : hifi.colors.tabBackgroundDark
RalewayRegular {
id: text
text: styleData.title
font.capitalization: Font.AllUppercase
size: hifi.fontSizes.tabName
width: tabView.tabCount > 1 ? styleData.availableWidth / tabView.tabCount : implicitWidth + 2 * hifi.dimensions.contentSpacing.x
elide: Text.ElideRight
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.lightGrayText
horizontalAlignment: Text.AlignHCenter
anchors.centerIn: parent
}
Rectangle { // Separator.
width: 1
height: parent.height
color: hifi.colors.black
anchors.left: parent.left
anchors.top: parent.top
visible: styleData.index > 0
Rectangle {
width: 1
height: 1
color: hifi.colors.baseGray
anchors.left: parent.left
anchors.bottom: parent.bottom
}
}
Rectangle { // Active underline.
width: parent.width - (styleData.index > 0 ? 1 : 0)
height: 1
anchors.right: parent.right
anchors.bottom: parent.bottom
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.baseGray
}
}
tabOverlap: 0
}
}
}
function updateVisiblity() {
if (visible) {
for (var i = 0; i < tabView.count; ++i) {
if (tabView.getTab(i).enabled) {
return;
}
}
shown = false;
}
}
function findIndexForUrl(source) {
for (var i = 0; i < tabView.count; ++i) {
var tab = tabView.getTab(i);
if (tab.originalUrl === source) {
return i;
}
}
return -1;
}
function findTabForUrl(source) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
return tabView.getTab(index);
}
function showTabForUrl(source, newVisible) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
var tab = tabView.getTab(index);
if (newVisible) {
toolWindow.shown = true
tab.enabled = true
} else {
tab.enabled = false;
updateVisiblity();
}
}
function findFreeTab() {
for (var i = 0; i < tabView.count; ++i) {
var tab = tabView.getTab(i);
if (tab && (!tab.originalUrl || tab.originalUrl === "")) {
return i;
}
}
return -1;
}
function removeTabForUrl(source) {
var index = findIndexForUrl(source);
if (index < 0) {
return;
}
var tab = tabView.getTab(index);
tab.title = "";
tab.enabled = false;
tab.originalUrl = "";
tab.item.url = "about:blank";
tab.item.enabled = false;
tabView.tabCount--;
}
function addWebTab(properties) {
if (!properties.source) {
console.warn("Attempted to open Web Tool Pane without URL");
return;
}
var existingTabIndex = findIndexForUrl(properties.source);
if (existingTabIndex >= 0) {
var tab = tabView.getTab(existingTabIndex);
return tab.item;
}
var freeTabIndex = findFreeTab();
if (freeTabIndex === -1) {
console.warn("Unable to add new tab");
return;
}
if (properties.width) {
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
}
if (properties.height) {
tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y);
}
var tab = tabView.getTab(freeTabIndex);
tab.title = properties.title || "Unknown";
tab.enabled = true;
tab.originalUrl = properties.source;
var result = tab.item;
result.enabled = true;
tabView.tabCount++;
result.url = properties.source;
return result;
}
}

View file

@ -45,18 +45,6 @@ Item {
}
}
onClicked: {
mouse.accepted = true;
Tablet.playSound(TabletEnums.ButtonClick);
webEntity.synthesizeKeyPress(glyph);
webEntity.synthesizeKeyPress(glyph, mirrorText);
if (toggle) {
toggled = !toggled;
}
}
onDoubleClicked: {
mouse.accepted = true;
}
@ -94,6 +82,14 @@ Item {
onReleased: {
if (containsMouse) {
Tablet.playSound(TabletEnums.ButtonClick);
webEntity.synthesizeKeyPress(glyph);
webEntity.synthesizeKeyPress(glyph, mirrorText);
if (toggle) {
toggled = !toggled;
}
keyItem.state = "mouseOver";
} else {
if (toggled) {

View file

@ -27,10 +27,12 @@ TextField {
property bool hasRoundedBorder: false
property bool error: false;
property bool hasClearButton: false;
property string leftPlaceholderGlyph: "";
placeholderText: textField.placeholderText
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
FontLoader { id: hifiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; }
font.family: firaSansSemiBold.name
font.pixelSize: hifi.fontSizes.textFieldInput
font.italic: textField.text == ""
@ -54,6 +56,7 @@ TextField {
}
style: TextFieldStyle {
id: style;
textColor: {
if (isLightColorScheme) {
if (textField.activeFocus) {
@ -102,6 +105,16 @@ TextField {
border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0)
HiFiGlyphs {
text: textField.leftPlaceholderGlyph;
color: textColor;
size: hifi.fontSizes.textFieldSearchIcon;
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter;
anchors.leftMargin: hifi.dimensions.textPadding - 2;
visible: text;
}
HiFiGlyphs {
text: hifi.glyphs.search
color: textColor
@ -132,7 +145,7 @@ TextField {
placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
padding.left: (isSearchField ? textField.height - 2 : 0) + hifi.dimensions.textPadding
padding.left: ((isSearchField || textField.leftPlaceholderGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding
padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
}

View file

@ -41,9 +41,9 @@ Item {
onNewViewRequestedCallback: {
// desktop is not defined for web-entities or tablet
if (typeof desktop !== "undefined") {
desktop.openBrowserWindow(request, profile);
desktop.openBrowserWindow(request, webViewCoreProfile);
} else {
tabletRoot.openBrowserWindow(request, profile);
tabletRoot.openBrowserWindow(request, webViewCoreProfile);
}
}

View file

@ -70,7 +70,15 @@ ModalWindow {
signal selectedFile(var file);
signal canceled();
signal selected(int button);
function click(button) {
clickedButton = button;
selected(button);
destroy();
}
property int clickedButton: OriginalDialogs.StandardButton.NoButton;
Component.onCompleted: {
console.log("Helper " + helper + " drives " + drives);
@ -628,7 +636,10 @@ ModalWindow {
case Qt.Key_Backtab:
event.accepted = false;
break;
case Qt.Key_Escape:
event.accepted = true;
root.click(OriginalDialogs.StandardButton.Cancel);
break;
default:
if (addToPrefix(event)) {
event.accepted = true
@ -793,7 +804,11 @@ ModalWindow {
case Qt.Key_Home:
event.accepted = d.navigateHome();
break;
}
case Qt.Key_Escape:
event.accepted = true;
root.click(OriginalDialogs.StandardButton.Cancel);
break;
}
}
}

View file

@ -1,85 +0,0 @@
//
// AvatarBrowser.qml
//
// Created by Bradley Austin Davis on 30 Aug 2015
// 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
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebChannel 1.0
import QtWebEngine 1.2
import "../../windows"
import "../../controls-uit"
import "../../styles-uit"
Window {
id: root
HifiConstants { id: hifi }
width: 900; height: 700
resizable: true
modality: Qt.ApplicationModal
Item {
anchors.fill: parent
property bool keyboardEnabled: false
property bool keyboardRaised: true
property bool punctuationMode: false
BaseWebView {
id: webview
url: Account.metaverseServerURL + "/marketplace?category=avatars"
focus: true
anchors {
top: parent.top
left: parent.left
right: parent.right
bottom: keyboard.top
}
// Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// Detect when may want to raise and lower keyboard.
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
}
}
Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
Component.onCompleted: {
keyboardEnabled = HMD.active;
}
}
}

View file

@ -99,25 +99,9 @@ Preference {
leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0
}
onClicked: {
if (typeof desktop !== "undefined") {
// Load dialog via OffscreenUi so that JavaScript EventBridge is available.
root.browser = OffscreenUi.load("dialogs/preferences/AvatarBrowser.qml");
root.browser.windowDestroyed.connect(function(){
root.browser = null;
});
} else {
root.browser = tabletAvatarBrowserBuilder.createObject(tabletRoot);
// Make dialog modal.
tabletRoot.openModal = root.browser;
}
ApplicationInterface.loadAvatarBrowser();
}
}
Component {
id: tabletAvatarBrowserBuilder;
TabletAvatarBrowser { }
}
}
}

View file

@ -22,10 +22,6 @@ OriginalDesktop.Desktop {
acceptedButtons: Qt.NoButton
}
// The tool window, one instance
property alias toolWindow: toolWindow
ToolWindow { id: toolWindow }
Action {
text: "Open Browser"
shortcut: "Ctrl+B"

View file

@ -50,7 +50,7 @@ Item {
id: avatarImage
visible: profileUrl !== "" && userName !== "";
// Size
height: isMyCard ? 70 : 42;
height: isMyCard ? 84 : 42;
width: visible ? height : 0;
anchors.top: parent.top;
anchors.topMargin: isMyCard ? 0 : 8;
@ -520,7 +520,7 @@ Item {
Slider {
id: gainSlider
// Size
width: thisNameCard.width;
width: isMyCard ? thisNameCard.width - 20 : thisNameCard.width;
height: 14
// Anchors
anchors.verticalCenter: nameCardVUMeter.verticalCenter;

View file

@ -28,7 +28,7 @@ Rectangle {
// Properties
property bool debug: false;
property int myCardWidth: width - upperRightInfoContainer.width;
property int myCardHeight: 80;
property int myCardHeight: 100;
property int rowHeight: 60;
property int actionButtonWidth: 55;
property int locationColumnWidth: 170;

View file

@ -145,7 +145,7 @@ Rectangle {
// Title text
RalewayRegular {
id: popText;
text: "PROOF OF PURCHASE";
text: "Proof of Provenance";
// Text size
size: 16;
// Anchors
@ -155,7 +155,7 @@ Rectangle {
anchors.right: titleBarText.right;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
color: hifi.colors.darkGray;
}
//
@ -182,7 +182,7 @@ Rectangle {
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
color: hifi.colors.darkGray;
}
RalewaySemiBold {
id: itemName;
@ -196,7 +196,7 @@ Rectangle {
anchors.right: itemNameHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.blueAccent;
color: hifi.colors.white;
elide: Text.ElideRight;
MouseArea {
anchors.fill: parent;
@ -205,7 +205,7 @@ Rectangle {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
}
onEntered: itemName.color = hifi.colors.blueHighlight;
onExited: itemName.color = hifi.colors.blueAccent;
onExited: itemName.color = hifi.colors.white;
}
}
@ -223,7 +223,7 @@ Rectangle {
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.lightGray;
color: hifi.colors.darkGray;
}
RalewayRegular {
id: ownedBy;
@ -236,7 +236,7 @@ Rectangle {
anchors.left: ownedByHeader.left;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
color: hifi.colors.white;
elide: Text.ElideRight;
}
AnonymousProRegular {
@ -252,7 +252,7 @@ Rectangle {
anchors.leftMargin: 6;
anchors.right: ownedByHeader.right;
// Style
color: hifi.colors.lightGray;
color: hifi.colors.white;
elide: Text.ElideRight;
verticalAlignment: Text.AlignVCenter;
}
@ -271,7 +271,7 @@ Rectangle {
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.lightGray;
color: hifi.colors.darkGray;
}
AnonymousProRegular {
id: edition;
@ -285,7 +285,7 @@ Rectangle {
anchors.right: editionHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
color: hifi.colors.white;
}
RalewayRegular {
@ -302,7 +302,7 @@ Rectangle {
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.lightGray;
color: hifi.colors.darkGray;
}
AnonymousProRegular {
id: dateOfPurchase;
@ -316,7 +316,7 @@ Rectangle {
anchors.right: dateOfPurchaseHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
color: hifi.colors.white;
}
RalewayRegular {
@ -349,7 +349,7 @@ Rectangle {
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderless;
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.left: parent.left;

Binary file not shown.

Before

(image error) Size: 63 KiB

After

(image error) Size: 62 KiB

View file

@ -25,7 +25,6 @@ Item {
id: root;
property string keyFilePath;
property bool showDebugButtons: true;
Connections {
target: Commerce;
@ -55,37 +54,6 @@ Item {
// Style
color: hifi.colors.blueHighlight;
}
HifiControlsUit.Button {
id: clearCachedPassphraseButton;
visible: root.showDebugButtons;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.left: helpTitleText.right;
anchors.leftMargin: 20;
height: 40;
width: 150;
text: "DBG: Clear Pass";
onClicked: {
Commerce.setPassphrase("");
sendSignalToWallet({method: 'passphraseReset'});
}
}
HifiControlsUit.Button {
id: resetButton;
visible: root.showDebugButtons;
color: hifi.buttons.red;
colorScheme: hifi.colorSchemes.dark;
anchors.top: clearCachedPassphraseButton.top;
anchors.left: clearCachedPassphraseButton.right;
height: 40;
width: 150;
text: "DBG: RST Wallet";
onClicked: {
Commerce.reset();
sendSignalToWallet({method: 'walletReset'});
}
}
ListModel {
id: helpModel;

View file

@ -1,73 +0,0 @@
//
// SendMoney.qml
// qml/hifi/commerce/wallet
//
// SendMoney
//
// Created by Zach Fox on 2017-08-18
// 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 Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Connections {
target: Commerce;
}
// "Unavailable"
RalewayRegular {
text: "You currently cannot send money to other High Fidelity users.";
// Anchors
anchors.fill: parent;
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -19,8 +19,7 @@ import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
// references XXX from root context
import "./sendMoney"
Rectangle {
HifiConstants { id: hifi; }
@ -316,18 +315,29 @@ Rectangle {
Connections {
onSendSignalToWallet: {
sendToScript(msg);
if (msg.method === 'transactionHistory_usernameLinkClicked') {
userInfoViewer.url = msg.usernameLink;
userInfoViewer.visible = true;
} else {
sendToScript(msg);
}
}
}
}
SendMoney {
id: sendMoney;
z: 997;
visible: root.activeView === "sendMoney";
anchors.top: titleBarContainer.bottom;
anchors.bottom: tabButtonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.fill: parent;
parentAppTitleBarHeight: titleBarContainer.height;
parentAppNavBarHeight: tabButtonsContainer.height;
Connections {
onSendSignalToWallet: {
sendToScript(msg);
}
}
}
Security {
@ -497,7 +507,7 @@ Rectangle {
Rectangle {
id: sendMoneyButtonContainer;
visible: !walletSetup.visible;
color: hifi.colors.black;
color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
@ -513,7 +523,7 @@ Rectangle {
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: hifi.colors.lightGray50;
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
@ -528,12 +538,24 @@ Rectangle {
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
id: sendMoneyTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.activeView = "sendMoney";
tabButtonsContainer.resetTabButtonColors();
}
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black;
}
}
// "SECURITY" tab button
@ -665,9 +687,16 @@ Rectangle {
// TAB BUTTONS END
//
HifiControls.TabletWebView {
id: userInfoViewer;
z: 998;
anchors.fill: parent;
visible: false;
}
Item {
id: keyboardContainer;
z: 998;
z: 999;
visible: keyboard.raised;
property bool punctuationMode: false;
anchors {
@ -713,6 +742,13 @@ Rectangle {
case 'inspectionCertificate_resetCert':
// NOP
break;
case 'updateConnections':
sendMoney.updateConnections(message.connections);
break;
case 'selectRecipient':
case 'updateSelectedRecipientUsername':
sendMoney.fromScript(message);
break;
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}

View file

@ -19,8 +19,6 @@ import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
@ -32,6 +30,20 @@ Item {
property int currentHistoryPage: 1;
property var pagesAlreadyAdded: new Array();
onVisibleChanged: {
if (visible) {
transactionHistoryModel.clear();
Commerce.balance();
initialHistoryReceived = false;
root.currentHistoryPage = 1;
root.noMoreHistoryData = false;
root.historyRequestPending = true;
Commerce.history(root.currentHistoryPage);
} else {
refreshTimer.stop();
}
}
Connections {
target: Commerce;
@ -189,20 +201,6 @@ Item {
color: hifi.colors.white;
// Alignment
verticalAlignment: Text.AlignVCenter;
onVisibleChanged: {
if (visible) {
transactionHistoryModel.clear();
Commerce.balance();
initialHistoryReceived = false;
root.currentHistoryPage = 1;
root.noMoreHistoryData = false;
root.historyRequestPending = true;
Commerce.history(root.currentHistoryPage);
} else {
refreshTimer.stop();
}
}
}
// "balance" text below field
@ -384,8 +382,8 @@ Item {
height: visible ? parent.height : 0;
AnonymousProRegular {
id: dateText;
text: model.created_at ? getFormattedDate(model.created_at * 1000) : "";
id: hfcText;
text: model.hfc_text || '';
// Style
size: 18;
anchors.left: parent.left;
@ -393,28 +391,33 @@ Item {
anchors.topMargin: 15;
width: 118;
height: paintedHeight;
color: hifi.colors.blueAccent;
wrapMode: Text.WordWrap;
font.bold: true;
// Alignment
horizontalAlignment: Text.AlignRight;
}
AnonymousProRegular {
id: transactionText;
text: model.text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.text) : model.text) : "";
text: model.transaction_text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.transaction_text) : model.transaction_text) : "";
size: 18;
anchors.top: parent.top;
anchors.topMargin: 15;
anchors.left: dateText.right;
anchors.left: hfcText.right;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: paintedHeight;
color: model.status === "invalidated" ? hifi.colors.redAccent : hifi.colors.baseGrayHighlight;
linkColor: hifi.colors.blueAccent;
wrapMode: Text.WordWrap;
font.strikeout: model.status === "invalidated";
onLinkActivated: {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
if (link.indexOf("users/") !== -1) {
sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link});
} else {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
}
}
}

View file

@ -0,0 +1,128 @@
//
// ConnectionItem.qml
// qml/hifi/commerce/wallet/sendMoney
//
// ConnectionItem
//
// Created by Zach Fox on 2018-01-09
// Copyright 2018 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 Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../../../../styles-uit"
import "../../../../controls-uit" as HifiControlsUit
import "../../../../controls" as HifiControls
import "../../wallet" as HifiWallet
Item {
HifiConstants { id: hifi; }
id: root;
property bool isSelected: false;
property string userName;
property string profilePicUrl;
height: 65;
width: parent.width;
Rectangle {
id: mainContainer;
// Style
color: root.isSelected ? hifi.colors.faintGray : hifi.colors.white;
// Size
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
height: root.height;
Item {
id: avatarImage;
visible: profileUrl !== "" && userName !== "";
// Size
anchors.verticalCenter: parent.verticalCenter;
anchors.left: parent.left;
anchors.leftMargin: 36;
height: root.height - 15;
width: visible ? height : 0;
clip: true;
Image {
id: userImage;
source: root.profilePicUrl !== "" ? ((0 === root.profilePicUrl.indexOf("http")) ?
root.profilePicUrl : (Account.metaverseServerURL + root.profilePicUrl)) : "";
mipmap: true;
// Anchors
anchors.fill: parent
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Item {
width: userImage.width;
height: userImage.height;
Rectangle {
anchors.centerIn: parent;
width: userImage.width; // This works because userImage is square
height: width;
radius: width;
}
}
}
}
AnimatedImage {
source: "../../../../../icons/profilePicLoading.gif"
anchors.fill: parent;
visible: userImage.status != Image.Ready;
}
}
RalewaySemiBold {
id: userName;
anchors.left: avatarImage.right;
anchors.leftMargin: 16;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: chooseButton.visible ? chooseButton.left : parent.right;
anchors.rightMargin: chooseButton.visible ? 10 : 0;
// Text size
size: 20;
// Style
color: hifi.colors.baseGray;
text: root.userName;
elide: Text.ElideRight;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
// "Choose" button
HifiControlsUit.Button {
id: chooseButton;
visible: root.isSelected;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.verticalCenter: parent.verticalCenter;
anchors.right: parent.right;
anchors.rightMargin: 24;
height: root.height - 20;
width: 110;
text: "CHOOSE";
onClicked: {
var msg = { method: 'chooseConnection', userName: root.userName, profilePicUrl: root.profilePicUrl };
sendToSendMoney(msg);
}
}
}
//
// FUNCTION DEFINITIONS START
//
signal sendToSendMoney(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,116 @@
//
// RecipientDisplay.qml
// qml/hifi/commerce/wallet/sendMoney
//
// RecipientDisplay
//
// Created by Zach Fox on 2018-01-11
// Copyright 2018 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 Hifi 1.0 as Hifi
import QtQuick 2.6
import QtQuick.Controls 2.2
import QtGraphicalEffects 1.0
import "../../../../styles-uit"
import "../../../../controls-uit" as HifiControlsUit
import "../../../../controls" as HifiControls
import "../../common" as HifiCommerceCommon
Item {
HifiConstants { id: hifi; }
id: root;
property bool isDisplayingNearby; // as opposed to 'connections'
property string displayName;
property string userName;
property string profilePic;
Item {
visible: root.isDisplayingNearby;
anchors.fill: parent;
RalewaySemiBold {
id: recipientDisplayName;
text: root.displayName;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.rightMargin: 12;
height: parent.height/2;
// Text size
size: 18;
// Style
color: hifi.colors.baseGray;
verticalAlignment: Text.AlignBottom;
elide: Text.ElideRight;
}
RalewaySemiBold {
text: root.userName;
// Anchors
anchors.bottom: parent.bottom;
anchors.left: recipientDisplayName.anchors.left;
anchors.leftMargin: recipientDisplayName.anchors.leftMargin;
anchors.right: recipientDisplayName.anchors.right;
anchors.rightMargin: recipientDisplayName.anchors.rightMargin;
height: parent.height/2;
// Text size
size: 16;
// Style
color: hifi.colors.baseGray;
verticalAlignment: Text.AlignTop;
elide: Text.ElideRight;
}
}
Item {
visible: !root.isDisplayingNearby;
anchors.fill: parent;
Image {
id: userImage;
source: root.profilePic;
mipmap: true;
// Anchors
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter;
height: parent.height - 36;
width: height;
layer.enabled: true;
layer.effect: OpacityMask {
maskSource: Item {
width: userImage.width;
height: userImage.height;
Rectangle {
anchors.centerIn: parent;
width: userImage.width; // This works because userImage is square
height: width;
radius: width;
}
}
}
}
RalewaySemiBold {
text: root.userName;
// Anchors
anchors.left: userImage.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
height: parent.height - 4;
// Text size
size: 16;
// Style
color: hifi.colors.baseGray;
verticalAlignment: Text.AlignVCenter;
elide: Text.ElideRight;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@ import "."
Overlay {
id: root
Image {
AnimatedImage {
id: image
property bool scaleFix: true
property real xStart: 0

View file

@ -62,7 +62,8 @@ StackView {
var callback = rpcCalls[message.id];
if (!callback) {
console.log('No callback for message fromScript', JSON.stringify(message));
// FIXME: We often recieve very long messages here, the logging of which is drastically slowing down the main thread
//console.log('No callback for message fromScript', JSON.stringify(message));
return;
}
delete rpcCalls[message.id];

View file

@ -188,8 +188,10 @@ Item {
TabletButton {
id: tabletButton
scale: wrapper.hovered ? 1.25 : wrapper.containsMouse ? 0.75 : 1.0
Behavior on scale { NumberAnimation { duration: 200; easing.type: Easing.Linear } }
// Temporarily disable magnification
// scale: wrapper.hovered ? 1.25 : wrapper.containsMouse ? 0.75 : 1.0
// Behavior on scale { NumberAnimation { duration: 200; easing.type: Easing.Linear } }
anchors.centerIn: parent
gridView: wrapper.GridView.view

View file

@ -22,7 +22,6 @@ Item {
anchors.fill: parent
id: d
objectName: "stack"
initialItem: topMenu
property var menuStack: []
property var topMenu: null;

View file

@ -106,7 +106,7 @@ Item {
if (isWebPage) {
var webUrl = tabletApps.get(currentApp).appWebUrl;
var scriptUrl = tabletApps.get(currentApp).scriptUrl;
loadSource("TabletWebView.qml");
loadSource("hifi/tablet/TabletWebView.qml");
loadWebUrl(webUrl, scriptUrl);
} else {
loader.load(tabletApps.get(currentApp).appUrl);

View file

@ -1,111 +0,0 @@
//
// TabletAvatarBrowser.qml
//
// Created by David Rowe on 14 Mar 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.5
import QtQuick.Controls 1.4
import QtWebChannel 1.0
import QtWebEngine 1.2
import "../../../../windows"
import "../../../../controls-uit"
import "../../../../styles-uit"
Item {
id: root
objectName: "ModelBrowserDialog"
property string title: "Attachment Model"
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
anchors.fill: parent
BaseWebView {
id: webview
url: (Account.metaverseServerURL + "/marketplace?category=avatars")
focus: true
anchors {
top: parent.top
left: parent.left
right: parent.right
bottom: footer.top
}
// Create a global EventBridge object for raiseAndLowerKeyboard.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// Detect when may want to raise and lower keyboard.
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ]
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
}
}
Rectangle {
id: footer
height: 40
anchors {
left: parent.left
right: parent.right
bottom: keyboard.top
}
color: hifi.colors.baseGray
Row {
anchors {
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: hifi.dimensions.contentMargin.x
}
Button {
text: "Cancel"
color: hifi.buttons.white
onClicked: root.destroy();
}
}
}
Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
Component.onCompleted: {
keyboardEnabled = HMD.active;
}
}

View file

@ -157,7 +157,7 @@
#include "scripting/AssetMappingsScriptingInterface.h"
#include "scripting/ClipboardScriptingInterface.h"
#include "scripting/DesktopScriptingInterface.h"
#include "scripting/GlobalServicesScriptingInterface.h"
#include "scripting/AccountServicesScriptingInterface.h"
#include "scripting/HMDScriptingInterface.h"
#include "scripting/MenuScriptingInterface.h"
#include "scripting/SettingsScriptingInterface.h"
@ -2093,6 +2093,11 @@ void Application::cleanupBeforeQuit() {
DependencyManager::destroy<AudioInjectorManager>();
DependencyManager::destroy<AudioScriptingInterface>();
// The PointerManager must be destroyed before the PickManager because when a Pointer is deleted,
// it accesses the PickManager to delete its associated Pick
DependencyManager::destroy<PointerManager>();
DependencyManager::destroy<PickManager>();
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
}
@ -2287,7 +2292,7 @@ void Application::initializeUi() {
QUrl{ "hifi/commerce/wallet/SecurityImageChange.qml" },
QUrl{ "hifi/commerce/wallet/SecurityImageModel.qml" },
QUrl{ "hifi/commerce/wallet/SecurityImageSelection.qml" },
QUrl{ "hifi/commerce/wallet/SendMoney.qml" },
QUrl{ "hifi/commerce/wallet/sendMoney/SendMoney.qml" },
QUrl{ "hifi/commerce/wallet/Wallet.qml" },
QUrl{ "hifi/commerce/wallet/WalletHome.qml" },
QUrl{ "hifi/commerce/wallet/WalletSetup.qml" },
@ -2376,9 +2381,11 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
surfaceContext->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance());
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
surfaceContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
surfaceContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());
surfaceContext->setContextProperty("UndoStack", &_undoStackScriptingInterface);
@ -2444,7 +2451,7 @@ void Application::initializeUi() {
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
}
void Application::updateCamera(RenderArgs& renderArgs) {
void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
PROFILE_RANGE(render, __FUNCTION__);
PerformanceTimer perfTimer("updateCamera");
@ -2459,6 +2466,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
// Using the latter will cause the camera to wobble with idle animations,
// or with changes from the face tracker
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_thirdPersonHMDCameraBoomValid= false;
if (isHMDMode()) {
mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
_myCamera.setPosition(extractTranslation(camMat));
@ -2471,12 +2479,25 @@ void Application::updateCamera(RenderArgs& renderArgs) {
}
else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
if (isHMDMode()) {
auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
_myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat)));
_myCamera.setPosition(extractTranslation(hmdWorldMat) +
myAvatar->getWorldOrientation() * boomOffset);
if (!_thirdPersonHMDCameraBoomValid) {
const glm::vec3 CAMERA_OFFSET = glm::vec3(0.0f, 0.0f, 0.7f);
_thirdPersonHMDCameraBoom = cancelOutRollAndPitch(myAvatar->getHMDSensorOrientation()) * CAMERA_OFFSET;
_thirdPersonHMDCameraBoomValid = true;
}
glm::mat4 thirdPersonCameraSensorToWorldMatrix = myAvatar->getSensorToWorldMatrix();
const glm::vec3 cameraPos = myAvatar->getHMDSensorPosition() + _thirdPersonHMDCameraBoom * myAvatar->getBoomLength();
glm::mat4 sensorCameraMat = createMatFromQuatAndPos(myAvatar->getHMDSensorOrientation(), cameraPos);
glm::mat4 worldCameraMat = thirdPersonCameraSensorToWorldMatrix * sensorCameraMat;
_myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat)));
_myCamera.setPosition(extractTranslation(worldCameraMat));
}
else {
_thirdPersonHMDCameraBoomValid = false;
_myCamera.setOrientation(myAvatar->getHead()->getOrientation());
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
@ -2489,6 +2510,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
}
}
else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_thirdPersonHMDCameraBoomValid= false;
if (isHMDMode()) {
auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
@ -2523,6 +2545,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
}
else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) {
_thirdPersonHMDCameraBoomValid= false;
EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer();
if (cameraEntity != nullptr) {
if (isHMDMode()) {
@ -4333,8 +4356,9 @@ void Application::updateLOD(float deltaTime) const {
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime));
DependencyManager::get<LODManager>()->autoAdjustLOD(maxRenderTime, deltaTime);
auto lodManager = DependencyManager::get<LODManager>();
lodManager->setRenderTimes(presentTime, engineRunTime, gpuTime);
lodManager->autoAdjustLOD(deltaTime);
} else {
DependencyManager::get<LODManager>()->resetLODAdjust();
}
@ -5099,7 +5123,8 @@ void Application::update(float deltaTime) {
_postUpdateLambdas.clear();
}
editRenderArgs([this](AppRenderArgs& appRenderArgs) {
editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
PerformanceTimer perfTimer("editRenderArgs");
appRenderArgs._headPose= getHMDSensorPose();
@ -5146,7 +5171,7 @@ void Application::update(float deltaTime) {
resizeGL();
}
this->updateCamera(appRenderArgs._renderArgs);
this->updateCamera(appRenderArgs._renderArgs, deltaTime);
appRenderArgs._eyeToWorld = _myCamera.getTransform();
appRenderArgs._isStereo = false;
@ -5448,7 +5473,7 @@ void Application::clearDomainOctreeDetails() {
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT);
skyStage->setBackgroundMode(graphics::SunSkyStage::SKY_DEFAULT);
DependencyManager::get<AnimationCache>()->clearUnusedResources();
DependencyManager::get<ModelCache>()->clearUnusedResources();
@ -5758,10 +5783,11 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCache>().data());
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
scriptEngine->registerGlobalObject("Account", GlobalServicesScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
scriptEngine->registerGlobalObject("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
scriptEngine->registerGlobalObject("AccountServices", AccountServicesScriptingInterface::getInstance());
qScriptRegisterMetaType(scriptEngine.data(), DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);
scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
@ -6161,7 +6187,7 @@ void Application::showAssetServerWidget(QString filePath) {
if (!hmd->getShouldShowTablet() && !isHMDMode()) {
DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);
} else {
static const QUrl url("hifi/dialogs/TabletAssetServer.qml");
static const QUrl url("../dialogs/TabletAssetServer.qml");
tablet->pushOntoStack(url);
}
}
@ -6790,6 +6816,15 @@ void Application::loadAddAvatarBookmarkDialog() const {
avatarBookmarks->addBookmark();
}
void Application::loadAvatarBrowser() const {
auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
// construct the url to the marketplace item
QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars";
QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js";
tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH);
DependencyManager::get<HMDScriptingInterface>()->openTablet();
}
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
// Get a screenshot and save it
@ -6807,7 +6842,8 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
void Application::takeSecondaryCameraSnapshot() {
postLambdaEvent([this] {
Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot());
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot());
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
});
}
@ -7371,11 +7407,13 @@ void Application::updateThreadPoolCount() const {
}
void Application::updateSystemTabletMode() {
qApp->setProperty(hifi::properties::HMD, isHMDMode());
if (isHMDMode()) {
DependencyManager::get<TabletScriptingInterface>()->setToolbarMode(getHmdTabletBecomesToolbarSetting());
} else {
DependencyManager::get<TabletScriptingInterface>()->setToolbarMode(getDesktopTabletBecomesToolbarSetting());
if (_settingsLoaded) {
qApp->setProperty(hifi::properties::HMD, isHMDMode());
if (isHMDMode()) {
DependencyManager::get<TabletScriptingInterface>()->setToolbarMode(getHmdTabletBecomesToolbarSetting());
} else {
DependencyManager::get<TabletScriptingInterface>()->setToolbarMode(getDesktopTabletBecomesToolbarSetting());
}
}
}

View file

@ -71,7 +71,7 @@
#include "UndoStackScriptingInterface.h"
#include <procedural/ProceduralSkybox.h>
#include <model/Skybox.h>
#include <graphics/Skybox.h>
#include <ModelScriptingInterface.h>
#include "FrameTimingsScriptingInterface.h"
@ -146,7 +146,7 @@ public:
void initializeGL();
void initializeUi();
void updateCamera(RenderArgs& renderArgs);
void updateCamera(RenderArgs& renderArgs, float deltaTime);
void paintGL();
void resizeGL();
@ -270,7 +270,7 @@ public:
void takeSecondaryCameraSnapshot();
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
graphics::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; }
gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; }
@ -309,6 +309,7 @@ public slots:
void toggleEntityScriptServerLogDialog();
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
Q_INVOKABLE void loadAvatarBrowser() const;
Q_INVOKABLE SharedSoundPointer getSampleSound() const;
void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const;
@ -666,7 +667,7 @@ private:
ConnectionMonitor _connectionMonitor;
model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ;
graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ;
gpu::TexturePointer _defaultSkyboxTexture;
gpu::TexturePointer _defaultSkyboxAmbientTexture;
@ -695,6 +696,9 @@ private:
void startHMDStandBySession();
void endHMDSession();
glm::vec3 _thirdPersonHMDCameraBoom { 0.0f, 0.0f, -1.0f };
bool _thirdPersonHMDCameraBoomValid { true };
QUrl _avatarOverrideUrl;
bool _saveAvatarOverrideUrl { false };
QObject* _renderEventHandler{ nullptr };

View file

@ -26,43 +26,50 @@ Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DO
LODManager::LODManager() {
}
float LODManager::getLODDecreaseFPS() {
float LODManager::getLODDecreaseFPS() const {
if (qApp->isHMDMode()) {
return getHMDLODDecreaseFPS();
}
return getDesktopLODDecreaseFPS();
}
float LODManager::getLODIncreaseFPS() {
float LODManager::getLODIncreaseFPS() const {
if (qApp->isHMDMode()) {
return getHMDLODIncreaseFPS();
}
return getDesktopLODIncreaseFPS();
}
// We use a "time-weighted running average" of the renderTime and compare it against min/max thresholds
// We use a "time-weighted running average" of the maxRenderTime and compare it against min/max thresholds
// to determine if we should adjust the level of detail (LOD).
//
// A time-weighted running average has a timescale which determines how fast the average tracks the measured
// value in real-time. Given a step-function in the mesured value, and assuming measurements happen
// faster than the runningAverage is computed, the error between the value and its runningAverage will be
// reduced by 1/e every timescale of real-time that passes.
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // sec
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.08f; // sec
//
// Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage to settle
// to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few
// multiples of the running average timescale:
const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
const uint64_t LOD_AUTO_ADJUST_PERIOD = 4 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f;
const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f;
void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
// compute time-weighted running average renderTime
void LODManager::setRenderTimes(float presentTime, float engineRunTime, float gpuTime) {
_presentTime = presentTime;
_engineRunTime = engineRunTime;
_gpuTime = gpuTime;
}
void LODManager::autoAdjustLOD(float realTimeDelta) {
float maxRenderTime = glm::max(glm::max(_presentTime, _engineRunTime), _gpuTime);
// compute time-weighted running average maxRenderTime
// Note: we MUST clamp the blend to 1.0 for stability
float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * renderTime; // msec
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec
if (!_automaticLODAdjust) {
// early exit
return;
@ -84,6 +91,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
<< "targetFPS =" << getLODDecreaseFPS()
<< "octreeSizeScale =" << _octreeSizeScale;
emit LODDecreased();
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
// to provide an FPS just above the decrease threshold. It will drift close to its
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODDecreaseFPS() + 1.0f);
}
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
}
@ -105,6 +116,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
<< "targetFPS =" << getLODDecreaseFPS()
<< "octreeSizeScale =" << _octreeSizeScale;
emit LODIncreased();
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
// to provide an FPS just below the increase threshold. It will drift close to its
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODIncreaseFPS() - 1.0f);
}
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
}
@ -119,11 +134,6 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) {
if (lodToolsDialog) {
lodToolsDialog->reloadSliders();
}
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
// to be at middle of target zone. It will drift close to its true value within
// about three few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
float expectedFPS = 0.5f * (getLODIncreaseFPS() + getLODDecreaseFPS());
_avgRenderTime = MSECS_PER_SECOND / expectedFPS;
}
}
@ -131,6 +141,18 @@ void LODManager::resetLODAdjust() {
_decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
}
float LODManager::getLODLevel() const {
// simpleLOD is a linearized and normalized number that represents how much LOD is being applied.
// It ranges from:
// 1.0 = normal (max) level of detail
// 0.0 = min level of detail
// In other words: as LOD "drops" the value of simpleLOD will also "drop", and it cannot go lower than 0.0.
const float LOG_MIN_LOD_RATIO = logf(ADJUST_LOD_MIN_SIZE_SCALE / ADJUST_LOD_MAX_SIZE_SCALE);
float power = logf(_octreeSizeScale / ADJUST_LOD_MAX_SIZE_SCALE);
float simpleLOD = (LOG_MIN_LOD_RATIO - power) / LOG_MIN_LOD_RATIO;
return simpleLOD;
}
const float MIN_DECREASE_FPS = 0.5f;
void LODManager::setDesktopLODDecreaseFPS(float fps) {

View file

@ -37,7 +37,7 @@ class AABox;
class LODManager : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
@ -49,34 +49,56 @@ public:
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
// User Tweakable LOD Items
Q_INVOKABLE QString getLODFeedbackText();
Q_INVOKABLE void setOctreeSizeScale(float sizeScale);
Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; }
Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust);
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
Q_INVOKABLE float getLODDecreaseFPS();
Q_INVOKABLE float getLODIncreaseFPS();
Q_INVOKABLE float getLODDecreaseFPS() const;
Q_INVOKABLE float getLODIncreaseFPS() const;
Q_PROPERTY(float presentTime READ getPresentTime)
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
Q_PROPERTY(float gpuTime READ getGPUTime)
Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
Q_PROPERTY(float lodLevel READ getLODLevel)
Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
float getPresentTime() const { return _presentTime; }
float getEngineRunTime() const { return _engineRunTime; }
float getGPUTime() const { return _gpuTime; }
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
void autoAdjustLOD(float renderTime, float realTimeDelta);
void setRenderTimes(float presentTime, float engineRunTime, float gpuTime);
void autoAdjustLOD(float realTimeDelta);
void loadSettings();
void saveSettings();
void resetLODAdjust();
float getAverageRenderTime() const { return _avgRenderTime; };
float getMaxTheoreticalFPS() const { return (float)MSECS_PER_SECOND / _avgRenderTime; };
float getLODLevel() const;
signals:
void LODIncreased();
void LODDecreased();
private:
LODManager();
bool _automaticLODAdjust = true;
float _avgRenderTime { 0.0f };
float _presentTime { 0.0f }; // msec
float _engineRunTime { 0.0f }; // msec
float _gpuTime { 0.0f }; // msec
float _avgRenderTime { 0.0f }; // msec
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };

View file

@ -574,8 +574,6 @@ Menu::Menu() {
avatar.get(), SLOT(setEnableMeshVisible(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true,
avatar.get(), SLOT(setUseAnimPreAndPostRotations(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true,
avatar.get(), SLOT(setEnableInverseKinematics(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSensorToWorldMatrix, 0, false,
@ -758,6 +756,14 @@ Menu::Menu() {
// Developer > Stats
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
// Developer > API Debugger
action = addActionToQMenuAndActionHash(developerMenu, "API Debugger");
connect(action, &QAction::triggered, [] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js");
scriptEngines->loadScript(defaultScriptsLoc.toString());
});
#if 0 /// -------------- REMOVED FOR NOW --------------
addDisabledActionAndSeparator(navigateMenu, "History");

View file

@ -194,7 +194,6 @@ namespace MenuOption {
const QString TurnWithHead = "Turn using Head";
const QString UseAudioForMouth = "Use Audio for Mouth";
const QString UseCamera = "Use Camera";
const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations";
const QString VelocityFilter = "Velocity Filter";
const QString VisibleToEveryone = "Everyone";
const QString VisibleToFriends = "Friends";

View file

@ -537,6 +537,7 @@ void MyAvatar::simulate(float deltaTime) {
// we've achived our final adjusted position and rotation for the avatar
// and all of its joints, now update our attachements.
Avatar::simulateAttachments(deltaTime);
relayJointDataToChildren();
if (!_skeletonModel->hasSkeleton()) {
// All the simulation that can be done has been done
@ -1061,11 +1062,6 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) {
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene());
}
void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
AnimClip::usePreAndPostPoseFromAnim = isEnabled;
reset(true);
}
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
_skeletonModel->getRig().setEnableInverseKinematics(isEnabled);
}
@ -1929,7 +1925,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
_prevShouldDrawHead = shouldDrawHead;
}
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f;
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale());
@ -2100,7 +2096,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
_actionMotorVelocity = motorSpeed * direction;
} else {
// we're interacting with a floor --> simple horizontal speed and exponential decay
_actionMotorVelocity = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_WALKING_SPEED * direction;
_actionMotorVelocity = getSensorToWorldScale() * _walkSpeed.get() * direction;
}
float boomChange = getDriveKey(ZOOM);
@ -2692,6 +2688,14 @@ float MyAvatar::getUserEyeHeight() const {
return userHeight - userHeight * ratio;
}
float MyAvatar::getWalkSpeed() const {
return _walkSpeed.get();
}
void MyAvatar::setWalkSpeed(float value) {
_walkSpeed.set(value);
}
glm::vec3 MyAvatar::getPositionForAudio() {
switch (_audioListenerMode) {
case AudioListenerMode::FROM_HEAD:
@ -2799,14 +2803,9 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
}
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
auto cameraMode = qApp->getCamera().getMode();
if (cameraMode == CAMERA_MODE_THIRD_PERSON) {
return false;
} else {
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
}
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
}
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {

View file

@ -163,6 +163,8 @@ class MyAvatar : public Avatar {
Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT)
Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed);
const QString DOMINANT_LEFT_HAND = "left";
const QString DOMINANT_RIGHT_HAND = "right";
@ -557,6 +559,9 @@ public:
const QUuid& getSelfID() const { return AVATAR_SELF_ID; }
void setWalkSpeed(float value);
float getWalkSpeed() const;
public slots:
void increaseSize();
void decreaseSize();
@ -594,7 +599,6 @@ public slots:
bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); }
void setEnableMeshVisible(bool isEnabled);
void setUseAnimPreAndPostRotations(bool isEnabled);
void setEnableInverseKinematics(bool isEnabled);
QUrl getAnimGraphOverrideUrl() const; // thread-safe
@ -841,6 +845,9 @@ private:
// height of user in sensor space, when standing erect.
ThreadSafeValueCache<float> _userHeight { DEFAULT_AVATAR_HEIGHT };
// max unscaled forward movement speed
ThreadSafeValueCache<float> _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED };
};
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);

View file

@ -34,7 +34,6 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle
}
static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix());
// check for pinned hips.
@ -106,7 +105,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (avatarHeadPose.isValid()) {
AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation());
params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose;
params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = true;
params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled;
} else {
// even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and
// down in desktop mode.
@ -114,7 +113,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
// postMult 180 is necessary to convert head from -z forward to z forward.
glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f));
params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = false;
params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0;
}
//
@ -135,10 +134,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (controllerPose.isValid()) {
AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation());
params.primaryControllerPoses[pair.second] = avatarToRigPose * pose;
params.primaryControllerActiveFlags[pair.second] = true;
params.primaryControllerFlags[pair.second] = (uint8_t)Rig::ControllerFlags::Enabled;
} else {
params.primaryControllerPoses[pair.second] = AnimPose::identity;
params.primaryControllerActiveFlags[pair.second] = false;
params.primaryControllerFlags[pair.second] = 0;
}
}
@ -166,15 +165,15 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (controllerPose.isValid()) {
AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation());
params.secondaryControllerPoses[pair.second] = avatarToRigPose * pose;
params.secondaryControllerActiveFlags[pair.second] = true;
params.secondaryControllerFlags[pair.second] = (uint8_t)Rig::ControllerFlags::Enabled;
} else {
params.secondaryControllerPoses[pair.second] = AnimPose::identity;
params.secondaryControllerActiveFlags[pair.second] = false;
params.secondaryControllerFlags[pair.second] = 0;
}
}
// if hips are not under direct control, estimate the hips position.
if (avatarHeadPose.isValid() && !params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips]) {
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) {
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
if (!_prevHipsValid) {
@ -200,7 +199,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix());
params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips;
params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips] = true;
params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated;
} else {
_prevHipsValid = false;

View file

@ -46,6 +46,8 @@ Handler(buy)
Handler(receiveAt)
Handler(balance)
Handler(inventory)
Handler(transferHfcToNode)
Handler(transferHfcToUsername)
void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) {
auto accountManager = DependencyManager::get<AccountManager>();
@ -116,23 +118,64 @@ void Ledger::inventory(const QStringList& keys) {
keysQuery("inventory", "inventorySuccess", "inventoryFailure");
}
QString amountString(const QString& label, const QString&color, const QJsonValue& moneyValue, const QJsonValue& certsValue) {
int money = moneyValue.toInt();
int certs = certsValue.toInt();
if (money <= 0 && certs <= 0) {
return QString();
QString hfcString(const QJsonValue& sentValue, const QJsonValue& receivedValue) {
int sent = sentValue.toInt();
int received = receivedValue.toInt();
if (sent <= 0 && received <= 0) {
return QString("-");
}
QString result(QString("<font color='#%1'> %2").arg(color, label));
if (money > 0) {
result += QString(" %1 HFC").arg(money);
}
if (certs > 0) {
if (money > 0) {
result += QString(",");
QString result;
if (sent > 0) {
result += QString("<font color='#B70A37'>-%1 HFC</font>").arg(sent);
if (received > 0) {
result += QString("<br>");
}
result += QString((certs == 1) ? " %1 certificate" : " %1 certificates").arg(certs);
}
return result + QString("</font>");
if (received > 0) {
result += QString("<font color='#3AA38F'>%1 HFC</font>").arg(received);
}
return result;
}
static const QString USER_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/users/";
static const QStringList KNOWN_USERS(QStringList() << "highfidelity" << "marketplace");
QString userLink(const QString& username) {
if (username.isEmpty()) {
return QString("someone");
}
if (KNOWN_USERS.contains(username)) {
return username;
}
return QString("<a href=\"%1%2\">%2</a>").arg(USER_PAGE_BASE_URL, username);
}
QString transactionString(const QJsonObject& valueObject) {
int sentCerts = valueObject["sent_certs"].toInt();
int receivedCerts = valueObject["received_certs"].toInt();
int sent = valueObject["sent_money"].toInt();
int dateInteger = valueObject["created_at"].toInt();
QString message = valueObject["message"].toString();
QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC));
QString result;
if (sentCerts <= 0 && receivedCerts <= 0) {
// this is an hfc transfer.
if (sent > 0) {
QString recipient = userLink(valueObject["recipient_name"].toString());
result += QString("Money sent to %1").arg(recipient);
} else {
QString sender = userLink(valueObject["sender_name"].toString());
result += QString("Money from %1").arg(sender);
}
if (!message.isEmpty()) {
result += QString("<br>with memo: <i>\"%1\"</i>").arg(message);
}
} else {
result += valueObject["message"].toString();
}
// no matter what we append a smaller date to the bottom of this...
result += QString("<br><font size='-2' color='#1080B8'>%1").arg(createdAt.toLocalTime().toString(Qt::DefaultLocaleShortDate));
return result;
}
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/";
@ -155,16 +198,13 @@ void Ledger::historySuccess(QNetworkReply& reply) {
// TODO: do this with 0 copies if possible
for (auto it = historyArray.begin(); it != historyArray.end(); it++) {
// We have 2 text fields to synthesize, the one on the left is a listing
// of the HFC in/out of your wallet. The one on the right contains an explaination
// of the transaction. That could be just the memo (if it is a regular purchase), or
// more text (plus the optional memo) if an hfc transfer
auto valueObject = (*it).toObject();
QString sent = amountString("sent", "EA4C5F", valueObject["sent_money"], valueObject["sent_certs"]);
QString received = amountString("received", "1FC6A6", valueObject["received_money"], valueObject["received_certs"]);
// turns out on my machine, toLocalTime convert to some weird timezone, yet the
// systemTimeZone is correct. To avoid a strange bug with other's systems too, lets
// be explicit
QDateTime createdAt = QDateTime::fromSecsSinceEpoch(valueObject["created_at"].toInt(), Qt::UTC);
QDateTime localCreatedAt = createdAt.toTimeZone(QTimeZone::systemTimeZone());
valueObject["text"] = QString("%1%2%3").arg(valueObject["message"].toString(), sent, received);
valueObject["hfc_text"] = hfcString(valueObject["sent_money"], valueObject["received_money"]);
valueObject["transaction_text"] = transactionString(valueObject);
newHistoryArray.push_back(valueObject);
}
// now copy the rest of the json -- this is inefficient
@ -189,13 +229,6 @@ void Ledger::history(const QStringList& keys, const int& pageNumber) {
keysQuery("history", "historySuccess", "historyFailure", params);
}
// The api/failResponse is called just for the side effect of logging.
void Ledger::resetSuccess(QNetworkReply& reply) { apiResponse("reset", reply); }
void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); }
void Ledger::reset() {
send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, QJsonObject());
}
void Ledger::accountSuccess(QNetworkReply& reply) {
// lets set the appropriate stuff in the wallet now
auto wallet = DependencyManager::get<Wallet>();
@ -205,11 +238,17 @@ void Ledger::accountSuccess(QNetworkReply& reply) {
auto salt = QByteArray::fromBase64(data["salt"].toString().toUtf8());
auto iv = QByteArray::fromBase64(data["iv"].toString().toUtf8());
auto ckey = QByteArray::fromBase64(data["ckey"].toString().toUtf8());
QString remotePublicKey = data["public_key"].toString();
wallet->setSalt(salt);
wallet->setIv(iv);
wallet->setCKey(ckey);
QStringList localPublicKeys = wallet->listPublicKeys();
if (remotePublicKey.isEmpty() && !localPublicKeys.isEmpty()) {
receiveAt(localPublicKeys.first(), "");
}
// none of the hfc account info should be emitted
emit accountResult(QJsonObject{ {"status", "success"} });
}
@ -268,3 +307,25 @@ void Ledger::certificateInfo(const QString& certificateId) {
request["certificate_id"] = certificateId;
send(endpoint, "certificateInfoSuccess", "certificateInfoFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::None, request);
}
void Ledger::transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage) {
QJsonObject transaction;
transaction["public_key"] = hfc_key;
transaction["node_id"] = nodeID;
transaction["quantity"] = amount;
transaction["message"] = optionalMessage;
QJsonDocument transactionDoc{ transaction };
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_node", "transferHfcToNodeSuccess", "transferHfcToNodeFailure");
}
void Ledger::transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage) {
QJsonObject transaction;
transaction["public_key"] = hfc_key;
transaction["username"] = username;
transaction["quantity"] = amount;
transaction["message"] = optionalMessage;
QJsonDocument transactionDoc{ transaction };
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_user", "transferHfcToUsernameSuccess", "transferHfcToUsernameFailure");
}

View file

@ -31,9 +31,10 @@ public:
void inventory(const QStringList& keys);
void history(const QStringList& keys, const int& pageNumber);
void account();
void reset();
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
void certificateInfo(const QString& certificateId);
void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage);
void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage);
enum CertificateStatus {
CERTIFICATE_STATUS_UNKNOWN = 0,
@ -52,6 +53,8 @@ signals:
void accountResult(QJsonObject result);
void locationUpdateResult(QJsonObject result);
void certificateInfoResult(QJsonObject result);
void transferHfcToNodeResult(QJsonObject result);
void transferHfcToUsernameResult(QJsonObject result);
void updateCertificateStatus(const QString& certID, uint certStatus);
@ -66,14 +69,16 @@ public slots:
void inventoryFailure(QNetworkReply& reply);
void historySuccess(QNetworkReply& reply);
void historyFailure(QNetworkReply& reply);
void resetSuccess(QNetworkReply& reply);
void resetFailure(QNetworkReply& reply);
void accountSuccess(QNetworkReply& reply);
void accountFailure(QNetworkReply& reply);
void updateLocationSuccess(QNetworkReply& reply);
void updateLocationFailure(QNetworkReply& reply);
void certificateInfoSuccess(QNetworkReply& reply);
void certificateInfoFailure(QNetworkReply& reply);
void transferHfcToNodeSuccess(QNetworkReply& reply);
void transferHfcToNodeFailure(QNetworkReply& reply);
void transferHfcToUsernameSuccess(QNetworkReply& reply);
void transferHfcToUsernameFailure(QNetworkReply& reply);
private:
QJsonObject apiResponse(const QString& label, QNetworkReply& reply);

View file

@ -29,6 +29,8 @@ QmlCommerce::QmlCommerce() {
connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult);
connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult);
connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus);
connect(ledger.data(), &Ledger::transferHfcToNodeResult, this, &QmlCommerce::transferHfcToNodeResult);
connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult);
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
@ -128,18 +130,6 @@ void QmlCommerce::generateKeyPair() {
getWalletAuthenticatedStatus();
}
void QmlCommerce::reset() {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
ledger->reset();
wallet->reset();
}
void QmlCommerce::resetLocalWalletOnly() {
auto wallet = DependencyManager::get<Wallet>();
wallet->reset();
}
void QmlCommerce::account() {
auto ledger = DependencyManager::get<Ledger>();
ledger->account();
@ -149,3 +139,27 @@ void QmlCommerce::certificateInfo(const QString& certificateId) {
auto ledger = DependencyManager::get<Ledger>();
ledger->certificateInfo(certificateId);
}
void QmlCommerce::transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
if (keys.count() == 0) {
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
return emit buyResult(result);
}
QString key = keys[0];
ledger->transferHfcToNode(key, nodeID, amount, optionalMessage);
}
void QmlCommerce::transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
if (keys.count() == 0) {
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
return emit buyResult(result);
}
QString key = keys[0];
ledger->transferHfcToUsername(key, username, amount, optionalMessage);
}

View file

@ -45,6 +45,9 @@ signals:
void updateCertificateStatus(const QString& certID, uint certStatus);
void transferHfcToNodeResult(QJsonObject result);
void transferHfcToUsernameResult(QJsonObject result);
protected:
Q_INVOKABLE void getWalletStatus();
@ -62,11 +65,12 @@ protected:
Q_INVOKABLE void inventory();
Q_INVOKABLE void history(const int& pageNumber);
Q_INVOKABLE void generateKeyPair();
Q_INVOKABLE void reset();
Q_INVOKABLE void resetLocalWalletOnly();
Q_INVOKABLE void account();
Q_INVOKABLE void certificateInfo(const QString& certificateId);
Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage);
Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage);
};
#endif // hifi_QmlCommerce_h

View file

@ -144,15 +144,13 @@ bool writeKeys(const char* filename, EC_KEY* keys) {
if ((fp = fopen(filename, "wt"))) {
if (!PEM_write_EC_PUBKEY(fp, keys)) {
fclose(fp);
qCDebug(commerce) << "failed to write public key";
QFile(QString(filename)).remove();
qCCritical(commerce) << "failed to write public key";
return retval;
}
if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
fclose(fp);
qCDebug(commerce) << "failed to write private key";
QFile(QString(filename)).remove();
qCCritical(commerce) << "failed to write private key";
return retval;
}
@ -168,7 +166,8 @@ bool writeKeys(const char* filename, EC_KEY* keys) {
QPair<QByteArray*, QByteArray*> generateECKeypair() {
EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1);
QPair<QByteArray*, QByteArray*> retval;
QPair<QByteArray*, QByteArray*> retval{};
EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE);
if (!EC_KEY_generate_key(keyPair)) {
qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error();
@ -517,6 +516,9 @@ bool Wallet::generateKeyPair() {
qCInfo(commerce) << "Generating keypair.";
auto keyPair = generateECKeypair();
if (!keyPair.first) {
return false;
}
writeBackupInstructions();
@ -653,20 +655,6 @@ QString Wallet::getKeyFilePath() {
}
}
void Wallet::reset() {
_publicKeys.clear();
delete _securityImage;
_securityImage = nullptr;
// tell the provider we got nothing
updateImageProvider();
_passphrase->clear();
QFile keyFile(keyFilePath());
keyFile.remove();
}
bool Wallet::writeWallet(const QString& newPassphrase) {
EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str());
if (keys) {

View file

@ -49,8 +49,6 @@ public:
bool walletIsAuthenticatedWithPassphrase();
bool changePassphrase(const QString& newPassphrase);
void reset();
void getWalletStatus();
enum WalletStatus {
WALLET_STATUS_NOT_LOGGED_IN = 0,

View file

@ -1,36 +0,0 @@
//
// AccountScriptingInterface.cpp
// interface/src/scripting
//
// Created by Stojce Slavkovski on 6/07/14.
// Copyright 2014 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 "AccountManager.h"
#include "AccountScriptingInterface.h"
#include "GlobalServicesScriptingInterface.h"
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
static AccountScriptingInterface sharedInstance;
return &sharedInstance;
}
bool AccountScriptingInterface::isLoggedIn() {
return GlobalServicesScriptingInterface::getInstance()->isLoggedIn();
}
void AccountScriptingInterface::logOut() {
GlobalServicesScriptingInterface::getInstance()->logOut();
}
bool AccountScriptingInterface::loggedIn() const {
return GlobalServicesScriptingInterface::getInstance()->loggedIn();
}
QString AccountScriptingInterface::getUsername() {
return GlobalServicesScriptingInterface::getInstance()->getUsername();
}

View file

@ -1,60 +0,0 @@
//
// AccountScriptingInterface.h
// interface/src/scripting
//
// Created by Stojce Slavkovski on 6/07/14.
// Copyright 2014 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_AccountScriptingInterface_h
#define hifi_AccountScriptingInterface_h
#include <QObject>
class AccountScriptingInterface : public QObject {
Q_OBJECT
/**jsdoc
* @namespace Account
* @property username {String} username if user is logged in, otherwise it returns "Unknown user"
*/
Q_PROPERTY(QString username READ getUsername)
Q_PROPERTY(bool loggedIn READ loggedIn)
signals:
/**jsdoc
* Triggered when username has changed.
* @function Account.usernameChanged
* @return {Signal}
*/
void usernameChanged();
void loggedInChanged(bool loggedIn);
public slots:
static AccountScriptingInterface* getInstance();
/**jsdoc
* Returns the username for the currently logged in High Fidelity metaverse account.
* @function Account.getUsername
* @return {string} username if user is logged in, otherwise it returns "Unknown user"
*/
QString getUsername();
/**jsdoc
* Determine if the user is logged into the High Fidleity metaverse.
* @function Account.isLoggedIn
* @return {bool} true when user is logged into the High Fidelity metaverse.
*/
bool isLoggedIn();
void logOut();
public:
AccountScriptingInterface(QObject* parent = nullptr) {}
bool loggedIn() const;
};
#endif // hifi_AccountScriptingInterface_h

View file

@ -1,5 +1,5 @@
//
// GlobalServicesScriptingInterface.cpp
// AccountServicesScriptingInterface.cpp
// interface/src/scripting
//
// Created by Thijs Wenker on 9/10/14.
@ -14,41 +14,41 @@
#include "DiscoverabilityManager.h"
#include "ResourceCache.h"
#include "GlobalServicesScriptingInterface.h"
#include "AccountServicesScriptingInterface.h"
GlobalServicesScriptingInterface::GlobalServicesScriptingInterface() {
AccountServicesScriptingInterface::AccountServicesScriptingInterface() {
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::onUsernameChanged);
connect(accountManager.data(), &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut);
connect(accountManager.data(), &AccountManager::loginComplete, this, &GlobalServicesScriptingInterface::connected);
connect(accountManager.data(), &AccountManager::usernameChanged, this, &AccountServicesScriptingInterface::onUsernameChanged);
connect(accountManager.data(), &AccountManager::logoutComplete, this, &AccountServicesScriptingInterface::loggedOut);
connect(accountManager.data(), &AccountManager::loginComplete, this, &AccountServicesScriptingInterface::connected);
_downloading = false;
QTimer* checkDownloadTimer = new QTimer(this);
connect(checkDownloadTimer, &QTimer::timeout, this, &GlobalServicesScriptingInterface::checkDownloadInfo);
connect(checkDownloadTimer, &QTimer::timeout, this, &AccountServicesScriptingInterface::checkDownloadInfo);
const int CHECK_DOWNLOAD_INTERVAL = MSECS_PER_SECOND / 2;
checkDownloadTimer->start(CHECK_DOWNLOAD_INTERVAL);
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
this, &GlobalServicesScriptingInterface::discoverabilityModeChanged);
this, &AccountServicesScriptingInterface::discoverabilityModeChanged);
_loggedIn = isLoggedIn();
emit loggedInChanged(_loggedIn);
}
GlobalServicesScriptingInterface::~GlobalServicesScriptingInterface() {
AccountServicesScriptingInterface::~AccountServicesScriptingInterface() {
auto accountManager = DependencyManager::get<AccountManager>();
disconnect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::onUsernameChanged);
disconnect(accountManager.data(), &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut);
disconnect(accountManager.data(), &AccountManager::loginComplete, this, &GlobalServicesScriptingInterface::connected);
disconnect(accountManager.data(), &AccountManager::usernameChanged, this, &AccountServicesScriptingInterface::onUsernameChanged);
disconnect(accountManager.data(), &AccountManager::logoutComplete, this, &AccountServicesScriptingInterface::loggedOut);
disconnect(accountManager.data(), &AccountManager::loginComplete, this, &AccountServicesScriptingInterface::connected);
}
GlobalServicesScriptingInterface* GlobalServicesScriptingInterface::getInstance() {
static GlobalServicesScriptingInterface sharedInstance;
AccountServicesScriptingInterface* AccountServicesScriptingInterface::getInstance() {
static AccountServicesScriptingInterface sharedInstance;
return &sharedInstance;
}
const QString GlobalServicesScriptingInterface::getUsername() const {
const QString AccountServicesScriptingInterface::getUsername() const {
auto accountManager = DependencyManager::get<AccountManager>();
if (accountManager->isLoggedIn()) {
return accountManager->getAccountInfo().getUsername();
@ -57,31 +57,31 @@ const QString GlobalServicesScriptingInterface::getUsername() const {
}
}
bool GlobalServicesScriptingInterface::isLoggedIn() {
bool AccountServicesScriptingInterface::isLoggedIn() {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->isLoggedIn();
}
bool GlobalServicesScriptingInterface::checkAndSignalForAccessToken() {
bool AccountServicesScriptingInterface::checkAndSignalForAccessToken() {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->checkAndSignalForAccessToken();
}
void GlobalServicesScriptingInterface::logOut() {
void AccountServicesScriptingInterface::logOut() {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->logout();
}
void GlobalServicesScriptingInterface::loggedOut() {
emit GlobalServicesScriptingInterface::disconnected(QString("logout"));
void AccountServicesScriptingInterface::loggedOut() {
emit AccountServicesScriptingInterface::disconnected(QString("logout"));
}
QString GlobalServicesScriptingInterface::getFindableBy() const {
QString AccountServicesScriptingInterface::getFindableBy() const {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
return DiscoverabilityManager::findableByString(discoverabilityManager->getDiscoverabilityMode());
}
void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) {
void AccountServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
if (discoverabilityMode.toLower() == "none") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::None);
@ -96,11 +96,11 @@ void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabil
}
}
void GlobalServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) {
void AccountServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) {
emit findableByChanged(DiscoverabilityManager::findableByString(discoverabilityMode));
}
void GlobalServicesScriptingInterface::onUsernameChanged(const QString& username) {
void AccountServicesScriptingInterface::onUsernameChanged(const QString& username) {
_loggedIn = (username != QString());
emit myUsernameChanged(username);
emit loggedInChanged(_loggedIn);
@ -135,7 +135,7 @@ void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoR
result.pending = object.property("pending").toVariant().toFloat();
}
DownloadInfoResult GlobalServicesScriptingInterface::getDownloadInfo() {
DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() {
DownloadInfoResult result;
foreach(const auto& resource, ResourceCache::getLoadingRequests()) {
result.downloading.append(resource->getProgress() * 100.0f);
@ -144,7 +144,7 @@ DownloadInfoResult GlobalServicesScriptingInterface::getDownloadInfo() {
return result;
}
void GlobalServicesScriptingInterface::checkDownloadInfo() {
void AccountServicesScriptingInterface::checkDownloadInfo() {
DownloadInfoResult downloadInfo = getDownloadInfo();
bool downloading = downloadInfo.downloading.count() > 0 || downloadInfo.pending > 0;
@ -155,7 +155,7 @@ void GlobalServicesScriptingInterface::checkDownloadInfo() {
}
}
void GlobalServicesScriptingInterface::updateDownloadInfo() {
void AccountServicesScriptingInterface::updateDownloadInfo() {
emit downloadInfoChanged(getDownloadInfo());
}

View file

@ -1,5 +1,5 @@
//
// GlobalServicesScriptingInterface.h
// AccountServicesScriptingInterface.h
// interface/src/scripting
//
// Created by Thijs Wenker on 9/10/14.
@ -9,8 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_GlobalServicesScriptingInterface_h
#define hifi_GlobalServicesScriptingInterface_h
#ifndef hifi_AccountServicesScriptingInterface_h
#define hifi_AccountServicesScriptingInterface_h
#include <QObject>
#include <QScriptContext>
@ -32,7 +32,7 @@ Q_DECLARE_METATYPE(DownloadInfoResult)
QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result);
void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoResult& result);
class GlobalServicesScriptingInterface : public QObject {
class AccountServicesScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged)
@ -41,7 +41,7 @@ class GlobalServicesScriptingInterface : public QObject {
Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL)
public:
static GlobalServicesScriptingInterface* getInstance();
static AccountServicesScriptingInterface* getInstance();
const QString getUsername() const;
bool loggedIn() const { return _loggedIn; }
@ -74,11 +74,11 @@ signals:
void loggedInChanged(bool loggedIn);
private:
GlobalServicesScriptingInterface();
~GlobalServicesScriptingInterface();
AccountServicesScriptingInterface();
~AccountServicesScriptingInterface();
bool _downloading;
bool _loggedIn{ false };
};
#endif // hifi_GlobalServicesScriptingInterface_h
#endif // hifi_AccountServicesScriptingInterface_h

View file

@ -49,6 +49,8 @@ void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptR
WindowScriptingInterface::WindowScriptingInterface() {
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged);
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &WindowScriptingInterface::disconnectedFromDomain);
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused);
connect(qApp, &Application::svoImportRequested, [this](const QString& urlString) {
@ -134,6 +136,10 @@ void WindowScriptingInterface::promptAsync(const QString& message, const QString
});
}
void WindowScriptingInterface::disconnectedFromDomain() {
emit domainChanged("");
}
CustomPromptResult WindowScriptingInterface::customPrompt(const QVariant& config) {
CustomPromptResult result;
bool ok = false;
@ -176,10 +182,6 @@ bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
return offscreenUi->isPointOnDesktopWindow(point);
}
glm::vec2 WindowScriptingInterface::getDeviceSize() const {
return qApp->getDeviceSize();
}
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
/// might be in same thread as a script that sets the reticle to invisible
void WindowScriptingInterface::ensureReticleVisible() const {
@ -389,11 +391,15 @@ QString WindowScriptingInterface::checkVersion() {
}
int WindowScriptingInterface::getInnerWidth() {
return qApp->getWindow()->geometry().width();
return qApp->getDeviceSize().x;
}
int WindowScriptingInterface::getInnerHeight() {
return qApp->getWindow()->geometry().height();
return qApp->getDeviceSize().y;
}
glm::vec2 WindowScriptingInterface::getDeviceSize() const {
return qApp->getDeviceSize();
}
int WindowScriptingInterface::getX() {
@ -405,6 +411,11 @@ int WindowScriptingInterface::getY() {
}
void WindowScriptingInterface::copyToClipboard(const QString& text) {
if (QThread::currentThread() != qApp->thread()) {
QMetaObject::invokeMethod(this, "copyToClipboard", Q_ARG(QString, text));
return;
}
qDebug() << "Copying";
QApplication::clipboard()->setText(text);
}

View file

@ -1,5 +1,5 @@
//
// WindowScriptingInterface.cpp
// WindowScriptingInterface.h
// interface/src/scripting
//
// Created by Ryan Huffman on 4/29/14.
@ -42,7 +42,7 @@ void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptR
* @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other
* chrome), in pixels. <em>Read-only.</em>
* @property {number} innerHeight - The height of the drawable area of the Interface window (i.e., without borders or other
* chrome) plus the height of the menu bar, in pixels. <em>Read-only.</em>
* chrome), in pixels. <em>Read-only.</em>
* @property {object} location - Provides facilities for working with your current metaverse location. See {@link location}.
* @property {number} x - The x coordinate of the top left corner of the Interface window on the display. <em>Read-only.</em>
* @property {number} y - The y coordinate of the top left corner of the Interface window on the display. <em>Read-only.</em>
@ -54,6 +54,7 @@ class WindowScriptingInterface : public QObject, public Dependency {
Q_PROPERTY(int innerHeight READ getInnerHeight)
Q_PROPERTY(int x READ getX)
Q_PROPERTY(int y READ getY)
public:
WindowScriptingInterface();
~WindowScriptingInterface();
@ -143,7 +144,7 @@ public slots:
/**jsdoc
* Prompt the user for input in a custom, modal dialog.
* @deprecated This funtion is deprecated and will be removed.
* @deprecated This function is deprecated and will soon be removed.
* @function Window.customPrompt
* @param {object} config - Configures the modal dialog.
* @returns {object} The user's response.
@ -300,7 +301,7 @@ public slots:
/**jsdoc
* Get Interface's build number.
* @function Window.checkVersion
* @returns {string} - Interface's build number.
* @returns {string} Interface's build number.
*/
QString checkVersion();
@ -326,7 +327,7 @@ public slots:
* full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the
* dimensions is adjusted in order to match the aspect ratio.
* @example <caption>Using the snapshot function and signals.</caption>
* function onStillSnapshottaken(path, notify) {
* function onStillSnapshotTaken(path, notify) {
* print("Still snapshot taken: " + path);
* print("Notify: " + notify);
* }
@ -339,7 +340,7 @@ public slots:
* print("Animated snapshot taken: " + animatedPath);
* }
*
* Window.stillSnapshotTaken.connect(onStillSnapshottaken);
* Window.stillSnapshotTaken.connect(onStillSnapshotTaken);
* Window.processingGifStarted.connect(onProcessingGifStarted);
* Window.processingGifCompleted.connect(onProcessingGifCompleted);
*
@ -515,6 +516,7 @@ public slots:
private slots:
void onMessageBoxSelected(int button);
void disconnectedFromDomain();
signals:
@ -553,7 +555,7 @@ signals:
/**jsdoc
* Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with
* <code>includeAnimated = false</code>.
* <code>includeAnimated = false</code> or {@link Window.takeSecondaryCameraSnapshot|takeSecondaryCameraSnapshot}.
* @function Window.stillSnapshotTaken
* @param {string} pathStillSnapshot - The path and name of the snapshot image file.
* @param {boolean} notify - The value of the <code>notify</code> parameter that {@link Window.takeSnapshot|takeSnapshot}

View file

@ -210,8 +210,8 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -386,8 +386,6 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -397,10 +395,8 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -154,8 +154,6 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -165,10 +163,8 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -132,8 +132,6 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -143,10 +141,8 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -208,8 +208,6 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -219,10 +217,8 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -275,8 +275,6 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -286,10 +284,8 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -306,8 +306,6 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -317,10 +315,8 @@ vectorType ModelOverlay::mapJoints(mapFunction<itemType> function) const {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -31,8 +31,7 @@ Overlay::Overlay() :
_alphaPulse(0.0f),
_colorPulse(0.0f),
_color(DEFAULT_OVERLAY_COLOR),
_visible(true),
_anchor(NO_ANCHOR)
_visible(true)
{
}
@ -49,8 +48,7 @@ Overlay::Overlay(const Overlay* overlay) :
_alphaPulse(overlay->_alphaPulse),
_colorPulse(overlay->_colorPulse),
_color(overlay->_color),
_visible(overlay->_visible),
_anchor(overlay->_anchor)
_visible(overlay->_visible)
{
}
@ -92,13 +90,6 @@ void Overlay::setProperties(const QVariantMap& properties) {
bool visible = properties["visible"].toBool();
setVisible(visible);
}
if (properties["anchor"].isValid()) {
QString property = properties["anchor"].toString();
if (property == "MyAvatar") {
setAnchor(MY_AVATAR);
}
}
}
// JSDoc for copying to @typedefs of overlay types that inherit Overlay.
@ -119,8 +110,6 @@ void Overlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*/
QVariant Overlay::getProperty(const QString& property) {
if (property == "type") {
@ -150,9 +139,6 @@ QVariant Overlay::getProperty(const QString& property) {
if (property == "visible") {
return _visible;
}
if (property == "anchor") {
return _anchor == MY_AVATAR ? "MyAvatar" : "";
}
return QVariant();
}

View file

@ -26,11 +26,6 @@ class Overlay : public QObject {
Q_OBJECT
public:
enum Anchor {
NO_ANCHOR,
MY_AVATAR
};
typedef std::shared_ptr<Overlay> Pointer;
typedef render::Payload<Overlay> Payload;
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
@ -63,7 +58,6 @@ public:
virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; };
xColor getColor();
float getAlpha();
Anchor getAnchor() const { return _anchor; }
float getPulseMax() const { return _pulseMax; }
float getPulseMin() const { return _pulseMin; }
@ -78,7 +72,6 @@ public:
void setDrawHUDLayer(bool drawHUDLayer);
void setColor(const xColor& color) { _color = color; }
void setAlpha(float alpha) { _alpha = alpha; }
void setAnchor(Anchor anchor) { _anchor = anchor; }
void setPulseMax(float value) { _pulseMax = value; }
void setPulseMin(float value) { _pulseMin = value; }
@ -118,7 +111,6 @@ protected:
xColor _color;
bool _visible; // should the overlay be drawn at all
Anchor _anchor;
unsigned int _stackOrder { 0 };

View file

@ -1,190 +0,0 @@
//
// OverlayPanel.cpp
// interface/src/ui/overlays
//
// Created by Zander Otavka on 7/2/15.
// Copyright 2014 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 "OverlayPanel.h"
#if OVERLAY_PANELS
#include <QVariant>
#include <RegisteredMetaTypes.h>
#include <DependencyManager.h>
#include <EntityScriptingInterface.h>
#include "avatar/AvatarManager.h"
#include "avatar/MyAvatar.h"
#include "Base3DOverlay.h"
PropertyBinding::PropertyBinding(QString avatar, QUuid entity) :
avatar(avatar),
entity(entity)
{
}
QVariant propertyBindingToVariant(const PropertyBinding& value) {
QVariantMap obj;
if (value.avatar == "MyAvatar") {
obj["avatar"] = "MyAvatar";
} else if (!value.entity.isNull()) {
obj["entity"] = value.entity;
}
return obj;
}
void propertyBindingFromVariant(const QVariant& objectVar, PropertyBinding& value) {
auto object = objectVar.toMap();
auto avatar = object["avatar"];
auto entity = object["entity"];
if (avatar.isValid() && !avatar.isNull()) {
value.avatar = avatar.toString();
} else if (entity.isValid() && !entity.isNull()) {
value.entity = entity.toUuid();
}
}
void OverlayPanel::addChild(OverlayID childId) {
if (!_children.contains(childId)) {
_children.append(childId);
}
}
void OverlayPanel::removeChild(OverlayID childId) {
if (_children.contains(childId)) {
_children.removeOne(childId);
}
}
QVariant OverlayPanel::getProperty(const QString &property) {
if (property == "anchorPosition") {
return vec3toVariant(getAnchorPosition());
}
if (property == "anchorPositionBinding") {
return propertyBindingToVariant(PropertyBinding(_anchorPositionBindMyAvatar ?
"MyAvatar" : "",
_anchorPositionBindEntity));
}
if (property == "anchorRotation") {
return quatToVariant(getAnchorRotation());
}
if (property == "anchorRotationBinding") {
return propertyBindingToVariant(PropertyBinding(_anchorRotationBindMyAvatar ?
"MyAvatar" : "",
_anchorRotationBindEntity));
}
if (property == "anchorScale") {
return vec3toVariant(getAnchorScale());
}
if (property == "visible") {
return getVisible();
}
if (property == "children") {
QVariantList array;
for (int i = 0; i < _children.length(); i++) {
array.append(OverlayIDtoScriptValue(nullptr, _children[i]).toVariant());
}
return array;
}
auto value = Billboardable::getProperty(property);
if (value.isValid()) {
return value;
}
return PanelAttachable::getProperty(property);
}
void OverlayPanel::setProperties(const QVariantMap& properties) {
PanelAttachable::setProperties(properties);
Billboardable::setProperties(properties);
auto anchorPosition = properties["anchorPosition"];
if (anchorPosition.isValid()) {
setAnchorPosition(vec3FromVariant(anchorPosition));
}
auto anchorPositionBinding = properties["anchorPositionBinding"];
if (anchorPositionBinding.isValid()) {
PropertyBinding binding = {};
propertyBindingFromVariant(anchorPositionBinding, binding);
_anchorPositionBindMyAvatar = binding.avatar == "MyAvatar";
_anchorPositionBindEntity = binding.entity;
}
auto anchorRotation = properties["anchorRotation"];
if (anchorRotation.isValid()) {
setAnchorRotation(quatFromVariant(anchorRotation));
}
auto anchorRotationBinding = properties["anchorRotationBinding"];
if (anchorRotationBinding.isValid()) {
PropertyBinding binding = {};
propertyBindingFromVariant(anchorPositionBinding, binding);
_anchorRotationBindMyAvatar = binding.avatar == "MyAvatar";
_anchorRotationBindEntity = binding.entity;
}
auto anchorScale = properties["anchorScale"];
if (anchorScale.isValid()) {
setAnchorScale(vec3FromVariant(anchorScale));
}
auto visible = properties["visible"];
if (visible.isValid()) {
setVisible(visible.toBool());
}
}
void OverlayPanel::applyTransformTo(Transform& transform, bool force) {
if (force || usecTimestampNow() > _transformExpiry) {
PanelAttachable::applyTransformTo(transform, true);
if (!getParentPanel()) {
if (_anchorPositionBindMyAvatar) {
transform.setTranslation(DependencyManager::get<AvatarManager>()->getMyAvatar()
->getPosition());
} else if (!_anchorPositionBindEntity.isNull()) {
EntityTreePointer entityTree = DependencyManager::get<EntityScriptingInterface>()->getEntityTree();
entityTree->withReadLock([&] {
EntityItemPointer foundEntity = entityTree->findEntityByID(_anchorPositionBindEntity);
if (foundEntity) {
transform.setTranslation(foundEntity->getPosition());
}
});
} else {
transform.setTranslation(getAnchorPosition());
}
if (_anchorRotationBindMyAvatar) {
transform.setRotation(DependencyManager::get<AvatarManager>()->getMyAvatar()
->getOrientation());
} else if (!_anchorRotationBindEntity.isNull()) {
EntityTreePointer entityTree = DependencyManager::get<EntityScriptingInterface>()->getEntityTree();
entityTree->withReadLock([&] {
EntityItemPointer foundEntity = entityTree->findEntityByID(_anchorRotationBindEntity);
if (foundEntity) {
transform.setRotation(foundEntity->getRotation());
}
});
} else {
transform.setRotation(getAnchorRotation());
}
transform.setScale(getAnchorScale());
transform.postTranslate(getOffsetPosition());
transform.postRotate(getOffsetRotation());
transform.postScale(getOffsetScale());
}
pointTransformAtCamera(transform, getOffsetRotation());
}
}
#endif

View file

@ -1,86 +0,0 @@
//
// OverlayPanel.h
// interface/src/ui/overlays
//
// Created by Zander Otavka on 7/2/15.
// Copyright 2014 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_OverlayPanel_h
#define hifi_OverlayPanel_h
#include <memory>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QUuid>
#include "PanelAttachable.h"
#include "Billboardable.h"
#include "Overlay.h"
#if OVERLAY_PANELS
class PropertyBinding {
public:
PropertyBinding() {}
PropertyBinding(QString avatar, QUuid entity);
QString avatar;
QUuid entity;
};
QVariant propertyBindingToVariant(const PropertyBinding& value);
void propertyBindingFromVariant(const QVariant& object, PropertyBinding& value);
class OverlayPanel : public QObject, public PanelAttachable, public Billboardable {
Q_OBJECT
public:
typedef std::shared_ptr<OverlayPanel> Pointer;
void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; }
// getters
glm::vec3 getAnchorPosition() const { return _anchorTransform.getTranslation(); }
glm::quat getAnchorRotation() const { return _anchorTransform.getRotation(); }
glm::vec3 getAnchorScale() const { return _anchorTransform.getScale(); }
bool getVisible() const { return _visible; }
// setters
void setAnchorPosition(const glm::vec3& position) { _anchorTransform.setTranslation(position); }
void setAnchorRotation(const glm::quat& rotation) { _anchorTransform.setRotation(rotation); }
void setAnchorScale(float scale) { _anchorTransform.setScale(scale); }
void setAnchorScale(const glm::vec3& scale) { _anchorTransform.setScale(scale); }
void setVisible(bool visible) { _visible = visible; }
const QList<OverlayID>& getChildren() { return _children; }
void addChild(OverlayID childId);
void removeChild(OverlayID childId);
OverlayID popLastChild() { return _children.takeLast(); }
void setProperties(const QVariantMap& properties);
QVariant getProperty(const QString& property);
virtual void applyTransformTo(Transform& transform, bool force = false) override;
private:
Transform _anchorTransform;
bool _anchorPositionBindMyAvatar = false;
QUuid _anchorPositionBindEntity;
bool _anchorRotationBindMyAvatar = false;
QUuid _anchorRotationBindEntity;
bool _visible = true;
QList<OverlayID> _children;
QScriptEngine* _scriptEngine;
};
#endif
#endif // hifi_OverlayPanel_h

View file

@ -68,16 +68,10 @@ void Overlays::cleanupAllOverlays() {
foreach(Overlay::Pointer overlay, overlaysWorld) {
_overlaysToDelete.push_back(overlay);
}
#if OVERLAY_PANELS
_panels.clear();
#endif
cleanupOverlaysToDelete();
}
void Overlays::init() {
#if OVERLAY_PANELS
_scriptEngine = new QScriptEngine();
#endif
}
void Overlays::update(float deltatime) {
@ -300,12 +294,6 @@ OverlayID Overlays::cloneOverlay(OverlayID id) {
if (thisOverlay) {
OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); }));
#if OVERLAY_PANELS
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(thisOverlay);
if (attachable && attachable->getParentPanel()) {
attachable->getParentPanel()->addChild(cloneId);
}
#endif
return cloneId;
}
@ -381,15 +369,6 @@ void Overlays::deleteOverlay(OverlayID id) {
}
}
#if OVERLAY_PANELS
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlayToDelete);
if (attachable && attachable->getParentPanel()) {
attachable->getParentPanel()->removeChild(id);
attachable->setParentPanel(nullptr);
}
#endif
_overlaysToDelete.push_back(overlayToDelete);
emit overlayDeleted(id);
}
@ -424,49 +403,6 @@ QObject* Overlays::getOverlayObject(OverlayID id) {
return nullptr;
}
#if OVERLAY_PANELS
OverlayID Overlays::getParentPanel(OverlayID childId) const {
Overlay::Pointer overlay = getOverlay(childId);
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(overlay);
if (attachable) {
return _panels.key(attachable->getParentPanel());
} else if (_panels.contains(childId)) {
return _panels.key(getPanel(childId)->getParentPanel());
}
return UNKNOWN_OVERLAY_ID;
}
void Overlays::setParentPanel(OverlayID childId, OverlayID panelId) {
auto attachable = std::dynamic_pointer_cast<PanelAttachable>(getOverlay(childId));
if (attachable) {
if (_panels.contains(panelId)) {
auto panel = getPanel(panelId);
panel->addChild(childId);
attachable->setParentPanel(panel);
} else {
auto panel = attachable->getParentPanel();
if (panel) {
panel->removeChild(childId);
attachable->setParentPanel(nullptr);
}
}
} else if (_panels.contains(childId)) {
OverlayPanel::Pointer child = getPanel(childId);
if (_panels.contains(panelId)) {
auto panel = getPanel(panelId);
panel->addChild(childId);
child->setParentPanel(panel);
} else {
auto panel = child->getParentPanel();
if (panel) {
panel->removeChild(childId);
child->setParentPanel(0);
}
}
}
}
#endif
OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
if (!_enabled) {
return UNKNOWN_OVERLAY_ID;
@ -717,62 +653,6 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) {
return QSizeF(0.0f, 0.0f);
}
#if OVERLAY_PANELS
OverlayID Overlays::addPanel(OverlayPanel::Pointer panel) {
QWriteLocker lock(&_lock);
OverlayID thisID = QUuid::createUuid();
_panels[thisID] = panel;
return thisID;
}
OverlayID Overlays::addPanel(const QVariant& properties) {
OverlayPanel::Pointer panel = std::make_shared<OverlayPanel>();
panel->init(_scriptEngine);
panel->setProperties(properties.toMap());
return addPanel(panel);
}
void Overlays::editPanel(OverlayID panelId, const QVariant& properties) {
if (_panels.contains(panelId)) {
_panels[panelId]->setProperties(properties.toMap());
}
}
OverlayPropertyResult Overlays::getPanelProperty(OverlayID panelId, const QString& property) {
OverlayPropertyResult result;
if (_panels.contains(panelId)) {
OverlayPanel::Pointer thisPanel = getPanel(panelId);
QReadLocker lock(&_lock);
result.value = thisPanel->getProperty(property);
}
return result;
}
void Overlays::deletePanel(OverlayID panelId) {
OverlayPanel::Pointer panelToDelete;
{
QWriteLocker lock(&_lock);
if (_panels.contains(panelId)) {
panelToDelete = _panels.take(panelId);
} else {
return;
}
}
while (!panelToDelete->getChildren().isEmpty()) {
OverlayID childId = panelToDelete->popLastChild();
deleteOverlay(childId);
deletePanel(childId);
}
emit panelDeleted(panelId);
}
#endif
bool Overlays::isAddedOverlay(OverlayID id) {
if (QThread::currentThread() != thread()) {
bool result;

View file

@ -27,7 +27,6 @@
#include "Overlay.h"
#include "PanelAttachable.h"
#include "OverlayPanel.h"
class PickRay;
@ -93,9 +92,6 @@ public:
void enable();
Overlay::Pointer getOverlay(OverlayID id) const;
#if OVERLAY_PANELS
OverlayPanel::Pointer getPanel(OverlayID id) const { return _panels[id]; }
#endif
/// adds an overlay that's already been created
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
@ -233,6 +229,7 @@ public slots:
/**jsdoc
* Get the overlay script object.
* @function Overlays.getOverlayObject
* @deprecated This function is deprecated and will soon be removed.
* @param {Uuid} overlayID - The ID of the overlay to get the script object of.
* @returns {object} The script object for the overlay if found.
*/
@ -467,30 +464,6 @@ public slots:
*/
bool isAddedOverlay(OverlayID id);
#if OVERLAY_PANELS
OverlayID getParentPanel(OverlayID childId) const;
void setParentPanel(OverlayID childId, OverlayID panelId);
/// adds a panel that has already been created
OverlayID addPanel(OverlayPanel::Pointer panel);
/// creates and adds a panel based on a set of properties
OverlayID addPanel(const QVariant& properties);
/// edit the properties of a panel
void editPanel(OverlayID panelId, const QVariant& properties);
/// get a property of a panel
OverlayPropertyResult getPanelProperty(OverlayID panelId, const QString& property);
/// deletes a panel and all child overlays
void deletePanel(OverlayID panelId);
/// return true if there is a panel with that id else false
bool isAddedPanel(OverlayID id) { return _panels.contains(id); }
#endif
/**jsdoc
* Generate a mouse press event on an overlay.
* @function Overlays.sendMousePressOnOverlay
@ -611,10 +584,6 @@ signals:
*/
void overlayDeleted(OverlayID id);
#if OVERLAY_PANELS
void panelDeleted(OverlayID id);
#endif
/**jsdoc
* Triggered when a mouse press event occurs on an overlay. Only occurs for 3D overlays (unless you use
* {@link Overlays.sendMousePressOnOverlay|sendMousePressOnOverlay} for a 2D overlay).
@ -731,15 +700,9 @@ private:
QMap<OverlayID, Overlay::Pointer> _overlaysHUD;
QMap<OverlayID, Overlay::Pointer> _overlaysWorld;
#if OVERLAY_PANELS
QMap<OverlayID, OverlayPanel::Pointer> _panels;
#endif
QList<Overlay::Pointer> _overlaysToDelete;
unsigned int _stackOrder { 1 };
#if OVERLAY_PANELS
QScriptEngine* _scriptEngine;
#endif
bool _enabled = true;
PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult,

View file

@ -65,23 +65,7 @@ namespace render {
}
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
if (args) {
if (overlay->getAnchor() == Overlay::MY_AVATAR) {
auto batch = args->_batch;
auto avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
glm::quat myAvatarRotation = avatar->getWorldOrientation();
glm::vec3 myAvatarPosition = avatar->getWorldPosition();
float angle = glm::degrees(glm::angle(myAvatarRotation));
glm::vec3 axis = glm::axis(myAvatarRotation);
float myAvatarScale = avatar->getModelScale();
Transform transform = Transform();
transform.setTranslation(myAvatarPosition);
transform.setRotation(glm::angleAxis(angle, axis));
transform.setScale(myAvatarScale);
batch->setModelTransform(transform);
overlay->render(args);
} else {
overlay->render(args);
}
overlay->render(args);
}
}
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay) {

View file

@ -13,18 +13,8 @@
#include <RegisteredMetaTypes.h>
#include "OverlayPanel.h"
bool PanelAttachable::getParentVisible() const {
#if OVERLAY_PANELS
if (getParentPanel()) {
return getParentPanel()->getVisible() && getParentPanel()->getParentVisible();
} else {
return true;
}
#else
return true;
#endif
}
// JSDoc for copying to @typedefs of overlay types that inherit PanelAttachable.
@ -67,15 +57,6 @@ bool PanelAttachable::applyTransformTo(Transform& transform, bool force) {
if (force || usecTimestampNow() > _transformExpiry) {
const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz
_transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD;
#if OVERLAY_PANELS
if (getParentPanel()) {
getParentPanel()->applyTransformTo(transform, true);
transform.postTranslate(getOffsetPosition());
transform.postRotate(getOffsetRotation());
transform.postScale(getOffsetScale());
return true;
}
#endif
}
return false;
}

View file

@ -30,8 +30,6 @@
#ifndef hifi_PanelAttachable_h
#define hifi_PanelAttachable_h
#define OVERLAY_PANELS 0
#include <memory>
#include <glm/glm.hpp>
@ -44,18 +42,12 @@ class OverlayPanel;
class PanelAttachable {
public:
// getters
#if OVERLAY_PANELS
std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; }
#endif
glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); }
glm::quat getOffsetRotation() const { return _offset.getRotation(); }
glm::vec3 getOffsetScale() const { return _offset.getScale(); }
bool getParentVisible() const;
// setters
#if OVERLAY_PANELS
void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; }
#endif
void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); }
void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); }
void setOffsetScale(float scale) { _offset.setScale(scale); }
@ -71,9 +63,6 @@ protected:
quint64 _transformExpiry = 0;
private:
#if OVERLAY_PANELS
std::shared_ptr<OverlayPanel> _parentPanel = nullptr;
#endif
Transform _offset;
};

View file

@ -127,8 +127,6 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -138,10 +136,8 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -128,8 +128,6 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -139,10 +137,8 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -47,8 +47,6 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -58,10 +56,8 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) :
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -224,8 +224,6 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -235,10 +233,8 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -50,7 +50,7 @@
#include "ui/DomainConnectionModel.h"
#include "ui/AvatarInputs.h"
#include "avatar/AvatarManager.h"
#include "scripting/GlobalServicesScriptingInterface.h"
#include "scripting/AccountServicesScriptingInterface.h"
#include <plugins/InputConfiguration.h>
#include "ui/Snapshot.h"
#include "SoundCache.h"
@ -192,7 +192,10 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
// in Qt 5.10.0 there is already an "Audio" object in the QML context
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
@ -208,7 +211,6 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
_webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get<DomainConnectionModel>().data());
_webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
@ -222,10 +224,6 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
// mark the TabletProxy object as cpp ownership.
QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");
_webSurface->getSurfaceContext()->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership);
// Override min fps for tablet UI, for silky smooth scrolling
setMaxFPS(90);
}
@ -486,8 +484,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
* the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise
* used.)
* @property {boolean} visible=true - If <code>true</code>, the overlay is rendered, otherwise it is not rendered.
* @property {string} anchor="" - If set to <code>"MyAvatar"</code> then the overlay is attached to your avatar, moving and
* rotating as you move your avatar.
*
* @property {string} name="" - A friendly name for the overlay.
* @property {Vec3} position - The position of the overlay center. Synonyms: <code>p1</code>, <code>point</code>, and
@ -497,10 +493,8 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) {
* @property {Quat} rotation - The orientation of the overlay. Synonym: <code>orientation</code>.
* @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a
* <code>parentID</code> set, otherwise the same value as <code>rotation</code>.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>,
* <code>filled</code>, and <code>filed</code>. Antonyms: <code>isWire</code> and <code>wire</code>.
* <strong>Deprecated:</strong> The erroneous property spelling "<code>filed</code>" is deprecated and support for it will
* be removed.
* @property {boolean} isSolid=false - Synonyms: <ode>solid</code>, <code>isFilled</code>, and <code>filled</code>.
* Antonyms: <code>isWire</code> and <code>wire</code>.
* @property {boolean} isDashedLine=false - If <code>true</code>, a dashed line is drawn on the overlay's edges. Synonym:
* <code>dashed</code>.
* @property {boolean} ignoreRayIntersection=false - If <code>true</code>,

View file

@ -1,6 +1,6 @@
set(TARGET_NAME animation)
setup_hifi_library(Network Script)
link_hifi_libraries(shared model fbx)
link_hifi_libraries(shared graphics fbx)
include_hifi_library_headers(networking)
include_hifi_library_headers(gpu)

View file

@ -13,8 +13,6 @@
#include "AnimationLogging.h"
#include "AnimUtil.h"
bool AnimClip::usePreAndPostPoseFromAnim = true;
AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) :
AnimNode(AnimNode::Type::Clip, id),
_startFrame(startFrame),
@ -138,14 +136,8 @@ void AnimClip::copyFromNetworkAnim() {
if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) {
AnimPose preRot, postRot;
if (usePreAndPostPoseFromAnim) {
preRot = animSkeleton.getPreRotationPose(animJoint);
postRot = animSkeleton.getPostRotationPose(animJoint);
} else {
// In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations.
preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot(), glm::vec3());
postRot = AnimPose::identity;
}
preRot = animSkeleton.getPreRotationPose(animJoint);
postRot = animSkeleton.getPostRotationPose(animJoint);
// cancel out scale
preRot.scale() = glm::vec3(1.0f);

View file

@ -25,8 +25,6 @@ class AnimClip : public AnimNode {
public:
friend class AnimTests;
static bool usePreAndPostPoseFromAnim;
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag);
virtual ~AnimClip() override;

View file

@ -591,19 +591,31 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
glm::vec3 d = basePose.trans() - topPose.trans();
float dLen = glm::length(d);
if (dLen > EPSILON) {
glm::vec3 dUnit = d / dLen;
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
// if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector.
// however if mid joint angle is in between the two blend between both solutions.
vec3 u = normalize(basePose.trans() - midPose.trans());
vec3 v = normalize(topPose.trans() - midPose.trans());
const float LERP_THRESHOLD = 3.05433f; // 175 deg
const float BENT_THRESHOLD = 2.96706f; // 170 deg
float jointAngle = acos(dot(u, v));
if (jointAngle < BENT_THRESHOLD) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = normalize(midPose.trans() - midPoint);
} else if (jointAngle < LERP_THRESHOLD) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD);
e = lerp(e, normalize(midPose.trans() - midPoint), alpha);
}
glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit;
float eProjLen = glm::length(eProj);
const float MIN_EPROJ_LEN = 0.5f;
if (eProjLen < MIN_EPROJ_LEN) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = midPose.trans() - midPoint;
eProj = e - glm::dot(e, dUnit) * dUnit;
eProjLen = glm::length(eProj);
}
glm::vec3 p = target.getPoleVector();
glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit;
float pProjLen = glm::length(pProj);
@ -634,16 +646,27 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
AnimPose geomToWorldPose = AnimPose(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix());
glm::vec3 dUnit = d / dLen;
glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector());
glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit;
float eProjLen = glm::length(eProj);
const float MIN_EPROJ_LEN = 0.5f;
if (eProjLen < MIN_EPROJ_LEN) {
// if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector.
// however if mid joint angle is in between the two blend between both solutions.
vec3 u = normalize(basePose.trans() - midPose.trans());
vec3 v = normalize(topPose.trans() - midPose.trans());
const float LERP_THRESHOLD = 3.05433f; // 175 deg
const float BENT_THRESHOLD = 2.96706f; // 170 deg
float jointAngle = acos(dot(u, v));
glm::vec4 eColor = RED;
if (jointAngle < BENT_THRESHOLD) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
e = midPose.trans() - midPoint;
eProj = e - glm::dot(e, dUnit) * dUnit;
eProjLen = glm::length(eProj);
e = normalize(midPose.trans() - midPoint);
eColor = GREEN;
} else if (jointAngle < LERP_THRESHOLD) {
glm::vec3 midPoint = topPose.trans() + d * 0.5f;
float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD);
e = lerp(e, normalize(midPose.trans() - midPoint), alpha);
eColor = YELLOW;
}
glm::vec3 p = target.getPoleVector();
@ -655,7 +678,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
YELLOW);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(e)),
RED);
eColor);
DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint),
geomToWorldPose.xformPoint(midPoint + POLE_VECTOR_LEN * glm::normalize(p)),
BLUE);
@ -1232,7 +1255,7 @@ void AnimInverseKinematics::initConstraints() {
// / /
// O--O O--O
loadDefaultPoses(_skeleton->getRelativeBindPoses());
loadDefaultPoses(_skeleton->getRelativeDefaultPoses());
int numJoints = (int)_defaultRelativePoses.size();

View file

@ -33,7 +33,7 @@ AnimManipulator::~AnimManipulator() {
}
const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeBindPoses());
return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeDefaultPoses());
}
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {

View file

@ -76,3 +76,5 @@ AnimPose::operator glm::mat4() const {
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f),
glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
}

View file

@ -56,14 +56,6 @@ int AnimSkeleton::getChainDepth(int jointIndex) const {
}
}
const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
return _absoluteBindPoses[jointIndex];
}
const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const {
return _relativeBindPoses[jointIndex];
}
const AnimPose& AnimSkeleton::getRelativeDefaultPose(int jointIndex) const {
return _relativeDefaultPoses[jointIndex];
}
@ -164,8 +156,6 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
_joints = joints;
_jointsSize = (int)joints.size();
// build a cache of bind poses
_absoluteBindPoses.reserve(_jointsSize);
_relativeBindPoses.reserve(_jointsSize);
// build a chache of default poses
_absoluteDefaultPoses.reserve(_jointsSize);
@ -192,28 +182,6 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
} else {
_absoluteDefaultPoses.push_back(relDefaultPose);
}
// build relative and absolute bind poses
if (_joints[i].bindTransformFoundInCluster) {
// Use the FBXJoint::bindTransform, which is absolute model coordinates
// i.e. not relative to it's parent.
AnimPose absoluteBindPose(_joints[i].bindTransform);
_absoluteBindPoses.push_back(absoluteBindPose);
if (parentIndex >= 0) {
AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse();
_relativeBindPoses.push_back(inverseParentAbsoluteBindPose * absoluteBindPose);
} else {
_relativeBindPoses.push_back(absoluteBindPose);
}
} else {
// use default transform instead
_relativeBindPoses.push_back(relDefaultPose);
if (parentIndex >= 0) {
_absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relDefaultPose);
} else {
_absoluteBindPoses.push_back(relDefaultPose);
}
}
}
for (int i = 0; i < _jointsSize; i++) {
@ -251,8 +219,6 @@ void AnimSkeleton::dump(bool verbose) const {
qCDebug(animation) << " {";
qCDebug(animation) << " index =" << i;
qCDebug(animation) << " name =" << getJointName(i);
qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i);
qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i);
qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i);
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
if (verbose) {
@ -287,8 +253,6 @@ void AnimSkeleton::dump(const AnimPoseVec& poses) const {
qCDebug(animation) << " {";
qCDebug(animation) << " index =" << i;
qCDebug(animation) << " name =" << getJointName(i);
qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i);
qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i);
qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i);
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
qCDebug(animation) << " pose =" << poses[i];

View file

@ -30,13 +30,6 @@ public:
int getNumJoints() const;
int getChainDepth(int jointIndex) const;
// absolute pose, not relative to parent
const AnimPose& getAbsoluteBindPose(int jointIndex) const;
// relative to parent pose
const AnimPose& getRelativeBindPose(int jointIndex) const;
const AnimPoseVec& getRelativeBindPoses() const { return _relativeBindPoses; }
// the default poses are the orientations of the joints on frame 0.
const AnimPose& getRelativeDefaultPose(int jointIndex) const;
const AnimPoseVec& getRelativeDefaultPoses() const { return _relativeDefaultPoses; }
@ -72,8 +65,6 @@ protected:
std::vector<FBXJoint> _joints;
int _jointsSize { 0 };
AnimPoseVec _absoluteBindPoses;
AnimPoseVec _relativeBindPoses;
AnimPoseVec _relativeDefaultPoses;
AnimPoseVec _absoluteDefaultPoses;
AnimPoseVec _relativePreRotationPoses;

View file

@ -179,7 +179,7 @@ void Rig::restoreRoleAnimation(const QString& role) {
} else {
qCWarning(animation) << "Rig::restoreRoleAnimation could not find role " << role;
}
auto statesIter = _roleAnimStates.find(role);
if (statesIter != _roleAnimStates.end()) {
_roleAnimStates.erase(statesIter);
@ -1050,52 +1050,6 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
}
}
void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
const QVector<int>& freeLineage, glm::mat4 rootTransform) {
ASSERT(false);
}
bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage) {
ASSERT(false);
return false;
}
float Rig::getLimbLength(int jointIndex, const QVector<int>& freeLineage,
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const {
ASSERT(false);
return 1.0f;
}
glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority) {
ASSERT(false);
return glm::quat();
}
glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) {
ASSERT(false);
return glm::vec3();
}
glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, float mix) {
ASSERT(false);
return glm::quat();
}
bool Rig::getJointRotationInConstrainedFrame(int jointIndex, glm::quat& quatOut) const {
ASSERT(false);
return false;
}
void Rig::clearJointStatePriorities() {
ASSERT(false);
}
glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) {
ASSERT(false);
return glm::quat();
}
void Rig::updateFromEyeParameters(const EyeParameters& params) {
updateEyeJoint(params.leftEyeJointIndex, params.modelTranslation, params.modelRotation, params.eyeLookAt, params.eyeSaccade);
updateEyeJoint(params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, params.eyeLookAt, params.eyeSaccade);
@ -1265,7 +1219,8 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin
return position;
}
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
bool leftArmEnabled, bool rightArmEnabled, float dt,
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) {
@ -1279,7 +1234,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
glm::vec3 handPosition = leftHandPose.trans();
glm::quat handRotation = leftHandPose.rot();
if (!hipsEnabled) {
if (!hipsEnabled || hipsEstimated) {
// prevent the hand IK targets from intersecting the torso
handPosition = deflectHandFromTorso(handPosition, hipsShapeInfo, spineShapeInfo, spine1ShapeInfo, spine2ShapeInfo);
}
@ -1326,7 +1281,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
glm::vec3 handPosition = rightHandPose.trans();
glm::quat handRotation = rightHandPose.rot();
if (!hipsEnabled) {
if (!hipsEnabled || hipsEstimated) {
// prevent the hand IK targets from intersecting the torso
handPosition = deflectHandFromTorso(handPosition, hipsShapeInfo, spineShapeInfo, spine1ShapeInfo, spine2ShapeInfo);
}
@ -1550,20 +1505,20 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
_animVars.set("isTalking", params.isTalking);
_animVars.set("notIsTalking", !params.isTalking);
bool headEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Head];
bool leftHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftHand];
bool rightHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightHand];
bool hipsEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Hips];
bool leftFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftFoot];
bool rightFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightFoot];
bool spine2Enabled = params.primaryControllerActiveFlags[PrimaryControllerType_Spine2];
bool leftArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_LeftArm];
bool rightArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_RightArm];
bool headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
bool rightHandEnabled = params.primaryControllerFlags[PrimaryControllerType_RightHand] & (uint8_t)ControllerFlags::Enabled;
bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled;
bool hipsEstimated = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated;
bool leftFootEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftFoot] & (uint8_t)ControllerFlags::Enabled;
bool rightFootEnabled = params.primaryControllerFlags[PrimaryControllerType_RightFoot] & (uint8_t)ControllerFlags::Enabled;
bool spine2Enabled = params.primaryControllerFlags[PrimaryControllerType_Spine2] & (uint8_t)ControllerFlags::Enabled;
bool leftArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_LeftArm] & (uint8_t)ControllerFlags::Enabled;
bool rightArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_RightArm] & (uint8_t)ControllerFlags::Enabled;
updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, leftArmEnabled, rightArmEnabled, dt,
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, dt,
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo);
@ -1623,7 +1578,7 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
for (int i = 0; i < (int)NumSecondaryControllerTypes; i++) {
int index = indexOfJoint(secondaryControllerJointNames[i]);
if (index >= 0) {
if (params.secondaryControllerActiveFlags[i]) {
if (params.secondaryControllerFlags[i] & (uint8_t)ControllerFlags::Enabled) {
ikNode->setSecondaryTargetInRigFrame(index, params.secondaryControllerPoses[i]);
} else {
ikNode->clearSecondaryTarget(index);
@ -1731,6 +1686,14 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const {
}
}
AnimPose Rig::getJointPose(int jointIndex) const {
if (isIndexValid(jointIndex)) {
return _internalPoseSet._absolutePoses[jointIndex];
} else {
return AnimPose::identity;
}
}
void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
const AnimPose geometryToRigPose(_geometryToRigTransform);

View file

@ -69,11 +69,16 @@ public:
NumSecondaryControllerTypes
};
enum class ControllerFlags : uint8_t {
Enabled = 0x01,
Estimated = 0x02
};
struct ControllerParameters {
AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space
bool primaryControllerActiveFlags[NumPrimaryControllerTypes];
uint8_t primaryControllerFlags[NumPrimaryControllerTypes];
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
bool secondaryControllerActiveFlags[NumSecondaryControllerTypes];
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
bool isTalking;
FBXJointShapeInfo hipsShapeInfo;
FBXJointShapeInfo spineShapeInfo;
@ -159,6 +164,7 @@ public:
// rig space
glm::mat4 getJointTransform(int jointIndex) const;
AnimPose getJointPose(int jointIndex) const;
// Start or stop animations as needed.
void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState);
@ -166,36 +172,6 @@ public:
// Regardless of who started the animations or how many, update the joints.
void updateAnimations(float deltaTime, const glm::mat4& rootTransform, const glm::mat4& rigToWorldTransform);
// legacy
void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority,
const QVector<int>& freeLineage, glm::mat4 rootTransform);
// legacy
bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector<int>& freeLineage);
// legacy
float getLimbLength(int jointIndex, const QVector<int>& freeLineage,
const glm::vec3 scale, const QVector<FBXJoint>& fbxJoints) const;
// legacy
glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority);
// legacy
glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex);
// legacy
glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation,
float priority, float mix = 1.0f);
// legacy
bool getJointRotationInConstrainedFrame(int jointIndex, glm::quat& rotOut) const;
// legacy
glm::quat getJointDefaultRotationInParentFrame(int jointIndex);
// legacy
void clearJointStatePriorities();
void updateFromControllerParameters(const ControllerParameters& params, float dt);
void updateFromEyeParameters(const EyeParameters& params);
@ -251,7 +227,8 @@ protected:
void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut);
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
bool leftArmEnabled, bool rightArmEnabled, float dt,
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo);
@ -338,7 +315,7 @@ protected:
float firstFrame;
float lastFrame;
};
struct RoleAnimState {
RoleAnimState() {}
RoleAnimState(const QString& roleId, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) :

View file

@ -782,7 +782,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
_selectedCodecName = selectedCodecName;
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName;
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput;
// release any old codec encoder/decoder first...
if (_codec && _encoder) {
@ -797,7 +797,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
if (_selectedCodecName == plugin->getName()) {
_codec = plugin;
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO);
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get();
break;
}
@ -1079,7 +1079,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
encodedBuffer = audioBuffer;
}
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber,
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput,
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,
packetType, _selectedCodecName);
_stats.sentPacket();
@ -1382,7 +1382,16 @@ void AudioClient::setIsStereoInput(bool isStereoInput) {
_desiredInputFormat.setChannelCount(1);
}
// change in channel count for desired input format, restart the input device
// restart the codec
if (_codec) {
if (_encoder) {
_codec->releaseEncoder(_encoder);
}
_encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
}
qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput;
// restart the input device
switchInputToAudioDevice(_inputDeviceInfo);
}
}
@ -1418,7 +1427,7 @@ void AudioClient::outputFormatChanged() {
_receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT);
}
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest) {
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) {
qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]";
bool supportedFormat = false;
@ -1601,7 +1610,7 @@ void AudioClient::outputNotify() {
}
}
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest) {
bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) {
qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]";
bool supportedFormat = false;

View file

@ -378,8 +378,8 @@ private:
void handleLocalEchoAndReverb(QByteArray& inputByteArray);
bool switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest = false);
bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest = false);
bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false);
bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false);
// Callback acceleration dependent calculations
int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const;

View file

@ -19,7 +19,7 @@
#include "AudioConstants.h"
void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber,
void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo,
const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale,
PacketType packetType, QString codecName) {
static std::mutex _mutex;
@ -30,9 +30,6 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes
Locker lock(_mutex);
auto audioPacket = NLPacket::create(packetType);
// FIXME - this is not a good way to determine stereoness with codecs....
quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0;
// write sequence number
auto sequence = sequenceNumber++;
audioPacket->writePrimitive(sequence);
@ -48,7 +45,8 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes
audioPacket->writePrimitive(numSilentSamples);
} else {
// set the mono/stereo byte
audioPacket->writePrimitive(isStereo);
quint8 channelFlag = isStereo ? 1 : 0;
audioPacket->writePrimitive(channelFlag);
}
// pack the three float positions

View file

@ -29,7 +29,7 @@ class AbstractAudioInterface : public QObject {
public:
AbstractAudioInterface(QObject* parent = 0) : QObject(parent) {};
static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber,
static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo,
const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale,
PacketType packetType, QString codecName = QString(""));

View file

@ -1,7 +1,7 @@
set(TARGET_NAME avatars-renderer)
AUTOSCRIBE_SHADER_LIB(gpu model render render-utils)
AUTOSCRIBE_SHADER_LIB(gpu graphics render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu model animation model-networking script-engine render render-utils image trackers entities-renderer)
link_hifi_libraries(shared gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer)
include_hifi_library_headers(avatars)
include_hifi_library_headers(networking)
include_hifi_library_headers(fbx)

View file

@ -32,6 +32,8 @@
#include <shared/Camera.h>
#include <SoftAttachmentModel.h>
#include <render/TransitionStage.h>
#include "ModelEntityItem.h"
#include "RenderableModelEntityItem.h"
#include "Logging.h"
@ -326,13 +328,15 @@ void Avatar::updateAvatarEntities() {
AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs();
if (!recentlyDettachedAvatarEntities.empty()) {
// only lock this thread when absolutely necessary
AvatarEntityMap avatarEntityData;
_avatarEntitiesLock.withReadLock([&] {
foreach (auto entityID, recentlyDettachedAvatarEntities) {
if (!_avatarEntityData.contains(entityID)) {
entityTree->deleteEntity(entityID, true, true);
}
}
avatarEntityData = _avatarEntityData;
});
foreach (auto entityID, recentlyDettachedAvatarEntities) {
if (!avatarEntityData.contains(entityID)) {
entityTree->deleteEntity(entityID, true, true);
}
}
// remove stale data hashes
foreach (auto entityID, recentlyDettachedAvatarEntities) {
@ -347,6 +351,65 @@ void Avatar::updateAvatarEntities() {
setAvatarEntityDataChanged(false);
}
void Avatar::relayJointDataToChildren() {
forEachChild([&](SpatiallyNestablePointer child) {
if (child->getNestableType() == NestableType::Entity) {
auto modelEntity = std::dynamic_pointer_cast<RenderableModelEntityItem>(child);
if (modelEntity) {
if (modelEntity->getRelayParentJoints()) {
if (!modelEntity->getJointMapCompleted() || _reconstructSoftEntitiesJointMap) {
QStringList modelJointNames = modelEntity->getJointNames();
int numJoints = modelJointNames.count();
std::vector<int> map;
map.reserve(numJoints);
for (int jointIndex = 0; jointIndex < numJoints; jointIndex++) {
QString jointName = modelJointNames.at(jointIndex);
int avatarJointIndex = getJointIndex(jointName);
glm::quat jointRotation;
glm::vec3 jointTranslation;
if (avatarJointIndex < 0) {
jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex);
jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex);
map.push_back(-1);
} else {
int jointIndex = getJointIndex(jointName);
jointRotation = getJointRotation(jointIndex);
jointTranslation = getJointTranslation(jointIndex);
map.push_back(avatarJointIndex);
}
modelEntity->setLocalJointRotation(jointIndex, jointRotation);
modelEntity->setLocalJointTranslation(jointIndex, jointTranslation);
}
modelEntity->setJointMap(map);
} else {
QStringList modelJointNames = modelEntity->getJointNames();
int numJoints = modelJointNames.count();
for (int jointIndex = 0; jointIndex < numJoints; jointIndex++) {
int avatarJointIndex = modelEntity->avatarJointIndex(jointIndex);
glm::quat jointRotation;
glm::vec3 jointTranslation;
if (avatarJointIndex >=0) {
jointRotation = getJointRotation(avatarJointIndex);
jointTranslation = getJointTranslation(avatarJointIndex);
} else {
jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex);
jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex);
}
modelEntity->setLocalJointRotation(jointIndex, jointRotation);
modelEntity->setLocalJointTranslation(jointIndex, jointTranslation);
}
}
Transform avatarTransform = _skeletonModel->getTransform();
avatarTransform.setScale(_skeletonModel->getScale());
modelEntity->setOverrideTransform(avatarTransform, _skeletonModel->getOffset());
modelEntity->simulateRelayedJoints();
}
}
}
});
_reconstructSoftEntitiesJointMap = false;
}
void Avatar::simulate(float deltaTime, bool inView) {
PROFILE_RANGE(simulation, "simulate");
@ -379,6 +442,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
}
head->setScale(getModelScale());
head->simulate(deltaTime);
relayJointDataToChildren();
} else {
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
_skeletonModel->simulate(deltaTime, false);
@ -1197,6 +1261,7 @@ void Avatar::setModelURLFinished(bool success) {
invalidateJointIndicesCache();
_isAnimatingScale = true;
_reconstructSoftEntitiesJointMap = true;
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts

View file

@ -330,6 +330,7 @@ protected:
// protected methods...
bool isLookingAtMe(AvatarSharedPointer avatar) const;
void relayJointDataToChildren();
void fade(render::Transaction& transaction, render::Transition::Type type);
@ -382,6 +383,7 @@ protected:
bool _isAnimatingScale { false };
bool _mustFadeIn { false };
bool _isFading { false };
bool _reconstructSoftEntitiesJointMap { false };
float _modelScale { 1.0f };
static int _jointConesID;

View file

@ -237,30 +237,14 @@ bool SkeletonModel::getRightHandPosition(glm::vec3& position) const {
return getJointPositionInWorldFrame(getRightHandJointIndex(), position);
}
bool SkeletonModel::restoreLeftHandPosition(float fraction, float priority) {
return restoreJointPosition(getLeftHandJointIndex(), fraction, priority);
}
bool SkeletonModel::getLeftShoulderPosition(glm::vec3& position) const {
return getJointPositionInWorldFrame(getLastFreeJointIndex(getLeftHandJointIndex()), position);
}
float SkeletonModel::getLeftArmLength() const {
return getLimbLength(getLeftHandJointIndex());
}
bool SkeletonModel::restoreRightHandPosition(float fraction, float priority) {
return restoreJointPosition(getRightHandJointIndex(), fraction, priority);
}
bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const {
return getJointPositionInWorldFrame(getLastFreeJointIndex(getRightHandJointIndex()), position);
}
float SkeletonModel::getRightArmLength() const {
return getLimbLength(getRightHandJointIndex());
}
bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const {
return isActive() && getJointPositionInWorldFrame(getFBXGeometry().headJointIndex, headPosition);
}

View file

@ -57,11 +57,6 @@ public:
/// \return true whether or not the position was found
bool getRightHandPosition(glm::vec3& position) const;
/// Restores some fraction of the default position of the left hand.
/// \param fraction the fraction of the default position to restore
/// \return whether or not the left hand joint was found
bool restoreLeftHandPosition(float fraction = 1.0f, float priority = 1.0f);
/// Gets the position of the left shoulder.
/// \return whether or not the left shoulder joint was found
bool getLeftShoulderPosition(glm::vec3& position) const;
@ -69,18 +64,10 @@ public:
/// Returns the extended length from the left hand to its last free ancestor.
float getLeftArmLength() const;
/// Restores some fraction of the default position of the right hand.
/// \param fraction the fraction of the default position to restore
/// \return whether or not the right hand joint was found
bool restoreRightHandPosition(float fraction = 1.0f, float priority = 1.0f);
/// Gets the position of the right shoulder.
/// \return whether or not the right shoulder joint was found
bool getRightShoulderPosition(glm::vec3& position) const;
/// Returns the extended length from the right hand to its first free ancestor.
float getRightArmLength() const;
/// Returns the position of the head joint.
/// \return whether or not the head was found
bool getHeadPosition(glm::vec3& headPosition) const;

View file

@ -530,9 +530,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
destinationBuffer += numValidityBytes; // Move pointer past the validity bytes
// sentJointDataOut and lastSentJointData might be the same vector
// build sentJointDataOut locally and then swap it at the end.
QVector<JointData> localSentJointDataOut;
if (sentJointDataOut) {
sentJointDataOut->resize(_jointData.size()); // Make sure the destination is resized before using it
localSentJointDataOut.resize(numJoints); // Make sure the destination is resized before using it
}
float minRotationDOT = !distanceAdjust ? AVATAR_MIN_ROTATION_DOT : getDistanceBasedMinRotationDOT(viewerPosition);
for (int i = 0; i < _jointData.size(); i++) {
@ -552,8 +556,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation);
if (sentJointDataOut) {
auto jointDataOut = *sentJointDataOut;
jointDataOut[i].rotation = data.rotation;
localSentJointDataOut[i].rotation = data.rotation;
}
}
@ -602,8 +605,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
if (sentJointDataOut) {
auto jointDataOut = *sentJointDataOut;
jointDataOut[i].translation = data.translation;
localSentJointDataOut[i].translation = data.translation;
}
}
@ -646,6 +648,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
if (outboundDataRateOut) {
outboundDataRateOut->jointDataRate.increment(numBytes);
}
if (sentJointDataOut) {
// Push new sent joint data to sentJointDataOut
sentJointDataOut->swap(localSentJointDataOut);
}
}
int avatarDataSize = destinationBuffer - startPosition;

View file

@ -358,7 +358,7 @@ class AvatarData : public QObject, public SpatiallyNestable {
Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName NOTIFY displayNameChanged)
// sessionDisplayName is sanitized, defaulted version displayName that is defined by the AvatarMixer rather than by Interface clients.
// The result is unique among all avatars present at the time.
Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName)
Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName NOTIFY sessionDisplayNameChanged)
Q_PROPERTY(bool lookAtSnappingEnabled MEMBER _lookAtSnappingEnabled NOTIFY lookAtSnappingChanged)
Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript)
Q_PROPERTY(QVector<AttachmentData> attachmentData READ getAttachmentData WRITE setAttachmentData)
@ -685,6 +685,7 @@ public:
signals:
void displayNameChanged();
void sessionDisplayNameChanged();
void lookAtSnappingChanged(bool enabled);
void sessionUUIDChanged();

View file

@ -15,6 +15,7 @@ ScriptAvatarData::ScriptAvatarData(AvatarSharedPointer avatarData) :
_avatarData(avatarData)
{
QObject::connect(avatarData.get(), &AvatarData::displayNameChanged, this, &ScriptAvatarData::displayNameChanged);
QObject::connect(avatarData.get(), &AvatarData::sessionDisplayNameChanged, this, &ScriptAvatarData::sessionDisplayNameChanged);
QObject::connect(avatarData.get(), &AvatarData::lookAtSnappingChanged, this, &ScriptAvatarData::lookAtSnappingChanged);
}

View file

@ -44,7 +44,7 @@ class ScriptAvatarData : public QObject {
//
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
Q_PROPERTY(QString displayName READ getDisplayName NOTIFY displayNameChanged)
Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName)
Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName NOTIFY sessionDisplayNameChanged)
Q_PROPERTY(bool isReplicated READ getIsReplicated)
Q_PROPERTY(bool lookAtSnappingEnabled READ getLookAtSnappingEnabled NOTIFY lookAtSnappingChanged)
@ -131,6 +131,7 @@ public:
signals:
void displayNameChanged();
void sessionDisplayNameChanged();
void lookAtSnappingChanged(bool enabled);
public slots:

View file

@ -1,7 +1,7 @@
set(TARGET_NAME baking)
setup_hifi_library(Concurrent)
link_hifi_libraries(shared model networking ktx image fbx)
link_hifi_libraries(shared graphics networking ktx image fbx)
include_hifi_library_headers(gpu)
add_dependency_external_projects(draco)

Some files were not shown because too many files have changed in this diff Show more