Merge branch 'master' into feature/dual-quat

This commit is contained in:
Anthony J. Thibault 2018-01-11 17:31:29 -08:00
commit 7753b8a0b5
118 changed files with 4765 additions and 1229 deletions

View file

@ -12,10 +12,8 @@ function(JOIN VALUES GLUE OUTPUT)
endfunction()
if (NOT DEV_BUILD)
set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc)
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
endif()
set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc)
generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg)
# set a default root dir for each of our optional externals if it was not passed
set(OPTIONAL_EXTERNALS "LeapMotion")
@ -74,9 +72,7 @@ qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}")
# add them to the interface source files
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}")
if (NOT DEV_BUILD)
list(APPEND INTERFACE_SRCS ${INTERFACE_QML_QRC})
endif()
if (UNIX)
install(

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

@ -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

@ -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

@ -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

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -36,6 +36,7 @@ Rectangle {
property bool pendingInventoryReply: true;
property bool isShowingMyItems: false;
property bool isDebuggingFirstUseTutorial: false;
property int pendingItemCount: 0;
// Style
color: hifi.colors.white;
Connections {
@ -79,18 +80,22 @@ Rectangle {
onInventoryResult: {
purchasesReceived = true;
if (root.pendingInventoryReply) {
inventoryTimer.start();
}
if (result.status !== 'success') {
console.log("Failed to get purchases", result.message);
} else {
} else if (!purchasesContentsList.dragging) { // Don't modify the view if the user's scrolling
var inventoryResult = processInventoryResult(result.data.assets);
var currentIndex = purchasesContentsList.currentIndex === -1 ? 0 : purchasesContentsList.currentIndex;
purchasesModel.clear();
purchasesModel.append(inventoryResult);
root.pendingItemCount = 0;
for (var i = 0; i < purchasesModel.count; i++) {
if (purchasesModel.get(i).status === "pending") {
root.pendingItemCount++;
}
}
if (previousPurchasesModel.count !== 0) {
checkIfAnyItemStatusChanged();
} else {
@ -103,6 +108,12 @@ Rectangle {
previousPurchasesModel.append(inventoryResult);
buildFilteredPurchasesModel();
purchasesContentsList.positionViewAtIndex(currentIndex, ListView.Beginning);
}
if (root.pendingInventoryReply && root.pendingItemCount > 0) {
inventoryTimer.start();
}
root.pendingInventoryReply = false;
@ -419,6 +430,8 @@ Rectangle {
visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0);
clip: true;
model: filteredPurchasesModel;
snapMode: ListView.SnapToItem;
highlightRangeMode: ListView.StrictlyEnforceRange;
// Anchors
anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom;
anchors.topMargin: 12;

View file

@ -25,8 +25,12 @@ Item {
HifiConstants { id: hifi; }
id: root;
property bool historyReceived: false;
property bool initialHistoryReceived: false;
property bool historyRequestPending: true;
property bool noMoreHistoryData: false;
property int pendingCount: 0;
property int currentHistoryPage: 1;
property var pagesAlreadyAdded: new Array();
Connections {
target: Commerce;
@ -36,32 +40,86 @@ Item {
}
onHistoryResult : {
historyReceived = true;
if (result.status === 'success') {
var sameItemCount = 0;
tempTransactionHistoryModel.clear();
tempTransactionHistoryModel.append(result.data.history);
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
if (!transactionHistoryModel.get(i)) {
sameItemCount = -1;
break;
} else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type &&
tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) {
sameItemCount++;
}
}
root.initialHistoryReceived = true;
root.historyRequestPending = false;
if (sameItemCount !== tempTransactionHistoryModel.count) {
transactionHistoryModel.clear();
if (result.status === 'success') {
var currentPage = parseInt(result.current_page);
if (result.data.history.length === 0) {
root.noMoreHistoryData = true;
console.log("No more data to retrieve from Commerce.history() endpoint.")
} else if (root.currentHistoryPage === 1) {
var sameItemCount = 0;
tempTransactionHistoryModel.clear();
tempTransactionHistoryModel.append(result.data.history);
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
transactionHistoryModel.append(tempTransactionHistoryModel.get(i));
if (!transactionHistoryModel.get(i)) {
sameItemCount = -1;
break;
} else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type &&
tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) {
sameItemCount++;
}
}
if (sameItemCount !== tempTransactionHistoryModel.count) {
transactionHistoryModel.clear();
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
transactionHistoryModel.append(tempTransactionHistoryModel.get(i));
}
calculatePendingAndInvalidated();
}
} else {
if (root.pagesAlreadyAdded.indexOf(currentPage) !== -1) {
console.log("Page " + currentPage + " of history has already been added to the list.");
} else {
// First, add the history result to a temporary model
tempTransactionHistoryModel.clear();
tempTransactionHistoryModel.append(result.data.history);
// Make a note that we've already added this page to the model...
root.pagesAlreadyAdded.push(currentPage);
var insertionIndex = 0;
// If there's nothing in the model right now, we don't need to modify insertionIndex.
if (transactionHistoryModel.count !== 0) {
var currentIteratorPage;
// Search through the whole transactionHistoryModel and look for the insertion point.
// The insertion point is found when the result page from the server is less than
// the page that the current item came from, OR when we've reached the end of the whole model.
for (var i = 0; i < transactionHistoryModel.count; i++) {
currentIteratorPage = transactionHistoryModel.get(i).resultIsFromPage;
if (currentPage < currentIteratorPage) {
insertionIndex = i;
break;
} else if (i === transactionHistoryModel.count - 1) {
insertionIndex = i + 1;
break;
}
}
}
// Go through the results we just got back from the server, setting the "resultIsFromPage"
// property of those results and adding them to the main model.
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
tempTransactionHistoryModel.setProperty(i, "resultIsFromPage", currentPage);
transactionHistoryModel.insert(i + insertionIndex, tempTransactionHistoryModel.get(i))
}
calculatePendingAndInvalidated();
}
calculatePendingAndInvalidated();
}
}
refreshTimer.start();
// Only auto-refresh if the user hasn't scrolled
// and there is more data to grab
if (transactionHistory.atYBeginning && !root.noMoreHistoryData) {
refreshTimer.start();
}
}
}
@ -134,9 +192,13 @@ Item {
onVisibleChanged: {
if (visible) {
historyReceived = false;
transactionHistoryModel.clear();
Commerce.balance();
Commerce.history();
initialHistoryReceived = false;
root.currentHistoryPage = 1;
root.noMoreHistoryData = false;
root.historyRequestPending = true;
Commerce.history(root.currentHistoryPage);
} else {
refreshTimer.stop();
}
@ -164,9 +226,12 @@ Item {
id: refreshTimer;
interval: 4000;
onTriggered: {
console.log("Refreshing Wallet Home...");
Commerce.balance();
Commerce.history();
if (transactionHistory.atYBeginning) {
console.log("Refreshing 1st Page of Recent Activity...");
root.historyRequestPending = true;
Commerce.balance();
Commerce.history(1);
}
}
}
@ -241,7 +306,7 @@ Item {
anchors.right: parent.right;
Item {
visible: transactionHistoryModel.count === 0 && root.historyReceived;
visible: transactionHistoryModel.count === 0 && root.initialHistoryReceived;
anchors.centerIn: parent;
width: parent.width - 12;
height: parent.height;
@ -364,7 +429,12 @@ Item {
onAtYEndChanged: {
if (transactionHistory.atYEnd) {
console.log("User scrolled to the bottom of 'Recent Activity'.");
// Grab next page of results and append to model
if (!root.historyRequestPending && !root.noMoreHistoryData) {
// Grab next page of results and append to model
root.historyRequestPending = true;
Commerce.history(++root.currentHistoryPage);
console.log("Fetching Page " + root.currentHistoryPage + " of Recent Activity...");
}
}
}
}

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

@ -127,9 +127,15 @@ Item {
GridView {
id: gridView
keyNavigationEnabled: false
highlightFollowsCurrentItem: false
property int previousGridIndex: -1
// true if any of the buttons contains mouse
property bool containsMouse: false
anchors {
fill: parent
topMargin: 20
@ -162,15 +168,31 @@ Item {
flow: GridView.LeftToRight
model: page.proxyModel
delegate: Item {
delegate: Control {
id: wrapper
width: gridView.cellWidth
height: gridView.cellHeight
hoverEnabled: true
property bool containsMouse: gridView.containsMouse
onHoveredChanged: {
if (hovered && !gridView.containsMouse) {
gridView.containsMouse = true
} else {
gridView.containsMouse = false
}
}
property var proxy: modelData
TabletButton {
id: tabletButton
// 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
buttonIndex: page.proxyModel.buttonIndex(uuid);
@ -224,6 +246,7 @@ Item {
PageIndicator {
id: pageIndicator
currentIndex: swipeView.currentIndex
visible: swipeView.count > 1
delegate: Item {
width: 15

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

@ -1851,6 +1851,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) {
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
});
EntityTreeRenderer::setRenderDebugHullsOperator([] {
return Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls);
});
// Preload Tablet sounds
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
@ -2441,7 +2444,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");
@ -2456,6 +2459,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));
@ -2468,12 +2472,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()
@ -2486,6 +2503,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));
@ -2520,6 +2538,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()) {
@ -5004,8 +5023,6 @@ void Application::update(float deltaTime) {
}
}
PerformanceTimer perfTimer("misc");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::update()");
@ -5014,11 +5031,13 @@ void Application::update(float deltaTime) {
// TODO: break these out into distinct perfTimers when they prove interesting
{
PROFILE_RANGE(app, "PickManager");
PerformanceTimer perfTimer("pickManager");
DependencyManager::get<PickManager>()->update();
}
{
PROFILE_RANGE(app, "PointerManager");
PerformanceTimer perfTimer("pointerManager");
DependencyManager::get<PointerManager>()->update();
}
@ -5044,8 +5063,8 @@ void Application::update(float deltaTime) {
// Update my voxel servers with my current voxel query...
{
PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
QMutexLocker viewLocker(&_viewMutex);
PerformanceTimer perfTimer("queryOctree");
QMutexLocker viewLocker(&_viewMutex);
quint64 sinceLastQuery = now - _lastQueriedTime;
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
@ -5081,12 +5100,14 @@ void Application::update(float deltaTime) {
}
}
avatarManager->postUpdate(deltaTime, getMain3DScene());
{
PerformanceTimer perfTimer("avatarManager/postUpdate");
avatarManager->postUpdate(deltaTime, getMain3DScene());
}
{
PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0);
PROFILE_RANGE_EX(app, "PostUpdateLambdas", 0xffff0000, (uint64_t)0);
PerformanceTimer perfTimer("postUpdateLambdas");
std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
for (auto& iter : _postUpdateLambdas) {
iter.second();
@ -5094,7 +5115,9 @@ void Application::update(float deltaTime) {
_postUpdateLambdas.clear();
}
editRenderArgs([this](AppRenderArgs& appRenderArgs) {
editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
PerformanceTimer perfTimer("editRenderArgs");
appRenderArgs._headPose= getHMDSensorPose();
auto myAvatar = getMyAvatar();
@ -5140,7 +5163,7 @@ void Application::update(float deltaTime) {
resizeGL();
}
this->updateCamera(appRenderArgs._renderArgs);
this->updateCamera(appRenderArgs._renderArgs, deltaTime);
appRenderArgs._eyeToWorld = _myCamera.getTransform();
appRenderArgs._isStereo = false;
@ -5208,12 +5231,20 @@ void Application::update(float deltaTime) {
}
});
AnimDebugDraw::getInstance().update();
{
PerformanceTimer perfTimer("limitless");
AnimDebugDraw::getInstance().update();
}
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
{
PerformanceTimer perfTimer("limitless");
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
}
// Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
getMain3DScene()->enqueueFrame();
{ // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
PerformanceTimer perfTimer("enqueueFrame");
getMain3DScene()->enqueueFrame();
}
}
void Application::sendAvatarViewFrustum() {
@ -6793,7 +6824,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);
});
}

View file

@ -146,7 +146,7 @@ public:
void initializeGL();
void initializeUi();
void updateCamera(RenderArgs& renderArgs);
void updateCamera(RenderArgs& renderArgs, float deltaTime);
void paintGL();
void resizeGL();
@ -695,6 +695,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

@ -50,7 +50,7 @@ float LODManager::getLODIncreaseFPS() {
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // 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 settle
// 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

View file

@ -698,7 +698,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned,
0, false, drawStatusConfig, SLOT(setShowNetwork(bool)));
}
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls);
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls, 0, false, qApp->getEntities().data(), SIGNAL(setRenderDebugHulls()));
// Developer > Ask to Reset Settings
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AskToResetSettings, 0, false);
@ -758,6 +758,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

@ -2799,14 +2799,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

@ -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;
@ -336,17 +335,25 @@ void MySkeletonModel::updateFingers() {
for (auto& link : chain) {
int index = _rig.indexOfJoint(link.second);
if (index >= 0) {
auto rotationFrameOffset = _jointRotationFrameOffsetMap.find(index);
if (rotationFrameOffset == _jointRotationFrameOffsetMap.end()) {
_jointRotationFrameOffsetMap.insert(std::pair<int, int>(index, 0));
rotationFrameOffset = _jointRotationFrameOffsetMap.find(index);
}
auto pose = myAvatar->getControllerPoseInSensorFrame(link.first);
if (pose.valid) {
glm::quat relRot = glm::inverse(prevAbsRot) * pose.getRotation();
// only set the rotation for the finger joints, not the hands.
if (link.first != controller::Action::LEFT_HAND && link.first != controller::Action::RIGHT_HAND) {
_rig.setJointRotation(index, true, relRot, CONTROLLER_PRIORITY);
rotationFrameOffset->second = 0;
}
prevAbsRot = pose.getRotation();
} else {
} else if (rotationFrameOffset->second == 1) { // if the pose is invalid and was set on previous frame we do clear ( current frame offset = 1 )
_rig.clearJointAnimationPriority(index);
}
rotationFrameOffset->second++;
}
}
}

View file

@ -28,6 +28,8 @@ private:
AnimPose _prevHips; // sensor frame
bool _prevHipsValid { false };
std::map<int, int> _jointRotationFrameOffsetMap;
};
#endif // hifi_MySkeletonModel_h

View file

@ -72,11 +72,16 @@ void Ledger::signedSend(const QString& propertyName, const QByteArray& text, con
send(endpoint, success, fail, QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request);
}
void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) {
void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail, QJsonObject& requestParams) {
auto wallet = DependencyManager::get<Wallet>();
QJsonObject request;
request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys());
send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, request);
requestParams["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys());
send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, requestParams);
}
void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) {
QJsonObject requestParams;
keysQuery(endpoint, success, fail, requestParams);
}
void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure) {
@ -169,6 +174,7 @@ void Ledger::historySuccess(QNetworkReply& reply) {
QJsonObject newDataData;
newDataData["history"] = newHistoryArray;
newData["data"] = newDataData;
newData["current_page"] = data["current_page"].toInt();
emit historyResult(newData);
}
@ -176,8 +182,11 @@ void Ledger::historyFailure(QNetworkReply& reply) {
failResponse("history", reply);
}
void Ledger::history(const QStringList& keys) {
keysQuery("history", "historySuccess", "historyFailure");
void Ledger::history(const QStringList& keys, const int& pageNumber) {
QJsonObject params;
params["per_page"] = 100;
params["page"] = pageNumber;
keysQuery("history", "historySuccess", "historyFailure", params);
}
// The api/failResponse is called just for the side effect of logging.

View file

@ -29,7 +29,7 @@ public:
bool receiveAt(const QString& hfc_key, const QString& old_key);
void balance(const QStringList& keys);
void inventory(const QStringList& keys);
void history(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);
@ -79,6 +79,7 @@ private:
QJsonObject apiResponse(const QString& label, QNetworkReply& reply);
QJsonObject failResponse(const QString& label, QNetworkReply& reply);
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request);
void keysQuery(const QString& endpoint, const QString& success, const QString& fail, QJsonObject& extraRequestParams);
void keysQuery(const QString& endpoint, const QString& success, const QString& fail);
void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure = false);
};

View file

@ -96,12 +96,12 @@ void QmlCommerce::inventory() {
}
}
void QmlCommerce::history() {
void QmlCommerce::history(const int& pageNumber) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList cachedPublicKeys = wallet->listPublicKeys();
if (!cachedPublicKeys.isEmpty()) {
ledger->history(cachedPublicKeys);
ledger->history(cachedPublicKeys, pageNumber);
}
}

View file

@ -60,7 +60,7 @@ protected:
Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false);
Q_INVOKABLE void balance();
Q_INVOKABLE void inventory();
Q_INVOKABLE void history();
Q_INVOKABLE void history(const int& pageNumber);
Q_INVOKABLE void generateKeyPair();
Q_INVOKABLE void reset();
Q_INVOKABLE void resetLocalWalletOnly();

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 {
@ -192,8 +194,7 @@ void WindowScriptingInterface::ensureReticleVisible() const {
/// Display a "browse to directory" dialog. If `directory` is an invalid file or directory the browser will start at the current
/// working directory.
/// \param const QString& title title of the window
/// \param const QString& directory directory to start the file browser at
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
/// \param const QString& directory directory to start the directory browser at
/// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue`
QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QString& directory) {
ensureReticleVisible();
@ -214,8 +215,7 @@ QScriptValue WindowScriptingInterface::browseDir(const QString& title, const QSt
/// Display a "browse to directory" dialog. If `directory` is an invalid file or directory the browser will start at the current
/// working directory.
/// \param const QString& title title of the window
/// \param const QString& directory directory to start the file browser at
/// \param const QString& nameFilter filter to filter filenames by - see `QFileDialog`
/// \param const QString& directory directory to start the directory browser at
void WindowScriptingInterface::browseDirAsync(const QString& title, const QString& directory) {
ensureReticleVisible();
QString path = directory;
@ -391,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() {
@ -459,6 +463,41 @@ int WindowScriptingInterface::openMessageBox(QString title, QString text, int bu
return createMessageBox(title, text, buttons, defaultButton);
}
/**jsdoc
* <p>The buttons that may be included in a message box created by {@link Window.openMessageBox|openMessageBox} are defined by
* numeric values:
* <table>
* <thead>
* <tr>
* <th>Button</th>
* <th>Value</th>
* <th>Description</th>
* </tr>
* </thead>
* <tbody>
* <tr> <td><strong>NoButton</strong></td> <td><code>0x0</code></td> <td>An invalid button.</td> </tr>
* <tr> <td><strong>Ok</strong></td> <td><code>0x400</code></td> <td>"OK"</td> </tr>
* <tr> <td><strong>Save</strong></td> <td><code>0x800</code></td> <td>"Save"</td> </tr>
* <tr> <td><strong>SaveAll</strong></td> <td><code>0x1000</code></td> <td>"Save All"</td> </tr>
* <tr> <td><strong>Open</strong></td> <td><code>0x2000</code></td> <td>"Open"</td> </tr>
* <tr> <td><strong>Yes</strong></td> <td><code>0x4000</code></td> <td>"Yes"</td> </tr>
* <tr> <td><strong>YesToAll</strong></td> <td><code>0x8000</code></td> <td>"Yes to All"</td> </tr>
* <tr> <td><strong>No</strong></td> <td><code>0x10000</code></td> <td>"No"</td> </tr>
* <tr> <td><strong>NoToAll</strong></td> <td><code>0x20000</code></td> <td>"No to All"</td> </tr>
* <tr> <td><strong>Abort</strong></td> <td><code>0x40000</code></td> <td>"Abort"</td> </tr>
* <tr> <td><strong>Retry</strong></td> <td><code>0x80000</code></td> <td>"Retry"</td> </tr>
* <tr> <td><strong>Ignore</strong></td> <td><code>0x100000</code></td> <td>"Ignore"</td> </tr>
* <tr> <td><strong>Close</strong></td> <td><code>0x200000</code></td> <td>"Close"</td> </tr>
* <tr> <td><strong>Cancel</strong></td> <td><code>0x400000</code></td> <td>"Cancel"</td> </tr>
* <tr> <td><strong>Discard</strong></td> <td><code>0x800000</code></td> <td>"Discard" or "Don't Save"</td> </tr>
* <tr> <td><strong>Help</strong></td> <td><code>0x1000000</code></td> <td>"Help"</td> </tr>
* <tr> <td><strong>Apply</strong></td> <td><code>0x2000000</code></td> <td>"Apply"</td> </tr>
* <tr> <td><strong>Reset</strong></td> <td><code>0x4000000</code></td> <td>"Reset"</td> </tr>
* <tr> <td><strong>RestoreDefaults</strong></td> <td><code>0x8000000</code></td> <td>"Restore Defaults"</td> </tr>
* </tbody>
* </table>
* @typedef Window.MessageBoxButton
*/
int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) {
auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,
static_cast<QFlags<QMessageBox::StandardButton>>(buttons), static_cast<QMessageBox::StandardButton>(defaultButton));

View file

@ -1,5 +1,5 @@
//
// WindowScriptingInterface.cpp
// WindowScriptingInterface.h
// interface/src/scripting
//
// Created by Ryan Huffman on 4/29/14.
@ -33,12 +33,28 @@ QScriptValue CustomPromptResultToScriptValue(QScriptEngine* engine, const Custom
void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptResult& result);
/**jsdoc
* The Window API provides various facilities not covered elsewhere: window dimensions, window focus, normal or entity camera
* view, clipboard, announcements, user connections, common dialog boxes, snapshots, file import, domain changes, domain
* physics.
*
* @namespace Window
* @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>
* @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>
*/
class WindowScriptingInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(int innerWidth READ getInnerWidth)
Q_PROPERTY(int innerHeight READ getInnerHeight)
Q_PROPERTY(int x READ getX)
Q_PROPERTY(int y READ getY)
public:
WindowScriptingInterface();
~WindowScriptingInterface();
@ -48,63 +64,623 @@ public:
int getY();
public slots:
/**jsdoc
* Check if the Interface window has focus.
* @function Window.hasFocus
* @returns {boolean} <code>true</code> if the Interface window has focus, otherwise <code>false</code>.
*/
QScriptValue hasFocus();
/**jsdoc
* Make the Interface window have focus.
* @function Window.setFocus
*/
void setFocus();
/**jsdoc
* Raise the Interface window if it is minimized, and give it focus.
* @function Window.raiseMainWindow
*/
void raiseMainWindow();
/**jsdoc
* Display a dialog with the specified message and an "OK" button. The dialog is non-modal; the script continues without
* waiting for a user response.
* @function Window.alert
* @param {string} message="" - The message to display.
* @example <caption>Display a friendly greeting.</caption>
* Window.alert("Welcome!");
* print("Script continues without waiting");
*/
void alert(const QString& message = "");
/**jsdoc
* Prompt the user to confirm something. Displays a modal dialog with a message plus "Yes" and "No" buttons.
* responds.
* @function Window.confirm
* @param {string} message="" - The question to display.
* @returns {boolean} <code>true</code> if the user selects "Yes", otherwise <code>false</code>.
* @example <caption>Ask the user a question requiring a yes/no answer.</caption>
* var answer = Window.confirm("Are you sure?");
* print(answer); // true or false
*/
QScriptValue confirm(const QString& message = "");
/**jsdoc
* Prompt the user to enter some text. Displays a modal dialog with a message and a text box, plus "OK" and "Cancel"
* buttons.
* @function Window.prompt
* @param {string} message - The question to display.
* @param {string} defaultText - The default answer text.
* @returns {string} The text that the user entered if they select "OK", otherwise "".
* @example <caption>Ask the user a question requiring a text answer.</caption>
* var answer = Window.prompt("Question", "answer");
* if (answer === "") {
* print("User canceled");
* } else {
* print("User answer: " + answer);
* }
*/
QScriptValue prompt(const QString& message, const QString& defaultText);
/**jsdoc
* Prompt the user to enter some text. Displays a non-modal dialog with a message and a text box, plus "OK" and "Cancel"
* buttons. A {@link Window.promptTextChanged|promptTextChanged} signal is emitted when the user OKs the dialog; no signal
* is emitted if the user cancels the dialog.
* @function Window.promptAsync
* @param {string} message - The question to display.
* @param {string} defaultText - The default answer text.
* @example <caption>Ask the user a question requiring a text answer without waiting for the answer.</caption>
* function onPromptTextChanged(text) {
* print("User answer: " + text);
* }
* Window.promptTextChanged.connect(onPromptTextChanged);
*
* Window.promptAsync("Question", "answer");
* print("Script continues without waiting");
*/
void promptAsync(const QString& message = "", const QString& defaultText = "");
/**jsdoc
* Prompt the user for input in a custom, modal dialog.
* @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.
*/
CustomPromptResult customPrompt(const QVariant& config);
/**jsdoc
* Prompt the user to choose a directory. Displays a modal dialog that navigates the directory tree.
* @function Window.browseDir
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @returns {string} The path of the directory if one is chosen, otherwise <code>null</code>.
* @example <caption>Ask the user to choose a directory.</caption>
* var directory = Window.browseDir("Select Directory", Paths.resources);
* print("Directory: " + directory);
*/
QScriptValue browseDir(const QString& title = "", const QString& directory = "");
/**jsdoc
* Prompt the user to choose a directory. Displays a non-modal dialog that navigates the directory tree. A
* {@link Window.browseDirChanged|browseDirChanged} signal is emitted when a directory is chosen; no signal is emitted if
* the user cancels the dialog.
* @function Window.browseDirAsync
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @example <caption>Ask the user to choose a directory without waiting for the answer.</caption>
* function onBrowseDirChanged(directory) {
* print("Directory: " + directory);
* }
* Window.browseDirChanged.connect(onBrowseDirChanged);
*
* Window.browseDirAsync("Select Directory", Paths.resources);
* print("Script continues without waiting");
*/
void browseDirAsync(const QString& title = "", const QString& directory = "");
/**jsdoc
* Prompt the user to choose a file. Displays a modal dialog that navigates the directory tree.
* @function Window.browse
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
* @returns {string} The path and name of the file if one is chosen, otherwise <code>null</code>.
* @example <caption>Ask the user to choose an image file.</caption>
* var filename = Window.browse("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)");
* print("File: " + filename);
*/
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
/**jsdoc
* Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A
* {@link Window.openFileChanged|openFileChanged} signal is emitted when a file is chosen; no signal is emitted if the user
* cancels the dialog.
* @function Window.browseAsync
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
* @example <caption>Ask the user to choose an image file without waiting for the answer.</caption>
* function onOpenFileChanged(filename) {
* print("File: " + filename);
* }
* Window.openFileChanged.connect(onOpenFileChanged);
*
* Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)");
* print("Script continues without waiting");
*/
void browseAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
/**jsdoc
* Prompt the user to specify the path and name of a file to save to. Displays a model dialog that navigates the directory
* tree and allows the user to type in a file name.
* @function Window.save
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
* @returns {string} The path and name of the file if one is specified, otherwise <code>null</code>. If a single file type
* is specified in the nameFilter, that file type extension is automatically appended to the result when appropriate.
* @example <caption>Ask the user to specify a file to save to.</caption>
* var filename = Window.save("Save to JSON file", Paths.resources, "*.json");
* print("File: " + filename);
*/
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
/**jsdoc
* Prompt the user to specify the path and name of a file to save to. Displays a non-model dialog that navigates the
* directory tree and allows the user to type in a file name. A {@link Window.saveFileChanged|saveFileChanged} signal is
* emitted when a file is specified; no signal is emitted if the user cancels the dialog.
* @function Window.saveAsync
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
* @example <caption>Ask the user to specify a file to save to without waiting for an answer.</caption>
* function onSaveFileChanged(filename) {
* print("File: " + filename);
* }
* Window.saveFileChanged.connect(onSaveFileChanged);
*
* Window.saveAsync("Save to JSON file", Paths.resources, "*.json");
* print("Script continues without waiting");
*/
void saveAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
/**jsdoc
* Prompt the user to choose an Asset Server item. Displays a modal dialog that navigates the tree of assets on the Asset
* Server.
* @function Window.browseAssets
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
* @returns {string} The path and name of the asset if one is chosen, otherwise <code>null</code>.
* @example <caption>Ask the user to select an FBX asset.</caption>
* var asset = Window.browseAssets("Select FBX File", "/", "*.fbx");
* print("FBX file: " + asset);
*/
QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
/**jsdoc
* Prompt the user to choose an Asset Server item. Displays a non-modal dialog that navigates the tree of assets on the
* Asset Server. A {@link Window.assetsDirChanged|assetsDirChanged} signal is emitted when an asset is chosen; no signal is
* emitted if the user cancels the dialog.
* @function Window.browseAssetsAsync
* @param {string} title="" - The title to display at the top of the dialog.
* @param {string} directory="" - The initial directory to start browsing at.
* @param {string} nameFilter="" - The types of files to display. Examples: <code>"*.json"</code> and
* <code>"Images (*.png *.jpg *.svg)"</code>. All files are displayed if a filter isn't specified.
* @example
* function onAssetsDirChanged(asset) {
* print("FBX file: " + asset);
* }
* Window.assetsDirChanged.connect(onAssetsDirChanged);
*
* Window.browseAssetsAsync("Select FBX File", "/", "*.fbx");
* print("Script continues without waiting");
*/
void browseAssetsAsync(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
/**jsdoc
* Open the Asset Browser dialog. If a file to upload is specified, the user is prompted to enter the folder and name to
* map the file to on the asset server.
* @function Window.showAssetServer
* @param {string} uploadFile="" - The path and name of a file to upload to the asset server.
* @example <caption>Upload a file to the asset server.</caption>
* var filename = Window.browse("Select File to Add to Asset Server", Paths.resources);
* print("File: " + filename);
* Window.showAssetServer(filename);
*/
void showAssetServer(const QString& upload = "");
/**jsdoc
* Get Interface's build number.
* @function Window.checkVersion
* @returns {string} - Interface's build number.
*/
QString checkVersion();
/**jsdoc
* Copies text to the operating system's clipboard.
* @function Window.copyToClipboard
* @param {string} text - The text to copy to the operating system's clipboard.
*/
void copyToClipboard(const QString& text);
/**jsdoc
* Takes a snapshot of the current Interface view from the primary camera. When a still image only is captured,
* {@link Window.stillSnapshotTaken|stillSnapshotTaken} is emitted; when a still image plus moving images are captured,
* {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted}
* are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings >
* General > Snapshots.
* @function Window.takeSnapshot
* @param {boolean} notify=true - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
* signal.
* @param {boolean} includeAnimated=false - If <code>true</code>, a moving image is captured as an animated GIF in addition
* to a still image.
* @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is <code>0</code> the
* 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) {
* print("Still snapshot taken: " + path);
* print("Notify: " + notify);
* }
*
* function onProcessingGifStarted(stillPath) {
* print("Still snapshot taken: " + stillPath);
* }
*
* function onProcessingGifCompleted(animatedPath) {
* print("Animated snapshot taken: " + animatedPath);
* }
*
* Window.stillSnapshotTaken.connect(onStillSnapshottaken);
* Window.processingGifStarted.connect(onProcessingGifStarted);
* Window.processingGifCompleted.connect(onProcessingGifCompleted);
*
* var notify = true;
* var animated = true;
* var aspect = 1920 / 1080;
* Window.takeSnapshot(notify, animated, aspect);
*/
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
/**jsdoc
* Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API.
* @function Window.takeSecondaryCameraSnapshot
*/
void takeSecondaryCameraSnapshot();
/**jsdoc
* Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that
* indicates whether or not a user connection was successfully made using the Web API.
* @function Window.makeConnection
* @param {boolean} success - If <code>true</code> then {@link Window.connectionAdded|connectionAdded} is emitted, otherwise
* {@link Window.connectionError|connectionError} is emitted.
* @param {string} description - Descriptive text about the connection success or error. This is sent in the signal emitted.
*/
void makeConnection(bool success, const QString& userNameOrError);
/**jsdoc
* Display a notification message. Notifications are displayed in panels by the default script, nofications.js. An
* {@link Window.announcement|announcement} signal is emitted when this function is called.
* @function Window.displayAnnouncement
* @param {string} message - The announcement message.
* @example <caption>Send and capture an announcement message.</caption>
* function onAnnouncement(message) {
* // The message is also displayed as a notification by notifications.js.
* print("Announcement: " + message);
* }
* Window.announcement.connect(onAnnouncement);
*
* Window.displayAnnouncement("Hello");
*/
void displayAnnouncement(const QString& message);
/**jsdoc
* Prepare a snapshot ready for sharing. A {@link Window.snapshotShared|snapshotShared} signal is emitted when the snapshot
* has been prepared.
* @function Window.shareSnapshot
* @param {string} path - The path and name of the image file to share.
* @param {string} href="" - The metaverse location where the snapshot was taken.
*/
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
/**jsdoc
* Check to see if physics is active for you in the domain you're visiting - there is a delay between your arrival at a
* domain and physics becoming active for you in that domain.
* @function Window.isPhysicsEnabled
* @returns {boolean} <code>true</code> if physics is currently active for you, otherwise <code>false</code>.
* @example <caption>Wait for physics to be enabled when you change domains.</caption>
* function checkForPhysics() {
* var isPhysicsEnabled = Window.isPhysicsEnabled();
* print("Physics enabled: " + isPhysicsEnabled);
* if (!isPhysicsEnabled) {
* Script.setTimeout(checkForPhysics, 1000);
* }
* }
*
* function onDomainChanged(domain) {
* print("Domain changed: " + domain);
* Script.setTimeout(checkForPhysics, 1000);
* }
*
* Window.domainChanged.connect(onDomainChanged);
*/
bool isPhysicsEnabled();
/**jsdoc
* Set what to show on the PC display: normal view or entity camera view. The entity camera is configured using
* {@link Camera.setCameraEntity} and {@link Camera|Camera.mode}.
* @function Window.setDisplayTexture
* @param {Window.DisplayTexture} texture - The view to display.
* @returns {boolean} <code>true</code> if the display texture was successfully set, otherwise <code>false</code>.
*/
// See spectatorCamera.js for Valid parameter values.
/**jsdoc
* <p>The views that may be displayed on the PC display.</p>
* <table>
* <thead>
* <tr>
* <th>Value</th>
* <th>View Displayed</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><code>""</code></td>
* <td>Normal view.</td>
* </tr>
* <tr>
* <td><code>"resource://spectatorCameraFrame"</code></td>
* <td>Entity camera view.</td>
* </tr>
* </tbody>
* </table>
* @typedef Window.DisplayTexture
*/
bool setDisplayTexture(const QString& name);
/**jsdoc
* Check if a 2D point is within the desktop window if in desktop mode, or the drawable area of the HUD overlay if in HMD
* mode.
* @function Window.isPointOnDesktopWindow
* @param {Vec2} point - The point to check.
* @returns {boolean} <code>true</code> if the point is within the window or HUD, otherwise <code>false</code>.
*/
bool isPointOnDesktopWindow(QVariant point);
/**jsdoc
* Get the size of the drawable area of the Interface window if in desktop mode or the HMD rendering surface if in HMD mode.
* @function Window.getDeviceSize
* @returns {Vec2} The width and height of the Interface window or HMD rendering surface, in pixels.
*/
glm::vec2 getDeviceSize() const;
/**jsdoc
* Open a non-modal message box that can have a variety of button combinations. See also,
* {@link Window.updateMessageBox|updateMessageBox} and {@link Window.closeMessageBox|closeMessageBox}.
* @function Window.openMessageBox
* @param {string} title - The title to display for the message box.
* @param {string} text - Text to display in the message box.
* @param {Window.MessageBoxButton} buttons - The buttons to display on the message box; one or more button values added
* together.
* @param {Window.MessageBoxButton} defaultButton - The button that has focus when the message box is opened.
* @returns {number} The ID of the message box created.
* @example <caption>Ask the user whether that want to reset something.</caption>
* var messageBox;
* var resetButton = 0x4000000;
* var cancelButton = 0x400000;
*
* function onMessageBoxClosed(id, button) {
* if (id === messageBox) {
* if (button === resetButton) {
* print("Reset");
* } else {
* print("Don't reset");
* }
* }
* }
* Window.messageBoxClosed.connect(onMessageBoxClosed);
*
* messageBox = Window.openMessageBox("Reset Something",
* "Do you want to reset something?",
* resetButton + cancelButton, cancelButton);
*/
int openMessageBox(QString title, QString text, int buttons, int defaultButton);
/**jsdoc
* Update the content of a message box that was opened with {@link Window.openMessageBox|openMessageBox}.
* @function Window.updateMessageBox
* @param {number} id - The ID of the message box.
* @param {string} title - The title to display for the message box.
* @param {string} text - Text to display in the message box.
* @param {Window.MessageBoxButton} buttons - The buttons to display on the message box; one or more button values added
* together.
* @param {Window.MessageBoxButton} defaultButton - The button that has focus when the message box is opened.
*/
void updateMessageBox(int id, QString title, QString text, int buttons, int defaultButton);
/**jsdoc
* Close a message box that was opened with {@link Window.openMessageBox|openMessageBox}.
* @function Window.closeMessageBox
* @param {number} id - The ID of the message box.
*/
void closeMessageBox(int id);
private slots:
void onMessageBoxSelected(int button);
void disconnectedFromDomain();
signals:
void domainChanged(const QString& domainHostname);
/**jsdoc
* Triggered when you change the domain you're visiting. <strong>Warning:</strong> Is not emitted if you go to domain that
* isn't running.
* @function Window.domainChanged
* @param {string} domain - The domain's IP address.
* @returns {Signal}
* @example <caption>Report when you change domains.</caption>
* function onDomainChanged(domain) {
* print("Domain changed: " + domain);
* }
*
* Window.domainChanged.connect(onDomainChanged);
*/
void domainChanged(const QString& domain);
/**jsdoc
* Triggered when you try to navigate to a *.json, *.svo, or *.svo.json URL in a Web browser within Interface.
* @function Window.svoImportRequested
* @param {string} url - The URL of the file to import.
* @returns {Signal}
*/
void svoImportRequested(const QString& url);
/**jsdoc
* Triggered when you try to visit a domain but are refused connection.
* @function Window.domainConnectionRefused
* @param {string} reasonMessage - A description of the refusal.
* @param {Window.ConnectionRefusedReason} reasonCode - Integer number that enumerates the reason for the refusal.
* @param {string} extraInfo - Extra information about the refusal.
* @returns {Signal}
*/
void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo);
/**jsdoc
* Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with
* <code>includeAnimated = false</code>.
* @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}
* was called with.
* @returns {Signal}
*/
void stillSnapshotTaken(const QString& pathStillSnapshot, bool notify);
/**jsdoc
* Triggered when a snapshot submitted via {@link Window.shareSnapshot|shareSnapshot} is ready for sharing. The snapshot
* may then be shared via the {@link Account.metaverseServerURL} Web API.
* @function Window.snapshotShared
* @param {boolean} isError - <code>true</code> if an error was encountered preparing the snapshot for sharing, otherwise
* <code>false</code>.
* @param {string} reply - JSON-formatted information about the snapshot.
* @returns {Signal}
*/
void snapshotShared(bool isError, const QString& reply);
/**jsdoc
* Triggered when the snapshot images have been captured by {@link Window.takeSnapshot|takeSnapshot} and the GIF is
* starting to be processed.
* @function Window.processingGifStarted
* @param {string} pathStillSnapshot - The path and name of the still snapshot image file.
* @returns {Signal}
*/
void processingGifStarted(const QString& pathStillSnapshot);
/**jsdoc
* Triggered when a GIF has been prepared of the snapshot images captured by {@link Window.takeSnapshot|takeSnapshot}.
* @function Window.processingGifCompleted
* @param {string} pathAnimatedSnapshot - The path and name of the moving snapshot GIF file.
* @returns {Signal}
*/
void processingGifCompleted(const QString& pathAnimatedSnapshot);
/**jsdoc
* Triggered when you've successfully made a user connection.
* @function Window.connectionAdded
* @param {string} message - A description of the success.
* @returns {Signal}
*/
void connectionAdded(const QString& connectionName);
/**jsdoc
* Triggered when you failed to make a user connection.
* @function Window.connectionError
* @param {string} message - A description of the error.
* @returns {Signal}
*/
void connectionError(const QString& errorString);
/**jsdoc
* Triggered when a message is announced by {@link Window.displayAnnouncement|displayAnnouncement}.
* @function Window.announcement
* @param {string} message - The message text.
* @returns {Signal}
*/
void announcement(const QString& message);
/**jsdoc
* Triggered when the user closes a message box that was opened with {@link Window.openMessageBox|openMessageBox}.
* @function Window.messageBoxClosed
* @param {number} id - The ID of the message box that was closed.
* @param {number} button - The button that the user clicked. If the user presses Esc, the Cancel button value is returned,
* whether or not the Cancel button is displayed in the message box.
* @returns {Signal}
*/
void messageBoxClosed(int id, int button);
/**jsdoc
* Triggered when the user chooses a directory in a {@link Window.browseDirAsync|browseDirAsync} dialog.
* @function Window.browseDirChanged
* @param {string} directory - The directory the user chose in the dialog.
* @returns {Signal}
*/
void browseDirChanged(QString browseDir);
/**jsdoc
* Triggered when the user chooses an asset in a {@link Window.browseAssetsAsync|browseAssetsAsync} dialog.
* @function Window.assetsDirChanged
* @param {string} asset - The path and name of the asset the user chose in the dialog.
* @returns {Signal}
*/
void assetsDirChanged(QString assetsDir);
/**jsdoc
* Triggered when the user specifies a file in a {@link Window.saveAsync|saveAsync} dialog.
* @function Window.saveFileChanged
* @param {string} filename - The path and name of the file that the user specified in the dialog.
* @returns {Signal}
*/
void saveFileChanged(QString filename);
/**jsdoc
* Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog.
* @function Window.openFileChanged
* @param {string} filename - The path and name of the file the user chose in the dialog.
* @returns {Signal}
*/
void openFileChanged(QString filename);
/**jsdoc
* Triggered when the user OKs a {@link Window.promptAsync|promptAsync} dialog.
* @function Window.promptTextChanged
* @param {string} text - The text the user entered in the dialog.
* @returns {Signal}
*/
void promptTextChanged(QString text);
// triggered when window size or position changes
/**jsdoc
* Triggered when the position or size of the Interface window changes.
* @function Window.geometryChanged
* @param {Rect} geometry - The position and size of the drawable area of the Interface window.
* @returns {Signal}
* @example <caption>Report the position of size of the Interface window when it changes.</caption>
* function onWindowGeometryChanged(rect) {
* print("Window geometry: " + JSON.stringify(rect));
* }
*
* Window.geometryChanged.connect(onWindowGeometryChanged);
*/
void geometryChanged(QRect geometry);
private:

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

@ -397,10 +397,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

@ -165,10 +165,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

@ -143,10 +143,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

@ -219,10 +219,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

@ -286,10 +286,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

@ -317,10 +317,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

@ -233,6 +233,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.
*/

View file

@ -138,10 +138,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

@ -139,10 +139,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

@ -58,10 +58,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

@ -235,10 +235,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

@ -222,10 +222,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);
}
@ -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

@ -1265,7 +1265,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 +1280,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 +1327,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 +1551,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 +1624,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);

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;
@ -252,7 +257,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);

View file

@ -37,6 +37,16 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
SkeletonModel::~SkeletonModel() {
}
void SkeletonModel::setURL(const QUrl& url) {
_texturesLoaded = false;
Model::setURL(url);
}
void SkeletonModel::setTextures(const QVariantMap& textures) {
_texturesLoaded = false;
Model::setTextures(textures);
}
void SkeletonModel::initJointStates() {
const FBXGeometry& geometry = getFBXGeometry();
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
@ -142,6 +152,13 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
Parent::simulate(deltaTime, fullUpdate);
}
// FIXME: This texture loading logic should probably live in Avatar, to mirror RenderableModelEntityItem and ModelOverlay,
// but Avatars don't get updates in the same way
if (!_texturesLoaded && getGeometry() && getGeometry()->areTexturesLoaded()) {
_texturesLoaded = true;
updateRenderItems();
}
if (!isActive() || !_owningAvatar->isMyAvatar()) {
return; // only simulate for own avatar
}

View file

@ -31,6 +31,9 @@ public:
SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr);
~SkeletonModel();
Q_INVOKABLE void setURL(const QUrl& url) override;
Q_INVOKABLE void setTextures(const QVariantMap& textures) override;
void initJointStates() override;
void simulate(float deltaTime, bool fullUpdate = true) override;
@ -115,8 +118,6 @@ protected:
void computeBoundingShape();
protected:
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
Avatar* _owningAvatar;
@ -128,6 +129,9 @@ protected:
glm::vec3 _defaultEyeModelPosition;
float _headClipDistance; // Near clip distance to use if no separate head model
private:
bool _texturesLoaded { false };
};
#endif // hifi_SkeletonModel_h

View file

@ -42,6 +42,7 @@
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
std::function<bool()> EntityTreeRenderer::_renderDebugHullsOperator = [] { return false; };
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
AbstractScriptingServicesInterface* scriptingServices) :

View file

@ -116,10 +116,14 @@ public:
EntityItemPointer getEntity(const EntityItemID& id);
void onEntityChanged(const EntityItemID& id);
static void setRenderDebugHullsOperator(std::function<bool()> renderDebugHullsOperator) { _renderDebugHullsOperator = renderDebugHullsOperator; }
static bool shouldRenderDebugHulls() { return _renderDebugHullsOperator(); }
signals:
void enterEntity(const EntityItemID& entityItemID);
void leaveEntity(const EntityItemID& entityItemID);
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
void setRenderDebugHulls();
public slots:
void addingEntity(const EntityItemID& entityID);
@ -255,6 +259,8 @@ private:
static int _entitiesScriptEngineCount;
static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc;
static std::function<bool()> _entitiesShouldFadeFunction;
static std::function<bool()> _renderDebugHullsOperator;
};

View file

@ -141,6 +141,10 @@ std::shared_ptr<T> make_renderer(const EntityItemPointer& entity) {
}
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity) {
connect(entity.get(), &EntityItem::requestRenderUpdate, this, [&] {
_needsRenderUpdate = true;
emit requestRenderUpdate();
});
}
EntityRenderer::~EntityRenderer() { }
@ -311,6 +315,9 @@ void EntityRenderer::setSubRenderItemIDs(const render::ItemIDs& ids) {
// Returns true if the item needs to have updateInscene called because of internal rendering
// changes (animation, fading, etc)
bool EntityRenderer::needsRenderUpdate() const {
if (_needsRenderUpdate) {
return true;
}
if (_prevIsTransparent != isTransparent()) {
return true;
}
@ -331,10 +338,6 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
return true;
}
if (_visible != entity->getVisible()) {
return true;
}
if (_moving != entity->isMovingRelativeToParent()) {
return true;
}
@ -363,6 +366,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
_moving = entity->isMovingRelativeToParent();
_visible = entity->getVisible();
_needsRenderUpdate = false;
});
}

View file

@ -125,6 +125,7 @@ protected:
bool _prevIsTransparent { false };
bool _visible { false };
bool _moving { false };
bool _needsRenderUpdate { false };
// Only touched on the rendering thread
bool _renderUpdateQueued{ false };

View file

@ -954,8 +954,23 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() {
using namespace render;
using namespace render::entities;
ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
connect(DependencyManager::get<EntityTreeRenderer>().data(), &EntityTreeRenderer::setRenderDebugHulls, this, [&] {
_needsCollisionGeometryUpdate = true;
emit requestRenderUpdate();
});
}
void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) {
if (didVisualGeometryRequestSucceed) {
_itemKey = ItemKey::Builder().withTypeMeta();
} else {
_itemKey = ItemKey::Builder().withTypeMeta().withTypeShape();
}
}
ItemKey ModelEntityRenderer::getKey() {
return ItemKey::Builder().withTypeMeta().withTypeShape();
return _itemKey;
}
uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
@ -1080,7 +1095,7 @@ bool ModelEntityRenderer::needsRenderUpdate() const {
return true;
}
if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
if (!_texturesLoaded) {
return true;
}
@ -1209,7 +1224,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
// Check for addition
if (_hasModel && !(bool)_model) {
model = std::make_shared<Model>(nullptr, entity.get());
connect(model.get(), &Model::setURLFinished, this, &ModelEntityRenderer::requestRenderUpdate);
connect(model.get(), &Model::setURLFinished, this, [&](bool didVisualGeometryRequestSucceed) {
setKey(didVisualGeometryRequestSucceed);
emit requestRenderUpdate();
});
connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate);
connect(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate);
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
@ -1275,7 +1293,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
setCollisionMeshKey(entity->getCollisionMeshKey());
_needsCollisionGeometryUpdate = false;
ShapeType type = entity->getShapeType();
if (_showCollisionGeometry && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) {
if (DependencyManager::get<EntityTreeRenderer>()->shouldRenderDebugHulls() && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) {
// NOTE: it is OK if _collisionMeshKey is nullptr
model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey);
// NOTE: the model will render the collisionGeometry if it has one
@ -1310,6 +1328,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) {
_texturesLoaded = true;
model->updateRenderItems();
} else if (!_texturesLoaded) {
emit requestRenderUpdate();
}
// When the individual mesh parts of a model finish fading, they will mark their Model as needing updating
@ -1342,20 +1362,11 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender");
DETAILED_PERFORMANCE_TIMER("RMEIrender");
ModelPointer model;
withReadLock([&]{
model = _model;
});
// If we don't have a model, or the model doesn't have visual geometry, render our bounding box as green wireframe
if (!model || (model && model->didVisualGeometryRequestFail())) {
static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getModelTransform()); // we want to include the scale as well
DependencyManager::get<GeometryCache>()->renderWireCubeInstance(args, batch, greenColor);
return;
}
// If the model doesn't have visual geometry, render our bounding box as green wireframe
static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getModelTransform()); // we want to include the scale as well
DependencyManager::get<GeometryCache>()->renderWireCubeInstance(args, batch, greenColor);
// Enqueue updates for the next frame
#if WANT_EXTRA_DEBUGGING
@ -1366,12 +1377,6 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
// Remap textures for the next frame to avoid flicker
// remapTextures();
bool showCollisionGeometry = (bool)(args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS);
if (showCollisionGeometry != _showCollisionGeometry) {
_showCollisionGeometry = showCollisionGeometry;
flagForCollisionGeometryUpdate();
}
}
void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames) {

View file

@ -133,12 +133,13 @@ class ModelEntityRenderer : public TypedEntityRenderer<RenderableModelEntityItem
friend class EntityRenderer;
public:
ModelEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {}
ModelEntityRenderer(const EntityItemPointer& entity);
protected:
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
void setKey(bool didVisualGeometryRequestSucceed);
virtual ItemKey getKey() override;
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
@ -169,10 +170,9 @@ private:
bool _hasTransitioned{ false };
#endif
bool _needsJointSimulation{ false };
bool _showCollisionGeometry{ false };
bool _needsCollisionGeometryUpdate{ false };
const void* _collisionMeshKey{ nullptr };
bool _needsJointSimulation { false };
bool _needsCollisionGeometryUpdate { false };
const void* _collisionMeshKey { nullptr };
// used on client side
bool _jointMappingCompleted{ false };
@ -185,6 +185,8 @@ private:
bool _shouldHighlight { false };
bool _animating { false };
uint64_t _lastAnimated { 0 };
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
};
} } // namespace

View file

@ -23,6 +23,8 @@ using namespace render::entities;
static uint8_t CUSTOM_PIPELINE_NUMBER = 0;
static gpu::Stream::FormatPointer _vertexFormat;
static std::weak_ptr<gpu::Pipeline> _texturedPipeline;
// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 11 to avoid collisions
static int32_t PARTICLE_UNIFORM_SLOT { 11 };
static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key) {
auto texturedPipeline = _texturedPipeline.lock();
@ -38,6 +40,9 @@ static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, co
auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
auto program = gpu::Shader::createProgram(vertShader, fragShader);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("particleBuffer"), PARTICLE_UNIFORM_SLOT));
gpu::Shader::makeProgram(*program, slotBindings);
_texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state);
}
@ -320,7 +325,7 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
transform.setScale(vec3(1));
}
batch.setModelTransform(transform);
batch.setUniformBuffer(0, _uniformBuffer);
batch.setUniformBuffer(PARTICLE_UNIFORM_SLOT, _uniformBuffer);
batch.setInputFormat(_vertexFormat);
batch.setInputBuffer(0, _particleBuffer, 0, sizeof(GpuParticle));

View file

@ -33,8 +33,9 @@ using namespace render;
using namespace render::entities;
static uint8_t CUSTOM_PIPELINE_NUMBER { 0 };
static const int32_t PAINTSTROKE_TEXTURE_SLOT{ 0 };
static const int32_t PAINTSTROKE_UNIFORM_SLOT{ 0 };
static const int32_t PAINTSTROKE_TEXTURE_SLOT { 0 };
// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 11 to avoid collisions
static const int32_t PAINTSTROKE_UNIFORM_SLOT { 11 };
static gpu::Stream::FormatPointer polylineFormat;
static gpu::PipelinePointer polylinePipeline;
#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT

View file

@ -1422,27 +1422,29 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
}
bool success = false;
MeshProxy* meshProxy = nullptr;
glm::mat4 transform = voxelToLocalMatrix();
withReadLock([&] {
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices();
if (!_meshReady) {
// we aren't ready to return a mesh. the caller will have to try again later.
success = false;
} else if (numVertices == 0) {
// we are ready, but there are no triangles in the mesh.
success = true;
} else {
success = true;
// the mesh will be in voxel-space. transform it into object-space
meshProxy = new SimpleMeshProxy(
_mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[=](glm::vec3 color){ return color; },
[=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
[&](uint32_t index){ return index; }));
result << meshProxy;
}
});
if (_mesh) {
MeshProxy* meshProxy = nullptr;
glm::mat4 transform = voxelToLocalMatrix();
withReadLock([&] {
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices();
if (!_meshReady) {
// we aren't ready to return a mesh. the caller will have to try again later.
success = false;
} else if (numVertices == 0) {
// we are ready, but there are no triangles in the mesh.
success = true;
} else {
success = true;
// the mesh will be in voxel-space. transform it into object-space
meshProxy = new SimpleMeshProxy(
_mesh->map([=](glm::vec3 position) { return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[=](glm::vec3 color) { return color; },
[=](glm::vec3 normal) { return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
[&](uint32_t index) { return index; }));
result << meshProxy;
}
});
}
return success;
}

View file

@ -145,10 +145,8 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
updateSkyboxMap();
if (_needBackgroundUpdate) {
if (BackgroundStage::isIndexInvalid(_backgroundIndex)) {
if (_skyboxMode == COMPONENT_MODE_ENABLED && BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundIndex = _backgroundStage->addBackground(_background);
} else {
}
_needBackgroundUpdate = false;
}
@ -164,20 +162,27 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
}
if (_visible) {
// FInally, push the light visible in the frame
// THe directional key light for sure
_stage->_currentFrame.pushSunLight(_sunIndex);
// The ambient light only if it has a valid texture to render with
if (_validAmbientTexture || _validSkyboxTexture) {
_stage->_currentFrame.pushAmbientLight(_ambientIndex);
// Finally, push the light visible in the frame
if (_keyLightMode == COMPONENT_MODE_DISABLED) {
_stage->_currentFrame.pushSunLight(_stage->getSunOffLight());
} else if (_keyLightMode == COMPONENT_MODE_ENABLED) {
_stage->_currentFrame.pushSunLight(_sunIndex);
}
// The background only if the mode is not inherit
if (_backgroundMode != BACKGROUND_MODE_INHERIT) {
if (_skyboxMode == COMPONENT_MODE_DISABLED) {
_backgroundStage->_currentFrame.pushBackground(INVALID_INDEX);
} else if (_skyboxMode == COMPONENT_MODE_ENABLED) {
_backgroundStage->_currentFrame.pushBackground(_backgroundIndex);
}
// The ambient light only if it has a valid texture to render with
if (_ambientLightMode == COMPONENT_MODE_DISABLED) {
_stage->_currentFrame.pushAmbientLight(_stage->getAmbientOffLight());
} else if (_ambientLightMode == COMPONENT_MODE_ENABLED) {
_stage->_currentFrame.pushAmbientLight(_ambientIndex);
}
// Haze only if the mode is not inherit
if (_hazeMode != COMPONENT_MODE_INHERIT) {
_hazeStage->_currentFrame.pushHaze(_hazeIndex);
@ -194,14 +199,13 @@ void ZoneEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction&
Parent::removeFromScene(scene, transaction);
}
void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
DependencyManager::get<EntityTreeRenderer>()->updateZone(entity->getID());
// FIXME one of the bools here could become true between being fetched and being reset,
// resulting in a lost update
bool sunChanged = entity->keyLightPropertiesChanged();
bool backgroundChanged = entity->backgroundPropertiesChanged();
bool keyLightChanged = entity->keyLightPropertiesChanged();
bool ambientLightChanged = entity->ambientLightPropertiesChanged();
bool skyboxChanged = entity->skyboxPropertiesChanged();
bool hazeChanged = entity->hazePropertiesChanged();
@ -211,9 +215,9 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
_lastDimensions = entity->getScaledDimensions();
_keyLightProperties = entity->getKeyLightProperties();
_ambientLightProperties = entity->getAmbientLightProperties();
_skyboxProperties = entity->getSkyboxProperties();
_hazeProperties = entity->getHazeProperties();
_stageProperties = entity->getStageProperties();
#if 0
if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) {
@ -236,15 +240,15 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
updateKeyZoneItemFromEntity();
if (sunChanged) {
updateKeySunFromEntity();
if (keyLightChanged) {
updateKeySunFromEntity(entity);
}
if (sunChanged || skyboxChanged) {
updateKeyAmbientFromEntity();
if (ambientLightChanged) {
updateAmbientLightFromEntity(entity);
}
if (backgroundChanged || skyboxChanged) {
if (skyboxChanged) {
updateKeyBackgroundFromEntity(entity);
}
@ -267,9 +271,10 @@ ItemKey ZoneEntityRenderer::getKey() {
bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
if (entity->keyLightPropertiesChanged() ||
entity->backgroundPropertiesChanged() ||
entity->ambientLightPropertiesChanged() ||
entity->hazePropertiesChanged() ||
entity->skyboxPropertiesChanged()) {
return true;
}
@ -310,7 +315,9 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
return false;
}
void ZoneEntityRenderer::updateKeySunFromEntity() {
void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity) {
setKeyLightMode((ComponentMode)entity->getKeyLightMode());
const auto& sunLight = editSunLight();
sunLight->setType(model::Light::SUN);
sunLight->setPosition(_lastPosition);
@ -322,20 +329,22 @@ void ZoneEntityRenderer::updateKeySunFromEntity() {
sunLight->setDirection(_keyLightProperties.getDirection());
}
void ZoneEntityRenderer::updateKeyAmbientFromEntity() {
void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& entity) {
setAmbientLightMode((ComponentMode)entity->getAmbientLightMode());
const auto& ambientLight = editAmbientLight();
ambientLight->setType(model::Light::AMBIENT);
ambientLight->setPosition(_lastPosition);
ambientLight->setOrientation(_lastRotation);
// Set the keylight
ambientLight->setAmbientIntensity(_keyLightProperties.getAmbientIntensity());
// Set the ambient light
ambientLight->setAmbientIntensity(_ambientLightProperties.getAmbientIntensity());
if (_keyLightProperties.getAmbientURL().isEmpty()) {
if (_ambientLightProperties.getAmbientURL().isEmpty()) {
setAmbientURL(_skyboxProperties.getURL());
} else {
setAmbientURL(_keyLightProperties.getAmbientURL());
setAmbientURL(_ambientLightProperties.getAmbientURL());
}
}
@ -370,8 +379,9 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity)
}
void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) {
setSkyboxMode((ComponentMode)entity->getSkyboxMode());
editBackground();
setBackgroundMode(entity->getBackgroundMode());
setSkyboxColor(_skyboxProperties.getColorVec3());
setProceduralUserData(entity->getUserData());
setSkyboxURL(_skyboxProperties.getURL());
@ -403,7 +413,6 @@ void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) {
_ambientTextureURL = ambientUrl;
if (_ambientTextureURL.isEmpty()) {
_validAmbientTexture = false;
_pendingAmbientTexture = false;
_ambientTexture.clear();
@ -431,7 +440,6 @@ void ZoneEntityRenderer::updateAmbientMap() {
_ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY);
}
editAmbientLight()->setAmbientMap(texture);
_validAmbientTexture = true;
} else {
qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL();
}
@ -447,7 +455,6 @@ void ZoneEntityRenderer::setSkyboxURL(const QString& skyboxUrl) {
_skyboxTextureURL = skyboxUrl;
if (_skyboxTextureURL.isEmpty()) {
_validSkyboxTexture = false;
_pendingSkyboxTexture = false;
_skyboxTexture.clear();
@ -467,7 +474,6 @@ void ZoneEntityRenderer::updateSkyboxMap() {
auto texture = _skyboxTexture->getGPUTexture();
if (texture) {
editSkybox()->setCubemap(texture);
_validSkyboxTexture = true;
} else {
qCDebug(entitiesrenderer) << "Failed to load Skybox texture:" << _skyboxTexture->getURL();
}
@ -475,14 +481,22 @@ void ZoneEntityRenderer::updateSkyboxMap() {
}
}
void ZoneEntityRenderer::setBackgroundMode(BackgroundMode mode) {
_backgroundMode = mode;
}
void ZoneEntityRenderer::setHazeMode(ComponentMode mode) {
_hazeMode = mode;
}
void ZoneEntityRenderer::setKeyLightMode(ComponentMode mode) {
_keyLightMode = mode;
}
void ZoneEntityRenderer::setAmbientLightMode(ComponentMode mode) {
_ambientLightMode = mode;
}
void ZoneEntityRenderer::setSkyboxMode(ComponentMode mode) {
_skyboxMode = mode;
}
void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) {
editSkybox()->setColor(color);
}

View file

@ -46,16 +46,20 @@ protected:
private:
void updateKeyZoneItemFromEntity();
void updateKeySunFromEntity();
void updateKeyAmbientFromEntity();
void updateKeySunFromEntity(const TypedEntityPointer& entity);
void updateAmbientLightFromEntity(const TypedEntityPointer& entity);
void updateHazeFromEntity(const TypedEntityPointer& entity);
void updateKeyBackgroundFromEntity(const TypedEntityPointer& entity);
void updateAmbientMap();
void updateSkyboxMap();
void setAmbientURL(const QString& ambientUrl);
void setSkyboxURL(const QString& skyboxUrl);
void setBackgroundMode(BackgroundMode mode);
void setHazeMode(ComponentMode mode);
void setKeyLightMode(ComponentMode mode);
void setAmbientLightMode(ComponentMode mode);
void setSkyboxMode(ComponentMode mode);
void setSkyboxColor(const glm::vec3& color);
void setProceduralUserData(const QString& userData);
@ -84,8 +88,10 @@ private:
const model::SunSkyStagePointer _background{ std::make_shared<model::SunSkyStage>() };
const model::HazePointer _haze{ std::make_shared<model::Haze>() };
BackgroundMode _backgroundMode{ BACKGROUND_MODE_INHERIT };
ComponentMode _hazeMode{ COMPONENT_MODE_INHERIT };
ComponentMode _keyLightMode { COMPONENT_MODE_INHERIT };
ComponentMode _ambientLightMode { COMPONENT_MODE_INHERIT };
ComponentMode _skyboxMode { COMPONENT_MODE_INHERIT };
ComponentMode _hazeMode { COMPONENT_MODE_INHERIT };
indexed_container::Index _sunIndex{ LightStage::INVALID_INDEX };
indexed_container::Index _shadowIndex{ LightStage::INVALID_INDEX };
@ -104,20 +110,18 @@ private:
bool _needHazeUpdate{ true };
KeyLightPropertyGroup _keyLightProperties;
AmbientLightPropertyGroup _ambientLightProperties;
HazePropertyGroup _hazeProperties;
StagePropertyGroup _stageProperties;
SkyboxPropertyGroup _skyboxProperties;
// More attributes used for rendering:
QString _ambientTextureURL;
NetworkTexturePointer _ambientTexture;
bool _pendingAmbientTexture{ false };
bool _validAmbientTexture{ false };
QString _skyboxTextureURL;
NetworkTexturePointer _skyboxTexture;
bool _pendingSkyboxTexture{ false };
bool _validSkyboxTexture{ false };
QString _proceduralUserData;
Transform _renderTransform;

View file

@ -0,0 +1,155 @@
//
// AmbientLightPropertyGroup.cpp
// libraries/entities/src
//
// Created by Nissim Hadar on 2017/12/24.
// Copyright 2013 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 "AmbientLightPropertyGroup.h"
#include <QJsonDocument>
#include <OctreePacketData.h>
#include "EntityItemProperties.h"
#include "EntityItemPropertiesMacros.h"
const float AmbientLightPropertyGroup::DEFAULT_AMBIENT_LIGHT_INTENSITY = 0.5f;
void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties,
QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL);
}
void AmbientLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientIntensity, float, setAmbientIntensity);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientURL, QString, setAmbientURL);
// legacy property support
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ambientLightAmbientIntensity, float, setAmbientIntensity, getAmbientIntensity);
}
void AmbientLightPropertyGroup::merge(const AmbientLightPropertyGroup& other) {
COPY_PROPERTY_IF_CHANGED(ambientIntensity);
COPY_PROPERTY_IF_CHANGED(ambientURL);
}
void AmbientLightPropertyGroup::debugDump() const {
qCDebug(entities) << " AmbientLightPropertyGroup: ---------------------------------------------";
qCDebug(entities) << " ambientIntensity:" << getAmbientIntensity();
qCDebug(entities) << " ambientURL:" << getAmbientURL();
}
void AmbientLightPropertyGroup::listChangedProperties(QList<QString>& out) {
if (ambientIntensityChanged()) {
out << "ambientLight-ambientIntensity";
}
if (ambientURLChanged()) {
out << "ambientLight-ambientURL";
}
}
bool AmbientLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, getAmbientIntensity());
APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, getAmbientURL());
return true;
}
bool AmbientLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt,
int& processedBytes) {
int bytesRead = 0;
bool overwriteLocalData = true;
bool somethingChanged = false;
READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, float, setAmbientIntensity);
READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, QString, setAmbientURL);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_INTENSITY, AmbientIntensity);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_URL, AmbientURL);
processedBytes += bytesRead;
Q_UNUSED(somethingChanged);
return true;
}
void AmbientLightPropertyGroup::markAllChanged() {
_ambientIntensityChanged = true;
_ambientURLChanged = true;
}
EntityPropertyFlags AmbientLightPropertyGroup::getChangedProperties() const {
EntityPropertyFlags changedProperties;
CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_INTENSITY, ambientIntensity);
CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_URL, ambientURL);
return changedProperties;
}
void AmbientLightPropertyGroup::getProperties(EntityItemProperties& properties) const {
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientIntensity, getAmbientIntensity);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientURL, getAmbientURL);
}
bool AmbientLightPropertyGroup::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = false;
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientIntensity, ambientIntensity, setAmbientIntensity);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientURL, ambientURL, setAmbientURL);
return somethingChanged;
}
EntityPropertyFlags AmbientLightPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties;
requestedProperties += PROP_AMBIENT_LIGHT_INTENSITY;
requestedProperties += PROP_AMBIENT_LIGHT_URL;
return requestedProperties;
}
void AmbientLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, getAmbientIntensity());
APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, getAmbientURL());
}
int AmbientLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) {
int bytesRead = 0;
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, float, setAmbientIntensity);
READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, QString, setAmbientURL);
return bytesRead;
}

View file

@ -1,21 +1,25 @@
//
// StagePropertyGroup.h
// AmbientLightPropertyGroup.h
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Created by Nissim Hadar on 2017/12/24.
// Copyright 2013 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_StagePropertyGroup_h
#define hifi_StagePropertyGroup_h
#ifndef hifi_AmbientLightPropertyGroup_h
#define hifi_AmbientLightPropertyGroup_h
#include <stdint.h>
#include <glm/glm.hpp>
#include <QtScript/QScriptEngine>
#include "PropertyGroup.h"
#include "EntityItemPropertiesMacros.h"
#include "PropertyGroup.h"
class EntityItemProperties;
class EncodeBitstreamParams;
@ -23,11 +27,7 @@ class OctreePacketData;
class EntityTreeElementExtraEncodeData;
class ReadBitstreamToTreeParams;
#include <stdint.h>
#include <glm/glm.hpp>
class StagePropertyGroup : public PropertyGroup {
class AmbientLightPropertyGroup : public PropertyGroup {
public:
// EntityItemProperty related helpers
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties,
@ -35,7 +35,7 @@ public:
EntityItemProperties& defaultEntityProperties) const override;
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override;
void merge(const StagePropertyGroup& other);
void merge(const AmbientLightPropertyGroup& other);
virtual void debugDump() const override;
virtual void listChangedProperties(QList<QString>& out) override;
@ -74,23 +74,10 @@ public:
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) override;
static const bool DEFAULT_STAGE_SUN_MODEL_ENABLED;
static const float DEFAULT_STAGE_LATITUDE;
static const float DEFAULT_STAGE_LONGITUDE;
static const float DEFAULT_STAGE_ALTITUDE;
static const quint16 DEFAULT_STAGE_DAY;
static const float DEFAULT_STAGE_HOUR;
static const float DEFAULT_AMBIENT_LIGHT_INTENSITY;
float calculateHour() const;
uint16_t calculateDay() const;
DEFINE_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, SunModelEnabled, sunModelEnabled, bool, DEFAULT_STAGE_SUN_MODEL_ENABLED);
DEFINE_PROPERTY(PROP_STAGE_LATITUDE, Latitude, latitude, float, DEFAULT_STAGE_LATITUDE);
DEFINE_PROPERTY(PROP_STAGE_LONGITUDE, Longitude, longitude, float, DEFAULT_STAGE_LONGITUDE);
DEFINE_PROPERTY(PROP_STAGE_ALTITUDE, Altitude, altitude, float, DEFAULT_STAGE_ALTITUDE);
DEFINE_PROPERTY(PROP_STAGE_DAY, Day, day, uint16_t, DEFAULT_STAGE_DAY);
DEFINE_PROPERTY(PROP_STAGE_HOUR, Hour, hour, float, DEFAULT_STAGE_HOUR);
DEFINE_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, AutomaticHourDay, automaticHourDay, bool, false);
DEFINE_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_AMBIENT_LIGHT_INTENSITY);
DEFINE_PROPERTY_REF(PROP_AMBIENT_LIGHT_URL, AmbientURL, ambientURL, QString, "");
};
#endif // hifi_StagePropertyGroup_h
#endif // hifi_AmbientLightPropertyGroup_h

View file

@ -965,7 +965,10 @@ void EntityItem::setMass(float mass) {
void EntityItem::setHref(QString value) {
auto href = value.toLower();
if (! (value.toLower().startsWith("hifi://")) ) {
// If the string has something and doesn't start with with "hifi://" it shouldn't be set
// We allow the string to be empty, because that's the initial state of this property
if ( !(value.toLower().startsWith("hifi://")) && !value.isEmpty()) {
return;
}
withWriteLock([&] {
@ -2712,9 +2715,17 @@ bool EntityItem::getVisible() const {
}
void EntityItem::setVisible(bool value) {
bool changed = false;
withWriteLock([&] {
_visible = value;
if (_visible != value) {
changed = true;
_visible = value;
}
});
if (changed) {
emit requestRenderUpdate();
}
}
bool EntityItem::isChildOfMyAvatar() const {

View file

@ -469,6 +469,9 @@ public:
static QString _marketplacePublicKey;
static void retrieveMarketplacePublicKey();
signals:
void requestRenderUpdate();
protected:
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;

View file

@ -33,8 +33,8 @@
AnimationPropertyGroup EntityItemProperties::_staticAnimation;
SkyboxPropertyGroup EntityItemProperties::_staticSkybox;
HazePropertyGroup EntityItemProperties::_staticHaze;
StagePropertyGroup EntityItemProperties::_staticStage;
KeyLightPropertyGroup EntityItemProperties::_staticKeyLight;
AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight;
EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1);
@ -79,6 +79,7 @@ void EntityItemProperties::debugDump() const {
getSkybox().debugDump();
getHaze().debugDump();
getKeyLight().debugDump();
getAmbientLight().debugDump();
qCDebug(entities) << " changed properties...";
EntityPropertyFlags props = getChangedProperties();
@ -182,47 +183,7 @@ void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) {
}
}
using BackgroundPair = std::pair<const BackgroundMode, const QString>;
const std::array<BackgroundPair, BACKGROUND_MODE_ITEM_COUNT> BACKGROUND_MODES = { {
BackgroundPair { BACKGROUND_MODE_INHERIT, { "inherit" } },
BackgroundPair { BACKGROUND_MODE_SKYBOX, { "skybox" } }
} };
QString EntityItemProperties::getBackgroundModeAsString() const {
return BACKGROUND_MODES[_backgroundMode].second;
}
QString EntityItemProperties::getBackgroundModeString(BackgroundMode mode) {
return BACKGROUND_MODES[mode].second;
}
void EntityItemProperties::setBackgroundModeFromString(const QString& backgroundMode) {
auto result = std::find_if(BACKGROUND_MODES.begin(), BACKGROUND_MODES.end(), [&](const BackgroundPair& pair) {
return (pair.second == backgroundMode);
});
if (result != BACKGROUND_MODES.end()) {
_backgroundMode = result->first;
_backgroundModeChanged = true;
}
}
using ComponentPair = std::pair<const ComponentMode, const QString>;
const std::array<ComponentPair, COMPONENT_MODE_ITEM_COUNT> COMPONENT_MODES = { {
ComponentPair{ COMPONENT_MODE_INHERIT,{ "inherit" } },
ComponentPair{ COMPONENT_MODE_DISABLED,{ "disabled" } },
ComponentPair{ COMPONENT_MODE_ENABLED,{ "enabled" } }
} };
QString EntityItemProperties::getHazeModeAsString() const {
// return "inherit" if _hazeMode is not valid
if (_hazeMode < COMPONENT_MODE_ITEM_COUNT) {
return COMPONENT_MODES[_hazeMode].second;
} else {
return COMPONENT_MODES[COMPONENT_MODE_INHERIT].second;
}
}
QString EntityItemProperties::getHazeModeString(uint32_t mode) {
QString EntityItemProperties::getComponentModeAsString(uint32_t mode) {
// return "inherit" if mode is not valid
if (mode < COMPONENT_MODE_ITEM_COUNT) {
return COMPONENT_MODES[mode].second;
@ -231,10 +192,27 @@ QString EntityItemProperties::getHazeModeString(uint32_t mode) {
}
}
void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) {
auto result = std::find_if(COMPONENT_MODES.begin(), COMPONENT_MODES.end(), [&](const ComponentPair& pair) {
return (pair.second == hazeMode);
QString EntityItemProperties::getHazeModeAsString() const {
return getComponentModeAsString(_hazeMode);
}
QString EntityItemProperties::getComponentModeString(uint32_t mode) {
// return "inherit" if mode is not valid
if (mode < COMPONENT_MODE_ITEM_COUNT) {
return COMPONENT_MODES[mode].second;
} else {
return COMPONENT_MODES[COMPONENT_MODE_INHERIT].second;
}
}
std::array<ComponentPair, COMPONENT_MODE_ITEM_COUNT>::const_iterator EntityItemProperties::findComponent(const QString& mode) {
return std::find_if(COMPONENT_MODES.begin(), COMPONENT_MODES.end(), [&](const ComponentPair& pair) {
return (pair.second == mode);
});
}
void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) {
auto result = findComponent(hazeMode);
if (result != COMPONENT_MODES.end()) {
_hazeMode = result->first;
@ -242,6 +220,45 @@ void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) {
}
}
QString EntityItemProperties::getKeyLightModeAsString() const {
return getComponentModeAsString(_keyLightMode);
}
void EntityItemProperties::setKeyLightModeFromString(const QString& keyLightMode) {
auto result = findComponent(keyLightMode);
if (result != COMPONENT_MODES.end()) {
_keyLightMode = result->first;
_keyLightModeChanged = true;
}
}
QString EntityItemProperties::getAmbientLightModeAsString() const {
return getComponentModeAsString(_ambientLightMode);
}
void EntityItemProperties::setAmbientLightModeFromString(const QString& ambientLightMode) {
auto result = findComponent(ambientLightMode);
if (result != COMPONENT_MODES.end()) {
_ambientLightMode = result->first;
_ambientLightModeChanged = true;
}
}
QString EntityItemProperties::getSkyboxModeAsString() const {
return getComponentModeAsString(_skyboxMode);
}
void EntityItemProperties::setSkyboxModeFromString(const QString& skyboxMode) {
auto result = findComponent(skyboxMode);
if (result != COMPONENT_MODES.end()) {
_skyboxMode = result->first;
_skyboxModeChanged = true;
}
}
EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
EntityPropertyFlags changedProperties;
@ -327,9 +344,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_STATIC_CERTIFICATE_VERSION, staticCertificateVersion);
CHECK_PROPERTY_CHANGE(PROP_NAME, name);
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode);
CHECK_PROPERTY_CHANGE(PROP_HAZE_MODE, hazeMode);
CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode);
CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode);
CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode);
CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl);
CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize);
@ -379,8 +398,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
changedProperties += _animation.getChangedProperties();
changedProperties += _keyLight.getChangedProperties();
changedProperties += _ambientLight.getChangedProperties();
changedProperties += _skybox.getChangedProperties();
changedProperties += _stage.getChangedProperties();
changedProperties += _haze.getChangedProperties();
return changedProperties;
@ -553,10 +572,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
// Zones only
if (_type == EntityTypes::Zone) {
_keyLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
_ambientLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BACKGROUND_MODE, backgroundMode, getBackgroundModeAsString());
_stage.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
_skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed);
@ -565,6 +582,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString());
_haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString());
}
// Web only
@ -751,9 +772,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName);
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL);
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(backgroundMode, BackgroundMode);
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(hazeMode, HazeMode);
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(keyLightMode, KeyLightMode);
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(ambientLightMode, AmbientLightMode);
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(skyboxMode, SkyboxMode);
COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl);
COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize);
@ -783,8 +805,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
_animation.copyFromScriptValue(object, _defaultSettings);
_keyLight.copyFromScriptValue(object, _defaultSettings);
_ambientLight.copyFromScriptValue(object, _defaultSettings);
_skybox.copyFromScriptValue(object, _defaultSettings);
_stage.copyFromScriptValue(object, _defaultSettings);
_haze.copyFromScriptValue(object, _defaultSettings);
COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL);
@ -910,9 +932,10 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(name);
COPY_PROPERTY_IF_CHANGED(collisionSoundURL);
COPY_PROPERTY_IF_CHANGED(backgroundMode);
COPY_PROPERTY_IF_CHANGED(hazeMode);
COPY_PROPERTY_IF_CHANGED(keyLightMode);
COPY_PROPERTY_IF_CHANGED(ambientLightMode);
COPY_PROPERTY_IF_CHANGED(skyboxMode);
COPY_PROPERTY_IF_CHANGED(sourceUrl);
COPY_PROPERTY_IF_CHANGED(voxelVolumeSize);
@ -932,8 +955,8 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
_animation.merge(other._animation);
_keyLight.merge(other._keyLight);
_ambientLight.merge(other._ambientLight);
_skybox.merge(other._skybox);
_stage.merge(other._stage);
_haze.merge(other._haze);
COPY_PROPERTY_IF_CHANGED(xTextureURL);
@ -1101,13 +1124,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float);
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3);
ADD_PROPERTY_TO_MAP(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray);
ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t);
ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString);
ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_MODE, BackgroundMode, backgroundMode, BackgroundMode);
ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString);
ADD_PROPERTY_TO_MAP(PROP_LINE_WIDTH, LineWidth, lineWidth, float);
ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector<glm::vec3>);
@ -1158,14 +1179,6 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color);
ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url);
ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_SUN_MODEL_ENABLED, Stage, stage, SunModelEnabled, sunModelEnabled);
ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_LATITUDE, Stage, stage, Latitude, latitude);
ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_LONGITUDE, Stage, stage, Longitude, longitude);
ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_ALTITUDE, Stage, stage, Altitude, altitude);
ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_DAY, Stage, stage, Day, day);
ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_HOUR, Stage, stage, Hour, hour);
ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_AUTOMATIC_HOURDAY, Stage, stage, AutomaticHourDay, automaticHourDay);
ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool);
ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool);
ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString);
@ -1188,6 +1201,10 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_RANGE, Haze, haze, HazeKeyLightRange, hazeKeyLightRange);
ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude);
ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t);
ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t);
// FIXME - these are not yet handled
@ -1414,14 +1431,12 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
_staticKeyLight.setProperties(properties);
_staticKeyLight.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
_staticStage.setProperties(properties);
_staticStage.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
_staticAmbientLight.setProperties(properties);
_staticAmbientLight.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)properties.getShapeType());
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL());
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, (uint32_t)properties.getBackgroundMode());
_staticSkybox.setProperties(properties);
_staticSkybox.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
@ -1432,6 +1447,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode());
_staticHaze.setProperties(properties);
_staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode());
APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode());
APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode());
}
if (properties.getType() == EntityTypes::PolyVox) {
@ -1768,12 +1787,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
}
if (properties.getType() == EntityTypes::Zone) {
properties.getKeyLight().decodeFromEditPacket(propertyFlags, dataAt , processedBytes);
properties.getStage().decodeFromEditPacket(propertyFlags, dataAt , processedBytes);
properties.getKeyLight().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
properties.getAmbientLight().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);
properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt , processedBytes);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed);
@ -1782,7 +1800,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode);
properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
}
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode);
}
if (properties.getType() == EntityTypes::PolyVox) {
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize);
@ -2010,13 +2032,13 @@ void EntityItemProperties::markAllChanged() {
_staticCertificateVersionChanged = true;
_keyLight.markAllChanged();
_ambientLight.markAllChanged();
_skybox.markAllChanged();
_backgroundModeChanged = true;
_hazeModeChanged = true;
_animation.markAllChanged();
_skybox.markAllChanged();
_stage.markAllChanged();
_haze.markAllChanged();
_sourceUrlChanged = true;
@ -2354,14 +2376,22 @@ QList<QString> EntityItemProperties::listChangedProperties() {
out += "staticCertificateVersion";
}
if (backgroundModeChanged()) {
out += "backgroundMode";
}
if (hazeModeChanged()) {
out += "hazeMode";
}
if (keyLightModeChanged()) {
out += "keyLightMode";
}
if (ambientLightModeChanged()) {
out += "ambientLightMode";
}
if (skyboxModeChanged()) {
out += "skyboxMode";
}
if (voxelVolumeSizeChanged()) {
out += "voxelVolumeSize";
}
@ -2463,8 +2493,8 @@ QList<QString> EntityItemProperties::listChangedProperties() {
getAnimation().listChangedProperties(out);
getKeyLight().listChangedProperties(out);
getAmbientLight().listChangedProperties(out);
getSkybox().listChangedProperties(out);
getStage().listChangedProperties(out);
getHaze().listChangedProperties(out);
return out;

View file

@ -41,12 +41,18 @@
#include "SimulationOwner.h"
#include "SkyboxPropertyGroup.h"
#include "HazePropertyGroup.h"
#include "StagePropertyGroup.h"
#include "TextEntityItem.h"
#include "ZoneEntityItem.h"
const quint64 UNKNOWN_CREATED_TIME = 0;
using ComponentPair = std::pair<const ComponentMode, const QString>;
const std::array<ComponentPair, COMPONENT_MODE_ITEM_COUNT> COMPONENT_MODES = { {
ComponentPair { COMPONENT_MODE_INHERIT, { "inherit" } },
ComponentPair { COMPONENT_MODE_DISABLED, { "disabled" } },
ComponentPair { COMPONENT_MODE_ENABLED, { "enabled" } }
} };
/// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an
/// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete
/// set of entity item properties via JavaScript hashes/QScriptValues
@ -173,13 +179,17 @@ public:
DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, particle::DEFAULT_RADIUS_FINISH);
DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, particle::DEFAULT_EMITTER_SHOULD_TRAIL);
DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);
DEFINE_PROPERTY_GROUP(AmbientLight, ambientLight, AmbientLightPropertyGroup);
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE);
DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA);
DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE);
DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME);
DEFINE_PROPERTY_REF_ENUM(PROP_BACKGROUND_MODE, BackgroundMode, backgroundMode, BackgroundMode, BACKGROUND_MODE_INHERIT);
DEFINE_PROPERTY_GROUP(Stage, stage, StagePropertyGroup);
DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED);
DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED);
DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_ENABLED);
// This is the default mode for zone creation
DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT);
DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup);
@ -248,9 +258,10 @@ public:
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
static QString getBackgroundModeString(BackgroundMode mode);
static QString getHazeModeString(uint32_t mode);
static QString getComponentModeString(uint32_t mode);
static QString getComponentModeAsString(uint32_t mode);
std::array<ComponentPair, COMPONENT_MODE_ITEM_COUNT>::const_iterator findComponent(const QString& mode);
public:
float getMaxDimension() const { return glm::compMax(_dimensions); }
@ -478,8 +489,11 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CertificateID, certificateID, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, StaticCertificateVersion, staticCertificateVersion, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, HazeMode, hazeMode, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, KeyLightMode, keyLightMode, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");

View file

@ -220,6 +220,10 @@ enum EntityPropertyList {
PROP_HAZE_KEYLIGHT_RANGE,
PROP_HAZE_KEYLIGHT_ALTITUDE,
PROP_KEY_LIGHT_MODE,
PROP_AMBIENT_LIGHT_MODE,
PROP_SKYBOX_MODE,
PROP_LOCAL_DIMENSIONS, // only used to convert values to and from scripts
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -243,21 +247,14 @@ enum EntityPropertyList {
// the size of the properties bitflags mask
PROP_KEYLIGHT_COLOR = PROP_COLOR,
PROP_KEYLIGHT_INTENSITY = PROP_INTENSITY,
PROP_KEYLIGHT_AMBIENT_INTENSITY = PROP_CUTOFF,
PROP_KEYLIGHT_DIRECTION = PROP_EXPONENT,
PROP_STAGE_SUN_MODEL_ENABLED = PROP_IS_SPOTLIGHT,
PROP_STAGE_LATITUDE = PROP_DIFFUSE_COLOR,
PROP_STAGE_LONGITUDE = PROP_AMBIENT_COLOR_UNUSED,
PROP_STAGE_ALTITUDE = PROP_SPECULAR_COLOR_UNUSED,
PROP_STAGE_DAY = PROP_LINEAR_ATTENUATION_UNUSED,
PROP_STAGE_HOUR = PROP_QUADRATIC_ATTENUATION_UNUSED,
PROP_STAGE_AUTOMATIC_HOURDAY = PROP_ANIMATION_FRAME_INDEX,
PROP_BACKGROUND_MODE = PROP_MODEL_URL,
PROP_SKYBOX_COLOR = PROP_ANIMATION_URL,
PROP_SKYBOX_URL = PROP_ANIMATION_FPS,
PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_PLAYING,
PROP_AMBIENT_LIGHT_INTENSITY = PROP_CUTOFF,
PROP_AMBIENT_LIGHT_URL = PROP_ANIMATION_PLAYING,
// Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for
// other properties which will never overlap with each other.
PROP_SOURCE_URL = PROP_MODEL_URL,

View file

@ -2281,6 +2281,31 @@ bool EntityTree::readFromMap(QVariantMap& map) {
properties.setOwningAvatarID(myNodeID);
}
// TEMPORARY fix for older content not containing these fields in the zones
if (properties.getType() == EntityTypes::EntityType::Zone) {
if (!entityMap.contains("keyLightMode")) {
properties.setKeyLightMode(COMPONENT_MODE_ENABLED);
}
if (!entityMap.contains("skyboxMode")) {
if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "nothing")) {
properties.setSkyboxMode(COMPONENT_MODE_INHERIT);
} else {
// Either the background mode field is missing (shouldn't happen) or the background mode is "skybox"
properties.setSkyboxMode(COMPONENT_MODE_ENABLED);
// Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour
if (properties.getAmbientLight().getAmbientURL() == "") {
properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL());
}
}
}
if (!entityMap.contains("ambientLightMode")) {
properties.setAmbientLightMode(COMPONENT_MODE_ENABLED);
}
}
EntityItemPointer entity = addEntity(entityItemID, properties);
if (!entity) {
qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType();

View file

@ -17,54 +17,41 @@
#include "EntityItemProperties.h"
#include "EntityItemPropertiesMacros.h"
const xColor KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 };
const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f;
const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f;
const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f };
void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties,
QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLight, keyLight, AmbientIntensity, ambientIntensity);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_AMBIENT_URL, KeyLight, keyLight, AmbientURL, ambientURL);
}
void KeyLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, xColor, setColor);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, ambientIntensity, float, setAmbientIntensity);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, glmVec3, setDirection);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, ambientURL, QString, setAmbientURL);
// legacy property support
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, xColor, setColor, getColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightAmbientIntensity, float, setAmbientIntensity, getAmbientIntensity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, glmVec3, setDirection, getDirection);
}
void KeyLightPropertyGroup::merge(const KeyLightPropertyGroup& other) {
COPY_PROPERTY_IF_CHANGED(color);
COPY_PROPERTY_IF_CHANGED(intensity);
COPY_PROPERTY_IF_CHANGED(ambientIntensity);
COPY_PROPERTY_IF_CHANGED(direction);
COPY_PROPERTY_IF_CHANGED(ambientURL);
}
void KeyLightPropertyGroup::debugDump() const {
qCDebug(entities) << " KeyLightPropertyGroup: ---------------------------------------------";
qCDebug(entities) << " color:" << getColor(); // << "," << getColor()[1] << "," << getColor()[2];
qCDebug(entities) << " intensity:" << getIntensity();
qCDebug(entities) << " direction:" << getDirection();
qCDebug(entities) << " ambientIntensity:" << getAmbientIntensity();
qCDebug(entities) << " ambientURL:" << getAmbientURL();
}
void KeyLightPropertyGroup::listChangedProperties(QList<QString>& out) {
@ -77,52 +64,38 @@ void KeyLightPropertyGroup::listChangedProperties(QList<QString>& out) {
if (directionChanged()) {
out << "keyLight-direction";
}
if (ambientIntensityChanged()) {
out << "keyLight-ambientIntensity";
}
if (ambientURLChanged()) {
out << "keyLight-ambientURL";
}
}
bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, getAmbientIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, getAmbientURL());
return true;
}
bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) {
bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt,
int& processedBytes) {
int bytesRead = 0;
bool overwriteLocalData = true;
bool somethingChanged = false;
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setAmbientIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, QString, setAmbientURL);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_COLOR, Color);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_INTENSITY, Intensity);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_AMBIENT_INTENSITY, AmbientIntensity);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_DIRECTION, Direction);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL);
processedBytes += bytesRead;
@ -134,9 +107,7 @@ bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFl
void KeyLightPropertyGroup::markAllChanged() {
_colorChanged = true;
_intensityChanged = true;
_ambientIntensityChanged = true;
_directionChanged = true;
_ambientURLChanged = true;
}
EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const {
@ -144,20 +115,15 @@ EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, color);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, intensity);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_INTENSITY, ambientIntensity);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, direction);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_URL, ambientURL);
return changedProperties;
}
void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) const {
void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) const {
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Color, getColor);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Intensity, getIntensity);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, AmbientIntensity, getAmbientIntensity);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Direction, getDirection);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, AmbientURL, getAmbientURL);
}
bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties) {
@ -165,9 +131,7 @@ bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Color, color, setColor);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Intensity, intensity, setIntensity);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, AmbientIntensity, ambientIntensity, setAmbientIntensity);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Direction, direction, setDirection);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, AmbientURL, ambientURL, setAmbientURL);
return somethingChanged;
}
@ -177,9 +141,7 @@ EntityPropertyFlags KeyLightPropertyGroup::getEntityProperties(EncodeBitstreamPa
requestedProperties += PROP_KEYLIGHT_COLOR;
requestedProperties += PROP_KEYLIGHT_INTENSITY;
requestedProperties += PROP_KEYLIGHT_AMBIENT_INTENSITY;
requestedProperties += PROP_KEYLIGHT_DIRECTION;
requestedProperties += PROP_KEYLIGHT_AMBIENT_URL;
return requestedProperties;
}
@ -196,9 +158,7 @@ void KeyLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, Enc
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, getAmbientIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, getAmbientURL());
}
int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
@ -211,9 +171,7 @@ int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char*
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setAmbientIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, QString, setAmbientURL);
return bytesRead;
}

View file

@ -81,9 +81,7 @@ public:
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR);
DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY);
DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_KEYLIGHT_AMBIENT_INTENSITY);
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION);
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL, ambientURL, QString, "");
};
#endif // hifi_KeyLightPropertyGroup_h

View file

@ -1,283 +0,0 @@
//
// StagePropertyGroup.cpp
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright 2013 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 <QDateTime>
#include <QDate>
#include <QTime>
#include <OctreePacketData.h>
#include "StagePropertyGroup.h"
#include "EntityItemProperties.h"
#include "EntityItemPropertiesMacros.h"
const bool StagePropertyGroup::DEFAULT_STAGE_SUN_MODEL_ENABLED = false;
const float StagePropertyGroup::DEFAULT_STAGE_LATITUDE = 37.777f;
const float StagePropertyGroup::DEFAULT_STAGE_LONGITUDE = 122.407f;
const float StagePropertyGroup::DEFAULT_STAGE_ALTITUDE = 0.03f;
const quint16 StagePropertyGroup::DEFAULT_STAGE_DAY = 60;
const float StagePropertyGroup::DEFAULT_STAGE_HOUR = 12.0f;
void StagePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_SUN_MODEL_ENABLED, Stage, stage, SunModelEnabled, sunModelEnabled);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_LATITUDE, Stage, stage, Latitude, latitude);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_LONGITUDE, Stage, stage, Longitude, longitude);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_ALTITUDE, Stage, stage, Altitude, altitude);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_DAY, Stage, stage, Day, day);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_HOUR, Stage, stage, Hour, hour);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_AUTOMATIC_HOURDAY, Stage, stage, AutomaticHourDay, automaticHourDay);
}
void StagePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
// Backward compatibility support for the old way of doing stage properties
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageSunModelEnabled, bool, setSunModelEnabled, getSunModelEnabled);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageLatitude, float, setLatitude, getLatitude);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageLongitude, float, setLongitude, getLongitude);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageAltitude, float, setAltitude, getAltitude);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageDay, uint16_t, setDay, getDay);
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageHour, float, setHour, getHour);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, sunModelEnabled, bool, setSunModelEnabled);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, latitude, float, setLatitude);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, longitude, float, setLongitude);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, altitude, float, setAltitude);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, day, uint16_t, setDay);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, hour, float, setHour);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, automaticHourDay, bool, setAutomaticHourDay);
}
void StagePropertyGroup::merge(const StagePropertyGroup& other) {
COPY_PROPERTY_IF_CHANGED(sunModelEnabled);
COPY_PROPERTY_IF_CHANGED(latitude);
COPY_PROPERTY_IF_CHANGED(longitude);
COPY_PROPERTY_IF_CHANGED(altitude);
COPY_PROPERTY_IF_CHANGED(day);
COPY_PROPERTY_IF_CHANGED(hour);
COPY_PROPERTY_IF_CHANGED(automaticHourDay);
}
void StagePropertyGroup::debugDump() const {
qCDebug(entities) << " StagePropertyGroup: ---------------------------------------------";
qCDebug(entities) << " _sunModelEnabled:" << _sunModelEnabled;
qCDebug(entities) << " _latitude:" << _latitude;
qCDebug(entities) << " _longitude:" << _longitude;
qCDebug(entities) << " _altitude:" << _altitude;
qCDebug(entities) << " _day:" << _day;
qCDebug(entities) << " _hour:" << _hour;
qCDebug(entities) << " _automaticHourDay:" << _automaticHourDay;
}
void StagePropertyGroup::listChangedProperties(QList<QString>& out) {
if (sunModelEnabledChanged()) {
out << "stage-sunModelEnabled";
}
if (latitudeChanged()) {
out << "stage-latitude";
}
if (altitudeChanged()) {
out << "stage-altitude";
}
if (dayChanged()) {
out << "stage-day";
}
if (hourChanged()) {
out << "stage-hour";
}
if (automaticHourDayChanged()) {
out << "stage-automaticHourDay";
}
}
bool StagePropertyGroup::appendToEditPacket(OctreePacketData* packetData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, getSunModelEnabled());
APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, getLatitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, getLongitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, getAltitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, getDay());
APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, getHour());
APPEND_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, getAutomaticHourDay());
return true;
}
bool StagePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) {
int bytesRead = 0;
bool overwriteLocalData = true;
bool somethingChanged = false;
READ_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, bool, setSunModelEnabled);
READ_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, float, setLatitude);
READ_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, float, setLongitude);
READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, setAltitude);
READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, setDay);
READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, setHour);
READ_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, bool, setAutomaticHourDay);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_SUN_MODEL_ENABLED, SunModelEnabled);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_LATITUDE, Latitude);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_LONGITUDE, Longitude);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_ALTITUDE, Altitude);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_DAY, Day);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_HOUR, Hour);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_AUTOMATIC_HOURDAY, AutomaticHourDay);
processedBytes += bytesRead;
Q_UNUSED(somethingChanged);
return true;
}
void StagePropertyGroup::markAllChanged() {
_sunModelEnabledChanged = true;
_latitudeChanged = true;
_longitudeChanged = true;
_altitudeChanged = true;
_dayChanged = true;
_hourChanged = true;
_automaticHourDayChanged = true;
}
EntityPropertyFlags StagePropertyGroup::getChangedProperties() const {
EntityPropertyFlags changedProperties;
CHECK_PROPERTY_CHANGE(PROP_STAGE_SUN_MODEL_ENABLED, sunModelEnabled);
CHECK_PROPERTY_CHANGE(PROP_STAGE_LATITUDE, latitude);
CHECK_PROPERTY_CHANGE(PROP_STAGE_LONGITUDE, longitude);
CHECK_PROPERTY_CHANGE(PROP_STAGE_ALTITUDE, altitude);
CHECK_PROPERTY_CHANGE(PROP_STAGE_DAY, day);
CHECK_PROPERTY_CHANGE(PROP_STAGE_HOUR, hour);
CHECK_PROPERTY_CHANGE(PROP_STAGE_AUTOMATIC_HOURDAY, automaticHourDay);
return changedProperties;
}
void StagePropertyGroup::getProperties(EntityItemProperties& properties) const {
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, SunModelEnabled, getSunModelEnabled);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Latitude, getLatitude);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Longitude, getLongitude);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Altitude, getAltitude);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Day, getDay);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Hour, getHour);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, AutomaticHourDay, getAutomaticHourDay);
}
bool StagePropertyGroup::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = false;
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, SunModelEnabled, sunModelEnabled, setSunModelEnabled);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Latitude, latitude, setLatitude);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Longitude, longitude, setLongitude);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Altitude, altitude, setAltitude);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Day, day, setDay);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Hour, hour, setHour);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, AutomaticHourDay, automaticHourDay, setAutomaticHourDay);
return somethingChanged;
}
EntityPropertyFlags StagePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties;
requestedProperties += PROP_STAGE_SUN_MODEL_ENABLED;
requestedProperties += PROP_STAGE_LATITUDE;
requestedProperties += PROP_STAGE_LONGITUDE;
requestedProperties += PROP_STAGE_ALTITUDE;
requestedProperties += PROP_STAGE_DAY;
requestedProperties += PROP_STAGE_HOUR;
requestedProperties += PROP_STAGE_AUTOMATIC_HOURDAY;
return requestedProperties;
}
void StagePropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, getSunModelEnabled());
APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, getLatitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, getLongitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, getAltitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, getDay());
APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, getHour());
APPEND_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, getAutomaticHourDay());
}
int StagePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) {
int bytesRead = 0;
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, bool, setSunModelEnabled);
READ_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, float, setLatitude);
READ_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, float, setLongitude);
READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, setAltitude);
READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, setDay);
READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, setHour);
READ_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, bool, setAutomaticHourDay);
return bytesRead;
}
static const float TOTAL_LONGITUDES = 360.0f;
static const float HOURS_PER_DAY = 24;
static const float SECONDS_PER_DAY = 60 * 60 * HOURS_PER_DAY;
static const float MSECS_PER_DAY = SECONDS_PER_DAY * MSECS_PER_SECOND;
float StagePropertyGroup::calculateHour() const {
if (!_automaticHourDay) {
return _hour;
}
QDateTime utc(QDateTime::currentDateTimeUtc());
float adjustFromUTC = (_longitude / TOTAL_LONGITUDES);
float offsetFromUTCinMsecs = adjustFromUTC * MSECS_PER_DAY;
int msecsSinceStartOfDay = utc.time().msecsSinceStartOfDay();
float calutatedHour = ((msecsSinceStartOfDay + offsetFromUTCinMsecs) / MSECS_PER_DAY) * HOURS_PER_DAY;
// calculate hour based on longitude and time from GMT
return calutatedHour;
}
uint16_t StagePropertyGroup::calculateDay() const {
if (!_automaticHourDay) {
return _day;
}
QDateTime utc(QDateTime::currentDateTimeUtc());
int calutatedDay = utc.date().dayOfYear();
// calculate day based on longitude and time from GMT
return calutatedDay;
}

View file

@ -42,8 +42,6 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(en
_shapeType = DEFAULT_SHAPE_TYPE;
_compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL;
_backgroundMode = BACKGROUND_MODE_INHERIT;
}
EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
@ -53,12 +51,13 @@ EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredPr
withReadLock([&] {
_keyLightProperties.getProperties(properties);
});
_stageProperties.getProperties(properties);
withReadLock([&] {
_ambientLightProperties.getProperties(properties);
});
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundMode, getBackgroundMode);
// Contains a QString property, must be synchronized
withReadLock([&] {
@ -72,6 +71,10 @@ EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredPr
COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode);
_hazeProperties.getProperties(properties);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode);
return properties;
}
@ -100,12 +103,12 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
withWriteLock([&] {
_keyLightPropertiesChanged = _keyLightProperties.setProperties(properties);
});
_stagePropertiesChanged = _stageProperties.setProperties(properties);
withWriteLock([&] {
_ambientLightPropertiesChanged = _ambientLightProperties.setProperties(properties);
});
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundMode, setBackgroundMode);
// Contains a QString property, must be synchronized
withWriteLock([&] {
@ -115,11 +118,16 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode);
_hazePropertiesChanged = _hazeProperties.setProperties(properties);
somethingChanged = somethingChanged || _keyLightPropertiesChanged || _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged;
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode);
somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged ||
_stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged;
return somethingChanged;
}
@ -141,15 +149,18 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
bytesRead += bytesFromKeylight;
dataAt += bytesFromKeylight;
int bytesFromStage = _stageProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, _stagePropertiesChanged);
somethingChanged = somethingChanged || _stagePropertiesChanged;
bytesRead += bytesFromStage;
dataAt += bytesFromStage;
int bytesFromAmbientlight;
withWriteLock([&] {
bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged);
});
somethingChanged = somethingChanged || _ambientLightPropertiesChanged;
bytesRead += bytesFromAmbientlight;
dataAt += bytesFromAmbientlight;
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
READ_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);
int bytesFromSkybox;
withWriteLock([&] {
@ -173,6 +184,10 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
bytesRead += bytesFromHaze;
dataAt += bytesFromHaze;
READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode);
READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode);
READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode);
return bytesRead;
}
@ -185,11 +200,12 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p
requestedProperties += _keyLightProperties.getEntityProperties(params);
});
requestedProperties += _stageProperties.getEntityProperties(params);
withReadLock([&] {
requestedProperties += _ambientLightProperties.getEntityProperties(params);
});
requestedProperties += PROP_SHAPE_TYPE;
requestedProperties += PROP_COMPOUND_SHAPE_URL;
requestedProperties += PROP_BACKGROUND_MODE;
withReadLock([&] {
requestedProperties += _skyboxProperties.getEntityProperties(params);
@ -202,6 +218,10 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p
requestedProperties += PROP_HAZE_MODE;
requestedProperties += _hazeProperties.getEntityProperties(params);
requestedProperties += PROP_KEY_LIGHT_MODE;
requestedProperties += PROP_AMBIENT_LIGHT_MODE;
requestedProperties += PROP_SKYBOX_MODE;
return requestedProperties;
}
@ -216,15 +236,13 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
bool successPropertyFits = true;
_keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties,
propertyFlags, propertiesDidntFit, propertyCount, appendState);
_stageProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties,
propertyFlags, propertiesDidntFit, propertyCount, appendState);
propertyFlags, propertiesDidntFit, propertyCount, appendState);
_ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties,
propertyFlags, propertiesDidntFit, propertyCount, appendState);
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType());
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL());
APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, (uint32_t)getBackgroundMode()); // could this be a uint16??
_skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties,
propertyFlags, propertiesDidntFit, propertyCount, appendState);
@ -236,6 +254,10 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode());
_hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties,
propertyFlags, propertiesDidntFit, propertyCount, appendState);
APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode());
APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode());
APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode());
}
void ZoneEntityItem::debugDump() const {
@ -244,13 +266,15 @@ void ZoneEntityItem::debugDump() const {
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
qCDebug(entities) << " _backgroundMode:" << EntityItemProperties::getBackgroundModeString(_backgroundMode);
qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getHazeModeString(_hazeMode);
qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode);
qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode);
qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode);
qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode);
_keyLightProperties.debugDump();
_ambientLightProperties.debugDump();
_skyboxProperties.debugDump();
_hazeProperties.debugDump();
_stageProperties.debugDump();
}
ShapeType ZoneEntityItem::getShapeType() const {
@ -313,7 +337,7 @@ QString ZoneEntityItem::getCompoundShapeURL() const {
void ZoneEntityItem::resetRenderingPropertiesChanged() {
withWriteLock([&] {
_keyLightPropertiesChanged = false;
_backgroundPropertiesChanged = false;
_ambientLightPropertiesChanged = false;
_skyboxPropertiesChanged = false;
_hazePropertiesChanged = false;
_stagePropertiesChanged = false;
@ -321,7 +345,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() {
}
void ZoneEntityItem::setHazeMode(const uint32_t value) {
if (value < COMPONENT_MODE_ITEM_COUNT) {
if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) {
_hazeMode = value;
_hazePropertiesChanged = true;
}
@ -330,3 +354,36 @@ void ZoneEntityItem::setHazeMode(const uint32_t value) {
uint32_t ZoneEntityItem::getHazeMode() const {
return _hazeMode;
}
void ZoneEntityItem::setKeyLightMode(const uint32_t value) {
if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) {
_keyLightMode = value;
_keyLightPropertiesChanged = true;
}
}
uint32_t ZoneEntityItem::getKeyLightMode() const {
return _keyLightMode;
}
void ZoneEntityItem::setAmbientLightMode(const uint32_t value) {
if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) {
_ambientLightMode = value;
_ambientLightPropertiesChanged = true;
}
}
uint32_t ZoneEntityItem::getAmbientLightMode() const {
return _ambientLightMode;
}
void ZoneEntityItem::setSkyboxMode(const uint32_t value) {
if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) {
_skyboxMode = value;
_skyboxPropertiesChanged = true;
}
}
uint32_t ZoneEntityItem::getSkyboxMode() const {
return _skyboxMode;
}

View file

@ -13,11 +13,11 @@
#define hifi_ZoneEntityItem_h
#include "KeyLightPropertyGroup.h"
#include "AmbientLightPropertyGroup.h"
#include "EntityItem.h"
#include "EntityTree.h"
#include "SkyboxPropertyGroup.h"
#include "HazePropertyGroup.h"
#include "StagePropertyGroup.h"
#include <ComponentMode.h>
class ZoneEntityItem : public EntityItem {
@ -66,19 +66,24 @@ public:
virtual void setCompoundShapeURL(const QString& url);
KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock<KeyLightPropertyGroup>([&] { return _keyLightProperties; }); }
void setBackgroundMode(BackgroundMode value) { _backgroundMode = value; _backgroundPropertiesChanged = true; }
BackgroundMode getBackgroundMode() const { return _backgroundMode; }
AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock<AmbientLightPropertyGroup>([&] { return _ambientLightProperties; }); }
void setHazeMode(const uint32_t value);
uint32_t getHazeMode() const;
void setKeyLightMode(uint32_t value);
uint32_t getKeyLightMode() const;
void setAmbientLightMode(uint32_t value);
uint32_t getAmbientLightMode() const;
void setSkyboxMode(uint32_t value);
uint32_t getSkyboxMode() const;
SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock<SkyboxPropertyGroup>([&] { return _skyboxProperties; }); }
const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; }
const StagePropertyGroup& getStageProperties() const { return _stageProperties; }
bool getFlyingAllowed() const { return _flyingAllowed; }
void setFlyingAllowed(bool value) { _flyingAllowed = value; }
bool getGhostingAllowed() const { return _ghostingAllowed; }
@ -87,7 +92,7 @@ public:
void setFilterURL(const QString url);
bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; }
bool backgroundPropertiesChanged() const { return _backgroundPropertiesChanged; }
bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; }
bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; }
bool hazePropertiesChanged() const {
@ -112,21 +117,22 @@ public:
static const bool DEFAULT_GHOSTING_ALLOWED;
static const QString DEFAULT_FILTER_URL;
static const uint32_t DEFAULT_HAZE_MODE{ (uint32_t)COMPONENT_MODE_INHERIT };
protected:
KeyLightPropertyGroup _keyLightProperties;
AmbientLightPropertyGroup _ambientLightProperties;
ShapeType _shapeType = DEFAULT_SHAPE_TYPE;
QString _compoundShapeURL;
BackgroundMode _backgroundMode = BACKGROUND_MODE_INHERIT;
// The following 3 values are the defaults for zone creation
uint32_t _keyLightMode { COMPONENT_MODE_INHERIT };
uint32_t _skyboxMode { COMPONENT_MODE_INHERIT };
uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT };
uint32_t _hazeMode{ DEFAULT_HAZE_MODE };
uint32_t _hazeMode { COMPONENT_MODE_INHERIT };
SkyboxPropertyGroup _skyboxProperties;
HazePropertyGroup _hazeProperties;
StagePropertyGroup _stageProperties;
bool _flyingAllowed { DEFAULT_FLYING_ALLOWED };
bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED };
@ -134,7 +140,7 @@ protected:
// Dirty flags turn true when either keylight properties is changing values.
bool _keyLightPropertiesChanged { false };
bool _backgroundPropertiesChanged{ false };
bool _ambientLightPropertiesChanged { false };
bool _skyboxPropertiesChanged { false };
bool _hazePropertiesChanged{ false };
bool _stagePropertiesChanged { false };

View file

@ -60,6 +60,7 @@ public:
QVector<int> indices;
QVector<glm::vec3> vertices;
QVector<glm::vec3> normals;
QVector<glm::vec3> tangents;
};
struct FBXJointShapeInfo {

View file

@ -303,17 +303,97 @@ FBXBlendshape extractBlendshape(const FBXNode& object) {
return blendshape;
}
using IndexAccessor = std::function<glm::vec3*(const FBXMesh&, int, int, glm::vec3*, glm::vec3&)>;
void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) {
const glm::vec3& normal = mesh.normals.at(firstIndex);
glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex));
if (glm::length(bitangent) < EPSILON) {
return;
static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents) {
glm::vec3 vertex[2];
glm::vec3 normal;
glm::vec3* tangent = vertexAccessor(mesh, firstIndex, secondIndex, vertex, normal);
if (tangent) {
glm::vec3 bitangent = glm::cross(normal, vertex[1] - vertex[0]);
if (glm::length(bitangent) < EPSILON) {
return;
}
glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex);
glm::vec3 normalizedNormal = glm::normalize(normal);
*tangent += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) *
glm::normalize(bitangent), normalizedNormal);
}
glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex);
glm::vec3 normalizedNormal = glm::normalize(normal);
mesh.tangents[firstIndex] += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) *
glm::normalize(bitangent), normalizedNormal);
}
static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents,
IndexAccessor accessor) {
// if we have a normal map (and texture coordinates), we must compute tangents
if (generateFromTexCoords && !mesh.texCoords.isEmpty()) {
tangents.resize(vertices.size());
foreach(const FBXMeshPart& part, mesh.parts) {
for (int i = 0; i < part.quadIndices.size(); i += 4) {
setTangents(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1), vertices, normals, tangents);
setTangents(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2), vertices, normals, tangents);
setTangents(mesh, accessor, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3), vertices, normals, tangents);
setTangents(mesh, accessor, part.quadIndices.at(i + 3), part.quadIndices.at(i), vertices, normals, tangents);
}
// <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0
// This is most likely evidence of a further problem in extractMesh()
for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) {
setTangents(mesh, accessor, part.triangleIndices.at(i), part.triangleIndices.at(i + 1), vertices, normals, tangents);
setTangents(mesh, accessor, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2), vertices, normals, tangents);
setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents);
}
if ((part.triangleIndices.size() % 3) != 0) {
qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three ";
}
}
}
}
static void createMeshTangents(FBXMesh& mesh, bool generateFromTexCoords) {
// This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't
// const in the lambda function.
auto& tangents = mesh.tangents;
createTangents(mesh, generateFromTexCoords, mesh.vertices, mesh.normals, mesh.tangents,
[&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) {
outVertices[0] = mesh.vertices[firstIndex];
outVertices[1] = mesh.vertices[secondIndex];
outNormal = mesh.normals[firstIndex];
return &(tangents[firstIndex]);
});
}
static void createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape) {
// Create lookup to get index in blend shape from vertex index in mesh
std::vector<int> reverseIndices;
reverseIndices.resize(mesh.vertices.size());
std::iota(reverseIndices.begin(), reverseIndices.end(), 0);
for (int indexInBlendShape = 0; indexInBlendShape < blendShape.indices.size(); ++indexInBlendShape) {
auto indexInMesh = blendShape.indices[indexInBlendShape];
reverseIndices[indexInMesh] = indexInBlendShape;
}
createTangents(mesh, generateFromTexCoords, blendShape.vertices, blendShape.normals, blendShape.tangents,
[&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) {
const auto index1 = reverseIndices[firstIndex];
const auto index2 = reverseIndices[secondIndex];
if (index1 < blendShape.vertices.size()) {
outVertices[0] = blendShape.vertices[index1];
if (index2 < blendShape.vertices.size()) {
outVertices[1] = blendShape.vertices[index2];
} else {
// Index isn't in the blend shape so return vertex from mesh
outVertices[1] = mesh.vertices[secondIndex];
}
outNormal = blendShape.normals[index1];
return &blendShape.tangents[index1];
} else {
// Index isn't in blend shape so return nullptr
return (glm::vec3*)nullptr;
}
});
}
QVector<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) {
@ -1570,27 +1650,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
}
}
// if we have a normal map (and texture coordinates), we must compute tangents
if (generateTangents && !extracted.mesh.texCoords.isEmpty()) {
extracted.mesh.tangents.resize(extracted.mesh.vertices.size());
foreach (const FBXMeshPart& part, extracted.mesh.parts) {
for (int i = 0; i < part.quadIndices.size(); i += 4) {
setTangents(extracted.mesh, part.quadIndices.at(i), part.quadIndices.at(i + 1));
setTangents(extracted.mesh, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2));
setTangents(extracted.mesh, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3));
setTangents(extracted.mesh, part.quadIndices.at(i + 3), part.quadIndices.at(i));
}
// <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0
// This is most likely evidence of a further problem in extractMesh()
for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) {
setTangents(extracted.mesh, part.triangleIndices.at(i), part.triangleIndices.at(i + 1));
setTangents(extracted.mesh, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2));
setTangents(extracted.mesh, part.triangleIndices.at(i + 2), part.triangleIndices.at(i));
}
if ((part.triangleIndices.size() % 3) != 0){
qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three ";
}
}
createMeshTangents(extracted.mesh, generateTangents);
for (auto& blendShape : extracted.mesh.blendshapes) {
createBlendShapeTangents(extracted.mesh, generateTangents, blendShape);
}
// find the clusters with which the mesh is associated

View file

@ -33,6 +33,15 @@
class QIODevice;
class FBXNode;
#define FBX_PACK_NORMALS 1
#if FBX_PACK_NORMALS
using NormalType = glm::uint32;
#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2
#else
using NormalType = glm::vec3;
#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ
#endif
/// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing
@ -114,6 +123,8 @@ public:
QHash<QString, ExtractedMesh> meshes;
static void buildModelMesh(FBXMesh& extractedMesh, const QString& url);
static glm::vec3 normalizeDirForPacking(const glm::vec3& dir);
FBXTexture getTexture(const QString& textureID);
QHash<QString, QString> _textureNames;

View file

@ -37,6 +37,20 @@
#include <memory>
#include <glm/detail/type_half.hpp>
#include <glm/gtc/packing.hpp>
using vec2h = glm::tvec2<glm::detail::hdata>;
#define FBX_PACK_COLORS 1
#if FBX_PACK_COLORS
using ColorType = glm::uint32;
#define FBX_COLOR_ELEMENT gpu::Element::COLOR_RGBA_32
#else
using ColorType = glm::vec3;
#define FBX_COLOR_ELEMENT gpu::Element::VEC3F_XYZ
#endif
class Vertex {
public:
@ -225,7 +239,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "Colors") {
data.colors = createVec4VectorRGBA(getDoubleVector(subdata), data.averageColor);
} else if (subdata.name == "ColorsIndex") {
} else if (subdata.name == "ColorsIndex" || subdata.name == "ColorIndex") {
data.colorIndices = getIntVector(subdata);
} else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) {
@ -543,6 +557,14 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
return data.extracted;
}
glm::vec3 FBXReader::normalizeDirForPacking(const glm::vec3& dir) {
auto maxCoord = glm::max(fabsf(dir.x), glm::max(fabsf(dir.y), fabsf(dir.z)));
if (maxCoord > 1e-6f) {
return dir / maxCoord;
}
return dir;
}
void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*");
@ -571,37 +593,116 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
gpu::BufferView vbv(vb, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
mesh->setVertexBuffer(vbv);
if (!fbxMesh.normals.empty() && fbxMesh.tangents.empty()) {
// Fill with a dummy value to force tangents to be present if there are normals
fbxMesh.tangents.reserve(fbxMesh.normals.size());
std::fill_n(std::back_inserter(fbxMesh.tangents), fbxMesh.normals.size(), Vectors::UNIT_X);
}
// Same thing with blend shapes
for (auto& blendShape : fbxMesh.blendshapes) {
if (!blendShape.normals.empty() && blendShape.tangents.empty()) {
// Fill with a dummy value to force tangents to be present if there are normals
blendShape.tangents.reserve(blendShape.normals.size());
std::fill_n(std::back_inserter(fbxMesh.tangents), blendShape.normals.size(), Vectors::UNIT_X);
}
}
// evaluate all attribute channels sizes
int normalsSize = fbxMesh.normals.size() * sizeof(glm::vec3);
int tangentsSize = fbxMesh.tangents.size() * sizeof(glm::vec3);
int colorsSize = fbxMesh.colors.size() * sizeof(glm::vec3);
int texCoordsSize = fbxMesh.texCoords.size() * sizeof(glm::vec2);
int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(glm::vec2);
const int normalsSize = fbxMesh.normals.size() * sizeof(NormalType);
const int tangentsSize = fbxMesh.tangents.size() * sizeof(NormalType);
// If there are normals then there should be tangents
assert(normalsSize == tangentsSize);
const auto normalsAndTangentsSize = normalsSize + tangentsSize;
const int normalsAndTangentsStride = 2 * sizeof(NormalType);
const int colorsSize = fbxMesh.colors.size() * sizeof(ColorType);
// Texture coordinates are stored in 2 half floats
const int texCoordsSize = fbxMesh.texCoords.size() * sizeof(vec2h);
const int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(vec2h);
int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(uint8_t);
if (fbxMesh.clusters.size() > UINT8_MAX) {
// we need 16 bits instead of just 8 for clusterIndices
clusterIndicesSize *= 2;
}
int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint16_t);
int normalsOffset = 0;
int tangentsOffset = normalsOffset + normalsSize;
int colorsOffset = tangentsOffset + tangentsSize;
int texCoordsOffset = colorsOffset + colorsSize;
int texCoords1Offset = texCoordsOffset + texCoordsSize;
int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize;
const int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint16_t);
// Normals and tangents are interleaved
const int normalsOffset = 0;
const int tangentsOffset = normalsOffset + sizeof(NormalType);
const int colorsOffset = normalsOffset + normalsSize + tangentsSize;
const int texCoordsOffset = colorsOffset + colorsSize;
const int texCoords1Offset = texCoordsOffset + texCoordsSize;
const int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
const int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize;
// Copy all attribute data in a single attribute buffer
auto attribBuffer = std::make_shared<gpu::Buffer>();
attribBuffer->resize(totalAttributeSize);
attribBuffer->setSubData(normalsOffset, normalsSize, (gpu::Byte*) fbxMesh.normals.constData());
attribBuffer->setSubData(tangentsOffset, tangentsSize, (gpu::Byte*) fbxMesh.tangents.constData());
attribBuffer->setSubData(colorsOffset, colorsSize, (gpu::Byte*) fbxMesh.colors.constData());
attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (gpu::Byte*) fbxMesh.texCoords.constData());
attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (gpu::Byte*) fbxMesh.texCoords1.constData());
// Interleave normals and tangents
if (normalsSize > 0) {
std::vector<NormalType> normalsAndTangents;
normalsAndTangents.reserve(fbxMesh.normals.size() + fbxMesh.tangents.size());
for (auto normalIt = fbxMesh.normals.constBegin(), tangentIt = fbxMesh.tangents.constBegin();
normalIt != fbxMesh.normals.constEnd();
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
const auto normal = normalizeDirForPacking(*normalIt);
const auto tangent = normalizeDirForPacking(*tangentIt);
const auto packedNormal = glm::packSnorm3x10_1x2(glm::vec4(normal, 0.0f));
const auto packedTangent = glm::packSnorm3x10_1x2(glm::vec4(tangent, 0.0f));
#else
const auto packedNormal = *normalIt;
const auto packedTangent = *tangentIt;
#endif
normalsAndTangents.push_back(packedNormal);
normalsAndTangents.push_back(packedTangent);
}
attribBuffer->setSubData(normalsOffset, normalsAndTangentsSize, (const gpu::Byte*) normalsAndTangents.data());
}
if (colorsSize > 0) {
#if FBX_PACK_COLORS
std::vector<ColorType> colors;
colors.reserve(fbxMesh.colors.size());
for (const auto& color : fbxMesh.colors) {
colors.push_back(glm::packUnorm4x8(glm::vec4(color, 1.0f)));
}
attribBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) colors.data());
#else
attribBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) fbxMesh.colors.constData());
#endif
}
if (texCoordsSize > 0) {
QVector<vec2h> texCoordData;
texCoordData.reserve(fbxMesh.texCoords.size());
for (auto& texCoordVec2f : fbxMesh.texCoords) {
vec2h texCoordVec2h;
texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x);
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
texCoordData.push_back(texCoordVec2h);
}
attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (const gpu::Byte*) texCoordData.constData());
}
if (texCoords1Size > 0) {
QVector<vec2h> texCoordData;
texCoordData.reserve(fbxMesh.texCoords1.size());
for (auto& texCoordVec2f : fbxMesh.texCoords1) {
vec2h texCoordVec2h;
texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x);
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
texCoordData.push_back(texCoordVec2h);
}
attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (const gpu::Byte*) texCoordData.constData());
}
if (fbxMesh.clusters.size() < UINT8_MAX) {
// yay! we can fit the clusterIndices within 8-bits
@ -612,40 +713,37 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
assert(fbxMesh.clusterIndices[i] <= UINT8_MAX);
clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]);
}
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) clusterIndices.constData());
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData());
} else {
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) fbxMesh.clusterIndices.constData());
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) fbxMesh.clusterIndices.constData());
}
attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (gpu::Byte*) fbxMesh.clusterWeights.constData());
attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) fbxMesh.clusterWeights.constData());
if (normalsSize) {
mesh->addAttribute(gpu::Stream::NORMAL,
model::BufferView(attribBuffer, normalsOffset, normalsSize,
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)));
}
if (tangentsSize) {
model::BufferView(attribBuffer, normalsOffset, normalsAndTangentsSize,
normalsAndTangentsStride, FBX_NORMAL_ELEMENT));
mesh->addAttribute(gpu::Stream::TANGENT,
model::BufferView(attribBuffer, tangentsOffset, tangentsSize,
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)));
model::BufferView(attribBuffer, tangentsOffset, normalsAndTangentsSize,
normalsAndTangentsStride, FBX_NORMAL_ELEMENT));
}
if (colorsSize) {
mesh->addAttribute(gpu::Stream::COLOR,
model::BufferView(attribBuffer, colorsOffset, colorsSize,
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)));
model::BufferView(attribBuffer, colorsOffset, colorsSize, FBX_COLOR_ELEMENT));
}
if (texCoordsSize) {
mesh->addAttribute(gpu::Stream::TEXCOORD,
model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize,
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)));
model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize,
gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
}
if (texCoords1Size) {
mesh->addAttribute( gpu::Stream::TEXCOORD1,
model::BufferView(attribBuffer, texCoords1Offset, texCoords1Size,
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)));
gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
} else if (texCoordsSize) {
mesh->addAttribute(gpu::Stream::TEXCOORD1,
model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize,
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)));
model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize,
gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
}
if (clusterIndicesSize) {

View file

@ -110,7 +110,8 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
GL_UNSIGNED_SHORT,
GL_BYTE,
GL_UNSIGNED_BYTE,
GL_UNSIGNED_BYTE
GL_UNSIGNED_BYTE,
GL_INT_2_10_10_10_REV,
};
bool checkGLError(const char* name = nullptr);

View file

@ -218,6 +218,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::NINT8:
result = GL_RGBA8_SNORM;
break;
case gpu::NINT2_10_10_10:
case gpu::NUINT32:
case gpu::NINT32:
case gpu::COMPRESSED:
@ -502,6 +503,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
}
case gpu::COMPRESSED:
case gpu::NUINT2:
case gpu::NINT2_10_10_10:
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
@ -553,6 +555,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
}
case gpu::COMPRESSED:
case gpu::NUINT2:
case gpu::NINT2_10_10_10:
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}
@ -671,6 +674,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
break;
case gpu::NUINT32:
case gpu::NINT32:
case gpu::NINT2_10_10_10:
case gpu::COMPRESSED:
case gpu::NUM_TYPES: // quiet compiler
Q_UNREACHABLE();

View file

@ -38,6 +38,7 @@ const Element Element::VEC2F_UV{ VEC2, FLOAT, UV };
const Element Element::VEC2F_XY{ VEC2, FLOAT, XY };
const Element Element::VEC3F_XYZ{ VEC3, FLOAT, XYZ };
const Element Element::VEC4F_XYZW{ VEC4, FLOAT, XYZW };
const Element Element::VEC4F_NORMALIZED_XYZ10W2{ VEC4, NINT2_10_10_10, XYZW };
const Element Element::INDEX_UINT16 { SCALAR, UINT16, INDEX };
const Element Element::INDEX_INT32 { SCALAR, INT32, INDEX };
const Element Element::PART_DRAWCALL{ VEC4, UINT32, PART };

View file

@ -39,6 +39,7 @@ enum Type : uint8_t {
NINT8,
NUINT8,
NUINT2,
NINT2_10_10_10,
COMPRESSED,
@ -65,6 +66,7 @@ static const int TYPE_SIZE[NUM_TYPES] = {
2,
1,
1,
4,
1
};
@ -86,6 +88,7 @@ static const bool TYPE_IS_INTEGER[NUM_TYPES] = {
false,
false,
false,
false,
false,
};
@ -326,6 +329,7 @@ public:
static const Element VEC2F_XY;
static const Element VEC3F_XYZ;
static const Element VEC4F_XYZW;
static const Element VEC4F_NORMALIZED_XYZ10W2;
static const Element INDEX_UINT16;
static const Element INDEX_INT32;
static const Element PART_DRAWCALL;

View file

@ -92,6 +92,15 @@ bool Stream::Format::setAttribute(Slot slot, Slot channel, Frequency frequency)
return true;
}
Stream::Attribute Stream::Format::getAttribute(Slot slot) const {
auto attribIt = _attributes.find(slot);
if (attribIt != _attributes.end()) {
return attribIt->second;
} else {
return Attribute();
}
}
void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) {
_buffers.push_back(buffer);
_offsets.push_back(offset);

View file

@ -112,6 +112,7 @@ public:
bool setAttribute(Slot slot, Slot channel, Frequency frequency = PER_VERTEX);
bool hasAttribute(Slot slot) const { return (_attributes.find(slot) != _attributes.end()); }
Attribute getAttribute(Slot slot) const;
const std::string& getKey() const { return _key; }

View file

@ -3,6 +3,7 @@
// libraries/midi/src
//
// Created by Burt Sloane
// Modified by Bruce Brown
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -14,30 +15,45 @@
#include <QtCore/QLoggingCategory>
#if defined Q_OS_WIN32
#include "Windows.h"
#endif
#if defined Q_OS_WIN32
const int MIDI_BYTE_MASK = 0x0FF;
const int MIDI_NIBBLE_MASK = 0x00F;
const int MIDI_PITCH_BEND_MASK = 0x3F80;
const int MIDI_SHIFT_STATUS = 4;
const int MIDI_SHIFT_NOTE = 8;
const int MIDI_SHIFT_VELOCITY = 16;
const int MIDI_SHIFT_PITCH_BEND = 9;
// Status Decode
const int MIDI_NOTE_OFF = 0x8;
const int MIDI_NOTE_ON = 0x9;
const int MIDI_POLYPHONIC_KEY_PRESSURE = 0xa;
const int MIDI_PROGRAM_CHANGE = 0xc;
const int MIDI_CHANNEL_PRESSURE = 0xd;
const int MIDI_PITCH_BEND_CHANGE = 0xe;
const int MIDI_SYSTEM_MESSAGE = 0xf;
#endif
const int MIDI_STATUS_MASK = 0x0F0;
const int MIDI_NOTE_OFF = 0x080;
const int MIDI_NOTE_ON = 0x090;
const int MIDI_CONTROL_CHANGE = 0x0b0;
const int MIDI_CONTROL_CHANGE = 0xb;
const int MIDI_CHANNEL_MODE_ALL_NOTES_OFF = 0x07b;
static Midi* instance = NULL; // communicate this to non-class callbacks
static Midi* instance = NULL; // communicate this to non-class callbacks
static bool thruModeEnabled = false;
static bool broadcastEnabled = false;
static bool typeNoteOffEnabled = true;
static bool typeNoteOnEnabled = true;
static bool typePolyKeyPressureEnabled = false;
static bool typeControlChangeEnabled = true;
static bool typeProgramChangeEnabled = true;
static bool typeChanPressureEnabled = false;
static bool typePitchBendEnabled = true;
static bool typeSystemMessageEnabled = false;
std::vector<QString> Midi::midiinexclude;
std::vector<QString> Midi::midioutexclude;
std::vector<QString> Midi::midiInExclude;
std::vector<QString> Midi::midiOutExclude;
#if defined Q_OS_WIN32
@ -47,7 +63,6 @@ std::vector<QString> Midi::midioutexclude;
std::vector<HMIDIIN> midihin;
std::vector<HMIDIOUT> midihout;
void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
switch (wMsg) {
case MIM_OPEN:
@ -58,23 +73,64 @@ void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD
if (midihin[i] == hMidiIn) {
midihin[i] = NULL;
instance->allNotesOff();
instance->midiHardwareChange();
}
}
break;
case MIM_DATA: {
int status = MIDI_BYTE_MASK & dwParam1;
int note = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_NOTE);
int vel = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_VELOCITY);
if (thruModeEnabled) {
instance->sendNote(status, note, vel); // relay the note on to all other midi devices
int device = -1;
for (int i = 0; i < midihin.size(); i++) {
if (midihin[i] == hMidiIn) {
device = i;
}
}
instance->noteReceived(status, note, vel); // notify the javascript
int raw = dwParam1;
int channel = (MIDI_NIBBLE_MASK & dwParam1) + 1;
int status = MIDI_BYTE_MASK & dwParam1;
int type = MIDI_NIBBLE_MASK & (dwParam1 >> MIDI_SHIFT_STATUS);
int note = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_NOTE);
int velocity = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_VELOCITY);
int bend = 0;
int program = 0;
if (!typeNoteOffEnabled && type == MIDI_NOTE_OFF) {
return;
}
if (!typeNoteOnEnabled && type == MIDI_NOTE_ON) {
return;
}
if (!typePolyKeyPressureEnabled && type == MIDI_POLYPHONIC_KEY_PRESSURE) {
return;
}
if (!typeControlChangeEnabled && type == MIDI_CONTROL_CHANGE) {
return;
}
if (typeProgramChangeEnabled && type == MIDI_PROGRAM_CHANGE) {
program = note;
note = 0;
}
if (typeChanPressureEnabled && type == MIDI_CHANNEL_PRESSURE) {
velocity = note;
note = 0;
}
if (typePitchBendEnabled && type == MIDI_PITCH_BEND_CHANGE) {
bend = ((MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_NOTE)) |
(MIDI_PITCH_BEND_MASK & (dwParam1 >> MIDI_SHIFT_PITCH_BEND))) - 8192;
channel = 0; // Weird values on different instruments
note = 0;
velocity = 0;
}
if (!typeSystemMessageEnabled && type == MIDI_SYSTEM_MESSAGE) {
return;
}
if (thruModeEnabled) {
instance->sendNote(status, note, velocity); // relay the message on to all other midi devices.
}
instance->midiReceived(device, raw, channel, status, type, note, velocity, bend, program); // notify the javascript
break;
}
}
}
void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
switch (wMsg) {
case MOM_OPEN:
@ -85,21 +141,45 @@ void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_P
if (midihout[i] == hmo) {
midihout[i] = NULL;
instance->allNotesOff();
instance->midiHardwareChange();
}
}
break;
}
}
void Midi::sendNote(int status, int note, int vel) {
for (int i = 0; i < midihout.size(); i++) {
if (midihout[i] != NULL) {
midiOutShortMsg(midihout[i], status + (note << MIDI_SHIFT_NOTE) + (vel << MIDI_SHIFT_VELOCITY));
void Midi::sendRawMessage(int device, int raw) {
if (broadcastEnabled) {
for (int i = 0; i < midihout.size(); i++) {
if (midihout[i] != NULL) {
midiOutShortMsg(midihout[i], raw);
}
}
} else {
midiOutShortMsg(midihout[device], raw);
}
}
void Midi::sendMessage(int device, int channel, int type, int note, int velocity) {
int message = (channel - 1) | (type << MIDI_SHIFT_STATUS);
if (broadcastEnabled) {
for (int i = 0; i < midihout.size(); i++) {
if (midihout[i] != NULL) {
midiOutShortMsg(midihout[i], message | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY));
}
}
} else {
midiOutShortMsg(midihout[device], message | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY));
}
}
void Midi::sendNote(int status, int note, int velocity) {
for (int i = 0; i < midihout.size(); i++) {
if (midihout[i] != NULL) {
midiOutShortMsg(midihout[i], status + (note << MIDI_SHIFT_NOTE) + (velocity << MIDI_SHIFT_VELOCITY));
}
}
}
void Midi::MidiSetup() {
midihin.clear();
@ -110,8 +190,8 @@ void Midi::MidiSetup() {
midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS));
bool found = false;
for (int j = 0; j < midiinexclude.size(); j++) {
if (midiinexclude[j].toStdString().compare(incaps.szPname) == 0) {
for (int j = 0; j < midiInExclude.size(); j++) {
if (midiInExclude[j].toStdString().compare(incaps.szPname) == 0) {
found = true;
break;
}
@ -122,7 +202,6 @@ void Midi::MidiSetup() {
midiInStart(tmphin);
midihin.push_back(tmphin);
}
}
MIDIOUTCAPS outcaps;
@ -130,8 +209,8 @@ void Midi::MidiSetup() {
midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS));
bool found = false;
for (int j = 0; j < midioutexclude.size(); j++) {
if (midioutexclude[j].toStdString().compare(outcaps.szPname) == 0) {
for (int j = 0; j < midiOutExclude.size(); j++) {
if (midiOutExclude[j].toStdString().compare(outcaps.szPname) == 0) {
found = true;
break;
}
@ -164,7 +243,13 @@ void Midi::MidiCleanup() {
midihout.clear();
}
#else
void Midi::sendNote(int status, int note, int vel) {
void Midi::sendRawMessage(int device, int raw) {
}
void Midi::sendNote(int status, int note, int velocity) {
}
void Midi::sendMessage(int device, int channel, int type, int note, int velocity){
}
void Midi::MidiSetup() {
@ -176,26 +261,30 @@ void Midi::MidiCleanup() {
}
#endif
void Midi::noteReceived(int status, int note, int velocity) {
if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) &&
((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON) &&
((status & MIDI_STATUS_MASK) != MIDI_CONTROL_CHANGE)) {
return; // NOTE: only sending note-on, note-off, and control-change to Javascript
}
void Midi::midiReceived(int device, int raw, int channel, int status, int type, int note, int velocity, int bend, int program) {
QVariantMap eventData;
eventData["device"] = device;
eventData["raw"] = raw;
eventData["channel"] = channel;
eventData["status"] = status;
eventData["type"] = type;
eventData["note"] = note;
eventData["velocity"] = velocity;
emit midiNote(eventData);
eventData["bend"] = bend;
eventData["program"] = program;
emit midiNote(eventData);// Legacy
emit midiMessage(eventData);
}
void Midi::midiHardwareChange() {
emit midiReset();
}
//
Midi::Midi() {
instance = this;
#if defined Q_OS_WIN32
midioutexclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing
midiOutExclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing (Lags)
#endif
MidiSetup();
}
@ -203,10 +292,18 @@ Midi::Midi() {
Midi::~Midi() {
}
void Midi::sendRawDword(int device, int raw) {
sendRawMessage(device, raw);
}
void Midi::playMidiNote(int status, int note, int velocity) {
sendNote(status, note, velocity);
}
void Midi::sendMidiMessage(int device, int channel, int type, int note, int velocity) {
sendMessage(device, channel, type, note, velocity);
}
void Midi::allNotesOff() {
sendNote(MIDI_CONTROL_CHANGE, MIDI_CHANNEL_MODE_ALL_NOTES_OFF, 0); // all notes off
}
@ -219,6 +316,7 @@ void Midi::resetDevices() {
void Midi::USBchanged() {
instance->MidiCleanup();
instance->MidiSetup();
instance->midiHardwareChange();
}
//
@ -245,16 +343,16 @@ QStringList Midi::listMidiDevices(bool output) {
void Midi::unblockMidiDevice(QString name, bool output) {
if (output) {
for (unsigned long i = 0; i < midioutexclude.size(); i++) {
if (midioutexclude[i].toStdString().compare(name.toStdString()) == 0) {
midioutexclude.erase(midioutexclude.begin() + i);
for (unsigned long i = 0; i < midiOutExclude.size(); i++) {
if (midiOutExclude[i].toStdString().compare(name.toStdString()) == 0) {
midiOutExclude.erase(midiOutExclude.begin() + i);
break;
}
}
} else {
for (unsigned long i = 0; i < midiinexclude.size(); i++) {
if (midiinexclude[i].toStdString().compare(name.toStdString()) == 0) {
midiinexclude.erase(midiinexclude.begin() + i);
for (unsigned long i = 0; i < midiInExclude.size(); i++) {
if (midiInExclude[i].toStdString().compare(name.toStdString()) == 0) {
midiInExclude.erase(midiInExclude.begin() + i);
break;
}
}
@ -264,9 +362,9 @@ void Midi::unblockMidiDevice(QString name, bool output) {
void Midi::blockMidiDevice(QString name, bool output) {
unblockMidiDevice(name, output); // make sure it's only in there once
if (output) {
midioutexclude.push_back(name);
midiOutExclude.push_back(name);
} else {
midiinexclude.push_back(name);
midiInExclude.push_back(name);
}
}
@ -274,3 +372,38 @@ void Midi::thruModeEnable(bool enable) {
thruModeEnabled = enable;
}
void Midi::broadcastEnable(bool enable) {
broadcastEnabled = enable;
}
void Midi::typeNoteOffEnable(bool enable) {
typeNoteOffEnabled = enable;
}
void Midi::typeNoteOnEnable(bool enable) {
typeNoteOnEnabled = enable;
}
void Midi::typePolyKeyPressureEnable(bool enable) {
typePolyKeyPressureEnabled = enable;
}
void Midi::typeControlChangeEnable(bool enable) {
typeControlChangeEnabled = enable;
}
void Midi::typeProgramChangeEnable(bool enable) {
typeProgramChangeEnabled = enable;
}
void Midi::typeChanPressureEnable(bool enable) {
typeChanPressureEnabled = enable;
}
void Midi::typePitchBendEnable(bool enable) {
typePitchBendEnabled = enable;
}
void Midi::typeSystemMessageEnable(bool enable) {
typeSystemMessageEnabled = enable;
}

View file

@ -3,6 +3,7 @@
// libraries/midi/src
//
// Created by Burt Sloane
// Modified by Bruce Brown
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -24,13 +25,16 @@ class Midi : public QObject, public Dependency {
SINGLETON_DEPENDENCY
public:
void noteReceived(int status, int note, int velocity); // relay a note to Javascript
void sendNote(int status, int note, int vel); // relay a note to MIDI outputs
void midiReceived(int device, int raw, int channel, int status, int type, int note, int velocity, int bend, int program); // relay a note to Javascript
void midiHardwareChange(); // relay hardware change to Javascript
void sendRawMessage(int device, int raw); // relay midi message to MIDI outputs
void sendNote(int status, int note, int velocity); // relay a note to MIDI outputs
void sendMessage(int device, int channel, int type, int note, int velocity); // relay a message to MIDI outputs
static void USBchanged();
private:
static std::vector<QString> midiinexclude;
static std::vector<QString> midioutexclude;
static std::vector<QString> midiInExclude;
static std::vector<QString> midiOutExclude;
private:
void MidiSetup();
@ -38,31 +42,63 @@ private:
signals:
void midiNote(QVariantMap eventData);
void midiMessage(QVariantMap eventData);
void midiReset();
public slots:
/// play a note on all connected devices
/// @param {int} status: 0x80 is noteoff, 0x90 is noteon (if velocity=0, noteoff), etc
/// @param {int} note: midi note number
/// @param {int} velocity: note velocity (0 means noteoff)
Q_INVOKABLE void playMidiNote(int status, int note, int velocity);
public slots:
// Send Raw Midi Packet to all connected devices
Q_INVOKABLE void sendRawDword(int device, int raw);
/// Send Raw Midi message to selected device
/// @param {int} device: device number
/// @param {int} raw: raw midi message (DWORD)
/// turn off all notes on all connected devices
Q_INVOKABLE void allNotesOff();
// Send Midi Message to all connected devices
Q_INVOKABLE void sendMidiMessage(int device, int channel, int type, int note, int velocity);
/// Send midi message to selected device/devices
/// @param {int} device: device number
/// @param {int} channel: channel number
/// @param {int} type: 0x8 is noteoff, 0x9 is noteon (if velocity=0, noteoff), etc
/// @param {int} note: midi note number
/// @param {int} velocity: note velocity (0 means noteoff)
/// clean up and re-discover attached devices
Q_INVOKABLE void resetDevices();
// Send Midi Message to all connected devices
Q_INVOKABLE void playMidiNote(int status, int note, int velocity);
/// play a note on all connected devices
/// @param {int} status: 0x80 is noteoff, 0x90 is noteon (if velocity=0, noteoff), etc
/// @param {int} note: midi note number
/// @param {int} velocity: note velocity (0 means noteoff)
/// ask for a list of inputs/outputs
Q_INVOKABLE QStringList listMidiDevices(bool output);
/// turn off all notes on all connected devices
Q_INVOKABLE void allNotesOff();
/// block an input/output by name
Q_INVOKABLE void blockMidiDevice(QString name, bool output);
/// clean up and re-discover attached devices
Q_INVOKABLE void resetDevices();
/// unblock an input/output by name
Q_INVOKABLE void unblockMidiDevice(QString name, bool output);
/// ask for a list of inputs/outputs
Q_INVOKABLE QStringList listMidiDevices(bool output);
/// block an input/output by name
Q_INVOKABLE void blockMidiDevice(QString name, bool output);
/// unblock an input/output by name
Q_INVOKABLE void unblockMidiDevice(QString name, bool output);
/// repeat all incoming notes to all outputs (default disabled)
Q_INVOKABLE void thruModeEnable(bool enable);
/// broadcast on all unblocked devices
Q_INVOKABLE void broadcastEnable(bool enable);
/// filter by event types
Q_INVOKABLE void typeNoteOffEnable(bool enable);
Q_INVOKABLE void typeNoteOnEnable(bool enable);
Q_INVOKABLE void typePolyKeyPressureEnable(bool enable);
Q_INVOKABLE void typeControlChangeEnable(bool enable);
Q_INVOKABLE void typeProgramChangeEnable(bool enable);
Q_INVOKABLE void typeChanPressureEnable(bool enable);
Q_INVOKABLE void typePitchBendEnable(bool enable);
Q_INVOKABLE void typeSystemMessageEnable(bool enable);
/// repeat all incoming notes to all outputs (default disabled)
Q_INVOKABLE void thruModeEnable(bool enable);
public:
Midi();

View file

@ -11,6 +11,8 @@
#include "Geometry.h"
#include <glm/gtc/packing.hpp>
using namespace model;
Mesh::Mesh() :
@ -72,12 +74,14 @@ void Mesh::evalVertexStream() {
int channelNum = 0;
if (hasVertexData()) {
_vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum));
auto stride = glm::max<gpu::Offset>(_vertexFormat->getChannelStride(channelNum), _vertexBuffer._stride);
_vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, stride);
channelNum++;
}
for (auto attrib : _attributeBuffers) {
BufferView& view = attrib.second;
_vertexStream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum));
auto stride = glm::max<gpu::Offset>(_vertexFormat->getChannelStride(channelNum), view._stride);
_vertexStream.addBuffer(view._buffer, view._offset, stride);
channelNum++;
}
}
@ -137,13 +141,15 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
std::function<glm::vec3(glm::vec3)> colorFunc,
std::function<glm::vec3(glm::vec3)> normalFunc,
std::function<uint32_t(uint32_t)> indexFunc) const {
const auto vertexFormat = getVertexFormat();
// vertex data
const gpu::BufferView& vertexBufferView = getVertexBuffer();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices();
gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3);
unsigned char* resultVertexData = new unsigned char[vertexSize];
unsigned char* vertexDataCursor = resultVertexData;
std::unique_ptr<unsigned char> resultVertexData{ new unsigned char[vertexSize] };
unsigned char* vertexDataCursor = resultVertexData.get();
for (gpu::BufferView::Index i = 0; i < numVertices; i++) {
glm::vec3 pos = vertexFunc(vertexBufferView.get<glm::vec3>(i));
@ -157,13 +163,24 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
gpu::Resource::Size colorSize = numColors * sizeof(glm::vec3);
unsigned char* resultColorData = new unsigned char[colorSize];
unsigned char* colorDataCursor = resultColorData;
std::unique_ptr<unsigned char> resultColorData{ new unsigned char[colorSize] };
unsigned char* colorDataCursor = resultColorData.get();
auto colorAttribute = vertexFormat->getAttribute(attributeTypeColor);
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
glm::vec3 color = colorFunc(colorsBufferView.get<glm::vec3>(i));
memcpy(colorDataCursor, &color, sizeof(color));
colorDataCursor += sizeof(color);
if (colorAttribute._element == gpu::Element::VEC3F_XYZ) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
glm::vec3 color = colorFunc(colorsBufferView.get<glm::vec3>(i));
memcpy(colorDataCursor, &color, sizeof(color));
colorDataCursor += sizeof(color);
}
} else if (colorAttribute._element == gpu::Element::COLOR_RGBA_32) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
auto rawColor = colorsBufferView.get<glm::uint32>(i);
auto color = glm::vec3(glm::unpackUnorm4x8(rawColor));
color = colorFunc(color);
memcpy(colorDataCursor, &color, sizeof(color));
colorDataCursor += sizeof(color);
}
}
// normal data
@ -171,22 +188,34 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
gpu::Resource::Size normalSize = numNormals * sizeof(glm::vec3);
unsigned char* resultNormalData = new unsigned char[normalSize];
unsigned char* normalDataCursor = resultNormalData;
std::unique_ptr<unsigned char> resultNormalData{ new unsigned char[normalSize] };
unsigned char* normalDataCursor = resultNormalData.get();
auto normalAttribute = vertexFormat->getAttribute(attributeTypeNormal);
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
glm::vec3 normal = normalFunc(normalsBufferView.get<glm::vec3>(i));
memcpy(normalDataCursor, &normal, sizeof(normal));
normalDataCursor += sizeof(normal);
if (normalAttribute._element == gpu::Element::VEC3F_XYZ) {
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
glm::vec3 normal = normalFunc(normalsBufferView.get<glm::vec3>(i));
memcpy(normalDataCursor, &normal, sizeof(normal));
normalDataCursor += sizeof(normal);
}
} else if (normalAttribute._element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
auto packedNormal = normalsBufferView.get<glm::uint32>(i);
auto normal = glm::vec3(glm::unpackSnorm3x10_1x2(packedNormal));
normal = normalFunc(normal);
memcpy(normalDataCursor, &normal, sizeof(normal));
normalDataCursor += sizeof(normal);
}
}
// TODO -- other attributes
// face data
const gpu::BufferView& indexBufferView = getIndexBuffer();
gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices();
gpu::Resource::Size indexSize = numIndexes * sizeof(uint32_t);
unsigned char* resultIndexData = new unsigned char[indexSize];
unsigned char* indexDataCursor = resultIndexData;
std::unique_ptr<unsigned char> resultIndexData{ new unsigned char[indexSize] };
unsigned char* indexDataCursor = resultIndexData.get();
for (gpu::BufferView::Index i = 0; i < numIndexes; i++) {
uint32_t index = indexFunc(indexBufferView.get<uint32_t>(i));
@ -197,25 +226,25 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
model::MeshPointer result(new model::Mesh());
gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData);
gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData.get());
gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer);
gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement);
result->setVertexBuffer(resultVertexBufferView);
gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultColorsBuffer = new gpu::Buffer(colorSize, resultColorData);
gpu::Buffer* resultColorsBuffer = new gpu::Buffer(colorSize, resultColorData.get());
gpu::BufferPointer resultColorsBufferPointer(resultColorsBuffer);
gpu::BufferView resultColorsBufferView(resultColorsBufferPointer, colorElement);
result->addAttribute(attributeTypeColor, resultColorsBufferView);
gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData);
gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData.get());
gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer);
gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement);
result->addAttribute(attributeTypeNormal, resultNormalsBufferView);
gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW);
gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData);
gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData.get());
gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer);
gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement);
result->setIndexBuffer(resultIndexesBufferView);
@ -239,6 +268,8 @@ void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
std::function<void(glm::vec3)> colorFunc,
std::function<void(glm::vec3)> normalFunc,
std::function<void(uint32_t)> indexFunc) {
const auto vertexFormat = getVertexFormat();
// vertex data
const gpu::BufferView& vertexBufferView = getVertexBuffer();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices();
@ -250,17 +281,36 @@ void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor);
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
colorFunc(colorsBufferView.get<glm::vec3>(i));
auto colorAttribute = vertexFormat->getAttribute(attributeTypeColor);
if (colorAttribute._element == gpu::Element::VEC3F_XYZ) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
colorFunc(colorsBufferView.get<glm::vec3>(i));
}
} else if (colorAttribute._element == gpu::Element::COLOR_RGBA_32) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
auto rawColor = colorsBufferView.get<glm::uint32>(i);
auto color = glm::unpackUnorm4x8(rawColor);
colorFunc(color);
}
}
// normal data
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
normalFunc(normalsBufferView.get<glm::vec3>(i));
auto normalAttribute = vertexFormat->getAttribute(attributeTypeNormal);
if (normalAttribute._element == gpu::Element::VEC3F_XYZ) {
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
normalFunc(normalsBufferView.get<glm::vec3>(i));
}
} else if (normalAttribute._element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
auto packedNormal = normalsBufferView.get<glm::uint32>(i);
auto normal = glm::unpackSnorm3x10_1x2(packedNormal);
normalFunc(normal);
}
}
// TODO -- other attributes
// face data

View file

@ -84,6 +84,46 @@ public:
void softReset();
/**jsdoc
* <p>The reasons that you may be refused connection to a domain are defined by numeric values:</p>
* <table>
* <thead>
* <tr>
* <th>Reason</th>
* <th>Value</th>
* <th>Description</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><strong>Unknown</strong></td>
* <td><code>0</code></td>
* <td>Some unknown reason.</td>
* </tr>
* <tr>
* <td><strong>ProtocolMismatch</strong></td>
* <td><code>1</code></td>
* <td>The communications protocols of the domain and your Interface are not the same.</td>
* </tr>
* <tr>
* <td><strong>LoginError</strong></td>
* <td><code>2</code></td>
* <td>You could not be logged into the domain.</td>
* </tr>
* <tr>
* <td><strong>NotAuthorized</strong></td>
* <td><code>3</code></td>
* <td>You are not authorized to connect to the domain.</td>
* </tr>
* <tr>
* <td><strong>TooManyUsers</strong></td>
* <td><code>4</code></td>
* <td>The domain already has its maximum number of users.</td>
* </tr>
* </tbody>
* </table>
* @typedef Window.ConnectionRefusedReason
*/
enum class ConnectionRefusedReason : uint8_t {
Unknown,
ProtocolMismatch,

View file

@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit:
case PacketType::EntityData:
case PacketType::EntityPhysics:
return static_cast<PacketVersion>(EntityVersion::OwnershipChallengeFix);
return static_cast<PacketVersion>(EntityVersion::ZoneStageRemoved);
case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::RemovedJurisdictions);

View file

@ -202,6 +202,8 @@ enum class EntityVersion : PacketVersion {
HazeEffect,
StaticCertJsonVersionOne,
OwnershipChallengeFix,
ZoneLightInheritModes,
ZoneStageRemoved
};
enum class EntityScriptCallMethodVersion : PacketVersion {

View file

@ -28,7 +28,6 @@
#include <QString>
#include <QUuid>
#include <BackgroundMode.h>
#include <SharedUtil.h>
#include <ShapeInfo.h>
#include <NLPacket.h>
@ -258,7 +257,6 @@ public:
static int unpackDataFromBytes(const unsigned char* dataBytes, rgbColor& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; }
static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, BackgroundMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); }
static int unpackDataFromBytes(const unsigned char* dataBytes, QString& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, xColor& result);

View file

@ -46,10 +46,7 @@ void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::S
}
batch.setModelTransform(_cauterizedTransform);
} else {
if (_clusterBuffer) {
batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _clusterBuffer);
}
batch.setModelTransform(_transform);
ModelMeshPartPayload::bindTransform(batch, locations, renderMode);
}
}

View file

@ -763,7 +763,8 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
_defaultLight = lp;
// Add the global light to the light stage (for later shadow rendering)
_defaultLightID = lightStage->addLight(lp);
// Set this light to be the default
_defaultLightID = lightStage->addLight(lp, true);
lightStage->addShadow(_defaultLightID);
}

View file

@ -37,14 +37,13 @@ enum LightClusterGridShader_MapSlot {
};
enum LightClusterGridShader_BufferSlot {
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 0,
DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT =1,
CAMERA_CORRECTION_BUFFER_SLOT = 2,
DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT = 0,
CAMERA_CORRECTION_BUFFER_SLOT = 1,
LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT,
LIGHT_INDEX_GPU_SLOT = 5,
LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT = 6,
LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT = 7,
LIGHT_INDEX_GPU_SLOT = 7,
LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 8,
LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT = 9,
LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT = 10,
};
FrustumGrid::FrustumGrid(const FrustumGrid& source) :

View file

@ -67,7 +67,6 @@ public:
NetworkTexturePointer _ambientTexture;
QString _ambientTextureURL;
bool _pendingAmbientTexture { false };
bool _validAmbientTextureURL { false };
protected:
model::LightPointer _light;

View file

@ -28,6 +28,37 @@ static const auto MAX_BIAS = 0.006f;
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
LightStage::LightStage() {
// Add off lights
const LightPointer ambientOffLight { std::make_shared<model::Light>() };
ambientOffLight->setAmbientIntensity(0.0f);
ambientOffLight->setColor(model::Vec3(0.0));
ambientOffLight->setIntensity(0.0f);
ambientOffLight->setType(model::Light::Type::AMBIENT);
_ambientOffLightId = addLight(ambientOffLight);
const LightPointer pointOffLight { std::make_shared<model::Light>() };
pointOffLight->setAmbientIntensity(0.0f);
pointOffLight->setColor(model::Vec3(0.0));
pointOffLight->setIntensity(0.0f);
pointOffLight->setType(model::Light::Type::POINT);
_pointOffLightId = addLight(pointOffLight);
const LightPointer spotOffLight { std::make_shared<model::Light>() };
spotOffLight->setAmbientIntensity(0.0f);
spotOffLight->setColor(model::Vec3(0.0));
spotOffLight->setIntensity(0.0f);
spotOffLight->setType(model::Light::Type::SPOT);
_spotOffLightId = addLight(spotOffLight);
const LightPointer sunOffLight { std::make_shared<model::Light>() };
sunOffLight->setAmbientIntensity(0.0f);
sunOffLight->setColor(model::Vec3(0.0));
sunOffLight->setIntensity(0.0f);
sunOffLight->setType(model::Light::Type::SUN);
_sunOffLightId = addLight(sunOffLight);
// Set default light to the off ambient light (until changed)
_defaultLightId = _ambientOffLightId;
}
LightStage::Shadow::Schema::Schema() {
@ -266,10 +297,12 @@ LightStage::Index LightStage::findLight(const LightPointer& light) const {
}
}
LightStage::Index LightStage::addLight(const LightPointer& light) {
LightStage::Index LightStage::addLight(const LightPointer& light, const bool shouldSetAsDefault) {
Index lightId;
auto found = _lightMap.find(light);
if (found == _lightMap.end()) {
auto lightId = _lights.newElement(light);
lightId = _lights.newElement(light);
// Avoid failing to allocate a light, just pass
if (lightId != INVALID_INDEX) {
@ -286,10 +319,15 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
updateLightArrayBuffer(lightId);
}
return lightId;
} else {
return (*found).second;
lightId = (*found).second;
}
if (shouldSetAsDefault) {
_defaultLightId = lightId;
}
return lightId;
}
LightStage::Index LightStage::addShadow(Index lightIndex, float maxDistance, unsigned int cascadeCount) {
@ -321,7 +359,7 @@ LightStage::LightPointer LightStage::removeLight(Index index) {
}
LightStage::LightPointer LightStage::getCurrentKeyLight() const {
Index keyLightId{ 0 };
Index keyLightId{ _defaultLightId };
if (!_currentFrame._sunLights.empty()) {
keyLightId = _currentFrame._sunLights.front();
}
@ -329,7 +367,7 @@ LightStage::LightPointer LightStage::getCurrentKeyLight() const {
}
LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
Index keyLightId{ 0 };
Index keyLightId { _defaultLightId };
if (!_currentFrame._ambientLights.empty()) {
keyLightId = _currentFrame._ambientLights.front();
}
@ -337,7 +375,7 @@ LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
}
LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
Index keyLightId{ 0 };
Index keyLightId { _defaultLightId };
if (!_currentFrame._sunLights.empty()) {
keyLightId = _currentFrame._sunLights.front();
}
@ -347,7 +385,7 @@ LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
}
LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const {
Index keyLightId{ 0 };
Index keyLightId { _defaultLightId };
if (!_currentFrame._sunLights.empty()) {
keyLightId = _currentFrame._sunLights.front();
}

View file

@ -118,7 +118,9 @@ public:
using Shadows = render::indexed_container::IndexedPointerVector<Shadow>;
Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light);
Index addLight(const LightPointer& light, const bool shouldSetAsDefault = false);
Index getDefaultLight() { return _defaultLightId; }
Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U);
@ -185,6 +187,11 @@ public:
Frame _currentFrame;
Index getAmbientOffLight() { return _ambientOffLightId; }
Index getPointOffLight() { return _pointOffLightId; }
Index getSpotOffLight() { return _spotOffLightId; }
Index getSunOffLight() { return _sunOffLightId; }
protected:
struct Desc {
@ -199,6 +206,13 @@ protected:
Descs _descs;
LightMap _lightMap;
// define off lights
Index _ambientOffLightId;
Index _pointOffLightId;
Index _spotOffLightId;
Index _sunOffLightId;
Index _defaultLightId;
};
using LightStagePointer = std::shared_ptr<LightStage>;

View file

@ -488,23 +488,15 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
}
void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
if (!_isBlendShaped) {
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
batch.setInputFormat((_drawMesh->getVertexFormat()));
batch.setInputStream(0, _drawMesh->getVertexStream());
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
batch.setInputFormat((_drawMesh->getVertexFormat()));
if (_isBlendShaped && _blendedVertexBuffer) {
batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3));
// Stride is 2*sizeof(glm::vec3) because normal and tangents are interleaved
batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), 2 * sizeof(NormalType));
batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
} else {
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
batch.setInputFormat((_drawMesh->getVertexFormat()));
if (_blendedVertexBuffer) {
batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3));
batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3));
batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
} else {
batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0);
batch.setInputFormat((_drawMesh->getVertexFormat()));
batch.setInputStream(0, _drawMesh->getVertexStream());
}
batch.setInputStream(0, _drawMesh->getVertexStream());
}
}

View file

@ -24,9 +24,13 @@
#include <PerfStat.h>
#include <ViewFrustum.h>
#include <GLMHelpers.h>
#include <TBBHelpers.h>
#include <model-networking/SimpleMeshProxy.h>
#include <DualQuaternion.h>
#include <glm/gtc/packing.hpp>
#include "AbstractViewStateInterface.h"
#include "MeshPartPayload.h"
@ -319,6 +323,34 @@ void Model::reset() {
}
}
#if FBX_PACK_NORMALS
static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) {
auto absNormal = glm::abs(normal);
auto absTangent = glm::abs(tangent);
normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z));
tangent /= glm::max(1e-6f, glm::max(glm::max(absTangent.x, absTangent.y), absTangent.z));
normal = glm::clamp(normal, -1.0f, 1.0f);
tangent = glm::clamp(tangent, -1.0f, 1.0f);
normal *= 511.0f;
tangent *= 511.0f;
normal = glm::round(normal);
tangent = glm::round(tangent);
glm::detail::i10i10i10i2 normalStruct;
glm::detail::i10i10i10i2 tangentStruct;
normalStruct.data.x = int(normal.x);
normalStruct.data.y = int(normal.y);
normalStruct.data.z = int(normal.z);
normalStruct.data.w = 0;
tangentStruct.data.x = int(tangent.x);
tangentStruct.data.y = int(tangent.y);
tangentStruct.data.z = int(tangent.z);
tangentStruct.data.w = 0;
packedNormal = normalStruct.pack;
packedTangent = tangentStruct.pack;
}
#endif
bool Model::updateGeometry() {
bool needFullUpdate = false;
@ -344,10 +376,28 @@ bool Model::updateGeometry() {
// TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes?
auto buffer = std::make_shared<gpu::Buffer>();
if (!mesh.blendshapes.isEmpty()) {
buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3));
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData());
std::vector<NormalType> normalsAndTangents;
normalsAndTangents.reserve(mesh.normals.size() + mesh.tangents.size());
for (auto normalIt = mesh.normals.begin(), tangentIt = mesh.tangents.begin();
normalIt != mesh.normals.end();
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
glm::uint32 finalNormal;
glm::uint32 finalTangent;
packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent);
#else
const auto finalNormal = *normalIt;
const auto finalTangent = *tangentIt;
#endif
normalsAndTangents.push_back(finalNormal);
normalsAndTangents.push_back(finalTangent);
}
buffer->resize(mesh.vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType)));
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData());
buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3),
mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData());
mesh.normals.size() * 2 * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
}
_blendedVertexBuffers.push_back(buffer);
}
@ -1015,7 +1065,7 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe
void Blender::run() {
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
QVector<glm::vec3> vertices, normals;
QVector<glm::vec3> vertices, normals, tangents;
if (_model) {
int offset = 0;
foreach (const FBXMesh& mesh, _meshes) {
@ -1024,8 +1074,10 @@ void Blender::run() {
}
vertices += mesh.vertices;
normals += mesh.normals;
tangents += mesh.tangents;
glm::vec3* meshVertices = vertices.data() + offset;
glm::vec3* meshNormals = normals.data() + offset;
glm::vec3* meshTangents = tangents.data() + offset;
offset += mesh.vertices.size();
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) {
@ -1040,6 +1092,7 @@ void Blender::run() {
int index = blendshape.indices.at(j);
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient;
meshNormals[index] += blendshape.normals.at(j) * normalCoefficient;
meshTangents[index] += blendshape.tangents.at(j) * normalCoefficient;
}
}
}
@ -1048,7 +1101,7 @@ void Blender::run() {
QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices",
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber),
Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices),
Q_ARG(const QVector<glm::vec3>&, normals));
Q_ARG(const QVector<glm::vec3>&, normals), Q_ARG(const QVector<glm::vec3>&, tangents));
}
void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) {
@ -1238,7 +1291,7 @@ bool Model::maybeStartBlender() {
}
void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents) {
auto geometryRef = geometry.lock();
if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) {
return;
@ -1246,6 +1299,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
_appliedBlendNumber = blendNumber;
const FBXGeometry& fbxGeometry = getFBXGeometry();
int index = 0;
std::vector<NormalType> normalsAndTangents;
for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
if (mesh.blendshapes.isEmpty()) {
@ -1253,11 +1307,67 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
}
gpu::BufferPointer& buffer = _blendedVertexBuffers[i];
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) vertices.constData() + index*sizeof(glm::vec3));
buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3),
mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) normals.constData() + index*sizeof(glm::vec3));
const auto vertexCount = mesh.vertices.size();
const auto verticesSize = vertexCount * sizeof(glm::vec3);
const auto offset = index * sizeof(glm::vec3);
index += mesh.vertices.size();
normalsAndTangents.clear();
normalsAndTangents.resize(normals.size()+tangents.size());
assert(normalsAndTangents.size() == 2 * vertexCount);
// Interleave normals and tangents
#if 0
// Sequential version for debugging
auto normalsRange = std::make_pair(normals.begin() + index, normals.begin() + index + vertexCount);
auto tangentsRange = std::make_pair(tangents.begin() + index, tangents.begin() + index + vertexCount);
auto normalsAndTangentsIt = normalsAndTangents.begin();
for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first;
normalIt != normalsRange.second;
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
glm::uint32 finalNormal;
glm::uint32 finalTangent;
packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent);
#else
const auto finalNormal = *normalIt;
const auto finalTangent = *tangentIt;
#endif
*normalsAndTangentsIt = finalNormal;
++normalsAndTangentsIt;
*normalsAndTangentsIt = finalTangent;
++normalsAndTangentsIt;
}
#else
// Parallel version for performance
tbb::parallel_for(tbb::blocked_range<size_t>(index, index+vertexCount), [&](const tbb::blocked_range<size_t>& range) {
auto normalsRange = std::make_pair(normals.begin() + range.begin(), normals.begin() + range.end());
auto tangentsRange = std::make_pair(tangents.begin() + range.begin(), tangents.begin() + range.end());
auto normalsAndTangentsIt = normalsAndTangents.begin() + (range.begin()-index)*2;
for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first;
normalIt != normalsRange.second;
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
glm::uint32 finalNormal;
glm::uint32 finalTangent;
packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent);
#else
const auto finalNormal = *normalIt;
const auto finalTangent = *tangentIt;
#endif
*normalsAndTangentsIt = finalNormal;
++normalsAndTangentsIt;
*normalsAndTangentsIt = finalTangent;
++normalsAndTangentsIt;
}
});
#endif
buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + offset);
buffer->setSubData(verticesSize, 2 * vertexCount * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
index += vertexCount;
}
}
@ -1424,10 +1534,11 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) {
}
}
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber,
const Geometry::WeakPointer& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals,
const QVector<glm::vec3>& tangents) {
if (model) {
model->setBlendedVertices(blendNumber, geometry, vertices, normals);
model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents);
}
_pendingBlenders--;
{

View file

@ -82,7 +82,7 @@ public:
/// Sets the URL of the model to render.
// Should only be called from the model's rendering thread to avoid access violations of changed geometry.
Q_INVOKABLE void setURL(const QUrl& url);
Q_INVOKABLE virtual void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; }
// new Scene/Engine rendering support
@ -118,7 +118,7 @@ public:
/// Sets blended vertices computed in a separate thread.
void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents);
bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); }
bool isAddedToScene() const { return _addedToScene; }
@ -140,7 +140,7 @@ public:
const Geometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; }
const QVariantMap getTextures() const { assert(isLoaded()); return _renderGeometry->getTextures(); }
Q_INVOKABLE void setTextures(const QVariantMap& textures);
Q_INVOKABLE virtual void setTextures(const QVariantMap& textures);
/// Provided as a convenience, will crash if !isLoaded()
// And so that getGeometry() isn't chained everywhere
@ -478,7 +478,7 @@ public:
public slots:
void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents);
private:
using Mutex = std::mutex;

View file

@ -68,10 +68,11 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs)
auto lightStage = context->_scene->getStage<LightStage>();
assert(lightStage);
lightStage->_currentFrame.pushSunLight(0);
lightStage->_currentFrame.pushAmbientLight(0);
hazeStage->_currentFrame.pushHaze(0);
lightStage->_currentFrame.pushSunLight(lightStage->getDefaultLight());
lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight());
backgroundStage->_currentFrame.pushBackground(0);
hazeStage->_currentFrame.pushHaze(0);
}
const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() {

View file

@ -60,20 +60,20 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
// alloc the resulting mesh
gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3);
unsigned char* combinedVertexData = new unsigned char[combinedVertexSize];
unsigned char* combinedVertexDataCursor = combinedVertexData;
std::unique_ptr<unsigned char> combinedVertexData{ new unsigned char[combinedVertexSize] };
unsigned char* combinedVertexDataCursor = combinedVertexData.get();
gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3);
unsigned char* combinedColorData = new unsigned char[combinedColorSize];
unsigned char* combinedColorDataCursor = combinedColorData;
std::unique_ptr<unsigned char> combinedColorData{ new unsigned char[combinedColorSize] };
unsigned char* combinedColorDataCursor = combinedColorData.get();
gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3);
unsigned char* combinedNormalData = new unsigned char[combinedNormalSize];
unsigned char* combinedNormalDataCursor = combinedNormalData;
std::unique_ptr<unsigned char> combinedNormalData{ new unsigned char[combinedNormalSize] };
unsigned char* combinedNormalDataCursor = combinedNormalData.get();
gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t);
unsigned char* combinedIndexData = new unsigned char[combinedIndexSize];
unsigned char* combinedIndexDataCursor = combinedIndexData;
std::unique_ptr<unsigned char> combinedIndexData{ new unsigned char[combinedIndexSize] };
unsigned char* combinedIndexDataCursor = combinedIndexData.get();
uint32_t indexStartOffset { 0 };
@ -105,27 +105,27 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
model::MeshPointer result(new model::Mesh());
gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData);
gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData.get());
gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer);
gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement);
result->setVertexBuffer(combinedVertexBufferView);
int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h
gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData);
gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData.get());
gpu::BufferPointer combinedColorsBufferPointer(combinedColorsBuffer);
gpu::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement);
result->addAttribute(attributeTypeColor, combinedColorsBufferView);
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData);
gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData.get());
gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer);
gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement);
result->addAttribute(attributeTypeNormal, combinedNormalsBufferView);
gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW);
gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData);
gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData.get());
gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer);
gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement);
result->setIndexBuffer(combinedIndexesBufferView);
@ -152,9 +152,10 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro
return QScriptValue(false);
}
const auto inverseTransposeTransform = glm::inverse(glm::transpose(transform));
model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[&](glm::vec3 color){ return color; },
[&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); },
[&](glm::vec3 normal){ return glm::vec3(inverseTransposeTransform * glm::vec4(normal, 0.0f)); },
[&](uint32_t index){ return index; });
MeshProxy* resultProxy = new SimpleMeshProxy(result);
return meshToScriptValue(_modelScriptEngine, resultProxy);

View file

@ -31,6 +31,7 @@ class ScriptEngines : public QObject, public Dependency {
Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT)
Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT)
Q_PROPERTY(QString debugScriptUrl READ getDebugScriptUrl WRITE setDebugScriptUrl)
public:
using ScriptInitializer = std::function<void(ScriptEnginePointer)>;
@ -41,6 +42,9 @@ public:
void loadScripts();
void saveScripts();
QString getDebugScriptUrl() { return _debugScriptUrl; };
void setDebugScriptUrl(const QString& url) { _debugScriptUrl = url; };
QString getScriptsLocation() const;
void loadDefaultScripts();
void setScriptsLocation(const QString& scriptsLocation);
@ -117,6 +121,7 @@ protected:
std::atomic<bool> _isStopped { false };
std::atomic<bool> _isReloading { false };
bool _defaultScriptsLocationOverridden { false };
QString _debugScriptUrl;
};
QUrl normalizeScriptURL(const QUrl& rawScriptURL);

View file

@ -1,23 +0,0 @@
//
// BackgroundMode.h
// libraries/physics/src
//
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_BackgroundMode_h
#define hifi_BackgroundMode_h
enum BackgroundMode {
BACKGROUND_MODE_INHERIT,
BACKGROUND_MODE_SKYBOX,
BACKGROUND_MODE_ITEM_COUNT,
};
#endif // hifi_BackgroundMode_h

View file

@ -262,7 +262,7 @@ controller::Pose ovrControllerPoseToHandPose(
pose.translation = toGlm(handPose.ThePose.Position);
pose.translation += rotation * translationOffset;
pose.rotation = rotation * rotationOffset;
pose.angularVelocity = toGlm(handPose.AngularVelocity);
pose.angularVelocity = rotation * toGlm(handPose.AngularVelocity);
pose.velocity = toGlm(handPose.LinearVelocity);
pose.valid = true;
return pose;

View file

@ -29,7 +29,8 @@ var DEFAULT_SCRIPTS_COMBINED = [
"system/notifications.js",
"system/dialTone.js",
"system/firstPersonHMD.js",
"system/tablet-ui/tabletUI.js"
"system/tablet-ui/tabletUI.js",
"system/emote.js"
];
var DEFAULT_SCRIPTS_SEPARATE = [
"system/controllers/controllerScripts.js"

View file

@ -0,0 +1,325 @@
//
// sunModel.js
// scripts/developer
//
// Created by Nissim Hadar on 2017/12/27.
// Copyright 2013 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
//
// Sun angle is based on the NOAA model - see https://www.esrl.noaa.gov/gmd/grad/solcalc/
//
(function() {
// Utility functions for trig. calculations
function toRadians(angle_degs) {
return angle_degs * (Math.PI / 180);
}
function toDegrees(angle_rads) {
return angle_rads * (180.0 / Math.PI);
}
// Code to check if Daylight Savings is active
Date.prototype.stdTimezoneOffset = function() {
var fy = this.getFullYear();
if (!Date.prototype.stdTimezoneOffset.cache.hasOwnProperty(fy)) {
var maxOffset = new Date(fy, 0, 1).getTimezoneOffset();
var monthsTestOrder = [6, 7, 5, 8, 4, 9, 3, 10, 2, 11, 1];
for(var mi = 0;mi < 12; ++mi) {
var offset = new Date(fy, monthsTestOrder[mi], 1).getTimezoneOffset();
if (offset != maxOffset) {
maxOffset = Math.max(maxOffset, offset);
break;
}
}
Date.prototype.stdTimezoneOffset.cache[fy] = maxOffset;
}
return Date.prototype.stdTimezoneOffset.cache[fy];
};
// Cache the result for per year stdTimezoneOffset so that you don't need to recalculate it when testing multiple dates in
// the same year.
Date.prototype.stdTimezoneOffset.cache = {};
Date.prototype.isDST = function() {
return this.getTimezoneOffset() < this.stdTimezoneOffset();
};
// The Julian Date is the number of days (fractional) that have elapsed since Jan 1st, 4713 BC
// See https://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
function getJulianDay(dateTime) {
var month = dateTime.getMonth() + 1;
var day = dateTime.getDate() + 1;
var year = dateTime.getFullYear();
if (month <= 2) {
year -= 1;
month += 12;
}
var A = Math.floor(year / 100);
var B = 2 - A + Math.floor(A / 4);
return Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5;
}
function getMinutes(dateTime) {
var hours = dateTime.getHours();
var minutes = dateTime.getMinutes();
var seconds = dateTime.getSeconds();
if (Date.prototype.isDST()) {
hour -= 1;
}
return hours * 60 + minutes + seconds / 60.0;
}
function calcGeomMeanAnomalySun(t) {
var M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
return M; // in degrees
}
function calcSunEqOfCenter(t) {
var m = calcGeomMeanAnomalySun(t);
var mrad = toRadians(m);
var sinm = Math.sin(mrad);
var sin2m = Math.sin(mrad + mrad);
var sin3m = Math.sin(mrad + mrad + mrad);
var C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289;
return C; // in degrees
}
function calcGeomMeanLongSun(t) {
var L0 = 280.46646 + t * (36000.76983 + t*(0.0003032))
while(L0 > 360.0) {
L0 -= 360.0
}
while(L0 < 0.0) {
L0 += 360.0
}
return L0 // in degrees
}
function calcSunTrueLong(t) {
var l0 = calcGeomMeanLongSun(t);
var c = calcSunEqOfCenter(t);
var O = l0 + c;
return O; // in degrees
}
function calcSunApparentLong(t) {
var o = calcSunTrueLong(t);
var omega = 125.04 - 1934.136 * t;
var lambda = o - 0.00569 - 0.00478 * Math.sin(toRadians(omega));
return lambda; // in degrees
}
function calcMeanObliquityOfEcliptic(t) {
var seconds = 21.448 - t * (46.8150 + t * (0.00059 - t * (0.001813)));
var e0 = 23.0 + (26.0 + (seconds / 60.0)) / 60.0;
return e0; // in degrees
}
function calcObliquityCorrection(t) {
var e0 = calcMeanObliquityOfEcliptic(t);
var omega = 125.04 - 1934.136 * t;
var e = e0 + 0.00256 * Math.cos(toRadians(omega));
return e; // in degrees
}
function calcSunDeclination(t) {
var e = calcObliquityCorrection(t);
var lambda = calcSunApparentLong(t);
var sint = Math.sin(toRadians(e)) * Math.sin(toRadians(lambda));
var theta = toDegrees(Math.asin(sint));
return theta; // in degrees
}
function calcEccentricityEarthOrbit(t) {
var e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
return e; // unitless
}
function calcEquationOfTime(t) {
// Converts from "mean" solar day (i.e. days that are exactly 24 hours)
// to apparent solar day (as observed)
// This is essentially the east-west component of the analemma.
//
// This is caused by 2 effects: the obliquity of the ecliptic, the eccentricity of earth's orbit
var epsilon = calcObliquityCorrection(t);
var l0 = calcGeomMeanLongSun(t);
var e = calcEccentricityEarthOrbit(t);
var m = calcGeomMeanAnomalySun(t);
var y = Math.tan(toRadians(epsilon) / 2.0);
y *= y;
var sin2l0 = Math.sin(2.0 * toRadians(l0));
var sinm = Math.sin(toRadians(m));
var cos2l0 = Math.cos(2.0 * toRadians(l0));
var sin4l0 = Math.sin(4.0 * toRadians(l0));
var sin2m = Math.sin(2.0 * toRadians(m));
var Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 -
0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
return toDegrees(Etime) * 4.0; // in minutes of time
}
function calcSunTrueAnomaly(t) {
var m = calcGeomMeanAnomalySun(t);
var c = calcSunEqOfCenter(t);
var v = m + c;
return v; // in degrees
}
function calcSunRadVector(t) {
var v = calcSunTrueAnomaly(t);
var e = calcEccentricityEarthOrbit(t);
var R = (1.000001018 * (1 - e * e)) / (1 + e * Math.cos(toRadians(v)));
return R; // in AUs
}
function parseJSON(json) {
try {
return JSON.parse(json);
} catch (e) {
return undefined;
}
}
var COMPUTATION_CYCLE = 5000; // Run every 5 seconds
this.preload = function(entityID) { // We don't have the entityID before the preload
// Define user data
var userDataProperties = {
"userData": "{ \"latitude\": 47.0, \"longitude\": 122.0 }"
};
Entities.editEntity(entityID, userDataProperties);
Script.setInterval(
function() {
// Read back user data
var userData = Entities.getEntityProperties(entityID, 'userData').userData;
var data = parseJSON(userData);
var latitude_degs = data.latitude;
var longitude_degs = data.longitude;
// These are used a lot
var latitude = toRadians(latitude_degs);
var longitude = toRadians(longitude_degs);
var dateTime = new Date();
var julianDay = getJulianDay(dateTime);
var localTimeMinutes = getMinutes(dateTime);
var timeZone = -dateTime.getTimezoneOffset() / 60;
var totalTime = julianDay + localTimeMinutes/1440.0 - timeZone / 24.0;
// J2000.0 is the epoch starting Jan 1st, 2000 (noon), expressed as a Julian day
var J2000 = 2451545.0
// Number of years that have passed since J2000.0
var julianDayModified = (J2000 - 2451545.0)/36525.0;
var eqTime = calcEquationOfTime(julianDayModified)
var theta_rads = toRadians(calcSunDeclination(julianDayModified));
var solarTimeFix = eqTime + 4.0 * longitude_degs - 60.0 * timeZone;
var earthRadVec = calcSunRadVector(julianDayModified);
var trueSolarTime = localTimeMinutes + solarTimeFix;
while (trueSolarTime > 1440) {
trueSolarTime -= 1440;
}
var hourAngle = trueSolarTime / 4.0 - 180.0;
if (hourAngle < -180.0) {
hourAngle += 360.0;
}
var hourAngleRadians = toRadians(hourAngle);
var csz = Math.sin(latitude) * Math.sin(theta_rads) +
Math.cos(latitude) * Math.cos(theta_rads) * Math.cos(hourAngleRadians);
csz = Math.min(1.0, Math.max(-1.0, csz));
var zenith_degs = toDegrees(Math.acos(csz));
var azDenom = ( Math.cos(latitude) * Math.sin(toRadians(zenith_degs)));
if (Math.abs(azDenom) > 0.001) {
azRad = (( Math.sin(latitude) * Math.cos(toRadians(zenith_degs)) ) - Math.sin(theta_rads)) / azDenom;
if (Math.abs(azRad) > 1.0) {
if (azRad < 0.0) {
azRad = -1.0;
} else {
azRad = 1.0;
}
}
var solarAzimuth_degs = 180.0 - toDegrees(Math.acos(azRad))
if (hourAngle > 0.0) {
solarAzimuth_degs = -solarAzimuth_degs;
}
} else {
if (latitude_degs > 0.0) {
solarAzimuth_degs = 180.0;
} else {
solarAzimuth_degs = 0.0;
}
}
if (solarAzimuth_degs < 0.0) {
solarAzimuth_degs += 360.0;
}
// Atmospheric Refraction correction
var exoatmElevation = 90.0 - zenith_degs;
if (exoatmElevation > 85.0) {
var refractionCorrection = 0.0;
} else {
var te = Math.tan(toRadians(exoatmElevation));
if (exoatmElevation > 5.0) {
var refractionCorrection = 58.1 / te - 0.07 / (te * te * te) + 0.000086 / (te * te * te * te * te);
} else if (exoatmElevation > -0.575) {
var refractionCorrection =
1735.0 + exoatmElevation *
(-518.2 + exoatmElevation * (103.4 + exoatmElevation * (-12.79 + exoatmElevation * 0.711)));
} else {
var refractionCorrection = -20.774 / te;
}
refractionCorrection = refractionCorrection / 3600.0;
}
var solarZenith = zenith_degs - refractionCorrection;
var solarAltitude_degs = 90.0 - solarZenith; // aka solar elevation
// Convert to XYZ
var solarAltitude = toRadians(solarAltitude_degs);
var solarAzimuth = toRadians(solarAzimuth_degs);
var xPos = Math.cos(solarAltitude) * Math.sin(solarAzimuth);
var zPos = Math.cos(solarAltitude) * Math.cos(solarAzimuth);
var yPos = -Math.sin(solarAltitude);
// Compute intensity, modelling the atmosphere as a spherical shell
// The optical air mass ratio at zenith is 1.0, and around 38.0 at the horizon
// The ratio is limited between 1 and 38
var EARTH_RADIUS_KM = 6371.0;
var ATMOSPHERE_THICKNESS_KM = 9.0;
var r = EARTH_RADIUS_KM / ATMOSPHERE_THICKNESS_KM;
var opticalAirMassRatio = Math.sqrt(r * r * csz * csz + 2 * r + 1) - r * csz;
opticalAirMassRatio = Math.min(38.0, Math.max(1.0, opticalAirMassRatio));
Entities.editEntity(
entityID, {
keyLight : {
direction: { x: xPos, y: yPos, z: zPos },
intensity: 1.0 / opticalAirMassRatio
}
}
);
},
COMPUTATION_CYCLE
);
};
});

View file

@ -9,34 +9,173 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var array = [];
function listKeys(string, object) {
if (string === "listKeys" || string === "array" || string === "buffer" || string === "i") {
return;
(function(){
var array = [];
var mainKeys = Object.keys(this);
var qml = Script.resourcesPath() + '/qml/CurrentAPI.qml';
var needsUpdate = false;
var updateTime = 20;
var updateData = [];
var deltaTime = 0;
var maxUpdatingMethods = 20;
var scriptPath = "";
if (ScriptDiscoveryService.debugScriptUrl != "") {
Script.include(ScriptDiscoveryService.debugScriptUrl);
}
if (typeof(object) !== "object" || object === null) {
array.push(string + " " + typeof(object));
return;
}
var keys = Object.keys(object);
for (var i = 0; i < keys.length; ++i) {
if (string === "") {
listKeys(keys[i], object[keys[i]]);
} else if (keys[i] !== "parent") {
listKeys(string + "." + keys[i], object[keys[i]]);
var window = new OverlayWindow({
title: 'API Debugger',
source: qml,
width: 1200,
height: 500
});
window.closed.connect(function () {
Script.stop();
});
function addMainKeys(){
var keys = Object.keys(this);
for (var i = 0; i < keys.length; i++) {
array.push({member:keys[i] , type: "class"});
}
}
}
listKeys("", this);
array.sort();
function memberHasValue(member, type) {
if (type === "function()") {
if (member.indexOf(".has") < 0 && member.indexOf(".is") < 0 && member.indexOf(".get") < 0) {
return false;
}
if (member.indexOf("indow") < 0) {
return true;
}
} else if (type === "boolean" || type === "string" || type === "number" || type === "user") {
return true;
}
return false;
}
var buffer = "\n======= JS API list =======";
for (var i = 0; i < array.length; ++i) {
buffer += "\n" + array[i];
}
buffer += "\n========= API END =========\n";
function listKeys(string, object) {
if (string === "listKeys" || string === "array" || string === "buffer" || string === "i") {
return;
}
if (typeof(object) !== "object" || object === null) {
var type = typeof(object);
if (type === "function") {
chain = string.split("(");
if (chain.length > 1) {
string = chain[0];
type = "function(" + chain[1];
} else {
type = "function()";
}
}
var value = "";
var hasValue = false;
if (memberHasValue(string, type)){
var evalstring = type === "function()" ? string+"()" : string;
try{
value = "" + eval(evalstring);
hasValue = true;
} catch(e) {
value = "Error evaluating";
}
}
array.push({member:string , type: type, value: value, hasValue: hasValue});
return;
}
var keys = Object.keys(object);
for (var i = 0; i < keys.length; ++i) {
if (string === "") {
listKeys(keys[i], object[keys[i]]);
} else if (keys[i] !== "parent") {
listKeys(string + "." + keys[i], object[keys[i]]);
}
}
}
function findMethods(addon, object, addkeys) {
array = [];
var string = addkeys ? "" : addon+".";
listKeys(string, object);
if (addkeys) {
addMainKeys();
}
array.sort(function(a, b){
if(a.member < b.member) return -1;
if(a.member > b.member) return 1;
return 0;
});
};
findMethods("", this, true);
window.sendToQml({type:"methods", data:array});
window.fromQml.connect(function(message){
if (message.type == "refreshValues") {
updateData = message.data;
updateValues();
} else if (message.type == "startRefreshValues") {
updateData = message.data;
if (updateData.length > maxUpdatingMethods) {
updateData = message.data;
updateValues();
} else {
needsUpdate = true;
deltaTime = updateTime;
}
} else if (message.type == "stopRefreshValues") {
needsUpdate = false;
deltaTime = 0;
} else if (message.type == "evaluateMember") {
var value = ""
try {
value = "" + eval(message.data.member);
} catch(e) {
value = "Error evaluating"
}
window.sendToQml({type:"evaluateMember", data:{value:value, index:message.data.index}});
} else if (message.type == "selectScript") {
scriptPath = Window.browse("Select script to debug", "*.js", "JS files(*.js)");
if (scriptPath) {
ScriptDiscoveryService.stopScript(Paths.defaultScripts + "/developer/utilities/tools/currentAPI.js", true);
}
}
});
function updateValues() {
for (var i = 0; i < updateData.length; i++) {
try {
updateData[i].value = "" + eval(updateData[i].member);
} catch(e) {
updateData[i].value = "Error evaluating"
}
}
window.sendToQml({type: "refreshValues", data: updateData});
}
Script.update.connect(function(){
deltaTime++;
if (deltaTime > updateTime) {
deltaTime = 0;
if (needsUpdate) {
updateValues();
}
}
});
Script.scriptEnding.connect(function(){
if (!scriptPath || scriptPath.length == 0) {
ScriptDiscoveryService.debugScriptUrl = "";
} else {
ScriptDiscoveryService.debugScriptUrl = scriptPath;
}
console.log("done running");
window.close();
});
}());
print(buffer);

Binary file not shown.

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